[Typescript] Enum 왜 쓰지 말아야하죠?

센코·2022년 1월 11일
22

** 급하신 분들은 맨아래 union Type을 사용하는 방법 및 결론을 바로 보세요~

발단

프로젝트를 진행 중 ts의 enum을 사용하지 않고 상수를 const object를 통해서 관리가 되고 있었습니다.
따라서, type | interface정의 할 경우, 정의된 상수의 를 사용하지 못하는 경우가 발생했습니다.

const DIRECTION = {
	UP  = 0,
	DOWN = 1,
	LEFT = 2,
	RIGHT = 3,
};

type TypeFoo = {
	direction: DIRECTION.UP;
	// ERROR: Cannot find namespace 'DIRECTION'.(2503)
}

위와 같은 방식의 문제가 발생했습니다.
그래서 enum 사용의 건의하였지만 성능적인 문제로 인하여 사용하지 않는다! 라고 들어서 알아보게 되었고 LINE Blog에서 TreeShaking과 여러 이유로 사용하지 않는 것을 권장했습니다. 검증해보고 추가적으로 어떠한 문제가 있고 그것을 해결할 방안에 대해서 생각해봅시다.

전체적인 내용은

블로그의 내용들에 대해 확인 및 검증 진행
어떻게 대처할 것인가?
결론 (최종적으로 어떻게 적용해 나아갈것 인가?)

에 대해 작성해보려고 합니다.

1. Enum은 왜 문제가 있는가?

enum과 같은 경우는 Typescript에서 enum이라은 열거형기능을 만들기 위해서 생성해낸 기능으로 다시 js로 트랜스파일이 되면서의 구조로 인해서 rollup과 같은 번들러에서 TreeShaking을 못하게 되는 문제가 있습니다. 추가적으로 트랜스파일 되면서 즉시실행함수의 형태로 트랜스되고 이는 Rollup에서는 IIFE를 ‘사용하지 않는 코드’라고 판단할 수 없게 됩니다. 그래서 TreeShaking이 안되게 됩니다.
하지만 제가 테스트 했을 경우는 webpack5에서 되는것으로 확인됩니다.

하지만 찾아보니 설정 및 상황에 따라 안되는 경우가 있는것으로 보입니다. enum으로 인해 생긴 즉시실행함수일 경우, 왜 못하는지도 좀 상세하게 나와있지만 이런것들로 const enum이 나왔기에 그렇구나~ 정도하고 넘어가겠습니다.
URL: https://stackoverflow.com/questions/68720866/why-does-webpack-5-include-my-unused-typescript-enum-exports-even-when-tree-sha

// Typescript
enum DIRECTION {
	UP = 0,
	DOWN = 1,
	LEFT = 2,
	RIGHT = 3,
}

// 트랜스파일
var DIRECTION;
(function (DIRECTION) {
    DIRECTION[DIRECTION["UP"] = 0] = "UP";
    DIRECTION[DIRECTION["DOWN"] = 1] = "DOWN";
    DIRECTION[DIRECTION["LEFT"] = 2] = "LEFT";
    DIRECTION[DIRECTION["RIGHT"] = 3] = "RIGHT";
})(DIRECTION || (DIRECTION = {}));
  • Rollup에서는 트리쉐이킹이 안된는 것을 테스트 해볼 수 있다.
  • 하지만 webpack에서는 되는것 같아보인다.

사용 안할 경우

사용 할 경우

추가적으로 number일 경우 enum형은 안전하지 않을 수 있습니다.

    enum NUM {
    	ZERO = 0,
    	ONE = 1,
    }
    const num: NUM = 2; // no Error
  • 이처럼 형태의 안정성을 위해서는 Union Type의 사용을 권장합니다.

2. 대처 방법

1. const enum을 사용하는 방법입니다.

const enum은 union type 처럼 Treeshaking이 정상적으로 되며, 기존의 함수가아닌 직접 메핑이 이루어지는것을 볼 수 있습니다.

