Eloquent: API 리소스

번역일: 2026년 6월 25일

Eloquent: API 리소스

소개

API를 개발할 때, Eloquent 모델과 실제로 클라이언트에 반환되는 JSON 응답 사이에 변환 레이어가 필요한 경우가 많습니다. 예를 들어, 특정 속성은 관리자에게만 노출하거나, 모델의 JSON 표현에 항상 특정 관계를 포함시키고 싶을 수 있습니다. Eloquent의 리소스 클래스를 사용하면 모델과 모델 컬렉션을 JSON으로 변환하는 작업을 명확하고 간결하게 처리할 수 있습니다.

물론, 모델이나 컬렉션에서 toJson 메서드를 호출해 직접 JSON으로 변환할 수도 있습니다. 그러나 API 리소스를 사용하면 모델과 관계의 JSON 직렬화를 훨씬 세밀하고 강력하게 제어할 수 있습니다.

리소스 생성

리소스 클래스는 make:resource Artisan 명령으로 생성합니다. 생성된 리소스 파일은 기본적으로 app/Http/Resources 디렉터리에 위치하며, Illuminate\Http\Resources\Json\JsonResource 클래스를 상속합니다.

php artisan make:resource UserResource

리소스 컬렉션

단일 모델을 변환하는 리소스 외에도, 모델 컬렉션 전체를 변환하는 리소스 컬렉션을 생성할 수 있습니다. 리소스 컬렉션을 사용하면 JSON 응답에 컬렉션 전체와 관련된 링크나 기타 메타 정보를 포함시킬 수 있습니다.

리소스 컬렉션을 생성하려면 --collection 플래그를 사용하거나, 클래스 이름에 Collection을 포함시키면 됩니다. 컬렉션 리소스는 Illuminate\Http\Resources\Json\ResourceCollection 클래스를 상속합니다.

php artisan make:resource User --collectionphp artisan make:resource UserCollection

개념 개요

NOTE

이 섹션은 리소스와 리소스 컬렉션에 대한 전반적인 개요입니다. 리소스가 제공하는 커스터마이징 기능을 깊이 이해하려면 이후 섹션도 꼭 읽어보세요.

본격적으로 살펴보기 전에, Laravel에서 리소스가 어떻게 사용되는지 큰 그림을 먼저 확인해 봅시다. 리소스 클래스는 단일 모델을 JSON 구조로 변환하는 역할을 합니다. 아래는 간단한 UserResource 예시입니다.

