CHAPTER 2. Diving into TypeScript

Types as in TypeScript

TypeScript์—์„œ ์œ ํ˜• ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๊ตฌ๋ฌธ์€ ๋‹ค์†Œ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

let variable: type;

์ด ์œ ํ˜•๋“ค์€ ๊ธฐ์–ตํ•˜๊ธฐ ์‰ฝ๋‹ค.

const poneyNumber: number = 0;
const poneyName: string = 'Rainbow Dash';

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ์œ ํ˜•์€ TS ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์œ ํ˜•์„ ์ถ”์ธกํ•  ์ˆ˜ ์žˆ๊ธฐ๋•Œ๋ฌธ์— ์„ ํƒ์ ์ด๋‹ค. ์œ ํ˜•์€ ๋‹ค์Œ์˜ Pony ํด๋ž˜์Šค์™€ ๊ฐ™์ด ๋‹น์‹ ์˜ ์•ฑ์—์„œ ๋‚˜์˜ฌ์ˆ˜ ์žˆ๋‹ค.

const pony: Pony = new Pony();

TypeScript๋Š” ์ผ๋ถ€ ์–ธ์–ด์—์„œ "generic"์œผ๋กœ ๋ถ€๋ฅด๋Š”๊ฒƒ์„ ์ง€์›ํ•œ๋‹ค.

const ponies: Array<Pony> = [new Pony()];

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋ฐฐ์—ด์ด '<>' ์ด๋ผ๋Š” ์ผ๋ฐ˜์ ์ธ ํ‘œ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ponies๋ฅผ ๋‹ด์„์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋‹น์‹ ์€ ์ด๊ฒƒ์— ๋Œ€ํ•ด ์˜๋ฌธ์„ ๊ฐ€์งˆ์ง€ ๋ชจ๋ฅธ๋‹ค. ์œ ํ˜• ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ์‹ค์ˆ˜๋ฅผ ํŒ๋ณ„ํ•˜๋„๋ก ๋„์šธ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

ponies.push('hello'); // error TS2345
// Argument of type 'string' is not assignable to parameter of type 'Pony'.

๊ทธ๋ž˜์„œ, ๋งŒ์•ฝ ๋‹น์‹ ์ด ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ด๊ฒƒ์€ ๋‹น์‹ ์˜ ์ฝ”๋“œ๋Š” ์ œ๋Œ€๋กœ ์ž‘์„ฑํ• ์ˆ˜ ์—†๋‹ค๋Š”๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š”๊ฐ€? ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. ์™œ๋ƒํ•˜๋ฉด TS๋Š” 'any' ๋ผ๊ณ  ๋ถˆ๋ฆฌ์šฐ๋Š” ํŠน๋ณ„ํ•œ ์œ ํ˜•์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

let changing: any = 2;
changing = true; // no problem

์ด๊ฒƒ์€ ๋‹น์‹ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’์ด๋‚˜ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ ์ปจํ…์ธ ์—์„œ ๊ฐ€์ ธ์˜จ ๋ณ€์ˆ˜์˜ ์œ ํ˜•์„ ๋ชจ๋ฅผ ๊ฒฝ์šฐ์— ๊ต‰์žฅํžˆ ์œ ์šฉํ•  ๊ฒƒ์ด๋‹ค.

๋งŒ์•ฝ ๋‹น์‹ ์ด ๋ณ€์ˆ˜๋ฅผ number ๋˜๋Š” boolean ์œ ํ˜•์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„๊ฒฝ์šฐ์—๋Š” union ์„ ์ด์šฉํ•ด๋ผ.

let changing: number|boolean = 2;
changing = true; // no problem

Enums

TypeScript๋Š” "enum"์„ ์ œ๊ณตํ•œ๋‹ค. race๋Š” 'ready', 'started', 'done'์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

enum RaceStatus {Ready, Started, Done}
   const race = new Race();
   race.status = RaceStatus.Ready;

