[Typescript] 고급 타입 - enum

mainsain·2024년 2월 8일
0

Typescript

목록 보기
3/8
post-thumbnail

enum을 왜 사용해야할까?

type CountryCode = 'AR' | 'CN' | 'JP' | 'KR' | 'GU' | 'GT' | 'GN' | 'US'
const countryCode: CountryCode = 'AR'

국가 코드가 들어가는 변수에 대한 타입을 지정해줄 때, 위처럼 union type으로 CountryCode를 만들고 이를 변수에서 사용할 수 있다. 그러나 구분하기 쉽지 않기에, value설명으로 key값을 활용하는 방법을 소개한다.

enum 타입

enum타입은 열거형이라고도 부르는데, 특정 값들의 집합(숫자형 vs 문자열형)을 의미한다.

enum ProgrammingLanguage {
  Typescript, // 0
  Javascript, // 1
  Java, // 2
  Python, // 3
  Kotlin, // 4
  Rust, // 5
  Go, // 6
}

// 각 멤버에게 접근하는 방식은 자바스크립트에서 객체의 속성에 접근하는 방식과 동일하다
ProgrammingLanguage.Typescript; // 0
ProgrammingLanguage.Rust; // 5
ProgrammingLanguage["Go"]; // 6

// 또한 역방향으로도 접근이 가능하다
ProgrammingLanguage[2]; // “Java”

타입스크립트의 enum은, 각 멤버의 값을 스스로 추론한다. 기본적인 방식은 0부터 1씩 늘려가며 값을 할당하는 것이다.

enum ProgrammingLanguage {
  Typescript = "Typescript",
  Javascript = "Javascript",
  Java = 300,
  Python = 400,
  Kotlin, // 401
  Rust, // 402
  Go, // 403
}

또한 각 멤버에 명시적으로 값을 할당할 수 있고, 일부 멤버에 직접 할당하지 않아도 이전 멤버를 기준으로 1씩 늘려가며 자동으로 할당한다. (당연히 문자열 enum은 자동증가 기능이 없다.)

enum의 주 사용처

주로 문자열 상수를 생성하는 데 사용된다. 응집력있는 집합 구조체를 만들 수 있으며, 사용자의 입장에서도 간편하게 활용할 수 있다.
또한 열거형은 그 자체로 변수 타입으로 지정할 수 있다. 이때 열거형을 타입으로 가지는 변수는, 해당 열거형이 가지는 모든 멤버를 값으로 받을 수 있다.

enum ItemStatusType {
  DELIVERY_HOLD = "DELIVERY_HOLD", // 배송 보류
  DELIVERY_READY = "DELIVERY_READY", // 배송 준비 중
  DELIVERING = "DELIVERING", // 배송 중
  DELIVERED = "DELIVERED", // 배송 완료
}

const checkItemAvailable = (itemStatus: ItemStatusType) => {
  switch (itemStatus) {
    case ItemStatusType.DELIVERY_HOLD:
    case ItemStatusType.DELIVERY_READY:
    case ItemStatusType.DELIVERING:
      return false;
    case ItemStatusType.DELIVERED:
    default:
      return true;
  }
};

checkItemAvailable(ItemStatusType.DELIVERY_HOLD);
checkItemAvailable("DELIVERED"); // Error
  • 타입 안정성 : ItemStatusType에 명시되지 않은 다른 문자열은 인자로 받을 수 없다. (enum을 사용하는 쪽에서 오타를 낼 확률이 0%이다. 무조건 enum값으로 써야하기 때문에.)
  • 명확한 의미전달과 높은 응집력 : ItemStatusType 타입이 다루는 값이 무엇인지 명확하다. key값에 의미 있는 값을 부여할 수 있다.
  • 가독성 : 응집도가 높기에 말하고자 하는 바가 명확하다.

⚠️ enum 주의사항

자동 추론

// enum ProgrammingLanguage {
//   Typescript, // 0
//   Javascript, // 1
//   Java, // 2
//   Python, // 3
//   Kotlin, // 4
// }

// ProgrammingLanguage.Rust; // 3

enum ProgrammingLanguage {
  Typescript, // 0
  Javascript, // 1
  Temp, // 2
  Java, // 3
  Python, // 4
  Kotlin, // 5
}

ProgrammingLanguage.Python; // 4
  • 동료 개발자가 enum중간에 Temp를 추가했다면, ProgrammingLanguage.Python = 3ProgrammingLanguage.Rust = 4가 되어버린다. 코드리뷰에서도 무심코 건너가버릴 수 있는 문제 중 하나이다.

역방향 접근(Reverse Mapping)으로 인한 문제

enum ProgrammingLanguage {
  Typescript, // 0
  Javascript, // 1
  Temp, // 2
  Java, // 3
  Python, // 4
  Kotlin, // 5
}

console.log(ProgrammingLanguage[10000]); // undefined
  • 리버스 매핑 시 할당된 값을 넘어서는 범위로 접근을 하더라도, 타입스크립트는 막지 않는 문제가 있다.
enum ProgrammingLanguage {
  Typescript, // 0
  Javascript, // 1
  Temp, // 2
  Java, // 3
  Python, // 4
  Kotlin, // 5
}

