마이리얼트립 클론 프로젝트 회고록

devCecy·2021년 1월 10일
1

Project

목록 보기
2/2

2주간 진행된 마이리얼트립 클론 프로젝트의 회고록 🌱

1. 클론 프로젝트 소개

✔️ 클론사이트 명 : 마이리얼트립
✔️ 팀명 및 구성 : 마이동묘트립
프론트) 김해인(PM), 안미현, 이하영 / 백엔드) 이주형, 우혁준
✔️ 프로젝트 기간 : 2020.12.28 ~ 2021.01.08 (2주)
✔️ 최종 결과물 영상 : (유튜브 주소 첨부 예정)
✔️ 담당 페이지 : 숙소 탭 - 메인, 리스트, 상세 페이지
✔️ 세부 구현사항

리스트 페이지)

  • styled component를 활용한 레이아웃 및 스타일링
  • date-range-picker를 이용한 달력 구현
  • 인원 선택 창 (1명 디폴트 설정)
  • fetch API를 활용한 리스트 Data rendering
  • Qurey string을 활용한 filter 기능 구현
  • Query string를 사용하여, 상품리스트에서 상세 페이지로 이동
  • 상품 리스트 Pagination 기능 구현

상세 페이지

  • styled component를 활용한 레이아웃 및 스타일링
  • 객실 선택 시, 사이드 바에 객실 명 및 가격 연동 기능
  • 컴포넌트 재사용을 통한 사이드 바 내용 변경
  • 스크롤 이벤트를 활용하여 특정 위치부터 사이드바가 fix되도록 구현
  • slick-sider를 이용해 이미지 슬라이더 구현
  • 리스트 페이지에서 사용했던 달력 & 인원 선택창 컴포넌트 재사용
  • 긴 글 숨김 및 버튼 클릭을 통해 전체 보기 기능 구현

메인 페이지

  • styled component를 활용한 레이아웃 및 스타일링
  • 숙소 버튼 클릭 시, 숙소 리스트 페이지로 이동

2. 구현한 기능 및 코드정리

2차 프로젝트는 1차 프로젝트와는 조금 다른 목표를 가지고 시작했다.

1차는 첫 프로젝트였기에 많은 기능을 구현하는데 초첨이 있었다면, 2차는 기능구현보다는 컴포넌트를 재사용하고, 코드를 정리하고, 새로 배운 styled component를 적용하는 데에 시간을 쏟았다.

🎈Code Refactoring

👩🏻‍💻 반복되는 코드는 js파일로 따로 빼 사용

반복적으로 사용되는 내용으로인해 코드가 길어진다면 같은 폴더에 js파일을 만들어 export하고, 사용할 컴포넌트에 데이터를 import해주면 코드를 짧고 간결하게 만들어 줄 수 있다.


숙소 메인페이지에 4가지의 숙소타입 이미지를 보여주고 있다. 처음에는 4개의 이미지를 삽입하는 코드를 반복하여 적어주었지만, js파일에 이미지 데이터를 따로 넣어주어 map을 돌리니 아래와 같이 짧고 깔끔해 졌다.

특히, 숙소 디테일 페이지에는 반복되는 내용으로인해 코드가 많이 길어졌는데 데이터를 따로 관리하자 코드가 획기적으로 깔끔해졌다.

import { mainThemeArr} from "./mainData"

 <MainThemeBox>
        {mainThemeArr.map((theme) => (
          <Link to="/accommodation_list">
            <ThemeImg key={theme.id} alt={theme.alt} src={theme.src} />
          </Link>
        ))}
      </MainThemeBox>

👩🏻‍💻 config.js 로 API 주소 관리

config.js파일을 생성하여 API주소를 변수로 만들어 사용했다.

API 주소가 변경될 때마다 API가 사용된 컴포넌트에서 하나하나 변경해 줄 필요 없이 config파일에서만 주소를 변경해 주면 되기 때문에 관리가 용이하다.

//confing.js파일 
export const ACCOMMODATION_DETAIL_API = 'http://192.168.0.46:8000/accommodation'
export const ACCOMMODATION_LIST_API = 'http://192.168.0.46:8000/accommodation/list'
//API를 주소를 사용할 컴포넌트 파일에 import
import * as config from "../../../config"
//API주소를 적어야 할 부분에 미리 선언한 변수를 사용
const getProducts = () => {
    fetch(config.ACCOMMODATION_LIST_API)
      .then((response) => response.json())
      .then((response) => setProductArr(response.accommodation_list))
  }

🎈컴포넌트 재사용

