NestJS를 이해하려면, Decorator를 알아야하고, Decorator를 알기 위해서는 Closure 개념과 비교하면 좋다.
근데 Closure 개념과 Decorator를 위해서는 First Class Function 이라는 개념에 대해 짚고 가는 것이 먼저이다. 따라서 이번 포스팅에서는 First Class Function, Closure, Decorator를 알아보려고 한다.
참고로 Decorator는 아직 ES의 정식 기능이 아니며, ECMA TC39의 실험적 기능이다. 하지만 Nest 등 최신 프레임워크 중에서는 Decorator를 지원하는 경우가 있고, Babel도 Decorator를 지원하고 있다.
이 이면에는 비밀이 있는데, Decorator는 새로운 문법이 아니라는 점이다!
사실 Decorator는 Java나 Python 등 다른 언어에서 이미 지원하고 있는 널리 통용되는 문법으로, 이미 많은 개발자들이 유용하게 쓰고 있는 문법이다. 따라서 언제일지는 모르겠지만, Decorator가 정식 ES에 지원되는 것은 시간문제일 뿐이다. 그렇기에 Decorator를 사용하거나 지원하는 패키지들이 이따금 눈에 보이는 것.
어쨌거나 본론으로 빠르게 넘어가서, First Class Function 부터 다뤄보자.
First Class Function
함수가 1급 객체가 되기 위한 조건이 몇 가지 있다. 하지만 그 중에서 오늘 우리가 주목해야 할 점은 함수를 다른 함수의 인자나, 변수에 할당할 수 있다는 요건이다.
그렇다. 함수가 1급 객체라는 뜻은 문자 그대로 함수를 완전한 의미에서의 객체와 같이 취급할 수 있다는 뜻이다. 따라서 우리는 함수를 재탕 삼탕 가능하다.
function square(x) {
const square = x * x;
console.log(`${x}에 대하여 square함수 실행결과 ${square}`);
return square;
}
square(4);
const alias = square;
alias(4);
function plus(x, y) {
const plus = x + y;
console.log(`${x}와 ${y}에 대하여 plus함수 실행결과 ${plus}`);
return plus;
}
plus(1, 2);
plus(square(4), 1);
plus(alias(4), 1);
위 코드에서, square 함수는 alias 라는 변수에 할당 되었다. 마치 객체처럼!
또한, plus 함수에서 square 함수 (혹은 alias 함수)를 인자로 받아오기도 하였다. 마치 객체처럼!
이것이 바로 일급 객체인 함수의 특징 중 하나이다.
사실 함수형 프로그래밍을 지원하는 모든 언어는 반드시 함수가 일급 객체이어야만 한다. 그리고 Javascript는 함수형 프로그래밍을 지원하는 언어이기도 하다.
Closure
MDN 공식 문서에 따를 때, Closure란, 함수와 함수가 선언된 어휘적 환경의 조합을 말한다. 여기서 어휘적 환경이란, Lexical Scope 를 의미한다.
Javascript에서 함수가 참조하는 변수는 함수가 호출된 위치가 아니라 함수가 선언된 위치에 따라 정의된다.
const grade = "C";
function amIGradeA() {
const grade = "A";
function yourGradeIs() {
if (grade === "A") {
console.log(`Am I Grade A? : Yes`);
} else {
console.log(`Am I Grade A? : No`);
}
}
return yourGradeIs();
}
amIGradeA();
위 코드에서 amIGradeA 함수 내부에 있는 yourGradeIs 함수가 바로 Closure다.
yourGradeIs 함수는 자신이 선언된 위치에서 참조 가능했던 grade 변수(전역 변수로서의 grade가 아닌 amIGradeA 함수 내부의 지역변수 grade를 말한다. 즉 이 grade의 값은 A이다)를 자신의 내부에서 참조하고 있기 때문이다.
yourGradeIs 함수 내부에는 grade에 관한 선언이 전혀 없다는 것이 포인트!
따라서 만약 아래와 같이 코드를 작성하면 Closure가 아니므로 더 이상 grade 변수를 참조할 수 없게 된다.
const grade = "C";
function amIGradeA() {
const grade = "A";
return yourGradeIs();
}
function yourGradeIs() {
if (grade === "A") {
console.log(`Am I Grade A? : Yes`);
} else {
console.log(`Am I Grade A? : No`);
}
}
amIGradeA();
더 이상 yourGradeIs 함수가 amIGradeA 함수 내부에 있지 않다. 따라서 amIGradeA 함수의 렉시컬 스코프 밖에 정의되었기에 amIGradeA 함수의 렉시컬 스코프에 존재하던 grade = "A" 가 아닌, yourGradeIs가 선언된 전역 스코프의 grade = "C"를 참조하고 있다.
Decorator
데코레이터는 기본적으로 클로저와 매우 유사하다. 다만, 함수가 일급 객체라는 점과 클로저를 합쳐 놓은 양상일 뿐이다.
즉, 데코레이터는 함수와 함수가 선언된 렉시컬 스코프상의 함수의 조합이다.
def decorator(decorated):
def for_understand_decorator():
print("이쁘게 꾸며줄거야")
decorated()
print("데코레이팅은 꾸미기니까!")
return for_understand_decorator
@decorator
def sayHi():
print("Hiiiiiiii!")
sayHi()
논의의 편의상, 그리고 문법적 편리함 때문에 파이썬 코드로 작성하였다.
위 코드에서 sayHi 함수의 앞뒤로 새로운 내용이 print 된 것을 볼 수 있다.
이처럼 decorator는 함수를 인자로 받아, 해당 함수의 내용을 바꾸고 꾸며주는 역할을 한다. 이 때, decorated로 전달되는 함수는 마치 closure와 같이 해당 decorator 내의 렉시컬 스코프의 영향을 받게 된다.
'Learning-Log > Computer Science' 카테고리의 다른 글
[JS] 모듈을 받아오는 import와 모듈을 내보내는 export (0) | 2022.07.15 |
---|---|
[ReactJS] styled-components와 함께 하는 즐거운 ReactJS (0) | 2022.07.12 |
[NestJS] NestJS, 한 번 빠지면 벗어날 수 없는 마성의 늪(2) : NestJS 시작하기 & Express 프로젝트를 Nest로 마이그레이션 하는 경우 (0) | 2022.07.11 |
[WSL] Ubuntu에서 현재 디렉토리를 윈도우 탐색기로 열기 (0) | 2022.07.11 |
[NestJS] NestJS, 한 번 빠지면 벗어날 수 없는 마성의 늪(1) : 소개 (0) | 2022.07.11 |