Nestjs

nestjs 와 데코레이터

2023. 8. 2. 00:28
목차
  1. 클래스 데코레이터
  2. 메서드 데코레이터
  3. 접근자 데코레이터
  4. 속성 데코레이터
  5. 매개변수 데코레이터

nest는 데코레이터를 적극적으로 활용하는 프레임워크입니다.

데코레이터를 적절히 사용하면 횡단 관심사(cross-cutting concern)을 분리하며

관점 지향 프로그래밍을 적용한 코드를 작성할 수 있는데요

다른 언어에 익숙하신 분들은 자바의 애너테이션, 파이썬의 데코레이터를 떠올려주셔도 됩니다.

 

타입스크립트에서 데코레이터는

클래스 , 메서드, 접근자, 프로퍼티, 매개변수에 적용이 가능하며

각 요소의 선언부 앞에 @로 시작하는 데코레이터를 선언하게되면 데코레이터로 구현된 코드를 함께 실행해줍니다.

 

이러한 데코레이터는 여러개의 데코레이터를 함께 사용하는 데코레이터 합성을 할 수 있습니다.

함수를 중첩해서 사용할 때와 비슷하게 생각을 할 수 있는데요

@f
@g
test

이러한 형태로 작성된 데코레이터 코드는 f(g(x))와 같다고 볼 수 있습니다.


클래스 데코레이터

클래스 데코레이터는 클래스의 바로 앞에 선언되는 데코레이터입니다.

클래스 데코레이터는 클래스의 "생성자"에 적용되며 클래스 정의를 읽거나 수정할 수 있습니다.

바꾸어 말하면 클래스 데코레이터는 생성자를 리턴하는 함수여야 합니다.

선언 파일과 선언 클래스(declare class를 의미합니다.) 에는 사용할 수 없습니다.

function reportableClassDecorator(T extends { new (...args:any[]): {} })>(contsructor:T){
  return class extends constructor {
    reportingURL = 'example.com'
  }
}

@reportableClassDecorator
class BugReport {
  type = 'report'
  title:string
  
  constructor(t:string) {
    this.title = t
  }
}

const bug = new BugReport("need")

console.log(bug)

이러한 형태로 사용가능하며 이 코드의 출력 결과는

type, reportingURL, title을 프로퍼티로 가지고 있는 객체가 됩니다.

 

메서드 데코레이터

메서드 데코레이터는 메서드의 바로 앞에 선언됩니다.

메서드의 속성 설명자에(property descriptor)에 적용되며 메서드의 정의를 읽거나 수정할 수 있습니다.

선언 파일, 오버로드 메서드, 선언 클래스에 사용할 수는 없습니다.

메서드 데코레이터는 총 세개의 인수를 가져야하는데요

 

1. 정적 멤버가 속한 클래스의 생성자 함수이거나 인스턴스 멤버에 대한 클래스의 프로토타입

2. 멤버의 이름

3. 멤버의 속성 설명자 (PropertyDescriptor) 타입

이 세가지를 가집니다.

 

PropertyDescriptor라고 하니까 되게 낯설고 막 초면인 것 같고 그런데

조금 찾아보니 프로퍼티 어트리뷰트에서 공부했던 내용들이 이름만 살짝 달라진 느낌이네요

프로퍼티 플래그들을 이렇게 부르는건가? 싶은 느낌이다.

const obj = { a: 2 };

Object.getOwnPropertyDescriptor(obj, 'a');
/*
{
  	configurable: true
  	enumerable: true
  	value: 2
	writable: true
}
*/

어..? configurable, enumerable, writable 오랜만이다!

싶어 반갑지만 얘네에 집중하느라 당연히 넘겼던 친구를 다시 한번 보자

value에는 그 프로퍼티가 갖고있는 값이 할당되어있다.

 

이것에 집중하여 메서드 데코레이터를 보면 이해가 쉬워진다.

 

