2021. 8. 25. 08:00ใBack-End ์์ ์ค/Spring Framework
์๋ ํ์ธ์? ์ฃผ๋ํ๋ ์ ๋๋ค.
๋ณธ ๋ด์ฉ์ ์ฃผ๋ํ๋์ด Web ๊ฐ๋ฐ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ๋์ ํ๋ ๊ธฐ์ ๋ค์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํ๋ ๊ณณ ์ ๋๋ค!
์ด ๊ธ์ ํจ๊ป ํ๋ก์ ํธ ํ๋ Back End ๊ฟ๋๋ฌด Crew๊ฐ ์์ฑํ ๊ฒ์ด์์.
์ฃผ๋ํ๋๋ Back End ๊ฐ๋ฐ์ ๋งก์์๋ต๋๋ค!
"์ด ํฌ์คํ ์ ์ฟ ํก ํํธ๋์ค ํ๋์ ์ผํ์ผ๋ก, ์ด์ ๋ฐ๋ฅธ ์ผ์ ์ก์ ์์๋ฃ๋ฅผ ์ ๊ณต๋ฐ์ต๋๋ค."
โ ์ด๋ฒ ํ๋ก์ ํธ์์ ์ฐ๋ฆฌ๋ JPA๋ฅผ ์ ์ฌ์ฉํ๋๊ฐ?
- sql ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์์ ๊ฐ์ฒด ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ด ๊ฐ๋ฅ
- ์์ฐ์ฑ์ด ์ฆ๊ฐํ๊ณ ๊ฐ๋จํ ๋ฉ์๋๋ก CRUD๊ฐ ๊ฐ๋ฅ
- ์ ์ง๋ณด์๊ฐ ์ฝ๋คJPA: ํ๋๋ง ์ถ๊ฐ, SQL์ JPA๊ฐ ์ฒ๋ฆฌ
- ๊ธฐ์กด: ํ๋ ๋ณ๊ฒฝ ์ ๋ชจ๋ SQL์ ์์
- Object์ RDB ๊ฐ์ ํจ๋ฌ๋ค์ ๋ถ์ผ์น ํด
๐ JPA ์์์ฑ
JPA์ ํต์ฌ์ ์ํฐํฐ๊ฐ ์์์ฑ ์ปจํ ์คํธ์ ํฌํจ๋์ด ์๋์ง ์๋์ง๋ก ๋๋ฉ๋๋ค. JPA์ ์ํฐํฐ ๋งค๋์ ๊ฐ ํ์ฑํ๋ ์ํ๋ก ํธ๋์ญ์ (@Transactional) ์์์ DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉด ์ด ๋ฐ์ดํฐ๋ ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ ์ง๋ ์ํ์ ๋๋ค. ์ด ์ํ์์ ํด๋น ๋ฐ์ดํฐ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ํธ๋์ญ์ ์ด ๋๋๋ ์์ ์ ํด๋น ํ ์ด๋ธ์ ๋ณ๊ฒฝ ๋ด์ฉ์ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
์ฐ๋ฆฌ๋ ์ํฐํฐ ๊ฐ์ฒด์ ํ๋ ๊ฐ๋ง ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋ณ๋๋ก update()์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ํ์๊ฐ ์๊ฒ ๋ฉ๋๋ค! ์ด ๊ฐ๋ ์ ๋ํฐ ์ฒดํน์ด๋ผ๊ณ ํฉ๋๋ค.
์์์ฑ ์ปจํ ์คํธ๋?
์ํฐํฐ๋ฅผ ๋ด๊ณ ์๋ ์งํฉ.
JPA๋ ์์ ์ปจํ ์คํธ์ ์ํ ์ํฐํฐ๋ฅผ DB์ ๋ฐ์ํ๋ค. ์ํฐํฐ๋ฅผ ๊ฒ์, ์ญ์ , ์ถ๊ฐ
ํ๊ฒ ๋๋ฉด ์์ ์ปจํ ์คํธ์ ๋ด์ฉ์ด DB์ ๋ฐ์๋๋ค. ์์ ์ปจํ ์คํธ๋ ์ง์ ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํ๊ณ Entity Manager๋ฅผ ํตํด์๋ง ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
JPA ์ฑ๋ฅ ์ต์ ํ
- 1์ฐจ ์บ์์ ๋์ผ์ฑ๋ณด์ฅ
- ๊ฐ์ ํธ๋ ์ ์ ์์์ ๊ฐ์ Entity๋ฅผ ๋ฐํ
- SQL๋ฅผ ํ๋ฒ๋ง ์คํ
- ์ง์ฐ๋ก๋ฉ
- ๊ฐ์ฒด๊ฐ ์ค์ ๋ก ์ฌ์ฉ๋ ๋ ๋ก๋ฉํ๋ ์ ๋ต
- Find ์์
Optional<DiscussionBoard> findById(@Param("id") Long id);
์์์ฒ๋ผ ์์ฑ์ ํ๊ฒ ๋๋ฉด id์ ๋ํ ๊ฐ์ FindById ๋ฉ์๋๋ฅผ ํตํด์ ๊ฐ์ ์กฐํ ํ ์ ์์ต๋๋ค.์ฆ, ๊ฐ์ด ์ค์ ๋ก ํ์ํ ์์ ์ JPA๊ฐUser์ ๋ํ SELECT ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค.DiscussionBoard ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ๋๋ถ๋ถ User๋ ๊ฐ์ด ํ์ํ๋ค๋ฉด ์ฆ์ ๋ก๋ฉ์ ์ฌ์ฉํ๋ค.์๋ ์ฌ์ง์ ์์ธ ์กฐํ๋ฅผ ํ์ ๋ N + 1 ์ ๋๋ค. ํ์ง๋ง ์กฐํ๋ฅผ ํ ๋ ์ฐ๊ด ๊ด๊ณ๊ฐ ์์ผ๋ฉด N + 1 ํ์์ด ๋ฐ๊ฒฌ์ด ๋์์ต๋๋ค. ํ๊ฐ์ Query๋ฌธ๋ง ํ์ํ์ง๋ง JPA์์๋ ์ฐ๊ด๊ด๊ณ์ ์๋ ํ ์ด๋ธ๊น์ง ๊ฐ์ด ์กฐํํด์ ์กฐํ๊ฐ 2๋ฒ์ด ๋๋ ํ์์ด ์์์ต๋๋ค. DiscussionBoard ์ User๊ฐ์ฒด ๊ฐ๊ฐ ๋ฐ๋ก ์กฐํํ๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ๋ฅผ 2๋ฒ ํ๊ฒ ๋๋ค. Optional<DiscussionBoard> findById(@Param("id") Long id) ์์๋ DiscussionBoard ๊ฐ์ฒด์ ๋ํ SELECT ์ฟผ๋ฆฌ๋ง ๋ ๋ฆฝ๋๋ค.
์ฆ, ๊ฐ์ด ์ค์ ๋ก ํ์ํ ์์ ์ JPA๊ฐUser์ ๋ํ SELECT ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฝ๋๋ค.
DiscussionBoard ์ User๊ฐ์ฒด ๊ฐ๊ฐ ๋ฐ๋ก ์กฐํํ๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ๋ฅผ 2๋ฒ ํ๊ฒ ๋ฉ๋๋ค.
DiscussionBoard ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ๋๋ถ๋ถ User๋ ๊ฐ์ด ํ์ํ๋ค๋ฉด ์ฆ์ ๋ก๋ฉ์ ์ฌ์ฉํฉ๋๋ค.
ํ์ง๋ง ์กฐํ๋ฅผ ํ ๋ ์ฐ๊ด ๊ด๊ณ๊ฐ ์์ผ๋ฉด N + 1 ํ์์ด ๋ฐ๊ฒฌ์ด ๋์์ต๋๋ค. ํ๊ฐ์ Query๋ฌธ๋ง ํ์ํ์ง๋ง JPA์์๋ ์ฐ๊ด๊ด๊ณ์ ์๋ ํ ์ด๋ธ๊น์ง ๊ฐ์ด ์กฐํํด์ ์กฐํ๊ฐ 2๋ฒ์ด ๋๋ ํ์์ด ์์์ต๋๋ค.
์๋ ์ฌ์ง์ ์์ธ ์กฐํ๋ฅผ ํ์ ๋ N + 1 ์ ๋๋ค.
N+1์ด๋
์ฐ๊ด ๊ด๊ณ์์ ๋ฐ์ํ๋ ์ด์๋ก ์ฐ๊ด ๊ด๊ณ๊ฐ ์ค์ ๋ ์ํฐํฐ๋ฅผ ์กฐํํ ๊ฒฝ์ฐ์ ์กฐํ๋ ๋ฐ์ดํฐ ๊ฐฏ์(n) ๋งํผ ์ฐ๊ด๊ด๊ณ์ ์กฐํ ์ฟผ๋ฆฌ๊ฐ ์ถ๊ฐ๋ก ๋ฐ์ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๊ฒ ๋๋ค.
๊ฒ์ํ ์์ธ๋ณด๊ธฐ FindById ๋ฉ์๋๋ฅผ ์ฌ์ฉ ์ ๊ฒ์ํ์ ๋ํ Query๋ฌธ๊ณผ ์ ์ ์ ๋ํ Query๋ฌธ์ด ๊ฐ์ด ๋์ค๊ฒ ๋์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ FetchJoin์ ์ฌ์ฉํ์ต๋๋ค.
FetchJoin ์ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ Entity๋ฅผ ํ๋ฒ์ ์กฐํ ํ ์ ์๋ ๊ธฐ๋ฅ์ด ์๋ค๋ ๊ฒ์ ๋ฃ๊ณ ์ ์ฉ์ ์์ผฐ์ต๋๋ค.
Update ์์
queryRepository.updateBoard(update, board.getId(), board.getUser().getId());
์์ ์ ํ ์์๋ Update ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ฝ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ ์์์ต๋๋ค.
Update ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ ๋ ์ฟผ๋ฆฌ๋ฌธ์ ๋๋ค.
Delete ์์
queryRepository.deleteBoard(board.getId(), board.getUser().getId());
์ญ์ ๋ฅผ ํ ์์๋ Delete ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ์ ์์์ต๋๋ค.
Delete๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ ๋ ์ฟผ๋ฆฌ๋ฌธ์ ๋๋ค.
Querydsl์ด๋?
- JPQL์ ๋น๋ ์ญํ ์ ํ๋ ์คํ
- Query๋ฅผ ์๋ฐ์ฝ๋๋ก ์์ฑ
- ํ์ ์ธ์ดํ
Querydsl๋ฅผ ์ฌ์ฉํ๋ฉด JPA๋ฅผ ์ฌ์ฉํ์ ๋๋ณด๋ค ์ฟผ๋ฆฌ๋ฌธ์ ์ง๊ด์ ์ผ๋ก ๋ณผ ์ ์์์ต๋๋ค.
Querydsl๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ JPAQueryFactoryํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค.
Q-Type์ ์ด์ฉํด์ Querydsl๋ฅผ ๋น๋ ํฉ๋๋ค.
- Find Querydsl ์์
-
public Optional<DiscussionDetailResponse> findByDiscussionId(Long id){ DiscussionDetailResponse board = queryFactory .select(Projections.constructor(DiscussionDetailResponse.class, discussionBoard.id, user.id, user.nickName, discussionBoard.title, discussionBoard.content, discussionBoard.createAt, discussionBoardReadhit.count)) .from(discussionBoard) .innerJoin(discussionBoard.user, user) .join(discussionBoardReadhit).on(discussionBoardReadhit.discussionBoard.id.eq(discussionBoard.id)) .where(discussionBoard.id.eq(id)) .fetchOne(); return Optional.ofNullable(board); }
๊ฒ์ํ ๋ฒํธ๋ฅผ ์กฐํํ๋ ์ฟผ๋ฆฌ๋ฌธ์ ๋๋ค. User์ DiscussionBoardReadhit์ ์ ๋ณด๋ ์กฐํ๋ฅผ ํด์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ Entity๋ inner join()์ ์ฌ์ฉํ๊ณ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ Entity๋ join().on()์ ์ฌ์ฉํด์ N+1 ํ์์ ๋ง์ผ๋ฉด์ ์กฐํ๋ฅผ ํ ์ ์์์ต๋๋ค.
Querydsl๋ก ์ฐ๊ด๊ด๊ณ์ธ User๋ innerjoin()์ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ discussionReadhit์ join().on()์ ์ด์ฉํด์ ์กฐํํ ์ฟผ๋ฆฌ๋ฌธ์ ๋๋ค.
Update Querydsl ์์
public void updateBoard(DiscussionEditRequest update, Long id, Long userId) {
JPAUpdateClause updateClause = new JPAUpdateClause(em, discussionBoard);
updateClause
.where(discussionBoard.id.eq(id), discussionBoard.user.id.eq(userId))
.set(discussionBoard.title, update.getTitle())
.set(discussionBoard.content, update.getContent())
.set(discussionBoard.createAt, update.getCreateAt())
.execute();
}
Update์์๋ JPAUpdateClaus๋ฅผ ์ฌ์ฉํด์ where ์ ๊ณผ set์ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ ์์์ต๋๋ค.
Update์์๋ JPAUpdateClaus๋ฅผ ์ฌ์ฉํด์ where ์ ๊ณผ set์ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ ์์์ต๋๋ค.
Delete Querydsl ์์
public void deleteBoard(Long id, Long userId) {
JPADeleteClause deleteClause = new JPADeleteClause(em, discussionBoard);
deleteClause
.where(discussionBoard.id.eq(id), discussionBoard.user.id.eq(userId))
.execute();
}
Delete์์๋ JPADeleteClaus๋ฅผ ์ฌ์ฉํด์ where์ ์ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ์ ์์์ต๋๋ค.
Querydsl๋ฅผ ์ฌ์ฉํด์ Delete์ ์ฟผ๋ฆฌ๋ฌธ์ ๋๋ค.
"์ด ํฌ์คํ ์ ์ฟ ํก ํํธ๋์ค ํ๋์ ์ผํ์ผ๋ก, ์ด์ ๋ฐ๋ฅธ ์ผ์ ์ก์ ์์๋ฃ๋ฅผ ์ ๊ณต๋ฐ์ต๋๋ค."
๊ฒฐ๋ก
JPA์ ๋ํด ๊ณต๋ถ๋ฅผ ํ๋ฉด์ ๋จ์ ๋ฉ์๋๋ช ์ผ๋ก JPA๊ฐ ์์์ DB์ ๋ฐ์ดํฐ๋ฅผ ๋ฑ๋ก, ์กฐํ, ์ ๋ฐ์ดํธ, ์ญ์ ๋ฅผ ํด์ฃผ๋ ๊ฒ์ ํธ๋ฆฌํ๋ค๊ฒ ๋ค๋ผ๋ ์๊ฐ์ ํ๊ฒ ๋์์ง๋ง, ์ง์ JPA๋ฅผ ์ฌ์ฉํ๋ฉด์ Insert ๊ตฌ๋ฌธ์ ์ฌ์ฉ์๊ฐ ์์๋ก ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํด์ ์คํ ์ํฌ ์ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
Entity์์ ์ฐ๊ด๊ด๊ณ์ธ ๊ฐ์ฒด๋ฅผ ์ง์ฐ๋ก๋ฉ์ผ๋ก ์ง์ ํ๋ฉด N+1๋ฅผ ๋ฐฉ์ง ํ ์ ์์ ์ค ์์์ง๋ง DiscussionBoard์์ User ์ ์ฐ๊ด๊ด๊ณ์ ์์ผ๋ ์ฟผ๋ฆฌ๋ฌธ์ด 2๊ฐ ๋ฐ์ํ์์ต๋๋ค. N+1 ํ์์ด ๋ฐ์ํ๊ฒ ๋ ๊ฒ์ ์๊ฒ ๋์๊ณ , ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ FetchJoin์ ์ด์ฉํด์ DiscussionBoard์ User ๊ฐ์ฒด๋ฅผ 1๊ฐ์ ์ฟผ๋ฆฌ๋ฌธ์ผ๋ก ์กฐํ๋ฅผ ํ๋ ๊ฒ์ ํ์ธ ํ ์ ์์์ต๋๋ค.
Querydsl์ Entity Class ๋ฅผ wrapping ํ Qxxx class ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ Entity Annotation์ด ์๋ ํด๋์ค๋ค์ Qxxx class ๋ก ์๋ ์์ฑํ๋๋ก ์ค์ ํด์ฃผ๊ฒ๋๋ฉด ์๋ฐ์ฝ๋๋ฅผ ์ด์ฉํด์ Entity์ Qxxx type-safe๋ฅผ ํตํด์ DB์ ๊ฐ์ ์กฐํ, ์ ์ฅ, ์ญ์ ๋ฅผ ํ ์ ์์์ต๋๋ค.
Querydsl์์ N+1ํ์์ ์ฐ๊ด๊ด๊ณ์ธ ๊ฒฝ์ฐ์๋ innerjoin(), ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ join().on()์ ํตํด์ ๋ฐฉ์ง ํ ์ ์์์ต๋๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์ JPA์ Querydsl๋ฅผ ์ด์ฉํด์ DB์ ๊ฐ์ ์ ์ฅํ์ต๋๋ค.
'Back-End ์์ ์ค > Spring Framework' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] ์ข์ ๊ฐ์ฒด ์งํฅ ์ค๊ณ์ 5๊ฐ์ง ์์น (SOLID) (0) | 2021.09.11 |
---|---|
[Spring] ์คํ๋ง ํต์ฌ ์๋ฆฌ (0) | 2021.09.10 |
[๋์ ๊ธฐ์ !] ํ ์คํธ ์ฝ๋ (0) | 2021.08.24 |
[๋์ ๊ธฐ์ !] Web Layer (0) | 2021.08.23 |
[Spring Boot] AOP (0) | 2021.08.20 |