
프로젝트의 주제
- 여행 상품 리스트를 보고 장바구니에 저장할 수 있는 사이트 구현
프로젝트의 요구사항
- 목데이터를 사용해서 과제를 진행
- 별도의 디자인 명세는 주어지지 않는다.
- 요구사항을 충족시키는 선에서 지원자의 판단하에 UI를 구성해주면 된다.
- 필수 기술: chakra-ui, (emotion)
- 필수기능에 부합하는 디자인을 chakra-ui를 이용하여 구현해주세요
- chakra-ui로 할 수 없거나 부가적인 스타일링은 emotion을 이용해 주세요
프로젝트 진행 전 신경쓴 점
기술스택 논의
- 프로젝트에 들어가기에 앞서 각각의 기술스택들은 어떤 것들이 있는지
- 그리고 그것들중 왜 이것을 사용했는지를 문서화하여 정리했다.
- 이를 통해 기술을 도입할 때 그리고 사용할 때 중요한 핵심인 “왜 사용하는가?”를 모두가 이해하고 진행하기에 기술스택을 사용함에 있어 어려움이 감소되었다.
- 이 과정에서 기술스택이 꼭 유행하는 것이 아닌 주어진 프로젝트에 맞는 기술스택을 사용해야 한다는 것을 다시금 느꼈다.

요구사항 분석
- 프로젝트에 들어가기에 앞서 요구사항들을 분석했다.
- 무턱대고 개발하는 것이 아닌 모두가 머리를 모아 먼저 로직을 정리해보는 시간을 갖었다.
- 이를 통해 팀원들이 코드리뷰에서 들어가는 시간을 조금이나마 절약할 수 있었다.

프로젝트에서 도입된 주요 기술
컴포넌트 구조 및 폴더 구조

