기존 코드 -> 김영한 쌤 코드 스타일로 리팩토링 -> 장형철 튜터님 코드 스타일로 리팩토링
1. 기존코드
기존의 데이터 베이스 접근 방식의 데이터 저장 흐름을 반영한 코드 구성
각각의 레파지토리를 생성하여 각각의 레파지리에 .save를 하는 방식으로 구현되어 있다.
-> 이 방식의 문제점
: JPA의 영속성 컨텍스트에 대한 이해가 전혀 없이 짠 코드.
@Service
@RequiredArgsConstructor
public class CafeService {
private final CafeRepository cafeRepository;
private final UserRepository userRepoistory;
private final CafeImageRepository cafeImageRepository;
private final CafeOptionRepository cafeOptionRepository;
private final S3Service s3Service;
@Transactional
public void createCafe(CafeCreateRequestDto requestDto, String email) {
// jwt token 사용 유저 정보 - ByEmail
User user = userRepoistory.findByUserEmail(email).orElseThrow();
List<MultipartFile> files = requestDto.getFiles();
// s3저장 후 url 반환받음
List<String> cafeImageUrlList = s3Service.upload(files, "cafeImage");
// 카페 생성 1번 case : cafe
Cafe cafe = new Cafe(requestDto, user);
cafeRepository.save(cafe);
// 카페 이미지 생성
List<CafeImage> cafeImageObjectList = new ArrayList<>();
MultipartFile file;
String cafeImageUrl;
for (int i = 0; i < files.size(); i++){
file = files.get(i);
cafeImageUrl = cafeImageUrlList.get(i);
CafeImage cafeImage = new CafeImage(file.getOriginalFilename(), cafeImageUrl, cafe);
cafeImageObjectList.add(cafeImage);
}
cafeImageRepository.saveAll(cafeImageObjectList);
// 카페 옵션 생성성
List<CafeOptionType> optionList = requestDto.getOptions();
List<CafeOption> cafeOptionObjectList = new ArrayList<>();
for (int i = 0; i < optionList.size() ; i++) {
CafeOptionType option = optionList.get(i);
CafeOption cafeOption = new CafeOption(option ,cafe);
cafeOptionObjectList.add(cafeOption);
}
cafeOptionRepository.saveAll(cafeOptionObjectList);
}
2. 김영한 강사님의 구현 코드를 반영하여 리팩토링
생성자를 이용한 객체 생성 + 정적 팩토리 메서드 사용
jpa의 영속성 컨텍스트를 이용한 코드 구현
-> 연관관계 매서드 활용
-> 각각의 객체를 생성하여 List에 담고 정적 팩토리 메서드를 이용하여 Cafe 클래스에 정적 팩토리 메서드를 구현하여 cafe를 생성하며 연관 관계 메서드를 통한 영속성 콘텍스트를 이용한 코드를 구현
-> 3개의 repository 사용에서 cafeRepository 하나 사용으로 충분 - 영속성 컨텍스트
** 정적 팩토리 메서드란?
: 객체 생성의 역할을 하는 클래스 메서드
@Service
@RequiredArgsConstructor
public class CafeService {
private final CafeRepository cafeRepository;
private final UserRepository userRepoistory;
private final S3Service s3Service;
@Transactional
public void createCafe(CafeCreatRequestDto requestDto, String email) {
// jwt token 사용 유저 정보 - ByEmail
User user = userRepoistory.findByUserEmail(email).orElseThrow();
List<MultipartFile> files = requestDto.getFiles();
// s3저장 후 url 반환받음
List<String> cafeImageUrlList = s3Service.upload(files, "cafeImage");
// 카페 이미지 생성
List<CafeImage> cafeImageObjectList = new ArrayList<>();
MultipartFile file;
String cafeImageUrl;
for (int i = 0; i < files.size(); i++) {
file = files.get(i);
cafeImageUrl = cafeImageUrlList.get(i);
CafeImage cafeImage = new CafeImage(file.getOriginalFilename(), cafeImageUrl);
cafeImageObjectList.add(cafeImage);
}
// 카페 옵션 생성성
List<CafeOptionType> optionList = requestDto.getOptions();
List<CafeOption> cafeOptionObjectList = new ArrayList<>();
for (CafeOptionType option : optionList) {
CafeOption cafeOption = new CafeOption(option);
cafeOptionObjectList.add(cafeOption);
}
Cafe cafeTest = Cafe.registCafe(requestDto, user, cafeImageObjectList, cafeOptionObjectList);
cafeRepository.save(cafeTest);
}
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Cafe extends Timestamped {
... 생략 ...
//==연관관계 편의 메서드==//
public void addUser(User user){
this.user = user;
user.getCafes().add(this); // 양방향 처리를 잘해주자
}
public void addCafeImage(CafeImage cafeImage){
cafeImages.add(cafeImage);
cafeImage.addCafe(this);
}
public void addCafeOption(CafeOption cafeOption) {
cafeOptions.add(cafeOption);
cafeOption.addCafe(this);
}
//==생성 메서드==// 여러개의 연관관계로 복잡한 생성은 별도의 생성 메서드가 있으면 좋다.
public static Cafe registCafe(CafeCreatRequestDto requestDto, User user, List<CafeImage> cafeImages, List<CafeOption> cafeOptions){
Cafe cafe = new Cafe();
cafe.addUser(user);
for (CafeImage cafeImage : cafeImages) {
cafe.addCafeImage(cafeImage);
}
for (CafeOption cafeOption : cafeOptions) {
cafe.addCafeOption(cafeOption);
}
cafe.cafeName = requestDto.getCafeName();
cafe.cafeZonecode = requestDto.getCafeZonecode();
cafe.cafeAddress = requestDto.getCafeAddress();
cafe.cafeAddressDetail = requestDto.getCafeAddressDetail();
cafe.cafeX = requestDto.getCafeX();
cafe.cafeY = requestDto.getCafeY();
cafe.cafeInfo = requestDto.getCafeInfo();
cafe.cafeInfoDetail = requestDto.getCafeInfoDetail();
cafe.cafePrecaution = requestDto.getCafePrecaution();
cafe.cafeWeekdayPrice = requestDto.getCafeWeekdayPrice();
cafe.cafeWeekendPrice = requestDto.getCafeWeekendPrice();
return cafe;
}
}
3. 장형철 튜터님 코드 스타일로 리팩토링
jpa의 영속성 컨텍스트를 이용한 코드 구현
정적 팩토리 메서드를 사용하지 않고 연관관계 메서드를 이용하여 영속성 콘텍스트에 넣어줌으로써 코드가 간결해짐.
ex) 이미지 객체를 생성하여 ArrayList에 담아 정적 팩토리 메서드를 거쳐 연관관계 메서드를 호출하는 부분이 간략해짐
@Service
@RequiredArgsConstructor
public class CafeService {
private final CafeRepository cafeRepository;
private final UserRepository userRepoistory;
private final S3Service s3Service;
@Transactional
public void createCafe(CafeCreateRequestDto requestDto, String email) {
// jwt token 사용 유저 정보 - ByEmail
User user = userRepoistory.findByUserEmail(email).orElseThrow();
Cafe cafe = new Cafe(requestDto);
user.addCafe(cafe);
List<MultipartFile> files = requestDto.getFiles();
// s3저장 후 url 반환받음
List<String> cafeImageUrlList = s3Service.upload(files, "cafeImage");
// 카페 이미지 생성
MultipartFile file;
String cafeImageUrl;
for (int i = 0; i < files.size(); i++) {
file = files.get(i);
cafeImageUrl = cafeImageUrlList.get(i);
CafeImage cafeImage = new CafeImage(file.getOriginalFilename(), cafeImageUrl);
cafe.addCafeImage(cafeImage);
}
// 카페 옵션 생성성
List<CafeOptionType> optionList = requestDto.getOptions();
for (CafeOptionType option : optionList) {
CafeOption cafeOption = new CafeOption(option);
cafe.addCafeOption(cafeOption);
}
cafeRepository.save(cafe);
}
}
package com.eventcafecloud.cafe.domain;
import com.eventcafecloud.cafe.dto.CafeCreateRequestDto;
import com.eventcafecloud.event.domain.Event;
import com.eventcafecloud.user.domain.User;
import lombok.*;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor // 다른곳에서 생성자 못쓰도록 막아둠
public class Cafe {
.. 생략 ..
@OneToMany(mappedBy = "cafe", cascade = CascadeType.ALL)
private List<CafeOption> cafeOptions = new ArrayList<>();
@OneToMany(mappedBy = "cafe", cascade = CascadeType.ALL)
private List<CafeImage> cafeImages = new ArrayList<>();
// @OneToMany(mappedBy = "cafe")
// private List<CafeReview> cafeReviews = new ArrayList<>();
@OneToMany(mappedBy = "cafe")
private List<CafeSchedule> cafeSchedules = new ArrayList<>();
@OneToMany(mappedBy = "cafe")
private List<Event> events = new ArrayList<>();
public Cafe(CafeCreateRequestDto requestDto) {
this.cafeName = requestDto.getCafeName();
this.cafeZonecode = requestDto.getCafeZonecode();
this.cafeAddress = requestDto.getCafeAddress();
this.cafeAddressDetail = requestDto.getCafeAddressDetail();
this.cafeX = requestDto.getCafeX();
this.cafeY = requestDto.getCafeY();
this.cafeInfo = requestDto.getCafeInfo();
this.cafeInfoDetail = requestDto.getCafeInfoDetail();
this.cafePrecaution = requestDto.getCafePrecaution();
this.cafeWeekdayPrice = requestDto.getCafeWeekdayPrice();
this.cafeWeekendPrice = requestDto.getCafeWeekendPrice();
}
//==연관관계 편의 메서드==//
public void addUser(User user){
this.user = user;
}
public void addCafeImage(CafeImage cafeImage){
cafeImages.add(cafeImage);
cafeImage.addCafe(this);
}
public void addCafeOption(CafeOption cafeOption) {
cafeOptions.add(cafeOption);
cafeOption.addCafe(this);
}
}
4. 결론
개발자의 스타일에 따라 코드 구현 방식은 달라질 수 있다. 어떤 방식의 코드 구현을 배우더라도 그걸 익히고 사용하고 이해할 수 있도록 공부하자
파이널 프로젝트에는 형철 튜터님의 방식을 이용하여 구현하였습니다.
두 코드 모두 기본 세팅은 (fetch = FetchType.LAZY)
'프로그래밍 > JPA Project - sparta' 카테고리의 다른 글
타임리프 값 -> 자바스크립트에서 사용하기 (0) | 2022.07.10 |
---|---|
전체 조회 로직 구현 (페이징o) (0) | 2022.07.09 |
top5 조회 로직 구현 (페이징x) (0) | 2022.07.08 |
Spring Security - 페이지, thymeleaf 접근 제한 (0) | 2022.07.04 |
S3 이미지 업로드, 삭제 (0) | 2022.06.29 |