function HandleError() {
  return function(target:any , propertyKey:string, descriptor:PropertyDescriptor) {
    console.log(target)

    const method = descriptor.value
    descriptor.value = function() {
       try {
         method()
       } catch(e) {
         console.log(e)
      }
    }
  

  }
}

이 코드는 인수로 받은 descriptor의 value를 뽑아서 method에 할당해두고

descriptor.value에 새로운 익명함수를 할당한다.

그리고 try문으로 기존 descriptor.value가 갖고있던 밸류를 래핑해주는데

 

이렇게 데코레이터를 달아주면 지저분한 try,catch문을 데코레이터로 꾸며주는것만으로도

수행시켜줄 수 있다. 

처음으로 데코레이터가 와닿은 포인트.

PropertyDescriptor는 다음과 같은 타입으로 정의되어있다.

interface PropertyDescriptor {
  configurable?: boolean;
  enumerable?: boolean;
  value?: any;
  writable?: boolean;
  get?():any;
  set?(v:any):void
}

configurable : 속성의 정의를 수정할 수 있는지 여부

enumerable : 열거형인지 여부

writable : 수정 가능 여부

오랜만에 보니 기억이 새록새록 나는 것 같다.

 

접근자 데코레이터

접근자 데코레이터는 접근자 (accessor) 바로 앞에 선언합니다.

접근자의 속성 설명자에 적용되며 접근자의 정의를 읽거나 수정할 수 있습니다.

접근자 데코레이터 역시 선언 파일과 선언 클래스에는 사용할 수 없어요

 

접근자 데코레이터가 반환하는 값은 해당 멤버의 속성 설명자가 됩니다!

그런데 접근자라는 개념이 좀 생소한데요.

쉽게 생각하면 getter, setter 함수들을 접근자라고 생각하면 됩니다.

function Enumerable(enumerable:boolean) {
  return function (target:any, propertyKey:string, descriptor:PropertyDescriptor) {
    descriptor.enumerable = enumerable
  }
}

class Person {
  constructor(private name:string) {}

  @Enumerable(true)
  get getName() {
    return this.name
  }
}

이러한 형태로 게터 세터 함수를 꾸며줄 수 있습니다.

 

속성 데코레이터

속성 데코레이터 역시 선언 파일 , 선언 클래스에서는 사용할 수 없습니다.

속성 데코레이터는 다음 두개의 인수를 가지는 함수여야 합니다.

 

1. 정적 멤버가 속한 클래스의 생성자 함수이거나 인스턴스 멤버에 대한 클래스의 프로토타입

2. 멤버의 이름

 

메서드 데코레이터나 접근자 데코레이터와의 가장 큰 차이는 세번째 인수 Property Descriptor가 없다는 것입니다.

공식문서에 따르면 반환값도 무시가 되는데 이는 현재 프로토타입의 멤버를 정의할 때

인스턴스 속성을 설명하는 메커니즘이 없으며

속성의 초기화 과정을 관찰하거나 수정할 방법이 없기 때문이라고 설명합니다.

 

function format(formatString:string) {
  return function (target: any, propertyKey: string): any {
    let value = target[propertyKey]
    function getter () {
      return `${formatString} ${value}`
    }
    function setter(newVal:string) {
      value= newVal
    } 
    return {
      get:getter,
      set:setter,
      enumerable:true,
      configurable:true
    }
  }
}

class Greeter {
  @format('Hello')
  greeting:string
}

const t = new Greeter()
t.greeting = 'world'

console.log(t.greeting)

이 코드는 t.greeting을 했을 때 위에서 정의해둔 getter 함수가 호출되며

사전에 매개변수로 전달한 Hello가 앞에 출력됩니다.

즉 Hello greeting의 형태가 되는 것이고 

t.greeting에는 world를 할당해두었기 때문에

console.log(t.greeting)의 출력값은 Hello world 가 됩니다.

 

매개변수 데코레이터

매개변수 데코레이터는 생성자 또는 메서드의 매개변수에 선언되어 적용됩니다.

역시 선언파일, 선언 클래스에는 사용할 수 없습니다.