src
┣ 📂components
┃ ┣ 📂common
┃ ┣ 📂OrderSummary
┃ ┣ 📂Reservation
┃ ┗ 📂Travel
┣ 📂constants
┣ 📂hooks
┣ 📂mocks
┣ 📂pages
┣ 📂providers
┃ ┣ 📂Reservation
┃ ┣ 📂Travel
┣ 📂router
┃ ┣ 📂loaders
┣ 📂types
┣ 📂utils
┣ 📜App.tsx
┣ 📜main.tsx
┗ 📜vite-env.d.ts
Design Pattern
- 우선 페이지를 두가지로 나누었다.
- 여행 상품 리스트를 볼 수 있는 Main페이지
- 저장한 여행아이템을 확인할 수 있는 Reservations페이지
- Main페이지는 다시 Filter와 TravelItemBox로 나뉜다.
- Filter는 다이어그램에는 안나왔지만 자식 컴포넌트로 FilterCheckboxGroup을 하나 뒀다.
- FilterCheckboxGroup는 chakra UI로만 구성되어져 있다.
- 이 chakra에서 제공하는 MenuItemOption이 check되면 MenuOptionGroup의 onChange에 바인딩된 함수를 통해 filter를 구현한다.
- TravelItemBox는 TravelItem으로 구성되어지고
- TravelItem은 ReservationButton을 자식으로 갖는다.
- 또한 QuantityButton도 자식으로 갖는데 이 버튼은 장바구니에서도 쓰이므로 공용컴포넌트에 넣어 재사용성을 높인다.
- Reservations페이지는 ReservationBox와 OrderSummary로
- ReservationBox는 ReservationItem으로
- ReservationItem은 QuantityButton과 DeleteButton으로 나뉘는데
- 이때 이 DeleteButton은 나중에 또 쓰일 수 있으므로 common 폴더에 넣어 놓는다.
- QuantityButton은 Main에서 쓰인 바로 그 버튼이다.
- 각 컴포넌트의 구성을 보면 작은 컴포넌트들이 모여 하나의 컴포넌트를 이루며 그 컴포넌트가 더 큰 컴포넌트를 구성해나아가는 것을 볼 수 있다.
hooks
- 비즈니스로직들을 따로 hook으로 빼서 관심사분리를 하였다.
- Providers를 통해 전역적으로 context에 있는 state들을 props drilling 걱정없이 사용할 수 있게 하였다.
다중 필터링 기능 구현
- 장소 필터와 가격 필터가 혼합되어 있는 다중필터링을 구현해야한다.
- 설계의 주안점은 다음과 같다.
- 우선 필터 자체의 기능만을 따로 filter함수로 만들고 utils로 빼놔서 추상화를 해놓는다.
- 이를 통해 관심사를 분리하고 유지보수를 쉽게 한다.
- 필터기능만 있기에 필터가 하나만 있을 때 두 개 있을 때 아예 없을 때 등등의 조건들을 비교적 쉽게 다룰 수 있다.
- filter함수에서 return되는 filteredData가 우리가 렌더링하는 item 목록이다.
- 필터가 더 늘어나는것을 대비하여 전역적으로 필터조건과 그 상태를 다루는 Reducer를 만든다.
- 이를 통해 필터가 더 늘어나더라도 빠르게 확장을 할 수 있다.
Filter컴포넌트에서 필터링이 된 데이터의 상태를 변경시키고, 이 데이터를 가지고 TravelItemBox컴포넌트에서 리렌더링을 해야하기 때문에 멀리 떨어진 컴포넌트에서도 상태를 공유할 수 있도록 Context API를 사용한다.
장바구니 기능 - localStorage사용
- 이 부분은 내가 처음에 만들때 실수를 했던 부분인데
- 나의 경우 localstorage를 진짜 저장소처럼 사용하여
- 장바구니를 만들때 +버튼을 누르면 localstorage의 숫자가 올라가고
- 이 localstorage의 데이터를 기반으로 다시 렌더링을 하는 방식을 사용했다.
- 지금 생각하면 왜 그랬는지, 무슨 생각으로 그랬는지 모르겠다..
- 이 장바구니의 localstorage방법은 그냥 저장을 하고 Reservations 최상위 부모페이지에서 한 번만 그냥 함수로 불러들이는 거다.
- 그리고 렌더링할때는 기존의 방식처럼 context를 사용해 전역적으로 수량 상태를 관리하면 된다.
- 이렇게 하면 유저가 새로고침했을 때만 localstorage를 반영하여 값이 계속 저장되게 할 수 있다.
- 물론 context에서 꾸준히 localstorage에서 set하는 함수를 적용해야한다.
- 즉, context와 localstorage를 계속해서 update하되 렌더링은 context에 있는 것을 하고
- useEffect를 이용해 리렌더링시에는 localstorage에 있는 데이터를 context로 업데이트하면 된다.
context API를 이용하여 props drilling 해결
QuantityButton 컴포넌트에서 수량을 변경하는 이벤트가 발생하여 상태를 변경시킬 때, 멀리 떨어져 있는 컴포넌트인 OrderSummary가 리렌더링 되어야 하기 때문에 Context API를 활용했다.
ReservationProvider를 구현하였고, Reservations 페이지의 하위 컴포넌트로 state와 dispatch의 스코프를 한정했다.
ReservationItem 컴포넌트에서는 dispatch 함수를 호출하여 수량 변경 버튼을 클릭하면 reducer를 통해 상태를 변경시킬 수 있게 됩니다. 그러면 OrderSummary 컴포넌트가 리렌더링되며 상품의 총 수량과 총 금액을 업데이트 할 수 있다.
끝으로
- 이번 과제를 하면서 필터링이라는 로직에 대해 깊게 생각해보는 경험을 했다.
- 필요에 의해 라이브러리를 써야 하며 그래서 꼭 redux를 사용하지는 않아도 된다는 것을 알았다.
- 컴포넌트 구조를 설계함에 있어 좀 더 꼼꼼한 설계와 더불어 단단한 이해를 통해 구조가 중간에 바뀐다 할지라도 유동적으로 잘 바꿔야 한다는 것을 알았다.