23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
안녕하세요, 데보션영 3기 TechWave팀의 김유빈입니다 🌊
저는 데보션영 도서 스터디에서 '자바 ORM 표준 JPA 프로그래밍' 도서로 데보션영 분들과 함께 JPA에 대해 탐구했는데요,
저희 스터디는 10월 5일 마지막 발표와 함께 종료되었습니다!
정말 열정적이었던 JPA 스터디의 후기를 지금부터 풀어보고자 합니다
먼저 저희 스터디에는 권지원, 박지우, 신중은, 김유빈, 염정 이렇게 5명의 팀원이 함께했습니다.
저는 개인적으로 평소 JPA에 대한 이해가 부족하다고 느끼고 있었기도 하고,
책의 저자인 김영한님의 Spring 강의를 들으면서 해당 책을 나중에라도 꼭 공부해야겠다고 생각하고 있었기 때문에 이번 기회에 신청하게 되었습니다!
특히 데보션에서 도서를 지원해주셔서 정말 감사하게 공부할 수 있었습니다 🥳
저희 팀은 매주 1~2 단원정도 씩 분량에 맞게 나누어 스터디 일정을 잡았고, 한 주에 한 명씩 돌아가면서 이번주 내용을 발표하는 형태로 스터디를 진행했습니다.
혼자 공부할 때는 미루기 쉬운데, 각자 정해진 파트가 있어 성실히 공부하게 되었던 것 같습니다.
특히 팀에서 스터디 규칙을 정해두었기 때문에 더 열심히 참여할 수 있었습니다.
발표자가 아니더라도 발표내용에 대한 질문, 코멘트, 새로 배운 것, 중요한 부분 등 최소 3개 이상을 반드시 공유해야 했기에 많은 공부가 되었습니다.
덕분에 담당한 단원을 꼼꼼하게 공부하고 정리할 수 있었습니다.
평소에 자주 사용하는 노션을 활용하여 공유하다보니, 개인적으로 정리하기에도 편리했습니다.
하지만 무엇보다 모든 팀원분들이 성실히 참여하셔서 성공적인 스터디가 될 수 있었던 것 같습니다!
아래는 실제 스터디 진행 리스트입니다.
각 주차별 진행된 내용의 핵심 단어를 기반으로 요약했습니다! 혹시 책에 관심있으신 분들이 있다면, 아래 요약 내용을 확인해보시면 큰 도움이 될 것 같습니다.
JPA란JPA(Java Persistence API)는 자바 개발자에게 객체와 관계형 데이터베이스 간의 매핑을 지원하는 ORM 기술 표준으로, 애플리케이션과 JDBC 사이에서 동작.
**ORM(Object-Relational Mapping)**객체와 관계형 데이터베이스 간의 패러다임 불일치를 해결하기 위해 객체와 테이블을 매핑하며, JPA는 SQL 작성 및 변환 작업을 대신 처리해 개발자의 부담을 줄임.
SQL을 직접 다뤘을 때의 문제점코드 반복, SQL 의존적 개발, 강한 의존관계 등으로 인해 계층 분할이 어렵고 수정 시 복잡성이 증가.
JPA의 역할SQL을 직접 작성하지 않고 API를 사용해 저장, 조회, 수정 등을 처리하며, 연관관계를 참조로 변환, 객체 그래프 탐색, 동일성 보장 등을 제공.
JPA의 간단한 동작 원리객체 저장 시 SQL을 자동 생성하여 데이터베이스에 저장하고, 조회 시 객체 그래프를 탐색하며 필요한 데이터를 적절히 조회.
H2 오류 해결H2 데이터베이스를 사용할 경우, 파일 경로(예: Users/user/test test.mv.db)가 올바르게 설정되어 있는지 확인.
엔티티 매니저와 영속성 컨텍스트EntityManagerFactory는 애플리케이션에서 하나만 생성해 공유하며, EntityManager는 가볍게 생성하지만 스레드 간 공유는 불가능. 영속성 컨텍스트는 엔티티를 영구 저장하고 관리하는 논리적 개념.
엔티티 생명주기엔티티는 비영속(new), 영속(managed), 준영속(detached), 삭제(removed)의 4가지 상태로 관리되며, persist(), remove(), detach(), merge() 메소드로 상태를 전환.
영속성 컨텍스트의 특징1차 캐시, 동일성 보장, 트랜잭션 지원 쓰기 지연, 변경 감지, 지연 로딩 등의 이점을 제공하며, 엔티티는 식별자 값(@Id)을 기준으로 관리.
플러시와 트랜잭션플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하며, 트랜잭션 커밋, JPQL 실행, flush() 호출 시 동작. 기본적으로 FlushModeType.AUTO 설정을 사용.
준영속 상태와 병합준영속 상태는 영속성 컨텍스트에서 분리된 상태로, detach(), clear(), close()로 전환 가능. 병합(merge)은 준영속 및 비영속 상태의 엔티티를 영속 상태로 복귀시키는 메소드.
@Entity와 매핑JPA 엔티티는 기본 생성자가 필요하며, @Table을 사용해 테이블 이름, 스키마 이름, 유니크 제약 조건 등을 설정.
데이터베이스 스키마 자동 생성DDL 자동 생성은 개발 환경에서 사용하고, 운영 환경에서는 직접 생성하거나 수정한 DDL을 사용하는 것을 권장.
기본 키 매핑JPA는 @Id로 직접 할당하거나 @GeneratedValue를 사용해 IDENTITY, SEQUENCE, TABLE, AUTO 전략 중 하나를 선택.
주요 어노테이션@Column은 컬럼 매핑을, @Enumerated는 enum 타입 매핑을, @Temporal은 날짜 매핑을, @Lob은 BLOB/CLOB 매핑을, @Transient는 매핑 제외를 설정.
IDENTITY와 SEQUENCE 비교IDENTITY는 DB에 기본 키 생성을 위임하고 즉시 저장하며, SEQUENCE는 시퀀스를 통해 식별자를 미리 조회해 성능 최적화를 지원.
연관관계 매핑 개념연관관계 매핑 시 다중성(다대일, 일대다, 일대일, 다대다), 단방향·양방향, 연관관계의 주인을 고려해 설계.
다대다의 한계와 해결다대다 매핑은 연결 테이블을 자동 처리하지만, 추가 컬럼이 필요한 경우 연결 엔티티를 사용해 일대다-다대일로 풀어내야 함.
일대다 단방향 매핑의 단점외래 키가 반대 테이블에 있어 추가적인 UPDATE SQL이 필요하며, 효율적이지 않음.
복합 키 매핑@IdClass 또는 @EmbeddedId를 사용해 복합 키를 매핑하며, 비식별 관계를 사용하는 것이 단순하고 편리.
상속관계 매핑객체의 상속 구조를 데이터베이스의 슈퍼타입-서브타입 관계에 매핑하며, 조인 전략, 단일 테이블 전략, 구현 클래스마다 테이블 전략을 선택적으로 사용.
복합 키 매핑복합 키를 매핑할 때 @IdClass 또는 @EmbeddedId를 사용하며, 식별 관계는 부모 테이블의 기본 키를 자식 테이블의 기본 키로 사용하는 구조. 비식별 관계는 대리 키를 사용하는 것을 권장.
조인 테이블 매핑연관관계 관리에 조인 컬럼 대신 조인 테이블을 사용하며, 일대일, 일대다, 다대다 관계에서도 활용 가능. 컬럼 추가 시 새로운 엔티티를 만들어 매핑 필요.
엔티티 하나에 여러 테이블 매핑@SecondaryTable을 사용해 한 엔티티를 여러 테이블에 매핑 가능하지만, 최적화 어려움으로 인해 별도 엔티티로 분리하는 것을 권장.
프록시와 지연 로딩프록시는 실제 엔티티 대신 데이터베이스 접근을 지연시키는 객체로, 지연 로딩(FetchType.LAZY) 설정 시 사용. 객체를 실제로 사용하기 전까지 데이터베이스 조회를 미룬다.
프록시 초기화와 특징프록시는 실제 클래스를 상속받아 생성되며, 실제 엔티티가 필요할 때 초기화(데이터베이스 조회)하여 참조를 저장. 프록시 객체와 실제 객체는 개발자가 구분 없이 사용할 수 있다.
즉시 로딩 vs. 지연 로딩즉시 로딩(FetchType.EAGER)은 연관 엔티티를 즉시 조회하며 조인 쿼리를 사용. 지연 로딩은 프록시를 반환하고 실제 사용 시 조회. 상황에 따라 둘을 조정해 사용.
컬렉션 래퍼Hibernate는 엔티티의 컬렉션을 PersistentBag 같은 컬렉션 래퍼로 변환해 관리. 컬렉션도 지연 로딩 처리하며, 프록시와 유사한 역할 수행.
9장 - 값 타입
값 타입은 식별자가 없으며 복사, 불변 객체로 사용하는 것이 안전.
@Embedded를 사용해 재사용 가능한 값 타입으로 객체 매핑 가능.
동일 객체 참조로 값이 공유되는 문제는 clone()으로 해결.
값 비교는 ==(동일성) 대신 equals()(동등성) 사용.
10장 - 객체지향 쿼리
JPQL은 엔티티 객체를 대상으로 SQL을 추상화한 객체지향 쿼리.
이름 기반 파라미터(:name)를 사용한 명확한 바인딩 추천.
페치 조인은 N+1 문제를 해결하며 연관 데이터를 한 번에 조회.
프록시 대신 실제 엔티티 반환으로 성능 및 코드 최적화.
Criteria자바 코드로 JPQL을 작성하는 공식 JPA 기능으로, 컴파일 시점 오류 검출, 동적 쿼리 작성이 가능. 하지만 복잡하고 실용성이 낮아 QueryDSL 사용 권장.
QueryDSLJPQL을 코드로 작성하며 직관적이고 간결한 문법 제공. 동적 쿼리, 조인, 페이징 등 다양한 기능을 지원하며, 컴파일 시점에 문법 오류를 검출할 수 있음.
네이티브 SQLJPA가 지원하지 않는 특정 데이터베이스 기능이나 쿼리를 직접 실행할 때 사용. JPQL과 달리 위치 기반 파라미터만 지원하며, 이식성이 떨어져 최소한으로 사용하는 것이 좋음.
벌크 연산대량 데이터를 수정하거나 삭제할 때 사용하며, 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리 실행. 벌크 연산 후에는 em.refresh()나 컨텍스트 초기화로 동기화 필요.
JPQL과 플러시 모드JPQL 실행 시 플러시 모드에 따라 영속성 컨텍스트가 반영됨. 기본값 AUTO는 쿼리 실행 전 플러시하며, COMMIT 모드는 커밋 시에만 플러시하여 성능 최적화 가능.
웹 애플리케이션 제작JSP와 스프링 MVC로 쇼핑몰 구현, 회원, 상품, 주문 기능 포함. JPA, Hibernate로 데이터 저장, 계층형 구조(Service, Repository) 사용.
도메인 모델 및 설계회원, 상품, 주문 도메인 설계. 다대다 관계를 주문상품 엔티티로 풀어내고, 카테고리 및 배송 정보 관리.
애플리케이션 구현회원 등록/조회, 상품 관리, 주문/취소 기능 구현. 상품 등록과 수정 시 병합 또는 변경 감지 사용.
주문 기능주문 생성, 취소, 내역 조회 포함. 동적 검색 구현, 상품 주문 시 재고 감소와 취소 시 재고 증가 로직 적용.
스프링 데이터 JPA 소개데이터 접근 계층의 반복 작업을 줄이고, CRUD를 쉽게 구현. JpaRepository 인터페이스 제공으로 기본 메소드 지원 및 메소드 이름으로 JPQL 생성 가능.
기능과 설정간단한 CRUD, 쿼리 메소드, @Query, 네이티브 쿼리, 페이징, 정렬, 벌크 연산 제공. @EnableJpaRepositories로 리포지토리 자동 등록.
사용자 정의 리포지토리인터페이스를 추가로 정의해 맞춤형 메소드 구현 가능. 기본 구현 클래스 이름은 RepositoryName + Impl.
QueryDSL 통합QueryDslPredicateExecutor로 간단히 사용 가능. 더 복잡한 기능은 QueryDslRepositorySupport로 직접 구현.
스프링 컨테이너의 트랜잭션 전략같은 트랜잭션 내에서는 동일한 영속성 컨텍스트를 사용하며, 트랜잭션 종료 후 엔티티는 준영속 상태가 된다. 멀티스레드 환경에서도 트랜잭션이 스레드별로 분리되므로 안전하다.
준영속 상태와 지연 로딩 문제트랜잭션 종료 후 지연 로딩 시 프록시 초기화 실패로 LazyInitializationException 발생. 변경 감지 문제는 없지만, 프리젠테이션 계층에서 지연 로딩을 지원해야 할 경우 문제가 된다.
지연 로딩 해결 방법
트랜잭션 내에서 연관 엔티티를 미리 로딩 또는 강제 초기화.
OSIV(Open Session In View)로 영속성 컨텍스트를 뷰 계층까지 유지.
스프링 OSIV 동작요청 시 서블릿 필터 또는 인터셉터에서 영속성 컨텍스트를 생성하고, 서비스 계층에서 트랜잭션 처리 후에도 영속성 컨텍스트를 유지. 컨트롤러와 뷰 계층에서 영속성 컨텍스트를 사용 가능하며, 응답 후 종료.
JPA 컬렉션JPA는 List, Set, Map을 지원하며, @OrderColumn은 데이터베이스에 순서 컬럼을 저장, @OrderBy는 DB의 ORDER BY를 사용해 컬렉션 정렬. 실무에서는 @OrderBy를 권장.
Converter와 ListenerConverter는 엔티티 데이터를 변환해 DB에 저장하며, Listener는 엔티티 이벤트(삽입, 수정, 삭제 등)를 처리. 리스너는 엔티티 직접 적용, 별도 등록, 기본 리스너로 사용 가능.
Fetch Join vs. Entity GraphFetch Join은 JPQL로 객체와 연관 엔티티를 한 번에 조회. Entity Graph는 어노테이션 기반으로 Fetch Join을 간단히 설정해 N+1 문제를 해결하며, 기본적으로 LEFT OUTER JOIN만 지원.
N+1 문제와 해결 방법연관 엔티티 조회 시 추가 쿼리가 발생(N+1 문제). Fetch Join이나 @EntityGraph로 한 번의 쿼리로 연관 데이터를 조회해 해결. @EntityGraph는 선언적 사용으로 간편하며 성능 최적화에 도움.
예외 처리JPA 예외는 javax.persistence.PersistenceException 기반의 언체크 예외로, 트랜잭션 롤백 여부에 따라 구분된다.
롤백 표시 예외: EntityExistsException, TransactionRequiredException 등
롤백 비표시 예외: NoResultException, QueryTimeoutException 등롤백 시 영속성 컨텍스트 초기화 필요.
엔티티 비교
같은 영속성 컨텍스트: 동일성과 동등성(==, .equals()) 모두 일치.
다른 영속성 컨텍스트: == 불일치, .equals()는 PK 기준으로 판단하도록 재정의 필요.
프록시 심화
프록시는 영속성 컨텍스트와 연결되어 초기화 가능.
부모 타입 조회 시 다형성 제한(instanceof 비교 불가).
해결: JPQL로 직접 조회, 인터페이스 제공, 하이버네이트 unProxy 사용.
N+1 문제 해결
Fetch Join, @EntityGraph로 연관 엔티티를 한 번에 조회.
하이버네이트 기능: @BatchSize(일괄 조회), @Fetch(FetchMode.SUBSELECT)(서브쿼리 사용).
트랜잭션과 격리 수준트랜잭션 격리 수준은 동시성과 무결성을 조정하며, READ COMMITTED가 JPA 기본 설정.
낙관적 락@Version 기반으로 데이터 변경 충돌 방지. OPTIMISTIC_FORCE_INCREMENT로 버전 강제 증가 가능.
비관적 락데이터베이스 락(PESSIMISTIC_WRITE, PESSIMISTIC_READ)을 사용해 즉시 충돌 감지.
2차 캐시애플리케이션 범위 캐시로 DB 접근 최소화. @Cacheable로 설정하며, 쿼리 캐시 사용 시 엔티티 캐시 병행 필요.
스터디가 끝나고, 목표했던 만큼 JPA를 어느정도 이해하게 된 것 같습니다!
혼자서 공부했다면 분명 끝까지 못했을 것 같은데, 좋은 팀원들과 함께하여 완주할 수 있었습니다.
함께 공부한 내용도 잘 정리하여 나중에 다시 볼 수 있도록 해두었습니다.
그리고 스터디 팀원분들 덕분에 스터디를 즐겁게 마무리할 수 있었습니다!
다음에 시간이 된다면 다같이 모여서 회고를 나누면 좋을 것 같습니다 😁
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.