RESTful 리소스 처리

Representational State Transfer(REST)은 분산 애플리케이션을 위한 아키텍처 스타일로, Roy Fielding이 2000년 박사학위 논문 Architectural Styles and the Design of Network-based Software Architectures에서 처음 설명했습니다. 다소 딱딱한 내용일 수 있으니 Martin Fowler의 Richardson Maturity Model을 함께 읽어보면 더 친절한 소개가 될 것입니다.

REST는 다양한 방식으로 해석되거나 오해되어 왔습니다. 아키텍처에서 Roy Fielding의 원칙을 더 많이 수용할수록 애플리케이션은 더 “RESTful”하다고 볼 수 있습니다.

CodeIgniter는 리소스 라우트와 ResourceController를 통해 리소스용 RESTful API를 쉽게 만들 수 있도록 도와줍니다.

리소스 라우트

단일 리소스에 대해 resource() 메서드로 빠르게 여러 RESTful 라우트를 생성할 수 있습니다. 이는 리소스의 전체 CRUD에 필요한 다섯 가지 기본 라우트(생성, 수정, 목록 조회, 단일 항목 보기, 삭제)를 만듭니다. 첫 번째 매개변수는 리소스 이름입니다:

<?php

$routes->resource('photos');

// Equivalent to the following:
$routes->get('photos/new', 'Photos::new');
$routes->post('photos', 'Photos::create');
$routes->get('photos', 'Photos::index');
$routes->get('photos/(:segment)', 'Photos::show/$1');
$routes->get('photos/(:segment)/edit', 'Photos::edit/$1');
$routes->put('photos/(:segment)', 'Photos::update/$1');
$routes->patch('photos/(:segment)', 'Photos::update/$1');
$routes->delete('photos/(:segment)', 'Photos::delete/$1');

참고

위의 순서는 가독성을 위한 것이며, 실제로 RouteCollection에서 라우트가 생성되는 순서는 올바른 라우트 해석을 보장합니다.

중요

라우트는 지정된 순서대로 매칭되므로, 예를 들어 resource photos가 get ‘photos/poll’보다 위에 있으면 resource 라인의 show 액션 라우트가 get 라인보다 먼저 매칭됩니다. 이를 해결하려면 get 라인을 resource 라인 위로 이동시켜 먼저 매칭되도록 하세요.

두 번째 매개변수는 생성되는 라우트를 수정하는 옵션의 배열을 받습니다. 이 라우트들은 주로 API 용도로 사용되지만, websafe 옵션을 지정하면 HTML 폼과 함께 작동하는 update 및 delete 메서드를 생성할 수 있습니다:

<?php

$routes->resource('photos', ['websafe' => 1]);

// The following equivalent routes are created:
$routes->post('photos/(:segment)/delete', 'Photos::delete/$1');
$routes->post('photos/(:segment)', 'Photos::update/$1');

사용할 컨트롤러 변경

사용할 컨트롤러의 이름을 controller 옵션으로 지정할 수 있습니다:

<?php

$routes->resource('photos', ['controller' => 'Gallery']);
// Would create routes like:
$routes->get('photos', '\App\Controllers\Gallery::index');
<?php

$routes->resource('photos', ['controller' => '\App\Gallery']);
// Would create routes like:
$routes->get('photos', '\App\Gallery::index');
<?php

use App\Controllers\Gallery;

$routes->resource('photos', ['namespace' => '', 'controller' => Gallery::class]);
// Would create routes like:
$routes->get('photos', '\App\Controllers\Gallery::index');

또한 컨트롤러 네임스페이스을 참조하세요.

사용할 플레이스홀더 변경

기본적으로 리소스 ID가 필요할 때 (:segment) 플레이스홀더가 사용됩니다. placeholder 옵션에 새 문자열을 지정하여 변경할 수 있습니다:

<?php

$routes->resource('photos', ['placeholder' => '(:num)']);

// Generates routes like:
$routes->get('photos/(:num)', 'Photos::show/$1');

생성되는 라우트 제한

only 옵션으로 생성되는 라우트를 제한할 수 있습니다. 이 옵션은 생성할 메서드 이름의 배열 또는 쉼표로 구분된 목록이어야 하며, 해당 메서드와 일치하는 라우트만 생성됩니다. 나머지는 무시됩니다:

<?php

$routes->resource('photos', ['only' => ['index', 'show']]);

또는 except 옵션으로 사용하지 않는 라우트를 제거할 수 있습니다. 이 옵션 역시 메서드 이름의 배열 또는 쉼표로 구분된 목록이어야 하며, 이 옵션은 only 이후에 적용됩니다:

<?php

$routes->resource('photos', ['except' => 'new,edit']);

유효한 메서드는 index, show, create, update, new, edit 및 delete 입니다.

ResourceController

ResourceController는 위의 리소스 라우트에 대응하는 메서드를 제공하여 RESTful API를 시작하기 위한 편리한 기반을 제공합니다.

이를 확장하고 modelName 및 format 속성을 재정의한 다음, 처리할 메서드들을 구현하세요:

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;

class Photos extends ResourceController
{
    protected $modelName = 'App\Models\Photos';
    protected $format    = 'json';

    public function index()
    {
        return $this->respond($this->model->findAll());
    }

    // ...
}

해당 라우팅은 다음과 같습니다:

<?php

$routes->resource('photos');

프레젠터 라우트

