JPA 적용 후기

Bgh
SSG TECH BLOG

--

안녕하세요 제휴 서비스팀에서 외부 제휴사로 상품 연동 업무를 하고 있는 개발자 변구훈입니다.

기존에는 mybatis를 이용하여 개발을 진행하였는데 JPA를 왜 사용해야 하는지와 사용하면 어떤 점이 좋은지, JPA를 적용하면서 느낀 점에 대해서 말해보겠습니다. 먼저 ORM 기술 등장 배경에 대해서 알아보겠습니다.

SQL 중심 개발의 문제점

객체 지향 프로그래밍과 관계형 데이터베이스는 서로 추구하는 패러다임이 다릅니다. 객체 지향 프로그래밍은 객체가 역할과 책임을 가지며 객체들끼리 협력하여 복잡한 시스템을 제어하는 것을 목표로 합니다. 관계형 데이터베이스는 데이터를 잘 정규화해서 유실되지 않게 보관하는 것이 목표이죠

아래 코드와 같이 팀과 회원 클래스가 있다고 생각해 보겠습니다.

public class Team {    private Long teamId;    private String teamName;}

회원 클래스는 team을 필드로 가지고 있고, 자기 자신의 팀을 업데이트할 수 있는 메서드를 가지고 있습니다. 클래스 자체에서 비즈니스 로직을 포함하고 있는 것이죠

public class Member {    private Long memberId;    private String memberName;    private Team team;    public void updateTeam(Team team) {        this.team = team;    }}

member 객체를 저장하려면 어떻게 해야 할까요?? member 테이블에는 member_id, member_name, team_id를 가지고 있고 member는 team 객체 자체를 가지고 있기 때문에 member 자체를 테이블에 저장할 수 없습니다. 회원 객체를 테이블에 맞게 변환해서 테이블에 저장해야 하는 것이죠.

반대로 테이블에서 객체를 다시 조회하려면 어떻게 해야 할까요? 회원 테이블과 팀테이블을 각각 조회해서 team 객체를 가진 member 객체를 만들 수도 있을 것이고, 회원 테이블과 팀 테이블을 조인해서 필요한 정보를 dto로 받아와서 만들 수도 있을 겁니다. 지금은 연관된 객체가 1개만 있지만, 여러개의 객체와 연관되어 있고, 연관된 객체가 또 다른 객체가 연관되어 있다면 엄청나게 복잡해질 것입니다.

즉, 객체 지향적으로 설계를 하여도 데이터베이스에 넣게 되면 변환 작업이 필요하기 때문에 더 복잡해지는 것이죠.

이런 문제를 해결하기 위해서 나온게 ORM(Object Relational Mapping) 기술입니다. 객체는 객체지향적으로 설계를 하고 관계형 데이터베이스는 관계형 데이터베이스대로 설계를 하면 ORM 프레임워크가 중간에서 매핑을 해줍니다.

JPA(Java Persistence APi)란?

JPA는 자바 진영의 ORM 기술 표준으로 대표적인 구현체로 Hibernate가 있습니다.

JPA의 장점

  • JPQ는 sql을 추상화한 JPQL이라는 언어를 사용하여 데이터베이스에 맞는 쿼리를 생성해 주기 때문에 특정 데이터베이스에 종속되지 않습니다.
  • 객체 중심으로 개발이 가능합니다.
  • CRUD와 페이징 처리를 위한 반복적인 쿼리들을 자동화할 수 있기 때문에 생상성을 향상시킬 수 있습니다.

JPA의 단점

  • 긴 학습시간
  • 객체 매핑을 잘못했을 경우 성능 저하 위험이 있습니다.
  • 복잡한 쿼리를 조회할 경우에는 SQL을 직접 작성해서 사용하는게 나은 경우들이 있습니다.

MyBatis VS JPA

mybatis를 사용할 때는 직접 SQL 문을 작성해야해서, 테이블을 하나 만들면 해당 테이블에 대한 CRUD 쿼리를 각각 만들어야 했습니다.

오라클을 사용할 때 페이징 쿼리를 작성하는 것도 상당히 복잡했는데 Pageable 클래스를 이용해서 쉽게 작성할 수 있게됐고요. 이런 부분들을 자동화하고 비즈니스 로직 개발에만 집중할 수 있기 때문에 생산성을 향상시킬 수 있었습니다.

그리고 비슷한 쿼리들이 여러 군데에서 생성되고 있어서 관리가 되지 않았는데 이 부분도 JPA가 제공하는 Repository 인터페이스 클래스 한 군데에서 관리할 수 있게 되었습니다.

도메인형 패키지 구조 적용

현재 사내에서 MSA로 전환하는 작업을 진행 중이고, JPA를 도입하면서 패키지도 도메인형 패키지 구조로 변경하였습니다. domain.item 패키지 아래에 상품 연동과 관련된 도메인을 모아두었고 프로젝트 전반에 사용되는 핵심 로직들을 도메인 단위로 작성을 해두었습니다. 외부에서 오는 요청을 처리하는 패키지는 api 패키지 쪽에서 처리할 수 있도록 하였고 domain 패키지 로직을 가져다가 사용하는 것이죠.

    │  ├─kotlin
│ │ └─com
│ │ └─ssg
│ │ └─alln
│ │ ├─api
│ │ │ ├─health
│ │ │ └─itemlnkg
│ │ ├─common
│ │ ├─domain
│ │ │ ├─common
│ │ │ └─item
│ │ └─infra
└─test

