๋ฐ˜์‘ํ˜•

์‹๋ณ„์ž ์˜์—ญ / ํƒ€์ž… ๊ตฌํ˜„ ์˜์—ญ


type, interface ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ํƒ€์ž… ์„ ์–ธ์€ = ๋“ฑํ˜ธ์™€ {} ์ค‘๊ด„ํ˜ธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‹๋ณ„์ž(์ขŒ), ํƒ€์ž… ๊ตฌํ˜„(์šฐ) ์˜์—ญ์œผ๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

// type ์‹๋ณ„์ž = ํƒ€์ž… ๊ตฌํ˜„
type MyType = string | number;

// interface ์‹๋ณ„์ž { ํƒ€์ž… ๊ตฌํ˜„ }
interface User {
  name: string;
  age: number;
}

 

์ œ์•ฝ ์กฐ๊ฑด | Constraints


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

๊ธฐ๋ณธ ๋ฌธ๋ฒ• : T extends UT๋Š” U์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ(T ⊆ U)์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Œ

ex) 64 extends number : O — 64๋Š” number์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ์ด๋ฏ€๋กœ
ex) number extends 64 : X — 64๋ณด๋‹ค number๊ฐ€ ๋” ํฐ ๊ฐœ๋…์ด๋ฏ€๋กœ

 

// string, number๋งŒ ๋ฐ›๋„๋ก ์ œ๋„ค๋ฆญ(T) ํƒ€์ž… ์ œ์•ฝ
interface User<T extends string | number> {
  name: string;
  age: T;
}

const userA: User<number> = { name: 'Smith', age: 32 }; // OK
const userA: User<string> = { name: 'Smith', age: '32' }; // OK

 

์ œ์•ฝ ์กฐ๊ฑด์„ ์ด์šฉํ•ด ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด ๋ฐฐ์—ด์ž„์„ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ œ๋„ค๋ฆญ ํƒ€์ž…(T)์ด ๋ฐฐ์—ด์ธ์ง€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ...T ๊ฐ™์€ Rest ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

// ํƒ€์ž… ์‹œ์Šคํ…œ์— Array.push๋ฅผ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ
type Push1<T, U> = [...T, U]; // Error! A rest element type must be an array type

type Push2<T extends unknown[], U> = [...T, U];
type Result = Push2<[1, 2], '3'>; // [1, 2, '3']

 

์ œ๋„ค๋ฆญ ํƒ€์ž…์— length ๊ฐ™์€ ํŠน์ • ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฑธ ์•Œ๋ ค์ค„ ๋•Œ๋„ ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ œ์•ฝ ์กฐ๊ฑด ์—†์ด ์‚ฌ์šฉํ•˜๋ฉด ์ œ๋„ค๋ฆญ ํƒ€์ž…์— length ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

// ์ฃผ์–ด์ง„ ๋ฐฐ์—ด์˜ ๊ธธ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ œ๋„ค๋ฆญ Length
type Length1<T> = T['length']; // Error! Type ‘length’ cannot be used to index type ‘T’

type Length2<T extends { length: number }> = T['length'];
type T0 = Length2<[1, 2, 3]>; // 3

// T๋ฅผ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•ด๋„ length ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
type Length3<T extends unknown[]> = T['length'];
type T1 = Length3<[1, 2]>; // 2

 

๐Ÿ’ก ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜์—๋„ = ๋“ฑํ˜ธ๋ฅผ ์‚ฌ์šฉํ•ด ๊ธฐ๋ณธ๊ฐ’์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ œ๋„ค๋ฆญ์„ ์„ ํƒ์ ์œผ๋กœ ๋ฐ›๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ž˜๋Š” K๊ฐ€ ์ฃผ์–ด์ง€์ง€ ์•Š์€ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด K์˜ ๊ธฐ๋ณธ๊ฐ’์„ keyof T๋กœ ์ง€์ •ํ•œ ์˜ˆ์‹œ(์ฐธ๊ณ ).

