사람들이 여행을 갈 때, 가서 뭘 할지, 비용은 얼마인지 등 여러 고민을 하게 된다. 바쁜 현대인의 이런 고민을 줄여주기 위해 플랫폼 안에 잘 짜인
체험 상품을 보고 간단하게 예약할 수 있는 웹 서비스 글로벌 노마드
2024.07.03 ~ 2024.07.18
백엔드 api swagger, 기획 notion, 시안 Figma를 받아서 프론트엔드 5인이 개발하였다.
전체적으로 사용자 입장에서 더 편리하게 이용할 수 있도록, Tab과 Enter 키만으로도 사용이 가능하도록 노력했다. UI의 접근성과 직관성을 높여, 모든 사용자가 손쉽게 기능을 사용할 수 있도록 하였다. 또한, 유사 서비스를 찾아보면서 어떤 기능이 사용자에게 유용한지 알아보고 이를 반영하였다.
Modal과 Popup
컴포넌트로 모달과 팝업을 구현하여 다양한 곳에서 일관된 디자인과 기능을 유지할 수 있도록 하였다. 모달의 닫기 버튼(x)은 오른쪽 상단에 배치하되, Tab 키를 사용하는 사용자의 접근성을 고려하여 코드에서는 맨 아래에 선언했다. 또한, Recoil을 사용하여 전역적으로 출력하도록 구현하였다. 이를 통해 애플리케이션의 여러 곳에서 모달과 팝업을 일관되게 사용할 수 있도록 하였다.
Pagination
후기에서 쓰이는 페이지네이션을 컴포넌트로 만들어서 여러 곳에서 사용할 수 있게 하였다.
Footer
react-slick
라이브러리를 사용하여 슬라이드가 되도록 하였다.Kakao Map API
를 활용하여 체험 장소를 지도에 표시하는 기능을 구현했다. 기획에는 없었지만, 사용자의 편의를 고려하여 사용자의 현재 위치와 목적지 간의 거리를 계산하고, 주소 복사 기능을 추가했다.Geolocation API
를 통해 사용자의 위치를 가져와 Haversine 공식을 사용하여 체험 장소까지의 거리를 계산했다. 이를 통해 사용자는 체험 장소까지의 거리와 이동 시간을 예측할 수 있게 되었으며, 주소 복사 버튼으로 위치 공유와 다른 지도 앱으로 주소를 쉽게 옮길 수 있도록 했다.react-calendar
와 date-fns
라이브러리를 사용하여 달력에서 예약 가능한 날짜를 직관적으로 확인할 수 있게 하였으며, 날짜별로 체험 시간을 출력하고 인원 수를 선택하여 예약이 가능하도록 하였다. 예약 신청 시 오류 처리는 react-query
의 onError를 이용하여 이벤트 분기 처리를 하였다.fullCalendar
라이브러리를 사용하여 예약 현황을 캘린더 형태로 시각화하였다. 캘린더에서는 예약, 승인, 완료 라벨을 통해 일정을 쉽게 확인할 수 있도록 하였다. 날짜를 클릭하면 예약 현황 모달이 출력되고, 모달에서 해당 날짜의 체험 시간을 드롭다운으로 선택할 수 있으며, 특정 시간 선택 시 예약 승인 및 거절 기능을 제공하였다. 승인 시 신청 이벤트가 승인 탭으로 이동하고, 나머지 신청 이벤트는 자동으로 거절 처리되어 거절 탭으로 이동하게 하였다.예약 후 체험이 완료된 경우, 사용자가 후기를 작성할 수 있도록 모달을 통해 별점과 후기를 입력할 수 있게 구현했다. 별점 기능에서는 onMouseEnter
, onMouseLeave
이벤트와 hover 처리를 활용하여 UI적으로 이해하기 쉽게 구현하였고, 사용자가 직관적으로 별점을 선택할 수 있도록 했다.
사용자 편의성을 향상시키기 위해 Recoil
과 LocalStorage
를 활용하여 전역적으로 다크모드 테마를 구현하였다. 이를 통해 사용자가 선택한 테마는 애플리케이션 전체에서 일관되게 유지되며, 브라우저를 닫았다가 다시 열어도 선택한 테마가 계속 적용되도록 하였다.
![]() | ![]() |
---|
체험 예약 부분과 예약 현황 페이지의 캘린더들이 프로젝트 초반에 나를 비롯한 팀원들에게 두려움의 대상이었다..... 남들이 어려워하는 부분을 도전하고 해결하는 경험을 갖고 싶어서 두 캘린더 모두 내가 맡아서 작업하였다..!!!
체험 예약 캘린더는 날짜 선택만 하면 되는 거라 비교적 간단할 거 같았지만, 캘린더 스타일을 커스텀 하는 부분이 생각대로 되지 않았다. 처음에는 react-datepicker
를 사용했는데 스타일 지정이 되지 않아서 react-calendar
로 변경하였다. 하지만 이 라이브러리도 커스텀 디자인이 css로만 가능하여서.. tailwind를 사용하는 프로젝트에서 css 파일을 추가해서 디자인할 수밖에 없었다... 그래도 선택 가능한 날짜도 직관적으로 보이도록 반영하였고, 만족스러운 결과를 얻을 수 있었다.
![]() | ![]() |
---|
예약 현황 페이지의 캘린더.. react-calendar
로 작업할 수 없었다. 라벨을 날짜에 띄울 수 있는 캘린더 라이브러리를 열심히 구글링해서... fullcalendar
라이브러리를 사용하기로 했다! 이 라이브러리 역시... tailwind로는 스타일 지정이 되지 않았고... styled-components를 사용해서 스타일 컴포넌트로 캘린더 컴포넌트를 감싸서 스타일 지정을 할 수 있었다...
상태 라벨 출력 부분은 도저히 모르겠어서 베프(gpt)의 도움을 많이 받았다 I♥GPT .. 풀캘린더는 캘린더의 본문? 각 날짜의 칸을 events라는 props로 지정할 수 있었다. api에서 예약 상태 개수를 받아와서 개수에 따라서 이벤트를 push하도록 구현했다.
const events =
data?.flatMap((item: getMyMonthScheduleResponse) => {
const { date, reservations } = item;
const events = [];
if (reservations.completed > 0) {
events.push({
title: `완료 ${reservations.completed}`,
start: date,
classNames: ['bg-var-gray3 text-var-gray8'],
});
}
if (reservations.pending > 0) {
events.push({
title: `예약 ${reservations.pending}`,
start: date,
classNames: ['bg-var-blue3 text-white'],
});
}
if (reservations.confirmed > 0) {
events.push({
title: `승인 ${reservations.confirmed}`,
start: date,
classNames: ['bg-var-orange1 text-var-orange2'],
});
}
return events;
}) || [];
(만들고 보니 깔끔한 UI가 참 마음에 든다!!)
이 캘린더가 요구하는 복잡한 내용은 이게 다가 아니다.. 각 날짜 클릭 시 현황 모달도 출력해야 한다.... 클릭 이벤트는 dateClick props를 사용하여 openModal이 선언된 핸들링 이벤트를 사용하였다.
const handleDateClick = (arg: DateClickArg) => {
const newDate = new Date(arg.dateStr);
setModalDate(newDate);
openModal({
title: '예약 정보',
hasButton: false,
content: (
<ReservationModalContent
selectedDate={newDate}
activityId={activityId}
/>
),
});
};
또... 모달을 open하는게 다가 아니였다... 모달 안에 신청, 승인, 거절 탭으로 화면이 나뉘고... 신청 탭에서 체험 시간 목록을 출력하고 특정 시간 선택하면 해당 시간의 예약 목록을 출력하고 특정 예약을 승인하거나 거절하도록 버튼을 추가하고, 한 예약을 승인하면 나머지는 자동으로 거절처리가 되도록 해야 했고.... 버튼 클릭 시 승인, 거절 탭에 바로 반영이 되도록 해야 했다..... 매우 복잡한 기획이었다. 로직을 정확히 이해하고, 다음 이벤트를 고려해 컴포넌트를 구현해야 하는 점이 요구되었다. 그래도 이 과정 덕분에 시안을 꼼꼼히 분석하고, 각 요소가 어떻게 연결되어 처리되는지에 대한 깊은 이해를 얻을 수 있었다.
팀원들과 소통이 너무 잘 되어서 작업하는데 수월하였다. 1~6시를 코어 타임으로 정해두고 디스코드로 음성채널에 접속한 상태로 작업을 하면서 이슈가 생기면 바로 의논을 할 수 있던 점이 좋았다. 또한, 팀원 별 PR 리뷰어를 지정해두고 리뷰 시간도 정해두니까 merge가 제때되고, 꼼꼼히 리뷰하게 되어서 좋았다.
저번 프로젝트 때 사용했던 build CI를 프로젝트 초반에 세팅해두었더니 배포 시 정말 편리했다. 프로젝트 작업을 하다보면 build error는 확인을 잘 안하게 되는 점이 해결되는 참 좋은 방법인 것 같다.