[PostgreSQL] 내가 enum을 쓰지 않는 이유
이 글은 연산 속도에 관해 이야기하지 않습니다. 단순히 확장성과 용량에 대해서만 이야기합니다.
도입
유저의 권한이라는 요소를 열거형으로 관리할 지 고민하였습니다.
본 글에서는 선택적인 요소를 어떻게 처리하는 것이 좋을지 고민해본 내용을 정리해보려고 합니다.
본문
상세한 설명은 후술. 비교 표:
방식 | 설명 | 저장 용량 |
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로 처리합니다.