티스토리 뷰

## 학습 계기

우테코 프론트엔드 리액트 페이먼츠 미션을 진행 중,
인덱스 시그니쳐에 대한 피드백을 받아 ‘이펙티브 타입스크립트’를 통해 인덱스 시그니쳐에 대해 학습하게 되었습니다.

<br>

## 인덱스 시그니쳐란?

```tsx
export interface CardNumbers {
  first: string;
  second: string;
  third: string;
  fourth: string;
  [key: string]: string; // 이 부분이 인덱스 시그니쳐!
}
```

제가 작성한 타입 선언입니다. 맨 아래에 보면 `[key: string]: string` 과 같은 문장이 있는데 이러한 모양이 인덱스 시그니쳐입니다.

<br>

자바스크립트의 장점 중 하나는 바로 객체를 생성하는 문법이 간단하다는 것입니다.

```jsx
const CardNumbers = {
  first: '1111',
  second: '2222',
  third: '3333',
  fourth: '4444',
}
```

자바스크립트 객체는 문자열 키를 타입의 값에 관계없이 매핑합니다.

<br>

타입스크립트에서는 타입에 **‘인덱스 시그니처’**를 명시하여 유연하게 매핑을 표현할 수 있습니다.

```tsx
type IndexSignature = {[property: string]: string};

const CardNumbers: IndexSignature {
  first: '1111',
  second: '2222',
  third: '3333',
  fourth: '4444',
} // 정상적으로 동작
```

`[property: string]: string` 이 인덱스 시그니쳐이며, 다음 세 가지 의미를 담고 있습니다.

1. 키의 이름 `(property)` : 키의 위치만 표시하는 용도입니다. 타입 체커에서는 사용하지 않기 때문에 무시할 수 있는 참고 정보라고 생각해도 됩니다.
2. 키의 타입 `(string)` : string이나 number 또는 symbol 조합이어야 하지만, 보통은 string을 사용합니다.
3. 값의 타입 `(string)` : 어떤 것이든 될 수 있습니다.

<br>

이렇게 타입 체크가 수행되면 네 가지 단점이 드러납니다.

1. 잘못된 키를 포함해 모든 키를 허용합니다. `first` 대신 `First` 혹은 `FI~R@S%TㅋㅋT` 로 작성해도 유효한 타입이 됩니다.
2. 특정 키가 필요하지 않습니다. `{}` 도 유효한 타입입니다.
3. 키마다 다른 타입을 가질 수 없습니다. 예를 들어, `fourth` 는 `string` 이 아니라 `number` 여야 할 수도 있습니다.
4. 타입스크립트 언어 서비스는 다음과 같은 경우에 도움이 되지 못합니다. 예를 들어 `first:` 를 입력할 때, 키는 무엇이든 가능하기 때문에 자동 완성 기능이 동작하지 않습니다.

<br>

결론은 인덱스 시그니쳐는 부정확하므로 더 나은 방법을 찾아야 합니다.

보다 더 나은 방법을 이용하면 타입스크립트에서 제공하는 언어 서비스를 모두 사용할 수 있습니다. (자동완성, 정의로 이동, 이름 바꾸기 등)

<br>

## 잠깐! 그러면 인덱스 시그니처는 언제 사용하나요?

이펙티브 타입스크립트에서는 런타임 때까지 객체의 속성을 알 수 없을 경우에만 인덱스 시그니쳐를 사용하라고 권장합니다.

<br>

즉, 인덱스 시그니쳐는 동적 데이터를 표현할 때 사용합니다. 예를 들어 CSV파일처럼 헤더 행(row)에 열(column) 이름이 있고, 데이터 행을 열 이름과 값으로 매핑하는 객체로 나타내고 싶은 경우입니다.

<br>

일반적인 상황에서 열 이름이 무엇인지 미리 알 방법은 없습니다. 이럴 때 인덱스 시그니쳐를 사용합니다. 반면에 열 이름을 알고 있다면, 미리 선언해 둔 타입으로 단언문을 사용하면 됩니다. (물론 선언해 둔 열들이 런타임에 실제로 일치한다는 보장은 없습니다. 이 부분이 걱정된다면 undefined를 추가하면 됩니다.)

<br>

## 인덱스 시그니쳐 보다 더 나은 방법

첫 번째는 인터페이스를 사용하는 방법입니다.

```tsx
export interface CardNumbers {
  first: string;
  second: string;
  third: string;
  fourth: string;
}
```

<br>

두 번째는 Record를 사용하는 방법입니다.
Record는 키 타입에 유연성을 제공하는 제너릭 타입입니다. 특히, string의 부분 집합을 사용할 수 있습니다.

```tsx
type CardNumbers = Record<'first' | 'second' | 'third' | 'fourth', string>;
//  Type CardNumbers = {
//    first: string;
//    second: string;
//    third: string;
//    fourth: string;
//  }
```

<br>

세 번째는 매핑된 타입을 사용하는 방법입니다.
매핑된 타입은 키마다 별도의 타입을 사용하게 해 줍니다.

```tsx
type CardNumbers = {[k in 'first' | 'second' | 'third' | 'fourth']: string};
//  Type CardNumbers = {
//    first: string;
//    second: string;
//    third: string;
//    fourth: string;
//  }
```

<br>

이번 기회에 인덱스 시그니쳐를 학습하게 되었는데, 정말 잘했다는 생각이 들었다.
타입스크립트를 잘 사용하면 본인의 실수도 줄일 수 있지만, 함께 코드를 작성하는 동료의 실수도 잡을 수 있는 장점이 있다.
그러나 인덱스 시그니쳐를 사용하게 되면 이러한 장점들을 누리지 못할 것같다고 느낀다. (뭔가 as와 같은 타입 단언 느낌)

타입스크립트는 학습이 어렵지만, 점차 익숙해지면 매우 유용하고 재미있게 느껴진다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함