VO(Value Object), ์šฐ๋ฆฌ๋Š” ์™œ ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ์“ฐ๊ณ  ์žˆ๋Š”๊ฐ€

minjookimm
  • #VO
  • #DTO
  • #frontend
  • #FE
  • #DDD

์•ˆ๋…•ํ•˜์„ธ์š” ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ๊น€๋ฏผ์ฃผ์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„  ์ €ํฌ ํŒ€์ด VO๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ณ  , ํ˜„์žฌ ์‚ฌ์šฉ ๋ฐฉ์‹์—์„œ ๋ฐœ๊ฒฌํ•œ ๋ฌธ์ œ์™€ ๊ฐœ์„  ๋ฐฉํ–ฅ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ ๋งํ•˜๋Š” VO๋Š” DDD์—์„œ ์ •์˜ํ•˜๋Š” Value Object๋ฅผ ๊ทธ๋Œ€๋กœ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ €ํฌ ํŒ€์—์„œ API ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ๋„๋ฉ”์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ๊ด€๋ จ ๋กœ์ง์„ ์บก์Аํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ ํด๋ž˜์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด ๊ธ€์—์„œ๋Š” ๋จผ์ € DDD์—์„œ ๋งํ•˜๋Š” VO์™€ ์ €ํฌ ํŒ€์˜ VO๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ์ง€ ์‚ดํŽด๋ณด๊ณ , ํ˜„์žฌ์˜ ์‚ฌ์šฉ ๋ฐฉ์‹๊ณผ ํ•จ๊ป˜ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉํ–ฅ์„ ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

1. VO๋ž€?

DDD์—์„œ ๋งํ•˜๋Š” Value Object

DDD์—์„œ Value Object๋Š” ๊ณ ์œ ํ•œ ์‹๋ณ„์ž๋ณด๋‹ค ๊ฐ’ ์ž์ฒด๋กœ ์˜๋ฏธ์™€ ๋™์ผ์„ฑ์„ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ Money, Address, Date Range ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ถˆ๋ณ€์„ฑ์„ ๊ฐ€์ง€๋ฉฐ, ํ•ด๋‹น ๊ฐ’๊ณผ ๊ด€๋ จ๋œ ๊ทœ์น™์ด๋‚˜ ํ–‰๋™์„ ๊ฐ์ฒด ์•ˆ์— ์บก์Аํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” VO๋Š”?

์ €ํฌ ํŒ€์—์„œ๋Š” OptionVo, ManagerVo์™€ ๊ฐ™์ด API ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ํƒ€์ž… ๋ณ€ํ™˜์ด๋‚˜ ํ•„๋“œ ๋งคํ•‘, ํŒŒ์ƒ๊ฐ’ ๊ณ„์‚ฐ ๋“ฑ์˜ ๋กœ์ง์„ ํ•จ๊ป˜ ์บก์Аํ™”ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ VO๋ผ๊ณ  ๋ถ€๋ฅด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ณด๋ฉด ํ˜„์žฌ ์ €ํฌ ํŒ€์˜ VO๋Š” DDD์—์„œ ๋งํ•˜๋Š” Value Object์™€ ๋™์ผํ•œ ๊ฐœ๋…์€ ์•„๋‹™๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค API DTO์™€ ๋„๋ฉ”์ธ ๋ชจ๋ธ ์‚ฌ์ด์˜ ๋ณ€ํ™˜ ์—ญํ• ๊ณผ ๋„๋ฉ”์ธ ๋กœ์ง์˜ ์ผ๋ถ€๋ฅผ ํ•จ๊ป˜ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์šฐ๋ฆฌ ํŒ€์ด VO๋ฅผ ์“ฐ๋Š” ๋ฒ•

ํƒ€์ž… ๋ณ€ํ™˜

API์—์„œ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’์„ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋„๋ฉ”์ธ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ž์—ด/์ˆซ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ธˆ์•ก์„ Decimal๋กœ ๋ณ€ํ™˜ํ•ด ๊ธˆ์•ก ์—ฐ์‚ฐ์ด ํ•„์š”ํ•œ ๊ณณ์—์„œ ์ผ๊ด€๋œ ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

this.addPrice = new Decimal(response.addPrice);

API ํ•„๋“œ โ†’ ๋„๋ฉ”์ธ ํ•„๋“œ ๋ณ€ํ™˜