👩🏻‍💻 디테일 페이지의 사이드바를 컴포넌트 재사용하여 만들었다.
화면 첫 렌더시에는 객실 선택 및 위시리스트 담기 버튼이 보여지지만, 객실을 실제로 선택하면 두번째 이미지와 같이 선택한 객실 명과 가격이 표시되어야 한다.

modal컴포넌트에 ChooseRoom이라는 조건을 통해 서로다른 내용을 가진 form컴포넌트가 렌더 될 수 있도록 만들어 주었다. ChooseRoom은 객실 버튼 클릭시 statefalse에서 truesetState 되는 정보가 담겨있다.

// modal.jsx
 return (
    <div>
      {ChooseRoom ? (
        <Form
          getPrice={getPrice}
          getName={getName}
          detailProductArr={detailProductArr}
          format={RESERVEPROPS}
          ChooseRoom={ChooseRoom}
          roomtype={roomtype}
        />
      ) : (
        <Form
          getPrice={getPrice}
          getName={getName}
          detailProductArr={detailProductArr}
          format={CHOOSEPROPS}
          ChooseRoom={ChooseRoom}
          roomtype={roomtype}
        />
      )}
    </div>
  )

그리고 같은 modal 컴포넌트에 form에 들어갈 내용을 객체로 만들어 변수로 선언해 주었다. 이 정보는 format이라는 props에 담겨 자식 컴포넌트로 전달 될 예정이다.

const RESERVEPROPS = {
  type: "reservation",
  text: "",
  data: [
    {
      type: "button",
      color: "blue",
      text: "예약하기",
    },
  ],
}

const CHOOSEPROPS = {
  type: "choose",
  person: "기준 2명",
  total: "총 금액",
  night: "1박",
  text: "명이 이 상품을 위시리스트에 담았습니다.",
  data: [
    {
      type: "button",
      color: "blue",
      text: "객실선택",
    },
    {
      type: "button",
      color: "white",
      text: "위시리스트 담기",
    },
  ],
}
  • format이라는 props를 전달 받은 Form.jsx컴포넌트.
    재사용 되어야 할부분을 에 formatmap돌려 만들어주고, 한 컴포넌트에만 사용되는 정보는 format && format.type === "reservation"와 같이 조건부 렌더링을 해주었다.
  • 두 컴포넌트 모두 호스트 정보와, 하단 쿠폰이미지가 변함없이 동일하게 사용되었다. 이런 부분은 FormLayout이라는 컴포넌트에 정보를 담아준다. 그리고 FormLayout이라는 컴포넌트 자체를 Form 컴포넌트에 사용하여 그 안에 변화하는 정보를 담아 준 것이다.
//Form.jsx 컴포넌트
//Modal.jsx로부터 format이라는 props를 전달 받았다. 
//가장 바깥쪽 FormLayout 컴포넌트는 공통되는 내용을 담고 있다. 
 <FormLayout detailProductArr={detailProductArr}>
      {format && format.type === "reservation" && (
        <>
          <>
            <Roomtype>{getName}</Roomtype>
            <InnerUpBox>
              <Font>기준2</Font>
              <>
                <Font>{purePrice}</Font>
              </>
            </InnerUpBox>
          </>
          <InnerDwonBox>
            <Font>총 금액</Font>
            <div>
              <Font>1</Font>
              <SecondSpan>{purePrice}</SecondSpan>
            </div>
          </InnerDwonBox>
        </>
      )}
      {format &&
        format.data.map((a) => (
          <Button type={a.color} onClick={handleLikeBtn}>
            {a.color === "white" ? (
              <i className={likeBtn ? "fas fa-heart full" : "far fa-heart"} />
            ) : (
              ""
            )}
            <span>{a.text}</span>
          </Button>
        ))}
      <WishBox>
        {format.type === "choose" && likePerson}
        {format.text}
      </WishBox>
    </FormLayout>

컴포넌트 재사용을 글로 설명하려니 쉽지가 않다.
핵심은, 비슷한 구조를 가지고 있는 컴포넌트를 다른 컴포넌트로 생성하는 것이 아니라 공통되는 구조는 함께 사용하고, 변화하는 내용은 객체에 데이터를 담아 조건부 렌더링을 통해 필요시에 불러와 사용한다는 것!

🎈styled component 적용

CSS, SASS에 이어 2차 프로젝트에는 styled component를 사용해 스타일링을 해주었다. styled component는 말 그대로 스타일 자체를 컴포넌트로 만들어 적용해 주는 방식이다.

