Laravel

Laravel, DTO ์ž‘์„ฑํ•˜๊ธฐ

byzz 2024. 5. 25. 21:04
๐Ÿ’ก PHP 8 ์ด์ƒ์˜ ๋ฒ„์ „์—์„œ ์œ ํšจํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ๋“ค์ด ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ผ๋ผ๋ฒจ์—์„œ DTO(Data Transfer Object)๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ๊ธ€์—์„œ๋Š” spatie/laravel-data ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. PHP์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ‹ฑํ•˜๊ณ  ๋ผ๋ผ๋ฒจ๊ณผ ํ˜ธํ™˜์ด ์ž˜ ๋˜์–ด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๐Ÿ‘

 

์˜คํ”ˆ์†Œ์Šค ๊ฐœ๋ฐœ ํšŒ์‚ฌ spatie์—์„œ ๊ฐœ๋ฐœ๋œ laravel-data๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์†Œ๊ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Powerful data objects for laravel
… can be used in various ways. Using this package you only need to describe your data once:
instead of a form request, you can use a data objectinstead of an API transformer, you can use a data objectinstead of manually writing a typescript definition, you can use… ๐Ÿฅ a data object
Laravel์„ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด
… ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์ •์˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค:
ํผ ์š”์ฒญ ๋Œ€์‹  ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.API ๋ณ€ํ™˜๊ธฐ ๋Œ€์‹  ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.์ง์ ‘ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ •์˜๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹ … ๐Ÿฅ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ค์น˜

composer require spatie/laravel-data

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

๊ทธ๋Ÿผ laravel-data ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

<?php

namespace App\Http\Controllers\Requests;

use Spatie\LaravelData\Data;

class Feedback extends Data
{
    public function __construct(
        public readonly int $id,
        public readonly int $point,
    ) {
    }
}

 

๊ธฐ๋ณธ์ ์ธ Data ํด๋ž˜์Šค ์ƒ์„ฑ ๋ฐฉ๋ฒ•์€ ์œ„์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.. Spatie\LaravelData\Data ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๊ณ  ์ •์˜ํ•  ๋ฐ์ดํ„ฐ ํƒ€์ž…๋“ค์„ ์ƒ์„ฑ์ž์— ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค๊ฐ€ ๊ฐ์ฒด๋กœ ์ƒ์„ฑ๋œ ์ดํ›„์—๋Š” ์†์„ฑ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” "๋ถˆ๋ณ€ ๊ฐ์ฒด ํŒจํ„ด"์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด readonly ์†์„ฑ์„ ์ถ”๊ฐ€์ ์œผ๋กœ ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๋ถˆ๋ณ€ ๊ฐ์ฒด ํŒจํ„ด์ด๋ž€? ๋ถˆ๋ณ€ ๊ฐ์ฒด ํŒจํ„ด์€ ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ํ•œ ๋ฒˆ ์„ค์ •๋˜๋ฉด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋„๋ก ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ, ์ด๋Š” ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์—ฌ์ค๋‹ˆ๋‹ค. Java์—์„œ๋Š” final ํ‚ค์›Œ๋“œ, C#์—์„œ๋Š” readonly ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์ค„์–ด๋“ค๊ณ , ๊ฐ์ฒด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด from static ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

$feedback = Feedback::from([
    'id' => 1,
    'point' => 5,
]);

 

PHPStorm IDE ๊ธฐ์ค€์œผ๋กœ from ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ์˜ ์—ฐ๊ด€ ๋ฐฐ์—ด ์ž‘์„ฑ์‹œ ์ž๋™์™„์„ฑ์ด ์ง€์›๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ „ํ˜€ ๋ถˆํŽธํ•จ์ด ์—†์Šต๋‹ˆ๋‹ค.
(VSCode๋Š” ์š”์ฆ˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋„ค์š”. ์ƒ์šฉ ์†Œํ”„ํŠธ์›จ์–ด์˜ ๋ง›์„ ์•Œ์•„๋ฒ„๋ฆฐ ์ดํ›„์— VSCode๋ฅผ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒŒ๋˜๋„ค์š”. ๐Ÿฅฒ)

Request ๋ฐ Validation ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌ

์ „ํ†ต์ ์ธ ๋ผ๋ผ๋ฒจ์˜ Request๋ฅผ ๋ฐ›์•„ ์œ ํšจ์„ฑ(Validation) ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