enum์€ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ์—ด๊ฑฐํ˜• ๋ณ€์ˆ˜์ด์ง€๋งŒ ์›ํ•˜๋Š” ์ˆซ์ž๋กœ ์…‹ํŒ…ํ•  ์ˆ˜ ์žˆ๋‹ค.

enum Medal {Gold = 1, Silver, Bronze}

Return types

๋‹น์‹ ์€ ํ•จ์ˆ˜์˜ ๋ฐ˜ํš๋˜๋Š” ์œ ํ˜•์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

function startRace(race: Race): Race {
  race.status = RaceStatus.Started;
  return race;
}

๋งŒ์•ฝ ํ•œ์ˆ˜๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด 'void'๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด์—ฌ์ฃผ๋ฉด ๋œ๋‹ค.

function startRace(race: Race): void {
  race.status = RaceStatus.Started;
}

Interfaces

์ดˆ๊ธฐ์— ๋งํ•˜์˜€๋“ฏ์ด, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋™์ ์ธ ์†์„ฑ์œผ๋กœ ๊ต‰์žฅํžˆ ์ข‹๋‹ค. ๊ทธ๋ž˜์„œ ํ•จ์ˆ˜๋Š” ์ผ์น˜ํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.

function addPointsToScore(player, points) {
  player.score += points;
}

ํ•จ์ˆ˜๋Š” score ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„ ์–ด๋– ํ•œ ๊ฐ์ฒด์—๋„ ์ ์šฉ๋  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. typeScript๋Š” ์–ด๋–ป๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ? ์ด๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ๋‹น์‹ ์€ object ์œ ํ˜•์„ ์ •์˜ํ•˜๋ฉด ๋œ๋‹ค.

function addPointsToScore(player: { score: number; }, points: number): void {
  player.score += points;
}

ํ•ด๋‹น ์ฝ”๋“œ๋Š” number ์œ ํ˜•์˜ score ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ์—ฌ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋‹น์‹ ์€ ์ด๊ฒƒ์„ interface๋กœ ์ •์˜ ํ•œ๊ฒƒ์ด๋‹ค.

interface HasScore {
  score: number;
}
   function addPointsToScore(player: HasScore, points: number): void {
   player.score += points;
   }

Optional arguments

๋˜ Javascript์˜ ๋‹ค๋ฅธ ๋Œ€์•ˆ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์„ ํƒ์ ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ๋‹น์‹ ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ˆ„๋ฝ์‹œํ‚ฌ์ˆ˜ ์žˆ๊ณ  ๊ทธ๊ฒƒ๋“ค์€ 'undefined'๋กœ ์ •์˜๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹น์‹ ์ด ์œ ํ˜•์ด ์ •ํ•ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ •์˜ํ–ˆ๋‹ค๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋‹น์‹ ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ˆ„๋ฝํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ํ‘œ๊ธฐํ•  ๊ฒƒ์ด๋‹ค.

addPointsToScore(player); // error TS2346
// Supplied parameters do not match any signature of call target.

ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์„ ํƒ์ ์ด๋ผ๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ๋‹น์‹ ์€ ํŒŒ๋ผ๋ฏธํ„ฐ ๋’ค์— '?' ๋ฅผ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ points ๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋‹ค.

function addPointsToScore(player: HasScore, points?: number): void {
  points = points || 0;
  player.score += points;
}

Functions as property

๋‹น์‹ ์€ ๋ณ€์ˆ˜ ๋Œ€์‹ ์— ํŠน์ •ํ•œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์•ผ๋งŒ ํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ์ˆ ํ•˜๋Š”๊ฒƒ์— ํฅ๋ฏธ๋ฅผ ๊ฐ€์งˆ ๊ฒƒ์ด๋‹ค.

function startRunning(pony) {
  pony.run(10);
}

interface ์„ ์–ธ์€ ์ด๋ ‡๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

