SpringBoot JPA활용 강의를 들으며 배운 내용을 현재 진행하고 있는 개인 프로젝트에 적용한 내용을 정리해보았다.
강의를 다 듣고 나서 개인 프로젝트를 시작하진 않았지만 강의 수강 전에 시작하든 후에 시작하든 나름 장단점이 있는 거 같다. 강의를 다 듣기 전에 만든 부분을 강의를 듣고 난 후 개선해볼 수도 있고 들은 후에 프로젝트를 시작하면 까먹을 뻔한 점들을 개인 프로젝트를 하면서 다시 상기시킬 수도 있기 때문이다.
아래 사용자의 영화 추천내역을 조회하는 화면을 만들면서 강의에서 배운 2가지를 적용해 볼 수 있었다.
1. Entity 직접 사용보다는 화면에 맞는 Form 객체를 사용
- 영화의 추천내역을 저장하는 Entity는 다음과 같다.
@Entity
@Table(name="recommendation")
@Getter @Setter
public class Recommendation {
@Id
@GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MOVIE_ID")
private Movie movie;
private String comment;
private String recipientName;
private Long recipientId;
private String recipientEmail;
}
이 Recommendation이란 Entity를 바로 View에 전달할 수도 있겠지만 그렇게 하면 화면을 위한 로직을 Recommendation에 직접 넣게 되는 경우가 생길수도 있다. 예를 들어 요즘 개인정보 보호가 아주 중요해져서 사용자 이메일을 마스킹해야 하는 요구사항이 생길 수 있다. 그렇다고 Entity에 이를 위한 로직을 넣기는 좀 애매하다.
그래서 Recommendation 대신 화면을 위한 객체 RecoSaved를 만들고 Recommendation 내부에 속한 Entity인 Member와 Movie에 대한 객체(MemberDto, MovieDto)도 생성하였다. 이제 MemberDto에 마스킹된 이메일 주소를 넣을 수 있게 되었다.
@Data
public class RecoSaved {
private MovieDto movie;
private String comment;
private MemberDto recommender; // 추천한사람
public RecoSaved(Recommendation recommendation){
movie = new MovieDto(recommendation.getMovie());
comment = recommendation.getComment();
recommender = new MemberDto(recommendation.getMember());
}
@Data
static class MemberDto{
private String email;
private String birthDate;
private String gender;
public MemberDto(Member member){
email = getMaskedEmail(member.getEmail());//개인정보 보호를 위해 마스킹된 이메일 주소 넣기
birthDate = member.getBirthDate();
gender = member.getGender();
}
}
@Data
static class MovieDto{
private String title;
private String director;
private String imageLink;
public MovieDto(Movie movie){
title = movie.getTitle();
director = movie.getDirector();
imageLink = movie.getImageLink();
}
}
2. 지연로딩(LAZY 로딩)으로 인한 N+1문제를 Fetch join으로 해결
위 화면을 불러오기 위해 실행되는 쿼리는 다음과 같이 총 3개다.
select
recommenda0_.id as id1_2_0_,
recommenda0_.comment as comment2_2_0_,
recommenda0_.member_id as member_i6_2_0_,
recommenda0_.movie_id as movie_id7_2_0_,
recommenda0_.recipient_email as recipien3_2_0_,
recommenda0_.recipient_id as recipien4_2_0_,
recommenda0_.recipient_name as recipien5_2_0_
from
recommendation recommenda0_
where
recommenda0_.id=?
select
movie0_.movie_id as movie_id1_1_0_,
movie0_.director as director2_1_0_,
movie0_.image_link as image_li3_1_0_,
movie0_.rating as rating4_1_0_,
movie0_.recommend_cnt as recommen5_1_0_,
movie0_.release_date as release_6_1_0_,
movie0_.movie_title as movie_ti7_1_0_
from
movie movie0_
where
movie0_.movie_id=?
select
member0_.member_id as member_i1_0_0_,
member0_.auth as auth2_0_0_,
member0_.birth_date as birth_da3_0_0_,
member0_.email as email4_0_0_,
member0_.gender as gender5_0_0_,
member0_.password as password6_0_0_
from
member member0_
where
member0_.member_id=?
굳이 이렇게 3개 쿼리를 실행할 필요 없이 recommendation, movie, member 테이블들을 한 번에 join해서 가져오는 게 성능상 유리하다. 이럴 때는 fetch join을 이용해서 가져올 수 있다.
public Recommendation findRecommendation(Long id){
TypedQuery<Recommendation> query1
= em.createQuery("select r from Recommendation r" +
" join fetch r.movie m" +
" join fetch r.member me" +
" where r.id = :rId"
, Recommendation.class);
query1.setParameter("rId",id);
return query1.getSingleResult();
}
위처럼 가지고 오면 아래와 같이 쿼리가 한 번만 나간다.
select
recommenda0_.id as id1_2_0_,
movie1_.movie_id as movie_id1_1_1_,
member2_.member_id as member_i1_0_2_,
recommenda0_.comment as comment2_2_0_,
recommenda0_.member_id as member_i6_2_0_,
recommenda0_.movie_id as movie_id7_2_0_,
recommenda0_.recipient_email as recipien3_2_0_,
recommenda0_.recipient_id as recipien4_2_0_,
recommenda0_.recipient_name as recipien5_2_0_,
movie1_.director as director2_1_1_,
movie1_.image_link as image_li3_1_1_,
movie1_.rating as rating4_1_1_,
movie1_.recommend_cnt as recommen5_1_1_,
movie1_.release_date as release_6_1_1_,
movie1_.movie_title as movie_ti7_1_1_,
member2_.auth as auth2_0_2_,
member2_.birth_date as birth_da3_0_2_,
member2_.email as email4_0_2_,
member2_.gender as gender5_0_2_,
member2_.password as password6_0_2_
from
recommendation recommenda0_
inner join
movie movie1_
on recommenda0_.movie_id=movie1_.movie_id
inner join
member member2_
on recommenda0_.member_id=member2_.member_id
where
recommenda0_.id=?
'개발잡담' 카테고리의 다른 글
Transaction은 뭐고 @Transactional은 언제 쓰는 건데? (0) | 2021.12.26 |
---|---|
글로만 배웠던 Enum 을 활용해보자 (0) | 2021.10.17 |
Spring은 어떻게 사용자 요청을 받아서 처리할까? (0) | 2021.08.22 |
ERD를 데이터베이스에 적용해보자(Feat. JPA) (0) | 2021.08.08 |
FlowChart, ERD (0) | 2021.08.07 |
댓글