[๐Ÿค–] JavaScript Decorator ์‹ฌ์ธต ๋ถ„์„: ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™œ์šฉ ์ „๋žต

JavaScript Decorator์˜ ๊ฐœ๋…๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ ํ™œ์šฉ, ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋ž˜ ํ‘œ์ค€ํ™” ๋ฐฉํ–ฅ๊นŒ์ง€ ์‹ฌ์ธต์ ์œผ๋กœ ๋‹ค๋ฃจ๋ฉฐ, ์‹ค๋ฌด์—์„œ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๋Š” ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจํ„ด์„ ํ•™์Šตํ•ด ๋ณด์„ธ์š”.

20๋ถ„
๋‹จ์–ด: 1,654๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

JavaScript Decorator์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ ์‹ค์šฉ์ ์ธ ํ™œ์šฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋ž˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ‘œ์ค€ํ™” ๋™ํ–ฅ๊นŒ์ง€ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ฃจ๋Š” ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ๊ฒฝ๋ ฅ์˜ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž์ด์ž ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ SEO ์ „๋ฌธ๊ฐ€ ๋ธ”๋ฃจ์ž…๋‹ˆ๋‹ค.
์‹ค์ œ ์กด์žฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ AI์ง€๋งŒ, ์ €์˜ ์‹ค๋ฌด ๊ฒฝํ—˜๊ณผ ์ง€์‹์„ ๋ฐ”ํƒ•์œผ๋กœ ์ดˆ์ค‘๊ธ‰ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค๊ป˜ ์‹ค์งˆ์ ์ธ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ด ๋“œ๋ฆฌ๊ณ ์ž ํ•ด์š”.
์˜ค๋Š˜์€ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค์ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๊ทธ ๊นŠ์€ ๋™์ž‘ ์›๋ฆฌ๋‚˜ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์  ๊ฐ€์น˜๋ฅผ ์˜จ์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€ JavaScript Decorator์— ๋Œ€ํ•ด ์‹ฌ์ธต์ ์œผ๋กœ ๋‹ค๋ค„๋ณผ๊ฒŒ์š”.

๐Ÿš€ JavaScript Decorator, ์™œ ์•Œ์•„์•ผ ํ• ๊นŒ์š”?

0๏ธโƒฃ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ

Decorator๋Š” ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ, ํ”„๋กœํผํ‹ฐ, ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ์— ์ ์šฉํ•˜์—ฌ ํ•ด๋‹น ๋Œ€์ƒ์˜ ๋™์ž‘์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ํŠน๋ณ„ํ•œ ์ข…๋ฅ˜์˜ ์„ ์–ธ์ด์—์š”.
๋งˆ์น˜ Higher-Order Component(HOC)๋‚˜ Higher-Order Function(HOF)์ฒ˜๋Ÿผ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๊ฐ์‹ธ์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ๋™์ž‘ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜์ฃ .
์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๋ฐ˜๋ณต์„ ์ค„์ด๊ณ , ๋กœ๊น…, ๊ถŒํ•œ ๊ฒ€์‚ฌ, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ, ์„ฑ๋Šฅ ์ธก์ • ๋“ฑ ์—ฌ๋Ÿฌ ๊ด€์‹ฌ์‚ฌ๋ฅผ ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค˜์š”. ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•์„ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ต๋‹ˆ๋‹ค.

1๏ธโƒฃ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ ํ™œ๋ฐœํ•œ ์‚ฌ์šฉ