interface CanRun {
  run(meters: number): void;
}
   function startRunning(pony: CanRun): void {
      pony.run(10);
   }
   const pony = {
      run: (meters) => logger.log(`pony runs ${meters}m`)
   };
   startRunning(pony);

Classes

ํด๋ž˜์Šค๋Š” interface๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. Pony ํด๋ž˜์Šค๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ๋Œ€๋กœ ๋™์ž‘ํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

class Pony implements CanRun {
  run(meters) {
   logger.log(`pony runs ${meters}m`);
  }
}

์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํ•ด๋‹น ํด๋ž˜์Šค์— num ์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ฐ•์ œ๋กœ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์•ˆ ์ข‹๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด number ๋Œ€์‹ ์— string์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์—๋Ÿฌ๋ฅผ ๋‚˜ํƒ€๋‚ผ ๊ฒƒ์ด๋‹ค.

class IllegalPony implements CanRun {
  run(meters: string) {
   console.log(`pony runs ${meters}m`);
  }
}
// error TS2420: Class 'IllegalPony' incorrectly implements interface 'CanRun'.
// Types of property 'run' are incompatible.

๋งŒ์•ฝ ์›ํ•œ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

class HungryPony implements CanRun, CanEat {
  run(meters) {
   logger.log(`pony runs ${meters}m`);
  }
  eat() {
   logger.log(`pony eats`);
  }
}

interface๋Š” ๋˜๋‹ค๋ฅธ interface๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

interface Animal extends CanRun, CanEat {}
class Pony implements Animal {
  // ...
}

๋‹น์‹ ์ด TypeScript์—์„œ ํด๋ž˜์Šค๋ฅผ ์ •์˜ ํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋ฉด ๋‹น์‹ ์€ ํด๋ž˜์Šค์— ํ•จ์ˆ˜์™€ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์งˆ์ˆ˜ ์žˆ๋‹ค. ๋‹น์‹ ์€ ์ด๊ฒƒ์ด TypeScript์—์„œ๋งŒ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์ด์ง€, ES6 ํ‘œ์ค€์ด ์•„๋‹Œ๊ฒƒ์„ ์ธ์ง€ํ•ด์•ผ ํ•  ์ง€๋„ ๋ชจ๋ฅธ๋‹ค.

class SpeedyPony {
  speed: number = 10;
  run() {
   logger.log(`pony runs at ${this.speed}m/s`);
  }
}

๋ชจ๋“ ๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ public์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€๋งŒ ๋‹น์‹ ์€ private์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜๋‚˜ ๋ณ€์ˆ˜๋ฅผ ์ˆจ๊ธธ ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ์— private๋˜๋Š” public์„ ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด ์ด๊ฒƒ์€ private , public ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐ์— ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์š”์•ฝ์ด๋‹ค.

class NamedPony {
  constructor(public name: string, private speed: number) {
  }
  run() {
   logger.log(`pony runs at ${this.speed}m/s`);
  }
}
   const pony = new NamedPony('Rainbow Dash', 10);
   // defines a public property name with 'Rainbow Dash'
   // and a private one speed with 10

๋” ์ž์„ธํ•œ ์ •๋ณด๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

class NamedPonyWithoutShortcut {
  public name: string;
  private speed: number;
  constructor(name: string, speed: number) {
   this.name = name;
   this.speed = speed;
  }
  run() {
   logger.log(`pony runs at ${this.speed}m/s`);
  }
}

Working with other libraries

