저스트코드 부트캠프 2차 프로젝트 회고 #Justbnb

Benzy·2022년 5월 29일
3

Wecode Fullstack

목록 보기
3/4
post-thumbnail

Justbnb 프로젝트 요약은 사진 한 장으로 가능. 2차 프로젝트 회고도 열정있게 레츠기릿🔥




1. 프로젝트 소개

✈ 클론 사이트 : Airbnb

✈ 사이트 소개 : 세계 최대의 숙박 공유 서비스를 제공하는 사이로 자신의 방이나 집, 별장 등 사람이 지낼 수 있는 모든 공간을 임대할 수 있다.

✈ 저스트비앤비 Github : 🔥 클릭!

🗓 진행 기간 : 2022.04.18 ~ 2022.04.29 (12일)

🙋 참여 인원 :

Front

Back

좋은 팀원분들과 프로젝트를 하게 되어서 정말 행복했다❤️‍🔥
2차 프로젝트여서 확실히 1차 프로젝트 때보다 티켓 배분이나 DB 모델링 관련 결정이 빨리 이루어졌다는 걸 느꼈다. 또한 이번 프로젝트에서 우리 팀의 제일 자신있는 점이라고 한다면 팀워크가 좋았다고 생각한다. 좋은 팀 분위기를 만들 수 있었던 큰 이유는 꾸준히 진행되었던 회의인 것 같다. 매일 데일리 미팅을 빠지지 않고 진행하면서 서로의 상황을 계속해서 공유했고, 스프린트 미팅을 통해서 프로젝트의 현재 진행 상황을 알고 목표를 상기시키며 좋은 결과물을 만들고자 했다. 프로젝트 개발단계에 들어가고 나면 워낙 바쁘다보니 프로젝트 전반적인 진행상황을 고려하지 못할 수도 있는데, 혜원님이 진행상황을 계속 체크해주시고 전체적인 프로젝트 흐름을 관리해주셔서 프로젝트가 수월하게 진행될 수 있었다고 생각한다! (배울 점 진짜 진짜 많은 혜원님 멋져요😍❣️)

🧑‍💻 사용된 기술

  • Language : JavaScript
  • Front-end : React
  • Back-end : Node JS
  • DataBase : MySQL

🗒 DB Diagram

모두 같이 고민하며 짠 DB 구조. 구조를 계속해서 고민해보며 서비스에 대해 관찰하고 분석할 수 있었다.




2. 구현 기능

시연 영상 : https://www.youtube.com/watch?v=RI3BDl8vvxU

구현 기능 요약

  1. Detail Page UI 및 기능
  • useLocation 훅 사용해서 List page 각 List들과 연결
  • 이미지 클릭 시 모달창 구현 후 이미지 배열 순서대로 렌더링
  • 숙소 편의시설 fetch로 받아온 정보에 따라서 조건부 렌더링
  • 모달 외부 영역 클릭 시 모달창 닫힘 구현
  1. React DatePicker 라이브러리 사용 캘린더 구현
  • 위시리스트 저장 유무에 따라서 텍스트, 아이콘 조건부 렌더링 구현
  • 두 개의 캘린더 중에 하나라도 데이터 변화 발생 시 useState와 props를 이용해 연동될 수 있도록 기능 구현
  • 날짜 데이터 변화 발생 시 숙박 일수 및 가격 계산 후 렌더링
  1. 카카오맵 API를 사용한 지도 기능 구현
  • fetch로 받아온 위도 경도를 사용해 숙소 위치를 마커로 나타내도록 구현
  • 이미지 마커에 마우스 hover 시 info window를 사용해 안내문이 나타나도록 구현
  • 마커 주변을 표시할 수 있도록 커스텀 도형을 통해 구현
  1. WishList 기능 구현
  • 로컬 스토리지에 저장된 토큰 유무로 로그인 확인 후 로그아웃 상태 시 로그인 모달창이 띄워지도록 기능 구현
  • 위시리스트 저장 유무에 따라서 텍스트, 아이콘 조건부 렌더링 구현
  1. 숙소 예약 기능 구현
  • 로컬 스토리지에 저장된 토큰 유무로 로그인 확인 후 로그아웃 상태 시 로그인 모달창이 띄워지도록 기능 구현
  • 숙소 호스트와 게스트 계정이 동일할 시 예약이 불가능하도록 기능 구현


