DB/JPA

JPA를 학습해야하는 이유

꾸준함. 2021. 8. 16. 02:44

개요

구글 트렌드를 보면 아래 사진처럼 전 세계적으로 MyBatis보다 JPA가 핫하다는 것을 알 수 있습니다.

 

 

하지만, 유독 우리나라와 중국에서만 아직까지 JPA보다 MyBatis가 많이 쓰이는 것으로 보입니다.

아무래도 각 기업마다 레거시 프로젝트들이 많고 타 국가보다 보수적인 성향을 지녀 JPA로 전향을 하지 않아 그런 듯합니다. (저 또한 JPA보다는 MyBatis가 더 익숙하며 현재 프로젝트에서는 섞어서 쓰고 있습니다.)

 

그럼에도 불구하고 JPA를 배워야 하는 이유는 우리가 이름만 들어도 아는 네카라쿠배당토와 같은 서비스 기업들이 JPA를 적용하고 있기 때문입니다.

또한, 결국에는 우리나라도 세계적인 트렌드에 맞춰 MyBatis보다 JPA를 더 중용하는 날이 머지않아 올 것으로 예측되기 때문에 JPA 개념이 어렵더라도 익히려고 노력하고 향후 프로젝트에는 JPA를 적용하는 노력을 들여야 한다고 생각합니다.

 

1. MyBatis

JPA에 대해 설명하기 전에 간단히 MyBatis에 대해 설명하고 장단점을 소개하겠습니다.

  • 개념
    • JDBC를 좀 더 편하게 사용할 수 있도록 객체를 SQL 혹은 저장 프로시저와 매핑해주는 persistence 프레임워크 
    • SQL Mapper
  • 장점
    • 다른 프레임워크에 비해 간단하며 소스 코드와 SQL을 분리할 수 있어 작업 분배하기 쉬움
    • SQL을 직접 다룰 수 있어 디버깅하기 쉬움
  • 단점
    • 테이블을 생성할 때마다 반복적인 CRUD SQL 코드를 작성해야 함
    • 테이블 내 칼럼 추가 시 CRUD SQL 코드를 전부 변경해야 함 (유지 보수하기 어려움)
    • SQL과 데이터베이스 벤더에 대한 종속성 문제

 

2. 객체 vs 관계형 데이터베이스(RDBMS)

MyBatis를 설명하다 갑자기 왜 뜬금없이 객체와 RDBMS에 관해 설명할까 싶을 겁니다.

하지만 이는 JPA를 적용해야 하는 이유를 서술하기 위한 빌드업 과정이니 끝까지 따라와 주시면 감사하겠습니다.

 

객체 vs RDBMS

  • 상속 관계
    • 테이블에도 슈퍼 타입 서브타입 관계와 같이 상속과 비슷한 관계가 있지만 객체의 상속 관계와는 다름
    • 테이블을 상속 관계로 나눈다고 하면 아래의 절차를 따라야 함
      • 각각의 테이블에 따른 JOIN SQL 작성
      • 각각의 객체 생성
      • 각각의 repository 생성
      • 각각의 service 생성...
      • 이러한 복잡한 과정을 거치기 싫어 DB에 저장할 객체에는 상속 관계를 안 쓰고 보통 객체를 통합한 하나의 객체로 불러옴
      • 반면, Java Collection에 저장할 경우 부모 타입으로 조회 후 다형성 활용 가능!
  • 연관 관계
    • 객체는 getter 메서드와 같은 참조를 사용
      • getter 메서드이므로 단반향
    • 테이블의 경우 FK 즉, 외래 키를 사용
      • 반면, 테이블의 경우 FK가 다른 테이블의 PK이므로 양방향
    • 아래와 같이 객체답게 모델링을 할 경우 조회할 때 상당히 번거로움
      • 삽입을 할 때 Teacher 객체를 넣지는 못하므로 teacher의 id 즉, FK를 넣어줘야 함
      • 이때, student.getTeacher(). getId()와 같이 여러 번 타고 들어가야 함
      • 또한, 조회할 때도 쿼리를 통해 받은 필드 정보들을 한 번에 Student 객체로 받을 수 없고 필드 정보들을 각각 Student 객체와 Teacher 객체에 적절히 넣어줘야 함 (번거로움)
        • 동일한 쿼리를 통해 Student 객체 정보를 받아와도 매번 새로운 객체를 생성하므로 두 객체는 서로 다름
      • 따라서, 결국 MyBatis와 같이 SQL Mapper를 사용할 때는 Student와 Teacher의 필드를 모두 받는 하나의 큰 객체를 정의한 뒤 한 번에 받음 (효율적이나 객체지향적이지는 않음)