매개변수 데코레이터는 호출될 때 3가지 인수와 함께 호출되는데요

반환값은 무시됩니다.

 

1. 정적 멤버가 속한 클래스의 생성자 함수이거나 인스턴스 멤버에 대한 클래스의 프로토타입

2. 멤버의 이름

3. 매개변수가 함수에서 몇 번째 위치에 선언되었는지를 나타내는 인덱스

 

매개변수 데코레이터를 통해 매개변수의 유효성 검사를 할 수 있습니다.

매개변수 데코레이터는 단독으로 사용하는 것보다 함수 데코레이터와 함께 사용할 때 유용하게 쓰입니다.

Nest에서 API 요청 매개변수에 대해 유효성 검사를 할 때에 이와 같은 형태의 데코레이터를 많이 사용합니다.

import { BadRequestException } from "@nestjs/common";

function MinLength(min: number) {
    return function (target:any , propertyKey: string , parameterIndex:number) {
        target.validators = {
            minLength: function (args:string[]) {
                return args[parameterIndex].length >= min
            }
        }
    }
}

function Validate(target:any, propertyKey:string, descriptor:PropertyDescriptor){
    const method = descriptor.value

    descriptor.value = function(...args) {
        Object.keys(target.validators).forEach(key => {
            if(!target.validators[key](args)) {
                throw new BadRequestException()
            }
        })
        method.apply(this, args)
    }
}

class User {
    private name: string;

    @Validate
    setName(@MinLength(3) name:string) {
        this.name = name
    }
}

const t = new User()
t.setName = new User()

console.log('-------')
t.setName('De')

 

반응형
저작자표시 비영리 (새창열림)

'Nestjs' 카테고리의 다른 글

nestjs 컨트롤러, 프로바이더, 모듈  (0) 2023.08.23
NestJS 시작해보기  (0) 2023.07.07
  1. 클래스 데코레이터
  2. 메서드 데코레이터
  3. 접근자 데코레이터
  4. 속성 데코레이터
  5. 매개변수 데코레이터
'Nestjs' 카테고리의 다른 글
  • nestjs 컨트롤러, 프로바이더, 모듈
  • NestJS 시작해보기
냠냠맨
냠냠맨
프론트엔드 개발 전반을 다루는 기술 블로그입니다.
냠냠맨
React와 TypeScript를 좋아하는 개발자
냠냠맨
전체
오늘
어제
  • all category (434)
    • CMC (0)
    • best (11)
    • 년간회고 (1)
    • cheetsheet (15)
    • 프로젝트 회고 (3)
    • 서평 (3)
    • SEO Study (1)
    • 프로젝트 진행기 (10)
    • testcode (9)
    • yarnberry (7)
    • css (21)
    • typescript (15)
    • redux (7)
    • react (43)
    • Next.js (9)
    • Nestjs (3)
    • javascript (44)
    • programmers (67)
    • leetcode (41)
    • frontend (41)
    • backjoon (1)
    • Next.js Beta Docs 번역 (12)
    • TIL (15)
      • html (3)
    • Network (12)
      • 간단 정리 시리즈 (2)
      • 질답 준비 (0)
    • 자료구조와 알고리즘 (2)
    • CS (4)
      • OS (1)
    • 취업준비 (2)
    • zoom websocket (2)
    • talk (6)
    • 면접대비 (1)
    • 코드스테이츠 프론트 (5)
    • 간헐적 회고 (18)

블로그 메뉴

  • leetcode
  • programmers
  • javascript
  • html
  • css

공지사항

인기 글

태그

  • 개발
  • border말풍선
  • 테오의스프린트17기
  • LeetCode
  • JavaScript
  • CSS
  • 코드스테이츠 #프론트엔드
  • 테오의스프린트
  • frontend
  • 말풍선
  • 프론트엔드
  • 주니어개발자
  • teosprint
  • 개발자

최근 댓글

최근 글

hELLO · Designed By 정상우.
냠냠맨
nestjs 와 데코레이터
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.