API์˜ ํ•„๋“œ๋ช…๊ณผ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ•„๋“œ๋ช…์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ VO์—์„œ ์ด๋ฅผ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด API์—์„œ๋Š” maxCountPerUser๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’์„ ๋„๋ฉ”์ธ์—์„œ๋Š” maxCount๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜น์€ ์—ฌ๋Ÿฌ ํ•„๋“œ๋ฅผ ์กฐํ•ฉํ•ด ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Option.voํŒŒ์ผ์„ ์˜ˆ์‹œ๋กœ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

export class OptionVo {
    id: number;
    name: string;
    addPrice: Decimal;
    maxCount?: number;
    // ...
    static from(response: GetGoodsOptionsResponse) { return new OptionVo(response); }
    constructor(response: GetGoodsOptionsResponse) {
        this.id = response.id;
        this.addPrice = new Decimal(response.addPrice);
        this.maxCount = response.maxCountPerUser; // API ํ•„๋“œ๋ช… โ†’ ๋„๋ฉ”์ธ ํ•„๋“œ๋ช…
        // ...
    }
}

์ปดํฌ๋„ŒํŠธ ์ž…์žฅ์—์„œ๋Š” option.addPrice.plus(goods.price)์ฒ˜๋Ÿผ ๋ฐ”๋กœ ์—ฐ์‚ฐํ•˜๋ฉด ๋˜๊ณ , new Decimal(...)์ด๋‚˜ API ํ•„๋“œ๋ช…๋„ ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ VO๋ฅผ ํ†ตํ•ด API ์‘๋‹ต์— ๋Œ€ํ•œ ๋ณ€ํ™˜ ๋กœ์ง์„ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๋„๋ฉ”์ธ์— ๋งž๊ฒŒ ๋ณ€ํ™˜๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹คํ˜•์„ฑ ํ‘œํ˜„

์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ƒํ’ˆ์€ ์ผ๋ฐ˜ ์ƒํ’ˆ๊ณผ ๋ฒˆ๋“ค ์ƒํ’ˆ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์™€ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์ €ํฌ ํŒ€์—์„œ๋Š” ์ด๋ฅผ BaseCartGoodsVo๋ฅผ ์ƒ์†ํ•œ CartGoodsVo์™€ CartBundleVo๋กœ ๋‚˜๋ˆ„์–ด ํ‘œํ˜„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

response.goods.isBundle ? CartBundleVo.from(...) : CartGoodsVo.from(...)

์ƒ์„ฑ ์‹œ์  - ์ฟผ๋ฆฌ์—์„œ VO๋กœ ๋ณ€ํ™˜

ํ˜„์žฌ ์ €ํฌ ํŒ€์—์„œ๋Š” API ์‘๋‹ต์„ ์ฟผ๋ฆฌ์—์„œ VO๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

queryFn: () => getManagers(query).then((response) => response.managers.map(ManagersVo.from))

์ปดํฌ๋„ŒํŠธ์—์„œ vo์˜ ์‚ฌ์šฉ๋ฒ•

VO๋ฅผ ๊ฑฐ์นœ ์ดํ›„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” API ์‘๋‹ต์˜ ๊ตฌ์ฒด์ ์ธ ํ˜•ํƒœ๋‚˜ ํƒ€์ž… ๋ณ€ํ™˜ ๊ณผ์ •์„ ์•Œ ํ•„์š” ์—†์ด, ๋„๋ฉ”์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ’๊ณผ ์†์„ฑ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

amount: order.price.toNumber(),

const isMembership = orderGoodsItem.isMembership;

์œ„์น˜์™€ ๋„ค์ด๋ฐ

  • src/vo/ย ์•„๋ž˜ ๋„๋ฉ”์ธ๋ณ„๋กœ ์ •๋ฆฌํ•ด ์ค‘์•™์ง‘์ค‘ํ™”ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ src/domains/*/*/vo/์ฒ˜๋Ÿผ ๊ฐ ๋„๋ฉ”์ธ ํด๋”์•ˆ์— ํ•จ๊ป˜ ๋‘๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์ €ํฌ ํŒ€์€ ํ”„๋กœ์ ํŠธ ํŠน์„ฑ์— ๋”ฐ๋ผ ์กฐ๊ธˆ ์ƒ์ดํ•˜๊ฒŒ ์‚ฌ์šฉ์ค‘์ž…๋‹ˆ๋‹ค.
  • ํด๋ž˜์Šค๋Š” static from(response) ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์™€ constructor(response)๋ฅผ ์‚ฌ์šฉํ•ด API ์‘๋‹ต์„ VO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ˜•ํƒœ๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