팀 프로젝트 첫 회의 날에 프로젝트 사이트를 고를 시에 많은 의견이 있었지만, 가장 많은 팀원들이 택했던 에어비앤비로 프로젝트를 진행하게 되었다. 워낙 기능이 많은 사이트이기 때문에, 기능 구현을 다 할 수 있을까하는 걱정도 있었지만, 2차 프로젝트이기 때문에 우리의 도전 정신을 일깨워 줄 수 있는 사이트를 고르기로 했다.

✔️ Detail Page 전체 모습

티켓 배분 회의 결과, 나는 디테일 페이지를 맡게 되었다.
1차 프로젝트 때 헤더를 맡았기 때문에 메인 페이지를 제외하고 맡고 싶었고 지도와 달력 관련 기능을 같이 구현해 볼 수 있다는 점에서 디테일 페이지를 선택했다.

1차 프로젝트 때는 작은 컴포넌트 단위로 티켓을 배분했는데, 이번에는 페이지 단위로 배분을 진행했기 때문에 맡은 페이지의 완성도를 높게 만들고 싶었다.

1. 메인 이미지 클릭 시 모달 나타내기

const {imageUrlArray} = props;
const [arrayIndex, setArrayIndex] = useState(0);

<PrevButton
	onClick={() => {
      setArrayIndex(arrayIndex -1);
    }}
    disabled={arrayIndex === 0 ? 'disabled' : null}
/>
<ImgWrapper>
      <img alt ="home" src={imageUrlArray[arrayIndex]} />
</ImgWrapper>
<NextButton
	onClick={() => {
    	setArrayIndex(arrayIndex + 1);
    }}
    disabled={arrayIndex === 4 ? 'disabled' : null}
/>

현재는 클릭한 이미지에 관계없이 메인 이미지부터 슬라이드가 시작되는데, 클릭된 이미지의 순서부터 슬라이드가 시작되도록 추가 구현을 진행할 예정이다.

2. 지도에 숙소 위치 표시

카카오맵 API를 이용했다. 지도 구현이 엄청 어려울까봐 걱정했는데, 공식문서가 너무 잘되어있어서 크게 어려움을 겪지 않았다.

지금은 Javascript 문법 그대로를 사용하고 있는데, 멘토님이 리액트에 맞게 포팅한 라이브러리를 알려주셔서 라이브러리를 사용해서 리팩토링을 진행하려고 한다.

참고링크

3. React DatePicker 사용해서 날짜 선택하기

예약 기능에 빠질 수 없는 달력과 관련 기능을 구현했다. 공식문서가 잘되어있고, 가장 범용적으로 사용된다는 점에서 선택했다.

// Detail.js
const [startDate, setStatrDate] = useState(new Date());
const [endDate, setEndDate] = useState(null);
const [selected, setSelected] = useState(null);

const [dateDeleted, setDateDeleted] = useState(false);
const [dateDiff, setDateDiff] = useState(0);

useEffect(() => {
  setSelected(startDate);
}, [startDate]);
useEffect(() => {
  setSelected(endDate);
} [endDate]);

// 캘린더 중 한 곳에서 날짜 선택 시 렌더링 된 후 처음으로 선택된 날짜가 계속 남아있는 오류 해결

가장 오랜 시간 오류와 싸우게 만들었던 캘린더 구현 부분이다. 1차 프로젝트에서는 라이브러리 사용을 자제하는 것이 목표 중 하나였기 때문에 라이브러리를 이용한 구현 부분에서 시간을 많이 쏟게 될 줄은 몰랐다..🥲

