IKEA 홈페이지 클론 프로젝트 후기

이나영·2021년 10월 17일
0

프로젝트

목록 보기
1/2

0. 개요

  • 프로젝트 종류
    기존 홈페이지 클론(대상: IKEA)
  • 진행 기간
    2021년 10월 5일 ~ 2021년 10월 15일
  • 참여 인원
    프론트엔드 4명, 백엔드 2명 (총 6명)
  • 참여 분야
    프론트엔드
  • 개인 사용 기술 스텍
    HTML, CSS, JavaScript, React, Sass
  • 결과물
    Youtube 영상 보러가기
    프론트 github
    백 github
  • 담당 파트
    제품 상세 페이지

1. 슬라이드

  • 이미지 모달
  • 상품 리스트

가. 분석

본 홈페이지를 보면 슬라이드가 갖고 있는 기능은 아래와 같다.

  • 왼쪽/오른쪽 화살표를 누르면 화면에 보이는 영역이 이동한다.
  • 이때 이동하는 영역은 화면에 완전히 보이는 item만큼.
    ex) 화면에서 완전히 보이는 item 3개와 절반 가려진 item이 존재한다면 3개만큼의 너비만 이동한다.
  • 스크롤바에서 다른 영역을 클릭하면 해당 위치로 이동한다. 위와 동일한 너비 계산이 적용된다.
  • 스크롤바 thumb를 누르면 thumb의 높이가 2배로 증가하면서 스크롤바를 붙잡은채 이동이 가능해진다.

여기서 덧붙여 고려한 사항이 '해당 슬라이드가 사용되는 곳은 어디인가'이다.

  • 이미지 모달 속
    : 보이는 이미지 1개로 고정.
    : 이미지 너비 100%로 고정.
    : 이동 영역 단위도 무조건 100%씩.
  • 페이지 하단 상품 추천 리스트
    : 보이는 이미지가 여러개.
    : 이미지 너비 = 슬라이드 너비/이미지 개수
    : 이동 영역 단위 = 완전히 보이는 item 개수 * 이미지 너비

나. 코드 작성

1) 슬라이드 제작

사실 overflow: scroll을 사용한다면 큰 수고 없이 스크롤 기능을 표현해 낼 수 있다. 그러나 최대한 본 홈페이지와 비슷하게 만들어보고 싶어 스크롤을 직접 구현하게 되었다.

무엇보다도 기존 css의 scroll을 사용하면 thumb의 너비를 조정이 불가능하다고 한다.

  • 기본 설정으로 .slider-list {display: flex}, .item {flex: 1 0 20vw}을 설정해주었다.

  • 화살표 버튼 혹은 스크롤을 클릭하면 이동할 너비를 계산해 슬라이드 영역은 margin-left , 스크롤의 thumbleft 값을 바꿔주었다.

  • thumbhover시 높이가 두배로 증가한다.

  • thumb를 클릭하면 onMouseDown이벤트로 isThumbClicked: true로 바꿔주고 이 값이 true인 동안 좌표 이동을 트래킹하여 슬라이드 영역과 thumb 위치를 이동시켜 준다.

  • 반대로 마우스 클릭을 종료하면 onMouseUp이벤트를 걸어 isThumbClicked: false로 전환시키고 트래킹을 종료한다.

  • 클릭시 새로 렌더 될 수 있게 이 모든 값은 state로 저장해 관리하였다.

2) 문제 발생 및 한계

state값이 변경할 때마다 화면이 리렌더링되다보니 thumb를 붙잡고 이동시킬 때 상당한 딜레이가 발생하였다.

또한 React가 비동기로 작동하다보니 thumb를 끌어 위치를 이동시킨 후 다른 영역을 클릭하면 thumb가 끌려가기 전의 값을 가져와 영역 계산을 해버리는 오류가 발생해버렸다.

이를 해결하기 위해 stateref로 바꿔보려 했으나 결국 해결 방안을 찾지 못했다....

다. 컴포넌트 관리

이미지 모달과 상품 리스트에서 모두 사용하기 위해 컴포넌트 속성을 다음과 같이 설정해주었다.

// 이미지 모달의 경우
<Slider
  productList={imageList}
  showItem="1"
  itemNums={imageList.length}
  selectedImg={selectedImg}
/>
// 상품 리스트의 경우
<Slider
  productList={productList}
  itemWidth={window.innerWidth/5}
  itemNums={imageList.length}
  selectedImg={selectedImg}