3. DTO์™€ ์šฐ๋ฆฌ์˜ VO

์—ฌ๊ธฐ์„œ ํ•œ ๊ฐ€์ง€ ๊ตฌ๋ถ„ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. API ์‘๋‹ต๊ฐ’์„ ๋‚ด๋ถ€์—์„œ ํ•„์š”ํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค๋Š” ์„ค๋ช…๋งŒ ๋ณด๋ฉด DTO์™€ VO์˜ ์—ญํ• ์ด ๋น„์Šทํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

DTO๊ฐ€ ๊ณ„์ธต์ด๋‚˜ ์‹œ์Šคํ…œ ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ๋ผ๋ฉด, VO๋Š” ๋„๋ฉ”์ธ์—์„œ ํ•ด๋‹น ๊ฐ’์ด ์–ด๋–ค ์˜๋ฏธ์™€ ๊ทœ์น™์„ ๊ฐ€์ง€๋Š”์ง€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์ดˆ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ €ํฌ ํŒ€์˜ VO๋Š” API ์‘๋‹ต์„ ๋„๋ฉ”์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ๊ณผ ๋„๋ฉ”์ธ ๊ด€๋ จ ๋กœ์ง์„ ์บก์Аํ™”ํ•˜๋Š” ์—ญํ• ์ด ํ•จ๊ป˜ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ DTO์™€ VO์˜ ์—ญํ• ์ด ์ผ๋ถ€ ์„ž์—ฌ ์žˆ๋Š” ํ˜•ํƒœ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. ํ˜„์žฌ VO ์‚ฌ์šฉ ๋ฐฉ์‹์—์„œ ๋ฐœ๊ฒฌํ•œ ๋ฌธ์ œ

์กฐ์‚ฌํ•˜๋ฉด์„œ VO๋ฅผ ๋” ์ž˜ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ์ง€์ ๋“ค์ด ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

โ‘  VO์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒŒ์ƒ๊ฐ’์„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๊ณ  ์žˆ๋‹ค

StatisticsMembershipCount.vo.ts์—๋Š” ํ˜„์žฌ ์œ ์ผํ•˜๊ฒŒ ํŒŒ์ƒ๊ฐ’์„ getter๋กœ ์ œ๊ณตํ•˜๋Š” ํŒจํ„ด์ด ์žˆ์Šต๋‹ˆ๋‹ค.

get existingJoinCount() { return this.totalJoinCount - this.newJoinCount; }

๊ทธ๋Ÿฐ๋ฐ ์‹ค์ œ ์‚ฌ์šฉ์ฒ˜์—์„œ๋Š” ํ•ด๋‹น getter๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‹ค์‹œ totalJoinCount - newJoinCount๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, VO์—์„œ ์ด๋ฏธ ์ •์˜ํ•œ ๊ณ„์‚ฐ ๋กœ์ง์„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‹ค์‹œ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. VO์— ํŒŒ์ƒ๊ฐ’์„ getter๋กœ ์ •์˜ํ–ˆ๋‹ค๋ฉด, ์‚ฌ์šฉ์ฒ˜์—์„œ๋„ ํ•ด๋‹น getter๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•ด์•ผ ํŒŒ์ƒ๊ฐ’์— ๋Œ€ํ•œ ๊ณ„์‚ฐ ๋กœ์ง์„ VO์—์„œ ์ผ๊ด€๋˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ๋ชจ๋“  ๊ณ„์‚ฐ ๋กœ์ง์„ VO๋กœ ์˜ฎ๊ฒจ์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ์—์„œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š” ๊ฐ’์ด๋‚˜ ๊ทœ์น™์ด๋ผ๋ฉด VO์—์„œ ๊ด€๋ฆฌํ•˜๊ณ , ๋‹จ์ˆœํžˆ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ณ„์‚ฐ์ด๋ผ๋ฉด ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋“ฑ ๊ธฐ์ค€์„ ๊ตฌ๋ถ„ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ๋ถ„์˜ ๊ธฐ์ค€์€ ํŒ€๋‚ด ๋…ผ์˜๊ฐ€ ํ•„์š”ํ•ด๋ณด์ž…๋‹ˆ๋‹ค

