정렬 기준이 바뀔 때마다 코드를 고쳐야 할까
정렬 기준이 바뀔 때마다 코드를 고쳐야 할까
요구사항
- 검색 조건에 다른 동적 쿼리 구현 (QueryDSL 사용)
- 사용자의 선택에 따른 동적 정렬 기능 제공 (Pageable의 Sort 활용)
고려한 두 가지 방법
방법 A: PathBuilder를 이용한 자동화 방식
1
new PathBuilder<>(Post.class ,"post").get(orderProperty)
장점
- 코드가 간결함
- 엔티티 필드 추가 시 수정 불필요
단점
- 엔티티의 모든 필드가 정렬 대상이 됨
- 런타임 오류 가능성
- 운영 환경에서 운영 기준에 따른 정렬 범위 통제가 어려움
방법 B: Enum 전략 패턴을 이용한 수동 매핑 (선택)
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
// 정렬 정책 정의
public enum PostSortType {
CREATED_AT("createdAt");
private final String property;
PostSortType(String property) {
this.property = property;
}
public static Optional<PostSortType> from(String property){
return Arrays.stream(values())
.filter(postSortType -> postSortType.property.equals(property))
.findFirst();
}
public OrderSpecifier<?> toOrder(QPost post, Sort.Direction direction) {
Order order = direction.isAscending() ? Order.ASC : Order.DESC;
return switch (this) {
case CREATED_AT -> new OrderSpecifier<>(order, post.createdAt);
case VIEW_COUNT -> new OrderSpecifier<>(order, post.viewCount);
};
}
}
1
2
3
4
5
6
7
8
9
10
11
// 쿼리 적용
private OrderSpecifier<?>[] getOrderSpecifiers(Pageable pageable) {
return pageable
.getSort()
.stream()
.map(order -> PostSortType.from(order.getProperty())
.<OrderSpecifier<?>>map(sortType -> sortType.toOrder(post, order.getDirection()))
.orElseGet(this::defaultOrder)
)
.toArray(OrderSpecifier<?>[]::new);
}
장점
- 화이트리스트 기반으로 허용된 필드만 정렬 가능 (보안성 좋음)
- 인덱스가 타는 안전한 컬럼만 정렬 조건으로 제한하여 DB 성능 제어 (성능 향상)
단점
- 코드 복잡도 증가
- 새로운 정렬 필드가 추가 될 때마다 Enum 클래스에도 동일하게 추가해줘야 함
방법 B를 선택한 이유
생산성 측면에서는 방법 A가 유리하지만, 실제 서비스 환경에서는 다음 두 가지 가치가 더 크다고 판단했다.
- 예측 가능한 시스템: 어떤 정렬 요청이 들어올지 모르는 불안정성보다, 개발자가 정의한 범위 내에서만 동작하도록 강제하는 것이 대규모 트래픽 상황에서 DB 장애를 막는 핵심이라고 생각함.
- 명확한 비즈니스 정책: Enum을 통해 “이러한 정렬 기능을 제공한다” 라는 정책을 코드로 문서화 함으로써, 동료 개발자가 더욱 쉽게 비즈니스 정책을 파악할 수 있을거라고 생각함.
설계 과정에서의 고민과 보완점
모든 기술 선택에는 정답이 없듯, 자동화가 주는 생산성과 수동 매핑이 주는 안정성 사이에서 고민했다.
결론적으로 나는 안정성을 선택했다. 클라이언트의 요청이 DB 쿼리까지 직접 닿는 정렬 기능 특성상, 보안(Password 필드 등 접근 차단)과 성능(Full Table Scan 방지)에 대한 통제권을 개발자가 가지고 있어야 된다고 생각한다. 물론 관리 포인트는 두 곳(Entity, Enum)으로 늘어나는 단점이 있지만, 운영 중 DB 장애 가능성을 아예 차단하는 것이 더 낫다고 판단했다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.