Language/Ts,Js

TypeScript - Enum 으로 상수 안전하게 관리 하기

Integer Essence 2023. 8. 5. 17:40

 

 

시작 전 체크 사항

 

상수란?  대문자로 선언하며 변하지 않는 값 을 의미한다.

 

 

매직 넘버, 매직 리터럴 이란?  의미있는 상수로 변환 될 수있는 숫자, 문자 를 말한다.  

 

 

Enum 이란? 열거형 이라고도 하며  의미있는 상수들의 집합을 관리할 수 있다.

 

 

결론 : 매직넘버 , 매직 리터럴 대신 상수를 써야되고 상수를 Enum으로 한꺼번 에 모아서 관리하면 유지보수에 편할것이다.

 

 

 

Enum의 종류 

 

Enum의 기본사용(선언,할당)

 

enum Direction {
    Up=1,
    Down,
    Left,
    Right,
}

공부하면서 안 것 이지만 초기화를 안해도된다.

아예 저기서 1 마저도 초기화를 안하면 자동으로 0,1,2,3 이들어간다 

 

 

숫자 열거형 과 문자열 열거형 그리고 이종 열거형

 

별거 없다 할당이 숫자로만 이루어진게 숫자형 enum 이고 문자로만 이루어진게 문자형 enum이다.

 

두가지를 섞어 쓰는게 이종 열거형 이다. 

 

 

숫자 열거형

 

숫자의 경우에는 계산된값이 사용될수도있다.

 

 

다만 첫번째에 계산된 값을 사용할경우 다음 값은 초기화 가 필요하다.

 

enum E {
    A = getSomeValue(),
    B, // 오류! 앞에 나온 A가 계산된 멤버이므로 초기화가 필요합니다.
}

 

 

이 이유는 다음과 같다. 

 

 

1. 중복된 값 방지 (서로 같은 값이면 상수가 의미가 없어짐)

2. 순차적인 값 보장

 

enum Direction {
    Up = 1,
    Down,
    Left,
    Right,
}

 

 

보통 숫자 열거형은 이런식으로 하나만 초기화 해놓으면 알아서 뒤에는 2,3,4 (DOWN=2 , LEFT=3 , RIGHT=4) 같은 형식으로 순차적으로 증가하는 형태라고 함 그래서 순차성 보장을 위해서 다음 값은 초기화가 필요하다.  

 

 

문자열 열거형

숫자 열거형 같은 자동 증가 같은 기능이없지만 

초기화된 값과 무관하게 유의미하고 읽기 좋은 값으로 이용할 수 있다. (=직렬화)

 

개인적으로 이 읽기좋은 값으로 바꾸는 게 은근히 재미있다.

 

 

이종 열거형

 

이종 열거형 처럼 섞어쓰는건 앵간하면 권장하지 않는다고 한다. 

 

 

 

권장하지않음

 

const 열거형 

 

이건 프로젝트 하다가 발견한 것인데. const enum 으로 열거형을 선언하게 되면 컴파일 시에 완전히 제거 되어 런타임시 추가 비용이 없다고 한다.

 

무슨 의미냐? 

const enum Direction {
  Up=1,
  Down=2,
  Left=3,
  Right=4,
}

let directions = [Direction.Up, Direction.Down, Direction.Left, Direction.Right];

 

로 코드를 작성했다면 실제 컴파일 된 파일에서는 싹 다 사라지고 

 

var directions = [1,2,3,4];

 

가 된다는 의미다. 

 

 

이걸로 알 수있는건 const 키워드가 없는 enum 은 

런타임에 존재하는 실제 객체라는 사실이다.

 

 

 

Enum 으로 확장성 고려해서 안전하게 상수 관리하기 

 

열거형과 keyof

 

keyof ? 언젠가 프로젝트에서 써봤는지는 모르겠으나 아무튼 정리해서 적어보는 건 이번이 처음이다.

Object.keys() 와 비슷하다. 

 

type Person = {
  name: string;
  age: number;
};

type PersonKeys = keyof Person; // "name" | "age"

타입으로 쓰면 이렇게 된다

 

열거형 도 마찬가지 

 

enum LogLevel {
    ERROR, WARN, INFO, DEBUG
}

/**
 * 이것은 아래와 동일합니다. :
 * type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
 */

 

 

in 과 as 의 차이 ?

 

상수를 매핑하다보니 key 에 도 타입선언 을 해줘야됬는데 여기서 갑자기 in 과 as의 차이가 갑자기 궁금해졌다.

 

[key in keyof typeof RequestCategoryNames]: string;

 

in 의 경우 기존 타입을 기반으로 새로운 타입을 형성하는데 쓰이고 

 

[key as keyof typeof RequestCategoryNames],

 

as 의 경우 이 값이 다음과 같은 값일 것이다라는 확정을 강제로 시켜주는 것이다. 

 

 

확장성을 고려한 enum? 

 

이건 사실 나의 표현은 아니다. 나는 상수 값은 변하면 안된다고 생각했고 고정된 값이라 생각해서 상수에 확장성이라는 건 생각해본적이없다.

 

단지 in과 as 키워드에 관해서 물어보다가 GPT 녀석이 확장성이라는 말을 하길래  갑자기 꽃혀서 

 

GPT에게 물어봤다 상수가 동적 이여도 되는거야?

 

 

"상수"라는 용어는 불변성을 의미하며, 한 번 할당된 후 값이 변경되지 않아야 한다는 것을 의미합니다. 그러나 상수의 값이 컴파일 타임에 알려져 있어야 한다는 의미는 아닙니다. 상수는 런타임에 계산될 수 있으며, 중요한 것은 그 값이 프로그램 실행 중에 변경되지 않는다는 것입니다.



GPT의 의견인데 들어보니 또 나에겐 새로운 관점이였고 틀린 말은 아니라고 생각되었기에 적어본다. 

 

단지 순수 100프로 내의견 입니다 라고 야부리 를 털지 않는것은 나는 주니어 이기 때문에 함부로 확신 해선 안된다고 생각해서 그렇다. 

 

나의 경우는 어떻게 확장성을 고려하여 enum 을 사용하여 상수를 만들었냐면

 

문자열 열거형을 만들고. 

 

만든 문자열 열거형을 토대로 객체를 매핑 한다음.

 

문자열 열거형과, 매경된 객체를 entries 나 배열 고차함수를 사용하여 동적으로 생성하고 Object.freeze 안에 가둬 둠으로써 변경을 방지한 형태로 사용했다.  

 

 


참조
-  타입스크립트 핸드북 한글 문서(대부분의 예제는 다 여기서 가져옴)

https://typescript-kr.github.io/pages/enums.html


내 경험 및 리팩토링 과정에서 얻은 생각 + GPT