<?php

use Illuminate\Http\Request;

class Controller {
    public function getFeedback(Request $request)
    {
        $validated = $request->validate([
            'id' => 'required|integer',
            'point' => 'require|integer',
        ]);

        ...
    }
}

 

๊ฐ„๊ฒฐํ•˜๊ณ  ์ž‘์„ฑํ•˜๊ธฐ ๋น ๋ฅธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ €๋Š” ๊ฐœ์ธ์ ์œผ๋กœ ๋ณต์žกํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๋“ค์–ด๊ฐ„ ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ ๋กœ์ง์—์„œ ๋งŽ์ด ๋ฒ—์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ„๋ฆฌ ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ๋‹ˆ์ฆˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด spatie/laravel-data ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

<?php

namespace App\Http\Controllers\Requests;

use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Attributes\Validation\In;
use Spatie\LaravelData\Attributes\Validation\IntegerType;
use Spatie\LaravelData\Attributes\Validation\StringType;
use Spatie\LaravelData\Attributes\Validation\Max;
use Spatie\LaravelData\Attributes\Validation\Required;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;

#[MapInputName(SnakeCaseMapper::class)]
class PointRequest extends Data
{
    public function __construct(
        #[Required, IntegerType]
        public readonly int $id,

        #[Required, In([1, 2, 3])]
        public readonly int $point,

        #[Nullable, StringType, Max(50)]
        public readonly ?string $userName = null,
    ) {
    }
}

 

์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ Spatie\LaravelData\Data ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๊ณ , PHP 8์—์„œ ์ถ”๊ฐ€๋œ Attributes์„ ์ด์šฉํ•ด์„œ PointRequest ํด๋ž˜์Šค์— ์ถ”๊ฐ€์ ์ธ ๋™์ž‘์ด ๋˜๋„๋ก ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

#[MapInputName(SnakeCaseMapper::class)] ์ด๋Š” API ์š”์ฒญ ํŒŒ๋ผ๋ฉ”ํ„ฐ๋“ค์˜ ๊ทœ์น™์€ snake_case๋กœ ์š”์ฒญ๋˜์ง€๋งŒ ๋‚ด๋ถ€ ์ฝ”๋“œ๋Š” camelCase ๊ทœ์น™์„ ๋งž์ถ”๊ธฐ ์œ„ํ•œ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

์š”์ฒญ ์„ค๋ช…
/api/point?id=1&point=5&user_name=Hello user_name ํŒŒ๋ผ๋ฉ”ํ„ฐ๋Š” public readonly ?string $userName๊ณผ ๋งคํ•‘๋จ

 

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•œ Request ํด๋ž˜์Šค๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํžˆ Controller์™€ Request ํŒŒ์ผ์ด ๋ถ„๋ฆฌ๋˜์–ด ๋ณด๋‹ค ๊ทœ๋ชจ๊ฐ€ ํฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๊ด€๋ฆฌ๋ฅผ ๋ณด๋‹ค ํšจ์œจ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

<?php

use App\Http\Controllers\Requests\PointRequest;

class Controller {
    public function getFeedback(PointRequest $request)
    {
        dd($request); // validation์ด ์ˆ˜ํ–‰๋œ ํ›„ ์ƒ์„ฑ๋œ ๊ฐ์ฒด

        ...
    }
}

 

์ปจํŠธ๋กค๋Ÿฌ ๋ ˆ์ด์–ด๊นŒ์ง€ ์ง„์ž…ํ•˜๊ธฐ ์ „์— ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. (์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ†ต๊ณผ๋˜์ง€ ๋ชปํ•˜๋ฉด ๋ผ๋ผ๋ฒจ์€ 422 Unprocessable Entity๋ฅผ ๋ฐ˜ํ™˜ํ•จ)

๋ณธ๊ฒฉ DTO(Data Transfer Object) ๊ฐ์ฒด๋กœ์˜ ์ด์šฉ

DTO ๊ฐ์ฒด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ DTO๋ฅผ ๋งŒ๋“ค์–ด ์–ด๋–ป๊ฒŒ DTO ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

BagDto ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ด ๋ด…๋‹ˆ๋‹ค.

<?php

namespace App\Services\Product\Dto;

