내일배움캠프 프로젝트

백오피스 프로젝트

공부처음하는사람 2024. 2. 13. 21:13

🛵배달 시스템 Backend Server

🎁 프로젝트 개요

  • 개발 기간 : 24.01.22 ~ 24.01.29 (1주)
  • 개발 환경 : Kotlin, Spring Boot, Supabase, PostgreSql
  • 프로젝트 이름 : 배달 프로젝트
  • 프로젝트 설명 : 배달 시스템을 모방한 백엔드 시스템 개발

👩 Team B05

  • 오재영
    • github
    • 역할 - 와이어 프레임, 프로필 관리, 계좌 관리, 인증/인가
  • 박유진
    • github
    • 역할 - ERD, 가게 CRUD, 메뉴 CRUD
  • 김성현
    • github
    • 역할 - ERD, 리뷰 / 리뷰 답글 CRUD
  • 윤승환
    • github
    • 역할 - API 명세, 주문 Flow Chart, 장바구니 / 주문 CRUD
  • 김현득
    • github
    • 역할 - 가게 CRUD 초안 작성

📚기술스택

Backend

  • Spring Boot: 3.2.1
  • Kotlin: 1.9.22
  • Data
    • Spring JPA: 3.2.3
    • QueryDsl: 5.0.0
  • Security
    • Spring Security: 6.2.0
    • JWT: io.jsonwebtoken:jjwt-api:0.12.3
    • Oauth 2.0

DB

collaboration

  • Git, GitHub Issue, Slack

🎈 주요기능

프로필 관리 / 계좌 관리

  • 회원가입/로그인/유저 정보 확인
  • Spring Security 활용
  • 소셜 로그인 기능 추가

가게 관리

  • 일반유저 : 가게 목록 조회/가게 정보 조회
  • 가게주인 : 본인 가게 목록 조회/개별 조회/가게 생성/정보 수정/영업상태 변경(영업중 / 영업중지)
    • 본인이 개설한 가게만 조회 / 생성 / 수정 / 상태변경 가능

메뉴 관리

  • 전체 메뉴 조회/메뉴추가/메뉴수정/메뉴 상태변경(판매중/품절/판매중단)
  • 각각 기능에 ROLE에 따라 인가 처리

리뷰 관리

  • 가게 조회 시 리뷰 정보 함께 조회 / 리뷰 CRUD
  • 자신의 가게에 리뷰 생성 불가
  • 리뷰 답글 기능

장바구니 / 주문

  • 일반유저 : 장바구니 메뉴 추가/제거, 주문 시도(계좌 잔액 검사), 주문 취소
  • 가게주인 : 주문 상태 변경(주문 취소,주문 확정, 조리 완료, 배달 완료)
  • 주문 조회 시 QueryDsl로 동적 쿼리 활용

🚩프로젝트 설정

  • DB 설정 환경변수 추가
  • SPRING_DATASOURCE_URL=#{DB 주소} ex) SPRING_DATASOURCE_URL=jdbc:postgresql://db.jrsvhsuhbgbvhmnyiovm.supabase.co:5432/postgres?user=postgres&password=#{password}
  • application.yml 파일에 google client 설정 추가
  • spring: security: oauth2: client: registration: google: client-id: #{client-id} client-secret: #{client-secret} redirect-uri: http://localhost:8080/login/oauth2/code/google scope: - profile - email

🏆 프로젝트 산출물


회고

 

이번 프로젝트에서도 댓글 관련 CRUD를 맡았다.
크게 문제가 있었던 부분은 없었는데, 리뷰에 대한 가게 사장의 답글의 매핑이 내 속을 썩였다.

일단 문제가 있었던 부분은, 리뷰와 답글을 OneToOne 1:1 단방향 매핑을 하기로 팀 회의에서 결정되었다.
리뷰는 주문당 1개밖에 작성할 수 없지만, (이러한 로직은 구현하지 못했으나, 추후에 수정을 한다면?) 가게 사장의 답글도 하나만 작성하게끔 했기때문에
1대1 매핑을 했다. 사실 나는 가게 사장의 경우엔 여러개의 답글을 달 수 있어야지 않을까? 라고 생각했으나, 1:1 매핑을 해보지 않았기 때문에 그냥 ok했다. 그런데 여기서 너무 헷갈렸던 부분이 있었는데,

  1. 리뷰가 삭제된다면 가게 사장의 답글도 삭제가 되게끔 설정해야한다.
  2. 1:1 매핑이라면 서로 동등한 관계가 아닐까? 연관관계의 주인을 어떻게 설정해야 할까?

