๐ซ ์ผ์ ๊ด๋ฆฌ ์ฑ Develop API ๋ฅผ ๊ตฌํํ๋ฉฐ ๊ฒช์ ๋ฌธ์ ์ ๊ณผ ํด๊ฒฐ๋ฐฉ๋ฒ, ์๋ก ์๊ฒ๋ ์ ์ ๊ธฐ๋กํฉ๋๋ค.

๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค(RDB) ํ ์ด๋ธ์ ๋งคํํ ๋ ๋๋ฉ์ธ๋ค์ด ๊ณตํต์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ ํ๋ (์์ฑ์ผ, ์์ ์ผ) ๊ฐ์ ํ๋ ๋ฐ ์ปฌ๋ผ์ด ์กด์ฌ.
์ด๋ฌํ ๊ณตํต ํ๋๋ฅผ ์ค๋ณต์์ด ์ฌ์ฉํ๊ธฐ ์ํด JPA์์ Auditing์ ์ ๊ณตํ๋ค. Audit์ ๊ฐ์ํ๋ค, ๊ฐ์ฌํ๋ค ์ ๋ป์ผ๋ก Spring JPA์์ ์๊ฐ์ ๋ํ ๊ฐ์ ์๋์ผ๋ก ๋ฃ์ด์ค ๋ ์ ์ฉํ๋ค.
@EnableJpaAuditing
@SpringBootApplication
public class JpaScheduleApplication {
public static void main(String[] args) {
SpringApplication.run(JpaScheduleApplication.class, args);
}
}
@EnableJpaAuditing ์ด๋
ธํ
์ด์
์ ์ถ๊ฐํ์ฌ JPA Auditing ํ์ฑํ
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
| ์ด๋ ธํ ์ด์ | ์ค๋ช |
|---|---|
| @MappedSuperclass | JPA Entity ํด๋์ค๋ค์ด ํด๋น ์ถ์ ํด๋์ค๋ฅผ ์์ํ ๊ฒฝ์ฐ ์ถ์ํด๋์ค์ ํ๋๋ฅผ ์ปฌ๋ผ์ผ๋ก ์ธ์ |
| @EntityListeners(AuditingEntityListener.class) | ํด๋น ํด๋์ค์ Auditing ๊ธฐ๋ฅ์ ํฌํจ |
| @CreatedDate | Entity๊ฐ ์์ฑ๋์ด ์ ์ฅ๋ ๋ ์๊ฐ์ด ์๋ ์ ์ฅ |
| @LastModifiedDate | ์กฐํํ Entity์ ๊ฐ์ ๋ณ๊ฒฝํ ๋ ์๊ฐ์ด ์๋ ์ ์ฅ |
๐ ์ฐธ๊ณ
JPA Auditing ๊ธฐ๋ฅ์ด๋?
Entity ํด๋์ค์ ๊ธฐ๋ณธ ์์ฑ์๊ฐ ์์ ๊ฒฝ์ฐ, JpaSystemException์ด ๋ฐ์ํ๋ค.
repository์์ ํด๋น ์ํฐํฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ๊ธฐ๋ณธ ์์ฑ์๊ฐ ํ์ํ๋ค. JPA๊ฐ DB์์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ ๊ฐ์ฒด๋ก ๋งคํํ์ฌ ๋ฐํํ๋ ๋ก์ง์ Java Reflection ์ด ์ ์ฉ๋์๊ธฐ ๋๋ฌธ์ด๋ค.
JPA๊ฐ ๊ด๋ฆฌํ๋ @Entity๋ก ์ ์ธํ๋ฉด JPA๋ Java Reflection ์ด ์ ์ฉ๋ ์ ์๋์ง ์ฒดํฌํ์ฌ ์ปดํ์ผ ์๋ฌ๋ฅผ ํ์ํ๋ค.

๊ตฌ์ฒด์ ์ธ ํด๋์ค ํ์ ์ ์์ง ๋ชปํด๋ ๊ทธ ํด๋์ค ์ ๋ณด(๋ฉ์๋, ํ์ ๋ฑ)์ ์ ๊ทผํ ์ ์๋๋ก ํด์ฃผ๋ Java API ์ด๋ค.
์ผ๋ฐ์ ์ผ๋ก ์บก์ํ๊ฐ ์ ์ฉ๋ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ธฐ ์ํด์ new ํค์๋(์์ฑ์)๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, setter๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋๋ค.
reflection ์ ์ฌ์ฉํ๋ฉด private ์ ๊ทผ ์ ์ด์๋ฅผ ๊ฐ์ง ๋ฉ์๋์๋ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
static ์์ญ์ ์ ์ฅ๋์ด์๋ ํด๋์ค ์ ๋ณด์ ์ ๊ทผ
โ org.springframework.util.ReflectionUtils