use Carbon\Carbon;
use App\Services\Product\Dto\BagItemDto;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\MapOutputName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
use Illuminate\Support\Collection;

#[MapOutputName(SnakeCaseMapper::class)]
class BagDto extends Data
{
    public function __construct(
        public readonly int $count,

        /** @var Collection<BagItemDto> */
        public readonly Collection $items,
    ) {
    }
}

 

array ํƒ€์ž… ๋˜๋Š” ๋ผ๋ผ๋ฒจ์˜ ๊ธฐ๋ณธ Collection ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด ๋ฆฌ์ŠคํŠธ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ PHPDoc์„ ์ด์šฉํ•ด ํƒ€์ž…์„ ๋ช…์‹œํ•ด์ฃผ๋Š”๊ฑธ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์•ผ IDE์—์„œ ์ •ํ™•ํžˆ ํƒ€์ž… ์ถ”๋ก ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (PHP๊ฐ€ Generic์„ ์•„์ง ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ๋งค์šฐ ์•ˆํƒ€๊น๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๐Ÿฅฒ)

 

๊ทธ๋ฆฌ๊ณ  BadItemDto ํด๋ž˜์Šค ํŒŒ์ผ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

<?php

namespace App\Services\Product\Dto;

use Spatie\LaravelData\Attributes\MapOutputName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;

#[MapOutputName(SnakeCaseMapper::class)]
class BagItemDto extends Data
{
    public function __construct(
        public readonly int $id,
        public readonly string $itemName,
    ) {
    }
}

 

๋‘ ๊ฐ€์ง€์˜ Dto ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$items = [
    ['id' => 1, 'itemName' => 'item1'],
    ['id' => 2, 'itemName' => 'item2'],
];

$bag = BagDto::from([
    'count' => 2,
    'items' => $items,
]);

 

 

BagDto::from(); ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ $items์— ์—ฐ๊ด€๋ฐฐ์—ด ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ž๋™์œผ๋กœ ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ ๊ฐ์ฒด๋กœ ์ƒ์„ฑ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋˜ํ•œ, ๊ฐ Dto ํด๋ž˜์Šค ํŒŒ์ผ ์ƒ๋‹จ์— ์ž‘์„ฑ๋ผ ์žˆ๋Š” #[MapOutputName(SnakeCaseMapper::class)] ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๊ฐ€ ๋ผ๋ผ๋ฒจ์—์„œ ์ถœ๋ ฅ๋ ๋•Œ ๋‹ค์Œ์˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด snake_case๋กœ ๋ณ€ํ™˜๋˜์–ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

 

์ด๋Š” ๋‚ด๋ถ€์˜ ์ปจ๋ฒค์…˜์„ camelCase๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ API ์‘๋‹ต ์ปจ๋ฒค์…˜์€ snake_case๋กœ ์ปจ๋ฒค์…˜์„ ์œ ์ง€ํ•˜๋Š”๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ผ๋ผ๋ฒจ Resources ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, DTO ์ž‘์„ฑ์„ ํ†ตํ•ด ์ด๋Ÿฐ ์„ธ์„ธํ•œ ๋ถ€๋ถ„๊นŒ์ง€ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์ œ๊ฒ ํฐ ์žฅ์ ์œผ๋กœ ๋ณด์˜€๊ณ , Resources ๊ธฐ๋Šฅ ๋Œ€์‹  Attributes์„ ํ†ตํ•ด ์ปจ๋ฒค์…˜์„ ์œ ์ง€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

์กฐ๊ธˆ ๋” ํด๋ž˜์Šค ์นœํ™”์ ์ธ ์ฝ”๋“œ๊ฐ€ ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๐Ÿ‘

Typescript Definition ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ

์ž˜ ์ž‘์„ฑ๋œ DTO ํŒŒ์ผ๋“ค์— Attributes๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋‹ค์Œ์˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Typescript ํƒ€์ž… ์ •์˜ ํŒŒ์ผ ๋˜ํ•œ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

php artisan typescript:transform

์•„! ํ•ด๋‹น ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” spatie/laravel-typescript-transformer ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

composer require --dev spatie/laravel-typescript-transformer

์ฆ‰ ์•„๋ž˜์˜ DTO ํด๋ž˜์Šค ํŒŒ์ผ์€

<?php

namespace App\Http\Controllers\Requests;