domain 패키지는 공통으로 사용하는 클래스와 인프라스트럭처와 관련된 코드 이외에는 의존하는 코드가 없기 때문에 다른 외부 제휴사에 상품을 연동해야 하는 상황이 와도 코드를 그대로 재활용할 수 있도록 구성하였습니다.

객체지향적 프로그래밍

JPA를 도입하기 전에는 데이터베이스에 종속적인 개발을 하다 보니까 대부분의 로직들이 서비스 레이어에 있었습니다.

예를 들어서 현재 상품이 유효한 판매 상태인지를 검증하는 로직을 만들 경우 현재 상품이 전시 기간인지, 판매 상태는 유효한지, 재고는 존재하는지 등을 따져야 하는데 이제는 엔티티 클래스에서 연관된 객체와의 상호 작용을 통해서 비즈니스 로직을 가지고 있도록 구현하였습니다.

각 제휴사별로 연동할 때마다 각자 구현하고 있던 로직들도 엔티티 클래스 안에 구현을 하였습니다. 서비스 레이어는 이제 엔티티 클래스의 로직을 가져다가 사용하고 트랜잭션을 묶어주는 역할을 하게되는 것이죠

JPA Auditing

JPA에서 제공하는 유용한 기능 중 하나로 Auditing 기능이 있습니다. Audit의 사전적 의미는 ‘감시하다’ 입니다. 데이터는 누가 등록을 했고 수정을 했는지, 언제 수정이 됐는지 등이 추적이 가능해야 하는데요, 이 기능을 이용하면 엔티티의 생성 또는 수정을 감시하여 생성일, 수정일, 등록자, 수정자 등의 데이터를 자동으로 입력할 수 있습니다.

기존에는 직접 이런 데이터를 세팅해서 insert, update를 하였는데 몇 가지 설정을 추가하면 프로젝트 전반에서 자동으로 입력을 해주기 때문에 실수할 확률도 줄어들고, 생산성도 향상시킬 수 있습니다.

변경 감지 기능(Dirty Checking)

오픈마켓 상품 로직 개발을 할 때 DBA로부터 실제로 변경이 됐을 경우에만 update 쿼리를 수행해달라는 요청이 왔습니다. 이때 자바 리플렉션을 이용해서 실제로 데이터가 변경이 됐는지 검사를 하고, 변경이 됐을 때만 업데이트 쿼리문을 수행하였습니다.

이렇게 개발을 하니까 비즈니스 로직 개발뿐만 아니라, 실제 변경이 됐는지 검사하는 로직 때문에 코드 복잡도가 높아져서 가독성이 떨어졌습니다. JPA에서는 조회한 엔티티의 데이터가 변경되면 트랜잭션이 종료되기 전에 변경이 된 것을 감지하고 update 쿼리문이 수행됩니다.

이 기능을 이용해서 실제로 변경이 일어날 때만 update 쿼리문이 수행될 수 있게 하였습니다.

이벤트 리스너

현재 전사적으로 MSA 프로젝트를 진행 중인데요, 제휴 상품 업무의 경우 SSG의 상품 데이터가 변경이 되면 상품 연동을 위해서 제휴사 상품 연동 API를 호출해야 합니다.

SSG의 상품 테이블의 상품 정보와 관련된 컬럼이 바뀌었을 때는 상품 수정 API를 호출해야 하고, 판매 상태 컬럼이바뀌면 판매 상태를 수정하는 API를 호출해야 합니다.

해당 처리를 위해서 현재는 집계 테이블을 따로 만들어서 어떤 집계 테이블에 쌓이냐에 따라 API를 호출하는 구조입니다.

JPA에서는 변경 감지 기능을 제공하기 때문에 어떤 컬럼이 변경이 됐는지 감지할 수 있다고 생각이 들었고 찾아보니 하이버네이트 이벤트 리스너라는게 있었습니다.

해당 기능을 사용하면 변경이 일어난 거를 감지하고 엔티티의 특정 필드 단위로 이벤트 발생이 가능합니다. 요 기능을 이용해서 제휴사 상품 연동 기능을 현재 구현 중인데 유용한 기능이라고 생각합니다.

최근 구글 트렌드

jpa와 mybatis의 구글 트렌드를 분석해 보았습니다. 2017년 초만 해도 mybatis에 대한 관심도가 jpa보다 훨씬 높을 것을 볼 수 있습니다. 시간이 흐를수록 격차가 좁혀지다가 최근에는 오히려 jpa가 추월한 것을 볼 수 있죠. 이런 동향을 보았을 때 앞으로도 JPA를 도입하는 기업들이 늘어날 거라고 예측할 수 있습니다.

지금까지 JPA의 장점과 사용해 보면서 느낀 점에 대해서 말씀드렸습니다. JPA를 사용하려면 상당히 많은 양을 학습해야 하고, 사용하면서 N+1문제가 발생하여 성능 저하가 발생할 수 있습니다.

따라서 성능 최적화에 대해서도 공부를 해야 하고 JPA의 동작 방식에 대해서도 깊게 이해를 해야합니다.

그럼에도 불구하고 너무 많은 장점이 있다는 생각이 들었고 관심이 있으시다면 제대로 공부해보는 것을 추천드립니다.

궁금하신 사항이 있으시면 댓글로 남겨주세요 :)

감사합니다.

--

--