아쉬운점이 대부분이었던 Wisely 클론 프로젝트 회고를 적어보려한다. 팀단위의 프로젝트가 익숙하지 않다보니 나오는 실수나 아쉬움은 당연할지도 모른다. 그러나 프로젝트 시작전 또는 개발을 공부하고부터 나 자신이 가졌던 동기부여가 사라져 있었다는 아쉬움이 정말 크다. 결과에 너무 의존했던 자신을 반성하며 아래 회고를 적어내려가겠다.
🌈 클론 사이트 선정🌈
- 생활용품 정기구독 및 구매 사이트 Wisely
❔ 선정이유 ❓
FE 측면
: 반응형 사이트, carousel, 아코디언, 데이터 가공 (평점별 별개수 핸들링,평점 반올림, 가격 쉼표추가 ) , 쿼리파라미터(페이지네이션, 오더링) 구현 가능
BE 측면
: 일반구매 및 정기구독 조건 구분, 상품 내부 옵션별 데이터 구성, 상품별 평점 계산 시 서브쿼리 사용, 쿼리파라미터(페이지네이션, 오더링), 로그인, 회원가입, 장바구니 담기 및 수량 조절 기능 구현 가능
내가 맡은거 ✋
아닌거 👉
- Login & Sing Up
👉 백엔드와 통신하면서 실시간 유효성 검사
👉 유효성 검사 후 회원가입 실시
👉 JWT 토큰 받아서 localStorage 저장- Main
👉 carousel
👉 카테고리 라우터
👉 인기제품 리스트 멀티 carousel- Product List
👉 품절 제품 disable
👉 카테고리 별 상품 데이터 filter
👉 데이터 정렬(높은 가격순, 낮은 가격순, 판매량순, 평점순)- Product Detail
✋ 상품 옵션 선택시 옵션 폼 생성
✋ 상품 옵션별 수량, 조절, 삭제
✋ 상품 정보 아코디언
✋ 장바구니 담기 POST
✋ 장바구니 담기 후 모달 생성
✋ 장바구니 라우터 (장바구니 이동 버튼 클릭 후 장바구니 이동)
✋ 리뷰 데이터 페이지네이션
✋ 평점별 게이지 bar
✋ Under Bar 스크롤 이벤트 (특정 영역 스크롤시 Under Bar 생성 제거)- Cart
👉 상품 수량, 조절, 삭제
👉 백엔드랑 통신후 실시간 재고 반영
👉 정기 구독 주기 선택
👉 포인트 결제
🌲 브랜치...
- 작은 기능 단위로 브랜치를 각각 판다음 개발이 진행되면 코드리뷰도 쉬워지고 그건 곳 머지가 빠르게 된다. 이런 git-flow방식이 처음이다 보니 세부적으로 더 나누지 못했던 아쉬움이 있다.
🎫 데이터 가공
Wisely 상품은 특정 상품에 옵션이 달려있어 한 상품에 대한 옵션 데이터를 추가로 받아와야 한다.
- 데이터 형식
{ "productDetail": [ { "countRating": "7", "avgRating": "2.8571", "productId": 1, "imageId": 1, "options": "빨강", "optionImg": "url_빨강", "sales": 100, "stock": 0, "categoryId": 2, "productName": "피부에 좋은 세럼", "price": "50000", "description": "트러블을 효과적으로 케어하는 고기능성 세럼", "descImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDdqPX%2FbtrJ0UidvT8%2FXkD6VbIi7XfBVwO8CB4DhK%2Fimg.png", "thumbImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYQjQ%2FbtrJ4FDRA64%2FsJ77YKRN8JmLDVaPlV1u7k%2Fimg.jpg" }, { "countRating": "7", "avgRating": "2.8571", "productId": 1, "imageId": 2, "options": "건성", "optionImg": "url_건성", "sales": 100, "stock": 0, "categoryId": 2, "productName": "피부에 좋은 세럼", "price": "50000", "description": "트러블을 효과적으로 케어하는 고기능성 세럼", "descImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDdqPX%2FbtrJ0UidvT8%2FXkD6VbIi7XfBVwO8CB4DhK%2Fimg.png", "thumbImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYQjQ%2FbtrJ4FDRA64%2FsJ77YKRN8JmLDVaPlV1u7k%2Fimg.jpg" }, { "countRating": "7", "avgRating": "2.8571", "productId": 1, "imageId": 3, "options": "abcd", "optionImg": "www.wesley.com", "sales": 100, "stock": 0, "categoryId": 2, "productName": "피부에 좋은 세럼", "price": "50000", "description": "트러블을 효과적으로 케어하는 고기능성 세럼", "descImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDdqPX%2FbtrJ0UidvT8%2FXkD6VbIi7XfBVwO8CB4DhK%2Fimg.png", "thumbImg": "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYQjQ%2FbtrJ4FDRA64%2FsJ77YKRN8JmLDVaPlV1u7k%2Fimg.jpg" } ],
우선 최종적으로 한 상품의 옵션에 대한 데이터는 이렇게 받아오기로 정했다. 이전까지는 중간중간 데이터가 오는 형식이 바뀌어서 데이터를 할당하는 구조를 여러번 바꿔야 했던게 시간을 많이 잡아먹었다.
이런 사태를 미연에 방지하려면 내가 할 페이지에 대해 어떤 데이터가 어떻게 필요하다 라는걸 미리 문서화 해놓으면 어느정도 해결이 가능할 것 같다.
🛒 옵션별 수량 정보
- 옵션별 수량정보를 변수에 담아야 하는데 받아오는 데이터에 수량에대한 데이터는 없기에 새롭게 key, value를 객체에 추가하는 함수를 이용했다.
const selectObj = product.productDetail setItem([ ...item, Object.assign(selectObj, { quantity: 1, }),
- 옵션 클릭시 옵션 폼 생성
- 옵션 클릭시 selectedItem state에 저장
const selectItem = ({ target }) => { const selectObj = product.productDetail[Number(target.id)]; if (!selectedItem.includes(selectObj)) { setSelectedItem([ ...selectedItem, Object.assign(selectObj, { quantity: 1, }), ]); }
- 수량과 imageId 각각 state에 저장
const [totalCount, setTotalCount] = useState({ total1: null, total2: null, total3: null, total4: null, }); const [imageId, setImageId] = useState({ imageId1: null, imageId2: null, imageId3: null, imageId4: null, });
- 한 상품당 옵션은 최대 4개라는 가정에 이런 코드가 나왔다.
이때당시 코드 구조를 너무 많이 바꿔서 지쳐있는 상태였고 리팩토링을 전혀 진행하지 않았던....- 일단 옵션이 4개라는 가정 말고 하나의 state에서 동적으로 담기도록 구성하면 어땠을까 하는 아쉬움이 있다.
간단하게 움짤을 보면서 처음~끝을 설명하겠다.
시작
이때만큼은 자신감과 의욕이 가득했다.중간
에러에게 폭행당하기 시작 점점 예민해지고 말수가 줄어들었다.
그러나 기분이 태도가 되면 안된다.끝
진짜 눈이 이렇게 된다.
익숙하지 않은 협업 방식, 처음 만난 팀원 그리고 부족했던 소통 탓에 고생을 사서했다. 그 결과 밤을 새서 작업해야하는 일이 생기고, 모두가 머리를 합쳐 해결해야하는 문제들도 생겼다.
- 각자 맡은 페이지가 결국에는 연결되어야 하는데 개발 중간에는 각자 어느정도 진행되었는지 직관적으로 알기가 쉽지않다. 이런 부분들을 미리 미리 스탠드업 미팅에서 공유할 수 있어서 다음 단계 구상이 쉬워졌다.
- 백엔드와 소통은 정말 어려운 부분중에 하나였다. 쿼리문이 어떻게 구성되는지 잘 모르고 설명 해준다고 다 이해하는 것도 아니었기에 우리팀 백엔드분들은 그림 또는 예시를 인용해 알기 쉽게 설명해주었고 이런 방식의 대화가 자주 이루어지다보니 소통도 간단해지고 한가지를 설명하는 시간도 많이 줄어들었다. 이런 부분들을 문서화 한다면 더욱 더 좋은 시너지가 날 것 같다.
💊아쉬운 점💊
- 아쉬운점 대부분을 차지하는건 소통이다.
서로 어느부분이 어떻게 개발되는지 모르는 상황에 중요한건 우리는 이렇게 데이터를 만든다. 내가 프론트단에 전해 줄 데이터 형식은 이렇게 될 것 같다. 또는 프론트에서 내가 맡은 페이지에 어떤 데이터들이 필요하며, 데이터 형식은 이렇게 오면 정말 가공하기 쉬울 것 같다. 라는 식의 대화가 미리 이루어 졌다면, 개발 시간을 좀 더 효율적이게 알차게 분배할 수 있었을 것 같다.
피그마같은 툴을 사용해 내가 맡은 페이지를 명시하고 데이터 형식을 정해둔 공용 문서를 만들면 좋을 것 같다.- 동기부여
프로젝트 시작전에 다졌던 동기부여가 어느순간 사라져있는 나 자신을 보게 되었다.
정신없이 기능 개발을 진행하고 프로젝트가 막바지에 다다랐을 때 코드를 보니 효율적이고 동적인 코드가 아닌 문제상황을 회피하기 위한 회피성 코드구조가 많았고, 결국 내가 쓰고있는 언어, 라이브러리들을 써서 얻을 수 있는 이점들을 살리지 못했다.
특히 Sass같은경우 extend와 mixin을 전혀 사용하지 않고 반복되는 코드를 일일이 적었던 점에 많은 아쉬움이 생긴다.
소통 잘 하면서 2차 화이팅하시죠!