📍JPA 공부를 시작하며...
프로젝트를 진행하면서 JPA를 사용했고 지금도 사용 중이긴 하지만, 그 안에 담긴 원리를 제대로 파악하고 있지 않은 채 JPA를 활용하고 있어서 때로는 답답했고 구글링을 하고 관련 블로그도 찾아보긴 했지만, 매번 찾아보는 것도 일이었다. 그래서 이번 기회에 김영한님의 'JAVA ORM 표준 JPA 프로그래밍'을 통해서 영속성, 트랜잭션 등 JPA 관련 공부를 해보기로 했다.
📍01. JPA 소개
💬 1-1 SQL을 직접 다룰 때 발생하는 문제점
자바 어플리케이션은 대부분 MySQL과 같은 RDB를 데이터저장소를 활용하고 이를 관리하는 용도로 SQL을 사용한다.
JDBC API를 이용해서 SQL을 DB에 전달하기 때문에 SQL을 다루게 된다.
✅ 반복되는 코드로 인한 비효율성
CRUD 기능 구현을 위해셔 개발을 진행하는 경우에, 객체와 DAO를 만들게 된다. 이 과정에서 SQL을 직접 다루면서 기능 구현을 하게 된다면 아래와 같은 과정을 거쳐야 한다.
조회의 경우,
1. SQL 작성
2. JDBC API를 사용해 SQL 실행
3. 조회 결과 객체로 매핑
실제로 데이터베이스 수업 팀플로 항공권 예매를 JDBC로 만들어보는 작업을 했었는데 정말 코드량이 어마어마했었다...😅 JPA가 있음에 감사...