가장 상위 파일에서 state를 props를 통해 하위 컴포넌트로 넘겨주는 방식으로 구현하면 두 개의 캘린더 컴포넌트 중 한 컴포넌트만 변화가 생겨도 연동이 될 거라 생각했다. 실제로 구현을 했을 때, 체크인 날짜와 체크아웃 날짜는 동시에 선택이 잘됐지만 클릭한 컴포넌트가 아닌 다른 컴포넌트에서 날짜 하나가 계속 선택되어 있는 오류가 있었다.

렌더링 후 처음으로 선택한 체크인 날짜가 계속해서 남아있고, 새로운 달이 렌더링 될 때는 오류로 선택되어 있던 날이 사라졌다. 혼자서 혹은 팀원들에게 도움을 요청했지만 해결할 수 없었고 결국 멘토님의 도움을 받아 selected를 다루는 함수를 통해서 오류를 해결 할 수 있었다.


// CalendarModal.js
let temp = useRef(end);
// 값이 변해도 렌더링이 일어나지 않음

const handleClose = useRef(() => {
  temp.current !== end && end
  	? setCalendarModalOpen(false)
  	: setCalendarModalOpen(true);
  temp.current = end;
}, [end]);

// 체크아웃 날짜 선택 시 모달이 닫히도록 구현
// end 값이 변할 시 모달이 닫히도록 구현하면 모달이 바로 닫히는 오류 해결 
// - end 값이 초기값으로 설정되기 때문

다음으로 많은 시간을 쏟았던 기능은 모달 안에 있는 캘린더에서 체크아웃 날짜 선택 시 모달이 닫히도록 구현하는 기능이었다. end 값이 변할 시에 닫히도록 시도해보았었지만 체크아웃 날짜가 선택되어 있는 상황에서 모달이 다시 열릴 때 초기값에서 체크아웃 날짜로 값이 변화되기 때문에 모달이 열리자마자 닫히게 되었다.

현재 end값의 이전 상태값을 이용할 수 있다면 문제를 해결할 수 있을 것이라고 생각했고 usePrevState를 사용해보려고 했지만 어려움을 겪고 있던 중에 혜원님께서 useRef를 통해서 변수를 만들 수 있다는 것을 알려주셨고, 위의 코드와 같은 방식으로 해결할 수 있었다.

참고 자료 : useRef로 컴포넌트 안의 변수 만들기

React에서 state 사용에 익숙해지다보니 사고가 좁아진 것 같다고 느꼈던 부분이었다.

4. 인원수 설정하기

인원 수와 반려동물 동반 여부를 선택할 수 있는 모달창을 구현했다. 사용자가 선택한 값에 맞추어 렌더링이 일어나고, 캘린더 모달과 인원수 모달 모두 모달 바깥을 누르면 창이 닫히도록 되어있다.

const wrapperRef = useRef();

useEffect(() => {
  document.addEventListener("mousedown", handleClickOutside);
  return () => {
    document.removeEventListener("mousedown", handleClickOutside);
  };
});

const handleClickOutside = event - {
  if (wrpaaerRef && !wrapperRef.current.contains(event.target)) {
  	setCountModalOpen(false);
  } else {
    setCountModalOpen(true);
  }
};

직접 처음부터 끝까지 생각해낸 코드는 아니지만 이 코드를 이해하면서 이벤트 버블링, event.target과 event.currentTarget의 차이점, addEventListener을 사용시 스스로 종료되지 않기 때문에 removeEventListener로 종료를 해줘야 한다는 점을 알게 되었다.

참조 블로그

5. 위시리스트 등록

저장 버튼 클릭 시 위시리스트 테이블로 데이터가 보내지고 저장된 상태에서 재클릭 시 위시리스트 테이블에서 제거되도록 구현했다. 로그인이 되어있지 않을 시에는 로그인 모달창이 보여진다.

페이지가 처음으로 렌더링 될 때 위시리스트 저장 여부를 조회한 후 저장 여부를 boolean 값으로 state에 저장한다. state 값에 따라서 텍스트가 달라지고 버튼 클릭 시 함수를 다르게 실행시킨다.