type MyType<T, K extends keyof T = keyof T> = T & { ... }

 

์กฐ๊ฑด๋ถ€ ํƒ€์ž… | Conditional Types


ํƒ€์ž… ๊ตฌํ˜„ ์˜์—ญ์—์„œ ์‚ฌ์šฉํ•˜๋Š” extends๋Š” ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ œ๋„ค๋ฆญ์ด ๊ฐ€์ง€๋Š” ํƒ€์ž… ์ข…๋ฅ˜์— ๋”ฐ๋ผ(์กฐ๊ฑด์— ๋”ฐ๋ผ) ๋‹ค๋ฅธ ๊ฐ’์„ ์ง€์ •ํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค.

๊ธฐ๋ณธ ๋ฌธ๋ฒ• : T extends U ? X : YT๊ฐ€ U์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฉด(๋ถ€๋ถ„ ์ง‘ํ•ฉ์ด๋ฉด) X, ์•„๋‹ˆ๋ฉด Y ๋ฆฌํ„ด

 

๐Ÿ’ก T๊ฐ€ ์œ ๋‹ˆ์˜จ์ผ ๋•Œ T extends U ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์œ ๋‹ˆ์˜จ T์˜ ๊ฐ ์š”์†Œ๋“ค์„ ์ˆœํšŒํ•˜๋ฉด์„œ ์กฐ๊ฑด ์ ์šฉ

// ์‹๋ณ„์ž ์˜์—ญ(์ขŒ)์—์„œ C ํƒ€์ž…์€ boolean์œผ๋กœ ์ œํ•œ(์ œ์•ฝ ์กฐ๊ฑด)
// ํƒ€์ž… ๊ตฌํ˜„ ์˜์—ญ(์šฐ)์—์„œ C ํƒ€์ž…์ด true๋ฉด T ๋ฐ˜ํ™˜, false๋ฉด F ๋ฐ˜ํ™˜(์กฐ๊ฑด๋ถ€ ํƒ€์ž…)
type If<C extends boolean, T, F> = C extends true ? T : F;
type A = If<true, 'a', 'b'>; // 'a'
type B = If<false, 'a', 'b'>; // 'b'

// Exclude ๋‚ด์žฅ ์œ ํ‹ธ ํƒ€์ž…์„ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ. T๊ฐ€ U์— ํ• ๋‹น๋  ์ˆ˜ ์—†๋Š” ์š”์†Œ๋งŒ ๋ฐ˜ํ™˜
// (T0) T์˜ 'a'๋Š” U์˜ 'a'์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ œ์™ธ(never)
// (T1) T์˜ 'a','b'๋Š” U์˜ 'a','b'์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ œ์™ธ(never)
type MyExclude<T, U> = T extends U ? never : T;
type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // "b" | "c"
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>; // "c"

 

๋ฐฐ์—ด๋กœ๋œ ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ T[number] ๋กœ ์ ‘๊ทผํ•˜๋ฉด(์ธ๋ฑ์Šค ํƒ€์ž…) T ๋ฐฐ์—ด์— ์žˆ๋Š” ๋ชจ๋“  ์š”์†Œ๋“ค์˜ ์œ ๋‹ˆ์˜จ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. T๊ฐ€ [1, 2, 3] ์ด๋ผ๋ฉด T[number]๋Š” 1 | 2 | 3์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ธ๋ฑ์Šค ํƒ€์ž…์ด ๋ฐ˜ํ™˜ํ•œ ์œ ๋‹ˆ์˜จ์— ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๊ฐ’์ด ๋ฐฐ์—ด ์š”์†Œ์— ํฌํ•จ๋๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ex) U extends T[number]