Class Student {
	
    // 고유 id
    private Long id;
    
    // 이름
    private String name;
    
    // 생년월일
    private LocalDateTime birthday;
    
    // 담임 선생님
    private Teacher homeroomTeacher;
}

Class Teacher {
	
    private Long id; // Student와 연관관계를 맺는 PK (Student의 FK)
    
    private String name; // 선생님 이름
    
    // 중략
}

 

 

* 반면, 자바 컬렉션을 사용한다면 위의 귀찮은 과정을 거치지 않고 바로 Student 객체 정보를 받을 수 있음

* 또한, 컬렉션을 사용할 경우 동일한 쿼리를 통해 두 Student 객체를 받은 뒤 비교를 하면 서로 같은 객체

 

// students라는 자바 컬렉션이 있다고 가정한다면
// 메서드가 상당히 간결해짐

public void insertStudent(Student student) {
	students.add(student);
}

public Student getStudent(Long studentId) {
	return students.getByStudentId(studentId);
}

public Teacher getHomeroomTeacher(Long studentId) {
    Student student = students.getByStudentId(studentId);
    
    return student.getHomeroomTeacher();
}

 

RDBMS에서 직접 작성한 SQL을 통해 객체 정보를 불러올 경우 발생할 수 있는 또 다른 문제들