<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { /** * 리소스를 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; } }

모든 리소스 클래스는 toArray 메서드를 정의하며, 이 메서드가 반환하는 배열이 라우트나 컨트롤러에서 응답으로 내보낼 때 JSON으로 변환됩니다.

리소스 클래스 내에서 $this를 통해 모델의 속성에 직접 접근할 수 있습니다. 리소스 클래스가 내부적으로 모델의 속성과 메서드 접근을 자동으로 위임(proxy)하기 때문입니다. 리소스를 정의한 뒤에는 라우트나 컨트롤러에서 다음과 같이 반환할 수 있습니다.

use App\Http\Resources\UserResource; use App\Models\User; Route::get('/user/{id}', function (string $id) { return new UserResource(User::findOrFail($id)); });

편의상, 모델의 toResource 메서드를 사용할 수도 있습니다. Laravel이 프레임워크 규칙에 따라 해당 모델에 맞는 리소스 클래스를 자동으로 찾아줍니다.

return User::findOrFail($id)->toResource();

toResource 메서드를 호출하면, Laravel은 모델 이름에 맞는 리소스 클래스를 모델의 네임스페이스에서 가장 가까운 Http\Resources 네임스페이스 안에서 찾습니다. 클래스 이름이 Resource로 끝나는 경우도 자동으로 인식합니다.

리소스 클래스가 이 네이밍 규칙을 따르지 않거나 다른 네임스페이스에 위치한 경우, 모델에 UseResource 속성(attribute)을 사용하여 기본 리소스를 직접 지정할 수 있습니다.

<?php namespace App\Models; use App\Http\Resources\CustomUserResource; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Attributes\UseResource; #[UseResource(CustomUserResource::class)] class User extends Model { // ... }

또는 toResource 메서드에 리소스 클래스를 직접 전달할 수도 있습니다.

return User::findOrFail($id)->toResource(CustomUserResource::class);

리소스 컬렉션

모델 컬렉션이나 페이지네이션 응답을 반환할 때는 라우트나 컨트롤러에서 리소스 클래스의 collection 메서드를 사용합니다.

use App\Http\Resources\UserResource; use App\Models\User; Route::get('/users', function () { return UserResource::collection(User::all()); });

편의상, Eloquent 컬렉션의 toResourceCollection 메서드를 사용할 수도 있습니다. Laravel이 규칙에 따라 적절한 리소스 컬렉션 클래스를 자동으로 찾아줍니다.

return User::all()->toResourceCollection();

toResourceCollection 메서드를 호출하면, Laravel은 모델 이름 뒤에 Collection이 붙은 리소스 컬렉션 클래스를 모델 네임스페이스에서 가장 가까운 Http\Resources 네임스페이스 안에서 탐색합니다.

리소스 컬렉션 클래스가 이 규칙을 따르지 않거나 다른 네임스페이스에 있다면, UseResourceCollection 속성으로 기본 컬렉션을 지정할 수 있습니다.

<?php namespace App\Models; use App\Http\Resources\CustomUserCollection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Attributes\UseResourceCollection; #[UseResourceCollection(CustomUserCollection::class)] class User extends Model { // ... }

또는 toResourceCollection 메서드에 클래스를 직접 전달할 수도 있습니다.

return User::all()->toResourceCollection(CustomUserCollection::class);

커스텀 리소스 컬렉션

기본적으로 리소스 컬렉션은 응답에 커스텀 메타 데이터를 추가할 수 없습니다. 컬렉션 응답을 직접 커스터마이징하려면 전용 리소스 컬렉션 클래스를 생성하세요.

php artisan make:resource UserCollection

생성된 리소스 컬렉션 클래스에서 응답에 포함할 메타 데이터를 자유롭게 정의할 수 있습니다.

<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class UserCollection extends ResourceCollection { /** * 리소스 컬렉션을 배열로 변환합니다. * * @return array<int|string, mixed> */ public function toArray(Request $request): array { return [ 'data' => $this->collection, 'links' => [ 'self' => 'link-value', ], ]; } }

정의한 리소스 컬렉션은 라우트나 컨트롤러에서 다음과 같이 반환합니다.

use App\Http\Resources\UserCollection; use App\Models\User; Route::get('/users', function () { return new UserCollection(User::all()); });

또는 toResourceCollection 메서드를 사용할 수도 있습니다. Laravel이 규칙에 따라 적절한 컬렉션 클래스를 자동으로 찾아줍니다.

return User::all()->toResourceCollection();

컬렉션 키 보존

라우트에서 리소스 컬렉션을 반환하면 Laravel은 기본적으로 컬렉션의 키를 숫자 순서로 재설정합니다. 원래 키를 유지하려면 리소스 클래스에 preserveKeys 프로퍼티를 추가하세요.

<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { /** * 컬렉션의 원래 키를 유지할지 여부를 나타냅니다. * * @var bool */ public $preserveKeys = true; }

preserveKeystrue로 설정되면, 컬렉션의 원래 키가 그대로 유지됩니다.

use App\Http\Resources\UserResource; use App\Models\User; Route::get('/users', function () { return UserResource::collection(User::all()->keyBy->id); });

기반 리소스 클래스 커스터마이징

리소스 컬렉션의 $this->collection 프로퍼티는 기본적으로 컬렉션의 각 항목을 단수형 리소스 클래스에 매핑한 결과로 자동 채워집니다. 단수형 리소스 클래스는 컬렉션 클래스 이름에서 Collection 접미사를 제거한 이름으로 추론됩니다. Resource 접미사의 유무는 상관없습니다.

예를 들어 UserCollection은 각 사용자 인스턴스를 UserResource로 매핑합니다. 이 동작을 변경하려면 $collects 프로퍼티를 오버라이드하세요.

<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\ResourceCollection; class UserCollection extends ResourceCollection { /** * 이 컬렉션이 사용할 리소스 클래스입니다. * * @var string */ public $collects = Member::class; }

리소스 작성

NOTE

개념 개요를 아직 읽지 않으셨다면, 먼저 읽고 오시기를 권장합니다.