// 렌더링 시 위시리스트 저장 여부에 따라 state에 저장
useEffect(() => {
  fetch(`$(PORT)/wish?accommodationsId=${location.state}`, {
    method: 'GET',
    headers: { accessToken: token },
  })
  	.then(res => res.json())
  	.then(result => {
    	result.wish[0].wish_yn === 'Y' ? setIsSaved(true) : setIsSaved(false);
  	});
}, []);

6. 숙소 예약 기능

체크인 날짜와 체크아웃 날짜가 선택 됐을 경우에만 예약하기 버튼이 활성화된다. 예약하기 버튼을 누르면 로그인 여부에 따라서 로그인 모달창 혹은 예약 기능 함수가 실행된다.

<Button
	type="button"
	disabled={
      checkInValue.lenght > 1 && checkOutValue.length > 1
      	? null
      	: 'disabled'
    }
	onClick={() => {
      login ? postReservation() : setIsLoginModalOpen(true);
    }}
>
예약하기
</Button>



3. 회고

회고록은 최대한 빨리 미리미리 작성하자 👊

1. 진행 상황 공유의 중요성

1차 프로젝트의 미숙했던 부분이자 2차 프로젝트에서 중요성을 크게 깨달은 부분이다. 전에는 티켓 분배 후 정해진 일정 안에 내가 맡은 부분을 다 해내는 것에 집중해 다른 팀원들의 진행 상황이나 프로젝트 목표의 전체적인 진행 상황을 잘파악하지 못했다.
그리고 프로젝트를 진행중에는 변수가 생기기 마련인데, 처음에는 블로커가 생기면 혼자서 해결하려고 하는 편이었다. 모두가 바쁜 상황인걸 알기에 내 업무는 최대한 스스로 해결하는 것이 옳다고 생각했다.

반대로 이번에는 블로커가 발생하면 미팅에서 간단하게라도 상황을 알렸다. 그러다 보니 팀원들이 문제 판단을 도와줘서 덜 중요한 부분은 뒤로 미루기도 하고, 문제를 같이 해결하기도 했다. 진행 속도가 전에 비해 더 빨라서 맡은 페이지의 대략적인 구현이 끝나고 마지막엔 남은 기능이 가장 많았던 호스팅 페이지를 돕기도 했다.

1차 프로젝트 때도 협업을 잘하고 싶은 마음은 동일했으나, 돌이켜 생각해보면 협업에 대해 제대로 이해하지 못한 상태라고 생각된다. 나의 문제를 팀원에게 공유하는게 부담이 될까봐 어려웠는데, 미팅을 매일 진행하며 공유하는 분위기가 만들어진게 좋은 방향이었다고 생각한다.

2. 말에는 힘이 있다.

다 같이 열정-! 열정, 열정, 열정!

프로젝트를 진행하면서 하루에 한 번 이상 무조건 외친 우리 팀구호🔥

처음에는 장난식으로 정한 구호였지만, 힘들 때마다 의욕을 되찾을 때 많은 도움을 받게 되었다. 긴 회의를 마치고 지칠 때에도 다 같이 열정!을 외치고 나면 힘이 됐었다. 힘이 들지 않았다고 하면 거짓말이지만, 밤을 새면서도 즐겁게 할 수 있었던 원동력의 4할 정도는 이런 마인드셋이었다고 생각한다. (나머지 6할은 열정맨들 덕분..🥰)

1차 프로젝트 회고록에 비해서 너무나 빈약한 2차 프로젝트 회고록에 아쉬워하며.. 마지막으로 힘든 일정 같이 지낸 저스트비앤비 팀원들 다들 앞으로도 열정! 열정! 열정!

profile
상호작용을 구현하는 개발자

4개의 댓글

comment-user-thumbnail
2022년 5월 31일

2차 프로젝트라 그런지 결과물이 한층 풍성해진듯 하네요.
개인적으로 캘린더 부분이 가장 인상깊었습니다.
재밌게 보구 갑니다!

1개의 답글
comment-user-thumbnail
2022년 6월 11일

진짜 🐶 멋있어여 굿굿!!

1개의 답글