/>
  • showItem이 존재한다면 이미지 모달로 간주하여 너비를 100%로 고정. 존재하지 않다면 itemWidth 값을 넣어준다.
  • 영역의 이동 가능 범위 계산을 위해 itemNums가 꼭 필요하다.
  • 부모 컴포넌트는 기본적으로 this.state.selectedImg: -1을 선언해 내려보내준다. (-1: 상품 리스트 / 0 이상: 이미지 모달)

2. 사이드 모달

가. 분석

제품 상세 페이지는 사이드 모달의 사용도가 매우 높았다.

  • 사용되는 부분
    : 제품 설명, 제품 크기, 상품평, 규격
  • 클릭 시 오른쪽에서 밀려 나오고 X버튼을 클릭하면 다시 오른쪽으로 밀려 들어간다.
  • 모달 영역 밖을 클릭 시 모달이 닫힌다.

나. 코드 작성

1) 모달 기본 구조

  • 모달의 열고 닫음은 모두 state로 관리하였다.
  • 모달의 기본 css 값으로 display: none을 주고, display: block을 가진 또 다른 class를 만들어 state 값에 따라 적용/미적용을 바꿔주었다.
  • 움직이는 모션은 animation으로 처리한다.

2) 문제 발생 및 한계

사실 이 문제는 앞서 등장한 이미지 모달에서도 동일하게 발생하였다.

animation은 오로지 해당 요소가 처음 등장할 때 적용된다.

그러다보니 display: none을 통해 모달을 닫으면
요소 자체가 화면에서 삭제되어 그 어떠한 animation도 적용시킬 수 없다.

이에 대한 해결방안으로는 display가 아닌 위치 그 자체를 화면 밖에서 안으로, 안에서 밖으로 이동시켜주는 방법이 있다.

다. 컴포넌트 관리

같은 형태의 모달이지만 여러 곳에서 사용되기 때문에 공통 컴포넌트로 만들어 놓고 클릭한 버튼에 따라 내용이 바뀌도록 해주었다.

switch (selectedBtn) {
  case 1:
    return <Description />;
  case 2:
    return <Size />;
  case 3:
    return <Review />;
  case 4:
    return (
      <Option />
    );
  default:
  break;
}

3. 평점


가. 분석

평점은 총 평점개인 평점 두 가지가 존재한다.

  • 개인 평점은 {등록 날짜, 리뷰 내용, 다섯가지 만족도 값}을 필요로 한다.
  • 총 평점은 개인 평점의 만족도에 대하여 각각의 평균값과, 종합 평균값을 필요로 한다.

나. 코드 작성

1) 값 계산

  • 개인 평점은 미리 만들어놓은 틀에 값만 각각 대입해주면 되기 때문에 별다른 문제가 없었다.
  • 조립, 가성비, 품질, 외관, 기능. 이 다섯 가지에 대하여 각각의 평균을 값으로 가지는 객체를 새로 만들어주었다.
  • 최상단에 들어가는 총 평균값을 계산하는 수식을 추가해주었다.

2) 별점 제작

  • 회색과 검정색 두가지를 버전을 만들고 position: absolute로 회색 별 위에 검정색 별이 정확하게 올라올 수 있도록 해줬다.
  • 별의 차오름은 Math.round(average * 2) / 2를 통해 0.25 기준으로 반올림한다.

3) 등급바 제작

  • flex를 이용해 영역을 5등분 한다.
  • 단, 5등분에 사용되는 div는 4개뿐이다. 구분선을 만들기 위함이기 때문에 자식은 4개만 만들고 부모에게 justify-content: flex-start를 준다면 5등분이 가능하다.
  • 각각의 영역은 justify-content: flex-end와 함께 flex-basis: 20%를 가진다.
  • 그 안에 하얀 점을 만들어 넣고 margin-right에 점의 절반 너비만큼 음수값을 지정해주면 구분선 중심에 정확하게 올라간다.
  • z-index값을 밑배경 < 값 표시기 < 흰 점 순서로 해준다.

다. 컴포넌트 관리

1) 평점 박스

총 평점의 경우 평균으로 계산된 하나의 객체 값이, 개인 평점의 경우 여러 사람들의 평점(객체 타입) 나열된 배열 값이 input으로 들어오기 때문에 이를 해결하기 위해 모두 배열 형태로 통일하였다.

// 총 평점의 경우
<ReviewBox
  id={0}
  reviewList={averageObj}
  reviewNum={reviewList.length}