// ํƒ€์ž… ์‹œ์Šคํ…œ์— Array.includes๋ฅผ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ
// ์‹๋ณ„์ž ์˜์—ญ(์ขŒ)์—์„œ T ํƒ€์ž…์„ ๋ฐฐ์—ด๋กœ ์ œํ•œ(์ œ์•ฝ ์กฐ๊ฑด)
// ํƒ€์ž… ๊ตฌํ˜„ ์˜์—ญ(์šฐ)์—์„œ U ํƒ€์ž…์ด T ๋ฐฐ์—ด ์š”์†Œ์— ํฌํ•จ๋œ๋‹ค๋ฉด true ๋ฐ˜ํ™˜, ์•„๋‹ˆ๋ฉด false ๋ฐ˜ํ™˜(์กฐ๊ฑด๋ถ€ ํƒ€์ž…)
type Includes<T extends unknown[], U> = U extends T[number] ? true : false;
type HasD = Includes<['A', 'B', 'C'], 'D'>; // false

 

T[number] ์ธ๋ฑ์Šค ํƒ€์ž…๊ณผ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์ด์šฉํ•ด ๋ฐฐ์—ด์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋นˆ ๋ฐฐ์—ด์ด ์˜จ๋‹ค๋ฉด T[0]์€ ๋™์ž‘ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์œผ๋กœ ๋นˆ ๋ฐฐ์—ด์ธ์ง€ ํ™•์ธํ•˜๊ณ  ๋ถ„๊ธฐ๋ฅผ ๋‚˜๋ˆ ์•ผ ํ•œ๋‹ค.

type First<T extends any[]> = T extends [] ? never : T[0];
type T0 = First<[1, 2, 3]> // 1
type T1 = First<[]> // never

 

๐Ÿ’ก never ํƒ€์ž…์€ ์–ด๋– ํ•œ ๊ฐ’๋„ ๊ฐ€์งˆ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—(๋นˆ ํƒ€์ž…) [] ์™€ never[]๋Š” ๋™์ผํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผํ•œ๋‹ค.

 

์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์ง€์ •ํ•  ๋•Œ ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์—ฐ์†ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

type MyType<T> =
  T extends string ? 'Str' :
    T extends number ? 'Num' :
      T extends boolean ? 'Boo' :
        T extends undefined ? 'Und' :
          T extends null ? 'Nul' :
            'Obj';

 

ํƒ€์ž… ์ถ”๋ก  | Infer


infer ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ํƒ€์ž… ์ถ”๋ก  ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. TS ์—”์ง„์ด ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ  ๊ทธ ๊ฐ’์€ ์ œ๋„ค๋ฆญ ํƒ€์ž…(U ๋“ฑ)์— ํ• ๋‹นํ•œ๋‹ค. infer ํ‚ค์›Œ๋“œ๋Š” ์ œ์•ฝ ์กฐ๊ฑด extends๊ฐ€ ์•„๋‹Œ, ์กฐ๊ฑด๋ถ€ ํƒ€์ž… extends ์ ˆ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ์ถ”๋ก ํ•œ ๋ณ€์ˆ˜๋Š”(์ œ๋„ค๋ฆญ ํƒ€์ž…) true ๋ถ„๊ธฐ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ๋ณธ ๋ฌธ๋ฒ• : T extends infer U ? U : TU๊ฐ€ ์ถ”๋ก  ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์ด๋ฉด U ๋ฆฌํ„ด, ์•„๋‹ˆ๋ฉด T ๋ฆฌํ„ด

 

// R์€ ์ œ๋„ค๋ฆญ์œผ๋กœ ๋ฐ›์€ number๊ฐ€ ๋˜๊ณ , number ํƒ€์ž…์€ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ R์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
// ์ฆ‰ MyType<number>๋Š” number๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ๋ณ€์ˆ˜ a์— number ํƒ€์ž…์ธ 888์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.
type MyType<T> = T extends infer R ? R : null;
const a: MyType<number> = 888; // OK
// ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” Paramters ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ตฌํ˜„ ์˜ˆ์‹œ
// T๊ฐ€ ํ•จ์ˆ˜์˜ ํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด false ๋ถ„๊ธฐ๋ฅผ ํƒ€์„œ never ๋ฐ˜ํ™˜(๋ฌด์‹œ)
// T๊ฐ€ ํ•จ์ˆ˜์˜ ํ˜•ํƒœ๋ผ๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์„ ๋งค๊ฐœ๋ณ€์ˆ˜ R์— ํ• ๋‹นํ•˜๊ณ  true ๋ถ„๊ธฐ์—์„œ R ๋ฐ˜ํ™˜
type Parameters<T> = T extends (...args: infer R) => any ? R : never;