use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Attributes\Validation\In;
use Spatie\LaravelData\Attributes\Validation\IntegerType;
use Spatie\LaravelData\Attributes\Validation\StringType;
use Spatie\LaravelData\Attributes\Validation\Max;
use Spatie\LaravelData\Attributes\Validation\Required;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[MapInputName(SnakeCaseMapper::class), TypeScript]
class PointRequest extends Data
{
    public function __construct(
        #[Required, IntegerType]
        public readonly int $id,

        #[Required, In([1, 2, 3])]
        public readonly int $point,

        #[Nullable, StringType, Max(50)]
        public readonly ?string $userName = null,
    ) {
    }
}

 

(TypeScript Attributes๋ฅผ ์ถ”๊ฐ€๋จ)

export interface PointRequest {
  id: number;
  point: '1' | '2' | '3';
  userName?: string;
}

 

์™€ ๊ฐ™์ด ๋ณ€ํ™˜๋œ ํŒŒ์ผ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋Š” ํšŒ์‚ฌ์˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์™€ ํ˜‘์—…ํ• ๋•Œ ๋„์›€์ด ๋  ์ˆ˜๋„ ๊ทธ๋‹ค์ง€ ํ•„์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜…

์‹ฌํ™”ํŽธ: ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์—ฌ ๊ฐ์ฒด ๋ณต์ œํ•˜๊ธฐ

์œ„์˜ ๋ชจ๋“  ์˜ˆ์‹œ๋“ค์€ "๋ถˆ๋ณ€ ๊ฐ์ฒด ํŒจํ„ด"์„ ์ ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— readonly ์†์„ฑ์„ ์ •์˜ํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ ๊ฐ’์ด ๋ณ€ํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์— ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์†์„ฑ์„ ๋ฎ์œผ๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿด๋• ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์ด ๋„์›€์ด ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

#[MapOutputName(SnakeCaseMapper::class)]
class BagItemDto extends Data
{
    public function __construct(
        public readonly int $id,
        public readonly string $itemName,
    ) {
    }

    public function withItemName(string $itemName): self
    {
        $bagItem = $this->with(); // ์›๋ž˜์˜ ๋ฐ์ดํ„ฐ๋“ค

        $bagItem['itemName'] = $itemName; // ๊ฐ’ ๋ณ€๊ฒฝ

        return self::from($bagItem); //  ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค ๋ฐ˜ํ™˜
    }
}
$bagItem = BagItemDto::from([
    'id' => 1,
    'itemName' => 'item1',
]);

$bagItem->withItemName('item2');

๋งˆ์น˜๋ฉฐ

์–ด๋””๊นŒ์ง€๋‚˜ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฑ„ํƒํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ณ ๋Š” ์„ ํƒ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ๊ทœ๋ชจ์— ๋”ฐ๋ผ์„œ ๊ทธ๋ฆฌ๊ณ  ํ•„์š”์„ฑ์— ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์ธ ๋ผ๋ผ๋ฒจ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋งŽ์€ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค๊ณผ ๊ฐ™์ด ํ˜‘์—…์ด ๋˜์–ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด ๋ชจ๋‘์˜ ๋™์˜๋ฅผ ๊ตฌํ•ด์•ผ ํ•  ๊ฒƒ์ด๊ณ , 1์ธ ๊ทœ๋ชจ์—์„œ๋Š” ๋ถˆํ•„์š”ํ•œ ๋…ธ๋™์ด ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ถฉ๋ถ„ํžˆ ๊ฒ€ํ†  ํ›„ ์‚ฌ์šฉํ•˜์‹œ๋Š” ํ˜„๋ช…ํ•œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. :)

 

๋ชจ๋“  ๊ธ€์€ ๋‹ค์Œ์˜ ๋ฌธ์„œ ๋งํฌ์—์„œ ๋ณด๋‹ค ์ •ํ™•ํ•œ ๋‚ด์šฉ ๊ทธ๋ฆฌ๊ณ  ์ถ”๊ฐ€์ ์ธ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

https://spatie.be/docs/laravel-data/v4/introduction

์ฐธ์กฐ

ํ•ด๋‹น ๊ธ€์€ ์ œ ๊ฐœ์ธ ์„œ๋ฒ„ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์žฌ์ž‘์„ฑ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.