[JPA] JPA의 장단점과 성능 비교, 성능 최적화
2023. 6. 8.
비즈니스 로직의 위치와 패러다임의 변화
백엔드 시스템 아키텍처를 설계할 때 지속적으로 대립해 온 논제 중 하나는 비즈니스 로직을 데이터베이스 계층에 둘 것인가, 아니면 애플리케이션 계층에 둘 것인가이다. 과거 데이터베이스 중심의 모놀리식 환경에서는 절차형 SQL로 작성된 저장 프로시저(Stored Procedure)를 활용해 대량의 데이터 연산과 비즈니스 로직을 일괄 처리하는 구조가 널리 쓰였다. 그러나 클라우드 환경의 도래와 함께 무상태(Stateless) 기반의 수평 확장, 단위 테스트 자동화, 지속적 통합 및 배포(CI/CD)가 표준으로 자리 잡으면서 로직의 주도권은 애플리케이션 계층으로 전환되었다. 자바 생태계에서는 이러한 흐름 속에서 SQL 중심의 SQL 매퍼(MyBatis)와 객체 모델 중심의 ORM(JPA, Hibernate) 그리고 이를 보완하는 QueryDSL이 데이터 접근 계층을 지탱해 왔다.
저장 프로시저의 특성과 아키텍처 관점의 한계
저장 프로시저는 데이터베이스 서버 내부에 컴파일되어 보관되는 절차형 프로그램이다. 애플리케이션과 데이터베이스 사이의 잦은 네트워크 왕복(Round-Trip)을 단 한 번의 호출로 압축하고, 데이터베이스 내부 메모리에서 대량 연산을 직접 수행하여 최종 결과만 반환하므로 네트워크 I/O 병목을 줄이는 데 효과적이다. 컴파일된 실행 계획 역시 프로시저 캐시에 저장되어 반복 실행 시 파싱 비용을 줄여준다. 다만 현대 RDBMS는 바인드 변수를 사용하는 일반 PreparedStatement에 대해서도 동일하게 라이브러리 캐시를 통해 실행 계획을 재사용하므로, 프로시저만의 독점적인 강점은 주로 네트워크 전송량 절감과 대규모 배치 집계에 국한된다.
반면 아키텍처 측면에서는 심각한 리소스 확장(Scalability) 불균형을 유발한다. 애플리케이션 서버는 저렴한 비용으로 자유롭게 인스턴스를 늘리는 수평 확장이 용이하지만, 데이터베이스는 트랜잭션 정합성(ACID)과 복제 제약으로 인해 스케일 아웃이 극도로 어렵고 고비용이다. 프로시저에 복잡한 비즈니스 로직을 집중시키면 고가의 데이터베이스 서버 CPU가 전체 시스템의 병목으로 전락한다. 또한 격리된 단위 테스트가 불가능하여 형상 관리와 CI/CD 파이프라인 자동화가 어렵고, 오라클의 PL/SQL이나 MS-SQL의 T-SQL처럼 벤더마다 문법이 달라 특정 데이터베이스에 완전히 종속되는 문제를 낳는다.
자바 ORM 진영: JPA와 QueryDSL
ORM(Object-Relational Mapping)은 객체 지향 언어의 객체 모델과 관계형 데이터베이스의 관계형 모델 사이에 존재하는 상속, 참조 연관관계, 동일성 식별 등의 패러다임 불일치를 해소하기 위해 등장한 표준 기술이다. JPA의 핵심인 영속성 컨텍스트는 1차 캐시를 통해 동일 트랜잭션 내 동일 식별자에 대한 메모리 주소 동일성을 보장하며, 쓰기 지연 저장소를 통해 커밋 시점에 SQL을 일괄 전송함으로써 네트워크 비용을 최적화한다. 엔티티를 조회한 스냅샷과 커밋 시점의 상태를 비교하는 변경 감지(Dirty Checking) 덕분에 별도의 수정 쿼리 호출 없이도 데이터 변경이 데이터베이스에 자동 반영된다.
문자열 기반으로 작성되어 컴파일 시점에 문법 오류를 잡지 못하는 JPQL의 한계는 QueryDSL을 통해 극복된다. QueryDSL은 엔티티 메타모델(Q-Class)을 기반으로 자바 코드를 통해 쿼리를 작성할 수 있게 하여 컴파일 시점에 타입 안정성을 보장하며, 복잡한 동적 검색 조건도 모듈화된 자바 메서드로 깔끔하게 분리할 수 있게 돕는다.
그러나 ORM은 즉시 로딩과 지연 로딩의 차이, 영속 상태 전이, 연관관계 탐색 시 발생하는 N+1 문제, OSIV의 생명주기 등 복잡한 내부 메커니즘에 대한 깊은 이해를 요구한다. 수십만 건 이상의 대용량 데이터를 일괄 수정하거나 삽입할 때는 1차 캐시 누적으로 인한 메모리 부족(OOM)과 변경 감지 연산 부하가 발생하므로, 주기적인 플러시 및 클리어, 무상태 세션(StatelessSession), 혹은 Spring Batch와 같은 배치 전용 처리가 동반되어야 한다.
SQL 매퍼 진영: MyBatis
MyBatis는 객체와 테이블을 자동으로 매핑하는 ORM과 달리, 개발자가 직접 작성한 SQL 문장과 자바 객체의 파라미터 및 반환 결과를 연결해 주는 SQL 매퍼 프레임워크이다. 순수 JDBC의 번거로운 커넥션 관리와 결과 매핑 보일러플레이트를 제거해 주면서도, SQL에 대한 완전한 통제권을 개발자에게 부여한다. 기본적으로 파라미터를 바인드 변수로 처리하여 SQL 인젝션을 방어하고 데이터베이스 실행 계획 캐시 재사용을 유도한다.
MyBatis의 가장 큰 무기는 세밀한 쿼리 튜닝과 데이터베이스 고유 기능의 제약 없는 활용이다. 복잡한 다중 조인, 윈도우 함수, 특정 데이터베이스 전용 인덱스 힌트 등을 자유롭게 구사할 수 있어 대규모 통계 화면이나 리포팅 작업에서 매우 강력하다. 또한 영속성 컨텍스트나 객체 그래프 매핑에 대한 학습 부담이 없어 SQL에 익숙한 개발자와 DBA가 신속하게 생산성을 낼 수 있다.
하지만 데이터베이스 스키마가 변경될 때마다 관련된 모든 SQL XML과 결과 매핑 객체를 수동으로 수정해야 하므로 유지보수 비용이 상당하다. 단순한 단건 조회나 기본 저장 로직조차 모든 SQL을 직접 작성해야 하며, XML 내 문자열로 정의된 쿼리는 컴파일 시점에 검증되지 않아 런타임에 호출되기 전까지 오류를 발견하기 어렵다는 구조적 한계를 지닌다.
핵심 특성 비교
비즈니스 로직의 위치 관점에서 저장 프로시저는 데이터베이스 서버 내부에 로직을 가두지만, JPA와 MyBatis는 애플리케이션 계층에 로직을 위치시켜 무상태 기반의 수평 확장을 지원한다. 타입 안정성 측면에서는 QueryDSL 메타모델 기반의 자바 컴파일 시점 검증이 가장 안전하며, MyBatis는 런타임 XML 파싱 시점에 의존한다. 단위 테스트 역시 POJO 기반으로 격리된 테스트를 구성할 수 있는 JPA 진영이 가장 유리하다.
반면 복잡한 쿼리 튜닝과 데이터베이스 고유 기능 활용 측면에서는 개발자가 SQL을 직접 작성하는 MyBatis와 데이터베이스 엔진 내부에서 동작하는 저장 프로시저가 객체 그래프 탐색에 최적화된 JPA보다 유리하다. 변경 비용과 유지보수성 측면에서는 객체 수정 시 컴파일 에러를 통해 영향도를 즉시 감지할 수 있는 JPA+QueryDSL이 가장 안정적이며, 컬럼 변경 시 다수의 XML을 수작업으로 동기화해야 하는 MyBatis나 프로시저는 수정 비용이 상대적으로 높다.
실무 권장 전략: 하이브리드 접근법
현대 대규모 트래픽 환경에서는 단일 기술만을 고집하기보다 시스템 영역별 특성에 맞춘 하이브리드 전략이 권장된다.
사용자 인증, 주문 결제, 상태 변경 등 비즈니스 규칙이 복잡하고 도메인 로직이 빈번하게 변경되는 핵심 도메인 영역은 JPA와 QueryDSL을 활용해 객체 지향적으로 모델링하고 컴파일 타임 안정성을 확보하는 것이 생산성과 유지보수성 면에서 가장 적합하다.
반면 백오피스의 대규모 집계 화면, 월말 정산, 다차원 통계 리포트처럼 엔티티 영속화나 1차 캐시가 불필요하고 정교한 조인 순서 제어나 윈도우 함수가 필수적인 영역에는 MyBatis, jOOQ, 혹은 JdbcTemplate을 결합하여 성능을 극대화한다. 스프링 환경에서는 단일 DataSource와 트랜잭션 매니저를 공유하여 두 프레임워크를 조화롭게 공존시킬 수 있다.
저장 프로시저의 경우 시스템 전반의 비즈니스 로직을 처리하는 용도로는 지양하되, 수백만 건 단위의 대규모 데이터 이관 배치나 네트워크 왕복 비용이 극단적인 병목으로 작용하여 데이터베이스 내부 메모리 집계가 정량적으로 우월함이 검증된 특수 배치 모듈에 한해 제한적으로 도입하는 것이 바람직하다.
참고
- Martin Fowler, Patterns of Enterprise Application Architecture (Data Mapper and Domain Logic Patterns)
- Martin Fowler, Evolutionary Database Design
- Jakarta Persistence Specification (Jakarta EE)
- Hibernate ORM User Guide (StatelessSession and Bulk Operations)
- MyBatis 3 Reference Documentation
- Oracle Database Concepts (Shared Pool and Library Cache)