Node.js

[PostgreSQL] 내가 enum을 쓰지 않는 이유

임채성 2024. 4. 14. 18:57

이 글은 연산 속도에 관해 이야기하지 않습니다. 단순히 확장성과 용량에 대해서만 이야기합니다.

도입

유저의 권한이라는 요소를 열거형으로 관리할 지 고민하였습니다.
본 글에서는 선택적인 요소를 어떻게 처리하는 것이 좋을지 고민해본 내용을 정리해보려고 합니다.

 

본문

상세한 설명은 후술. 비교 표: 

방식 설명 저장 용량
SMALL INT 저장 용량이 가장 작음, 애플리케이션이 문자열과 매핑해주는 방식 2byte
ENUM 확장이 일어나면 DDL이 변경되는 방식, 확실한 무결성 4byte * 가짓수 n = 4n
VARCHAR 저장 용량이 큰 방식 (문자열 매핑은 필요없음) 4byte * 길이 n = 4n
별도 테이블 확장에 가장 열려있음. 저장 용량을 가장 많이 차지하는 방식 가장 큼

열거 요소로 enum을 선택하지 않은 이유

enum을 선택하지 않은 이유는 다음과 같습니다:

  • enum 요소는 확장에 닫혀있다.(핵심 이유)
  • enum 요소는 SMALL INT방식보다 2n배는 크다.

 

enum은 확장에 닫혀 있다.

enum 요소가 추가, 삭제, 수정되는 경우에는 DDL이 변경되어야합니다.
FK를 사용하는 경우에는 ON UPDATE/ON DELETE 제약 조건을 통해 무결성을 가져갈 수 있습니다. ENUM에서는 프로시저를 구현해야하므로 굉장히 복잡해집니다.

 

궁금할 수 있는 부분: postgreSQL에서는 enum을 type으로 별도로 선언할 수 있습니다. 그렇다면 type만을 변경하면 DDL 변경이 일어나지 않는다고 볼 수 있지 않나요?

 

enum type은 확장에 닫혀있다.

type 선언이라는 건 SQL(표준)에 없습니다.
DBMS에서 제공하는 일부 기능이라는 것이므로 타 DBMS로 마이그레이션 시 enum type이 없다면 일이 더 생길 뿐입니다.

 

정리

권한 요소를 enum으로 작업하게 되면 권한에 변경이 일어난다면 DDL이 변경되는 위험한 작업입니다.
유지보수가 힘들며, 마이그레이션 시 별도의 문제가 발생할 수도 있습니다.

 

고려해본 다른 요소들

우선 결론적으로 SMALL INT별도의 테이블로 구현하는 방식을 선택하였습니다. 

VARCHAR 방식

이 방식도 추천받았습니다.
추천자분의 말씀으로는 "VARCHAR는 값에 의도가 나타나므로 타 팀과의 커뮤니케이션 없이도 해당 값이 어떤 의미인지 쉽게 알 수 있다." 라고 하셨다. 저는 저장 용량 때문에 이 방식은 선택하지 않았습니다.

 

SMALL INT 방식 (채택)

애플리케이션에서 문자열과 매핑해줘야하는 방식이긴 하지만, 저장 용량이 가장 저렴하기 때문에 지속 운영 시 많은 메리트를 가져갈 수있을 것이라고 생각합니다.
다만 매핑할 문자열을 모든 팀에서 알고 있어야 하므로 문서화는 필수입니다.

 

별도의 테이블 (채택)

"하루종일 저장 용량을 논하더니 왜 테이블로 분리하는 것을 선택했냐?" 라고 의문을 가질 수 있을 것 같습니다.
이 방식은 타 방식들에 비해 확장에 가장 열려있기 때문이다. 예를 하나 들어보겠습니다:

현재 구현하고 있는 서비스에서 '카테고리' 라는 항목이 있다. 이 카테고리도 enum으로 선언할 수 있습니다.
그럼 요구사항을 확장해보자. 카테고리에 depth가 추가된다고 한다. 'IT/전자제품, IT/모바일' 확장될 수 있는 기획입니다.

다만 enum으로 하면 이를 해결하기 굉장히 어려워진다. '상위 카테고리와 하위 카테고리로도 조회할 수 있어야 한다'면?

 

위와 같이 쉽게 확장될 수 있는 경우에는 별도의 테이블 방식을 채택하기로 했습니다.

 

결론

절대로 enum이 small int, 별도 테이블 방식보다 별로라는게 아니다. 무결성을 확실히 가져갈 수 있는 장점도 있습니다.
다만 내가 생각했을 때는 무결성을 조금 잃더라도 저장 용량을 줄이는 것이 더 좋아보인다고 생각되었을 뿐입니다.

 

그래서 어떻게 관리할거임?

  • 프론트엔드: 항상 권한은 문자열로 요청/응답합니다. 프론트는 role이 어떻게 구현되어 있는지 알면 안됩니다.
  • 백엔드: 데이터 삽입 전 무결성 검증과 변환을 담당합니다. 내부적으로 enum을 가지고 있어서 role에 관한 요청온다면 int로 변환해줍니다.
  • 데이터베이스: 항상 smallint로 처리합니다.