오늘 한 일
오늘은 타입스크립트 인터페이스 부분 추가적인 공부와 제네릭, 데코레이터에 대해서 공부했다. 추가적으로 스파르타 강의를 다시 복습하면서 개인과제를 어떻게 만들면 좋을지에 대해 구상해봤다.
알고리즘 스터디를 하는 날이었는데 알고리즘 문제를 풀다가 아스키 코드로 풀면 좋을 것 같은 문제여서 아스키 코드에 대해 간략하게 정리해봤다.
배운 부분
아스키 코드
https://yoonchan1121.tistory.com/133
아스키코드[JS]
아스키 코드 아스키 코드(ASCII 코드)는 영문 알파벳, 숫자, 특수 문자 등을 컴퓨터에서 사용할 수 있도록 각 문자에 대응하는 7비트 또는 8비트의 이진수로 표현한 것이다. ASCII는 "American Standard Cod
yoonchan1121.tistory.com
강의 들으며 정리한 부분(Decorator)
데코레이터는 메타 프로그래밍에 매우 유용하게 사용될 수 있는 기능이다.
메타 프로그래밍이란?
데코레이터의 주 사용처가 페이지의 최종 사용자에게 직접적인 영향을 주는 곳이 아니다 라는 것.
데코레이터는 코드 작성에 특화된 도구로서 다른 개발자들이 사용하기 쉽게 만드는 것이 목적이다.
기본 설정
데코레이터를 사용하기에 앞서 tsconfig 파일에서 기본적으로 설정해주어야 하는 것들이 몇 가지 있다.
- target: es6
- experimentalDecorators: true (중요, 이거 설정 안하면 데코레이터 못씀)
데코레이터 만들기
첫 번째 만들어 볼 데코레이터는 클래스용 데코레이터.
데코레이터는 원래도 클래스에서 쓰는 것이지만 , 이건 그 중에서도 클래스 자체에 추가할 것이다
기본적인 Person 클래스를 하나 만들고 시작하자.
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
const pers = new Person();
console.log(pers);
데코레이터를 이제부터 만들어 볼건데 데코레이터에서 반드시 알아야 할 사실은 데코레이터는 결국 함수라는 것이다. 그 중에서도 특정 방식으로 클래스 같은 데 추가할 수 있는 함수이다.
function Logger() {
console.log('Logging...');
}
Logger라는 함수를 하나 만들고 이 함수를 직접 호출하지 않고 Person 클래스의 데코레이터로 추가해 볼 것이다.
@ 바로 뒤에는 반드시 함수를 지정해야 한다. 실행하는 게 아니라 지정하면 그 함수가 데코레이터가 된다.
데코레이터에는 실제로 인자가 필요하다. 필요한 인자의 개수는 데코레이터를 추가하는 대상에 따라 달라진다. 지금 데코레이터를 추가하려는 곳은 클래스이므로 인자가 1개 필요하다. 이 데코레이터의 대상(target)인 타깃 클래스의 생성자 함수를 넘겨줘야 한다. 따라서 이 함수의 인자로는 함수 타입을 받아야 한다.
function Logger(constructor: Function) { // 함수의 인자를 1개 설정해주었음.constructor에서 console.log가 실행되기 때문에 Function으로 넘겨준듯?
console.log('Logging...');
console.log(constructor);
}
@Logger
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
const pers = new Person();
console.log(pers);
데코레이터는 클래스가 인스턴스화 될 때가 아니라 정의될 때 실행된다.
따라서 클래스의 인스턴스를 생성할 필요가 없다. 클래스를 인스턴스화 하는 코드를 삭제해도 데코레이터의 로그는 여전히 출력된다.
데코레이터는 자바스크립트가 클래스 정의와 생성자 함수 정의를 만난 시점에 실행된다.
데코레이터를 만드는 또 다른 방법
데코레이터 팩토리
위와 같이 데코레이터를 만들 수도 있지만 데코레이터 팩토리를 정의할 수도 있다.
팩토리는 데코레이터 함수를 반환하는데 이를 데코레이터로 추가할 때 원하는 값을 설정할 수 있다.
위에서 만든 함수를 팩토리로 변환해 보도록 하자.
// 기존 코드
function Logger(constructor: Function) {
console.log('Logging...');
console.log(constructor);
}
@Logger
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
const pers = new Person();
console.log(pers);
매개변수로 받던 constructor을 return 문 함수의 매개변수로 넘겨주고 동작부분도 모두 return문의 함수에 넣어주었다.
// 바꾼 코드
function Logger(logString:string) {
return function(constructor:Function) {
console.log(logString);
console.log(constructor);
}
}
@Logger("LOGGING - PERSON")
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
const pers = new Person();
console.log(pers);
바꾼 코드에서 데코레이터를 적용하려면 이렇게 @Logger()처럼 함수 형식으로 실행해야 한다. 그래야 이 외부 함수를 실행해 반환 값이 내부 함수를 적용할 수 있기 때문이다.
추가적으로 팩토리 함수에 logString이라는 매개변수도 넣어줬는데 이렇게 사용하게 된다면 데코레이터 함수가 실행될 때 사용할 값을 팩토리 함수를 통해 지정할 수 있다는 점이 좋다.
이제 여기서 데코레이터를 호출해 데코레이터 함수를 실행하는 것이 아니라, 데코레이터 함수를 반환하는 함수를 실행하기 때문이다.
팩토리를 사용하면 이렇게 값을 전달해 내부에서 반환되는 데코레이터 함수에서 사용할 수 있다.
따라서 데코레이터 팩토리를 사용하면 좀 더 다양한 방식으로 데코레이터와 내부 수행 작업을 구성할 수 있다.
더 유용한 데코레이터 만들기
데코레이터를 만들어 html 코드로 된 템플릿을 렌더링 해보자.(hookId에 해당하는 DOM 요소에 렌더링 할 것이다)
function WithTemplate(template: string, hookId: string) {
return function (_: Function) {
const hookEl = document.getElementById(hookId);
if (hookEl) {
hookEl.innerHTML = template;
}
};
}
@WithTemplate('<h1>My Person Object</h1>', 'app')
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
// 브라우저에 Person Object 출력
- _ : 원래 이 자리에는 위에서 만들었던 데코레이터와 마찬가지로 constructor가 들어있었는데 이 함수에서는 사용하지 않기 때문에 constructor를 빼줬다. 하지만 이 자리에는 뭐가 들어오는지에 대해서는 명시해줘야하기 때문에 _를 적어준 것이다. _의 의미는 뭐가 들어올 지에 대해서는 인지하고 있지만 사용하지 않을 것이다! 라는 의미를 내포하고 있다.(타입스크립트에게 말해주는 것임).
현재 constructor를 안써준 이유는 데코레이터를 통해 단순히 템플릿을 렌더링하는 것 뿐이기 때문이다.
이렇게 작성하게 된다면 데코레이터를 통해 My Person Object가 브라우저에 렌더링 되는 것을 확인할 수 있다.
조금 다르게 만들 수도 있는데 아까 사용하지 않았던 constructor를 사용해서 클래스가 인스턴스화 될 때 name에 있는 값을 브라우저에 렌더링하게 만들 수도 있다.
function WithTemplate(template: string, hookId: string) {
return function (constructor: any) {
const hookEl = document.getElementById(hookId);
const p = new constructor();
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = p.name;
}
};
}
// @Logger()
@WithTemplate('<h1>My Person Object</h1>', 'app')
class Person {
name = 'Max';
constructor() {
console.log('Creating person object..');
}
}
// 브라우저에 Max 출력
여러 데코레이터 추가하기
데코레이터를 사용할 수 있는 곳이라면 어디든지 1개 이상의 데코레이터를 추가할 수 있다.
여러 개의 데코레이터를 사용한다고 했을 때 데코레이터가 실행되는 순서는 아래 작성한 데코레이터가 먼저 실행된다.
@Logger()
@WithTemplate()
class Person {...}
이렇게 코드가 작성되어 있다고 가정했을 때, WithTemplate 데코레이터가 먼저 실행되고 Logger 데코레이터가 나중에 실행된다.
데코레이터 팩토리는 실행 순서가 다르다.
데코레이터 팩토리 같은 경우에는 일반적인 자바스크립트 규칙이 적용되어 위에 있는 함수가 아래 있는 함수보다 먼저 실행된다.
function Logger() {
console.log('LOGGER FACTORY');
return function (constructor: Function) {
console.log('Logging...');
console.log(constructor);
};
}
function WithTemplate(template: string, hookId: string) {
console.log('TEMPLATE FACTORY');
return function (constructor: any) {
const hookEl = document.getElementById(hookId);
const p = new constructor();
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = p.name;
}
};
}
따라서 LOGGER FACTORY가 먼저 실행되고 그 다음 TEMPLATE FACTORY가 실행된다.
'📁Dev Log > TIL' 카테고리의 다른 글
2024_01_02 TIL (1) | 2024.01.03 |
---|---|
2023_12_29 TIL (1) | 2023.12.30 |
2023_12_27 TIL (2) | 2023.12.28 |
2023_12_26 TIL (2) | 2023.12.27 |
2023_12_22 TIL (1) | 2023.12.23 |