JPA

JPA์—์„œ ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์„ ์šฐ์„ ์œผ๋กœ ํ•˜๋Š” ์ด์œ  (๋‹จ๋ฐฉํ–ฅ → ํ•„์š”์‹œ ์–‘๋ฐฉํ–ฅ)

devJK93 2024. 12. 9.

๐Ÿ“ JPA ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘๊ณผ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘

์™œ ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์„ ์šฐ์„ ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

  1. ๋‹จ์ˆœ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ
    • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์€ ์ฝ”๋“œ๋ฅผ ๋‹จ์ˆœํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
    • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ๊ฐ์ฒด ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์–‘์ชฝ์—์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  2. ์„ฑ๋Šฅ
    • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์€ ํ•„์š” ์—†๋Š” ์ถ”๊ฐ€ ์ฟผ๋ฆฌ ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
    • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ์ž˜๋ชป ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ SELECT๋‚˜ UPDATE ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์—ฐ๊ด€๊ด€๊ณ„์˜ ๋ณต์žก์„ฑ
    • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ๋‘ ๊ฐ์ฒด ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์–‘์ชฝ์—์„œ ๋™๊ธฐํ™”ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ž˜๋ชป ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ๋ˆ„๋ฝํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘๊ณผ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์˜ ์ฐจ์ด

1. ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘ (์ถ”์ฒœ)

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team; // Member → Team ๋‹จ๋ฐฉํ–ฅ
}
  • Member ์—”ํ‹ฐํ‹ฐ์—์„œ๋งŒ Team์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ„๋‹จํ•˜๊ณ , ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.
  • Team์—์„œ Member๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š์•„๋„ ๋Œ€๋ถ€๋ถ„์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

2. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team; // Member → Team
}

@Entity
public class Team {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>(); // Team → Member
}
  • ์–‘์ชฝ์—์„œ ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • Member์™€ Team ๊ฐ„์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋™๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ž˜๋ชป ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์–‘๋ฐฉํ–ฅ ๋งคํ•‘์˜ ๋ฌธ์ œ์  ์˜ˆ์‹œ

1. ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ถ”๊ฐ€ ์ฟผ๋ฆฌ ๋ฐœ์ƒ

Team team = em.find(Team.class, 1L);
List<Member> members = team.getMembers();
for (Member member : members) {
    System.out.println(member.getName());
}
  • ๋ฌธ์ œ: team.getMembers()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, JPA๋Š” ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ Member๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์ด ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ์—๋„ ๋ถˆํ•„์š”ํ•œ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ ๋ˆ„๋ฝ์œผ๋กœ ์ธํ•œ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜

Team team = new Team();
team.setName("teamA");

Member member = new Member();
member.setName("member1");
member.setTeam(team); // Team์— ์ถ”๊ฐ€๋˜์ง€ ์•Š์Œ

em.persist(team);
em.persist(member);
  • ๋ฌธ์ œ: team.getMembers()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด member๊ฐ€ ๋ฆฌ์ŠคํŠธ์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์—์„œ๋Š” ๋ฐ˜๋“œ์‹œ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ(team.addMember(member))๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์œผ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ

1. ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์œผ๋กœ ์ถฉ๋ถ„ํ•œ ์˜ˆ

Member member = new Member();
member.setName("member1");
member.setTeam(team); // ๋‹จ๋ฐฉํ–ฅ ์„ค์ •
em.persist(member);
  • ๊ฐ„๋‹จํ•œ ๋งคํ•‘ ๊ตฌ์กฐ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ช…ํ™•ํ•ด์ง.
  • ๋ถˆํ•„์š”ํ•œ ์ฟผ๋ฆฌ์™€ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€.

2. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ

Team team = em.find(Team.class, 1L);
List<Member> members = team.getMembers(); // ํ•„์š”ํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ

๊ฒฐ๋ก 

  • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์€ ์„ค๊ณ„์™€ ์œ ์ง€๋ณด์ˆ˜์˜ ๋ณต์žก์„ฑ์„ ์ค„์ด๊ณ , ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•„์š”ํ•  ๋•Œ๋งŒ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜๊ณ , ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์œ ์ง€๋ณด์ˆ˜์™€ ์„ฑ๋Šฅ์— ์•…์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ–ˆ๋‹ค๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ๋ถ€์ž‘์šฉ

1. ๋ฌดํ•œ ๋ฃจํ”„ ๋ฌธ์ œ

์›์ธ:

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์—์„œ toString(), hashCode(), ๋˜๋Š” equals() ๋ฉ”์„œ๋“œ์—์„œ ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

@Override
public String toString() {
    return "Member{" +
            "id=" + id +
            ", team=" + team + // team.toString() ํ˜ธ์ถœ
            '}';
}
    

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ toString()์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Jackson, Lombok ๋“ฑ)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ @JsonIgnore ๋˜๋Š” @ToString.Exclude๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

2. ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ

์›์ธ:

์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๊ณผ ๋น„์ฃผ์ธ์„ ์ œ๋Œ€๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

Member member = new Member();
Team team = new Team();

member.setTeam(team); // ์ฃผ์ธ ์„ค์ •
team.getMembers().add(member); // ๋น„์ฃผ์ธ ์„ค์ •

em.persist(member);
em.persist(team);
    

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๋งŒ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ๋•Œ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
public void addMember(Member member) {
    this.members.add(member);
    member.setTeam(this); // ์ฃผ์ธ ํ•„๋“œ ์„ค์ •
}
    

