# Reflect-metadata

## reflect-metadata란

> TypeScript(or JS)에서 데코레이터를 활용하여 메타데이터를 관리하기 위해 사용되는 라이브러리

* 자바스크립트의 [Reflact](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 객체를 이용하여 클래스 또는 함수에 메타데이터를 추가하거나 수정할 수 있게 해줌
* 데코레이터를 추가하여 런타임 때 코드의 동작을 수정하는것

{% hint style="info" %}
[**Metadata**](https://ko.wikipedia.org/wiki/%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0) → "데이터의 대한 데이터", "어떤 목적을 가지고 만들어진 데이터", "데이터의 추가 정보"

즉,  메타데이터는 타입스크립트 데코레이터를 통해 추가된 클래스, 메서드, 프로퍼티의 부가적인 정보를 의미한다.
{% endhint %}

## Reflect

> ES6에서 도입된 내장 객체
>
> 리플렉션은 코드가 실행되는 동안 스스로 검사하고 수정할 수 있는 기능

자바스크립트에서 메타프로그래밍을 할 때 사용 된다.

### 메타데이터 추가

> key: value 구조의 메타데이터 추가
>
> metadataKey에 식별되므로 원하는 만큼 메타데이터를 추가할 수 있다.

```javascript
Reflect.defineMetadata(metadataKey, metadataValue, target);

REflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
```

### 메타데이터 가져오기

> 대상의 특정 메타데이터 키에 식별되는 메타데이터 값 추출

```javascript
const meta = Reflect.getMetadata(metadataKey, target);

const meta = Reflect.getMetadata(metadataKey, target, propertyKet);
```

### 그 외 다양한 api

```javascript
// 메타데이터 키가 있는지 확인 -> boolean
const result = Reflect.hasMetadata(metadataKey, target);

// 개체의 정의된 모든 메타데이터의 키 가져오기 -> Array
const metadataKeys = Reflect.getMetadataKeys(target);

// 특정 키의 메타데이터 삭제 -> boolean
const result = Reflect.deleteMetadata(metadataKey, target);
```

{% hint style="info" %}
**메타 프로그래밍이란?**

프로그램이이 자신의 코드를 분석해서 수정하는 방식으로 자기 자신(프로그램)을 다루는 코드를 작성하는 패턴
{% endhint %}

## Decorator(@)

> 함수 또는 클래스 선언이나 클래스 멤버 위에 추가하는 주석으로, 해당 개체의 동작 방식을 변경하는 **함수**

[타입스크립트 "데코레이터"의 해부학 및 사용 패턴](https://medium.com/jspoint/anatomy-of-typescript-decorators-and-their-usage-patterns-487729b34ae6)

놀랍게도 TS에만 있는 기능이 아닌 네이티브한 JS 기능(표준화 X)이라고 한다.

`@` 접두사를 사용하는 **자바스크립트 함수, 전달받은 인자(클래스, 함수)의 동작을 수정하여 반환한다.**

즉, 클래스나 함수 위에 배치하면 런타임때 해당 함수, 클래스를 인수로 사용하여 호출되고 전달받은 함수,클래스의 동작을 수정하여 반환한다.

```tsx
@singleton()
class SingletonClass {
  //...
}
```

TS에서 데코레이터 기능을 사용하려면 `tsconfig.json` 데코레이터 관련 옵션을 다음과 같이 추가해주어야 한다.

```json
{
  "compilerOptions": {
    "experimentalDecorators": true, // 데코레이터(실험적인) 사용 여부
    "emitDecoratorMetadata": true, // 데코레이터에서 메타 데이터 추출 가능
  }
}
```

### Reflect.metadata()

> Reflect.metadata()는 decorator(@)를 만드는 메서드
>
> 내부적으로 데코레이션하는 개체에 메타데이터를 추가하는 `Reflect.defineMetadata`를 구현한다

```typescript
@Reflect.metadata(metadataKey, metadataValue)
class DecorationClass {
  @Reflect.metadata(metadataKey, metadataValue)
  decorationMethod(){...}
}
```

함수로 만들어서 사용&#x20;

```typescript
function fooDecorator() {
  return Reflect.metadata('foo', 'bar');
}

@fooDecrator()
class WoongClass { }

Reflect.getMetadata('foo', WoongClass); // 'bar';
```