console.log(Object.keys(ProgrammingLanguage));
// [
//   '0',          '1',
//   '2',          '3',
//   '4',          '5',
//   'Typescript', 'Javascript',
//   'Temp',       'Java',
//   'Python',     'Kotlin'
// ]
  • 위 코드는 놀랍게도 객체의 키를 뽑아보면 저렇게 나온다.
  • 리버스 매핑이 가능하기에, 결과가 저렇게 나오며 Object.values키워드를 사용해도 동일하다.

const enum

const enum ProgrammingLanguage {
  // ...
}

console.log(Object.keys(ProgrammingLanguage)); // Error
console.log(ProgrammingLanguage[0]); // Error

const enum을 활용해 열거형을 선언해버리면, 리버스 매핑을 막아버릴 수 있다. 자바스크립트에서의 객체에 접근하는 것과 유사하게 동작한다.

이처럼 사용하는데에 주의가 필요하다. 따라서 typescript에선 enum을 사용하면 안된다는 말이 나오고 있으며, 우아한 형제들은 어떻게 받아들이는지 알아보자.

🎙️ 우아한 형제들 인터뷰 (우아한 타입스크립트 with 리액트)

❓ 팀 내에서 enum과 유니온 타입을 사용하나요?

배달이팀 : 딱히 제한은 없는데 enum을 선호하는 편이에요. IDE에서 지원을 다 해주긴 하는데 유니온 타입은 내가 어떤 타입을 가졌는지 전부 기억해야하고, 변경이 필요하면 사용되는 곳을 모두 찾아서 바꿔야할 때가 있어요. 특히 string 타입의 유니온 타입은 리팩터링하기에 번거로운점이 많은 것 같아요. 유니온 타입은 타입이니까 순회가 안 되지만, enum은 값이기 때문에 이터러블하죠.
즉, enum은 값이기 때문에 검증이 가능합니다. 하지만 enum은 정의부를 바꾸면 알아서 사용하는 쪽에서도 변경이 되니까 편한 것 같아요. 그래서 넓은 범위에서 확장해서 써야한다면 enum을 사용하고 있습니다. 또한 코드 가독성이 유니온 타입에 비해서 더 좋은 점도 있는 것 같아요. 우리 팀은 DX(개발경험)적인 측면에서 enum을 더 선호한다고 할 수 있겠네요.
물론, enum은 트리쉐이킹이 안되기때문에 번들 사이즈에 영향을 줄 수 있지만 const enum을 사용하면 해결할 수 있어요. 사실 enum을 쓴다고 해서 전체 파일의 번들 사이즈가 서비스에 영향을 미칠정도는 아니라 크게 고민하진 않아요.

  • 트리쉐이킹 : js, ts에서 사용하지 않는 코드를 삭제하는 방식이다.

냥이팀 : enum은 잘 사용하지 않았아요. 타입스크립트에서 타입을 선언하는 용도로 enum이 있어야하는지 잘 모르겠어요. enum이 들어갈 수 있는 값을 타입으로 강제해놓고 객체로 만들어야 맞지 않나 하는 생각을 많이 했어요.
enum은 타입을 위한 문법이라기보다 개발을 위한 문법 같아요. enum의 기능이 타입스크립트 컴파일러에 의해 동작하는 것이 이상하게 느껴집니다. 예를 들어 enum의 리버스 매핑 기능은 컴파일러에서 처리되면 안되는 동작이라고 생각해요. 그래서 사용하지 않는 편이에요.
타입스크립트에서 처음 배울 때 타입스크립트를 자바스크립트에서 실행되는 구문이라고 생각해서 에러를 내는 경우가 많은데 enum도 비슷한 것 같아요.

메이팀 : 우리 팀도 enum은 지양하고 있어요. enum은 사용하기엔 되게 편한데 자바스크립트로 컴파일될 때 IIFE로 바뀌는게 크진 않지만 성능에 영향을 줄 수 있다는 것을 본 것 같아서 그 이후론 습관적으로 안 쓰기 시작했어요.

❓ enum외에 const enum을 사용하나요?

배달이팀 : 우리 팀은 enumaration 폴더를 따로 만들어서 사용하고 있습니다. 이때 이 폴더에 정의한 enum을 외부에서 전역적으로 참조할 때는 const enum을 사용합니다. const enum은 빌드 과정에서 참조 값만 남기기 때문에 트리쉐이킹이 된다는 장점이 있습니다. 또한 어차피 enum도 상수를 쓰기 위한 것으로 생각하기 때문에 const enum을 사용하는게 적절하다고 판단됩니다. 물론 isolate모드를 활성화하고 const enum을 쓰면 안된다는 의견도 있지만, 현재로서는 더 명확하고 정적(static)인 값을 사용할 수 있다는 장점이 더 큰 것 같아요.

왕팀 : 우리는 const enum을 사용하지 않습니다. const enum은 enum과 다르게 직접적인 값으로 치환되기 때문에 전체 네임스페이스에 접근하지 못하고 순회할 수도 없다는 단점을 가지고 있는 것 같아요.

레퍼런스

https://velog.io/@jay/typescript-enum-be-careful
https://techblog.woowahan.com/9804/#toc-1

profile
새로운 자극을 주세요.

0개의 댓글