JS๋กœ ์ž‘์„ฑ๋œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ž‘์—… ํ•  ๋Œ€, ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์–ด๋–ค ์œ ํ˜•์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์˜ˆ์ƒ๋˜๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ์šด๋ช…์œผ๋กœ ์ƒ๊ฐํ• ์ง€ ๋ชจ๋ฅธ๋‹ค. TypeScript์˜ ๊ฐ•์ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฉค๋ฒ„๋Š” ์ผ๋ฐ˜์ ์ธ ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜ํ•ด ๋…ธ์ถœ ๋œ ์œ ํ˜•๊ณผ ํ•จ์ˆ˜๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜์˜€๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ์—๋Š” ํŠน๋ณ„ํ•œ ํ™•์žฅ์ž(".d.ts")๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋“ค์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํ•จ์ˆ˜์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ํฌํ•จํ•œ๋‹ค. ์ด ํŒŒ์ผ์„ ์ฐพ๋Š” ์ข‹์€ ์žฅ์†Œ๋Š” DefinitelyTyped ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋งŒ์•ฝ ๋‹น์‹ ์ด Angularjs 1 ๋ฒ„์ ผ์˜ ํ”„๋กœ์ ํŠธ์—์„œ ts ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค๋ฉด ๋‹น์‹ ์€ npm์„ ์‚ฌ์šฉํ•˜์—ฌ ์ ์ ˆํ•œ ํŒŒ์ผ์„ ๋‹ค์šด ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

npm install --save-dev @types/angular

๋˜๋Š” ์ˆ˜๋™์œผ๋กœ ๋‹ค์šด๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ•ด๋‹น ํŒŒ์ผ์€ ๋‹น์‹ ์˜ ์ฝ”๋“œ ์ƒ๋‹จ์— ํฌํ•จ๋  ๊ฒƒ์ด๊ณ , ์ปดํŒŒ์ผ ์ฒดํฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

/// <reference path="angular.d.ts" />
angular.module(10, []); // the module name should be a string
// so when I compile, I get:
// Argument of type 'number' is not assignable to parameter of type 'string'.

"reference path="angular.d.ts" ๋Š” TS์— ์˜ํ•ด ํŠน๋ณ„ํ•˜๊ฒŒ ์„ ์–ธ๋œ ์ฃผ์„์œผ๋กœ ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค angular.d.ts๋ฅผ ์ฐพ์œผ๋ผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์ด์ œ ๋‹น์‹ ์ด AngularJS ํ•จ์ˆ˜๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•˜์˜€๋‹ค๋ฉด ๋™์ ์œผ๋กœ ์‹คํ–‰๋  ํ•„์š” ์—†์ด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์•Œ๋ ค์ค„ ๊ฒƒ์ด๊ณ  ๋‹น์‹ ์€ ํ•ด๋‹น ์˜์—ญ์„ ๊ณ ์น ์ˆ˜ ์žˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” node_modules ํด๋”์— ์ข…์†๋ฌผ๋กœ ํŒจํ‚ค์ง€ ๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ์—๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ๋ฐœ๊ฒฌํ• ์ˆ˜ ์žˆ๋‹ค. ์ ์  ๋” ๋งŽ์€ ํ”„๋กœ์ ํŠธ ๋“ค์ด ํ•ด๋‹น ๋ฐฉ์‹์„ ์ฑ„ํƒํ•˜๊ณ  ์žˆ์œผ๋ฉฐ Angular 2 ๋˜ํ•œ ๊ทธ๋ ‡๋‹ค. ๊ทธ๋ž˜์„œ ๋‹น์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํฌํ•จํ•˜๋Š”๊ฒƒ์— ๋Œ€ํ•ด์„œ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋‹น์‹ ์ด NPM์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์กด์„ฑ์„ ์œ ์ง€ํ•œ๋‹ค๋ฉด TS ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ž๋™์œผ๋กœ ํ™•์ธํ•  ๊ฒƒ์ด๋‹ค.

Decorators

