DevLog ๐ถ
[JPA] OSIV ์ฐ๋จนํ๊ธฐ 1ํธ - ํ๋ก์ ๊ฐ์ฒด์ ์ค์์ ์ํ ์ํฐํฐ ๋ณธ๋ฌธ
[JPA] OSIV ์ฐ๋จนํ๊ธฐ 1ํธ - ํ๋ก์ ๊ฐ์ฒด์ ์ค์์ ์ํ ์ํฐํฐ
dolmeng2 2023. 5. 21. 16:44๊ธ์์ผ์ ๊ทผ๋กํ๋ฉด์ ๊ตฌ๊ตฌ์ ๊ทผ๋ก ํฌ๋ฃจ๋ค์ ๋์ ๋๋ถ์ JPA๋ฅผ ๋ค์๊ธ ๊ณต๋ถํ๊ฒ ๋๋ค ๐
๋๋ ์์ ํ๊ฒ ์ ์๋ ๋ด์ฉ์ ์๋์ง๋ผ ์๋๋๋ก ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
๐ฑ ์์์ฑ ์ปจํ ์คํธ์ ํธ๋์ญ์
๐ก ์์์ฑ ์ปจํ ์คํธ๋?
์ํฐํฐ๋ฅผ ์์ํ์ํค๋ ํ๊ฒฝ. EntityManager๋ฅผ ํตํด ์ํฐํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๋ฉด ์์์ฑ ์ปจํ ์คํธ์ ๋ณด๊ด๋ ์ํฐํฐ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์์ฒญ์ ์ฒ๋ฆฌํ๊ฒ ๋๋ค. EntityManager ์์ฑ ์ 1๊ฐ๊ฐ ๋ง๋ค์ด์ง๋ค.
๐ฌ ํน์ง
- ์๋ณ์ ๊ฐ์ผ๋ก ์ํฐํฐ๋ฅผ ๊ตฌ๋ถํ๋ค.
- ํธ๋์ญ์ ์ปค๋ฐ ์์ ์ flush. (์ฐ๊ธฐ ์ง์ฐ - ๋ด๋ถ ์ฟผ๋ฆฌ ์ ์ฅ์์ SQL ์ ์ฅ ํ ํ ๋ฒ์ ํ๋ฌ์)
- 1์ฐจ ์บ์๋ฅผ ์ฌ์ฉํ๋ค. (์ฒ์์ 1์ฐจ ์บ์์์ ์ํฐํฐ ์กฐํ > ์์ผ๋ฉด DB ์กฐํ > 1์ฐจ ์บ์์ ์ ์ฅํ๋ ํํ = '์์ํ')
- 1์ฐจ ์บ์์ ์ ์ฅ๋ ์ํฐํฐ๋ == ๋น๊ต๊ฐ ๊ฐ๋ฅํ๋ค. (๋์ผ์ฑ ๋ณด์ฅ)
- ๋ณ๊ฒฝ ๊ฐ์ง ('์์ ์ํ์' ๋ณ๊ฒฝ๋ ์ํฐํฐ์ ๋ํด์ ์ ๋ฐ์ดํธ ์ฟผ๋ฆฌ ์ ์ฅ > ํ๋ฌ์ > ํธ๋์ญ์ ์ปค๋ฐ)
ํธ๋์ญ์ ์ด ์์๋๋ฉด ์์์ฑ ์ปจํ ์คํธ๋ ์์ฑ๋๊ณ , ์ข ๋ฃ๋๋ฉด ์ปจํ ์คํธ๋ ํจ๊ป ์ข ๋ฃ๋๋ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ , ๋์ผํ ํธ๋์ญ์ ์์์๋ ํญ์ ๊ฐ์ ์์์ฑ ์ปจํ ์คํธ์ ์ ๊ทผํ๊ฒ ๋๋ค.
์ฌ๊ธฐ์ ๋งํ๋ ํธ๋์ญ์ ์ ์ฐ๋ฆฌ๊ฐ ์คํ๋ง์ ๊ฐ๋ฐํ๋ฉด์ ์ฌ์ฉํ @Transactional ์ด๋ ธํ ์ด์ ์ ๋ฒ์๋ฅผ ์๋ฏธํ๋ค.
@Transactional์ด ๋ถ์ด์์ผ๋ฉด ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ํธ๋์ญ์ AOP๊ฐ ๋จผ์ ๋์ํ๋๋ฐ (์ด์ ๋ํด์๋ ์ถํ ๋ค๋ฃจ์ด ๋ณด๊ฒ ๋ค.) ๊ฐ๋จํ๊ฒ ์๊ฐํ๋ฉด ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ํธ๋์ญ์ ์ ์์ํ๊ณ , ์ ์์ ์ผ๋ก ์ข ๋ฃ๋๋ฉด ํธ๋์ญ์ ์ปค๋ฐ ํ ์ข ๋ฃํ๋ ๊ฒ์ด๋ค.
์ด๋, JPA์์๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ํ๋ฌ์ํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ DB์ ๋ฐ์ํ ๋ค, DB ํธ๋์ญ์ ์ ์ปค๋ฐํ๊ฒ ๋๋ค.
ํํ ์ฐ๋ฆฌ๋ ๊ด์ต์ ์ผ๋ก ๊ฐ๋ฐํ ๋ @Transactional ์ด๋ ธํ ์ด์ ์ ์๋น์ค ๋ ์ด์ด์ ๋ถ์ด๊ณค ํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋น์ค ๊ณ์ธต์ด ๋๋๋ ์์ ์ ํธ๋์ญ์ ์ด ์ข ๋ฃ๋๋ฉฐ ์์์ฑ ์ปจํ ์คํธ๋ ํจ๊ป ์ข ๋ฃ๋์ด, ์๋น์ค์ ๋ ํ์งํ ๋ฆฌ ๋ ์ด์ด์์๋ ์์ ์ํ์ธ ์ํฐํฐ๊ฐ ์ปจํธ๋กค๋ฌ๋ ๋ทฐ ๊ฐ์ presentation layer์์๋ ์ค์์ ์ํ๊ฐ ๋๋ค.
๊ทธ๋์ ์ปจํธ๋กค๋ฌ๋ ๋ทฐ (ํน์ DTO)์์ ์ค์์ ์ํ์ธ ์ํฐํฐ๋ฅผ ํตํด ์ง์ฐ ๋ก๋ฉ์ ์๋ํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
๐ก ์ง์ฐ ๋ก๋ฉ (Lazy Loading)์ด๋?
- ์ํฐํฐ๋ฅผ ์กฐํํ ๋ ์ฐ๊ด๋ ์ํฐํฐ์ ๋ํด ์ค์ DB์์ ๋ฐ๋ก ์ ๊ทผํ์ง ์๊ณ , ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ์ค์ ๋ก ์ฌ์ฉํ๋ ์์ ์ ๋ก๋ฉํ๋ ๋ฐฉ๋ฒ. ์ง์ฐ ๋ก๋ฉ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์ค์ ์ํฐํฐ ๊ฐ์ฒด ๋์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๋ฅผ ์ง์ฐํ ์ ์๋ ๊ฐ์ง ๊ฐ์ฒด๊ฐ ํ์ํ๋ฉฐ, ์ด๋ฅผ ์ํด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
- OneToMany, ManyToMany์์ ๋ํดํธ๋ก ์ฌ์ฉํ๋ ์ ๋ต์ด๋ค.
ํ์ง๋ง, ์คํ๋ง ๋ถํธ๋ฅผ ํ์ฉํ์ฌ ๊ฐ๋ฐํ ๋๋ ๋ค๋ฅด๋ค.
์ฒ์ ์คํ๋ง๋ถํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ฑํ์ํค๋ฉด ์๋์ ๊ฐ์ ๋ฌธ๊ตฌ๊ฐ ๋จ๋ ๊ฒ์ ๋ณผ ์ ์์ ๊ฒ์ด๋ค.
spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
๊ธฐ๋ณธ์ ์ผ๋ก ์คํ๋ง๋ถํธ๋ OSIV๋ฅผ ํ์ฑํํ๊ธฐ ๋๋ฌธ์ view rendering ์์ ๊น์ง DB ์ฟผ๋ฆฌ์ ์ํฅ์ ๋ฐ์ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ด๋ก ๋ง ๋ณด๋ฉด ๋น์ฐํ ์ดํดํ๊ธฐ ํ๋ค๋ค. ๊ฐ๋จํ ํด๋์ค๋ฅผ ๊ตฌ์ฑํ์ฌ ์ง์ ์ดํด๋ณด์.
(OSIV์ ๋ํด์๋ ๋ค์ ํฌ์คํ ์์ ๋ ์์ธํ ๋ค๋ฃฐ ์์ ์ด๋ค.)
๐ฑ ํ๋ก์ ๊ฐ์ฒด
JPA๋ฅผ ์๊ฐํ๋ฉด ๋บ ์ ์๋ ๊ฐ๋ ์ค ํ๋๊ฐ ํ๋ก์ ๊ฐ์ฒด์ด๋ค.
์ฝ๋๋ฅผ ๋ณด๊ธฐ ์ ์ ๊ฐ๋จํ๊ฒ ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ด๋ค ๊ฒ์ธ์ง ์ง๊ณ ๋์ด๊ฐ๋ณด์.
JPA์์ ์ํฐํฐ๋ฅผ ํ๋ ์กฐํํ ๋๋ EntityManager.find()๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด๋ ์์์ฑ ์ปจํ ์คํธ์ ์ํฐํฐ๊ฐ ์์ผ๋ฉด ์ค์ DB๋ฅผ ์กฐํํ๋ ๋ฉ์๋์ด๋ค. ํ์ง๋ง, ์ด๋ฐ ์์ผ๋ก ์ง์ ์กฐํํ๊ฒ ๋๋ฉด ํด๋น ์ํฐํฐ์ ์ฌ์ฉ ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ฌด์กฐ๊ฑด DB๋ฅผ ์กฐํํ๊ธฐ ๋๋ฌธ์ cost๊ฐ ๋งค์ฐ ๋๋ค. ๋ง์ฝ DB ์กฐํ๋ฅผ ์ค์ ์ฌ์ฉ ์์ ๊น์ง ๋ฏธ๋ฃจ๊ณ ์ถ๋ค๋ฉด EntityManager.getReference()๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค.
์ด๋ DB ์กฐํ ๋์ ์ ์ด๋ฅผ ๋์ฒดํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
์ด๋ฌํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด์๋ '์์'์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ ์์ฑ์๊ฐ ํ์ํ๋ค.
์์ฑ์๊ฐ ์์ ์๋ ์ํฉ์ด๋ผ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ ์์์ ๋ง๋ค์ด์ฃผ์ง๋ง, ํ๋๋ผ๋ ๋ค๋ฅธ ์์ฑ์๊ฐ ์์ผ๋ฉด ๋ง๋ค์ด์ฃผ์ง ์๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ ์์ฑ์๋ ์ฌ๋งํ๋ฉด ๋ง๋ค์ด์ค์ผ ํ๋ค.
ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ๋ํ ์ฐธ์กฐ๊ฐ์ ๊ฐ์ง๊ณ ์๋ค๊ฐ, ํ๋ก์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ์ค์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํด์ค๋ค.
์์ ์ํฉ์์ ํ๋ก์ ๊ฐ์ฒด๋ crew.getName()์ ํตํด ์ง์ ์ ๊ทผํ๊ฒ ๋๋ฉด ์ค์ DB๋ฅผ ์กฐํํ์ฌ ์ค์ ์ํฐํฐ๋ฅผ ์์ฑํ๊ฒ ๋๋๋ฐ, ์ด๋ฅผ 'ํ๋ก์ ๊ฐ์ฒด์ ์ด๊ธฐํ'๋ผ๊ณ ํ๋ค.
1. ํ๋ก์ ๊ฐ์ฒด์ crew.getName()์ ํธ์ถํ์ฌ ์ค์ ๋ฐ์ดํฐ ์กฐํํ๊ธฐ
2. ์ค์ ์ํฐํฐ๊ฐ ์์ฑ๋์ง ์์๋ค๋ฉด, ์์์ฑ ์ปจํ ์คํธ์๊ฒ ์ค์ ์ํฐํฐ์ ์์ฑ ์์ฒญ
3. ์์์ฑ ์ปจํ ์คํธ๋ DB๋ฅผ ์กฐํํ์ฌ ์ค์ ์ํฐํฐ ๊ฐ์ฒด ์์ฑ
4. ํ๋ก์ ๊ฐ์ฒด๋ ์์ฑ๋ ์ํฐํฐ ๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ๋ณด๊ด
5. ๋ณด๊ดํ ์ฐธ์กฐ๊ฐ(crew)์ getName()์ ํธ์ถํ์ฌ ๊ฒฐ๊ณผ ๋ฐํ
์ด๋ฌํ ํ๋ก์ ๊ฐ์ฒด๋ ์ฒ์ ์ฌ์ฉ๋ ๋ ๋ฑ ํ ๋ฒ๋ง ์ด๊ธฐํ๋๋ค.
๋ํ, ์์์ฑ ์ปจํ ์คํธ์ ์ด๋ฏธ ์ํฐํฐ๊ฐ ์์ผ๋ฉด DB๋ฅผ ์กฐํํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ em.getReference()๋ฅผ ํธ์ถํด๋ ํ๋ก์ ๊ฐ์ฒด ๋์ ์ค์ ์ํฐํฐ๋ฅผ ๋ฐํํ๊ฒ ๋๋ค!
์์ธํ ๋ด์ฉ์ ์ฐจ์ฐจ JPA ๊ณต๋ถํ๋ฉด์ ์ฌ๋ฆด ์์ ! ๐
๐ฑ ํ ์ด๋ธ ์ค๊ณ
โ๏ธ Entity
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Wootecho {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private Course course;
}
์ฐํ ์ฝ ํด๋์ค์ด๋ค. Course๋ผ๋ enum์๋ ๋ฐฑ์๋, ํ๋ก ํธ, ์๋๋ก์ด๋๋ผ๋ ๊ณผ์ ์ ๋ณด๊ฐ ๋ค์ด๊ฐ์๋ค.
@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.LAZY)
@JoinColumn(name = "wootecho_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho;
}
๊ฐ๋จํ ํฌ๋ฃจ ํด๋์ค์ด๋ค.
ํฌ๋ฃจ ํด๋์ค์ ์ฐํ ์ฝ ํด๋์ค๋ N:1์ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ์ด๋ ์ง์ฐ ๋ก๋ฉ ์ ๋ต์ ์ฑํํ์๋ค. (FetchType.LAZY)
๋ํ, ์ธ๋ํค ์ ์ฝ ์กฐ๊ฑด์ ๋ํ ์ค์ ์ ์ง์ ํด์ฃผ์ง ์์๋ค.
์ค์ ํ ์ด๋ธ์ ๊ด๊ณ๋ ์๋์ ๊ฐ์ด ๋งบ์ด์ง๋ค.
ํ์ง๋ง, ์ด๋ ๋ ผ๋ฆฌ์ ์ธ ์ฐ๊ด๊ด๊ณ์ผ ๋ฟ์ด๋ฉฐ ์ค์ ๋ก ํ ์ด๋ธ ์คํฌ๋ฆฝํธ๋ฅผ ๋ณด๋ฉด ์๋์ ๊ฐ์ด ์ธ๋ํค ์ ์ฝ์กฐ๊ฑด์ด ์ค์ ๋์ง ์์ ๊ฒ์ ๋ณผ ์ ์๋ค.
CREATE TABLE `wootecho` (
`id` bigint NOT NULL AUTO_INCREMENT,
`course` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
CREATE TABLE `crew` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`wootecho_id` bigint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
crew ํ ์ด๋ธ์ ๋จ์ํ wootecho_id๋ผ๋ ํ๋๋ฅผ ๊ฐ์ง๊ณ ์์ ๋ฟ์ด์ง, ์ธ๋ํค๋ก ๊ฑธ๋ ค์์ง ์๋ค.
์ด๋ ๋ค์๊ณผ ๊ฐ์ ์ค์ ๋๋ฌธ์ด๋ค.
@JoinColumn(... foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Wootecho wootecho;
์ฌ๊ธฐ์ foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) ๋ฅผ ์ค์ ํด์ฃผ๋ฉด, ๋ฌผ๋ฆฌ์ ์ธ ์ธ๋ํค๋ฅผ ๋งบ์ง ์๊ฒ ๋๋ค.
์ค์ ๋ก ์ค๋ฌด์์๋ ์ธ๋ํค๋ฅผ ๊ฑธ์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์ธ๋ํค๋ฅผ ๊ฑธ๋ฉด ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ ์ ์์ง๋ง (๋ถ๋ชจ๊ฐ ์ญ์ ๋์์ ๋ ๊ณ ์๋ก ๋จ์ ์์ ํ๋๊ฐ ๋จ์ง ์๋๋ก ํจ)
์์ ํ ์ด๋ธ์ INSERT ์ฐ์ฐ ์์ ๋ถ๋ชจ ํ ์ด๋ธ๋ ํ์ธํด์ผ ํ๊ณ , ๋ณ๊ฒฝ ์์๋ ๊ณ ๋ คํด์ผ ํ ์ ์ด ๋ง๊ณ , ํ ์คํธํ๊ธฐ๋ ์ด๋ ต๊ธฐ ๋๋ฌธ์ด๋ค.
์ํ๋์ ์ด์ ๋ํด์ ์๋์ ๊ฐ์ด ๋ต๋ณ์ ๋จ๊ฒจ์ฃผ์ ์ ์ด ์๋ค.
๋ ์ญ์ ๊ฐ์ธ์ ์ผ๋ก ์ธ๋ํค๋ฅผ ๊ฑฐ๋ ๊ฑธ ์ ํธํ์ง๋ง... ์๋ง ์์ ๋๋ฉ์ธ๋ง ๋ง์ ธ๋ดค๊ธฐ ๋๋ฌธ์ผ ๊ฒ ๊ฐ๋ค ๐
์๋ฌดํผ, ์ด๊ฒ ์ค์ํ ๊ฒ์ ์๋๋ค. ๋๋ฉ์ธ์ ์์ ๊ฐ์ด ์ค๊ณํ๊ณ , ๋ ํ์งํ ๋ฆฌ์ ์๋น์ค, ์ปจํธ๋กค๋ฌ๋ ๋ค์๊ณผ ๊ฐ๋ค.
โ๏ธ Repository
public interface CrewRepository extends JpaRepository<Crew, Long> {
}
ํํ ์ฌ์ฉํ๋ Jpa๋ฅผ ์ฌ์ฉํ ๋ ํ์งํ ๋ฆฌ ๊ตฌ์ฑ์ด๋ค.
โ๏ธ Service
@Service
@Transactional(readOnly = true)
@AllArgsConstructor
public class CrewService {
private final CrewRepository crewRepository;
public Crew getById(final Long wootechoId) {
final Crew findCrew = crewRepository.findById(wootechoId)
.orElseThrow(() -> new IllegalArgumentException("์กด์ฌํ์ง ์๋ ์์ด๋์
๋๋ค."));
return findCrew;
}
}
์๋น์ค์์๋ ๋จ์ํ ์์ด๋๋ฅผ ํตํด ํฌ๋ฃจ ์ํฐํฐ๋ฅผ ์กฐํํด์ค๋ ๋ก์ง๋ง ์๋ค.
โ๏ธ Controller
@RestController
@RequestMapping("/crew")
@AllArgsConstructor
public class CrewController {
private final CrewService crewService;
@GetMapping("/{crewId}")
public ResponseEntity<CrewResponse> getById(@PathVariable Long crewId) {
final Crew crew = crewService.getById(crewId);
return ResponseEntity.ok(CrewResponse.of(crew));
}
}
์ปจํธ๋กค๋ฌ์ด๋ค. ์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ์ ์ ์๋น์ค์์ ์ํฐํฐ๋ฅผ ๋ฐ์์์ dto๋ก ๋ณํํ๋ ๋ก์ง์ ์ด๊ณณ์์ ํ๋ค๋ ๊ฒ์ด๋ค.
โ๏ธ Dto
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class WootechoResponse {
private final Long id;
private final String course;
public static WootechoResponse of(final Wootecho wootecho) {
return new WootechoResponse(wootecho.getId(), wootecho.getCourse().name());
}
}
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class CrewResponse {
private final Long id;
private final String name;
private final WootechoResponse wootechoResponse;
public static CrewResponse of(final Crew crew) {
return new CrewResponse(crew.getId(), crew.getName(), WootechoResponse.of(crew.getWootecho()));
}
}
ํฌ๋ฃจ์ ๋ํ ์๋ต ์ ๋ณด๋ฅผ ๋ํ๋ด๊ธฐ ์ํด์ crew ์ํฐํฐ์ ์๋ ๊ฐ๋ค๊ณผ ์ฐ๊ด๋ ์ํฐํฐ์ธ wootecho ์ํฐํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์๋ค.
์ ๋ง ๊ฐ๋จํ ์กฐํ์ฉ API๊ฐ ์์ฑ๋์๋ค.
๐ฑ ์ค์์ ์ํ์ ์ง์ฐ ๋ก๋ฉ
์ค์์ ์ํ์์๋ ์ง์ฐ ๋ก๋ฉ์ด ๋์ํ์ง ์๋๋ค.
๐ก ์ค์์ ์ํ ์ํฐํฐ๋?
์์์ฑ ์ปจํ ์คํธ๊ฐ ๊ด๋ฆฌํ๋ ์์ ์ํ์ธ ์ํฐํฐ๊ฐ ๋ ์ด์ ์์์ฑ ์ปจํ ์คํธ์ ์ํด ๊ด๋ฆฌ๋์ง ์๋ ์ํ
์ง์ฐ ๋ก๋ฉ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ค์ ํด์ฃผ๊ณ , ์ํฐํฐ ์กฐํ ์ ์ฐ๊ด๋ ์ํฐํฐ์ ๋ํด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์กฐํํ๋ค.
ํ์ง๋ง, ์ค์์ ์ํ์ธ ์ํฐํฐ๋ ์์์ฑ ์ปจํ ์คํธ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ง์ฐ ๋ก๋ฉ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅํด์ง๋ค.
์ด๋ฅผ ์ค์ ๋ก ํ์ธํด๋ณด์.
์ฐ์ , ์์ ๋งํ๋ ๊ฒ์ฒ๋ผ ์คํ๋ง๋ถํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ทฐ๊น์ง DB ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ๋ ๊ฒ์ ํ์ฉํ๊ธฐ ๋๋ฌธ์ ์ด ์ต์ ์ ๊บผ์ผ ํ๋ค.
spring:
jpa:
open-in-view: false
application.yml์์ open-in-view ์ต์ ์ false๋ก ์ค์ ํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ , ํฌ๋ฃจ 1๋ช ์ ์กฐํํ๋ API ์์ฒญ์ ๋ ๋ ค๋ณด์. ๋๋ '์ ธ๋'๋ผ๋ ํฌ๋ฃจ๋ฅผ ์กฐํํ ์์ ์ด๋ค.
could not initialize proxy [com.example.study.osiv.Wootecho#1] - no Session
์ธ์ ์ด ์๊ธฐ ๋๋ฌธ์ proxy๋ฅผ ์ด๊ธฐํํ ์ ์๋ค๊ณ ๋์จ๋ค.
์ง์ฐ ๋ก๋ฉ์ ํ๊ธฐ ์ํด์๋ ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ 'ํ๋ก์ ๊ฐ์ฒด'๋ก์ ๋ง๋ค์ด๋๊ณ , ์ฌ์ฉ ์์ ์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค๊ณ ๋งํ์๋ค.
// CrewResponse.java
public static CrewResponse of(final Crew crew) {
return new CrewResponse(crew.getId(), crew.getName(), WootechoResponse.of(crew.getWootecho()));
}
CrewResponse์ ์ฝ๋์ด๋ค. ์ฌ๊ธฐ์ crew.getWootecho()๋ฅผ ํตํด ์กฐํ๋ฅผ ํ ๋์๋ ๋ด๊ฒจ์๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ๊ฒ ๋๋ค.
์ค์ ๋ก ๋ณด๋ฉด, HibernateProxy๋ผ๋ ์น๊ตฌ๋ฅผ ํตํด์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์จ ๊ฒ์ ๋ณผ ์ ์๋ค.
// WootechoResponse.java
public static WootechoResponse of(final Wootecho wootecho) {
return new WootechoResponse(wootecho.getId(), wootecho.getCourse().name());
}
๊ทธ๋ฆฌ๊ณ , WootechoResponse์์ ์ค์ ๋ก ๊ฐ์ ์ฑ์๋ฃ์ผ๋ ค๊ณ ํ ๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํ๋ฉด์ ์ค์ ๊ฐ์ ์ฑ์๋ฃ์ผ๋ ค๊ณ ํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
dto ๋ณํ์ controller = presentation layer์์ ํ๋ฉด์, ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์๋น์ค ๋ ์ด์ด๋ฅผ ์ง๋ ํธ๋์ญ์ ๋ฒ์๊ฐ ๋๋๋ฉด์ ์์์ฑ ์ปจํ ์คํธ๋ ํจ๊ป ์ ๊ฑฐ๋์๊ธฐ ๋๋ฌธ์ด๋ค. no session์ด๋ผ๋ ๊ฒ์ด ๊ณง EntityManager๊ฐ ์์ด์ ๊ฐ์ ๊ฐ์ ธ์ฌ ์๊ฐ ์๋ ๊ฒ์ด๋ค.
service ๋ ์ด์ด์์ ์กฐํํด์จ Crew ์ํฐํฐ๋ '์์ ์ํ'์์ผ๋, controller ๋ ์ด์ด๋ก ๋์ด์ค๋ฉด์ ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ ๊ฑฐ๋์ด Crew ์ํฐํฐ๋ ์ค์์ ์ํ์ ์ํฐํฐ๊ฐ ๋์๋ค.
์ฌ์ค ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์ปจํธ๋กค๋ฌ์์ ๋ฐํํ์ง ์๊ณ , ๊ทธ๋ฅ ์๋น์ค์์ DTO๋ฅผ ๋ฐํํ๋๋ก ์ฝ๋๋ฅผ ๊ฐ์ ํ๋ ๊ฒ์ด๋ค.
// CrewService.java
public CrewResponse getById(final Long wootechoId) {
final Crew findCrew = crewRepository.findById(wootechoId)
.orElseThrow(() -> new IllegalArgumentException("์กด์ฌํ์ง ์๋ ์์ด๋์
๋๋ค."));
return CrewResponse.of(findCrew);
}
์ด๋ ๊ฒ ๋๋ฉด ์์์ฑ ์ปจํ ์คํธ ๋ฒ์๊ฐ ์ด์์๋ ๋ฒ์์์ ํ๋ก์ ๊ฐ์ฒด ์ด๊ธฐํ๊ฐ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๊ฑฑ์ ํ ํ์๊ฐ ์๋ค.
ํ์ง๋ง, ์ฐ๋ฆฌ๋ ์ปจํธ๋กค๋ฌ์์ ์ํฐํฐ๋ฅผ dto๋ก ๋ณํํ๋ ์์ ์ ํ ์ ์๋ ๊ฒ์ผ๊น?
๋ค์ ํฌ์คํ ์์ ์ด๋ป๊ฒ ํ๋ฉด ์ด๋ฅผ ๊ฐ์ ํ ์ ์์์ง ์ดํด๋ณด์.