Home > Spring > πŸƒ[Spring] 지연 λ‘œλ”©(Lazy Loading)은 λ¬΄μ—‡μΈκ°€μš”?

πŸƒ[Spring] 지연 λ‘œλ”©(Lazy Loading)은 λ¬΄μ—‡μΈκ°€μš”?
Spring Framework

πŸƒ[Spring] 지연 λ‘œλ”©(Lazy Loading)은 λ¬΄μ—‡μΈκ°€μš”?

  • JPAμ—μ„œ μ‹€μ œ 데이터가 ν•„μš”ν•œ μ‹œμ κΉŒμ§€ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회λ₯Ό μ§€μ—°ν•˜λŠ” κΈ°λ²•μž…λ‹ˆλ‹€.
  • μ—”ν‹°ν‹°λ₯Ό 처음 μ‘°νšŒν•  λ•ŒλŠ” μ—°κ΄€λœ 데이터λ₯Ό μ¦‰μ‹œ λ‘œλ“œν•˜μ§€ μ•Šκ³ , κ·Έ μ—°κ΄€λœ 데이터가 μ‹€μ œλ‘œ μ‚¬μš©λ  λ•Œ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‘°νšŒν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.
    • 이 방식은 λΆˆν•„μš”ν•œ 데이터 쑰회λ₯Ό μ€„μ—¬μ„œ μ„±λŠ₯을 μ΅œμ ν™”ν•˜λŠ” 데 μœ λ¦¬ν•©λ‹ˆλ‹€.

1️⃣ 지연 λ‘œλ”©(Lazy Loading)의 κΈ°λ³Έ λ™μž‘.

  • 지연 λ‘œλ”©(Lazy Loading)을 μ„€μ •ν•˜λ©΄ μ—°κ΄€λœ μ—”ν‹°ν‹°λ‚˜ μ»¬λ ‰μ…˜μ€ μ²˜μŒμ— ν”„λ‘μ‹œ 객체둜 λ‘œλ“œλ©λ‹ˆλ‹€.
    • ν”„λ‘μ‹œλŠ” μ‹€μ œ μ—”ν‹°ν‹°λ₯Ό λŒ€μ‹ ν•˜λŠ” 객체둜, λ°μ΄ν„°λ² μ΄μŠ€ μ‘°νšŒκ°€ ν•„μš”ν•  λ•Œ ν”„λ‘μ‹œκ°€ μ‹€μ œ 데이터λ₯Ό μ‘°νšŒν•˜μ—¬ 값을 μ œκ³΅ν•©λ‹ˆλ‹€.
      • λ”°λΌμ„œ μ²˜μŒλΆ€ν„° μ—°κ΄€λœ 데이터λ₯Ό λͺ¨λ‘ λ‘œλ“œν•˜λŠ” 것이 μ•„λ‹ˆλΌ μ‹€μ œ μ ‘κ·Ό μ‹œμ μ— λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ λ‘œλ“œλ˜λ„λ‘ μ§€μ—°λ©λ‹ˆλ‹€.

πŸ™‹β€β™‚οΈ JPAμ—μ„œμ˜ β€œν”„λ‘μ‹œ(Proxy)”

ν”„λ‘μ‹œ(Proxy)λŠ” JPAμ—μ„œ μ‹€μ œ μ—”ν‹°ν‹°λ₯Ό λŒ€μ‹ ν•˜μ—¬ μƒμ„±λ˜λŠ” κ°€μ§œ κ°μ²΄μž…λ‹ˆλ‹€.
이 ν”„λ‘μ‹œλŠ” μ—°κ΄€λœ μ—”ν‹°ν‹°λ₯Ό 지연 λ‘œλ”©ν•  λ•Œ μ‚¬μš©λ˜λ©°, μ‹€μ œ 데이터가 ν•„μš”ν•œ μ‹œμ κΉŒμ§€ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회λ₯Ό μ§€μ—°ν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€.
ν”„λ‘μ‹œ(Proxy)λŠ” μ‹€μ œ 엔티티와 λ™μΌν•œ μΈν„°νŽ˜μ΄μŠ€λ‚˜ 클래슀λ₯Ό 상속받아 λ§Œλ“€μ–΄μ§„ 객체둜, μ‹€μ œ 데이터가 ν•„μš”ν•œ μ‹œμ μ— λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터λ₯Ό λ‘œλ“œν•˜μ—¬ 값을 μ œκ³΅ν•©λ‹ˆλ‹€.

2️⃣ μ˜ˆμ‹œ: @OneToMany 지연 λ‘œλ”©(Lazy Loading) μ„€μ •.

  • 예λ₯Ό λ“€μ–΄, Team μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬ Member 엔티티와 μ—°κ΄€λ˜μ–΄ μžˆμ„ λ•Œ @OneToMany κ΄€κ³„μ˜ 지연 λ‘œλ”©(Lazy Loading)을 μ„€μ •ν•΄λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team", fetch = FetchType.LAZY) // 지연 λ‘œλ”© μ„€μ •
    private List<Member> members = new ArrayList<>();
    
    // getter, setter
}

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
    // getter, setter
}

3️⃣ μ„€λͺ….

  • Team μ—”ν‹°ν‹°μ˜ members ν•„λ“œμ— fetch = FetchType.LAZY μ˜΅μ…˜μ„ μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€.
  • Team μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•  λ•Œ members λ¦¬μŠ€νŠΈλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ¦‰μ‹œ λ‘œλ“œλ˜μ§€ μ•ŠμœΌλ©°, members μ»¬λ ‰μ…˜μ— μ‹€μ œ μ ‘κ·Όν•˜λŠ” μˆœκ°„ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‘°νšŒκ°€ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€.