SQL을 직접 작성할 경우 위에서 서술한 객체와 RDBMS의 차이점에서부터 발생하는 문제뿐만 아니라 아래의 문제점들도 발생할 수 있습니다.

  • 엔티티 신뢰 문제
    • 처음 실행하는 SQL에 따라 탐색 범위가 결정이 되기 때문에 연관 관계에 있는 객체들에 대해 Null 체크를 계속해줘야 함
    • 위 예시처럼 Student와 Teacher와의 관계만 있다면 위와 같은 일이 벌어질 확률이 낮지만, Student 내 Teacher, Class, SchoolMate 등과 같이 연관 관계가 많아질 경우 SQL을 실행했을 때 해당 객체들에 대한 정보를 모두 받아온다는 보장을 하기 어려움 (개발과 SQL 작성을 모두 같이했다면 문제없겠지만 작업 분배를 할 경우 문제가 충분히 발생할 수 있음
  • 모든 객체를 미리 로딩할 수 없음
    • 상황에 따라 아래와 같이 동일한 학생 조회 메서드를 여러 벌 생성해야 할 수 있음
    • studentDAO.getStudent();
    • studentDAO.getStudentAndTeacher();
    • studentDAO.getStudentAndTeacherAndClass();
    • studentDAO.getStudentAndTeacherAndClassAndSchoolMate();

 

* 계층형 아키텍처이지만 진정한 의미의 계층 분할이 어려움

 

정리

정리를 하자면 객체답게 모델링을 진행할수록 매핑 작업만 늘어나기 때문에 객체를 Java Collection에 저장하듯이 DB에 저장할 방법을 모색했고 그래서 나온 개념이 JPA

 

3. JPA

  • 개념
    • Java Persistence API의 줄임말
    • 자바 진영의 ORM 기술의 표준
    • ORM이란?
      • Object-relational mapping의 줄임말
      • 객체는 객체대로 설계하며 관계형 데이터베이스는 RDBMS대로 설계하고 ORM 프레임워크가 중간에서 매핑을 해주는 개념
      • 정리를 하자면 DAO -> JPA -> JDBC API -> DB와 같은 과정을 거침
    • JPA 2.1 표준 명세를 구현한 구현체는 총 3개이며 대부분 Hibernate를 사용함 (나머지는 EclipseLink, DataNucleus)
  • 장점
    • SQL 중심적인 개발이 아닌 객체 중심 개발이 가능
      • nativeQuery 옵션을 통해 직접 SQL을 작성하지 않는 이상 JPA 메서드 선언을 하면 SQL이 자동 생성됨
        • 데이터베이스 벤더마다 미묘하게 다른 데이터 타입이나 SQL을 JPA를 이용할 경우 신경 쓰지 않아도 됨 (ex. ORACLE vs MySQL)
      • 따라서, 테이블을 생성할 때마다 반복적인 CRUD 작업을 거치지 않아도 되고 이에 따라 생산성 및 유지보수가 용이
        • SQL을 직접 작성할 경우 기존 테이블에 칼럼이 추가할 때마다 DML을 모두 수정했어야 하는데 JPA를 사용할 경우 그럴 필요 없음
      • 앞서 객체 vs RDBMS에서 정리했듯이 이 둘은 개념이 다른데 JPA가 중간에서 매핑을 해주므로 자바 프로그래밍에 적합하게 객체지향적 개발이 가능
        • 상속 관계의 객체들이 있더라도 jpa의 persist 메서드를 통해 개발자가 객체를 삽입하면 jpa에서 알아서 객체들에 맞게 삽입해줌
        • 조회할 때도 마찬가지로 jpa의 find 메서드를 통해 개발자가 객체를 조회하면 jpa에서 알아서 sql을 실행하여 객체를 반환해줌
        • 정리를 하면, 개발자가 연관 관계를 설정한 뒤 persist 메서드를 통해 객체를 저장하면 find 메서드를 통해 객체를 조회할 때 연관 관계가 다 설정되어 있음 (Entity 신뢰성 문제 해결)
    • JPA는 앞서 객체 vs RDMS에서 다룬 컬렉션 예제처럼 동작하므로 동일한 트랜잭션에서 조회한 Entity는 같음을 보장
    • 성능 향상 기대
      • 캐싱 기능 지원
      • transactional write-behind
        • 총 5개의 객체를 삽입하는 메서드가 있다고 가정
        • 원래대로라면 삽입할 때마다 네트워크를 타서 총 5번의 네트워크 지연이 발생
        • 하지만, JPA는 JDBC BATCH SQL 기능을 통해 한번에 모아서 삽입하므로 단 한 번만 네트워크를 탐
        • 또한, 비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않도록 UPDATE 및 DELETE SQL을 트랜잭션이 커밋하는 순간 실행하고 커밋 (바로 수행하지 않음)
      • Lazy Loading 지원
        • 앞서 Student, Teacher 예시를 통해 설명
        • 기존대로라면 Student 객체를 불러올 때 Teacher 객체도 함께 불러옴 (즉시 로딩)
        • Lazy Loading의 경우 Student 객체를 불러올 때 Teacher 객체를 제외한 필드들만 불러오고 Teacher 객체가 필요할 때 Teacher를 불러옴
        • 즉, 객체가 실제 사용될 때 로딩
  • 단점
    • 체감상 MyBatis보다 훨씬 어렵다
      • 공식 문서의 양도 방대하고 접근하기 쉽지 않음
      • 또한, SQL을 자동으로 생성하다 보니 에러가 났을 때 디버깅하기 쉽지 않음
      • 여러 테이블과 조인을 할 때 JPA를 적용하기 쉽지 않음
        • 그래서 저 같은 경우 한 테이블 내에서 처리할 수 있는 쿼리는 JPA를 사용하고 두 개 이상의 테이블에 대한 JOIN이 이루어질 때는 MyBatis를 사용하고 있습니다.
      • 운영 이관을 협력사한테 맡길 때, 협력사가 JPA에 미숙할 확률이 매우 높음 (물론 좋은 서비스 회사는 이럴 일이 없을 거라고 생각한다.)
    • 테이블을 설계할 때 JPA에 적합하도록 설계를 해야 하기 때문에 많은 시간이 들어감
      • 당연히 이렇게 해야 하지만 별도 DBA가 없이 여러 개발자가 테이블을 생성하고 사용할 경우 규격이 각자 다를 수 있음 (물론 좋은 서비스 회사는 이럴 일이 없을 거라고 생각한다.)
      • 특히, 서비스 런칭 이후 클라이언트의 요구사항에 따라 테이블을 추가로 생성할 때 위와 같은 오류가 많이 발생

 

출처

자바 ORM 표준 JPA 프로그래밍 - 기본 편 (김영한 강사님)

https://www.slideshare.net/dongmyo/mybatis-jpa-123381168

 

MyBatis에서 JPA로

NHN Forward 2018 컨퍼런스에서 발표한 자료입니다. http://forward.nhnent.com/ MyBatis로 작성된 예제 프로그램을 JPA로 마이그레이션하는 방법에 대해 설명합니다. cf.) https://github.com/nhnent/hands-on-labs.ja…

www.slideshare.net

 

반응형

'DB > JPA' 카테고리의 다른 글

[JPA] 상속관계 매핑  (2) 2021.09.07
[JPA] 다양한 연관관계 매핑  (0) 2021.08.31
[JPA] 연관관계 매핑 간단 정리  (0) 2021.08.28
[JPA] Entity 매핑 정리  (0) 2021.08.23
[JPA] PersistenceContext 간단 정리  (0) 2021.08.20