리소스 클래스의 핵심은 모델을 배열로 변환하는 toArray 메서드입니다. 이 메서드에서 반환하는 배열이 라우트나 컨트롤러의 API 응답으로 사용됩니다.

<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { /** * 리소스를 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; } }

리소스를 정의한 뒤에는 라우트나 컨트롤러에서 바로 반환할 수 있습니다.

use App\Models\User; Route::get('/user/{id}', function (string $id) { return User::findOrFail($id)->toResource(); });

관계 포함

응답에 연관된 리소스를 포함하려면 toArray 메서드가 반환하는 배열에 추가하면 됩니다. 아래 예시에서는 PostResourcecollection 메서드를 사용해 사용자의 블로그 포스트를 응답에 포함합니다.

use App\Http\Resources\PostResource; use Illuminate\Http\Request; /** * 리소스를 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'posts' => PostResource::collection($this->posts), 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; }

NOTE

이미 로드된 관계만 조건부로 포함하고 싶다면, 조건부 관계 섹션을 참고하세요.

리소스 컬렉션 작성

단일 리소스가 모델 하나를 배열로 변환한다면, 리소스 컬렉션은 모델 컬렉션 전체를 배열로 변환합니다. 각 모델마다 별도의 리소스 컬렉션 클래스를 정의할 필요는 없습니다. 모든 Eloquent 모델 컬렉션은 toResourceCollection 메서드를 제공하여 즉석에서 리소스 컬렉션을 생성할 수 있습니다.

use App\Models\User; Route::get('/users', function () { return User::all()->toResourceCollection(); });

다만, 컬렉션과 함께 반환할 메타 데이터를 커스터마이징해야 한다면 전용 리소스 컬렉션 클래스를 직접 정의하세요.

<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class UserCollection extends ResourceCollection { /** * 리소스 컬렉션을 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'data' => $this->collection, 'links' => [ 'self' => 'link-value', ], ]; } }

단일 리소스와 마찬가지로, 리소스 컬렉션도 라우트나 컨트롤러에서 바로 반환할 수 있습니다.

use App\Http\Resources\UserCollection; use App\Models\User; Route::get('/users', function () { return new UserCollection(User::all()); });

또는 toResourceCollection 메서드로 편리하게 반환할 수도 있습니다. Laravel이 규칙에 따라 적절한 컬렉션 클래스를 자동으로 탐색합니다.

return User::all()->toResourceCollection();

데이터 래핑

기본적으로 리소스 응답이 JSON으로 변환될 때, 최상위 리소스 데이터는 data 키로 래핑됩니다. 예를 들어 일반적인 리소스 컬렉션 응답은 다음과 같습니다.

{ "data": [ { "id": 1, "name": "홍길동", "email": "hong@example.com" }, { "id": 2, "name": "김철수", "email": "kim@example.com" } ] }

최상위 래핑을 비활성화하려면 Illuminate\Http\Resources\Json\JsonResourcewithoutWrapping 메서드를 호출하세요. 일반적으로 모든 요청에서 실행되는 AppServiceProvider 또는 다른 서비스 프로바이더boot 메서드에서 호출합니다.

<?php namespace App\Providers; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { JsonResource::withoutWrapping(); } }

WARNING

withoutWrapping 메서드는 최상위 응답에만 영향을 미칩니다. 직접 리소스 컬렉션에 수동으로 추가한 data 키는 제거되지 않습니다.

중첩 리소스 래핑

리소스의 관계를 어떻게 래핑할지는 자유롭게 결정할 수 있습니다. 중첩 여부에 관계없이 모든 리소스 컬렉션을 data 키로 래핑하려면, 각 리소스마다 전용 컬렉션 클래스를 정의하고 data 키 안에 컬렉션을 반환하면 됩니다.

이렇게 하면 최상위 리소스가 data 키로 이중으로 래핑되지 않을까 걱정이 될 수 있지만, Laravel은 리소스가 실수로 이중 래핑되는 상황을 방지하므로 걱정하지 않아도 됩니다.

<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\ResourceCollection; class CommentsCollection extends ResourceCollection { /** * 리소스 컬렉션을 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return ['data' => $this->collection]; } }

데이터 래핑과 페이지네이션

리소스 응답으로 페이지네이션된 컬렉션을 반환하면, withoutWrapping을 호출했더라도 항상 data 키로 래핑됩니다. 페이지네이션 응답에는 페이지네이터 상태를 담는 metalinks 키가 반드시 포함되기 때문입니다.

{ "data": [ { "id": 1, "name": "홍길동", "email": "hong@example.com" }, { "id": 2, "name": "김철수", "email": "kim@example.com" } ], "links": { "first": "http://example.com/users?page=1", "last": "http://example.com/users?page=1", "prev": null, "next": null }, "meta": { "current_page": 1, "from": 1, "last_page": 1, "path": "http://example.com/users", "per_page": 15, "to": 10, "total": 10 } }

페이지네이션

Laravel 페이지네이터 인스턴스를 리소스 클래스의 collection 메서드나 커스텀 리소스 컬렉션에 전달할 수 있습니다.

use App\Http\Resources\UserCollection; use App\Models\User; Route::get('/users', function () { return new UserCollection(User::paginate()); });

편의상, 페이지네이터의 toResourceCollection 메서드를 사용할 수도 있습니다. Laravel이 규칙에 따라 적절한 리소스 컬렉션 클래스를 자동으로 탐색합니다.

return User::paginate()->toResourceCollection();

페이지네이션 응답에는 항상 페이지네이터 상태를 담은 metalinks 키가 포함됩니다.

{ "data": [ { "id": 1, "name": "홍길동", "email": "hong@example.com" }, { "id": 2, "name": "김철수", "email": "kim@example.com" } ], "links": { "first": "http://example.com/users?page=1", "last": "http://example.com/users?page=1", "prev": null, "next": null }, "meta": { "current_page": 1, "from": 1, "last_page": 1, "path": "http://example.com/users", "per_page": 15, "to": 10, "total": 10 } }

페이지네이션 정보 커스터마이징

페이지네이션 응답의 links 또는 meta 키에 포함되는 정보를 커스터마이징하려면 리소스에 paginationInformation 메서드를 정의하세요. 이 메서드는 $paginated 데이터와 linksmeta 키를 담은 $default 배열을 인자로 받습니다.

/** * 리소스의 페이지네이션 정보를 커스터마이징합니다. * * @param \Illuminate\Http\Request $request * @param array $paginated * @param array $default * @return array */ public function paginationInformation($request, $paginated, $default) { $default['links']['custom'] = 'https://example.com'; return $default; }

조건부 속성

특정 조건이 충족될 때만 속성을 응답에 포함하고 싶을 때가 있습니다. 예를 들어, 현재 사용자가 관리자인 경우에만 특정 값을 노출하는 경우입니다. Laravel은 이런 상황을 위한 다양한 헬퍼 메서드를 제공합니다. when 메서드를 사용하면 조건부로 속성을 추가할 수 있습니다.

/** * 리소스를 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'secret' => $this->when($request->user()->isAdmin(), 'secret-value'), 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; }

이 예시에서 secret 키는 인증된 사용자의 isAdmin 메서드가 true를 반환할 때만 최종 응답에 포함됩니다. false를 반환하면 클라이언트에 응답이 전송되기 전에 secret 키가 제거됩니다. when 메서드를 사용하면 배열 빌드 시 조건문 없이도 명확하게 리소스를 정의할 수 있습니다.

when 메서드의 두 번째 인자로 클로저를 전달하면, 조건이 true일 때만 값을 계산합니다.

'secret' => $this->when($request->user()->isAdmin(), function () { return 'secret-value'; }),

whenHas 메서드를 사용하면, 모델에 해당 속성이 실제로 존재할 때만 응답에 포함할 수 있습니다.

'name' => $this->whenHas('name'),

whenNotNull 메서드를 사용하면, 속성값이 null이 아닐 때만 응답에 포함할 수 있습니다.

'name' => $this->whenNotNull($this->name),

조건부 속성 병합

같은 조건에 따라 여러 속성을 한 번에 포함하거나 제외해야 할 때는 mergeWhen 메서드를 사용합니다. 조건이 true일 때만 지정한 속성들이 응답에 추가됩니다.

/** * 리소스를 배열로 변환합니다. * * @return array<string, mixed> */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 25일