From 08151df47be96fe38b51d094acff3784e6e5e271 Mon Sep 17 00:00:00 2001 From: InSooBeen Date: Fri, 8 Nov 2024 03:21:31 +0900 Subject: [PATCH 1/2] keyword: submit week5 keyword.md --- keyword/chapter05/keyword.md | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 keyword/chapter05/keyword.md diff --git a/keyword/chapter05/keyword.md b/keyword/chapter05/keyword.md new file mode 100644 index 0000000..0604e03 --- /dev/null +++ b/keyword/chapter05/keyword.md @@ -0,0 +1,172 @@ +# 5주차 핵심 키워드🎯 + +## Domain + +- 비즈니스 로직이나 애플리케이션의 핵심 개념을 나타내는 용어로 해결하고자하는 문제 영역을 뜻함 +- 하나의 도메인은 하위 도메인으로 나눌 수 있음 (ex. 온라인 쇼핑몰이라는 도메인 → 결제, 장바구니, 배송 등의 하위 도메인) + +## Domain Model + +- 특정 문제와 관련된 모든 주제의 개념 모델로, 다양한 엔티티, 엔티티의 속성, 역할, 관계, 제약을 기술함 +- 문제에 대한 솔루션은 기술하지 않음 +- 도메인을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화시킨 것 + +## Domain Driven Design + +- 과거에는 도메인 전문가가 만든 도메인 모델을 구현 담당자가 구현함 → 이 과정에서 많은 도메인 지식의 유실이 발생 → 전문가가 요구한 소프트웨어가 만들어지지 않는 문제 발생 +- 문제를 해결하고자 도메인 모델의 적용 범위를 구현까지 확장하여 도메인 지식이 구현 코드에 반영되도록 한 것이 도메인 주도 설계 방식임 + + +## 양방향 연관관계 + +- 서로 간의 참조가 가능한 두 객체는 양방향 연관관계에 있음 +- 즉, 하나의 외래 키로 데이터베이스 테이블 간에 서로를 참조하는 관계를 의미, 외래키를 통해 양방향 조회 가능 +- 서로 다른 단방향 관계 2개로 보는 것이 더 정확함 +- ex) 회원과 팀(다대일 관계), 팀과 회원(일대다 관계) + +## 양방향 매핑 + +```java +//Memebr 클래스 +@Entity +public class Member { + + @Id + @Column(name = "MEMBER_ID") + private String id; + + private String username; + + @ManyToOne + @JoinColumn(name="TEAM_ID") + private Team team; + + // 연관관계 설정 + public void setTeam(Team team) { + this.team = team; + } + + // Getter, Setter ... +} + +//Team 클래스 +@Entity +public class Team { + + @Id + @Column(name = "TEAM_ID") + private String id; + + private String name; + + //일대다 관계는 여러 건과 연관관계를 맺을 수 있으므로 컬렉션을 사용 + //@OneToMany 속성은 양방향 관계일 때 사용, 반대쪽 매핑의 필드 이름을 값으로 주면 됨 + @OneToMany(mappedBy = "team") + private List members = new ArrayList(); + + // Getter, Setter ... + +} +``` + +## 양방향 매핑의 규칙 : 연관관계의 주인 + +**객체** + +엄밀히 말하면 객체에는 양방향 연관관계가 없으며, 서로 다른 단방향 연관관계 2개를 애플리케이션 로직으로 묶어서 양방향인 것처럼 보이게 하는 것 + +**데이터베이스 테이블** + +데이터이스 테이블은 외래 키 하나로 양쪽이 서로 조인할 수 있으므로 테이블은 외래 키 하나만으로 양방향 연관관계를 맺음 + +- Entity를 양방향으로 설정하면 객체의 참조는 2개인데 외래키는 1개이므로 차이가 발생함 +- JPA에서는 두 객체의 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인이라고 함 + +**양방향 매핑의 규칙** + +- 두 연관관계 중 하나를 연관관계의 주인으로 정해야 함 +- 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리할 수 있음. 주인이 아닌 쪽은 읽기만 가능 +- 주인은 mappedBy 속성을 사용하지 않으며, 주인이 아닌 쪽에서 mappedBy 속성 값으로 연관관계의 주인을 지정함 (위의 코드에선 Member클래스의 team 필드가 Team클래스의 외래키 역할을 하며, Member클래스가 연관관계의 주인임) +- 연관관계가 주인은 외래키가 있는 곳으로 정함 + + +## N+1 문제의 정의 + +특정 객체를 대상으로 수행한 쿼리가 해당 객체가 가지고 있는 연관관계 조회하게 되면서 N번의 추가적인 쿼리가 발생하는 문제 + +## N+1 문제의 원인과 해결방법 + +객체는 연관관계를 통해 레퍼런스를 가지고 있으면 언제든지 메모리 내에서 **Random Access**를 통해 연관 객체에 접근할 수 있지만 관계형 데이터베이스의 경우 **Select 쿼리**를 통해서만 조회할 수 있기 때문 + +**fetch 전략이 **지연(Lazy) 로딩*일 때*** + +> ***지연로딩** +객체를 조회하는 시점에서 실제 객체가 아닌, 가짜(프록시) 객체를 가져온 후 , 객체를 사용하는 시점에 실제 객체를 가져오도록 하는 방식* +> + +findAll을 통해 엔티티 A 목록을 조회하는데 A에는 연관된 엔티티 B의 목록이 있다고 하자. + +1. select * from A를 통해 A의 목록을 조회함. 이때, 지연 로딩으로 설정했으므로 B 목록은 사용하는 시점에서 가져오기 위해 가짜(프록시) 객체로 가지고 있음. + ⇒ 쿼리 1회 발생 +2. B목록을 확인하기 위해 for문을 이용하여 엔티티 A에 있는 B목록을 확인함. 이때, 엔티티 A의 B 목록은 프록시 객체이기 때문에 JPA는 1차 캐시 저장소에 현재 엔티티 A의 B목록을 확인하고, 없기 때문에 쿼리(select * from B where A_id=엔티티 A의 id)를 통해 데이터를 가져옴. + ⇒ 쿼리 N회 발생 + +⇒ N+1 문제가 발생함 + +**해결 방법: Fetch join** + +> ***Fetch join** +연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 방법 +사용방법: 쿼리문에 직접 fetch를 명시하기 또는 @EntityGraph 사용* +> + +1. select A.*, B.* from A join fetch B 를 통해 조회하기 + ⇒ 지연로딩에서는 B의 목록이 프록시 객체로 가져와졌지만, fetch join에서는 진짜 B 객체를 가지고 옴. + ⇒ 쿼리 1회 발생 +2. 반복문을 수행할 때, B목록이 실제 객체이므로 DB를 거치지 않고 데이터를 꺼내서 반환함. + + ⇒ 쿼리 0회 발생 + + +따라서, 1개의 쿼리로 문제를 해결할 수 있음 + +**fetch 전략이 즉시 로딩(EAGER)일 때** + +> ***즉시 로딩** +데이터를 조회할 때 연관된 모든 객체의 데이터까지 한 번에 불러오는 방식* +> + +**JPQL이 즉시 로딩 쿼리를 생성할 때 특징** + +- JPQL은 처음 쿼리를 만들 때 크루에 연관관계가 있는 Entity는 신경쓰지 않고, 조회 대상이 되는 Entity 기준으로만 쿼리를 작성함 +- 이후 연관관계를 확인하여 추가적인 쿼리를 발생시킴 + +findAll을 통해 엔티티 A 목록을 조회하는데 A에는 연관된 엔티티 B의 목록이 있다고 하자. + +1. select * from A를 통해 A목록을 조회함. + ⇒ 쿼리 1회 발생 +2. A와 연관된 엔티티 B의 존재를 확인한 후, 글로벌 패치 전략을 확인함. 이때, 전략이 즉시로딩이므로 for문을 통해 select * from B where A_id=엔티티 id라는쿼리로 엔티티를 조회함. + ⇒ N번의 쿼리 발생 + +따라서, N+1 문제가 발생함. + +**해결방법 : 즉시로딩 사용을 지양하고, 지연 로딩 + fetch 조인 사용하기** + +## Fecth Join의 문제 + +위에서는 즉시 로딩과 지연 로딩에서 발생하는 해결방안으로 fetch join이 등장하지만, fetch join을 사용할 때 생기는 문제도 존재함. + +**대표적인 fetch join의 문제 : @OneToMany 관계의 정확한 페이징 처리가 불가능함** + +- DB에서 1 : N관계를 표현하려면 한 개의 Entitiy이지만, N개의 행으로 표현을 해야함. + ⇒ 5개의 엔티티를 불러오는 페이징 처리를 원했는데 5개의 행을 불러오는 페이징 처리가 됨. +- 문제 해결을 위해 fetch join + 페이징 처리를 하면 JPA는 fetch join한 데이터를 전부 가져온 뒤, 인메모리에 넣고 가공함. 이때, Limit 처리가 되지 않음. + ⇒ 데이터가 많아지면 메모리 부하가 발생함 + +**해결방법: @ManyToOne일 때만 fetch join 사용하기 또는 @BatchSize() 사용하기** + +> *@BatchSize +여러 개의 Entity를 조회할 때 지정된 Size 만큼 Where 절이 같은 여러 개의 SELECT 쿼리들을 하나의 IN 쿼리로 만들어 줌* +> + +N+1문제가 발생하지 않는 것은 아니지만, select * from B where A_id=? 쿼리를 select * from B where A_id in (?,?,?) 방식으로 N+1문제가 발생하게 하여, 성능을 최적화 할 수 있음. \ No newline at end of file From a3d8c300b31696394a4c15b2cf5dd50b7f12fd1f Mon Sep 17 00:00:00 2001 From: InSooBeen Date: Fri, 8 Nov 2024 03:22:06 +0900 Subject: [PATCH 2/2] keyword: submit week6 keyword.md --- keyword/chapter06/keyword.md | 213 +++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 keyword/chapter06/keyword.md diff --git a/keyword/chapter06/keyword.md b/keyword/chapter06/keyword.md new file mode 100644 index 0000000..5236232 --- /dev/null +++ b/keyword/chapter06/keyword.md @@ -0,0 +1,213 @@ +# 6주차 핵심 키워드🎯 + +## Fetch Type + +- JPA가 하나의 Entity를 조회할 때, 연관관계에 있는 객체들을 어떻게 가져올 것인지를 나타내는 설정값 +- JPA는 사용자가 직접 쿼리를 생성하는 것이 아닌, JPA에서 JPQL을 이용하여 쿼리문을 생성함 + ⇒ 객체와 필드를 보고 쿼리를 생성 +- 코드에 다른 객체와 연관관계 매핑이 되어있다면 그 객체들까지 조회하며, 객체를 불러오는 방식을 설정할 수 있는데 이를 Fetch Type이라고 함. +- default 값: `@xxToOne`에서는 EAGER, `@xxToMany`에서는 LAZY + +## 지연로딩 + +> ***지연로딩** +객체를 조회하는 시점에서 실제 객체가 아닌, 가짜(프록시) 객체를 가져온 후 , 객체를 사용하는 시점에 실제 객체를 가져오도록 하는 방식* +> + +- `@xxToxx(Fetch = fetchType.LAZY)` 의 형식으로 사용함 + +```java +@Entity +public class User { + @Id + @GeneratedValue + private long id; + private String firstName; + private String lastName; + + @ManyToOne(fetch = FetchType.EAGER) // 지연 로딩 + @JoinColumn(name = "team_id", nullable = false) + private Team team; +} + +@Entity +public class Team { + @Id + @GeneratedValue + private long id; + private String name; + + @OneToMany(fetch = FetchType.EAGER) + private List users = new ArrayList<>(); +} +``` + +- 지연 로딩을 사용하면 User를 조회하는 시점이 아닌 Team을 사용하는 시점에 쿼리가 발생하게 할 수 있음. (단, Team을 사용하는 시점에는 추가적인 N번의 쿼리가 발생함) + +**지연로딩의 장점** + +- 지연 로딩은 필요한 시점에만 연관된 데이터를 로딩하므로 성, 메모리 사용의 최적화가 가능함(반면, 즉시 로딩은 필요하지 않은 데이터까지 가져올 수 있음) +- 데이터를 사용하는 시점에만 로딩하므로, 데이터베이스 접근이 최적화 됨 +- 객체를 조회할 때 필요한 데이터만 로딩되므로 무한한 순환 참조를 방지할 수 있음 + +## 즉시로딩 + +> ***Fetch join** +연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 방법 +사용방법: 쿼리문에 직접 fetch를 명시하기 또는 @EntityGraph 사용* +> +- `@xxToxx(Fetch = fetchType.EAGER)` 의 형식으로 사용함 + +```java +@Entity +public class User { + @Id + @GeneratedValue + private long id; + private String firstName; + private String lastName; + + @ManyToOne(fetch = FetchType.LAZY) // 즉시 로딩 + @JoinColumn(name = "team_id", nullable = false) + private Team team; +} + +@Entity +public class Team { + @Id + @GeneratedValue + private long id; + private String name; + + @OneToMany(fetch = FetchType.LAZY) + private List users = new ArrayList<>(); +} +``` + +즉시 로딩을 사용하면 User를 조회하는 시점에 Team을 조회하는 쿼리까지 발생하여 연관된 데이터를 한 번에 불러옴. + +**즉시로딩의 장점** + +- 즉시로딩을 사용하면 연관된 객체까지 한 번에 조회되므로 객체 간의 관계를 편리하게 활용할 수 있음. +- 데이터베이스에서 조인을 사용하여 복잡한 연관관계를 해결하지 않고, 모든 데이터를 한 번에 가져올 수 있음. + + +## Fetch Join + +> ***Fetch Join** +SQL의 조인이 아닌, JPQL에서 성능 최적화를 위해 제공하는 기능으로, Fetch Join을 사용하면 연관된 Entity나 컬렉션을 한번에 조회할 수 있음.* +> + +**사용하는 이유** + +- 글로벌 로딩 전략을 지연로딩(LAZY)로 설정한 경우 Fetch join을 이용하면 성능을 향상시킬 수 있음 + +## Join VS Fetch Join + +**일반 Join** + +- 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT 하여 영속화하는 Entity는 오직 JPQL에서 조회하여 주체가 되는 Entity임. + +Fetch Join + +- 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT하여 영속화 함. + +## Fetch Join의 한계점 + +- fetch join 대상에는 별칭을 줄 수 없음. + ⇒ JPA 표준에서는 별칭을 지원하지 않고 hibernate를 포함한 몇몇의 구현체들은 별칭을 지원하긴 하나, 별칭을 잘못 사용하면 연관된 데이터의 수가 달라져 데이터 무결성이 깨질 수 있으므로 사용하지 않는 것이 좋음. +- 둘 이상의 컬렉션을 fetch 할 수 없음. +- 일대다 관계에서 컬렉션을 fetch Join하면 페이징 API를 사용할 수 없음 + + +## @EntityGraph + +> ***@EntityGraph** +JPA에서 fetch join을 해당 어노테이션을 통해 사용할 수 있도록 만든 기능* +> + +**@EntityGraph의 2가지 타입** + +- FETCH: @EntityGraph에 명시한 attribute는 EAGER로 fetch 하며, 나머지는 LAZY로 fetch함 + (default) +- LOAD: @EntityGraph에 명시한 attribute는 EAGER로 fetch하며, 나머지는 Entity에 명시한 fetch type을 따름 + +## @EntityGraph vs Fetch Join + +- @EntityGraph는 fetch type을 eager로 변환하는 방식이며, outer left join을 사용하여 데이터를 가져옴. + ⇒조회하는 엔티티에 해당되지 않는 연관 엔티티까지 조회가 됨 +- fetch join은 따로 outer join을 명시하지 않으면 inner join을 수행하여 데이터를 가져옴. + ⇒ 조회하는 엔티티에 해당되는 연관 엔티티만 조회가 됨 + +**사용 방법** + +**`@EntityGraph(attributePaths = {"fetch join 할 객체의 필드명"}`** 의 형식으로 사용함 + +**사용 시 주의사항** + +- Fetch Depth를 설정하여 연관된 엔티티 객체를 가져오는 깊이를 제한하여 무한 재귀 호출을 방지하는 것이 좋음 +- 즉시로딩을 사용하는 것이기 때문에, 성능에 영향을 주지 않으려면 필요한 경우에만 사용해야 함 + + +## JPQL + +> ***JPQL** +JPA는 SQL을 추상화 한 JPQL이라는 객체 지향 쿼리 언어를 제공함. +⇒ SQL과 달리 테이블이 아닌 엔티티 객체를 대상으로 쿼리를 만듦 +⇒ 데이터 베이스 구조와 독립적* +> +- **객체 지향적** : 엔티티 클래스와 속성을 기반으로 쿼리를 작성하기 때문에 객체 지향 프로그래밍에 부합하고, 엔티티 간의 관계를 쉽게 활용할 수 있음 +- **데이터베이스 독립적:** 특정 데이터베이스 구문에 의존하지 않으므로 다양한 데이터 베이스 시스템에서 사용 가능 +- **성능** : 때때로 Native SQL에 비해 최적화가 덜 되어있어 성능이 떨어질 수 있음 +- **하위 쿼리 제한적 :** From 절의 하위 쿼리 사용이 제한적임 + +**JPA문법 대신 JPQL을 사용하는 이유는?** + +- JPA 문법은 CRUD 명령어 처리에서 효율적인 명령어 기능을 제공하지만, 검색 명령어에 대해 비효율적임 +- JPA는 객체 중심이므로 검색을 할 때에도 엔티티 객체를 대상으로 하는데, DB의 모든 데이터를 엔티티로 변경하여 처리하는 것이 불가능함 + +⇒ 특수한 경우에 대해 처리할 수 있도록 만든 쿼리가 JPQL임 + + **** + +**JPQL의 문법** + +`select u from User as u where a.age > 18` ⇒ 기본적으로 SQL과 유사한 구문을 사용함 + +- 엔티티 클래스 이름, 엔티티 필드의 대소문자가 일치해야 함 (대문자, 소문자 구분 필요) +- JPQL의 키워드는 대소문자를 구분하지 않음 +- 별칭을 붙이는 것이 필수적임 +- SQL을 추상화한 것이므로 특정 SQL에 의존하지 않음 +- JPQL은 최종적으로 SQL로 변환되어 데이터베이스에 적용 + +**JPQL의 장점** + +- JPA와 통합되어 있기 때문에, 일관된 API를 제공함 +- 객체 지향 패러다임에 맞춰 쿼리를 작성할 수 있음. + +**JPQL의 단점** + +- 기본 문자열로 작성되기 때문에 컴파일시 에러가 발생하지 않으며, 런타임에 문제가 발생함 + ⇒ JPQL이 길어지면 문자열 사이의 에러를 찾기 힘들어짐. +- 정적 쿼리에 적합하며, 복잡한 동적 쿼리 작성에 한계가 있음. +- 엔티티 객체에 의존적이므로 객체 수정에 따른 JPQL의 수정이 필수적임. + + +## QueryDSL + +> *QueryDSL +JPQL을 자바 코드로 작성할 수 있도록 하는 라이브러리* +> +- 정적 타입을 이용해 SQL과 같은 쿼리를 생성할 수 있도록 함. +- 쿼리를 문자열로 작성하는 것이 아닌, QueryDSL이 제공하는 Fluent API를 이용해 쿼리를 생성할 수 있도록 함. + +**QueryDSL을 사용하는 이유?** + +- 기존 JPQL은 문자열로 쿼리를 작성했기 때문에, 컴파일 시점에 에러가 발생하지 않음. + ⇒QueryDSL을 통해 자바 코드로 쿼리를 작성하게 함으로써, 컴파일 시점에 에러를 잡을 수 있게 되었음 +- JPQL을 이용해 동적 쿼리를 다루기 위해서는 문자열을 조건에 맞게 조합해서 사용해야 했기 때문에, 코드가 복잡해지고 런타임 에러 발생 확률이 높았음 + ⇒ QueryDSL을 통해 복잡한 동적 쿼리를 Q클래스, 메서드를 활용하여 쉽게 다룰 수 있음, 가독성도 향상 + +**QueryDSL의 단점** + +- 초기 설정이 복잡하며, JPA와의 통합을 위해 추가적인 설정과 코드기 필요함 \ No newline at end of file