[Laravel 기본] 8. Routing

이번 포스팅 부터는 라라벨 핵심 컨셉인 아닌 실질적인 사용법에 대해 설명하고자 [Laravel 기본] 이라는 타이틀로 수정하였습니다. [Laravel 기본] 은 대부분 사용법에 대한 포스팅이 될 것입니다. 많은 기능, 메소드를 모두 다루지 않는 방향으로 작성하려고 합니다. 기본 사용법을 익히고 커스텀이 필요한 부분이나 특수한 상황은 라라벨 공식 사이트 또는 라라벨 코리아 에서 익히는 것을 권장합니다.

Routing


Laravel 어플리케이션에서 Routing 의 시점을 생각하자!!

웹 어플리케이션 내에서 Routing은 요청 URL 를 분석해 알맞은 처리 경로를 안내해 주는 역할을 합니다. 라라벨 어플리케이션에서는 어떻게 동작할까요?? Http Kernel 으로 들어온 요청을 분석하여 미들웨어를 통과시킨 후 route 에 도착하여 해당 작업을 하게 됩니다.

Route Directory

2번째 포스팅(라라벨 설치와 디렉토리 구조)에서 잠깐 스쳐 지나갔던 route 디렉토리입니다.

/routes 디렉토리
  • web.php => 라라벨 어플리케이션의 기본적인 routing 을 담당하는 곳입니다. web route 미들웨어 통과시킵니다. 이 곳의 라우터들은 Session state, CRSF Protection 등을 받습니다.
  • api.php => stateless 한 api 요청들을 처리하는 곳입니다. api route 미들웨어를 통과시킵니다.
  • console.php => console 관련 요청 처리
  • channels.php => BroadCasting 관련 요청 처리

주로 사용하는 web.php과 api.php를 중심으로 보면 되겠습니다. web.php 의 라우터는 상태를 가지고 있는 형태의 요청을 처리하게 되며 api.php는 상태가 없는 형태의 요청을 처리합니다.

여기서 궁금한 것이 하나 생겼습니다. 요청이 route 디렉토리까지 어떻게 전달되는 것일까요? 라라벨 어플리케이션의 모든 핵심 서비스들은 서비스 프로바이더와 연관되어 있습니다. app/Providers/ 디렉토리를 열어 보면 RouteServiceProvider 에 routing 과 관련된 설정을 찾아 볼 수 있었습니다.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    protected $namespace = 'App\Http\Controllers';

    public function boot()
    {
        //
        parent::boot();
    }

   // web.php config
   // web 미들웨어를 통과시키며 web.php의 라우터들을 그룹화 한다.
    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

   // api.php config
   // api 미들웨어를 통과시키며 api.php의 라우터들을 그룹화 한다.
   // default 로 /api 가 prefix로 설정되어있다.
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}

Using Router

Router 를 사용하는 방법에 대해 다뤄보겠습니다. 기본적으로 라라벨 Facade 를 이용해 호출하는데 첫 번째 파라미터로는 URI 를, 다음 파라미터는 Closure 를 사용합니다. 다음은 라우터를 사용하는 기본적인 예제입니다.

// www.example.com/foo GET Method Call
Route::get('foo', function () {
    return 'Hello World';
});

// www.example.com/foo GET Method Call
// RouterServieProvider 에 Controller 디렉토리가 namespace 로 등록되어 있음을 확인할 수 있다.
Route::get('foo', 'FooController@index');

GET 메소드 외 다양한 HTTP 메소드들을 제공합니다.

Route::get($uri, $callback); // GET
Route::post($uri, $callback); // POST
Route::put($uri, $callback); // PUT
Route::patch($uri, $callback); // PATCH
Route::delete($uri, $callback); // DELETE
Route::options($uri, $callback); // OPTIONS -> 사용가능한 HTTP METHOD 를 return 한다.

다른 URI 로 접근할 수 있는 Redirect 기능과 바로 View로 보내는 view() 메소드도 지원합니다.

Route::redirect('/here', '/there');

Route::view('/home', 'home');

Using Route Parameter

