2.5장: API 라우트
학습 시간: 45분
1. 라우트(Route)란 무엇인가요? 쉽게 설명하기
컨트롤러(PlanetController
)를 큰 오피스 센터라고 상상해 보세요. 각 메소드(index
, store
, show
)는 자신의 업무를 수행하는 부서입니다.
라우트(Route)는 건물 입구에 붙어 있는 주소판과 같습니다. 이것은 다음과 같이 말합니다:
- "누군가
/planets
주소로 GET 메소드를 통해 왔다면 —index
부서(모두 보여주기)로 보내세요." - "누군가
/planets
주소로 POST 메소드를 통해 (데이터와 함께) 왔다면 —store
부서(새로운 것 생성)로 보내세요."
라우트 없이는 외부의 어떤 요청도 코드 내에서 필요한 부서를 찾지 못할 것입니다. API에서 이러한 "주소판"의 주요 파일은 routes/api.php
입니다.
Laravel 11+에서는 기본적으로 "API 주소록"이 없습니다. 우리는 php artisan install:api
명령을 실행하여 직접 만들었습니다. 이제 routes/api.php
파일이 생겼고, 이것은 우리 API의 모든 라우트를 관리하는 주요 제어 센터입니다.
api.php와 web.php의 주요 차이점:
/api
접두사: Laravel은 이 파일의 모든 URL 주소에 자동으로 /api를 추가합니다./planets
라우트는/api/planets
로 바뀝니다.- "무상태(Stateless)": 일반적인 웹처럼 세션과 쿠키 파일이 없습니다. 각 요청은 독립적이며 인증에 필요한 모든 정보(일반적으로 헤더의 API 토큰)를 포함해야 합니다.
2. 초보자의 길: 라우트 수동 생성
원리를 이해하기 위해 직접 단 하나의 라우트를 만들어 봅시다. 우리의 목표는 /api/planets
URL이 모든 행성 목록을 표시하도록 하는 것입니다.
routes/api.php
를 열고 다음을 작성하세요:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PlanetController; // 컨트롤러의 위치를 지정합니다.
// (1) (2) (3)
Route::get( '/planets', [PlanetController::class, 'index'] );
// ^ ^ ^
// (HTTP 메소드) (URL 주소) (호출할 컨트롤러 및 메소드)
이 줄을 부분별로 분석해 봅시다:
Route::get(...)
— 우리는 "이 라우트는 GET 요청에만 작동합니다"라고 말합니다.'/planets'
— 이것은 Laravel이 수신할 URL입니다./api
접두사를 고려하면 전체 주소는http://space-api.test/api/planets
가 됩니다.[PlanetController::class, 'index']
— 이것은 "목적지"입니다. 우리는 "요청이 오면PlanetController
클래스를 찾아 그 안에 있는index()
메소드를 호출하세요"라고 말합니다.
이제 모든 것이 연결됩니다! 요청 -> 라우트 -> 컨트롤러 -> 메소드.
그럼 만약 ID로 하나의 행성을 가져와야 한다면 어떨까요? 예를 들어, /api/planets/5
와 같이요.
여기서 {planet}
은 "템플릿" 또는 변수입니다. Laravel은 이 위치에 무엇이든 올 수 있다는 것을 이해합니다 (ID, 슬러그). 그런 다음 이 값을 show(Planet $planet)
메소드로 전달합니다. Laravel이 ID를 통해 행성을 스스로 찾는 이러한 "마법"을 라우트 모델 바인딩(Route Model Binding)이라고 합니다.
3. 마스터의 길: apiResource
— 모든 것을 지배하는 한 줄
각 라우트를 수동으로 생성하는 것(index
, show
, store
, update
, destroy
에 대해)은 지루한 일입니다. Laravel 개발자들은 이 점을 이해하고 강력한 도우미인 apiResource
를 만들었습니다.
우리가 작성했던 모든 것을 지우고 한 줄로 대체하세요:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PlanetController;
Route::apiResource('planets', PlanetController::class);
이 한 줄은 내부적으로 무엇을 할까요? 이 한 줄은 우리가 컨트롤러에서 이미 구현한 표준 CRUD 작업을 위한 전체 라우트 세트를 자동으로 생성합니다.
메소드 | URL | 연결되는 메소드 | 목적 |
---|---|---|---|
GET | /api/planets |
index() |
모든 행성 목록 가져오기 |
POST | /api/planets |
store() |
새 행성 생성 |
GET | /api/planets/{planet} |
show() |
특정 행성 하나 보여주기 |
PUT/PATCH | /api/planets/{planet} |
update() |
기존 행성 업데이트 |
DELETE | /api/planets/{planet} |
destroy() |
행성 삭제 |
직접 확인해 볼 수 있습니다. 터미널에서 다음 명령어를 실행하세요:
생성된 모든 라우트가 포함된 테이블을 보게 될 것입니다. apiResource
는 표준 API를 생성할 때 시간을 절약해 주는 가장 좋은 친구입니다.
4. 특별 미션 및 라우트 순서
만약 apiResource
에 없는 비표준 라우트가 필요하다면 어떨까요? 예를 들어, /api/planets/random
주소로 무작위 행성을 가져오는 경우요.
추가해 봅시다. 하지만 여기에는 초보자 10명 중 9명이 빠지는 치명적인 함정이 있습니다.
잘못된 순서 (작동 안함!):
Route::apiResource('planets', PlanetController::class);
Route::get('/planets/random', [PlanetController::class, 'random']); // <-- 작동하지 않습니다.
GET /planets/{planet}
라우트를 생성한 Route::apiResource
를 먼저 보게 됩니다. /planets/random
을 요청하면 Laravel은 "random"을 행성 ID라고 생각하여 데이터베이스에서 ID가 "random"인 행성을 찾으려고 시도할 것이고, 그러면 오류가 발생할 것입니다.
올바른 순서 (작동함!):
<?php
use App\Http\Controllers\PlanetController;
use Illuminate\Support\Facades\Route;
// 1. 먼저 구체적인 라우트를 선언합니다.
Route::get('/planets/random', [PlanetController::class, 'random']);
// 2. 그 다음에만 변수가 있는 일반 라우트를 선언합니다.
Route::apiResource('planets', PlanetController::class);
⚠️ 중요!
api/planets/random 라우트를 테스트하려면 PlanetController에 새 핸들러를 추가해야 합니다:
규칙: 항상 더 구체적인 라우트(/random
과 같은)를 더 일반적이고 템플릿화된 라우트(/{planet}
과 같은) 앞에 선언하세요.
5. 그룹화: 정리하기
라우트가 많아지면 그룹화할 수 있으며, 그렇게 해야 합니다.
A. API 버전 관리
미래에 기존 API를 사용하는 오래된 애플리케이션이 손상되지 않도록, URL에 /api/v1/...
과 같이 버전을 추가하는 것이 일반적입니다.
<?php
Route::prefix('v1')->group(function () {
// 이 그룹 내부의 모든 라우트는 /v1 접두사를 갖게 됩니다.
// 최종 URL: /api/v1/planets
Route::get('/planets/random', [PlanetController::class, 'random']);
Route::apiResource('planets', PlanetController::class);
});
B. 라우트 보호 (미들웨어) 행성을 보는 것은 누구나 할 수 있지만, 생성, 업데이트, 삭제는 인증된 사용자만 가능하다고 가정해 봅시다.
<?php
// 모두에게 공개된 라우트
Route::get('/planets', [PlanetController::class, 'index']);
Route::get('/planets/{planet}', [PlanetController::class, 'show']);
// "패스"(인증)를 요구하는 라우트 그룹
Route::middleware('auth:sanctum')->group(function () {
Route::post('/planets', [PlanetController::class, 'store']);
Route::put('/planets/{planet}', [PlanetController::class, 'update']);
Route::delete('/planets/{planet}', [PlanetController::class, 'destroy']);
});
여기서 middleware('auth:sanctum')
는 그룹 내 라우트에 접근하려는 모든 사람의 "패스"를 확인하는 경비원과 같습니다.
6. Postman을 통한 테스트
이제 모든 라우트가 설정되었으니, 테스트해 볼 시간입니다.
- Herd를 사용한다면: 귀하의 웹사이트는 이미
http://space-api.test
와 같은 주소로 작동 중입니다. - 그렇지 않다면:
php artisan serve
명령으로 로컬 서버를 실행하세요. 주소는http://localhost:8000
이 될 것입니다.
Postman을 열고 요청을 보내세요:
GET http://space-api.test/api/planets
GET http://space-api.test/api/planets/random
POST http://space-api.test/api/planets
(Body
탭 ->raw
->JSON
에서 JSON 요청 본문을 추가하는 것을 잊지 마세요).
POST 요청 예시:
{
"name": "Kepler-186f",
"description": "Первая планета земного размера в обитаемой зоне",
"size_km": 15000,
"solar_system": "Kepler-186",
"is_habitable": true
}
8. 자주 발생하는 라우팅 오류
- 404 Not Found
- 잘못된 URL (
/api/planets
대신/api/planet
) php artisan serve
를 잊음- 405 Method Not Allowed
- 잘못된 HTTP 메소드 (예: POST 대신 GET)
- Missing Controller
- 컨트롤러 이름 오타 (
PlanetControler
) - Route Name Collision
- 라우트 이름 중복
복습 퀴즈
🚀 장 요약:
우주 API를 위한 "하이퍼스페이스 경로"를 구축했습니다! 이제:
- 🗺️ 모든 엔드포인트는
/api/...
로 접근 가능합니다. - 🔗 리소스 경로는 컨트롤러에 연결됩니다.
- 🛡️ 특별한 작업을 위한 사용자 정의 경로가 추가되었습니다.
- ✅ 경로는 Postman을 통해 테스트되었습니다.
우주가 요청을 위해 열렸습니다! 다음으로 "우주 쓰레기"로부터 보호하는 — 데이터 유효성 검사를 추가할 것입니다.
📌 확인:
php artisan route:list
를 실행합니다.- planets에 대한 5개 이상의 경로가 보이는지 확인합니다.
- 브라우저/Postman에서 GET /api/planets의 작동을 확인합니다.
⚠️ 404 오류가 발생하면:
routes/api.php
에Route::apiResource
가 있는지 확인합니다.- 서버가 실행 중인지 (
php artisan serve
) 확인합니다.- Windows의 경우: 방화벽에서 포트 8000을 허용합니다.