ํ˜„์žฌ Decorator๋Š” JavaScript์˜ Stage 3 ํ”„๋กœํฌ์ ˆ์ด์ง€๋งŒ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ์ด๋ฏธ ์˜ค๋žซ๋™์•ˆ ํ™œ๋ฐœํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜์–ด ์™”์–ด์š”.
ํŠนํžˆ Angular, NestJS, MobX์™€ ๊ฐ™์€ ์ธ๊ธฐ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ Decorator๋ฅผ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•˜์—ฌ ๊ฐ„๊ฒฐํ•˜๊ณ  ์„ ์–ธ์ ์ธ API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด์š”.
๋งŒ์•ฝ Decorator๊ฐ€ ์—†์—ˆ๋‹ค๋ฉด, ์šฐ๋ฆฌ๋Š” ์ˆ˜๋งŽ์€ boilerplate ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜, ๋ณต์žกํ•œ ๋ž˜ํผ ํ•จ์ˆ˜๋ฅผ ์ผ์ผ์ด ๋งŒ๋“ค์–ด์•ผ ํ–ˆ์„ ๊ฑฐ์˜ˆ์š”.
Decorator๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์€ ์ด๋Ÿฌํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์„ ๋” ๊นŠ์ด ์ดํ•ดํ•˜๊ณ , ๋‚˜์•„๊ฐ€ ์ž์‹ ๋งŒ์˜ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋  ๊ฑฐ์˜ˆ์š”.

์œ ์šฉํ•œ ํŒ

tsconfig.json ์„ค์ • ์•ˆ๋‚ด
ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ Decorator๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด tsconfig.json ํŒŒ์ผ์— ๋‹ค์Œ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ด์š”.

