본문으로 바로가기

개요

라라벨은 기본적으로 서버사이드 렌더링을 통해 페이지를 응답하기 위한 Web Route 와 비동기를 통한 Response Body 를 응답하기 위한 API Route 를 제공한다. Route 의 식별은 URI 의 prefix 를 통해 /api 로 시작하는 경우 API Route 에 정의된 경로를  맵핑하도록 되어있다.
이 장에서는 해당 라우팅 정책을 URI 의 prefix 가 아닌 도메인(정확히는 서브 도메인)을 통해 식별되도록 변경하는 방법을 소개하고자 한다.
※ 해당 문서는 PHP에 대한 기본지식 및 Laravel Framework 전반의 구조와 라이프사이클을 이해하고 있다는 전제로 작성되었습니다.
  • Laravel v8.72.0 (PHP v7.4.25)

주요 파일 경로

# 라우팅의 맵핑이 정의된 파일
{ProjectRoot}/routes
ㄴ api.php
ㄴ web.php

# web route 와 api route 식별정책을 정의한 Service Provider
{ProjectRoot}/app/Providers
ㄴ RouteServiceProvider.php

 

수정 및 실행

  1. API Route 에 맵핑을 정의
  2. RouteServiceProvider 의 prefix 를 통해 API Route를 식별하도록 정의된 구문을 도메인 기준으로 식별하도록 변경.
# routes/web.php
<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

# http(s)://your-domain/box1
Route::get('/box1', function () {
    return 'this is web route box1.';
});


# routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
# AS-IS : http(s)://your-domain/api/box1
# TO-BE : http(s)://api.your-domain/box1
Route::get('/box1', function () {
    return 'this is api route box1.';
});
<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to the "home" route for your application.
     *
     * This is used by Laravel authentication to redirect users after login.
     *
     * @var string
     */
    public const HOME = '/home';

    /**
     * The controller namespace for the application.
     *
     * When present, controller route declarations will automatically be prefixed with this namespace.
     *
     * @var string|null
     */
    // protected $namespace = 'App\\Http\\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            # prefix를 통한 api route 식별
            /*Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));*/

            # 도메인 기반의 api route 식별 방식으로 변경
            Route::domain('api.tailerbox.com') 
            // 도메인은 .env 의 환경변수 등록을 통해 아래와 같은 형태로 사용하길 권장함.
            // Route::domain(env('API_DOMAIN')) 
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });
    }
}

RouteServiceProvider 의 수정 전과 후는 아래와 같이 다르게 접근되어야 한다.

  • AS-IS : http://tailerbox.com/api/box1
  • TO-BE : http://api.tailerbox.com/box1

여기서 잠깐

RouteServiceProvider 의 API와 Web Route는 $this->routes() 라는 부모 메소드를 통해 바인딩 되며 콜백함수가 단일 인자로 전달되어 선언된 것을 확인할 수 있다.

따라서, /routes/api.php 파일에서 box1 이라는 맵핑이 존재하지 않는 경우 /routes/web.php 에서 해당 맵핑을 찾도록 동작한다는 것을 알 수 있다.

prefix 를 통해 /api 로 들어오는 이하의 URI를 모두 API로 간주하는 경우 문제될게 없겠지만 domain 기반으로 변경한 이후에는 동일한 이름의 라우트 맵핑이 얼마든지 존재할 수 있기 때문에 이에 대한 조치가 필요하다.

 

<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;
# 동일한 이름의 Request 객체가 이미 있으므로 Class 의 Alias 를 지정
use Illuminate\Support\Facades\Request as FacadeRequest;
use Illuminate\Support\Facades\Route;
//use Illuminate\Support\Facades\URL;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to the "home" route for your application.
     *
     * This is used by Laravel authentication to redirect users after login.
     *
     * @var string
     */
    public const HOME = '/home';

    /**
     * The controller namespace for the application.
     *
     * When present, controller route declarations will automatically be prefixed with this namespace.
     *
     * @var string|null
     */
    // protected $namespace = 'App\\Http\\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            # prefix를 통한 api route 식별
            /*Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));*/

            if (FacadeRequest::server('HTTP_HOST') === env('API_DOMAIN')) {
                # 도메인 기반의 api route 식별 방식으로 변경
                Route::domain(env('API_DOMAIN'))
                    ->middleware('api')
                    ->namespace($this->namespace)
                    ->group(base_path('routes/api.php'));
            } elseif (FacadeRequest::server('HTTP_HOST') === env('WEB_DOMAIN')) {
                Route::middleware('web')
                    ->namespace($this->namespace)
                    ->group(base_path('routes/web.php'));
            }
        });
    }

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });
    }
}

 

 

'Framework > Laravel' 카테고리의 다른 글

[Laravel] i18n 국제화 적용하기  (0) 2021.11.19
[Laravel] Error 핸들링 (CustomException)  (0) 2021.11.19
[Laravel] 설치하기  (0) 2021.11.18