4️⃣ Lazy Loading의 μž₯점.

1️⃣ μ„±λŠ₯ μ΅œμ ν™”.

  • ν•„μš”ν•œ λ°μ΄ν„°λ§Œ μ‘°νšŒν•˜μ—¬ λΆˆν•„μš”ν•œ 쿼리 싀행을 λ°©μ§€ν•˜λ―€λ‘œ μ„±λŠ₯이 μ΅œμ ν™”λ©λ‹ˆλ‹€.

2️⃣ λ©”λͺ¨λ¦¬ νš¨μœ¨μ„±.

  • ν•„μš”ν•œ μˆœκ°„κΉŒμ§€ λ©”λͺ¨λ¦¬μ— 데이터λ₯Ό λ‘œλ“œν•˜μ§€ μ•ŠμœΌλ―€λ‘œ λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

3️⃣ 큰 μ—°κ΄€ 관계 관리.

  • μ—°κ΄€λœ 데이터가 λ§Žμ€ 경우, 초기 λ‘œλ”© μ‹œ μ„±λŠ₯ μ €ν•˜λ₯Ό λ°©μ§€ν•˜κ³  ν•„μš”ν•œ μ‹œμ μ— ν•„μš”ν•œ λ°μ΄ν„°λ§Œ 뢈러올 수 μžˆμŠ΅λ‹ˆλ‹€.

5️⃣ Eager Loading과의 비ꡐ.

  • λ°˜λŒ€λ‘œ μ¦‰μ‹œ λ‘œλ”©(Eager Loading)은 μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•  λ•Œ μ—°κ΄€λœ λͺ¨λ“  데이터λ₯Ό ν•œ λ²ˆμ— ν•¨κ»˜ λ‘œλ“œν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.
  • μ¦‰μ‹œ λ‘œλ”©(Eager Loading)은 fetch = FetchType.EAGER μ˜΅μ…˜μ„ 톡해 μ„€μ •ν•  수 있으며, μ²˜μŒλΆ€ν„° μ—°κ΄€λœ λͺ¨λ“  데이터λ₯Ό λ‘œλ”©ν•˜κΈ° λ•Œλ¬Έμ— 쿼리가 많이 μ‹€ν–‰λ˜κ±°λ‚˜, λ§Žμ€ 데이터λ₯Ό λΆˆν•„μš”ν•˜κ²Œ λ‘œλ“œν•˜λŠ” N+1 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

6️⃣ Lazy Loading μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­.

1️⃣ ν”„λ‘μ‹œ μ΄ˆκΈ°ν™” 문제.

  • 지연 λ‘œλ”©λœ ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜ λ²”μœ„λ₯Ό λ²—μ–΄λ‚œ ν›„μ—λŠ” μ΄ˆκΈ°ν™”ν•  수 μ—†κΈ° λ•Œλ¬Έμ—, μ—°κ΄€ 데이터λ₯Ό μ ‘κ·Όν•˜λ €λ©΄ νŠΈλžœμž­μ…˜ λ‚΄μ—μ„œ μ ‘κ·Όν•΄μ•Ό ν•©λ‹ˆλ‹€.

2️⃣ N+1 문제.

  • 지연 λ‘œλ”©μ„ 잘λͺ» μ‚¬μš©ν•˜λ©΄ N+1 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 예λ₯Ό λ“€μ–΄, 리슀트 ν˜•νƒœμ˜ μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•  λ•Œ 각 μ—”ν‹°ν‹°μ˜ μ—°κ΄€ 데이터에 λŒ€ν•΄ μΆ”κ°€ 쿼리가 λ°œμƒν•˜λŠ” κ²½μš°μž…λ‹ˆλ‹€.
      • 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ 페치 쑰인(Fetch Join)을 μ‚¬μš©ν•˜μ—¬ ν•„μš”ν•œ 데이터λ₯Ό ν•œ λ²ˆμ— κ°€μ Έμ˜€λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

3️⃣ JPA ν‘œμ€€ μ œμ•½.

  • @OneToMany 및 @ManyToManyλŠ” 기본적으둜 LAZY둜 μ„€μ •λ˜μ§€λ§Œ, @ManyToOne 및 @OneToOne κ΄€κ³„λŠ” 기본이 EAGER둜 μ„€μ •λ©λ‹ˆλ‹€.
    • λ”°λΌμ„œ ν•„μš”μ— 따라 λͺ…μ‹œμ μœΌλ‘œ fetch = FetchType.LAZY둜 μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

7️⃣ μš”μ•½.

  • 지연 λ‘œλ”©(Lazy Loading)은 μ—°κ΄€λœ 데이터λ₯Ό μ‹€μ œ μ‚¬μš© μ‹œμ κΉŒμ§€ μ§€μ—°ν•˜μ—¬ μ‘°νšŒν•¨μœΌλ‘œμ¨ μ„±λŠ₯을 μ΅œμ ν™”ν•˜λŠ” κΈ°λ²•μž…λ‹ˆλ‹€.
    • 이λ₯Ό 톡해 λΆˆν•„μš”ν•œ 데이터 쑰회λ₯Ό λ°©μ§€ν•˜κ³  λ©”λͺ¨λ¦¬ νš¨μœ¨μ„±μ„ 높일 수 μžˆμ§€λ§Œ, μ‚¬μš© μ‹œ μ£Όμ˜ν•  점을 κ³ λ €ν•˜μ—¬ 졜적의 λ°©μ‹μœΌλ‘œ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.