[ Decorator ]
- 2020년 9월 meeting에서 TC39에 제시될 새로운 제안
- 가장 단순하게는 코드의 한 부분을 다른 코드로 감싸는 방법 (= 장식)
- 정의 중에 클래스 요소, 기타 JavaScript 구문 양식에서 호출되는 함수, 잠재적으로 decorator가 반환한 새 값으로 래핑(또는 대체)됨
※ 사실 이미 다른 방식으로 decorator의 느낌처럼 JavaScript에서 사용 가능했음..(아래 처럼)
function loggingDecorator(wrapped) {
return function() {
console.log('Starting');
const result = wrapped.apply(this, arguments);
console.log('Finished');
return result;
}
}
const wrapped = loggingDecorator(doSomething);
< JavaScript에서 Decorator 사용 방법 >
@ + 데코레이팅할 코드
- 원하는만큼 사용 가능
- 선언한 순서대로 적용
- class 선언, method, accessor, property, parameter에 모두 선언 가능
예제)
@log()
@immutable()
class Example {
@time('demo')
doSomething() {
//
}
}
예제 설명)
- 클래스를 정의하고 3개의 decorator를 적용
- 2개는 클래스 자체에, 1개는 클래스 속성에 적용
- @log : 클래스에 대한 모든 access를 기록
- @immutable : 클래스를 변경 불가능하게 만듦 / 새 인스턴스에서 Object.freeze를 호출 가능
- @time : 메서드가 실행되는 데 걸리는 시간을 기록하고 고유태그를 사용하여 로그아웃
- 현재 decorator를 사용하려면, 트랜스파일러 지원이 필요
- babel을 사용하는 경우 transform-decorators-legacy 플러그인을 사용하여 간단히 활성화 가능
--> 플러그인에서 사용되는 레거시는 babel5방식을 지원(표준이 아님)
< Decorator를 사용하는 이유 >
- JavaScript에서 함수 구성이 이미 가능하지만 동일한 기술을 다른 코드(클래스나 클래스 속성)에 적용하는 것이 훨씬 어렵거나 불가능 => Decorator 제안은 이런 문제를 해결하는데 사용할 수 있음
- 작성중인 코드의 실제 의도를 더 명확하게 파악 가능
- 코드가 간결해지고 명확해짐
< Decorator 유형 >
- 현재 지원되는 decorator 유형은 클래스 및 클래스 멤버(속성, 메서드, getter, setter)
- decorator는 실제로 다른 함수를 반환하고, 데코레이팅되는 항목의 적절한 세부사항과 함게 호출되는 함수일 뿐
- decorator 함수는 프로그램이 처음 실행될 때 한번 evaluate되고, 데코레이팅된 코드는 반환값으로 대체됨
1. Class member decorators
- Property decorator는 속성, 메서드, getter, setter 등 클래스의 단일 멤버에 적용
- Property decorator 함수는 세가지 메개변수로 호출됨
- (1) target : 멤버가 있는 클래스
- (2) name : 클래스의 멤버 이름
- (3) descriptor : Object.defineProperty에 전달되는 객체
ex1)
@readonly 사용법
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Example {
a() {}
@readonly
b() {}
}
const e = new Example();
e.a = 1;
e.b = 2;
// TypeError: Cannot assign to read only property 'b' of object '#<Example>'
ex2)
@log 사용법
function log(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`Arguments: ${args}`);
try {
const result = original.apply(this, args);
console.log(`Result: ${result}`);
return result;
} catch (e) {
console.log(`Error: ${e}`);
throw e;
}
}
}
return descriptor;
}
class Example {
@log
sum(a, b) {
return a + b;
}
}
const e = new Example();
e.sum(1, 2);
// Arguments: 1,2
// Result: 3
2. Class decorators
- Class decorator는 전체 클래스 정의에 적용
- decorator 함수는 데코레이팅되는 생성자 함수인 단일 매개 변수로 호출됨
-- 생성된 클래스의 각 인스턴스가 아니라 생성자 함수에 적용됨
- 인스턴스를 조작하려면 래핑된 생성자 버전을 반환하여 직접 수행해야함
- 모든 작업은 동일한 방식으로 간단한 함수 호출 정도만 수행할 수 있기 때문에 class member decorator보다 유용하지 않음
- Class decorator로 수행하는 모든 작업은 클래스 생성자를 대체하기 위해서 새로운 생성자 함수를 반환해야함 (아래처럼)
function log(Class) {
return (...args) => {
console.log(args);
return new Class(...args);
};
}
예제)
@log
class Example {
constructor(name, age) {
}
}
const e = new Example('Graham', 34);
// [ 'Graham', 34 ]
console.log(e);
// Example {}
출처
https://github.com/tc39/proposal-decorators
https://www.sitepoint.com/javascript-decorators-what-they-are/
'Programming > JavaScript & TypeScript' 카테고리의 다른 글
Array.prototype.splice() 사용법 (0) | 2020.10.06 |
---|---|
Strict mode(엄격 모드), use strict 란? (0) | 2020.09.29 |
[Node.js] 싱글 스레드(Single-thread)와 이벤트 루프(Event loop) (0) | 2020.09.25 |
npm install 시 npm ERR! code EINTEGRITY 에러 해결 방법 (0) | 2020.09.22 |
[Angular5] translate 현재 사용중인 언어 가져오기 (0) | 2020.09.22 |