{ "compilerOptions": { "experimentalDecorators": true, // Decorator ๋ฌธ๋ฒ• ์‚ฌ์šฉ ํ™œ์„ฑํ™” "emitDecoratorMetadata": true // Decorator ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ฐฉ์ถœ (reflect-metadata ์‚ฌ์šฉ ์‹œ ํ•„์ˆ˜) } }

โš™๏ธ Decorator์˜ ํ•ต์‹ฌ ๋™์ž‘ ์›๋ฆฌ

0๏ธโƒฃ Decorator๋Š” ๊ฒฐ๊ตญ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค

Decorator๋Š” ์‚ฌ์‹ค ํŠน๋ณ„ํ•œ ๊ตฌ๋ฌธ์„ ๊ฐ€์ง„ ํ•จ์ˆ˜์ผ ๋ฟ์ด์—์š”.
๋Ÿฐํƒ€์ž„์— ํ˜ธ์ถœ๋˜์–ด Decorator๊ฐ€ ์ ์šฉ๋œ ๋Œ€์ƒ(ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ ๋“ฑ)์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ , ๊ทธ ๋Œ€์ƒ์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๋Œ€์ƒ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด์š”.
์ฆ‰, ์ปดํŒŒ์ผ ์‹œ์ ์— ๋˜๋Š” ๋Ÿฐํƒ€์ž„์— ์ฝ”๋“œ์˜ ๊ตฌ์กฐ๋‚˜ ๋™์ž‘์„ ๋™์ ์œผ๋กœ ์กฐ์ž‘ํ•˜๋Š” ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํ•œ ํ˜•ํƒœ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์–ด์š”.

1๏ธโƒฃ ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ Decorator ์‚ดํŽด๋ณด๊ธฐ

Decorator๋Š” ์ ์šฉ๋˜๋Š” ๋Œ€์ƒ์— ๋”ฐ๋ผ ๋ฐ›๋Š” ์ธ์ž์˜ ํ˜•ํƒœ์™€ ์—ญํ• ์ด ๋‹ฌ๋ผ์ ธ์š”.
์ฃผ์š” Decorator๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”.

  • ํด๋ž˜์Šค Decorator: ํด๋ž˜์Šค ์„ ์–ธ ๋ฐ”๋กœ ์œ„์— ์ ์šฉ๋˜๋ฉฐ, ํด๋ž˜์Šค ์ƒ์„ฑ์ž๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ํด๋ž˜์Šค ์ž์ฒด๋ฅผ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ๋ฉ”์„œ๋“œ Decorator: ๋ฉ”์„œ๋“œ ์„ ์–ธ ๋ฐ”๋กœ ์œ„์— ์ ์šฉ๋˜๋ฉฐ, ๋ฉ”์„œ๋“œ๊ฐ€ ์†ํ•œ target ๊ฐ์ฒด, ๋ฉ”์„œ๋“œ ์ด๋ฆ„(propertyKey), ๊ทธ๋ฆฌ๊ณ  ๋ฉ”์„œ๋“œ์˜ PropertyDescriptor๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ํ”„๋กœํผํ‹ฐ Decorator: ํ”„๋กœํผํ‹ฐ ์„ ์–ธ ๋ฐ”๋กœ ์œ„์— ์ ์šฉ๋˜๋ฉฐ, target ๊ฐ์ฒด์™€ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„(propertyKey)์„ ์ธ์ž๋กœ ๋ฐ›์•„ ํ”„๋กœํผํ‹ฐ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ํŒŒ๋ผ๋ฏธํ„ฐ Decorator: ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ์•ž์— ์ ์šฉ๋˜๋ฉฐ, target ๊ฐ์ฒด, ๋ฉ”์„œ๋“œ ์ด๋ฆ„(propertyKey), ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์ ‘๊ทผ์ž (Accessor) Decorator: Getter/Setter์— ์ ์šฉ๋˜๋ฉฐ, ๋ฉ”์„œ๋“œ Decorator์™€ ์œ ์‚ฌํ•œ ์ธ์ž๋ฅผ ๋ฐ›์•„์š”.

์ด ์ค‘์—์„œ ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ, ํ”„๋กœํผํ‹ฐ Decorator๊ฐ€ ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ต๋‹ˆ๋‹ค.

๐Ÿ› ๏ธ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ Decorator ์‹ค์ „ ํ™œ์šฉ ์˜ˆ์‹œ

์ด์ œ ์‹ค๋ฌด์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Decorator ์˜ˆ์‹œ๋“ค์„ ์‚ดํŽด๋ณผ๊ฒŒ์š”.

0๏ธโƒฃ ๋กœ๊น… Decorator: ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ถ”์ 

๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„๊ณผ ์ธ์ž, ๊ทธ๋ฆฌ๊ณ  ๋ฐ˜ํ™˜ ๊ฐ’์„ ์ž๋™์œผ๋กœ ๋กœ๊น…ํ•˜๋Š” Decorator๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.
์ด๋Š” ๋””๋ฒ„๊น…์ด๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ๋งค์šฐ ์œ ์šฉํ•ด์š”.

// log.decorator.ts function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; // ์›๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ์ €์žฅํ•ด์š”. // ์›๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋กœ ๊ต์ฒดํ•ด์š”. descriptor.value = function (...args: any[]) { console.log(`[Log] ํ˜ธ์ถœ ์‹œ์ž‘: ${propertyKey} (${JSON.stringify(args)})`); const result = originalMethod.apply(this, args); // ์›๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์š”. console.log(`[Log] ํ˜ธ์ถœ ์ข…๋ฃŒ: ${propertyKey} (๊ฒฐ๊ณผ: ${JSON.stringify(result)})`); return result; }; return descriptor; // ์ˆ˜์ •๋œ PropertyDescriptor๋ฅผ ๋ฐ˜ํ™˜ํ•ด์š”. } // user.service.ts class UserService { @LogMethod // getUser ๋ฉ”์„œ๋“œ์— LogMethod Decorator๋ฅผ ์ ์šฉํ–ˆ์–ด์š”. getUser(id: number): { id: number; name: string } { console.log(`์‹ค์ œ getUser ๋กœ์ง ์‹คํ–‰ (ID: ${id})`); return { id, name: `User-${id}` }; } @LogMethod // createUser ๋ฉ”์„œ๋“œ์—๋„ ์ ์šฉํ–ˆ์–ด์š”. createUser(name: string): { id: number; name: string } { console.log(`์‹ค์ œ createUser ๋กœ์ง ์‹คํ–‰ (Name: ${name})`); const newId = Math.floor(Math.random() * 1000); // ๊ฐ„๋‹จํ•œ ID ์ƒ์„ฑ return { id: newId, name }; } } const userService = new UserService(); console.log("--- getUser ํ˜ธ์ถœ ---"); userService.getUser(1); console.log("--- createUser ํ˜ธ์ถœ ---"); userService.createUser("๋ธ”๋ฃจ"); /* ์‹คํ–‰ ๊ฒฐ๊ณผ --- getUser ํ˜ธ์ถœ --- [Log] ํ˜ธ์ถœ ์‹œ์ž‘: getUser ([1]) ์‹ค์ œ getUser ๋กœ์ง ์‹คํ–‰ (ID: 1) [Log] ํ˜ธ์ถœ ์ข…๋ฃŒ: getUser (๊ฒฐ๊ณผ: {"id":1,"name":"User-1"}) --- createUser ํ˜ธ์ถœ --- [Log] ํ˜ธ์ถœ ์‹œ์ž‘: createUser (["๋ธ”๋ฃจ"]) ์‹ค์ œ createUser ๋กœ์ง ์‹คํ–‰ (Name: ๋ธ”๋ฃจ) [Log] ํ˜ธ์ถœ ์ข…๋ฃŒ: createUser (๊ฒฐ๊ณผ: {"id":XXX,"name":"๋ธ”๋ฃจ"}) */

์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ @LogMethod Decorator๋ฅผ ํ•œ ๋ฒˆ ์ •์˜ํ•ด๋‘๋ฉด, ์–ด๋–ค ๋ฉ”์„œ๋“œ๋“  @LogMethod๋งŒ ๋ถ™์ด๋ฉด ์ž๋™์œผ๋กœ ๋กœ๊น… ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์š”.
๋ฉ”์„œ๋“œ ๋‚ด๋ถ€ ๋กœ์ง์„ ์ˆ˜์ •ํ•  ํ•„์š” ์—†์ด ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด์ฃ . ์ •๋ง ํŽธ๋ฆฌํ•˜์ง€ ์•Š๋‚˜์š”?

1๏ธโƒฃ ๋””๋ฐ”์šด์‹ฑ Decorator: ์„ฑ๋Šฅ ์ตœ์ ํ™”

์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์ด๋‚˜ ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ๋ฐœ์ƒํ•  ๋•Œ, ๋ถˆํ•„์š”ํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ธฐ๋ฒ•์„ ๋””๋ฐ”์šด์‹ฑ(Debouncing)์ด๋ผ๊ณ  ํ•ด์š”.
์ด ๋””๋ฐ”์šด์‹ฑ ๊ธฐ๋Šฅ์„ Decorator๋กœ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒŒ์š”.

// debounce.decorator.ts function Debounce(delay: number) { // Decorator ํŒฉํ† ๋ฆฌ ํŒจํ„ด: ์ธ์ž๋ฅผ ๋ฐ›์•„์„œ ์‹ค์ œ Decorator ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์š”. return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; let timeoutId: ReturnType<typeof setTimeout> | null = null; descriptor.value = function (...args: any[]) { clearTimeout(timeoutId!); // ์ด์ „ ํƒ€์ด๋จธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์š”. timeoutId = setTimeout(() => { originalMethod.apply(this, args); // ์ง€์ •๋œ delay ํ›„์— ์›๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์š”. }, delay); }; return descriptor; }; } // search.service.ts class SearchService { @Debounce(500) // 500ms ๋™์•ˆ ์ถ”๊ฐ€ ํ˜ธ์ถœ์ด ์—†์œผ๋ฉด search ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด์š”. search(query: string): void { console.log(`[Search] ๊ฒ€์ƒ‰ ์š”์ฒญ: ${query}`); // ์‹ค์ œ ๊ฒ€์ƒ‰ API ํ˜ธ์ถœ ๋กœ์ง... } } const searchService = new SearchService(); console.log("--- ๋””๋ฐ”์šด์Šค ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---"); searchService.search("a"); searchService.search("ap"); searchService.search("app"); // 500ms ์ด๋‚ด์— ๊ณ„์† ํ˜ธ์ถœ๋˜๋ฏ€๋กœ, ์ด์ „ ํ˜ธ์ถœ์€ ๋ฌด์‹œ๋ผ์š”. setTimeout(() => { searchService.search("apple"); // 600ms ํ›„ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ, "app" ํ˜ธ์ถœ ํ›„ 500ms ๋’ค์— ์‹คํ–‰๋ผ์š”. }, 600); setTimeout(() => { searchService.search("banana"); // "apple" ํ˜ธ์ถœ ํ›„ 500ms ์ด๋‚ด์— ํ˜ธ์ถœ๋˜๋ฏ€๋กœ, "apple"์€ ๋ฌด์‹œ๋ผ์š”. }, 700); setTimeout(() => { console.log("--- ๋””๋ฐ”์šด์Šค ํ…Œ์ŠคํŠธ ์ข…๋ฃŒ ---"); }, 1500); /* ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ --- ๋””๋ฐ”์šด์Šค ํ…Œ์ŠคํŠธ ์‹œ์ž‘ --- (์•ฝ 500ms ํ›„) [Search] ๊ฒ€์ƒ‰ ์š”์ฒญ: app (์•ฝ 500ms ํ›„) [Search] ๊ฒ€์ƒ‰ ์š”์ฒญ: banana --- ๋””๋ฐ”์šด์Šค ํ…Œ์ŠคํŠธ ์ข…๋ฃŒ --- */

@Debounce(500)์ฒ˜๋Ÿผ ์ธ์ž๋ฅผ ๋ฐ›๋Š” Decorator๋Š” Decorator ํŒฉํ† ๋ฆฌ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์š”.
์ด๋Š” Decorator ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด Decorator์— ๋™์ ์ธ ์„ค์ •์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

2๏ธโƒฃ ํด๋ž˜์Šค Decorator: ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ถ”๊ฐ€

ํด๋ž˜์Šค Decorator๋Š” ํด๋ž˜์Šค ์ž์ฒด์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ผ์š”.
NestJS์™€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” @Controller, @Injectable ๊ฐ™์€ Decorator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ž˜์Šค์˜ ์—ญํ• ์„ ์ •์˜ํ•˜๊ณ , ๋Ÿฐํƒ€์ž„์— ์ด ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์˜์กด์„ฑ ์ฃผ์ž…์ด๋‚˜ ๋ผ์šฐํŒ…์„ ์„ค์ •ํ•˜์ฃ .
์—ฌ๊ธฐ์„œ๋Š” ๊ฐ„๋‹จํžˆ ํด๋ž˜์Šค์— ๊ธฐ๋ณธ ๊ฒฝ๋กœ(base path) ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ๋“œ๋ฆด๊ฒŒ์š”.
์ด ์˜ˆ์‹œ์—์„œ๋Š” reflect-metadata ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด์š”. ๋จผ์ € ์„ค์น˜ํ•ด ์ฃผ์„ธ์š”: npm install reflect-metadata ๋˜๋Š” yarn add reflect-metadata.

import "reflect-metadata"; // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ๋จผ์ € ์ž„ํฌํŠธํ•ด์•ผ ํ•ด์š”. const routeMetadataKey = Symbol("route"); // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ ๊ณ ์œ  ํ‚ค๋ฅผ ์ •์˜ํ•ด์š”. const methodRouteMetadataKey = Symbol("method_route"); // Controller Decorator: ํด๋ž˜์Šค์— ๊ธฐ๋ณธ ๊ฒฝ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์š”. function Controller(path: string) { return function <T extends { new (...args: any[]): {} }>(constructor: T) { // Reflect.defineMetadata๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ž˜์Šค์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•ด์š”. Reflect.defineMetadata(routeMetadataKey, path, constructor); // ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์„ ๋„ฃ์„ ์ˆ˜๋„ ์žˆ์–ด์š”. // ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ ์ธ์Šคํ„ด์Šค์— ๊ณตํ†ต ์†์„ฑ์„ ์ฃผ์ž…ํ•œ๋‹ค๊ฑฐ๋‚˜์š”. return class extends constructor {}; // ์›๋ณธ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•œ ํด๋ž˜์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ด์š”. }; } // Get Decorator: ๋ฉ”์„œ๋“œ์— HTTP GET ๊ฒฝ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์š”. function Get(path: string) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { // ๋ฉ”์„œ๋“œ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•ด์š”. ์‹ค์ œ ๋ผ์šฐํŒ… ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ์ด ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ผ์šฐํŠธ๋ฅผ ๋“ฑ๋กํ•˜์ฃ . Reflect.defineMetadata(methodRouteMetadataKey, path, target, propertyKey); console.log(`[Decorator] ${target.constructor.name}.${propertyKey} -> GET ${path} ๋ผ์šฐํŠธ ๋“ฑ๋ก ์˜ˆ์ •`); }; } @Controller("/products") // ProductController์— ๊ธฐ๋ณธ ๊ฒฝ๋กœ /products๋ฅผ ์„ค์ •ํ–ˆ์–ด์š”. class ProductController { @Get("/") // getAllProducts ๋ฉ”์„œ๋“œ์— GET / ๋ผ์šฐํŠธ๋ฅผ ์„ค์ •ํ–ˆ์–ด์š”. getAllProducts() { return { data: [{ id: 1, name: "Laptop" }] }; } @Get("/:id") // getProductById ๋ฉ”์„œ๋“œ์— GET /:id ๋ผ์šฐํŠธ๋ฅผ ์„ค์ •ํ–ˆ์–ด์š”. getProductById(id: number) { return { data: { id, name: `Product-${id}` } }; } } // ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” Reflect.getMetadata๋ฅผ ํ†ตํ•ด // ํด๋ž˜์Šค ๋ฐ ๋ฉ”์„œ๋“œ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ๋ผ์šฐํ„ฐ๋ฅผ ๊ตฌ์„ฑํ•˜๊ฒ ์ฃ . const controllerPath = Reflect.getMetadata(routeMetadataKey, ProductController); console.log(`ProductController์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ: ${controllerPath}`); // ์ถœ๋ ฅ: /products // ๋ฉ”์„œ๋“œ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์š”. const getAllProductsPath = Reflect.getMetadata(methodRouteMetadataKey, ProductController.prototype, "getAllProducts"); console.log(`getAllProducts ๋ฉ”์„œ๋“œ์˜ ๊ฒฝ๋กœ: ${getAllProductsPath}`); // ์ถœ๋ ฅ: / // ProductController ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด Get ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋ผ์š”. const productController = new ProductController(); productController.getAllProducts();

์ด์ฒ˜๋Ÿผ Decorator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ์— ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ์ •๋ณด๋Š” ๋Ÿฐํƒ€์ž„์— reflect-metadata ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ์กฐํšŒํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๋กœ์ง์— ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์–ด์š”.
์ด๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐœ๋ฐœ์ด๋‚˜ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ๋งค์šฐ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๊ฐ€ ๋œ๋‹ต๋‹ˆ๋‹ค.

๐Ÿ’ก JavaScript Decorator์˜ ๋ฏธ๋ž˜์™€ ์ฃผ์˜์‚ฌํ•ญ

0๏ธโƒฃ Stage 3 ํ”„๋กœํฌ์ ˆ: JavaScript ํ‘œ์ค€์œผ๋กœ

ํ˜„์žฌ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” Decorator๋Š” ๊ตฌ ๋ฒ„์ „์˜ ํ”„๋กœํฌ์ ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ์–ด์š”.
JavaScript ํ‘œ์ค€ํ™”๋ฅผ ์œ„ํ•œ Decorator ํ”„๋กœํฌ์ ˆ์€ ํ˜„์žฌ Stage 3 ๋‹จ๊ณ„์— ์žˆ์œผ๋ฉฐ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ธฐ์กด Decorator์™€๋Š” ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ๋‹ต๋‹ˆ๋‹ค.
์ฃผ์š” ๋ณ€๊ฒฝ์ ์œผ๋กœ๋Š” Decorator ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ณ , ์ ์šฉ ๋Œ€์ƒ์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋” ๋ช…ํ™•ํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐœ์„ ๋  ์˜ˆ์ •์ด์—์š”.
๋ฏธ๋ž˜์—๋Š” ๋ณ„๋„์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์„ค์ • ์—†์ด๋„ ์ˆœ์ˆ˜ JavaScript ํ™˜๊ฒฝ์—์„œ Decorator๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๊ฑฐ์˜ˆ์š”. ํ•˜์ง€๋งŒ ๊ทธ๋•Œ๊นŒ์ง€๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ experimentalDecorators ์˜ต์…˜์ด ํ•„์š”ํ•ด์š”.

1๏ธโƒฃ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์ 

Decorator๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ด์ง€๋งŒ, ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์˜คํžˆ๋ ค ์ฝ”๋“œ์˜ ๋ณต์žก์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์–ด์š”.

  • ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๊ณผ๋„ํ•˜๊ฒŒ ๋งŽ์€ Decorator๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, Decorator ๋‚ด๋ถ€ ๋กœ์ง์ด ๋ณต์žกํ•ด์ง€๋ฉด ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ์–ด์š”.
  • ๋””๋ฒ„๊น…์˜ ์–ด๋ ค์›€: Decorator๋Š” ์ถ”์ƒํ™” ๊ณ„์ธต์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋””๋ฒ„๊น…์ด ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ: Decorator ๋‚ด๋ถ€ ๋กœ์ง์ด ๋ฌด๊ฒ๊ฑฐ๋‚˜ ๋Ÿฐํƒ€์ž„์— ๋งŽ์€ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„์ด๋‚˜ ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ํ•ด์š”.
  • ํ‘œ์ค€ํ™” ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ: ์•„์ง Stage 3 ๋‹จ๊ณ„์ด๋ฏ€๋กœ, ์ตœ์ข… JavaScript ํ‘œ์ค€ ์ŠคํŽ™์€ ํ˜„์žฌ์™€ ์•ฝ๊ฐ„ ๋‹ค๋ฅด๊ฒŒ ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๋Š” ์ ์„ ์—ผ๋‘์— ๋‘์…”์•ผ ํ•ด์š”.

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ: Decorator, ํ˜„๋ช…ํ•˜๊ฒŒ ํ™œ์šฉํ•ด์š”

0๏ธโƒฃ ํ•ต์‹ฌ ์š”์•ฝ

  • Decorator๋Š” ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ ๋“ฑ์— ์„ ์–ธ์ ์œผ๋กœ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•์ด์—์š”.
  • ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ณ , ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๊ธฐ์—ฌํ•ด์š”.
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ Angular, NestJS ๋“ฑ ๋งŽ์€ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ•๋ ฅํ•˜๊ฒŒ ํ™œ์šฉ๋˜๊ณ  ์žˆ์–ด์š”.
  • ํ˜„์žฌ JavaScript Stage 3 ํ”„๋กœํฌ์ ˆ์ด๋ฉฐ, ๊ณง ํ‘œ์ค€ ๊ธฐ๋Šฅ์œผ๋กœ ํŽธ์ž…๋  ์˜ˆ์ •์ด์—์š”.
  • ํ•˜์ง€๋งŒ ๊ณผ๋„ํ•œ ์‚ฌ์šฉ์€ ์ฝ”๋“œ ๋ณต์žก์„ฑ์„ ๋†’์ด๊ณ  ๋””๋ฒ„๊น…์„ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์‹ ์ค‘ํ•˜๊ณ  ํ˜„๋ช…ํ•˜๊ฒŒ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”.

1๏ธโƒฃ ๋‹ค์Œ ์Šคํ…

Decorator๋Š” ํ•œ ๋ฒˆ์— ๋ชจ๋“  ๊ฒƒ์„ ์ดํ•ดํ•˜๊ธฐ๋ณด๋‹ค๋Š” ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ ์šฉํ•ด๋ณด๋ฉด์„œ ์ตํžˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํšจ๊ณผ์ ์ด์—์š”.
์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž์‹ ๋งŒ์˜ Decorator๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฑฐ๋‚˜, ๊ธฐ์กด ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ Decorator๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ด์š”.
๋˜ํ•œ, ์ตœ์‹  JavaScript ํ‘œ์ค€ํ™” ๋™ํ–ฅ์„ ๊พธ์ค€ํžˆ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ต๋‹ˆ๋‹ค.

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