โ Modifier(์ ์ด์)๊ฐ Public์ด ์๋๋ผ๋ฉด ์ธ์๋ก ๋ฐ์ Method๋ฅผ setAccessible(true) : ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ๋ง๋ ๋ค.
์ ๊ทผ ๊ฐ๋ฅ(public)ํ๋๋ก ๋ง๋ ๋ค์ ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ํตํด ์์ฑ๋ ๊ฐ์ฒด ํ๋, ๋ฉ์๋์ ์ง์ ์ ๊ทผํ์ฌ ๊ฐ์ ๋ณ๊ฒฝํ๋ค.
๐ซ Controller์์ @RequestBody์ ๋ฐ์ธ๋ฉํ์ฌ JSON ๋ฌธ์์ด์ ๊ฐ์ ๋ฐ์ ๋ setter ๋ฉ์๋๋ฅผ ๋ง๋ค์ง ์์๋ ๊ฐ์ฒด์ ์ ์ฅํ๋ ์ด์ ๋ Java Reflection์ด ๊ธฐ๋ณธ์์ฑ์๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์์ฑํ ํ ํ๋ ๊ฐ์ ๊ฐ์ ๋ก ๋งคํํด์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค.
์ด๋ฌํ ์ด์ ๋ก JPA๋ Java Reflection์ ์ฌ์ฉํ๊ธฐ ์ํด ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ๋ฐ๋์ ์์ฑํ๋๋ก ๊ฐ์ ํ๋ค.
๐ ์ฐธ๊ณ
JPA์ Entity๋ ๊ธฐ๋ณธ ์์ฑ์๊ฐ ์ ๋ฐ๋์ ํ์ํ ๊น?
Schedule ๊ณผ Member ์ฌ์ด์ @ManyToOne(N:1) ๊ด๊ณ๋ก ๋งคํ๋์ด ์์ ๊ฒฝ์ฐ,@ManyToOne ์ด๋
ธํ
์ด์
์ fetch ํ์
์ ์ค ์ ์๋ค.
@Getter
@Entity
@Table(name = "schedule")
public class Schedule extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
public Schedule() {
}
}

โ๏ธ ํ๋ก์ ๊ฐ์ฒด(Proxy)
JPA์ ์ค์ ์ํฐํฐ๋ฅผ ํ์ํ ๋๋ง ๊บผ๋ด์ธ ์ ์๋๋ก ํ๋ ๊ฐ์ง ๊ฐ์ฒด์ด๋ค.
๐ค ์ด๋ค ์ ๋ต์ ์จ์ผํ ๊น?
๋๋ถ๋ถ์ ๋น์ค๋์ค ๋ก์ง์์ Schedule ๊ณผ Member ์ ๊ฐ์ด ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ผ๋ฉด LAZY ๋ก๋ฉ์ด ์ํด
๐ ์กฐํ๋ฅผ ํ ๋ฒ๋ง ํ๋๊ฒ ์ข์ผ๋๊น ๋ฌด์กฐ๊ฑด EAGER(์ฆ์ ๋ก๋ฉ)์ ์ฌ์ฉํ๋ฉด ๋๋์?
๐ ์ฐธ๊ณ
[JPA] ์ฆ์ ๋ก๋ฉ๊ณผ ์ง์ฐ ๋ก๋ฉ(FetchType.LAZY or EAGER)
[JPA] ํ๋ก์
์ฆ์ ๋ก๋ฉ์์ ๋ฐ์ํ๋ N + 1์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ โ ์ง์ฐ ๋ก๋ฉ
์ง์ฐ ๋ก๋ฉ + fetch join์ ์ฌ์ฉํ๋๋ผ๋ N + 1 ์ด ๋ฐ์ํ๋ค.
โ ํด๋น ์ฐ๊ฒฐ Entity ์ ํ๋ก์๋ก ๊ฑธ์ด๋๊ณ , ๊ฒฐ๊ตญ ์ฌ์ฉํ ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ฒ์ ์กฐํ ํ ๋๋ N+1์ด ๋ฐ์ํ์ง ์์ง๋ง ์ฐ๊ฒฐ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด ํ๋ก์์ ๋ํ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ฐ์ํ๋ค.
๐ ์ฐธ๊ณ
JPA ๋ชจ๋ N+1 ๋ฐ์ ์ผ์ด์ค๊ณผ ํด๊ฒฐ์ฑ
๐ ํ์ต ๋ด์ฉ ์ถ๊ฐ์์