김영한 "실전! 스프링 부트와 JPA 활용1"을 최근에 다시 들었다.
내용을 전부 정리하기 보다는 챕터별로 중요하게 느낀 내용 위주로 간략하게 정리하는게 목표다.
실전! 스프링 부트와 JPA 활용1 강의 내용 정리
프로젝트 세팅
spring.jpa.hibernate.ddl-auto 옵션
- none: Hibernate가 DDL을 관리하지 않음. (기본값)
- create: 애플리케이션 시작 시 기존 테이블을 삭제하고 새로 생성.
- create-drop: create와 동일하지만 애플리케이션 종료 시 테이블 삭제.
- update: 기존 테이블 구조를 유지하면서 변경된 부분만 업데이트.
- validate: 테이블이 엔티티와 일치하는지 확인만 하고 수정하지 않음.
보통 운영 환경에서는 none 또는 validate,
개발 환경에서는 **update 또는 create**를 많이 사용한다.
도메인 분석 설계
다대다 관계일 경우 일대다 다대일로 보통 풀어서 설계한다.
이유는 어차피 Entity를 다대다로 만들더라도 데이터베이스는 일대다 다대일로 바뀌어서 만들어진다. 따라서 데이터베이스와 구조가 일치하지 않아서 복잡성이 높아질 수 있고, 실무에서는 중간테이블에 또 다른 정보를 넣기도 해서 일대다 다대일로 풀어서 구현하자.
외래 키가 있는 곳을 연관관계의 주인으로 정하자.
연관관계의 주인은 비즈니스상에 우위에 있다고 정하는 것이 아니라 외래키를 관리하는 쪽이 맡으면 된다.(단 무조건은 아니다.)
mappedBy <- 연관관계 주인이 아닌쪽(양방향일 경우) JoinColumn <- 연관관계 주인쪽
3. cascade 옵션에 대해서 한번 보자.
CascadeType.RESIST
– 엔티티를 생성하고, 연관 엔티티를 추가하였을 때 persist() 를 수행하면 연관 엔티티도 함께 persist()가 수행된다. 만약 연관 엔티티가 DB에 등록된 키값을 가지고 있다면 detached entity passed to persist Exception이 발생한다.
완전히 보모와 생명주기 동일할 때 사용
CascadeType.MERGE
– 트랜잭션이 종료되고 detach 상태에서 연관 엔티티를 추가하거나 변경된 이후에 부모 엔티티가 merge()를 수행하게 되면 변경사항이 적용된다.(연관 엔티티의 추가 및 수정 모두 반영됨)
CascadeType.REMOVE
– 삭제 시 연관된 엔티티도 같이 삭제됨
CascadeType.DETACH
– 부모 엔티티가 detach()를 수행하게 되면, 연관된 엔티티도 detach() 상태가 되어 변경사항이 반영되지 않는다.
CascadeType.ALL
– 모든 Cascade 적용
getter는 열지만 setter는 닫자
~ToOne쪽에 fetch = FetchType.Lazy로 닫자.(~ToMany는 어차피 자동적용이지만 팀프로젝트시 명시적으로 닫자)
Entity가져올 때 연관된 데이터도 다 가져와주는 좋은 기능이지만 연관관계가 복잡한 경우 전부 가져오므로 그냥 일괄적으로 닫고
필요할 때 가져오는 Lazy로 설정하자. 필요할 경우 fetch join 같은걸 사용하거나 하면 된다.
EAGER는 JPA가 자동으로 JOIN을 사용해서 데이터를 한 번에 가져오려고 함 → 성능 이슈 발생 가능
임베디드 타입 (ex Address)
주문에 주소, 회원의 주소처럼 공통적으로 사용되고 실제 엔티티에 핵심은 아니지만 내용이 많은경우 이렇게 빼는 듯하다.
컬렉션은 필드에서 초기화 하자. (필드 선언할 때 초기화도 진행)
nul문제에서 안전하다.
예제 애플리케이션구조
회원 도메인 개발
Repository
@Repository : 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 변환
@PersistenceContext: 엔티티 매니저(EntityManager) 주입
@PersistenceUnit: 엔티티 매니터 팩토리(EntityManagerFactory)주입 (어차피 안 썼었음 직접 EntityManagerFactory 이거 안 쓰니까)
Service
@Transactional: 트랜잭션, 영속성 컨텍스트
읽기 전용일 경우 readOnly=true 설정하기
회원가입시 검증로직이 있어도 멀티 스레드 환경을 고려해서 회원명 컬럼에 유니크 제약조건을 추가하는 것이 안전하다.(유니크여야할 경우만)
스프링 필드 주입 대신에 생성자 주입을 사용하자.
생성자하나면 @Autowired 생략해도 주입해줌
테스트 관련
@RunWith(SpringRunner.class): 스프링과 테스트 통합
@SpringBookTest: 스프링 부트 띄우고 테스트 (이게 없으면 @Autowired 다 실패)
@Transactional : 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가
끝나면 트랜잭션을 강제로 롤백
tset/resources/application.yml 에 만들면 test시에는 이거 읽음.
이렇게 통합적인 테스트 말고 단위테스트를 권장하기는 한다.
주문 도메인 개발
동적 쿼리 비교
JPQL 동적으로 처리하기 위해서 상당히 많은 코드 필요 + 문자열로 만들다보니 컴파일 시점에 오류를 알 수 없다.
Criteria 문자열은 아니지만 너무 복잡 보고 해석이 안 된다.
QueryDSL 제일 편하다 이걸 사용하자.
웹 개층 개발
폼 객체 vs 엔티티 직접 사용
요구사항이 간단할 때는 상관없을 수 있지만 조금만 복잡해지면 엔티티는 화면에 종속적으로 변하고 너저분해 질 수 있다.
결국 유지보수를 어렵게 만든다. 실무에서는 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야한다.
화면이나 API에 맞는 폼객체나 DTO를 사용하자.
변경 감지와 병합
준영속 엔티티 = 영속성 엔티티가 더는 관리하지 않는 엔티티를 발한다. (특징 식별자를 가지고있다.)DB에 한번 저장됨
준영속 엔티티를 수정하는 2가지 방법
변강 감지 사용(엔티티 조회 -> 변경 -> 더티체킹 -> DB변경 ), 병합(merge) 사용
준영속 엔티티의 식별자값으로 조회함 -> 영속 엔티티의 값을 준영속 엔티티의 값으로 덮어버림 (병합) -> 트랜잭션 커밋 시점 변경감지 update
문제 : 병합을 사용하면 모든 속성이 변경된다. 병합시 값이 없으면 null로 업데이트 할 위험도 있다.
엔티티를 변경할 때는 변경 감지를 활용하자(병합쓰지 말자.)
정리
간략하게 정리가 끝났다. JPA로 프로젝트하기 전에 해당 내용을 상기하고 프로젝트를 진행하면 좋을 것 같다.
'JPA' 카테고리의 다른 글
실전! 스프링 데이터 JPA 강의 정리 (0) | 2025.02.12 |
---|---|
[에러정리] TransientObjectException : save the transient instance before flushing 에러 (0) | 2025.02.04 |
JPA 양방향 연관관계의 주인 (0) | 2025.02.01 |
JPA (2) | 2025.02.01 |