Home > Backend Development > πŸ“š[Backend Development] Domain Layer, JPA Entity μƒμ„±μ‹œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•  μ€‘μš” 원칙듀

πŸ“š[Backend Development] Domain Layer, JPA Entity μƒμ„±μ‹œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•  μ€‘μš” 원칙듀
Backend Ddevelopment Layer Architecture Domain Layer JPA Spring Framework

πŸ“š[Backend Development] Domain Layer, JPA Entity μƒμ„±μ‹œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•  μ€‘μš” 원칙듀

Java Spring ν”„λ ˆμž„μ›Œν¬λ‘œ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•  λ•Œ Domain Layer, 특히 JPA Entityλ₯Ό 생성할 λ•Œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•  μ€‘μš”ν•œ 원칙듀이 μžˆμŠ΅λ‹ˆλ‹€.

이 원칙듀은 μ½”λ“œμ˜ μ•ˆμ •μ„±, μœ μ§€λ³΄μˆ˜μ„±, 그리고 μ˜ˆμƒμΉ˜ λͺ»ν•œ 버그λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ κΌ­ ν•„μš”ν•©λ‹ˆλ‹€.

βœ… 1. κΈ°λ³Έ μƒμ„±μž(No-Arg Constructor)λ₯Ό λ°˜λ“œμ‹œ μ œκ³΅ν•˜μ„Έμš”.

JPAλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‘°νšŒν•œ λ°μ΄ν„°λ‘œ 객체λ₯Ό 생성할 λ•Œ, λ¨Όμ € 빈 객체λ₯Ό λ§Œλ“  ν›„ 각 ν•„λ“œμ— 값을 μ±„μ›Œ λ„£μŠ΅λ‹ˆλ‹€.
μ΄λ•Œ 빈 객체λ₯Ό λ§Œλ“€κΈ° μœ„ν•΄ νŒŒλΌλ―Έν„°κ°€ μ—†λŠ” κΈ°λ³Έ μƒμ„±μžκ°€ λ°˜λ“œμ‹œ ν•„μš”ν•©λ‹ˆλ‹€.

  • 방법 : Lombok의 @NoArgsConstructor(access = AccessLevel.PROTECTED)λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 κ°€μž₯ μ’‹μŠ΅λ‹ˆλ‹€.
  • 이유 : protexted둜 접근을 μ œν•œν•˜λ©΄, κ°œλ°œμžκ°€ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ new Product() 처럼 λΆˆμ™„μ „ν•œ 객체λ₯Ό μ‹€μˆ˜λ‘œ μƒμ„±ν•˜λŠ” 것을 막아 μ•ˆμ •μ„±μ„ 높일 수 μžˆμŠ΅λ‹ˆλ‹€.

βœ… 2. Setter μ‚¬μš©μ„ μ§€μ–‘ν•˜κ³ , λΆˆλ³€μ„±(Immutability)을 μΆ”κ΅¬ν•˜μ„Έμš”.

