DevLog ๐ถ
[JPA] OSIV ์ฐ๋จนํ๊ธฐ 2ํธ - ์ค์์ ์ํ ์ํฐํฐ๋ฅผ ์ง์ฐ ๋ก๋ฉํ๊ธฐ (FetchType.EAGER์ N+1 ๋ฌธ์ , fetch join) ๋ณธ๋ฌธ
[JPA] OSIV ์ฐ๋จนํ๊ธฐ 2ํธ - ์ค์์ ์ํ ์ํฐํฐ๋ฅผ ์ง์ฐ ๋ก๋ฉํ๊ธฐ (FetchType.EAGER์ N+1 ๋ฌธ์ , fetch join)
dolmeng2 2023. 5. 21. 19:22๐ฑ ๋ค์ด๊ฐ๊ธฐ ์
์ง๋ ํฌ์คํ ์์๋ ์ค์์ ์ํ์ ์ํฐํฐ๋ ์ง์ฐ ๋ก๋ฉํ ์ ์๋ค๊ณ ๋งํ๋ค.
์ด๋, ์ง์ฐ ๋ก๋ฉ์ ์ํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ํด ์์์ฑ ์ปจํ ์คํธ๊ฐ ํ์ํ๋ฐ, ์ค์์ ์ํ์ ์ํฐํฐ๋ ์์์ฑ ์ปจํ ์คํธ์ ๊ด๋ฆฌ ๋ฒ์์์ ๋ฒ์ด๋ฌ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด, ์ค์์ ์ํ์ ์ํฐํฐ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผ ํ ๊น?
๐ฑ ๊ธ๋ก๋ฒ ํ์น ์ ๋ต์ LAZY์์ EAGER๋ก ์์ ํ๊ธฐ
๊ฐ๋จํ๊ฒ ๋งํ๋ฉด ์ง์ฐ ๋ก๋ฉ์ ์ฌ์ฉํ์ง ๋ง๊ณ , ์ฆ์ ๋ก๋ฉ์ ์ฌ์ฉํ์๋ ๊ฒ์ด๋ค ๐
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Crew {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wootecho_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho;
}
ํฌ๋ฃจ ํด๋์ค์ fetch ์ ๋ต์ EAGER๋ก ์ค์ ํ์๋ค. (์ด์ฐจํผ @ManyToOne์ ๋ํดํธ ์ ๋ต์ด fetch์ด๊ธฐ ๋๋ฌธ์ ๋ช ์์ ์ผ๋ก ์ค์ ํ์ง ์์๋ ๋๊ฐ๋ค.)
์ฆ์ ๋ก๋ฉ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด, Crew๋ฅผ ์กฐํํ ๋ ์ฐ๊ด๋ ์ํฐํฐ์ธ Wootecho ์ญ์ ํจ๊ป ๊ฐ์ ธ์ค๊ฒ ๋๋ค.
์ค์ ๋ก ์ฟผ๋ฆฌ๋ฅผ ํ์ธํด๋ณด์.
๋ค์๊ณผ ๊ฐ์ด Join์ ํตํด ํ ๋ฒ์ ๊ฐ์ ธ์จ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฌ join์ ํตํด ๊ฐ์ ธ์ค๋ ๊ฑฐ๋ฉด ๋ ์ข์ ๊ฑฐ ์๋์ผ?
๋ง์ฝ, crew ์ํฐํฐ์ ์ฐ๊ด๋ ์ํฐํฐ๊ฐ ์ง๊ธ์ฒ๋ผ ํ๋๊ฐ ์๋๋ผ ์ฌ๋ฌ ๊ฐ๋ผ๋ฉด?
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Crew {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wootecho_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wootecho2_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho2;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wootecho3_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho3;
}
์๋นํ ์ด์ง๋ฌ์ด ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ฌ์ง์ด, ์ ์์ ๋ ์ ๋ง ๊ฐ๋จํ ์์ ์ธ๋ฐ๋ ์ฌ๋ฌ join์ด ๊ฑธ๋ฆฌ๊ฒ ๋๋ ๊ฒ์ด๋ค.
๐ฌ N+1 ๋ฌธ์
์ด๋ JPQL์ ์ฌ์ฉํ์ ๋์ ๋ฌธ์ ์ด๋ค.
JPQL์ ํตํด JPA๊ฐ SQL์ ์ฌ์ฉํ๊ฒ ๋๋ฉด, ๊ธ๋ก๋ฒ ํ์น ์ ๋ต์ ์ ๊ฒฝ์ฐ์ง ์๊ณ JPQL ์์ฒด ์ ๋ณด๋ง ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ฐ๊ด๋ ์ํฐํฐ์ ๋ํด์ join ๋์ where ์ ๋ก ๋ฌถ์ธ ๋ค์ ๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๊ฒ ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ์ฌํํ๊ธฐ ์ํด์๋ OneToMany ๊ด๊ณ๋ฅผ ์ดํด๋ด์ผ ํ๊ธฐ ๋๋ฌธ์, ์ฐํ ์ฝ๋ฅผ ๊ธฐ์ค์ผ๋ก ์กฐํ API๋ฅผ ์์ฑํ ๊ฒ์ด๋ค.
์ฐํ ์ฝ ์ํฐํฐ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Wootecho {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private Course course;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "wootecho")
private List<Crew> crews;
}
crew์ ๋ํ ์ ๋ณด๋ฅผ ์ถ๊ฐํด์ฃผ๊ณ , ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ Wootecho๋ก ์ค์ ํด์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ , JPQL์ ์ฌ์ฉํ๊ธฐ ์ํด์ ์์ ๊ฐ์ด ์ปค์คํ ๋ฉ์๋๋ฅผ ์์ฑํด์ฃผ์๋ค.
public interface WootechoRepository extends JpaRepository<Wootecho, Long> {
@Query("select w from Wootecho w")
List<Wootecho> findWootechos();
}
์๋น์ค์ ์ปจํธ๋กค๋ฌ, DTO์ด๋ค. ๊ฐ๋จํ ์์ ์ด๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ ์ค๋ช ์ ์์ฑํ์ง ์๊ฒ ๋ค.
// WootechoService.java
@Service
@RequiredArgsConstructor
public class WootechoService {
private final WootechoRepository wootechoRepository;
public List<Wootecho> getAll() {
return wootechoRepository.findWootechos();
}
}
// WootechoController.java
@RestController
@RequestMapping("/wootecho")
@RequiredArgsConstructor
public class WootechoController {
private final WootechoService wootechoService;
@GetMapping
public ResponseEntity<List<WootechoResponse>> getAll() {
final List<Wootecho> wootechos = wootechoService.getAll();
final List<WootechoResponse> result = wootechos.stream()
.map(WootechoResponse::of)
.toList();
return ResponseEntity.ok(result);
}
}
์๋ก์ด WootechoService์ WootechoController๋ฅผ ์์ฑํ์๋ค.
์ด์ , ์ฐํ ์ฝ ์ ์ฒด๋ฅผ ์กฐํํด๋ณด์. ํ์ฌ ํ ์ด๋ธ์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋์ด ์๋ค.
Hibernate:
select
w1_0.id,
w1_0.course
from
wootecho w1_0
Hibernate:
select
c1_0.wootecho_id,
c1_0.id,
c1_0.name
from
crew c1_0
where
c1_0.wootecho_id=?
Hibernate:
select
c1_0.wootecho_id,
c1_0.id,
c1_0.name
from
crew c1_0
where
c1_0.wootecho_id=?
Hibernate:
select
c1_0.wootecho_id,
c1_0.id,
c1_0.name
from
crew c1_0
where
c1_0.wootecho_id=?
๋ค์๊ณผ ๊ฐ์ด ๊ต์ฅํ ๊ธธ์ด์ง ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ ๊ฒ์ ๋ณผ ์ ์๋ค. ํ๋์ฉ ๋ณด์.
์์ ๊ฐ์ด Wootecho๋ง ์กฐํํ๋ ค๊ณ ํ์ผ๋, ์ฆ์ ๋ก๋ฉ์ผ๋ก ์ธํด Crew์ ๋ํ ์ ๋ณด๋ ๊ฐ์ ธ์ค๋ฉด์ 1+N๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
JPQL์ด ์ฝ๋ ๋ถ์ ์ ๋ค์๊ณผ ๊ฐ์ ์ ์ฐจ๋ฅผ ๋ฐ์ผ๋ฉฐ ๋ฐ์ํ ๋ฌธ์ ์ด๋ค.
1. select w from Wootecho w๋ฅผ ๋ณด๊ณ select * from wootecho ์ฟผ๋ฆฌ ์์ฑ
2. ์์ฑ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ฐํ์ผ๋ก List<Wootecho> ์ํฐํฐ ์์ฑ
3. ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋ ์ดํ, ์ฆ์ ๋ก๋ฉ ์ ๋ต์ผ๋ก ์ธํด์ Wootecho์ ์ฐ๊ด๋ Crew ์ํฐํฐ ์ญ์ ํจ๊ป ์ฆ์ ๋ก๋ฉ
4. Wootecho ์ํฐํฐ์ ๊ฐ์๋งํผ select * from crew where wootecho_id = ? ์ฟผ๋ฆฌ ๋ฐ์
๐ฑ LAZY๋ก ์ ์งํ๋ฉด์ fetch join ์ฌ์ฉํ๊ธฐ
์ง์ฐ ๋ก๋ฉ์ ์ฌ์ฉํ๋ฉด์, jpql์ ํธ์ถํ๋ ์์ ์ ํ ๋ฒ์ ๊ฐ์ ธ์ค๋๋ก ์ต์ ํ๋ฅผ ํ๋ ์ ๋ต์ด๋ค.
public interface WootechoRepository extends JpaRepository<Wootecho, Long> {
@Query("select w from Wootecho w join fetch w.crews")
List<Wootecho> findWootechos();
}
์์ ๊ฐ์ด jpql์ join fetch๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
์ด ์ํ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ์ดํด๋ณด์.
๊น๋ํ๊ฒ join์ ์ด์ฉํ์ฌ ํ ๋ฒ์ ๊ฐ์ ธ์จ ๊ฒ์ ํ์ธํ ์ ์๋ค ๐
๐ฑ ๊ฐ์ ๋ก ์ด๊ธฐํํ๊ธฐ - Hibernate.initialize()
์์์ฑ ์ปจํ ์คํธ๊ฐ ์ด์์์ ๋ ํ๋ ์ ํ ์ด์ ๊ณ์ธต์์ ํ์ํ ์ํฐํฐ๋ฅผ ๊ฐ์ ๋ก ์ด๊ธฐํํ์ฌ ๋ฐํํ ์ ์๋ค.
JPA๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ด๋ จ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๊ธฐ ๋๋ฌธ์, ํ์ด๋ฒ๋ค์ดํธ์์ ์ ๊ณตํ๋ Hibernate.initialize()๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ฐ๋ฆฌ๊ฐ ์ด์ ํฌ์คํ ๋ถํฐ ๊ณ ๋ฏผํ๋ 'ํฌ๋ฃจ๋ฅผ ์กฐํํ ๋ ์ฐํ ์ฝ ์ํฐํฐ๋ฅผ ํจ๊ป ์กฐํํ๋ ๊ฒฝ์ฐ'์ ๋ํด ์ ์ฉํด๋ณด์.
public class Crew {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "wootecho_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho;
}
๋ค์ Crew ์ํฐํฐ์์ Wootecho ์ํฐํฐ์ ํ์น ์ ๋ต์ LAZY๋ก ์ค์ ํด์ค๋ค. (๋ง์ฐฌ๊ฐ์ง๋ก Wootecho ์ํฐํฐ์ Crew ์ญ์ LAZY๋ก ์ค์ ํด์ฃผ์๋ค.)
// CrewService.java
public Crew getById(final Long wootechoId) {
final Crew findCrew = crewRepository.findById(wootechoId)
.orElseThrow(() -> new IllegalArgumentException("์กด์ฌํ์ง ์๋ ์์ด๋์
๋๋ค."));
Hibernate.initialize(findCrew.getWootecho());
return findCrew;
}
๊ทธ๋ฆฌ๊ณ , ๋ค์๊ณผ ๊ฐ์ด getById() ๋ฉ์๋์ Hibernate.initialize()๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
๊ธฐ์กด์๋ no Session Exception์ด ํฐ์ก๋ ๊ฒ๊ณผ ๋ค๋ฅด๊ฒ, ์ ์กฐํ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
Hibernate ๊ธฐ์ ์ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ๋ถ๋ด์ค๋ฝ๋ค๋ฉด, ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ด์์๋ ๋ฒ์์์ ํ๋ ์ ํ ์ด์ ๊ณ์ธต์ด ์ฌ์ฉํ ๋ฐ์ดํฐ๋ฅผ ์ค์ ๋ก ์ฌ์ฉํด๋ฒ๋ฆฌ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
// CrewService.java
public Crew getById(final Long wootechoId) {
final Crew findCrew = crewRepository.findById(wootechoId)
.orElseThrow(() -> new IllegalArgumentException("์กด์ฌํ์ง ์๋ ์์ด๋์
๋๋ค."));
findCrew.getWootecho().getCourse(); // ์ค์ ์ฌ์ฉํด๋ฒ๋ฆฌ๊ธฐ
return findCrew;
}
์ด๋ ๊ฒ ํด๋ ์์ ๋์ผํ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค.
ํ์ง๋ง, ์ด๋ ๊ฒ ํ๋ก์๋ฅผ ์ด๊ธฐํํ๋ ์ญํ ์ ์๋น์ค ๊ณ์ธต์ด ๋ด๋นํ๋ฉด ๋ทฐ๊ฐ ํ์ํ ์ํฐํฐ์ ๋ฐ๋ผ์ ์๋น์ค ๊ณ์ธต์ ๋ก์ง์ ๋ณ๊ฒฝํด์ผ ํ๋ค.
๋ง์ฝ CrewResponse๊ฐ ์๋ ๋ค๋ฅธ DTO ๊ฐ์ฒด๊ฐ ์๊ธฐ๊ณ , ๊ฑฐ๊ธฐ์๋ course๋ง ํ์ํ ๊ฒ์ด ์๋ name์ด๋ ๋ค๋ฅธ ํ๋๋ค์ ๊ฐ๋ค์ด ํ์ํ๋ค๋ฉด? ๋ทฐ์ ๋ณ๊ฒฝ์ฌํญ์ด ์๋น์ค๊น์ง ํจ๊ป ์ํฅ์ ๋ฐ๊ธฐ ๋๋ฌธ์ ์ ์ง๋ณด์ ํฌ์ธํธ๊ฐ ์ฆ๊ฐํ๋ ๊ฒ์ด๋ค.
์ด๋ฅผ ์ํด ์๋น์ค ๊ณ์ธต์์ ํ๋ ์ ํ ์ด์ ๊ณ์ธต์ ์ํ ํ๋ก์ ์ด๊ธฐํ ์ญํ ์ ๋ถ๋ฆฌํด์ผ ํ๋ฉฐ, FACADE ํจํด์ ํ์ฉํ ์ ์๋ค.
์๋น์ค ์๋จ์์ Facade๊ฐ 1์ฐจ์ ์ผ๋ก ํ๋ ์ ํ ์ด์ ๊ณ์ธต๊ณผ ๋๋ฉ์ธ ๋ชจ๋ธ ์ฌ์ด์ ๋ ผ๋ฆฌ์ ์์กด์ฑ์ ๋ถ๋ฆฌํด์ค์ผ๋ก์, ํ๋ ์ ํ ์ด์ ๊ณ์ธต์์ ํ์ํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํ๋ ์ญํ ์ ๋ด๋นํ๋ ๊ฒ์ด๋ค.
@Component
@RequiredArgsConstructor
public class CrewFacade {
private final CrewService crewService;
public Crew getById(final Long crewId) {
final Crew crew = crewService.getById(crewId);
// ํ๋ก์ ๊ฐ์ฒด๊ฐ ํ์ํ ๋ฐ์ดํฐ ์ด๊ธฐํ
crew.getWootecho().getCrews();
return crew;
}
}
// CrewController.java
@GetMapping("/{crewId}")
public ResponseEntity<CrewResponse> getById(@PathVariable Long crewId) {
final Crew crew = crewFacade.getById(crewId);
return ResponseEntity.ok(CrewResponse.of(crew));
}
ํ์ง๋ง... ์ด๋ฐ ์์ผ๋ก ์ค๊ฐ ๊ณ์ธต์ด ๋ค์ด๊ฐ๋ฉด ๊ฒฐ๊ณผ์ ์ผ๋ก ๊ฐ๋ฐ์๊ฐ ์์ฑํด์ผ ํ๋ ์ฝ๋๊ฐ ๋ง์์ง๋ค๋ ๊ฒ์ด๋ค.
๋ํ, JPA์ ๋ํ ์ดํด๋๊ฐ ์๋ ๊ฐ๋ฐ์์ ํ์ ์ ํ๊ฒ ๋๋ค๋ฉด ํด๋น ์ฝ๋์ ๋ํด ์ ๋๋ก ์ดํดํ์ง ๋ชปํ ์ํ๋ก ์ ๊ฑฐํด๋ฒ๋ฆฌ๋ ์ํฉ์ด ๋ฐ์ํ ์๋ ์๋ค ๐ฅฒ
๊ฒฐ๊ตญ ์ง๊ธ๊น์ง์ ๋ชจ๋ ๋ฌธ์ ๋ '์ํฐํฐ๊ฐ ํ๋ ์ ํ ์ด์ ๊ณ์ธต์์ ์ค์์ ์ํ์ด๊ธฐ ๋๋ฌธ์' ๋ฐ์ํ๋ ๋ฌธ์ ์ด๋ค.
์ด๋ฐ ์์ด์ ์ธ ๋ฌธ์ ๋ถํฐ ํด๊ฒฐ์ ํด๋ณด์.
๐ฑ OSIV (Open session In View)
OSIV๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋ทฐ๊น์ง ์ด์ด๋๋ค๋ ์๋ฏธ์ด๋ค. ๋ทฐ๊น์ง ์ปจํ ์คํธ๊ฐ ์ด๋ ค ์๊ธฐ ๋๋ฌธ์ ์๋์ผ๋ก ์ง์ฐ ๋ก๋ฉ์ด ๊ฐ๋ฅํด์ง๋ ๊ฒ์ด๋ค.
cf. ์ฐธ๊ณ ๋ก, OSIV๋ ํ์ด๋ฒ๋ค์ดํธ์์ ์ฌ์ฉํ๋ ์ฉ์ด์ฌ์ JPA์์๋ OEIV (Open EntityManager In View)๋ฅผ ๋ ๋ง์ด ์ฌ์ฉํ์ง๋ง, OSIV๊ฐ ๋ ์ต์ํ๊ธฐ ๋๋ฌธ์ OSIV๋ผ๊ณ ๋ถ๋ฅด๊ฒ ๋ค.
OSIV๋ ์ฌ๋ฌ ๋ฐฉ์์ด ์๋๋ฐ, ํด๋ผ์ด์ธํธ์ ์์ฒญ๊ณผ ์๋ช ์ฃผ๊ธฐ๊ฐ ๋์ผํ Transaction per request OSIV๊ฐ ์กด์ฌํ๋ค.
์์ฒญ์ด ๋ค์ด์ค์๋ง์ ์๋ธ๋ฆฟ ํํฐ๋ ์คํ๋ง ์ธํฐ์ ํฐ์์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋ง๋ค๋ฉด์ ํธ๋์ญ์ ์ ์์ํ๊ณ , ์์ฒญ์ด ๋๋ ๋ ์ปจํ ์คํธ์ ํธ๋์ญ์ ์ ํจ๊ป ์ข ๋ฃํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์ด๋ฌ๋ฉด ์์์ฑ ์ปจํ ์คํธ๊ฐ ๋๊น์ง ์ด์ ์์ด์ ์ด๋์ ์กฐํํ๋ ์์ ์ํ๊ฐ ๋๋ค.
์ง์ฐ ๋ก๋ฉ ์ญ์ ์์ ๋กญ๊ฒ ๊ฐ๋ฅํ์ง๋ง, ์ํฐํฐ์ ํธ๋์ญ์ ์๋ช ์ฃผ๊ธฐ๊ฐ ๋๋ฌด ๊ธธ๊ธฐ ๋๋ฌธ์ ํ๋ ์ ํ ์ด์ ์์ญ์์ ์ํฐํฐ๋ฅผ ์์ ๋กญ๊ฒ ์ฐธ์กฐํ ์ ์๋ค๋ ๊ฒ์ด๋ค. ์ํฐํฐ๋ ์ฝ์ดํ ์์ญ์ด๊ธฐ ๋๋ฌธ์ ์ธ๋ถ์์ ์ฝ๊ฒ ์ฐธ์กฐํ๋ ๊ฑด ์ข์ง ์๋ค. ๋๋ฉ์ธ์ด DTO๋ฅผ ์ฐธ์กฐํ์ง ์๋ ๊ฒ๊ณผ ๋์ผํ ์์ญ์ด๋ผ๊ณ ๋ด๋ ๋๋ค. ใ ใ
(๊ฐ์ธ์ ์ธ ์๊ฐ์ผ๋ก๋ entity๊ฐ controller ์์ญ๊น์ง ์ฐธ์กฐ๋๋ ๊ฑธ ์ ํธํ์ง ์๊ธฐ ๋๋ฌธ์ ๊ทธ๋ฌํ ๊ด์ ์์๋ ๋ณ๋ก๋ค)
๊ทธ๋ฌ๋ฉด, ์คํ๋ง์์๋ ์ด๋ค OSIV ์ ๋ต์ ์ฑํํ๊ณ ์์๊น?
- ๋ค์ํ OSIV ํด๋์ค๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ํํฐ๋ ์ธํฐ์ ํฐ๊ฐ ๋ฑ๋กํ์ฌ ์ฌ์ฉํ ์ ์๋ค. (์ค์ํ ๋ด์ฉ์ ์๋๋ ์๋ตํ๊ฒ ๋ค)
๐ฌ ์คํ๋ง ํ๋ ์์ํฌ๊ฐ ์ ๊ณตํ๋ OSIV
OSIV๋ฅผ ์ฌ์ฉํ์ง๋ง, ๋น์ฆ๋์ค ๊ณ์ธต๊น์ง๋ง ํธ๋์ญ์ ์ ์ฌ์ฉํ๋ ์ ๋ต์ด๋ค.
ํด๋ผ์ด์ธํธ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์๋ธ๋ฆฟ ํํฐ๋ ์คํ๋ง ํํฐ์์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์์ฑํ์ง๋ง, ํธ๋์ญ์ ์ ์์ํ์ง ์๋๋ค.
์ดํ, ์๋น์ค ๊ณ์ธต์์ @Transactional์ ์์ํ ๋ ์ด๊ธฐ์ ์์ฑํ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋ฐํ์ผ๋ก ํธ๋์ญ์ ์ ์์ํ๋ค.
์๋น์ค ๊ณ์ธต์ด ์ข ๋ฃ๋๋ฉด ํธ๋์ญ์ ์ปค๋ฐ ํ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ํ๋ฌ์ํ๊ณ , ํธ๋์ญ์ ์ ์ข ๋ฃํ์ง๋ง ์ปจํ ์คํธ๋ ์ข ๋ฃํ์ง ์๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด ํ๋ ์ ํ ์ด์ ๊ณ์ธต๊น์ง ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ด์์๊ธฐ ๋๋ฌธ์ ์ํฐํฐ๋ ์์ ์ํ๋ฅผ ์ ์งํ๊ฒ ๋๊ณ , ์ง์ฐ ๋ก๋ฉ์ด ๊ฐ๋ฅํ๊ฒ ๋๋ค!
์ดํ ํํฐ๋ ์ธํฐ์ ํฐ๋ก ์์ฒญ์ด ๋ค์ ๋๋์์ค๋ฉด ์ปจํ ์คํธ๋ฅผ ์ข ๋ฃํด์ค๋ค. (์ด๋, ํ๋ฌ์๋ฅผ ํธ์ถํ์ง ์๋๋ค!)
โญ๏ธ ๊ธฐ์กด์ ๋ฐฉ๋ฒ์ ๋นํด ํ๋ ์ ํ ์ด์ ๊ณ์ธต์์๋ ํธ๋์ญ์ ์ด ์๊ธฐ ๋๋ฌธ์ ์ํฐํฐ์ ๋ํ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ๋ค.
-> ์ ํํ๊ฒ๋, ์ด๋ฏธ ์๋น์ค ๊ณ์ธต์์ ํ๋ฌ์๊น์ง ์งํ๋์๊ณ ํํฐ-์ธํฐ์ ํฐ์์๋ ํ๋ฌ์๋ฅผ ํธ์ถํ์ง ์๊ธฐ ๋๋ฌธ์, ํ๋ฌ์๋ฅผ ํธ์ถํ์ง ์์ ์ํฐํฐ์ ๋ณ๊ฒฝ์ฌํญ์ ๋ํด DB๋ก ๋ฐ์ํ์ง ์๋ ๊ฒ์ด๋ค.
-> ๊ฐ์ ๋ก ํ๋ฌ์ํ๋๋ผ๋ ํธ๋์ญ์ ๋ฒ์ ๋ฐ์ด๋ผ ์์ ํ ์ ์๋ค๋ ์์ธ๊ฐ ๋ฐ์ํ๋ค. (TransactionRequiredException)
ํ์ง๋ง, ์ง์ฐ ๋ก๋ฉ์ ํตํด์ ์ํฐํฐ๋ฅผ ์กฐํํด์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ๋จ์ํ ์กฐํ๋ ๊ฐ๋ฅํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
๐ก ์ฌ๊ธฐ์ ์ค์ํ ๊ฑด, ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ด์์๊ธฐ ๋๋ฌธ์ ๋ด๋ถ์ ๋ณ๊ฒฝ์ฌํญ์ด ์ ์ฅ๋์ด ์๋ ๊ฒ์ด๋ค.
ํ๋ ์ ํ ์ด์ ๋ ์ด์ด์์ @Transactional ํฌํจํ ๋น์ฆ๋์ค ๋ก์ง์ ํธ์ถํ๊ฒ ๋๋ฉด ํด๋น ๋น์ฆ๋์ค ๋ก์ง์ด ์ข ๋ฃ๋ ๋ ์ด๋ฏธ ์ด์ ์ ์ ์ฅ๋์๋ ๋ณ๊ฒฝ์ฌํญ์ ํจ๊ป flush ํด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์๊ธฐ์น ๋ชปํ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
- ๋ณดํต์ ์ปจํธ๋กค๋ฌ์์ ์๋น์ค๋ฅผ ๋จผ์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ผ์ ๊ฑฐ์ ์๊ฒ ์ง๋ง...
ํ๋ฌ์๋ฅผ ํธ์ถํ์ง ์์์ ๋ณ๊ฒฝ์ฌํญ์ ๋ฐ์ํ์ง ์๋๋ค๋ ์ ์ ๊ธฐ์ตํ์!
๐ฑ ๋ค์ ๋๊ณ ๋์์...
์คํ๋ง๋ถํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก OSIV๋ฅผ true๋ก ์ค์ ํด๋์๊ธฐ ๋๋ฌธ์ ๋ทฐ๊น์ง ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ๋ค.
ํ์ง๋ง ๊ฐ์ธ์ ์ผ๋ก OSIV ์ต์ ์ ๋๊ณ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค๊ณ ์๊ฐํ๋ค.
์ฐ์ DB ์ปค๋ฅ์ ์ ๊ณ์ ์ก๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฆฌ์์ค์ ๋ญ๋น๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
(์ง์ฐ ๋ก๋ฉ์ ํตํด์ ๋ฐ์์ค๋ ค๋ฉด ์๋ฌดํผ ์ปค๋ฅ์ ์ด ์ด์ ์์ด์ผ ํ๋๊น)
๋ํ, ์คํ๋ง์ OSIV ์ ๋ต์ ํ๋ ์ ํ ์ด์ ์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ง์ง๋ง, ์ ์ด์ ํ๋ ์ ํ ์ด์ ๊น์ง ์ํฐํฐ๊ฐ ์ฐธ์กฐ ๊ด๊ณ๋ฅผ ๊ฐ์ง๋ ๊ฑด ๋ ์ด์ด์ ๋จ๋ฐฉํฅ ํ๋ฆ์ ๊นฌ๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค. (๊ฐ์ ์๋ฏธ๋ก DTO ์ญ์ ์๋น์ค ๋ ์ด์ด์์ ์์ฑํ ๋ค์์ ๋ทฐ๋ก ๋ฐํํ๋ ๊ฒ์ ์ ํธํ๋ค.)
๊ฐ ๋ ์ด์ด๊ฐ์ ๋จ๋ฐฉํฅ ๊ด๊ณ๋ฅผ ์ ์งํ๊ธฐ ์ํด์๋ ์ํฐํฐ์ ๋ํ ์กฐ์์ ์๋น์ค ๋ ์ด์ด๊น์ง๋ง ์งํํ๊ณ , ์ดํ ๋ทฐ์ ์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ๋ DTO๋ฅผ ํตํด ์ฐ๊ด๊ด๊ณ๋ฅผ ๋์ ์ํ๋ก ์กฐ์ํ๋ ๊ฒ ๋ ์ข๋ค๋ ๊ฒ์ด ๋์ ์๊ฒฌ์ด๋ค ๐
(๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ ๋ฐฉ๋ฒ์ผ๋ก Command-Query separation ์ด๋ผ๋ ๊ฒ๋ ์๋๋ฐ, ํต์ฌ ๋ก์ง๊ณผ ๋ทฐ ๊ด๋ จ ์๋น์ค ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด๋ค. ๋์ค์ ๋ฏธ์ ํ ๋ ํ ๋ฒ ์ด๊ฑธ๋ก ์ ์ฉํด๋ด์ผ๊ฒ ๋ค...)