diff --git a/keyword/chapter05/keyword.md b/keyword/chapter05/keyword.md new file mode 100644 index 0000000..1b32ba1 --- /dev/null +++ b/keyword/chapter05/keyword.md @@ -0,0 +1,105 @@ +## Domain + +▶️ 특정 시스템이 다루는 비즈니스 규칙, 데이터, 기능 등을 포함하는 개념 + +즉, 시스템이 다루는 비즈니스 문제 영역 + +ex) 쇼핑몰 → 고객, 상품, 주문 등… + +금융 시스템 → 계좌, 거래, 고객 등… + +### JPA에서 도메인 + +- JPA에서는 도메인을 표현하기 위해 도메인 엔티티 클래스 정의 (@Entity) +- 데이터베이스 테이블과 매핑되며, 특정 도메인에 해당하는 데이터와 비즈니스 로직을 캡슐화 + +### DB에서 도메인 + +- 각 속성들이 가질 수 있는 값의 모임 + + ex) 성별 → 남성, 여성 + + 나라 → 대한민국, 미국, 중국, 일본 + +- 각 도메인은 테이블, 뷰, 인덱스, 제약조건 등의 구조로 설계됨 + +### 도메인의 중요성 + +- 시스템이 해결하려는 비즈니스 문제의 핵심이므로 정확한 설계가 중요 +- 비즈니스 요구사항을 잘 반영해야 이후 유지보수 및 기능 확장에 용이 +- 데이터 무결성을 유지하는데 중요함 + + + +## 연관관계 매핑 + +### ▶️ 두 객체가 서로 참조해야 하는 상황에서 정의하는 연관관계 방식 + +- 양방향 관계는 단방향 관계 2개를 맺은 것 + + → 실제로 양방향 연관관계는 없음, 단지 양방향인 것 처럼 보이게 한 것 + +- 양방향 객체 둘 중 하나는 반드시 외래 키를 관리해야 함 + +### MappedBy + +- 양방향 매핑을 할 때에는 반드시 한쪽 객체에 MappedBy 옵션을 설정해야 함 + +```java +@OneToMany(mappedBy = "member", cascade = CascadeType.ALL) +@Builder.Default + private List reviewList = new ArrayList<>(); +``` + +### 연관관계 주인 + +- 객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 함 +- 연관관계 주인만 외래 키를 관리해야 함 +- 주인은 mappedBy 속성을 사용하지 않음 +- 주인이 아니면 mappedBy 속성으로 주인을 지정 + + #### 주인 정하기 + + - 외래 키가 있는 곳을 주인으로 정함 + - N : 1 관계일 경우 N에 해당하는 쪽에 외래 키가 존재 (N이 주인) + + → @ManyToOne 어노테이션을 사용하는 클래스가 주인 + + +## N+1 문제 +### ▶️ 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 개수(N) 만큼 연관관계의 조회 쿼리가 추가로 발행하여 데이터를 읽어오는 것 + +ex) 1번의 쿼리를 날렸을 때 의도하지 않은 N번의 쿼리가 추가적으로 실행 + +- JPA Repository를 활용해 인터페이스 메소드를 호출할 때 발생 +- 1 : N 또는 N : 1 관계를 가진 엔티티를 조회할 때 발생 + +### 발생 상황 + +- JPA Fetch 전략이 EAGER 전략으로 데이터를 조회하는 경우 +- JPA Fetch 전략이 LAZY 전략으로 데이터를 가져온 이후에 연관 관계인 하위 엔티티를 다시 조회하는 경우 + +### 발생 이유 + +- JPA Repository로 find시 실행하는 첫 쿼리에서 하위 엔티티까지 한번에 가져오지 않고 하위 엔티티를 추가로 조회하기 때문 +- JPQL은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL만 가지고 SQL을 생성하기 때문 + +### EAGER (즉시 로딩)인 경우 + +1. JPQL에서 만든 SQL을 통해 데이터를 조회 +2. 이후 JPA에서 Fetch 전략을 가지고 해당 데이터의 연관 관계인 하위 엔티티들을 추가 조회 +3. 2번 과정으로 인해 N + 1 문제 발생 + +### LAZY (지연 로딩)인 경우 + +1. JPQL에서 만든 SQL을 통해 데이터를 조회 +2. JPA에서 Fetch 전략을 가지지만, 지연 로딩이기 때문에 추가 조회는 하지 않음 +3. 하위 엔티티를 가지고 작업을 하게 되면 추가 조회가 발생하면서 N + 1 문제 발생 + +### 해결 방법 + +#### Fetch join + +- N + 1 자체가 발생하는 이유는 한쪽 테이블만 조회하고 연결된 다른 테이블은 따로 조회하기 때문 +- 미리 두 테이블을 JOIN하여 한 번에 모든 데이터를 가져오면 됨 + diff --git a/keyword/chapter06/keyword.md b/keyword/chapter06/keyword.md new file mode 100644 index 0000000..b85478f --- /dev/null +++ b/keyword/chapter06/keyword.md @@ -0,0 +1,80 @@ +# 제목 없음 + +## **지연로딩과 즉시로딩의 차이** + +### 지연 로딩 (Lazy Loading) + +▶️ 엔티티를 처음 조회할 때 연관 엔티티 데이터를 가져오지 않고, 해당 데이터가 실제로 필요한 시점에 가져오는 방식 + +1. 장점 + - 메모리 효율이 증가 + - 불필요한 데이터베이스 호출 감소 + - 초기 조회에서 연관 데이터를 가져오지 않기 때문에 성능 향상 +2. 단점 + - 실제로 데이터를 사용할 때 추가적인 호출이 필요하므로 시스템 설계에 따라 해당 과정에서 과부하가 걸린다면 성능이 저하됨 + - 연관된 데이터를 사용하는 시점에 트랜잭션이 반드시 열려있어야 함 + +### 즉시 로딩 (Eager Loading) + +▶️ 엔티티를 조회할 때 연관된 모든 데이터를 함께 가져오는 방식 + +1. 장점 + - 초기 조회 시 연관된 데이터를 함께 가져오기 때문에 이후에 추가 쿼리가 발생하지 않음 +2. 단점 + - 불필요한 데이터까지 가져와서 메모리가 낭비될 수 있음 + - 연관 데이터가 많을 경우 속도가 저하됨 + - 필요하지 않은 데이터를 조회하게 되면 전체적인 성능과 효율이 감소 + +### 차이점 + +| 지연 로딩 | 구분 | 즉시 로딩 | +|:------------------------------------:|:-------:|:-----------------------------:| +| 실제로 필요할 때 | 로딩 시점 | 처음 엔티티 조회할 때 | +| 초기 조회가 빠름, 추가 쿼리 발생 가능 | 성능 | 처음 조회가 느림, 추가 쿼리가 없음 | +| 불필요한 데이터가 없어 메모리 사용이 적음 | 메모리 사용 | 모든 데이터를 로딩하기 때문에 메모리 사용량이 증가 | +| fetch = FetchType.LAZY | 설정 방식 | fetch = FetchType.EAGER | +| 연관된 데이터를 사용할지 여부가 불확실하거나 필요하지 않은 경우 | 사용 기준 | 연관 데이터가 자주 사용되거나 항상 필요한 경우 | + +## QueryDSL + +▶️ JPQL을 Java 코드로 작성할 수 있도록 하는 라이브러리로 도메인 특화 언어 + +### 1. 특징 + +- 타입 안전성 : 컴파일 시점에서 검사가 되기 때문에 런타임 오류를 사전에 방지 +- 도메인 특화 : 도메인 엔티티에 맞춘 쿼리를 작성할 수 있는 API를 제공 +- 다양한 데이터베이스와 연계 가능 + +### 2. 장점 + +- 유연한 쿼리 작성 가능 : 복잡한 동적 쿼리를 쉽게 작성할 수 있음 +- 조건 추가나 수정이 쉬어 비즈니스 로직의 가독성과 유지보수성 증가 +- 타입 안전성 +- JPQL과 SQL을 모두 지원하기 때문에 유연한 사용이 가능 + +### 3. 단점 + +- 설정이 복잡하고 Gradle 버전에 따른 지원 여부가 다름 +- 엔티티 구조가 변경되면 Q 클래스도 다시 생성되어야 하기 때문에 추가 설정이 필요 + +### 4. 사용 설정 + +```java +dependencies { + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta' // Q클래스 자동 생성 +} +``` + +### 5. 사용 예제 + +```java +QMember member = QMember.member; +JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); + +List members = queryFactory + .selectFrom(member) + .where(member.age.lt(20)) // 20살 이하 회원 조회 + .fetch(); + +``` \ No newline at end of file diff --git a/mission/chapter05/img.png b/mission/chapter05/img.png new file mode 100644 index 0000000..6f4ac09 Binary files /dev/null and b/mission/chapter05/img.png differ diff --git a/mission/chapter05/mission.md b/mission/chapter05/mission.md new file mode 100644 index 0000000..db8013f --- /dev/null +++ b/mission/chapter05/mission.md @@ -0,0 +1 @@ +![img.png](img.png) \ No newline at end of file