โ‘ก ๋‚ ์งœ ํฌ๋งทํŒ… ๋กœ์ง์˜ ์ฑ…์ž„ ๋ฒ”์œ„๊ฐ€ ๋ช…ํ™•ํ•˜์ง€ ์•Š๋‹ค

ํ˜„์žฌ ์ €ํฌํŒ€์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด dayjs๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ๊ฐ ๋‚ ์งœ ํฌ๋งทํŒ…์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธˆ์•ก์˜ ๊ฒฝ์šฐ Decimal์„ ์‚ฌ์šฉํ•ด ์—ฐ์‚ฐ์— ํ•„์š”ํ•œ ํƒ€์ž…์„ VO์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ๋‚ ์งœ์˜ ๊ฒฝ์šฐ startAt, endAt ์ž์ฒด๋ฅผ VO์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ ํ™”๋ฉด์— ํ‘œ์‹œํ•  ํ˜•์‹๊นŒ์ง€ VO์—์„œ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

โ‘ข ๋™์ผํ•œ ๋„๋ฉ”์ธ ์•ˆ์—์„œ๋„ VO ์ ์šฉ ๊ธฐ์ค€์ด ์ผ๊ด€๋˜์ง€ ์•Š๋‹ค

partner ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋™์ผํ•œ ๋„๋ฉ”์ธ ์•ˆ์—์„œ๋„ VO๋ฅผ ์ ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์„œ๋กœ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

// useGetManager.ts, useGetManagers.ts โ†’ VO๋กœ ๊ฐ์Œˆ
queryFn: () => getManager(id).then((response) => ManagerVo.from(response))

// useGetPermissionChannels.ts, useGetPermissionArtis.ts ๋“ฑ โ†’ VO ์—†์ด raw ๊ทธ๋Œ€๋กœ
select: (data) => data.channels

๊ฐ™์€ ๋„๋ฉ”์ธ์—์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋Š” VO๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ์–ด๋–ค ๋ฐ์ดํ„ฐ๋Š” API ์‘๋‹ต์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ถˆ์ผ์น˜ ๋•Œ๋ฌธ์— ํŒ€ ํšŒ์˜์—์„œ API ์‘๋‹ต์€ ๊ธฐ๋ณธ์ ์œผ๋กœ VO๋กœ ๊ฐ์‹ผ๋‹ค๋Š” ์ปจ๋ฒค์…˜์„ ์ •ํ–ˆ๊ณ , ์œ„ ์ฝ”๋“œ๋Š” ๊ทธ ์ด์ „์— ์ž‘์„ฑ๋œ ๋ ˆ๊ฑฐ์‹œ์ž…๋‹ˆ๋‹ค. ๋‚จ์€ ์ผ์€ ์ด ์ฝ”๋“œ๋“ค์„ ์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

