Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 백준
- 해시맵
- 구현
- 너비우선탐색
- Backtracking
- 깊이우선탐색
- greedy
- 동적계획법
- Algorithm
- 네트워크
- BFS
- 부분수열의합
- DFS
- 브루트포스
- 그리디
- 스프링
- 백트래킹
- ReactiveProgramming
- DP
- programmers
- 프로그래머스
- 이분탐색
- boj
- DynamicProgramming
- Spring
- Network
- 해시
- HashMap
- 알고리즘
- dynamic programming
Archives
- Today
- Total
옌의 로그
ENUM과 DB 매핑 전략 (feat. Enum Converter,, 코드 테이블,,) 본문
최근 프로젝트에서 enum을 자주 사용하게 되면서, 한 가지 고민이 생겼다.
- 코드 상에서는 enum으로 깔끔하게 관리하고 싶은데
- DB에는 그 값이 int로 저장된다면?
예를 들어, 상품 상태(ProductStatus)를 enum으로 선언하고 있지만 DB에는 0, 1, 2 같은 숫자로 저장하는 구조였다. 그럼 이 숫자들이 도대체 뭘 의미하는지 코드만 봐서는 알기 어렵다.
이를 해결하기 위해 사용했던 방법들을 정리하고, 코드 테이블 방식까지 비교해보려 한다.
1. Enum의 순서(ORDINAL)를 그대로 사용하는 방식
가장 간단하고 자주 쓰이는 방법이다. enum 선언 순서가 곧 DB의 숫자 값이 된다.
@Getter
public enum ProductStatus {
READY, // 0
IN_PROGRESS, // 1
COMPLETE // 2
}
Entity엔 이렇게 선언한다
@Enumerated(EnumType.ORDINAL)
private ProductStatus status;
장점
- 코드가 매우 간단하다.
- 별도의 매핑 로직이 필요 없다.
단점
- 순서가 바뀌면 망한다.
- 중간에 enum을 추가하거나 제거하면 기존 데이터 해석이 꼬인다.
- 의미가 명확하지 않다. (0이 뭔 의미인지 알기 어려움)
개인적으로는 단기 프로젝트나 테스트용 DB라면 쓸 수도 있겠지만, 실서비스라면 비추천한다.
2. EnumConverter를 활용한 자동 매핑
1. 공통 인터페이스 정의
public interface BaseEnum<T> {
T getCode(); // DB에 저장되는 값
}
2. 실제 enum에 적용
@Getter
@RequiredArgsConstructor
public enum ProductStatus implements BaseEnum<Integer> {
READY(0, "준비중"),
IN_PROGRESS(1, "진행중"),
COMPLETE(2, "완료됨");
private final Integer code;
private final String label;
@JsonCreator // JSON → enum 역직렬화 시 사용
public static ProductStatus of(Integer code) {
return Arrays.stream(values())
.filter(v -> v.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Invalid ProductStatus code: " + code));
}
}
3. BaseEnum용 공통 Converter
public abstract class AbstractBaseEnumConverter<E extends Enum<E> & BaseEnum<T>, T>
implements AttributeConverter<E, T> {
private final Class<E> enumClass;
protected AbstractBaseEnumConverter(Class<E> enumClass) {
this.enumClass = enumClass;
}
@Override
public T convertToDatabaseColumn(E attribute) {
return attribute != null ? attribute.getCode() : null;
}
@Override
public E convertToEntityAttribute(T dbData) {
if (dbData == null) return null;
return Arrays.stream(enumClass.getEnumConstants())
.filter(e -> dbData.equals(e.getCode()))
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException("Unknown code [" + dbData + "] for enum " + enumClass.getSimpleName()));
}
}
4. 실제 Enum용 Converter 구현
@Converter(autoApply = false) // 명시적으로 지정할 수 있게
public class ProductStatusConverter extends AbstractBaseEnumConverter<ProductStatus, Integer> {
public ProductStatusConverter() {
super(ProductStatus.class);
}
}
Entity에서는 다음과 같이 사용:
@Convert(converter = ProductStatusConverter.class)
private ProductStatus productStatus;
이점 요약
항목설명
재사용성 | BaseEnum + 공통 Converter로 모든 enum에 적용 가능 |
유지보수성 | enum 값에 의미 부여 (label 등)와 안전한 역매핑 |
확장성 | 다른 타입 (String, Long 등)도 쉽게 확장 가능 |
안정성 | 잘못된 DB 값에 대한 방어 로직 포함 (orElseThrow) |
3. (번외) 코드 테이블과 조인하여 관리하는 방식
Enum이 아니라 DB 테이블로 관리하는 방식도 있다.
id | code | name |
1 | PRODUCT_READY | 준비중 |
2 | PRODUCT_INPROG | 진행중 |
3 | PRODUCT_DONE | 완료됨 |
이렇게 “코드 테이블”을 별도로 만들어놓고, FK로 조인하여 의미를 해석하게 만든다.
사용 예시
@Entity
public class Product {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "status_id")
private Code productStatus;
}
@Entity
public class Code {
@Id
private Long id;
private String code; // ex. "PRODUCT_READY"
private String name; // ex. "준비중"
}
장점
- 코드값의 의미를 DB에서 명확하게 관리할 수 있다.
- 코드 변경이 필요할 경우 enum을 수정하지 않아도 된다.
- 운영/기획 팀에서 직접 코드 테이블을 관리 가능.
단점
- 단순 enum에 비해 구현과 조회가 번거롭다.
- enum처럼 switch-case를 못 쓴다.
- 코드값 오타에 대한 컴파일 타임 체크가 불가능
보통 코드값이 외부에 노출되거나, 관리 주체가 기획/운영인 경우 코드 테이블 방식을 많이 쓴다.
실제 서비스에서 어떻게 선택?
구분 | enum ordinal | enum + converter | 코드 테이블 |
장점 | 단순함 | 명확한 값 관리 | 유연한 외부 관리 |
단점 | 순서 변경 위험 | 구현 번거로움 | 조회 비용, 타입 안정성 낮음 |
추천 케이스 | 테스트, 임시 | 내부 로직 enum | 외부 노출, 다국어 지원 |
마무리하며. . .
처음에는 무조건 enum converter가 필요한 줄 알고 만들었었는데, 단순한 status 들의 경우 enum converter가 오히려 오버 스펙이 된단 걸 깨달은 어느 날. . 상태값이 크게 바뀔 일이 없는 것들의 경우 ordinal로 관리하는게 더 easy한 코드인 것 같다.
추가로, 코드 테이블은 아직 사용해본 적이 없어서 어떤 상황에서 유용하게 쓰일 수 있을 지 잘 모르겠다.
기회가 된 다면 써보고 싶다!
'스터디 > 기타' 카테고리의 다른 글
[Reactive Programming] Blocking I/O와 Non-Blockin I/O (1) | 2025.09.25 |
---|---|
[Reactive Programming] 리액티브 프로그래밍 & 리액티브 스트림즈 (1) | 2025.08.29 |
(충격) 지금까지 배치 서버를 써본 줄 알았는데… 아니었음 (3) | 2025.08.26 |
DDD와 Hexagonal Architecture, 상품관리 시스템에 적용해보기 (4) | 2025.08.08 |
[Linux] linux환경에서 javac로 java 컴파일하기 (0) | 2024.04.20 |
Comments