토이 프로젝트2 : 여행 여정을 기록과 관리하는 SNS 서비스 2단계 회고
🌟 지난 프로젝트의 피드백
✔️ MVC 패턴이 잘 지켜지지 않았다. -> DDD 구조를 바탕으로 역할을 명확하게 나누어 진행.
✔️ 변수와 메소드 이름이 불분명 및 통일이 안됐다. -> 리팩토링 과정에서 꼼꼼하게 검토
🌟 이번 프로젝트의 목표
- JPA의 이해
- 코드 리뷰 적극적
- DDD 설계의 이해
🌟 규칙
1) 깃 플로우 브랜치 전략 사용.
2) 코드 컨벤션 - 구글 코드 컨벤션
📌 기능 소개
위의 이미지대로 기획을 하였고, 밑에처럼 기능을 나누어 역할을 분담했다.
내가 맡은 파트는 '여정 n개 삽입' 과 '여정 n개 삭제' 였다.
📌 ERD
create_at : 레코드 생성 날짜
updated_at : 레코드 마지막 수정 날짜
deleted_at : 레코드 삭제 처리 시 실제로 삭제가 아닌 삭제 날짜
모두 자동 완성이다.
여행 (Trip) : 여정 (Itinerary) = 1 : n 관계
여정 : 이동/숙소/체류 = 1 : 1 매핑 이자 부모 - 자식 관계
여기서 조금 많이 수정됐지만, 수정권한이 없어서 일단 초기 모델로 가져왔다.
여행과 여정을 1:n으로 하였고, 여정들의 종류에 따라 1:1 테이블을 만들었다.
1:n 관계는 여러번 짜봤는데 1:1은 처음 해봐서 진짜 힘들었다 ㅠ
📌 Entity
▶️ Trip (여행) : Itineraty List 를 갖는다.
@Entity
@EntityListeners(AuditingEntityListener.class)
@Getter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Trip extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonIgnore
private Long tripId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "memberId")
@JsonIgnore
private Member member;
@Column(nullable = false)
private String tripName;
@Column(nullable = false)
private LocalDate startDate;
@Column(nullable = false)
private LocalDate endDate;
@ColumnDefault("true")
private Boolean isDomestic;
@ColumnDefault("false")
private Boolean isDeleted;
@OneToMany(mappedBy = "trip",
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
List<Itinerary> itineraryList = new ArrayList<>();
//CascadeType.ALL -> 상위 객체 작업 하위객체 모두한테 전파.
//fetch = FetchType.EAGER -> 실제 조회할 때 한방 쿼리로 다 조회.(itinerary을 사용할 때 쿼리 안나가도 된다.)
//orphanRemoval = true -> 부모가 자식에 대한 참조를 끊을 때, 참조가 끊어진 자식 Entity(고아 객체)를 DB에서 삭제하도록 설정할 수 있다.
//우리는 soft delete 이므로 굳이 필요는 없음.
▶️ Itinerary (여정)
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
@SuperBuilder
public class Itinerary extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long itineraryId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tripId")
private Trip trip;
@Column(nullable = false)
private String itineraryName;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private ItineraryType itineraryType;
@Column(nullable = false)
private Integer itineraryOrder;
@ColumnDefault("false")
private Boolean isDeleted;
▶️ Movement (이동)
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Movement extends Itinerary {
@Column(nullable = false)
private LocalDateTime departureDate;
@Column(nullable = false)
private LocalDateTime arrivalDate;
@Column(nullable = false)
private String departurePlace;
@Column(nullable = false)
private String arrivalPlace;
@Column(nullable = false)
private String transportation;
▶️ Lodgement (숙소)
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Lodgement extends Itinerary {
@Column(nullable = false)
private LocalDateTime checkIn;
@Column(nullable = false)
private LocalDateTime checkOut;
▶️ Stay (체류)
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Stay extends Itinerary {
@Column(nullable = false)
private LocalDateTime departureDate;
@Column(nullable = false)
private LocalDateTime arrivalDate;
📌 잘한 점
1. JPA 를 이해할 수 있었다.
특히 슈퍼 - 서브 타입이 이해하기 어려웠지만, 계속적으로 엔티티를 수정해 나가면서 이해할 수 있었다.
2. 리팩토링에 신경 썼다.
특히 🌟 팩토리 패턴 🌟을 이용해 if문을 없애면서 가독성 있는 코드를 짤 수 있었다.
[spring-boot] [리팩토링] if 문 없이 객체 만들어보자 - 팩토리 패턴, 상속관계 엔티티 생성
이 게시글은 pjh3749님의 게시글을 참고하였습니다. 📌 문제 상황 프로젝트를 하던 중 .. 상속관계에 있는 엔티티들을 매번 빌더로 만들어야하는 상황이었다. 방법이 없어 보여 switch 문 사용해서
nebulaisme.tistory.com
3. 깃에 능숙해졌다
충돌 나도 두렵지 않아졌다.
📌 아쉬운 점
1. 여정을 Day별로 출력하지 못해 아쉬웠다.
이건 프로젝트가 끝나고 나서 트리플 이라는 앱을 사용해보고 느꼈다.
2. java docs를 사용하지 못해 아쉬웠다.
자바 독스 사용한 팀이 대부분이었는데, 우리팀은 적용하지 못해 아쉬웠다.
📌 개선 방안
1. 트리플 앱을 보며, 여정 부분을 Day별로 출력하고 싶다.
- request, response 별로 나누어서 처음부터 다시 설계를 해야할 것 같다.
2. JPA를 더 공부하여 조금 더 깔끔한 엔티티 구조를 갖고 싶다.
슈퍼 - 서브 타입을 사용하면서 req 와 res의 필드 이름이 통일되지 못한 경우가 발생했다.
이 부분을 고칠 것이다.