개인프로젝트를 하다가 유저 타입을 여러개 사용할 일이 있었는데,
유저 타입을 일일히 설정하고 싶지 않았다.
그래서 enum을 사용하려고 했다.
enum에 대한 장단점은 이 블로그에 잘 나와있다.
위 블로그를 읽어보고 백엔드는 enum을 쓰는게 효율적이겠다 싶어서 사용하려고 했다.
그래서 typescripte에서 enum을 사용하려고 했더니,
static 객체로 Enum을 다루기엔 기본 내장된 enum이 너무 별로라는 글을 보았다.
그래서 ts-jenum이라는 패키지를 설치해서 사용한다는 향로님 글을 보았다.
# 활용
## src/libs/util/enum.ts
import { Enum, EnumType } from 'ts-jenum';
@Enum('code')
export class UserType extends EnumType<UserType>() {
static readonly supervisor = new UserType('supervisor', '최고관리자');
static readonly employee = new UserType('employee', '직원');
static readonly admin = new UserType('admin', '관리자');
static readonly customer = new UserType('customer', '고객');
private constructor(
readonly _code: string,
readonly _name: string,
) {
super();
}
static isValid(userType: string): boolean {
// 유효한 userType 값인지 확인하는 로직 작성
return Object.values(UserType).find((enumValue) => enumValue.code === userType);
}
get code(): string {
return this._code;
}
get name(): string {
return this._name;
}
equals(code: string): boolean {
return this.code === code;
}
toCodeName() {
return {
code: this.code,
name: this.name,
};
}
}
위처럼 UserType에 대한 클래스를 작성하고 타입을 정의해줬다.
isValid는 입력한 유저타입이 맞는지 확인하는 로직이고
name이나 code, toCodeName() 메서드를 활용해서 결과값을 꺼내쓰면 된다.
import { UserType } from '~/libs/util/enum';
import ERROR_CODE from '~/libs/exception/errorCode';
import ErrorResponse from '~/libs/exception/errorResponse';
if (!UserType.isValid(userType)) {
return next(new ErrorResponse(ERROR_CODE.USER_TYPE_INVAILD_INPUT));
}
const userEnum = UserType[userType].name;
위처럼 유저타입이 맞는지 아닌지 isValid 메서드로 에러처리를 했다.
그리고 맞으면 db에 저장하기 위해 name을 뽑아썼었다.
# 수정할 점 1
// AS-IS
static isValid(userType: string): boolean {
// 유효한 userType 값인지 확인하는 로직 작성
return Object.values(UserType).find((enumValue) => enumValue.code === userType);
}
// TO-BE
static isValid(userType: string): boolean {
// 유효한 userType 값인지 확인하는 로직 작성
return (this.values() as UserType[]).some((enumValue) => enumValue.code === userType);
}
1. find -> some 사용
some 메서드가 조건을 만족하는지 확인하면서 중간에 조건을 만족하는 값을 찾으면 종료되므로 성능이 향상됨
2. Object -> this 사용
this.values를 사용하는 이유는 TypeScript에서 EnumType을 사용하는 클래스의 인스턴스 메서드를 정의할 때, 해당 인스턴스를 통해 Enum의 값에 접근하는 것이 더 일반적이고 명확하기 때문이다
this.values를 사용하면 EnumType 클래스의 인스턴스를 통해 Enum의 모든 값에 접근할 수 있으며, 이는 해당 EnumType 클래스의 현재 인스턴스를 나타낸다.
이렇게 함으로써 코드가 더 명확해지고 유지보수가 쉬워진다.
static readonly supervisor = new UserType('supervisor', '최고관리자');
참고로 () => readonly BodyType[]는 함수 타입을 나타낸다.
return (this.values() as UserType[]).some((enumValue) => enumValue.code === userType);
이 함수를 호출하면 배열이 반환이 되어서 (this.values())로 한번 더 감싸게 되었다.
# 수정할 점 2
# AS-IS
const userEnum = UserType[userType].name;
# TO-BE
const userEnum = UserType[userType].toCodeName();
console.log(userEnum.code); // 'employee'
console.log(userEnum.name); // '직원'
위 코드에서 UserType[userType]로 열거형 값을 가져온다.
그 후에 toCodeName 메서드를 호출하여 { code, name } 객체를 얻는다.
이렇게 하면 코드의 가독성이 향상되며, UserType 클래스의 내부 구현에 대한 의존성이 낮아진다.
아래처럼 작성되는 것이 UserType 클래스 내의 로직이 외부로 노출되지 않고 코드 일관성이 유지되는 방법이므로
코드가 한줄 추가되더라도 아래처럼 쓰도록 하자.
# enum 사용할때 value로 number를 string보다 추천하는 이유
- Type Safety: TypeScript에서 Enum 값에 숫자를 사용하면 타입 안정성을 유지할 수 있다. 숫자를 사용하면 해당 Enum 값 이외의 값이 들어오지 않도록 타입 검사가 가능합니다. 문자열을 사용할 경우 오타로 인해 의도치 않은 값이 할당될 수 있다.
- Enum Value의 의미 전달: 숫자를 사용하면 Enum 값이 어떤 의미를 가지고 있는지 조금 더 명시적으로 드러낼 수 있다. 1, 2, 3이 각각 어떤 의미를 가지고 있는지 이해하기 쉽다.(1에 가까울수록 상급자인 경우)
- Enum Size: 숫자는 일반적으로 문자열보다 더 적은 메모리를 사용한다. 특히 Enum이 큰 경우, 숫자를 사용하면 메모리 사용량을 감소시킬 수 있다.
- JSON과 호환성: 서버에서 전달되는 데이터 등을 다루는 경우, Enum 값이 숫자일 때 JSON과 더 잘 호환될 수 있다. 일부 언어에서는 Enum 값을 숫자로 더 쉽게 다룰 수 있다.
메모리, 타입 안정성, 호환성 등의 이유로 enum을 number로 선호한다는 글도 있었다.
근데, 나의 경우에는 number로 넣으면 데이터베이스에서 이 값이 뭔지 다시 확인해야하는 귀찮음이 있지만 string은 명시적이기 때문에 string을 더 선호하게 되는 것 같다.
# 참고
https://jojoldu.tistory.com/621
https://techblog.woowahan.com/2527/
https://www.reddit.com/r/learnpython/comments/unrfon/string_vs_integervalued_enums_a_discussion/
'JaveScript > ExpressJS' 카테고리의 다른 글
닉네임 랜덤 제너레이터 (0) | 2023.12.17 |
---|---|
dayjs 사용해서 나이 계산하기 (0) | 2023.12.14 |
expressjs + typeorm 0.3 세팅 후 사용법 (1) | 2023.12.12 |
morgan 세팅 (0) | 2023.11.30 |
ExpressJS 구조 파악하기. (0) | 2023.07.20 |