type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]

declare function f1(arg: { a: number; b: string }): void;
type T2 = Parameters<typeof f1>; // [{a: number, b: string}]
type T3 = Parameters<string>; // Error! Type 'string' does not satisfy the constraint '(...args: any) => any'

 

Promise<Type>์€ Type์„ Promise๋กœ ๋ž˜ํ•‘ํ•œ ํƒ€์ž…์ด๋‹ค. ๋ฐ˜๋Œ€๋กœ Promise ๋“ฑ์œผ๋กœ ๋ž˜ํ•‘ํ•œ ํƒ€์ž…์„ ์–ป์–ด๋‚ด๋Š” ๊ฒƒ์„ ์–ธ๋ž˜ํ•‘์ด๋ผ๊ณ  ํ•œ๋‹ค(ํƒ€์ž… ๋‚ด๋ถ€์— ์žˆ๋Š” ํƒ€์ž…์„ ์–ป์–ด๋‚ด๋Š” ๊ฒƒ). ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์— ๋Œ€ํ•œ ํƒ€์ž… ์ถ”๋ก ์„ ์ œ๊ณตํ•œ๋‹ค. ๋”ฐ๋ผ์„œ Promise๋กœ ๊ฐ์‹ผ ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด R ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹น๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. R ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” true ๋ถ„๊ธฐ(... ? true : false)์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// TS 4.5์— ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ Awaited ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ๊ตฌํ˜„ ์˜ˆ์‹œ
// Promise ๋‚ด๋ถ€์— ์–ด๋–ค ํƒ€์ž…์ด ์˜ฌ์ง€ ๋ชจ๋ฅด๋ฏ€๋กœ infer ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด ํƒ€์ž… ์ถ”๋ก 
// T๊ฐ€ Promise๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด T๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ (false ๋ถ„๊ธฐ),
// T๊ฐ€ Promise๊ณ  Promise๋กœ ๊ฐ์‹ผ ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด R ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ถ”๋ก ํ•œ ํƒ€์ž… ํ• ๋‹น
// Promise<Promise<string>> ์ฒ˜๋Ÿผ ๋‹ค์ค‘ ๋ž˜ํ•‘ํ–ˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ true ๋ถ„๊ธฐ์—์„œ ์žฌ๊ท€์ ์œผ๋กœ ์–ธ๋ž˜ํ•‘
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;

type UnWrap1 = Awaited<string>; // string
type UnWrap2 = Awaited<Promise<number>>; // number

 

T๊ฐ€ ๋ฐฐ์—ด์ด๋ผ๋ฉด Rest ๋ฌธ๋ฒ•๊ณผ infer ํ‚ค์›Œ๋“œ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

// A, B ์ œ๋„ค๋ฆญ์„ ๋ฐ›์•„ ๋™์ผํ•œ ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
type Equal<A, B> = A extends B ? (B extends A ? true : false) : false;

type Includes<T extends unknown[], U> = T extends [infer F, ...infer R]
  ? Equal<F, U> extends true // F์™€ U๊ฐ€ ๊ฐ™๋‹ค๋ฉด true ๋ฐ˜ํ™˜, ์•„๋‹ˆ๋ฉด R๊ณผ U๋ฅผ ๋„˜๊ฒจ์„œ ์žฌ๊ท€ํ˜ธ์ถœ
    ? true
    : Includes<R, U>
  : false; // T๊ฐ€ ๋นˆ๋ฐฐ์—ด์ด๋ผ๋ฉด false ๋ฐ˜ํ™˜

type T0 = Includes<['a', 1, 2, 3], 'a'>; // true

 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•