์ด๊ฒƒ์€ ์ƒˆ๋กœ์šด ํŠน์ง•์ด๋ฉฐ Angularjs๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์˜ค์ง TypeScript 1.5 ์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ณง ์•Œ์ˆ˜ ์žˆ๋“ฏ์ด Angular 2 Componenet๋Š” Decorator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค๋ช…๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์€ ๋ชจ๋“  ์–ธ์–ด์—์„œ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, decorator์— ๋Œ€ํ•ด์„œ ๋“ค์–ด๋ณด์ง€ ์•Š์•˜์„ ์ˆ˜๋„ ์žˆ๋‹ค. decorator๋Š” ๋ฉ”ํƒ€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ• ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋ผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋“ค์€ java, c#, python ๋˜๋Š” ๋‚ด๊ฐ€ ์•Œ์ง€ ๋ชปํ•˜๋Š” ์–ธ์–ด์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜์–ด์ง€๋Š” ์„ ์–ธ๊ณผ ๊ฐ™์€ ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•  ๊ฒƒ์ด๋‹ค. ์–ธ์–ด์— ๋”ฐ๋ผ์„œ ๋‹น์‹ ์€ ์†์„ฑ, ํ•จ์ˆ˜, ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ• ์ˆ˜ ์žˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์„ ์–ธ์€ ์–ธ์–ด์— ์˜ํ•ด ์‚ฌ์šฉ๋˜์–ด์ง€์ง€ ์•Š๊ณ  ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋œ์–ด ์ง„๋‹ค.

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

Angular2์—์„œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜ํ•ด ์ œ๊ณตํ•˜๋Š” decorator๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. decorator์˜ ์—ญํ™œ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. decorator๋Š” ์šฐ๋ฆฌ์˜ ํด๋ž˜์Šค์— ์•ฝ๊ฐ„์˜ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. " ํ•ด๋‹น ํด๋ž˜์Šค๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.", "์ด๊ฒƒ์€ ์„ ํƒ์ ์ธ ์˜์กด์ด๋‹ค." "์ด๊ฒƒ์€ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•๋œ ์†์„ฑ์ด๋‹ค." ๋‹น์‹ ์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •์œผ๋กœ ๋„ฃ๋Š”๋‹ค๋ฉด decorator๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ์š”๊ตฌ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

TypeScript์—์„œ๋Š” decorator๋Š” @๋กœ ์‹œ์ž‘ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํด๋ž˜์Šค์— ์ ์šฉํ• ์ˆ˜ ์žˆ๊ณ  ๋ณ€์ˆ˜ ๋˜๋Š” ํ•จ์ˆ˜ ๋˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. decorator๋Š” ์ƒ์„ฑ์ž์—๋Š” ์ ์šฉ๋ ์ˆ˜ ์—†์œผ๋‚˜ ์ƒ์„ฑ์ž์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ์ ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

์ด๊ฒƒ์— ๋Œ€ํ•ด ๋” ํ™•์‹คํžˆ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ฐ„๋‹จํ•œ decorator๋ฅผ ๋นŒ๋“œํ•ด๋ณด์ž. @log๋Š” ๋ชจ๋“  ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

class RaceService {
  @Log()
  getRaces() {
   // call API
  }
  @Log()
  getRace(raceId) {
   // call API
  }
}

์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด, ์šฐ๋ฆฌ๋Š” ์ด ์™€๊ฐ™์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

const Log = function () {
  return (target: any, name: string, descriptor: any) => {
   logger.log(`call to ${name}`);
   return descriptor;
  };
};

๋‹น์‹ ์ด decorator์— ์ ์šฉํ•˜๊ธธ ์›ํ•˜๋Š” ๊ฒƒ์— ๋”ฐ๋ผ์„œ, ํ•จ์ˆ˜๋Š” ๋งค๋ฒˆ ๊ฐ™์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ–์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. ํ•ด๋‹น ํ•จ์ˆ˜ decorator๋Š” ์„ธ๊ฐ€์ง€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค.

  • target : decorator์˜ ์˜ํ•ด ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ํ•จ์ˆ˜

  • name : targeted ํ•จ์ˆ˜์˜ ์ด๋ฆ„

  • descriptor : targeted๋œ ํ•จ์ˆ˜์˜ ์„ค๋ช…

