Eloquent 뮤테이터 & 캐스팅
번역일: 2026년 6월 25일
Eloquent 뮤테이터 & 캐스팅
소개
접근자(accessor), 뮤테이터(mutator), 속성 캐스팅(attribute casting)은 모델 인스턴스에서 Eloquent 속성 값을 읽거나 설정할 때 값을 변환할 수 있게 해주는 기능입니다.
예를 들어, 데이터베이스에 저장할 때는 Laravel 암호화를 사용해 값을 암호화하고, Eloquent 모델에서 읽을 때는 자동으로 복호화할 수 있습니다. 또는 데이터베이스에 JSON 문자열로 저장된 값을 모델을 통해 접근할 때 PHP 배열로 자동 변환할 수도 있습니다.
접근자와 뮤테이터
접근자 정의
접근자는 Eloquent 속성 값을 읽을 때 값을 변환합니다. 접근자를 정의하려면 모델에 protected 메서드를 추가합니다. 메서드 이름은 실제 데이터베이스 컬럼명의 camelCase 표기법과 일치해야 합니다.
아래 예시에서는 first_name 속성에 대한 접근자를 정의합니다. 모델에서 first_name 속성에 접근할 때 Eloquent가 이 메서드를 자동으로 호출합니다. 접근자/뮤테이터 메서드는 반드시 Illuminate\Database\Eloquent\Casts\Attribute를 반환 타입으로 선언해야 합니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 사용자의 이름을 가져옵니다.
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
);
}
}모든 접근자 메서드는 Attribute 인스턴스를 반환하며, 이 인스턴스 안에서 속성을 읽거나 쓰는 방식을 정의합니다. 위 예시에서는 get 인자만 정의해 읽기 동작만 지정했습니다.
컬럼의 원본 값이 접근자로 전달되고, 이 값을 가공해 반환하면 됩니다. 접근자 값에 접근하려면 모델 인스턴스에서 속성명을 그대로 사용하면 됩니다.
use App\Models\User;
$user = User::find(1);
$firstName = $user->first_name;NOTE
계산된 값을 모델의 배열/JSON 표현에 포함하려면 값을 JSON에 추가해야 합니다.
여러 속성으로 값 객체 생성하기
접근자의 get 클로저는 두 번째 인자로 $attributes를 받을 수 있습니다. 이 인자에는 모델의 모든 현재 속성이 배열로 담겨 자동으로 전달됩니다. 이를 활용해 여러 컬럼 값을 하나의 값 객체로 조합할 수 있습니다.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
/**
* 사용자의 주소를 처리합니다.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
);
}접근자 캐싱
접근자에서 값 객체를 반환하는 경우, 해당 값 객체에 가해진 변경 사항은 모델이 저장되기 전에 자동으로 모델에 반영됩니다. Eloquent가 접근자가 반환한 인스턴스를 내부적으로 보관해, 같은 접근자를 여러 번 호출해도 동일한 인스턴스를 반환하기 때문입니다.
use App\Models\User;
$user = User::find(1);
$user->address->lineOne = '변경된 주소 1행';
$user->address->lineTwo = '변경된 주소 2행';
$user->save();문자열이나 불리언처럼 원시 타입을 반환하는 접근자에서 캐싱을 활성화하려면(특히 연산 비용이 높은 경우) shouldCache 메서드를 사용합니다.
protected function hash(): Attribute
{
return Attribute::make(
get: fn (string $value) => bcrypt(gzuncompress($value)),
)->shouldCache();
}반대로, 객체 캐싱을 비활성화하고 싶다면 withoutObjectCaching 메서드를 사용합니다.
/**
* 사용자의 주소를 처리합니다.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
)->withoutObjectCaching();
}뮤테이터 정의
뮤테이터는 Eloquent 속성 값을 설정할 때 값을 변환합니다. 뮤테이터를 정의하려면 Attribute::make() 호출 시 set 인자를 제공하면 됩니다. 아래 예시에서는 first_name 속성에 뮤테이터를 추가합니다. 모델에서 first_name에 값을 설정할 때 자동으로 호출됩니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 사용자의 이름을 처리합니다.
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
set: fn (string $value) => strtolower($value),
);
}
}뮤테이터 클로저는 설정하려는 값을 받아 가공한 뒤 반환합니다. 뮤테이터를 사용하려면 Eloquent 모델에 속성을 평소처럼 할당하기만 하면 됩니다.
use App\Models\User;
$user = User::find(1);
$user->first_name = 'Sally';위 예시에서 set 콜백은 'Sally' 값을 받아 strtolower를 적용한 결과를 모델 내부의 $attributes 배열에 저장합니다.
여러 속성을 동시에 변환하기
뮤테이터의 set 클로저에서 배열을 반환하면 여러 모델 속성을 한 번에 설정할 수 있습니다. 배열의 각 키는 모델의 실제 속성명(데이터베이스 컬럼명)과 일치해야 합니다.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
/**
* 사용자의 주소를 처리합니다.
*/
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
set: fn (Address $value) => [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
],
);
}속성 캐스팅
속성 캐스팅은 접근자나 뮤테이터를 별도로 정의하지 않고도 모델 속성의 타입을 자동으로 변환하는 편리한 방법입니다. 모델의 casts 메서드에서 속성명과 변환할 타입을 배열로 반환하면 됩니다.
지원하는 캐스트 타입은 다음과 같습니다.
arrayAsFluent::classAsStringable::classAsUri::classbooleancollectiondatedatetimeimmutable_dateimmutable_datetimedecimal:<precision>doubleencryptedencrypted:arrayencrypted:collectionencrypted:objectfloathashedintegerobjectrealstringtimestamp
예를 들어, 데이터베이스에 0 또는 1의 정수로 저장된 is_admin 속성을 불리언으로 캐스팅할 수 있습니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_admin' => 'boolean',
];
}
}캐스트를 정의하면 데이터베이스에 정수로 저장되어 있더라도 is_admin에 접근할 때 항상 불리언으로 변환됩니다.
$user = App\Models\User::find(1);
if ($user->is_admin) {
// ...
}런타임에 임시로 캐스트를 추가해야 한다면 mergeCasts 메서드를 사용하세요. 기존에 정의된 캐스트에 추가됩니다.
$user->mergeCasts([
'is_admin' => 'integer',
'options' => 'object',
]);WARNING
값이 null인 속성에는 캐스팅이 적용되지 않습니다. 또한 관계(relationship)와 동일한 이름의 캐스트를 정의하거나, 모델의 기본 키(primary key)에 캐스트를 할당해서는 안 됩니다.
Stringable 캐스팅
Illuminate\Database\Eloquent\Casts\AsStringable 캐스트 클래스를 사용하면 모델 속성을 Illuminate\Support\Stringable 플루언트 객체로 캐스팅할 수 있습니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\AsStringable;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'directory' => AsStringable::class,
];
}
}배열 및 JSON 캐스팅
array 캐스트는 직렬화된 JSON을 저장하는 컬럼에 특히 유용합니다. 데이터베이스의 JSON 또는 TEXT 컬럼에 array 캐스트를 지정하면, Eloquent 모델을 통해 접근할 때 PHP 배열로 자동 역직렬화됩니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => 'array',
];
}
}캐스트가 정의되면 options 속성에 접근할 때 JSON이 PHP 배열로 자동 변환되고, 값을 설정하면 자동으로 JSON으로 직렬화되어 저장됩니다.
use App\Models\User;
$user = User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save();JSON 속성의 특정 필드만 간결하게 업데이트하려면 해당 속성을 대량 할당 가능하게 설정하고 -> 연산자를 사용하세요.
$user = User::find(1);
$user->update(['options->key' => 'value']);JSON과 유니코드
한국어 등 유니코드 문자를 이스케이프 없이 JSON으로 저장하려면 json:unicode 캐스트를 사용하세요.
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => 'json:unicode',
];
}ArrayObject 및 컬렉션 캐스팅
기본 array 캐스트는 원시 PHP 배열을 반환하기 때문에, 배열의 특정 오프셋을 직접 수정하면 PHP 오류가 발생합니다.
$user = User::find(1);
$user->options['key'] = $value; // PHP 오류 발생이 문제를 해결하기 위해 Laravel은 AsArrayObject 캐스트를 제공합니다. JSON 속성을 PHP의 ArrayObject 클래스로 캐스팅하며, 개별 오프셋을 자유롭게 수정할 수 있습니다.
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsArrayObject::class,
];
}마찬가지로, JSON 속성을 Laravel 컬렉션으로 캐스팅하는 AsCollection 캐스트도 제공합니다.
use Illuminate\Database\Eloquent\Casts\AsCollection;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsCollection::class,
];
}기본 컬렉션 클래스 대신 커스텀 컬렉션 클래스를 사용하려면 캐스트 인자로 클래스명을 전달하세요.
use App\Collections\OptionCollection;
use Illuminate\Database\Eloquent\Casts\AsCollection;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsCollection::using(OptionCollection::class),
];
}of 메서드를 사용하면 컬렉션 아이템을 특정 클래스로 매핑할 수 있습니다. 내부적으로 컬렉션의 mapInto 메서드가 사용됩니다.
use App\ValueObjects\Option;
use Illuminate\Database\Eloquent\Casts\AsCollection;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'options' => AsCollection::of(Option::class)
];
}컬렉션 아이템을 객체로 매핑할 때, 해당 객체는 JSON 직렬화 방식을 정의하기 위해 Illuminate\Contracts\Support\Arrayable과 JsonSerializable 인터페이스를 구현해야 합니다.
<?php
namespace App\ValueObjects;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
class Option implements Arrayable, JsonSerializable
{
public string $name;
public mixed $value;
public bool $isLocked;
/**
* 새 Option 인스턴스를 생성합니다.
*/
public function __construct(array $data)
{
$this->name = $data['name'];
$this->value = $data['value'];
$this->isLocked = $data['is_locked'];
}
/**
* 인스턴스를 배열로 반환합니다.
*
* @return array{name: string, data: string, is_locked: bool}
*/
public function toArray(): array
{
return [
'name' => $this->name,
'value' => $this->value,
'is_locked' => $this->isLocked,
];
}
/**
* JSON으로 직렬화할 데이터를 반환합니다.
*
* @return array{name: string, data: string, is_locked: bool}
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}바이너리 캐스팅
모델에 자동 증가 ID 외에 바이너리 타입의 uuid 또는 ulid 컬럼이 있는 경우, AsBinary 캐스트를 사용해 바이너리 표현으로의 자동 변환을 설정할 수 있습니다.
use Illuminate\Database\Eloquent\Casts\AsBinary;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'uuid' => AsBinary::uuid(),
'ulid' => AsBinary::ulid(),
];
}캐스트가 정의된 후에는 UUID/ULID 속성에 객체나 문자열을 할당하면 Eloquent가 자동으로 바이너리 형식으로 변환해 저장하고, 읽을 때는 항상 일반 텍스트 문자열로 반환합니다.
use Illuminate\Support\Str;
$user->uuid = Str::uuid();
return $user->uuid;
// "6e8cdeed-2f32-40bd-b109-1e4405be2140"날짜 캐스팅
기본적으로 Eloquent는 created_at과 updated_at 컬럼을 PHP DateTime을 상속한 Carbon 인스턴스로 캐스팅합니다. 모델의 casts 메서드에서 추가 날짜 속성을 datetime 또는 immutable_datetime 타입으로 캐스팅할 수 있습니다.
date 또는 datetime 캐스트를 정의할 때 날짜 포맷을 지정할 수 있습니다. 이 포맷은 모델을 배열이나 JSON으로 직렬화할 때 사용됩니다.
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'created_at' => 'datetime:Y-m-d',
];
}컬럼이 날짜로 캐스팅되면 해당 속성에 UNIX 타임스탬프, Y-m-d 형식의 날짜 문자열, 날짜-시간 문자열, DateTime/Carbon 인스턴스를 모두 할당할 수 있습니다.
모델의 모든 날짜에 적용할 기본 직렬화 포맷을 바꾸려면 serializeDate 메서드를 정의하세요. 이 메서드는 데이터베이스 저장 포맷에는 영향을 주지 않습니다.
/**
* 배열 / JSON 직렬화를 위해 날짜를 포맷합니다.
*/
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format('Y-m-d');
}데이터베이스에 실제로 저장되는 날짜 포맷을 지정하려면 $dateFormat 프로퍼티를 정의하세요.
/**
* 날짜 컬럼의 저장 포맷입니다.
*
* @var string
*/
protected $dateFormat = 'U';날짜 캐스팅, 직렬화, 타임존
기본적으로 date 및 datetime 캐스트는 애플리케이션의 timezone 설정과 무관하게 UTC ISO-8601 형식(YYYY-MM-DDTHH:MM:SS.uuuuuuZ)으로 날짜를 직렬화합니다. 날짜를 항상 UTC로 저장하고 이 직렬화 포맷을 유지하는 것을 강력히 권장합니다. UTC를 일관되게 사용하면 PHP 및 JavaScript의 다른 날짜 라이브러리와의 호환성이 최대화됩니다.
datetime:Y-m-d H:i:s처럼 커스텀 포맷이 적용된 경우, Carbon 인스턴스의 내부 타임존(보통 timezone 설정값)이 직렬화에 사용됩니다. 단, created_at과 updated_at처럼 timestamp 컬럼은 이 규칙에서 제외되어 항상 UTC로 포맷됩니다.
Enum 캐스팅
PHP의 Enum으로 속성을 캐스팅할 수도 있습니다. casts 메서드에서 속성명과 Enum 클래스를 지정하면 됩니다.
use App\Enums\ServerStatus;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'status' => ServerStatus::class,
];
}캐스트를 정의하면 해당 속성을 읽고 쓸 때 자동으로 Enum 인스턴스로 변환됩니다.
if ($server->status == ServerStatus::Provisioned) {
$server->status = ServerStatus::Ready;
$server->save();
}Enum 배열 캐스팅
하나의 컬럼에 여러 Enum 값을 배열로 저장해야 한다면 AsEnumArrayObject 또는 AsEnumCollection 캐스트를 사용하세요.
use App\Enums\ServerStatus;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
/**
* 캐스팅할 속성을 반환합니다.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'statuses' => AsEnumCollection::of(ServerStatus::class),
];
}암호화 캐스팅
encrypted 캐스트는 Laravel의 내장 암호화 기능을 사용해 모델 속성 값을 암호화합니다. encrypted:array, encrypted:collection, encrypted:object, AsEncryptedArrayObject, AsEncryptedCollection 캐스트도 각각의 비암호화 버전과 동일하게 동작하되, 데이터베이스에 저장될 때 값이 암호화됩니다.
암호화된 텍스트는 길이가 예측 불가능하고 원본보다 길어지므로, 연결된 데이터베이스 컬럼은 TEXT 타입 이상이어야 합니다. 또한 값이 암호화되어 있으므로 암호화된 속성으로는 쿼리나 검색을 수행할 수 없습니다.
키 교체
Laravel은 app 설정 파일의 key 값(주로 APP_KEY 환경 변수)으로 문자열을 암호