/>

// 개인 평점의 경우
<ReviewBox
  id={review.id}
  reviewList={reviewList}
/>
  • 모든 input 값을 배열 형태로 만들었기 때문에 총 평점에 들어가는 객체는 배열 중 index 0번째로 고정된다.

2) 별점

제품 상세 페이지에서 사용되는 경우와 리뷰 모달 내에서 사용되는 경우가 서로 다르다.

// 제품 상세 페이지에서 사용되는 경우
<Stars
  reviewList={product.reviews}
  reviewNum={product.reviews.length}
/>

// 리뷰 모달 내에서 사용되는 경우
<Stars
  id={id}
  reviewList={thisReviewList}
  reviewNum={reviewNum}
/>
  • 별점 옆에 리뷰 갯수가 표시되는 경우는 reviewNum의 여부로 판단한다.
  • 리뷰 모달은 <ReviewBox />에서 <Stars />state가 전달되기 때문에 개인 평점에서 처럼 reviewNum을 애초부터 전달받지 못했다면 <Stars />this.props.reviewNum 값은 당연히 false이다.
  • 리뷰 전체 값이 배열로 들어오기 때문에 이때는 id 값의 여부로 구분된다.
  • id값이 존재한다면 배열의 해당 index 값을 지정하여 사용. 그렇지 않다면 <Stars /> 컴포넌트 내에서 자체적으로 평균값을 계산한다.

4. 개인 평가

가. 잘한 점

1) 컴포넌트화

개인적으로 이 프로젝트를 진행하면서 가장 신경 쓴 부분이었다. 코딩에 들어가기 앞서 페이지 분석을 통해 공통적으로 사용될 수 있는 부분이 어디인가를 파악하고, 동일한 형태라면 최대한 재사용할 수 있도록 노력하였다.

2) CSS 충돌 미발생

프론트엔드 엔지니어만 무려 4명이 참여하는 프로젝트이기 때문에 Sass Nesting을 통해 Git Merge를 했을 때 서로의 CSS가 문제를 일으켜 화면이 깨지는 것을 방지했다.

나. 아쉬운 점

1) 변수명 짓기

나름 잘 지었다고 생각한 변수명을 타인이 읽고나서 '이게 뭘 가리키는 건가요?'라는 질문을 받았을 때 순간적으로 말문이 막혀버렸다.

생각해보면 id라는 변수만 해도 어떤 것의 id인지를 하나도 명시하지 않았었다..!

앞으로는 이름 짓는 데 훨씬 더 신중할 필요가 있어보인다.

2) 백엔드와의 소통

백엔드가 정해서 주면 프론트엔드가 그 내용에 맞추겠다는 생각하에 소통을 등한시 했다는 점이 이 프로젝트에서 가장 후회하는 부분이다.

시간이 걸리더라도 내가 이 페이지에서 어떤 데이터들이 필요하고, 백에서 어떤 이름으로 보낼 것인지를 미리 정확하게 정하고 갔다면 오히려 후에 수정할 일 없이 스무스한 진행이 가능했을 거란 생각이 프로젝트 내내 남아있었다.

5. 끝맺음

2주간 정말 쉴틈없이, 그리고 끊임없이 코드에 대한 고민을 했다. 어떻게 하면 내가 직면한 문제를 해결할 수 있을지를 고민하고, 어떻게 하면 시간 안에 목표한 지점까지 도달할 수 있을지를 고민했다.

프로젝트는 늘 기한이 주어진다. 나 혼자 취미로 하는 것이 아닌 이상 기한은 정해져있고 그것을 준수해야만 한다. 어찌 보면 욕심을 버리는 일이 가장 중요한게 아닌가 싶다.

Agile 방식은 '버전1'을 만들고, 이를 진화시켜 '버전2'를 만들고, 또 이를 진화시켜 '버전3'을 만들어낸다. 기본 구성을 먼저 갖춘 후에 기능을 추가해도 될 일을 당장 눈앞에 보이는 것을 해내고 싶다는 욕심때문에 오히려 계획이 어그러진다는 것은 마음을 상당히 힘들게 만들었다.

이 다음의 프로젝트에서는 부디 내가 스스로 그 욕심을 잘 다스릴 수 있기를 응원한다.

p.s. 우리 팀 모두 정말 수고 많았고 2주동안 함께 달려주고 서로 지탱해준 점에 매우 감사합니다.

0개의 댓글