//TypeScript .../constants.ts
export const enum DIRECTION {
	UP = 0,
	DOWN = 1,
	LEFT = 2,
	RIGHT = 3,
}

//index.js
import { DIRECTION } from '.../constants';
console.log(DIRECTION.UP);

//트랜스파일
console.log(0 /* UP */);
  • 트리 쉐이킹도 잘됩니다.

  • const enum을 사용할 경우 (865바이트(디스크에 4KB 있음))

  • BUT const enum은 Babel로 트랜스파일 될 수 없습니다.

    • babel-plugin-const-enum를 사용하여 const enum → enum 또는 const enum → const object로 변경을 진행 시켜서 사용해야합니다.
  • --isolatedModules 옵션 사용 시 declare, *.d.ts Ambient Context 영역에서 선언된 const Enum을 다른 곳에서 사용하려고 할 경우, Error 발생합니다. 또한 Ambient Context 밖에서도 이상하게 동작하곤 한다고 합니다. 그냥 다른 모듈에 const enum이 선언되었다면 그 모듈도 실행해야되지만 isolatedModules 일 경우, 실행할 수 없게된다.

2. union Type을 사용하는 방법

위처럼 enum에는 여러 사용에 있어 문제를 갖고 있습니다.
따라서 union Type의 사용을 가장 권장한다고 합니다.

//TypeScript .../constants.ts
export const DIRECTION = {
	UP: 0,
	DOWN: 1,
	LEFT: 2,
	RIGHT: 3,
} as const;
type DIRECTION = typeof DIRECTION[keyof typeof DIRECTION];

// index.js
import { DIRECTION } from '.../constants'

//트랜스파일
var DIRECTION = {
    UP: 0,
    DOWN: 1,
    LEFT: 2,
    RIGHT: 3,
};
  • 트리 쉐이킹도 잘됩니다.

  • 사용할 경우 (3,220바이트(디스크에 4KB 있음))

    • 번들링 된 코드
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "DIRECTION": () => (/* binding */ DIRECTION)
/* harmony export */ });
// export const enum DIRECTION {
// 	UP = 0;
// 	DOWN = 1;
// 	LEFT = 2;
// 	RIGHT = 3;
// }
var DIRECTION = {
	UP: 0,
	DOWN: 1,
	LEFT: 2,
	RIGHT: 3,
};

/***/ })
/******/ 	]);
/***************************************매우김...*********************************/
  • 문제가 되었던 type에서 상수를 사용하기 됩니다.(형태의 안정성도 보장됩니다.)

결론

몇가지의 제약 사항이 있지만 const enum이 선언할 때, 파일 크기에 있어서도 더 효율적인 모습을 보이는 것은 사실입니다.
또한 처음에 제기되었던 type에서 상수 사용는 경우에도 잘동작이됩니다.
하지만 babel 및 enum의 number type 사용할 경우의 문제등이 있어 사용하기 꺼려지는 것이 사실입니다.
결국 enum의 number일 경우 때문에 사용에 있어 type정의에서 사용되기 부적절할것으로 생각됩니다.

그래서!!! LINE Blog에 설명되었던 것 처럼 Union type > const enum 이렇게 사용되는게 좋을 것으로 보입니다!
하지만, 팀내부에서 상의에 따라서 babel 설정 이후, const enum 내부에 string만 있을 경우는 사용하고 number가 들어갈 경우, Union type을 사용하는 규칙을 정해 놓고 사용한다면 충분히 좋은 방향으로써 사용될 수 있다고 생각합니다.

환경

"devDependencies": {
    "ts-loader": "^9.2.6",
    "typescript": "^4.5.4",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
}

참조

profile
안녕하세요! 프론트엔드개발자입니다.

1개의 댓글

comment-user-thumbnail
2023년 1월 17일

여기도 참 잘 정리되어 있습니다. https://techblog.woowahan.com/9804/

답글 달기