5. ๊ฐœ์„  ๋ฐฉํ–ฅ ์ œ์•ˆ

  1. VO ์ปจ๋ฒค์…˜ ๋ฌธ์„œํ™”์™€ ๋ ˆ๊ฑฐ์‹œ ์ •๋ฆฌ โ€œAPI ์‘๋‹ต์€ ๊ธฐ๋ณธ์ ์œผ๋กœ VO๋กœ ๊ฐ์‹ผ๋‹คโ€๋Š” ์ปจ๋ฒค์…˜์„ CLAUDE.md๋‚˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์ŠคํŠธ์— ๋ช…์‹œํ•˜๊ณ , ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์€ ์ฝ”๋“œ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  2. ๋‚ ์งœยท๊ธˆ์•ก์˜ ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ํ‘œํ˜„ ๋กœ์ง ๊ตฌ๋ถ„ ๊ธˆ์•ก์˜ ๊ณ„์‚ฐ์— ํ•„์š”ํ•œ Decimal, ๋‚ ์งœ์˜ ์œ ํšจ๊ธฐ๊ฐ„ยท๊ธฐ๊ฐ„ ๊ณ„์‚ฐ ๋“ฑ ๋„๋ฉ”์ธ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ๋กœ์ง์€ VO์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ™”๋ฉด ํ‘œ์‹œ๋ฅผ ์œ„ํ•œ ํฌ๋งทํŒ…์€ ๋ณ„๋„์˜ ํ‘œํ˜„ ์˜์—ญ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ€๊ฒฉ ํฌ๋งทํŒ… ๋กœ์ง์€ ๊ณต์šฉ ํŒจํ‚ค์ง€๋กœ ๋ถ„๋ฆฌํ•ด ํ”„๋กœ์ ํŠธ๋ณ„ ์ค‘๋ณต ๊ตฌํ˜„์„ ์ค„์ผ ์ˆ˜ ์žˆ์„๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  3. getter ์ถ”๊ฐ€ ์‹œ ์‚ฌ์šฉ์ฒ˜๊นŒ์ง€ ํ•จ๊ป˜ ๋ฆฌํŒฉํ† ๋ง ์ƒˆ๋กœ์šด getter๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ธฐ์กด์— ๋™์ผํ•œ ๊ฐ’์„ ์ง์ ‘ ๊ณ„์‚ฐํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ์šฉ์ฒ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ•จ๊ป˜ ํ™•์ธํ•ด VO์— ์ •์˜๋œ ๊ณ„์‚ฐ ๋กœ์ง์˜ ์ค‘๋ณต ๊ตฌํ˜„์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  4. VO๋ผ๋Š” ์ด๋ฆ„๊ณผ ์‹ค์ œ ์—ญํ•  ์‚ฌ์ด์˜ ๊ฐ„๊ทน ํ˜„์žฌ ์ €ํฌ ํŒ€์˜ VO๋Š” DDD์˜ Value Object์™€ ๋‹ฌ๋ฆฌ API ์‘๋‹ต์„ ๋„๋ฉ”์ธ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ๊ณผ ๋„๋ฉ”์ธ ๋กœ์ง์„ ํ•จ๊ป˜ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๋ชจ๋ฅด๋Š” ์‚ฌ๋žŒ์ด OptionVo์™€ ๊ฐ™์€ ์ด๋ฆ„๋งŒ ๋ดค์„ ๋•Œ, ์ผ๋ฐ˜์ ์œผ๋กœ ์•Œ๊ณ  ์žˆ๋Š” Value Object๋ฅผ ๊ธฐ๋Œ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ˜„์žฌ์˜ VO๋ผ๋Š” ๋„ค์ด๋ฐ์ด ์‹ค์ œ ์—ญํ• ์„ ์ถฉ๋ถ„ํžˆ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๋Š”์ง€ ๊ฒ€ํ† ํ•ด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋ณด๋‹จ ํ˜„์žฌ VO๊ฐ€ ์–ด๋–ค ์ฑ…์ž„์„ ๊ฐ€์ ธ์•ผ ํ•˜๋Š”์ง€๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

๋‹น์—ฐํ•˜๊ฒŒ ์“ฐ๊ณ  ์žˆ๋˜ VO๋ฅผ ๋‹ค์‹œ ๋“ค์—ฌ๋‹ค๋ณด๋‹ˆ, DDD์˜ ์ •์˜์™€ ์ผ์น˜ํ•˜๋Š”์ง€๋ณด๋‹ค ์ง€๊ธˆ ์šฐ๋ฆฌ VO๊ฐ€ ์–ด๋–ค ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ๊ณ  ๊ทธ ์—ญํ• ์— ๋งž๋Š” ๊ธฐ์ค€์„ ๊ฐ€์ง€๊ณ  ์“ฐ์ด๋Š”์ง€๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ ํŒ€์˜ VO๋Š” DDD์˜ Value Object์™€ ๋™์ผํ•œ ํ˜•ํƒœ๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋‚˜๋ฆ„์˜ ๋ช…ํ™•ํ•œ ์—ญํ• ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ€ ์•ˆ์—์„œ ๊ทธ ์—ญํ• ๊ณผ ์‚ฌ์šฉ ๊ธฐ์ค€์ด ํ•ฉ์˜๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ด๋ฆ„์ด ๋ฌด์—‡์ด๋“  ์ถฉ๋ถ„ํžˆ ์œ ์šฉํ•œ ๋„๊ตฌ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์„ ๊ณ„๊ธฐ๋กœ ๊ทธ ๊ธฐ์ค€์„ ์กฐ๊ธˆ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ๋‹ค๋“ฌ์–ด๊ฐ€๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