3. N+1 ๋ฌธ์ œ

์›์ธ:

์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์ฆ‰์‹œ ๋กœ๋”ฉ(FetchType.EAGER)์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์„ ๋•Œ, ํ•œ ์ฟผ๋ฆฌ์—์„œ ๋‹ค์ˆ˜์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋ฉด ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€์ ์ธ ์ฟผ๋ฆฌ(N๊ฐœ)๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

List teams = em.createQuery("SELECT t FROM Team t", Team.class).getResultList();
for (Team team : teams) {
    System.out.println(team.getMembers().size()); // ๊ฐ ํŒ€์˜ ๋ฉค๋ฒ„ ์กฐํšŒ
}
    

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)์œผ๋กœ ๋ณ€๊ฒฝ:
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private List members;
    
  • ๋˜๋Š” ํŽ˜์น˜ ์กฐ์ธ(Fetch Join)์„ ์‚ฌ์šฉ:
List teams = em.createQuery(
    "SELECT t FROM Team t JOIN FETCH t.members", Team.class).getResultList();
    

4. ์„ค๊ณ„ ๋ณต์žก๋„ ์ฆ๊ฐ€

์›์ธ:

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋Š” ๋‹จ๋ฐฉํ–ฅ๋ณด๋‹ค ์„ค๊ณ„์™€ ๊ตฌํ˜„์ด ๋ณต์žกํ•˜๋ฉฐ, ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์–‘๋ฐฉํ–ฅ์ด ๊ผญ ํ•„์š”ํ•œ์ง€ ๊ฒ€ํ† :
  • ํ•œ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ํƒ์ƒ‰ํ•ด๋„ ์ถฉ๋ถ„ํ•˜๋‹ค๋ฉด ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋กœ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค.

5. ์„ฑ๋Šฅ ๋ฌธ์ œ

์›์ธ:

์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋กœ ์ธํ•ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋กœ๋”ฉํ•˜๊ฑฐ๋‚˜ ์กฐ์ธ ์ฟผ๋ฆฌ๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • JPQL๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์กฐํšŒํ•˜๊ฑฐ๋‚˜, ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ์ค„์—ฌ์„œ ๋ณต์žก์„ฑ์„ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค.
  • ๋ณต์žกํ•œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ๊ฒฝ์šฐ DTO๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

6. ์‚ญ์ œ ์ œ์•ฝ ๋ฌธ์ œ (Cascade ๊ด€๋ จ)

์›์ธ:

์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ ์‚ญ์ œ ์‹œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์‚ญ์ œํ•˜๋ ค๋ฉด CascadeType.ALL์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์˜๋„์น˜ ์•Š๊ฒŒ ๊ด€๋ จ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์‚ญ์ œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

team.getMembers().remove(member);
    

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • CascadeType์€ ์‹ ์ค‘ํžˆ ์‚ฌ์šฉํ•˜๊ณ , ์‚ญ์ œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

7. ํ”„๋ก์‹œ ์ดˆ๊ธฐํ™” ๋ฌธ์ œ

์›์ธ:

์ง€์—ฐ ๋กœ๋”ฉ(FetchType.LAZY)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์ „์— ์„ธ์…˜์ด ์ข…๋ฃŒ๋˜๋ฉด LazyInitializationException์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

@Transactional
public void someMethod() {
    Team team = em.find(Team.class, 1L);
    em.close(); // ์„ธ์…˜ ์ข…๋ฃŒ
    team.getMembers(); // LazyInitializationException ๋ฐœ์ƒ
}
    

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ํŽ˜์น˜ ์กฐ์ธ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•  ์‹œ์ ์— ์„ธ์…˜์ด ์—ด๋ ค ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ •๋ฆฌ

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์š” ๋ถ€์ž‘์šฉ๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

๋ฌธ์ œ์  ์›์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
๋ฌดํ•œ ๋ฃจํ”„ toString(), equals(), hashCode() ์‚ฌ์šฉ ์—ฐ๊ด€ ๊ฐ์ฒด ์ œ์™ธ, @JsonIgnore ๋“ฑ ์ ์šฉ
๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์„ ์ œ๋Œ€๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์Œ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ๋งŒ ์ˆ˜์ •, ํŽธ์˜ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
N+1 ๋ฌธ์ œ ์ฆ‰์‹œ ๋กœ๋”ฉ ์‚ฌ์šฉ ์ง€์—ฐ ๋กœ๋”ฉ ์„ค์ •, JPQL ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉ
์„ค๊ณ„ ๋ณต์žก๋„ ์ฆ๊ฐ€ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๋‚จ๋ฐœ ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋กœ ๋Œ€์ฒด
์„ฑ๋Šฅ ๋ฌธ์ œ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ, ๋ณต์žกํ•œ ์กฐ์ธ JPQL ๋˜๋Š” DTO ์‚ฌ์šฉ
์‚ญ์ œ ์ œ์•ฝ ๋ฌธ์ œ ์ž˜๋ชป๋œ CascadeType ์„ค์ • Cascade ์„ค์ • ์ตœ์†Œํ™”
ํ”„๋ก์‹œ ์ดˆ๊ธฐํ™” ๋ฌธ์ œ ์ง€์—ฐ ๋กœ๋”ฉ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์‹œ ์„ธ์…˜ ์ข…๋ฃŒ ์„ธ์…˜ ์œ ์ง€, ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉ

๋Œ“๊ธ€