| 데코레이터
사전준비
tsconfig.json에서
아래 두개를 true로 변경해준다(주석을 풀어준다)
데코레이터란
데코레이터는 함수다
function InitClass(params: any){
console.log("InitClass :", params);
}
데코레이터는 무조건 class 와 만 같이 쓴다. (내부 외부, 멤버 변수, 메소드, 파라미터...)
@InitClass
class ExampleClass{
constructor() {}
}
실행시 에러가 뜰 경우 --experimentalDecorators 옵션을 앞에 붙여서 실행
실행결과를 보면
ExampleClass의 생성자 함수가 params로 들어간 것을 확인할 수 있다
코드 어디를 봐도
함수를 실행시킨다거나, new ExampleClass()와 같이 인스턴스화를 시킨적이 없다
그런데도 InitClass함수(데코레이터)가 실행되고 파라미터로 ExampleClass클래스의 생성자 함수가 데코레이터의 파라미터로 전달된 것을 알 수 있다
데코레이터는
런타임에 클래스에 붙어서 실행되는 함수이기때문이다
new Class() 인스턴스화 없이도 실행이 된다
아래 예제를 보자
function Controller(constructor: Function){
console.log("Controller : " , constructor);
}
function Get(params: any):any{
console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] ", params);
}
function Column(params: any):any{
console.log("Column !!",params);
}
@Controller
class ExampleController{
@Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
}
@Get("/api/v1/user")
getReq() {}
@Post("/api/v1/board")
postReq() {}
}
controller에만 생성자가 넘어가고
나머지는 () 값들만 파라미터로 넘어간다
데코레이터 펙토리
학창시절에 f(g(x)) 이런함수를 배웠을 것이다
이를 코드로 표현하면
f(g(x)) ----> f() { return g() }, g:데코레이터 함수
g ---> f(g(x)), f: 데코레이터 팩토리 (목적: 인자전달, param 전달)
아래와 같은 코드가 있다고 하자
controller에 string 값을 넘기고자 한다
function Controller(constructor: any):any{
console.log("Controller : " , constructor);
}
function Get(params: any):any{
console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] ", params);
}
function Column(params: any):any{
console.log("Column !!",params);
}
@Controller("/api/vi")
class ExampleController{
@Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
console.log('hi')
}
@Get("/user")
getReq() {}
@Post("/board")
postReq() {}
}
그럼 아래와 같은 결과가 나온다
string은 잘 넘어갔지만 자동으로 넘어갔던 생성자인 constructor가 안넘어간것을 알 수 있다
constructor도 함께 넘겨주기위해서는
아래와같이 Controller에 return으로 함수를 추가해주면되는데
function Controller(constructor: any):any{
console.log("Controller : " , constructor);
return (target: any) => {
console.log(target);
}
}
function Get(params: any):any{
//console.log("[GET] ", params);
}
function Post(params: any):any{
//console.log("[POST] ", params);
}
function Column(params: any):any{
//console.log("Column !!",params);
}
@Controller("/api/vi")
class ExampleController{
@Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
console.log('hi')
}
@Get("/user")
getReq() {}
@Post("/board")
postReq() {}
}
저 리턴함수의 파라미터로 constructor가 자동으로 넘겨진다
그러면 Controller는 데코레이터 팩토리가 되고
그안에 return()=>{} 은 데코레이터 함수가 된다
function Controller(constructor: any):any{
console.log("Controller : " , constructor);
return (target: any) => {
console.log(target);
}
}
function Get(params: any):any{
//console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] deco deco Factory", params);
return (
constructor: Function,
propertyKey: string,
descriptor: PropertyDescriptor,
)=> {
console.log("[POST] deco deco Func: ", constructor,propertyKey,descriptor);
};
}
function Column(params: any):any{
//console.log("Column !!",params);
}
function UserGuard(): any{
console.log("UseGuard Factory : ");
return (
constructor: Function,
propertyKey: string,
descriptor: PropertyDescriptor,
)=> {
console.log("UseGuard deco Func: ", constructor,propertyKey,descriptor);
};
}
@Controller("/api/vi")
class ExampleController{
@Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
}
@Get("/user")
getReq() {}
@Post("/board")
@UserGuard()
postReq() {}
}
결과를 보면
데코레이터 팩토리가 Top to Bottom 순서로 실행이 먼저되고
데코레이터함수들이 Bottom To Top 순서로 실행된다
전체를 감싸고있던 Controller 데코레이터 펙토리와 함수는
클래스 내부에 것들이 다 실행되고 나서야
제일 마지막에 실행된다
중요하게 봐야할것이 바로 constructor과 propertykey이다
먼저 property 를 보면
메소드 이름이 그대로 넘어가는 것을 알 수있다
그 다음 constructor를 보자
혹시몰라서
임의로 데코레이터 없는 메서드 hi() {} 만 달랑 추가하고 실행해봤다
hi도 들어가는 것을보니
ExampleController안에 있는 모든 메서드가 넘어가는 것을 알 수 있다
또한 다른 데코레이터함수들에도 모든 메서드가 동일하게 넘어가있는것을 확인할 수 있다
아래와 같이 활용할 수 있다
보통 constructor라고 안하고 target 이라고 적고 타입은 any를 적는다
그리고 메소드들에 console.log 추가하고 실행시켜보자
function Controller(constructor: any):any{
console.log("Controller : " , constructor);
return (target: any) => {
console.log(target);
}
}
function Get(params: any):any{
//console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] deco deco Factory", params);
return (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
)=> {
console.log("[POST] deco deco Func: ", target,propertyKey,descriptor);
target.getReq();
target[propertyKey]();
};
}
function Column(params: any):any{
//console.log("Column !!",params);
}
function UserGuard(): any{
// console.log("UseGuard Factory : ");
// return (
// constructor: Function,
// propertyKey: string,
// descriptor: PropertyDescriptor,
// )=> {
// console.log("UseGuard deco Func: ", constructor,propertyKey,descriptor);
// };
}
@Controller("/api/vi")
class ExampleController{
@Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
}
@Get("/user")
getReq() {
console.log('getReq method process!');
}
@Post("/board")
@UserGuard()
postReq() {
console.log('postReq method process!');
}
}
결과를 보면 Post 데코레이터 함수를 통해 다른 메소드를 실행 시키는 것을 확인할 수 있다
어떻게 보면
인스턴스화 시키지 않고
static 클래스를 불러서 그 클래스의 메소드를 실행시키는 것과 비슷하다고 볼 수 있다
데코레이터를 통한 함수 데이터 조작
데코레이터는 return으로 반드시 클래스를 반환해야하고
그 클래스는 파라피터로 받는 constructor를 상속받아야한다
function Controller(constructor: {new (email: string): any}):any{
return class extends constructor{
_email = "deco@naver.com"
}
}
function Get(params: any):any{
//console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] deco deco Factory", params);
return (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
)=> {
console.log("[POST] deco deco Func: ", target,propertyKey,descriptor);
target.getReq();
target.postReq();
};
}
function Column(params: any):any{
//console.log("Column !!",params);
}
function UserGuard(): any{
// console.log("UseGuard Factory : ");
// return (
// constructor: Function,
// propertyKey: string,
// descriptor: PropertyDescriptor,
// )=> {
// console.log("UseGuard deco Func: ", constructor,propertyKey,descriptor);
// };
}
@Controller
class ExampleController{
// @Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
}
// @Get("/user")
getReq() {
console.log('getReq method process!');
}
// @Post("/board")
// @UserGuard()
postReq() {
console.log('postReq method process!');
}
}
console.log(new ExampleController("example@inflearn.com"))
example@inflearn.com 을 입력했지만 출력은
deco@naver.com 로 출력되고 있다
만약
console.log(new ExampleController("example@inflearn.com"))
이 라인을 주석처리한다면
function Controller(constructor: {new (email: string): any}):any{
return class extends constructor{
_email = "deco@naver.com"
}
}
function Get(params: any):any{
//console.log("[GET] ", params);
}
function Post(params: any):any{
console.log("[POST] deco deco Factory", params);
return (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
)=> {
console.log("[POST] deco deco Func: ", target,propertyKey,descriptor);
target.getReq();
target.postReq();
};
}
function Column(params: any):any{
//console.log("Column !!",params);
}
function UserGuard(): any{
// console.log("UseGuard Factory : ");
// return (
// constructor: Function,
// propertyKey: string,
// descriptor: PropertyDescriptor,
// )=> {
// console.log("UseGuard deco Func: ", constructor,propertyKey,descriptor);
// };
}
@Controller
class ExampleController{
// @Column("email")
private _email: string;
constructor(email: string) {
this._email = email;
}
// @Get("/user")
getReq() {
console.log('getReq method process!');
}
// @Post("/board")
// @UserGuard()
postReq() {
console.log('postReq method process!');
}
}
//console.log(new ExampleController("example@inflearn.com"))
데코레이터를 사용했지만
아무것도 실행되지않는다
'웹 개발 > #️⃣ TypeScript' 카테고리의 다른 글
TS | Import 와 Export (0) | 2023.06.12 |
---|---|
TS | 제네릭 (0) | 2023.06.04 |
TS | key와 같이 쓰는 타입 (0) | 2023.06.03 |
TS | 대수타입 -union, intersection (0) | 2023.06.01 |
TS | 클래스 - readonly와 생성자 (0) | 2023.06.01 |