Entity 객체에 λ¬΄λΆ„λ³„ν•œ setter λ©”μ„œλ“œλ₯Ό 열어두면, μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ—¬λŸ¬ κ³³μ—μ„œ 객체의 μƒνƒœκ°€ μ˜λ„μΉ˜ μ•Šκ²Œ 변경될 수 μžˆμ–΄ λ°μ΄ν„°μ˜ 일관성을 ν•΄μΉ˜κ³  버그λ₯Ό μœ λ°œν•˜κΈ° μ‰½μŠ΅λ‹ˆλ‹€.

  • 방법 :
    1. 객체 생성은 @Builderλ₯Ό 톡해 λͺ…ν™•ν•˜κ²Œ ν•©λ‹ˆλ‹€.
    2. setterλ₯Ό λ§Œλ“œλŠ” λŒ€μ‹ , μƒνƒœλ₯Ό λ³€κ²½ν•΄μ•Ό ν•  λ•ŒλŠ” κ·Έ μ˜λ„κ°€ λͺ…ν™•νžˆ λ“œλŸ¬λ‚˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ©”μ„œλ“œλ₯Ό λ§Œλ“œμ„Έμš”. (예: product.changePrice(newPrice)

βœ… 3. λͺ¨λ“  ν•„λ“œλ₯Ό ν¬ν•¨ν•˜λŠ” equals()와 hashCode()λ₯Ό ν”Όν•˜μ„Έμš”.

JPA Entity에 일반적인 Lombok의 @EqualsAndHashCodeλ₯Ό μ‚¬μš©ν•˜λ©΄, 연관관계 ν•„λ“œλ‘œ 인해 예기치 μ•Šμ€ λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
두 μ—”ν‹°ν‹°κ°€ μ„œλ‘œλ₯Ό μ°Έμ‘°ν•˜λŠ” 경우 λ¬΄ν•œ 루프에 빠질 수 μžˆμŠ΅λ‹ˆλ‹€.

  • 방법 : 객체의 κ³ μœ μ„±μ„ 보μž₯ν•˜λŠ” PK(@Id) ν•„λ“œλ§Œμ„ λΉ„κ΅ν•˜λ„λ‘ κ΅¬ν˜„ν•˜λŠ” 것이 κ°€μž₯ μ•ˆμ „ν•©λ‹ˆλ‹€.
  • Lombok μ‚¬μš© μ‹œ : @EqualsAndHashCode(of = "productId") 와 같이 of 속성을 μ‚¬μš©ν•˜μ—¬ PK ν•„λ“œλ§Œ λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•΄ μ£Όμ„Έμš”.

βœ… 4. toString() μ‚¬μš©μ— μ£Όμ˜ν•˜μ„Έμš”.

@ToString μ–΄λ…Έν…Œμ΄μ…˜μ„ 무심코 μ‚¬μš©ν•˜λ©΄, μ—°κ΄€λœ λͺ¨λ“  μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•˜λ €λŠ” 쿼리가 λ°œμƒν•˜μ—¬ μ„±λŠ₯ μ €ν•˜λ₯Ό μΌμœΌν‚€κ±°λ‚˜, μ–‘λ°©ν–₯ μ—°κ΄€κ΄€κ³„μ—μ„œ λ¬΄ν•œ 루프와 StackOverflowErrorλ₯Ό μœ λ°œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • 방법 : 연관관계λ₯Ό λ§Ίκ³  μžˆλŠ” ν•„λ“œλŠ” @ToString.Excludeλ₯Ό μ‚¬μš©ν•˜μ—¬ toString() κ²°κ³Όμ—μ„œ μ œμ™Έν•˜λŠ” 것이 μ•ˆμ „ν•©λ‹ˆλ‹€.

βœ… 5. Entityλ₯Ό API μš”μ²­(Request)/응닡(Response)에 직접 μ‚¬μš©ν•˜μ§€ λ§ˆμ„Έμš”.

EntityλŠ” λ°μ΄ν„°λ² μ΄μŠ€ ν…Œμ΄λΈ”κ³Ό 직접 μ—°κ²°λœ, μ‹œμŠ€ν…œμ˜ κ°€μž₯ 핡심적인 데이터 λͺ¨λΈμž…λ‹ˆλ‹€.
이λ₯Ό 외뢀에 κ·ΈλŒ€λ‘œ λ…ΈμΆœν•˜λ©΄ λ³΄μ•ˆμ— μ·¨μ•½ν•˜κ³ , λ‚΄λΆ€ 둜직의 변경이 API λͺ…세에 직접적인 영ν–₯을 주게 λ˜μ–΄ μœ μ—°μ„±μ΄ λ–¨μ–΄μ§‘λ‹ˆλ‹€.

  • 방법 : λ°˜λ“œμ‹œ DTO(Data Transfer Object) λ₯Ό μ‚¬μš©ν•˜μ—¬, API의 μš”μ²­κ³Ό 응닡 데이터λ₯Ό Entity와 λΆ„λ¦¬ν•˜μ„Έμš”. μ΄λŠ” μ§€κΈˆκΉŒμ§€ μš°λ¦¬κ°€ API λͺ…μ„Έμ„œλ₯Ό μ„€κ³„ν•˜λ©° μ§€μΌœμ˜¨ κ°€μž₯ μ€‘μš”ν•œ μ›μΉ™μž…λ‹ˆλ‹€.