데코레이터(Decorator)

2023. 9. 5. 02:27·개발이야기

데코레이터란 무엇인가? 디자인 패턴으로써의 데코레이터의 의미와 typescript에서의 데코레이터 사용법을 알아보자.

디자인패턴으로써의 데코레이터

구조 패턴에 속한다. AOP(Aspect Oriented Programming)의 주요 개념인 흩어진 관심사(Crosscutting Concerns)를 구현하는데 탁월하다. 아래 두 글에서 적절한 비유로 잘 설명해주고 있다.

https://refactoring.guru/ko/design-patterns/decorator
https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html

위 두 링크의 내용을 간단히 요약해보겠다.

출처: https://refactoring.guru/ko/design-patterns/decorator

우리가 알림서비스를 개발한다고 가정하자. 유저가 어떤 행동을 했느냐에 따라, SMS 알림을 줄 수도, Facebook 알림을 줄 수도, Slack 알림을 줄 수도 있다. SMS와 Facebook 알림을 줄 수도 있고, Facebook과 Slack 알림을 줄 수도 있고, SMS와 Facebook과 Slack 알림을 모두 줄 수도 있다.

아래와 같이 구현해야 한다면 너무 피곤할 것이다.

const smsNotifier = new SMSNotifier();
const facebookNotifier = new FacebookNotifier();
const slackNotifier = new SlackNotifier();
const smsAndFacebookNotifier = new SMSAndFacebookNotifier();
const smsAndSlackNotifier = new SMSAndSlackNotifier();
const slackAndFacebookNotifier = new SlackAndFacebookNotifier();
const allNotifier = new AllNotifier();

function do() {
  doSomething();

  // 이 함수는 sms와 slack 알림을 주고 싶으니까 smsAndSlackNotifier를 골라!
  const send = smsAndSlackNotifier.send;
  send();
}

이렇게 구현한다면 우아하지 않겠는가.

function do() {
  doSomething();

  // 이 함수는 sms와 slack 알림을 주고 싶으니까 smsNotifier와 slackNotifier를 골라!
  const notifier = new SMSDecorator(new SlackDecorator(new Notifier()))
  notifier.send();
}

typescript에서 데코레이터라함은 보통 @를 이용한 문법을 의미한다. 아래처럼 쓰는 것이 제일 우아할 것이다.

@SMSNotify
@SlackNotify
function do() {
  doSomething();
}

데코레이터란?

@를 이용한 문법은 처음 보면 어떻게 동작하는지 가늠이 가지 않아 마법처럼 느껴질 수 있다. 하지만, 이 문법은 typescript, java(Spring Framework AOP), python 등 다양한 언어에서 채택한 문법이다. 아래의 두 python 코드가 완전히 같은 코드라는 사실을 이해해보자.

@decorator
def func(*args):
    print('this is decorated func!')
def func(*args):
    print('this is decorated func!')

func = decorator(func)

typescript에서의 데코레이터

@ 데코레이터는 typescript에서는 완전히 정식으로 채택된 문법은 아니다. 현재 2023년 9월 기준, 이 문법은 tc39 proposal stage3에 머물러있다. 그래서 typescript에서 이 문법을 사용하기 위해서는 tsconfig.json 파일에서 experimentalDecorator 옵션에 true를 주어야 사용가능하다.

typescript에서 데코레이터를 야무지게 사용한다고 있는 두 가지 라이브러리/프레임워크를 소개한다. 두 사례 모두 흩어진 관심사(Crosscutting Concerns)들을 데코레이터를 활용하여 아주 읽기 쉽고, 생산적이게 제공한다.

TypeORM

NodeJS 진영의 ORM이다. 엔티티 모델을 데코레이터를 활용하여 아주 간단하게 표현할 수 있다.

// 출처: https://typeorm.io/
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    age: number
}

NestJS

NodeJS 진영의 서버 프레임워크이다. 아래 주소는 NestJS에서 공식적으로 제공하는 샘플 중 하나다.

https://github.com/nestjs/nest/tree/master/sample/01-cats-app

 Controller, service 등의 코어한 개념과, pipe, guard, middleware 등의 부가적인 기능등을 심플하게 작성할 수 있다.

import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { Roles } from '../common/decorators/roles.decorator';
import { RolesGuard } from '../common/guards/roles.guard';
import { ParseIntPipe } from '../common/pipes/parse-int.pipe';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { Cat } from './interfaces/cat.interface';

@UseGuards(RolesGuard)
@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  @Roles(['admin'])
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Get(':id')
  findOne(
    @Param('id', new ParseIntPipe())
    id: number,
  ) {
    // get by ID logic
  }
}

4종류의 데코레이터

이 섹션은 아래 영상에 더 구체적으로 설명되어 있다.

typescript에서의 데코레이터는 아래의 4종류로 구분 가능하다.

  • Class Decorator
  • Method Decorator
  • Property Decorator
  • Parameter Decorator
@Component({
  id: 'Hello World',
}) // Class Decorator
export class TestClass {
  static elementId: string;

  @Prop // Property Decorator
  id: number;

  @MethodTest // Method Decorator
  printId(@Param prefix: string = ""): string { // Parameter Decorator
    return prefix + this.id;
  }
}

각 Decorator들의 구체적인 구현 방법은 앞서 공유한 유튜브 링크를 참고하자

'개발이야기' 카테고리의 다른 글

Node.js에서 csv 파일 다루기 및 ios-윈도우 간 한글 깨짐 문제 해결  (1) 2024.01.06
타입스크립트: ts2322 error 해결을 위한 서브타입 관련 개념 총정리  (1) 2023.11.26
한글의 유니코드 인코딩과, javascript에서 한글 문자열을 다루는 방식  (1) 2023.10.19
HTTP의 역사: 0.9부터 3.0까지  (1) 2023.09.14
Artillery와 함께, 웹 부하테스트 빠르게 익히기  (0) 2023.08.12
'개발이야기' 카테고리의 다른 글
  • 타입스크립트: ts2322 error 해결을 위한 서브타입 관련 개념 총정리
  • 한글의 유니코드 인코딩과, javascript에서 한글 문자열을 다루는 방식
  • HTTP의 역사: 0.9부터 3.0까지
  • Artillery와 함께, 웹 부하테스트 빠르게 익히기
준별
준별
  • 준별
    준별개발
    준별
  • 전체
    오늘
    어제
    • 분류 전체보기 (59)
      • 개발이야기 (15)
        • 토막글 (11)
      • 일상이야기 (6)
      • 개인 공부 (23)
      • 생각과 기록 (2)
  • 블로그 메뉴

    • 홈
    • 방명록
    • Github
    • Linkedin
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    persistent connection
    http1.0
    정보보호개론
    zsh세팅
    이산구조
    맥북초기세팅
    http1.1
    powerlevel10k
    터미널꾸미기
    artillery
    클램쉘
    http3.0
    터미널세팅
    맥북터미널세팅
    필수툴
    전산기조직
    실전압축
    바이브코딩
    k9s
    맥북세팅
    데스크셋업
    맥북
    데이터베이스
    http pipelining
    nestjs
    nodejs
    조합형
    http2.0
    zsh-autosuggestion
    Zsh
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
준별
데코레이터(Decorator)
상단으로

티스토리툴바