import styled from "styled-components"
//styled component를 사용한 코드 모습.
 <SearchBarBox>
    <SearchBarInnerBox>
       <SearchBarName>제주도</SearchBarName>
       <SearchBar />
         {setDate}
    </SearchBarInnerBox>
 </SearchBarBox>
//기본적으로는 아래와 같은 구조로 사용해주고, 
const SearchBarBox = styled.div`
  height: 72px;
  border: 1px solid ${theme.Color.grey[100]};
  margin-bottom: 50px;
  box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.11);
`

//같은 스타일링을 사용하는 컴포넌트가 있다면, 그 컴포넌트를 불러오기만 하면 된다. 
const MealNoticeBox = styled(shareProperties)``

// props를 사용해 다른 스타일링을 적용해줄수도 있고, 
const SubTitle = styled.span`
  font-size: ${(props) => (props.name === "time" ? "24px" : "16px")};
  font-weight: ${(props) => (props.name === "time" ? "300" : "bold")};
  color: rgb(73, 80, 86);
`
// alt와 같이 속성의 값을 부여해 줄수도 있다. 
const SliderImg = styled.img.attrs(() => ({
  alt: "main image",
}))`
  width: 477px;
  height: 350px;
`

🌈 익숙해져있는 css, Sass에서 styled component로 넘어오는게 처음에는 쉽지 않았다. 클래스명을 짓는 수고로움이 사라진다는데, 오히려 더 늘어난 것 만 같고 내가 작성한 코드를 개발자 도구를 통해 볼때, 클래스명이 랜덤하게 생성되어 있기 때문에 코드의 위치를 찾는것도 쉽지 않았다. 더불어, 컴포넌트 명을 짓는 방식을 잘못 이해해 중간에 대공사를 하기도 했고😂

그런데 팀원들의 브랜치가 모두 merge된 후에도 전혀 깨지지 않은 스타일을 보고나서야 styled component사용의 이유를 알게 되었다.. 최고!

🎈라이브러리 사용

1차 프로젝트 시 라이브러리 이용을 거의 하지 않았다. 그래서 2차에는 라이브러리 사용의 감을 잡고 편리함도 느껴보고 싶었다.

1) slick-slider
숙소 디테일 페이지로 들어가면 숙소의 사진을 보여주는부분이 있는데, 실제로 마이리얼트립 사이트에는 모달창이 뜨는 형식으로 되어있다. 슬라이더 라이브러리를 사용해 보기위해 나는 이부분을 슬라이드 형식으로 바꿨다.

프로젝트 내내 동기들이 라이브러리 이야기를 하는 것을 많이 들을 수 있었는데, 라이브러리를 사용해보지 않은 나로써는 어떻게 사용하는 것인지 감이 잘 오지 않았다. 그런데 막상 사용해 보고나니, 이렇게 간단하다고?.. 싶을만큼 원하는 기능을 빠르게 구현할 수 있었다.

import Slider from "react-slick"
 const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 3,
    slidesToScroll: 1,
  }
<Slider {...settings}>
        {detailProductArr &&
          detailProductArr.map((img, idx) => <SliderImg key={idx} src={img} />)}
      </Slider>
```![](https://velog.velcdn.com/images%2Fdev_cecy%2Fpost%2F1e69aec4-bc02-4de9-9ea2-e102d2af9add%2F%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-01-10%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.23.58.png)

**2) date-range-picker**
슬라이더 라이브러리와 마찬가지로 달력 또한 라이브러리를 사용해 보았다. 틀은 갖춰져있되, 원하는 방식으로 커스터마이징 해서 사용할 수 있는 점이 편리했다.

![](https://velog.velcdn.com/images%2Fdev_cecy%2Fpost%2Fe37cb7bf-b18c-42fd-b134-ac3e9bcee745%2FJan-10-2021%2018-26-32.gif)

```jsx
// 초기 기간범위 값을 1일로 잡아주었다.
import { DateRange } from "react-date-range";
import { addDays } from "date-fns";

const [state, setState] = useState([
    {
      startDate: new Date(),
      endDate: addDays(new Date(), 1),
      key: "selection",
    },
  ]);
//달력이 수평으로 2개월치가 나올 수 있도록 설정했다. 
 <DateRange
            editableDateInputs={true}
            onChange={(item) => setState([item.selection])}
            moveRangeOnFirstSelection={false}
            ranges={state}
            months={2}
            direction="horizontal"
            // locales="korean"
          />

🎈그 외,

