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, editdelete 입니다.

ResourceController

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

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

<?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, removedelete 입니다.

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)