presenter() 메서드를 사용하여 리소스 컨트롤러와 일치하는 프레젠테이션 컨트롤러를 빠르게 생성할 수 있습니다. 이는 리소스의 뷰를 반환하거나 해당 뷰에서 제출된 폼을 처리하는 컨트롤러 메서드에 대한 라우트를 생성합니다.

프레젠테이션은 일반 컨트롤러로 처리할 수 있으므로 필수는 아니며 편의 기능입니다. 사용법은 리소스 라우팅과 유사합니다:

<?php

$routes->presenter('photos');

// Equivalent to the following:
$routes->get('photos/new', 'Photos::new');
$routes->post('photos/create', 'Photos::create');
$routes->post('photos', 'Photos::create');   // alias
$routes->get('photos', 'Photos::index');
$routes->get('photos/show/(:segment)', 'Photos::show/$1');
$routes->get('photos/(:segment)', 'Photos::show/$1');  // alias
$routes->get('photos/edit/(:segment)', 'Photos::edit/$1');
$routes->post('photos/update/(:segment)', 'Photos::update/$1');
$routes->get('photos/remove/(:segment)', 'Photos::remove/$1');
$routes->post('photos/delete/(:segment)', 'Photos::delete/$1');

참고

위의 순서는 가독성을 위한 것이며, 실제로 RouteCollection에서 라우트가 생성되는 순서는 올바른 라우트 해석을 보장합니다.

동일한 photos에 대해 리소스와 프레젠터 컨트롤러 둘 다 라우트를 가지지 않도록 구분해야 합니다. 예를 들어 다음과 같이 구별할 수 있습니다:

<?php

$routes->resource('api/photo');
$routes->presenter('admin/photos');

두 번째 매개변수는 생성되는 라우트를 수정하는 옵션 배열을 받습니다.

사용할 컨트롤러 변경

사용할 컨트롤러의 이름을 controller 옵션으로 지정할 수 있습니다:

<?php

$routes->presenter('photos', ['controller' => 'Gallery']);
// Would create routes like:
$routes->get('photos', '\App\Controllers\Gallery::index');
<?php

$routes->presenter('photos', ['controller' => '\App\Gallery']);
// Would create routes like:
$routes->get('photos', '\App\Gallery::index');
<?php

use App\Controllers\Gallery;

$routes->presenter('photos', ['namespace' => '', 'controller' => Gallery::class]);
// Would create routes like:
$routes->get('photos', '\App\Controllers\Gallery::index');

또한 컨트롤러 네임스페이스을 참조하세요.

사용할 플레이스홀더 변경

기본적으로 리소스 ID가 필요할 때 (:segment) 플레이스홀더가 사용됩니다. placeholder 옵션에 새 문자열을 지정하여 변경할 수 있습니다:

<?php

$routes->presenter('photos', ['placeholder' => '(:num)']);

// Generates routes like:
$routes->get('photos/(:num)', 'Photos::show/$1');

생성되는 라우트 제한

only 옵션으로 생성되는 라우트를 제한할 수 있습니다. 이 옵션은 생성할 메서드 이름의 배열 또는 쉼표로 구분된 목록이어야 하며, 해당 메서드와 일치하는 라우트만 생성됩니다. 나머지는 무시됩니다:

<?php

$routes->presenter('photos', ['only' => ['index', 'show']]);

또는 except 옵션으로 사용하지 않는 라우트를 제거할 수 있습니다. 이 옵션 역시 메서드 이름의 배열 또는 쉼표로 구분된 목록이어야 하며, 이 옵션은 only 이후에 적용됩니다:

<?php

$routes->presenter('photos', ['except' => 'new,edit']);

유효한 메서드는 index, show, new, create, edit, update, remove 및 delete 입니다.

ResourcePresenter

ResourcePresenter는 리소스의 뷰를 표시하고, 해당 뷰의 폼 데이터를 처리하기 위한 편리한 시작점을 제공하며, 위의 리소스 라우트와 일치하는 메서드를 제공합니다.

이를 확장하여 modelName 속성을 재정의하고, 처리하려는 메서드들을 구현하세요:

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourcePresenter;

class Photos extends ResourcePresenter
{
    protected $modelName = 'App\Models\Photos';

    public function index()
    {
        return view('templates/list', $this->model->findAll());
    }

    // ...
}

해당 라우팅은 다음과 같습니다:

<?php

$routes->presenter('photos');

프레젠터/컨트롤러 비교

이 표는 resource()와 presenter()가 생성하는 기본 라우트와 해당 컨트롤러 함수들을 비교하여 보여줍니다.

동작

메서드

컨트롤러 라우트

프레젠터 라우트

컨트롤러 함수

프레젠터 함수

새로 만들기

GET

photos/new

photos/new

new()

new()

생성

POST

photos

photos

create()

create()

생성(별칭)

POST

photos/create

create()

목록

GET

photos

photos

index()

index()

보기

GET

photos/(:segment)

photos/(:segment)

show($id = null)

show($id = null)

보기(별칭)

GET

photos/show/(:segment)

show($id = null)

편집

GET

photos/(:segment)/edit

photos/edit/(:segment)

edit($id = null)

edit($id = null)

업데이트

PUT/PATCH

photos/(:segment)

update($id = null)

업데이트(웹세이프)

POST

photos/(:segment)

photos/update/(:segment)

update($id = null)

update($id = null)

제거

GET

photos/remove/(:segment)

remove($id = null)

삭제

DELETE

photos/(:segment)

delete($id = null)

삭제(웹세이프)

POST

photos/delete/(:segment)

delete($id = null)

delete($id = null)