들어가며
프로젝트를 진행 하면서 ORM 기술을 선택할 때 "Spring 프로젝트니까 JPA 쓰면 되지"라고 결정하고 싶지 않았습니다.
StyleHub는 패션 이커머스 플랫폼으로 주문, 결제, 재고, 쿠폰, 포인트 등 여러 도메인이 복잡하게 연결된 구조를 가지고 있습니다.
특히 `orders → order_items → products → stores`처럼 테이블 간 연관관계가 깊기 때문에 어떤 데이터 접근 기술을 선택하느냐에 따라 개발 생산성뿐만 아니라 유지보수 방식 자체가 달라질 수 있다고 생각했습니다.
그래서 기술을 먼저 정하고 개발을 시작하기보다는, 프로젝트의 도메인 구조에 가장 적합한 기술이 무엇인지 직접 비교해보기로 했습니다.
이번 글에서는 StyleHub 프로젝트에서 데이터 접근 기술을 선택하기 위해 JDBC, MyBatis, JPA를 비교하며 고민했던 과정과 최종적으로 JPA를 선택하게 된 이유를 정리해보려 합니다.
1. JDBC — 비즈니스 로직보다 데이터 접근 코드가 더 많아진다
StyleHub에는 13개 테이블이 있습니다. JDBC로 각 테이블의 CRUD를 구현하면 아래처럼 연결, 쿼리 실행, 결과 매핑, 예외 처리, 자원 반납을 매번 직접 작성해야 합니다.
기능 하나를 추가할 때마다 비즈니스 로직보다 데이터 접근 코드를 작성하는 데 더 많은 시간을 쏟게 되는 구조입니다.
동시성 제어, 트랜잭션 정합성처럼 핵심 문제에 집중해야 하는 프로젝트에서 이 비용은 너무 비싸다고 판단했습니다.
2. MyBatis — 연관관계가 깊어질수록 한계가 드러난다
StyleHub의 도메인 구조는 orders → order_items → products → stores처럼 연관관계가 깊습니다.
MyBatis는 객체 간 연관관계를 직접 관리해야 하기 때문에, 연관된 객체를 조회할 때마다 JOIN 쿼리를 직접 작성하고 결과를 수동으로 매핑해야 합니다.
결국 테이블 중심으로 개발하게 되는 구조인데, 도메인 모델을 객체 중심으로 설계하고자 했던 방향과 맞지 않아 후보에서 제외했습니다.
3. JPA를 도입 이유
1. 객체 중심 설계와 연관관계 매핑 자동화
MyBatis를 선택하면 겪는 문제가 바로 이것이었습니다.
연관관계가 깊은 도메인 구조에서 테이블 중심으로 개발하다 보면 객체 간 관계를 개발자가 직접 관리해야 하고 실수가 잦아집니다. StyleHub에서 주문을 조회할 때 주문 항목, 상품, 결제 정보까지 함께 필요한 경우가 많은데, JPA의 @OneToMany, @ManyToOne 매핑을 활용하면 orders.getOrderItems()처럼 객체 그래프를 탐색하듯 연관 데이터에 접근할 수 있어 JOIN 쿼리를 직접 작성할 필요가 없어집니다.
2. 생산성
동시성 제어, 트랜잭션 정합성처럼 핵심 문제에 집중해야 하는 프로젝트에서 save(), findById(), delete() 같은 공통 CRUD를 직접 구현하는 시간이 아까웠습니다.
Spring Data JPA가 이를 자동으로 제공해 비즈니스 로직에 집중할 수 있는 환경이 만들어졌습니다. 즉 SQL 작성 시간을 줄이고 비즈니스 로직에 집중하기 위해 JPA를 도입했습니다.
4. JPA의 단점
JPA를 선택했지만 동적 쿼리 문제는 해결되지 않았습니다. 카테고리, 가격 범위, 스토어를 동시에 필터링하는 상품 검색을 JPQL로 구현하면 문자열을 직접 조합해야 하고, 오타나 문법 오류가 컴파일 타임이 아닌 런타임에서야 발견됩니다. 즉 서비스가 실제로 실행되기 전까지 오류를 잡을 수 없는 구조입니다.
이 한계를 인지하고 QueryDSL을 추가로 도입했습니다. QueryDSL 도입 이유는 다음 글에서 다룹니다.
마치며
JPA를 선택한 덕분에 데이터 접근 코드 대신 동시성 제어와 트랜잭션 정합성이라는 핵심 문제에 집중할 수 있는 환경이 만들어졌습니다.
JDBC, MyBatis, JPA를 비교하면서 기술은 기능이 많은 것을 고르는 게 아니라, 해결해야 할 문제에 가장 적합한 것을 고르는 것이
중요하다는걸 다시한번 느꼈습니다.
그리고 선택한 기술의 한계를 인지하고 보완할 수 있을 때 비로소 그 기술을 제대로 쓰고 있다고 생각합니다.
'stylehub 프로젝트' 카테고리의 다른 글
| [StyleHub#7]Transactional이 동작하지 않는다? — Spring Self-Invocation 버그 발견과 해결 (0) | 2026.03.17 |
|---|---|
| [StyleHub#6]@Transactional 범위 최소화로 커넥션 점유 시간을 줄인 과정 (feat. BCrypt) (1) | 2026.03.15 |
| [StyleHub #5] 왜 QueryDSL을 도입했는가 — JPQL의 한계를 설계 단계에서 미리 마주하다 (0) | 2026.03.09 |
| [StyleHub #2]커머스 DB 설계: ERD 설계하면서 가장 많이 고민했던 9가지 (0) | 2026.03.08 |
| [StyleHub #1]프로젝트 주제 선정 이유 - feat : 무신사 (1) | 2026.03.04 |