์šฐ๋ฆฌ๋Š” ํ•จ์ˆ˜ ์ด๋ฆ„์„, ๊ทธ๋Ÿฌ๋‚˜ ๋‹น์‹ ์€ ๋‹น์‹ ์ด ์›ํ•˜๋Š” ์–ด๋–ค ๊ฒƒ์ด๋“  ํ• ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋“ค์–ด, getRace() ๋˜๋Š” getRaces() ํ•จ์ˆ˜๋Š” ๋งค ๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ธŒ๋ผ์šฐ์ ธ ๋กœ๊ทธ์—์„œ ์ถ”์ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

raceService.getRaces();
// logs: call to getRaces
raceService.getRace(1);
// logs: call to getRace

user ์™€ ๊ฐ™์ด, Angular2์˜ decorator๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์‚ดํŽด ๋ณด๊ฒ ๋‹ค.

@Component({ selector: 'ns-home' })
class HomeComponent {
  constructor(@Optional() hello: HelloService) {
   logger.log(hello);
  }
}

@Component decorator๋Š” Home ํด๋ž˜์Šค์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. Angular2 ๊ฐ€ ๋กœ๋“œ๋˜์—ˆ์„ ๋•Œ, ๊ทธ๊ฒƒ์€ Home ํด๋ž˜์Šค๋ฅผ ํ™•์ธํ•  ๊ฒƒ์ด๊ณ  component๋ผ๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ•  ๊ฒƒ์ด๋‹ค. ๋ณด์‹œ๋‹ค์‹œํ”ผ, decorator๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋‚˜๋Š” decorator์˜ ๊ฐ€๊ณต๋˜์ง€ ์•Š์€ ๊ฐœ๋…์„ ์†Œ๊ฐœํ•˜๊ธธ ์›ํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  decorator๋ฅผ ์ฑ… ์ „์ฒด์—์„œ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค. TypeScript ๋Œ€์‹ ์— Babel๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ง€์ ํ•ด์•ผ ํ•œ๋‹ค. ๋ชจ๋“  Angular 2 decorator๋ฅผ ์ง€์›ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ๋„ ์žˆ๋‹ค. : angular2-annotations

Babel์€ ํด๋ž˜์Šค ์†์„ฑ๋„ ์ง€์›ํ•˜์ง€๋งŒ TypeScript์—์„œ ์ œ๊ณตํ•˜๋Š” ์œ ํ˜• ์‹œ์Šคํ…œ์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹น์‹ ์€ Babel์„ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๊ณ  "ES6+"๋กœ ์ž‘์„ฑํ•˜์—ฌ๋ผ. ๊ทธ๋Ÿฌ๋‚˜ ๋‹น์‹ ์€ ์œ ํ˜•๋“ค์„ ์‚ฌ์šฉํ• ์ˆ˜ ์—†์„๊ฒƒ์ด๊ณ  ๊ทธ๋“ค์€ ์˜์กด์„ฑ ์ฃผ์ž…์— ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์™„์ „ํžˆ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์œ ํ˜•์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•ด ๋” ๋งŽ์€ decorator๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚˜์˜ ์กฐ์–ธ์€ TypeScript ์‹œ๋„ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๊ฒƒ์œผ๋กœ ๋ถ€ํ„ฐ ๋‚˜์˜จ ๋‚˜์˜ ๋ชจ๋“  ๊ฒฝํ—˜์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹น์‹ ์ด ๋‚˜๋จธ์ง€์— ๋Œ€ํ•œ ๊ฒƒ์„ ์žŠ๊ณ  ์œต์šฉํ•œ๊ณณ์— ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ด๊ฒƒ์€ ๋ฐฉํ•ด๋˜์ง€ ์•Š๋Š”๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด ์‹ซ๋‹ค๋ฉด, decorator๋Š” Babel๋˜๋Š” Traceur ๋ฅผ ์‚ฌ์šฉํ•œ ES6 ๋˜๋Š” ์‹ฌ์ง€์–ด ES5๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. (์†”์งํžˆ ES5๋กœ๋Š” angular 2 ์•ฑ์ด ์•ˆ์ข‹์€ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.)

Reference URL

Last updated