데이터베이스가 아니라 자바 컬렉션에 객체를 저장한다면, 한 두줄로 끝낼 수 있다.
겍체가 한 두 개라면, 음... 그래 그냥 참고 SQL 다 작성하면 돼~~ 마인드로 접근하겠지만,
객체가 100개면 어떡해요 ㅠㅠ
그러면 JPA 쓰면 됨 😁
✅ SQL에 의존적인 개발
개발 도중 요구사항 변경으로 객체가 필드가 더 추가되는 경우에는 CRUD에 해당하는 DAO를 일일히 확인해가며 코드를 수정해야하며 작은 수정을 위해 CRUD 코드와 SQL 대부분을 변경해야 하기도 한다. 즉, SQL을 숨기는 형태로 작성되었어도 불가피하게 DAO 확인을 위해 SQL 실행 코드를 확인해야한다는 것이다.
문제점
1. 진정한 의미의 계층 분할 어려움
2. 엔티티 신뢰할 수 없음
3. SQL에 의존적인 개발 불가피
✅ JPA와 문제 해결
앞서서 살펴본 문제점을 JPA가 해결할 수 있다 ㅎ..ㅎ
JPA는?
- 개발자가 직접 SQL 작성 ❌
- JPA가 제공하는 API 사용
JPA가 제공하는 CRUD API를 대략 정리해보자!
저장 기능
jpa.persist(member);
persist()는 객체를 데이터베이스에 저장한다.
이 메소드를 호출하면 JPA가 객체와 매핑 정보를 보고 적절한 INSERT SQL을 생성해서 데이터베이스에 전달한다
* 매핑 정보 : 어떤 객체를 어떤 테이블에 관리할지 정의한 정보
조회 기능
String memberId = "helloId";
Member member = jpa.find(Member.class, memberId);
find()는 객체 하나를 데이터베이스에서 조회합니다. 저장 때와 마찬가지로 매핑정보를 보고 적절한 SQL을 작성해주는데 이때는 조회이기 때문에 SELECT를 생성해서 이를 DB에 저장하게 되고 객체를 생성해서 반환하게 됩니다.
수정 기능
JPA에서 별도의 수정 메소드를 제공하진 않지만 객체를 조회해서 값을 변경하면 트랜잭션을 커밋할 때 UPDATE SQL이 전달됩니다.
연관된 객체 조회
Member member = jpa.finde(Member.class, memberId);
Team team = member.getTeam();
위의 코드를 보면 멤버 객체와 팀이 연관관계이고 member와 연관된 팀 객체를 조회하는 코드입니다. 이와 같이 연관된 객체를 사용할 때 적절한 SELECT SQL을 실행해서 연관된 객체를 조회할 수 있습니다.
JPA를 사용하긴 했지만 수정기능과 연관된 객체 조회에 부분에서 그 원리가 정말 궁금해졌습니다! 신기하네요... 이 책을 다 보고나면 통달할 수 있겠죠? 😁
💬 1-2 패러다임의 불일치
복잡한 어플리케이션의 경우에 대부분 객체 지향 언어로 개발이 되고 도메인 모델도 객체를 이용해 모델링하면 편하다. 그리고 이 객체를 관계형 데이터베이스에 저장하게 된다. 그런데 객체지향은 추상화, 상속, 다형성 같은 특징과 관계형 데이터베이스는 관련이 없다. 즉, 둘의 기능과 표현 방법이 다르다는 점이 이게 객체와 관계형 데이터베이스의 패러다임 불일치 문제다. 그리고 이러한 불일치를 해결하기 위해서 많은 비용이 투자된다.
이 부분을 정리하면서 느낀 점은, 어떤 데이터베이스, 어떤 프레임워크, 어떤 방식 이런 걸 생각하는 것 모두 선택의 영역인데 나는 그냥 "내가 아는 거니까 써야지" 이렇게 접근했던 것 같다. 앞으로는 선택한 방법의 원리나 문제점을 생각하는 과정이 필요할 것 같다고 느꼈다.
✨ 상속
처음 자바를 배우면서 되게 신기하게 생각했던 상속! 상속하면, 그 당시 교수님께서 자주 드는 예시인 학교구성원 - 교수, 학생, 교직원이 제일 먼저 생각난다. 이처럼 객체는 상속 기능을 가지고 있으나, 일반적으로 테이블에서는 상속이 없다.
슈퍼타입 서브타입 관계를 이용해서 유사한 형태를 구현할 수는 있다.
abstract class Item {
Long id;
String name;
int price;
}
class Album extends Item }
String artist;
}
class Movie extends Item {
String director;
String actor;
}
class Book extends Item {
String author;
String isbn;
}
위와 같은 객체 모델 코드가 있다고 할 때 Album을 저장하기 위해서는
INSERT INTO Item ...
INSERT INTO ALBUM
과 같이 객체를 분해해서 두 SQL을 만들게 된다. 나머지 Movie, Book도 마찬가지이다.
만약에 JDBC API를 이용해서 완성하려면
1. 부모 객체에서 부모 데이터 꺼내기
2. Item용 INSERT SQL 작성
3. 자식 데이터만 꺼내서 ALBUM용 INSERT SQL 작성
과 같은 과정을 거쳐야 하고 자식 타입을 구분할 DTYPE도 저장해야 한다.
조회도 마찬가지로 상당한 양의 코드를 작성하게 되는데 이러한 과정이 모두 패러다임의 불일치를 해결하기위해 드는 비용이다. 데이터베이스가 아닌 자바 컬렉션에 저장하는 경우에는 패러다임의 불일치를 해결할 필요가 없기 때문에 간단해진다.
✅ JPA와 상속
객체와 데이터베이스 간의 패러다임 불일치를 해결해주는 것 또한 JPA이다.
만약에 Item을 상속한 Album 객체를 저장한다고 한다면,
// JPA 메소드
jpa.persist(album);
// SQL
INSERT INTO ITEM ...
INSERT INTO ALBUM...
위와 같이 persist() 메서드만 이용하면 객체가 저장된다. 훨씬 코드량이 줄어들었다.
Album 또한 앞서 살펴보았던 find()를이용해서 객체를 조회하면 되고 이 과정에서 JPA가 ITEM과 ALBUM을 조인해서 데이터를 가져와 반환하기 때문에 개발자 입장에서는 훨씬 작성해야 할 코드가 줄어든다.
✨ 연관관계
연관관계 또한 객체와 테이블이 다루는 방식이 다르다.
객체
참조에 접근하여 연관된 객체를 조회
테이블
외래 키를 이용해서 다른 테이블과 연관관계를 조인을 이용 -> 연관된 테이블 조회
이러한 패러다임 불일치는 극복하기가 훨씬 어렵다...
예시>
객체
Member 객체가 해당 객체의 team 필드에 Team 객체의 참조를 보관하여 관계를 맺으므로 해당 필드에 접근 시 Team 객체를 조회할 수 있게 된다.
class Member {
Team team;
// 다른 필드 생략
Team getTeam(){
return this.team;
}
}
class Team {
}
member.getTeam();
테이블
MEMBER 테이블은 MEMBER.TEAM_ID 외래 키 컬럼을 이용해서 TEAM 테이블에 관계를 맺으므로 외래 키를 사용해 조인하면서 연관된 TEAM 테이블을 조회하게 된다.
SELECT M.*, T.*
FROM MEMBER M\
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
객체와 테이블은 위와 같이 각각 참조, 외래키와 조인을 사용한다는 차이가 있다.
그리고 객체의 경우에는 참조가 있는 방향으로만 조회 가능하다.
그래서 member.getTeam()은 가능하나 team.getMember()는 불가능하다.
그러나 테이블은 외래키를 통해서 MEMBER JOIN TEAM도 가능하고 TEAM JOIN MEMBER도 가능하다!
✅ 객체를 테이블에 맞추어 모델링
객체를 테이블 형식으로 억지로 꾸겨넣은 모델링을 하는 방법을 알아볼 것이다!
class Member{
String id;
Long teamId;
String username;
}
class Team {
Long id;
String name;
}
테이블에서 외래키를 사용하여 조인을 하는 것처럼 해당 필드에서 외래키를 추가해주었다.
이러한 방식을 적용하면 테이블 저장 및 조회에서는 편리하나 teamId 필드에 문제가 생긴다.
객체에서 아래와 같이 연관된 객체를 찾기 위해서는 참조 보관이 필요하다는 점이다.
Team team = member.getTeam();
즉 객체를 테이블에 맞추어 모델링하게 되면 참조를 통한 조회를 할 수 없고 이는 객체지향의 특징 없는 안 좋은 객체 모델링이다 ㅠㅠ
✅ 객체지향 모델링
class Member {
String id;
Team team;
String username;
Team getTeam() {
return team;
}
}
class Team {
Long id;
String name;
}
일단 Member.team에서 연관된 team의 참조를 보관하기에 참조를 통해 회원과 연관된 팀을 조회할 수 있다.
그렇지만 또 문제가 발생한다.
테이블을 저장하거나 조회하기 어렵다는 문제가 또 등장한다.
왜냐면 멤버 객체는 팀 필드로 연관관계를 맺고 멤버 테이블은 팀 아이디 외래 키로 연관 관계를 맺기 때문이다.
그렇기 때문에 이러한 패러다임 불일치로 인해 개발자가 중간 조율의 역할을 가지게 된다.
저장
객체를 데이터베이스에 저장하기 위해서는 team 필드를 TEAM_ID 외래 키 값으로 변환해야 한다.
즉 위의 예시에는 member.getTeam.getId() 와 같은 작업이 필요하게 되는 것이다
조회
외래 키 값을 참조로 변환하여 객체에 보관하는 과정이 필요하다.
그래서 먼저 SELECT 문으로 Member, Team 테이블을 조회하고
member.setTeam(team); 과 같이 직접 연관관계를 설정하여야 한다.
✅ JPA와 연관관계
JPA는 이렇게 연관관계와 관련된 패러다임 불일치 문제도 해결한다.
member.setTeam(team);
jpa.persist(member);
회원과 팀의 관계를 설정한 후 회원 객체를 저장하는 코드이다. JPA가 team의 참조를 외래 키로 변화하여 SQL을 데이터베이스에 전달한다. 조회의 경우에도 외래키를 참조로 변환하는 일을 JPA가 처리해주기 때문에 작성해야하는 코드량이 확실히 줄어들게 된다.
물론 이때까지의 문제들은, 개발자가 코드를 많이 작성되면 해결되는 문제들이었으나, 그것만으로도 해결되지 않는 연관관계에 대해 극복하기 어려운 불일치 문제도 존재한다.
✅ 객체그래프 탐색
객체 그래프 탐색은 참조를 이용해서 연관된 객체를 찾는 것이다.
객체 그래프 탐색 코드 예시
member.getOrder().getOrderItem() ...
위와 같이 객체는 마음껏 객체 그래프를 탐색할 수 있어야 하지만 SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지가 정해진다.
어디까지 객체그래프 탐색이 가능한지 알기 위해서는 DAO를 열어 SQL을 직접 확인하는 과정이 필요하다. 이렇게 강력한 의존관계 문제가 다시 한 번 발생하게 되는 것이다. 객체 그래프를 신뢰하여 사용할 수 도와주는 건 또 JPA다.
✅ JPA와 객체 그래프 탐색
JPA를 사용하면 객체 그래프를 자유롭게 탐색할 수 있다.
연관된 객체를 사용할 때 적절한 SQL을 실행하기에 연관된 객체를 신뢰하고 조회할 수 있는 것이다.
또한, 이 기능은 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미루는 지연로딩이다.
JPA는 지연로딩을 투명하게 처리한다.
-> 엔티티를 로딩할 때 지연 로딩을 사용하여 필요할 때만 데이터를 로드하고, 이를 개발자가 의식하지 않고도 처리할 수 있게 한다
투명하게 처리한다는 의미를 잘 모르겠어서 찾아봤는데
"투명하게 처리한다"는 의미는, 이런 방식이 개발자가 의도적으로 처리할 필요 없이 자동으로 이루어진다는 뜻이라고 한다!!
Member member =jpa.find(Member.class, memberId);
Order order = member.getOrder();
order.getOrderDate();
위와 같이 지연로딩은 멤버를 조회하는 시점에서 SQL 조인을 사용해서 함께 조회하기 때문에 효과적이다.
또한, 즉시로딩할 건지 지연로딩할 건지를 간단하게 정의할 수 있어 간편하다
✅ 비교
데이터베이스의 비교
- 기본 키의 값으로 로우 비교
객체의 동일성 비교
- == 비교
- 객체 인스턴스의 주소 값 비교
객체의 동등성 비교
- equals() 사용 비교
- 객체 내부의 값 비교-
객체의 동일성 비교
- == 비교
- 객체 인스턴스의 주소 값 비교
객체의 동등성 비교
- equals() 사용 비교
- 객체 내부의 값 비교
여기서 딱 생각나는 예시는 String 객체 비교에서 우리가 일반적으로 생각하는 비교를 사용하려면 동등성 비교를 해야한다는 것이다 !
어쨌든 객체와 데이터베이스는 다른 비교를 사용한다는 점이 중점이다. 이것 역시 패러다임의 불일치다.
이 불일치를 해결하기 위해서는 데이터베이스의 같은 로우 조회시 같은 인스턴스를 반환할 수 있도록 해야 하는데 이 작업은 쉽지 않다고 한다.
✅ JPA와 비교
JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //true
위의 코드에서는 동일성 비교에 성공하게 된다😊😊
✨ 정리
데이터베이스와 객체 간의 패러다임 불일치 문제가 많은 부분에서 발생하고 이를 극복하기 위해서 드는 비용이 많다. 이는 복잡성이 커질수록 극복비용이 더 커져서 객체 모델링을 하는 의미가 무색해지는데 이러한 문제는JPA를 사용하여 해결할 수 있다~
💬 1.3 JPA란 무엇인가?
JPA는 자바 진영의 ORM 기술 표준으로 어플리케이션과 JDBC 사이에서 동작한다.
ORM(Object-relational Mapping)은 객체와 관계형 데이터베이스를 매핑해준다. SQL을 대신 생성해준 것 외에도 패러다임 불일치 문제를 해결해줄 수 있는 것이 큰 장점이다. 그러므로 개발자는 매핑 방법만 생각하면 되므로 객체지향 어플리케이션 개발에만 집중할 수 있다.
✨ JPA 소개
JPA는 자바 ORM 기술에 대한 API 표준 명세로 인터페이스의 모음이라고 볼 수 있다.
✨ JPA를 사용해야 하는 이유
생산성
SQL을 직접 작성하지 않아도 되므로 객체 설계 중심에 집중할 수 있음
유지보수
- SQL을 직접 다루면 수정사항이 생겼을 때 JDBC API 코드를 모두 변경해야하나 JPA 사용 시에는 삭제/수정할 코드가 줄어든다.
- 패러다임 불일치를 해결하므로 유지보수하기 좋은 도메인 모델 설계가 가능하다
패러다임의 불일치 해결
상속, 연관관계, 객체 그래프 탐색, 비교하기 같은 문제를 해결해준다
성능
JPA는 애플리케이션과 데이터베이스 사이에서 동작하는데 이처럼 계층이 하나 더 있으면 최적화에 도움을 줄 수도 있다.
데이터 접근 추상화와 벤더 독립성
관계형 데이터베이스는 같은 기능이 벤더마다 사용법이 달라서 각 데이터베이스마다 사용법을 배워야 하는 경우가 생길 수도 있고, 그래서 데이터베이스를 손쉽게 바꿀 수 없다. 그러나, JPA룰 사용하면, 특정 데이터베이스 기술에 종속되지 않으므로 다른 데이터베이스로 변경이 쉽다.
표준
다른 구현 기술로 손쉽게 변경이 가능하다.
이번 장에서는 왜 JPA를 사용해야 하는지, JPA를 사용하지 않았을 경우 발생하는 문제점에 대해 다루어보았다. 별 생각없이 사용하던 것들이 사실은 이렇게 큰 문제가 있음을 깨닫게 되었다. 단지 JPA만 국한되는 점이 아니라, 어떤 프레임워크, 어떤 데이터베이스, 어떤 기술을 사용할 때 그에 대한 장점과 단점을 확실히 알아놓는 편이 코드를 작성할 때 생산성을 높일 수 있음을 여실히 느낄 수 있었다...
'BE > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[JPA STUDY] 4장. 엔티티 매핑 (0) | 2025.03.30 |
---|---|
[JPA STUDY] 03. 영속성 관리 (0) | 2025.03.22 |
[ JPA STUDY ] 2장.JPA 시작 (0) | 2025.03.15 |