🌈 스크롤 이벤트
사이드바가 원하는 스크롤 위치에 도달했을 때 fix되도록 만들어 주었고, 다양한 곳에 활용 가능할것이라고 생각되어 남겨놓는다.

 const [scrollTop, setScrollTop] = useState(0)

  useEffect(() => {
    window.addEventListener("scroll", handleOnScroll)
    return () => window.removeEventListener("scroll", handleOnScroll)
  }, [])

  const handleOnScroll = (e) => {
    const scrollTop = ("scroll", e.srcElement.scrollingElement.scrollTop)
    setScrollTop(scrollTop)
  }
 <RightSideBox name={scrollTop > 440 ? "fixed" : ""}>
  //코드생략
 </RightSideBox>

🌈 기억에 남는 상황
Mock data나, API주소로 데이터를 불러오면 데이터가 왔다가 오지 않았다가 하는 현상이 발생했다. 이 문제로 데이터를 불러오지 못하고 컴포넌트 내에서 임시로 사용했는데, 발표를 위해서 꼭 해결해야했다. 그러다 QnA방에서 종택님을 만났는데 (살짝 무서웠지만,) 감동 그자체 였다.. 왜 데이터가 불러와 지지않는다고 생각하세요? 부터 하나하나 그 이유를 찾아가도록 이끌어주시는 것을 보고 내가 그동안 코드를 이해하면서 적고 있었나? 하는 반성과 더불어 코드를 좀 더 깊게 이해하면서 공부하고 싶다는 자극을 주셔서 감사했다.

위의 과정에서 빈 객체는 true이며, 반대연산자!!!를 왜 사용하는지를 설명해 주셨는데, 이건 벨로그로 따로 정리해서 남길 예정!

🌈 깔끔해진 폴더 구조!
프로젝트 중간에 멘토님이 방문해 주셨는데, 그때 깔끔하지 못한 나의 코드에 대해 답답함이 있었다. 그래서 준식님을 붙잡고 리팩토링 해야할 중요한 부분들에대해 물었고, 그날 덕분에 조금은 뻥 뚤린 마음으로 리팩토링을 할 수 있었다. 짧은 프로젝트 기간으로 인해 아직도 고치고 싶은 코드들이 많이 보이지만, 어떤 부분을 중점적으로 여겨야하는지에 대한 골격을 잡을 수 있었다.

그 중 하나인 폴더 구조! 처음에는 하나의 폴더에 모두 담겨있어 나조차 보기 불편했는데, 페이지별로 분류해주니 아주 깔끔해 졌다.

3. 마지막으로,

프로젝트가 중간쯤 흘러가던 어느날 밤, 자려고 침대에 누웠는데 문득 좋은사람이 되고싶다는 생각이 들었다. 함께 먹고자고 생활하다보니 모든 시간을 팀원들과 공유하게 되었는데 그 과정에서 팀원들의 배려를 눈으로 목격하고 느끼다보니 마음 속에 감사함이 쌓여서 그런 생각이 들었던 것 같다. 내 마음에 있는 감사함을 입밖으로 모두 꺼내어 말하지 못하는 부끄러워함으로 인해 다 표현하지 못한 것 같지만, 참 모두에게 진심으로 많이 미안하고 감사했다.

스스로는, 어떻게하면 함께 일하고 싶은 좋은 동료이자 사람이 될 수 있을까, 라는 고민이 마음속에 자리 잡았으며, 인간적인 나의 연약함과 부족함 또한 돌아볼 수 있어 의미있고 감사한 시간이였다.

균형있는 팀이 되도록 모든 부분에서 이끌어주시고 배려하고 챙겨주신 든든한 피엠 해인님, 마동트의 비쥐엠 담당이자 누구보다 먼저 일어나 아침을 준비하고 항상 즐거운 분위기가 가득하도록 만들어주신 주형님, 피곤함에도 나의 코드를 함께 봐주시고 해결해주신 css요정 & 하트전도사 하영님, 데이터 사진이 못생겼다는 나의 끊임없는 구박에도 항상 웃음을 잃지않으시고, 결국 근사한 데이터를 주신 혁준님까지 정말 정말 감사하고 또 감사했습니다! 끝까지 서로의 일상 대화를 잘 못알아 듣는것 조차 너무 웃기고 재밌는 시간들이었습니다👻💜 모두 다른 곳으로 기업협업에 가게되었는데 그곳에서도 더 즐겁고 많이 성장하는 시간이 되실거라고 믿습니다! 진심으로 마동트 팀원들 모두가 잘됐으면 좋겠어요. 화이팅 ❣️

profile
🌈그림으로 기록하는 개발자🌈

0개의 댓글