/users/1 , /users/2 등 변하는 세그먼트를 지닌 URI 들이 존재합니다. 이런 경우 라라벨에서는 다음과 같이 변하는 세그먼트를 표현합니다. {} 중괄호 안에 이름을 명시하고 Closure 에서 사용하면 됩니다. 단, – 문자를 사용하면 안된다고 합니다. _ (언더바)를 권장한다고 합니다.

// /user/1 , /user/2 ... /user/{id}
Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

// 다수의 경우에도 사용 가능하다.
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {});

// {} 안에 ? 를 사용하는 경우 디폴트 값을 설정할 수 있다.
Route::get('user/{name?}', function ($name = 'Jennie') {
    return $name;
});

// 다음과 같이 정규식으로 {id} 값을 제한할 수 있다.
Route::get('user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route model Binding

라라벨 Route에는 Model id 값 기준으로 모델을 자동적으로 주입해주는 기능이 있습니다. 자동 바인딩과 마찬가지로 Type Hinting 을 통해 자동 바인딩을 제공합니다. 어떻게 사용하는지 예제로 간단하게 알아보겠습니다.

// {user}라는 파라미터 값에 Model(User) id를 입력해주면 Closure 자동 바인딩된 Model(User) 를 사용할 수 있다
Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

모든 Model 객체가 타입 힌팅으로 제공되는 것이 아닙니다. 라라벨에서 제공하는 Eloquent Model 을 상속하여 구현하고 있어야 자동 바인딩을 사용할 수 있습니다. Model 객체 소스 코드를 열어 보겠습니다.

<?php

namespace Illuminate\Database\Eloquent;

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
    // ... //

    // Eloqent abstract Model 를 구현하는 경우
    // this->getRouteKeyName() => default : id 
    public function resolveRouteBinding($value)
    {
        return $this->where($this->getRouteKeyName(), $value)->first();
    }
    // ... //
}

resolveRouteBinding() 이라는 메소드를 통하여 타입 힌팅으로 제공된 Model 를 id 값으로 DB에 접근해 모델을 가져옵니다. 만약 id 값 이외의 key 값으로 모델을 가져오고 싶은 경우 이 resolveRouteBinding 메소드를 오버라이딩하여 사용하면 됩니다. 필자의 경우에는 업무 처리에서 특정 모델 바인딩 실패 시 에러 처리를 수정해야하는 경우(기본적으로 바인딩 실패시 Error code 404 )가 있어서 이 메소드를 수정한 기억이 있습니다.

또한, 타입 힌팅을 이용한 바인딩 외 명시적으로 모델을 주입하고 싶은 경우에는 다음과 같이 RouteServiceProvider 에서 boot 시 명시적으로 모델을 지정해 주면 됩니다.

// app/Providers/RouteServiceProvider.php

public function boot()
{
    parent::boot();
    
    // 명시적 모델 바인딩
    // 라우트야 user 라는 이름은 해당 모델을 사용할꺼야
    Route::model('user', App\User::class);
}

ETC

지금까지 라우터의 기본적인 사용에 대해 알아보았습니다. 기본적인 사용 방법으로 충분히 구현가능하지만 더 효율적으로 다루기 위해 router 에는 다음과 같은 다양한 기능이 추가로 존재합니다. 해당 기능들은 모두 라라벨 공식 사이트에서 자세히 찾아보실 수 있습니다.

  • Named Routing => 특정 라우트에 name (label, tag)를 부여하여 전역 함수 route()를 사용하여 호출할 수 있습니다.
  • Route Grouping => 라우트를 특정 조건(미들웨어, 네임스페이스, 서브 도매인, Prefix) 등으로 그룹화(chunking)합니다.
  • Route Fallback => 요청이 정의한 라우트들과 매칭되지 않는 경우 실행되는 메소드. 모든 메소드를 검색 후 해당 메소드가 실행된다.
  • Rate Limiting => 라우트 요청을 제한하는 throttle 미들웨어를 제공하여 Limit 를 간단하게 구현할 수 있다.

TL;DR

  • 라라벨에서 다양한 Routing 기능을 간편하게 제공한다. /routes 디렉토리 아래 라우트를 정의하면 된다.
  • 라라벨 Route 는 기본적으로 Route::get() 형태로 Facade를 이용하여 간단하게 사용한다.
  • 라라벨 Route는 Eloquent Model 구현 시 자동 바인딩으로 의존성 주입을 해결한다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다