위 두가지가 첫번째로 고민되었던 부분이다.
단순히 생각한다면 1:1이라면 서로 동등한 관계일텐데, 누가 주인인지 어떻게 설정해야할까에 대해 고민을 했다.
사실 강의에선 주로 일대다 관계에 대한 내용이라, 1:1에서 mappedby를 사용할 수 있는지 몰랐었고,
OneToMany쪽에 mappedby를 사용해서 주인을 설정해주었는데, OneToOne에선 어디에 작성을 하는게 맞을까 하며 고민을 했다.
사실 지금와서 보면 굉장히 단순한데.. 그때 당시엔 생각을 너무 많이 하다보니 당연한 것에 대해 의심이 들기까지 시작한 단계였다.

뭐 고민했던게 허망할정도로 review Entity에서

 

    @OneToOne(mappedBy = "review", fetch = FetchType.EAGER, orphanRemoval = true)
    var replies: ReplyByReview?
// review Entity

    @OneToOne
    @JoinColumn(name = "review_id")
    var review: Review,
// reply entity

 

이런식으로 작성을 해 주인을 정해주었다.

 

FetchType.EAGER을 사용한 이유는 내가 fetchType에 대한 글을 읽어보았는데, eager은 그 리뷰에 한번 더 클릭해서 답글을 보는 것이 아닌, 처음부터 리뷰와 답글을 한꺼번에 보여준다 라고 이해 했기때문에 EAGER을 사용했다.

 

강의에선 보통은 lazy를 많이 쓴다고 한다. 그래서 왜 eager를 사용했냐는 질문을 많이 들었었다. 조금 개발 공부를 하다 오신분께

설명을 해드렸는데, 그 개념 자체는 맞긴 한데 lazy로도 내가 원하는 방식으로 설정할 수 있다고 한다.

n+1을 방지하기 위해 lazy를 사용하는 것인데, 이 부분에 대한 지식이 아직까진 확 와닿지 않는다.

fetch에 대해 공부를 더 해봐야 할것같다.

 

연관관계를 수정한 후에 또 닥쳐온 일이, 답글삭제가 안되는 에러가 생겼다. 고아객체를 삭제하기 위해 

cascadeType을 ALL, orphanRemoval = true 이렇게 작성 한 후 실행해봤는데, 분명 swagger에선 http요청이 정상적으로 나타나는데

db에선 댓글이 삭제가 되지 않는것이다. 원인이 무엇일까 한참을 찾고 물어보며 다닌결과

cascadeType이 ALL일 경우에, 답글은 부모댓글인 리뷰의 생명주기를 따라가다보니 답글만 삭제가 안되는 일이 발생한 것이다.

 

사실 강의에선 Cascade와 orphanremoval이 항상 세트로 따라다녀서, 반드시 같이 써야되는 줄 알았다.

당연히 리뷰를 삭제하면 답글도 삭제가 되어야하고, 답글도 독자적으로 삭제를 할 수 있어야하는데 cascade때문에 안되는 것을 확인했다.

cascade를 지우고 orphanremoval만 작성하니 내가 원하는대로 답글만 따로 삭제할 수 있었다..

 

진짜 이거때문에 잠도 못자고 이거해보고 저거해보고 디버깅도 서투르지만 이것저것 안해본게 없는데, 다른조원분이 정말 친절하고 

감사하게도 같이 머리를 맞대고 고민해주셔서 빨리 해결할 수 있었다. 그런데 이게 해결되니 너무 기쁜것임ㅋㅋ! 동욱님께 또 감사감사.

 

이번 프로젝트에도 무언가를 하나 배워간다는게 참 좋았다. 다음엔 무엇을 배우게 될까 궁금하다!