[wedidas] 1차 프로젝트 팀 회고

누리·2022년 10월 29일
1

Team Project

목록 보기
8/8

🚀 My first Team Project

팀프로젝트를 시작하기 전 막연한 걱정에 사로잡혀있었다. 과연 내가 공부한 범위 만으로 완성도 높은 사이트를 구현해 낼 수 있을까 나 자신에 대한 의문도 들었고, 팀으로 진행되는 프로젝트는 처음이다보니 내가 팀원으로써 환영받을 수 있을 사람일까에 대한 걱정도 조금 됐다.
지금 프로젝트가 끝난 시점에서 되돌아보니, 내가 걱정했던 기술적인 측면에서의 문제는 그렇게 어려운것 처럼 느껴지지는 않는다. 물론 당시에는 어떻게 해야할지 막막하고 모르는 기술 스택들이 많은 상태에서 무엇을 구글링 해야할지도 몰라 해맸었지만 계속 찾고 생각하는 과정을 반복하다보니 어떻게든 내가 맡은 필수구현 항목들은 해낼 수 있었다.
오히려 팀원들과의 소통적인 측면에서 아쉬움이 많이 남는다.
이후 프로젝트에서는 좀더 발전된 나를 바라는 마음으로 이번 프로젝트를 통해 느꼈던 점을 솔직하게 기록해 본다.

프로젝트 소개

  • 팀이름 : Wedidas
  • 이 프로젝트는 아디다스를 참조하여 교육목적상으로 제작하였습니다.

개발기간 및 인원

개발기간 : 2022년 10월 17일(월) ~ 2022년 10월 27일(목)
개발인원 : 5명
1. 프론트엔드

  • 김온누리(본인) : 로그인페이지, 메인페이지, 위시리스트페이지
  • 김우진 : 공통네비게이션바, 상품디테일페이지
  • 박성수 : 회원가입페이지, 상품리스트페이지, 장바구니페이지
  1. 백엔드
  • 정도영 : ERD구성, 데이터추가, 상품리스트 페이지 API, 상세페이지 API
  • 천송인 : ERD구성, 로그인회원가입 API, 장바구니 위시리스트 API, 디테일 작업

기술 스택

  1. 프론트엔드 : React.js, SCSS
  2. 백엔드 : Node.js, Express, MySql, Bcrypt
  3. 공통 : Github, slack, trello, RESTful API

프로젝트 진행과정

매일 10시에 15분 정도 Daily Standing Meeting 진행하여 Trello를 작성하고 작업내용을 공유했다

  • To Do - 이번 스프린트에 해야 할 티켓들
  • Doing - 현재 개발중인 티켓들
  • In Review - PR 작성 후 리뷰중인 티켓들
  • Done - 완료된 티켓들

카테고리를 4가지로 나눠서 구분했고, 회의록을 따로 작성하여 공통적으로 봐야할 부분들이 기록되어 있어 그때 그때 확인할 수 있도록 trello를 활용하였다.
전체 프로젝트 과정에서 진행 척도를 파악하기가 수월했다.

프로젝트 시연영상

wedidas 보러가기

내가 구현한 영역

  1. 메인페이지
    메인페이지의 중점은 바로 캐러셀 기능이다.
    나는 총 2가지의 캐러셀을 구현했는데 하나는 여러 아이템들이 들어간 슬라이드 캐러셀이고, 하나는 페이지를 들어가면 가장 먼저 보이는 kv영역의 zindex를 이용한 두 이미지의 교차되는 캐러셀이다.
    캐러셀 구현은 사실 라이브러리로 쉽게 구현이 가능한 것으로 알고 있지만 이번 프로젝트에서는 특정 라이브러리를 사용하지 않고 구현하는것이 목표였기 때문에 구현을 어떻게 해야할지 고민하는 것에 시간이 꽤 소요됐었다.
  • 매인 배너 캐러셀

    먼저 kv영역의 캐러셀을 살펴보면 재생버튼을 클릭하면 이미지가3초지날때 마다 교차로 바뀌고 다시 일시정지 버튼을 클릭하면 이미지 변경이 되지 않고 멈추는 모습을 볼 수 있다.

    이미지 두개를 번갈아 가며 zindx가 조절되는 것으로 컨셉을 잡고 클래스명을 부여하는 방법으로 사용했다. 하지만 이때 useEffect 훅안에서 setInterval을 사용하는것이 문제가 됐었다. 재생버튼에 불리언 값을 주고 재생버튼의 불리언 값이 변할때 조건을 읽어 true일때 setInterval 함수를 실행시키고 cleanup을 할때 clearInterval 함수를 부르는 방식으로 코드를 작성했는데 조건이 false가 됐음에도 setInterval 함수가 멈추지 않고 계속 불려져 구글링을 계속 했던 것 같다.

    결국 단순한 문제였다. 의존성 배열에 isPlayToggle값을 넣어주지 않아 isPlayToggle값을 추적하지 못하여 useEffect가 실행되지 않았던 것이었다

    useEffect(() => {
       if (isPlayToggle === true) {
         const timer = setInterval(() => {
           if (currentImage === 'show') {
             setCurrentImage('');
           } else if (currentImage === '') {
             setCurrentImage('show');
           }
         }, 3000);
         return () => {
           clearInterval(timer);
         };
       }
     }, [currentImage, isPlayToggle]);

    해결하고 나서는 허무하기도 했지만 이렇게 고민하면서 찾아보니 custom훅을 사용한 setInterval을 이용한 useEffect 코드도 접할 수 있었다. 다음 프로젝트때는 재사용성을 고려하여 커스텀훅을 사용하여 만들어 보고 싶다.

  • 제품리스트 캐러셀

    제품리스트들이 담긴 캐러셀이다. 첫페이지에서는 next 버튼만 존재하고 슬라이드가 넘어가면 prev버튼도 나타난다. 페이지네이션도 구현하여 해당 페이지네이션을 클릭하면 해당 위치로 이동한다. 한페이지당 4개의 아이템이 나오도록 구현하였는데 확장성을 위해 이 부분을 개선하는데 많은 노력이 들어갔던 기억이 있다.

    처음에는 단순히 전체 페이지 수를 정하고 그 안에서 들어가는 영역은 400% width로 맞춘다음 justify-content: space-between 속성으로 아이템마다 간격이 알아서 맞추어 지도록 만들었지만 아이템 갯수에 따라 한페이지당 보여지는 갯수도 다르고 아이템마다 간격이 달라지기 때문에 아이템이 잘려서 보여져도 해결할 방법이 없었다.

    이후 이를 개선하기위해 전체 페이지수를 정할때 아이템이 몇개씩 보여질지로 계산을 하여 아이템이 많아지면 페이지의 수도 늘어나도록 코드를 짜고, 그것을 기반으로 하여 전체 width값을 계산하여 변수에 저장하고,

    const slideRef = useRef(null);
     const ONE_PAGE = 4;
     const totalPage = Math.ceil(sources.length / ONE_PAGE) - 1;
     const width = (totalPage + 1) * 100 + '%';

    useRef로 페이지를 이동시킬때 이 값들을 토대로 계산을 하여 마지막 페이지에 도달하는 조건에서, 아이템 갯수가 4개로 나누어 떨어지지 않더라도 마지막 아이템 이 가장 슬라이드 끝 부분으로 오도록 계산을 하여 코드를 작성하였다.

    useEffect(() => {
       slideRef.current.style.transition = 'transform 0.7s ease-in-out';
       slideRef.current.style.transform = `translateX(-${
         currentSlide * (100 / (totalPage + 1))
       }%)`;
       const itemRatio = 100 / (totalPage + 1) / ONE_PAGE;
    
       if (currentSlide === totalPage && sources.length % ONE_PAGE !== 0) {
         slideRef.current.style.transform = `translateX(-${
           (100 / (totalPage + 1)) * totalPage -
           itemRatio * (ONE_PAGE - (sources.length % ONE_PAGE))
         }%)`;
       }
     }, [currentSlide, totalPage, sources.length]);

    처음 캐러셀을 구현해낸것에 안주하지 않고 확장성을 고려한 개선을 이루어 냈다는것에 뿌듯했다.

  1. 로그인 페이지
    로그인 페이지를 구현하며 백엔드와 소통도 하고 에러메세지 핸들링을 해볼 수 있어서 좋았다.
    처음에 이메일 input값과 비밀번호 input값을 따로 onChange값을 받았는데 리팩토링 코드리뷰를 받고 name값으로 한번에 처리해 줄 수 있어 좋았던 기억이 난다.

    const [userInfoValue, setUserInfoValue] = useState({ email: '', pw: '' });
    
     const onChangeUserInfoValue = event => {
       setUserInfoValue({
         ...userInfoValue,
         [event.target.name]: event.target.value,
       });
     };
    <input
    className="input"
    type="email"
    name="email"
    placeholder="이메일 *"
    value={userInfoValue.email}
    onChange={onChangeUserInfoValue}
    required
    />
  2. 위시리스트 페이지
    위시리스트는 아이템리스트에서 하트버튼이 클릭되면 백엔드에서 하트버튼이 클릭된 아이템들의 데이터를 배열로 만들어 내가 해야할 일은 만들어진 배열 값을 받아오고 다시 하트버튼을 클릭하면 백엔드에 delete 메서드를 통해 값을 지우는 것을 요청하면 되는 페이지였다.

    처음에는 값을 지운다음 리렌더링하면 화면상에서 지워진 데이터를 반영한 리스트를 불러올것이라 생각 하였는데 제대로 동작하지 않아 화면에서 지우는 것은 프론트 측면에서 지우는 방법으로 구현하였다.
    onRemove 함수를 콜백에 저장하여 wishlistitem 컴포넌트로 넘겨 주었으나 이 경우에는 useCallback을 사용한다고 해서 효율성이 좋아지지 않는다는 피드백을 받아서 그냥 함수로 넘겨주었다.

    //Wishlist.js
    const onRemove = id => {
       setWishItemList(wishItemList.filter(item => item.productId !== id));
     };
    const handleWishClick = () => {
       onRemove(data.productId);
       deleteItem();
     };
    
     const deleteItem = () => {
       fetch(`${api.wishlists}?productId=${data.productId}`, {
         method: 'DELETE',
         headers: {
           authorization: localStorage.getItem('token'),
         },
       });
     };

    이때 하트버튼을 클릭했을때 아이템이 제대로 지워지지 않고 주소에 undefined 값이 들어갔었는데 fetch 주소가 잘못되었나 생각했으나 해당 아이템 컴포넌트에 <Link>로 감싸져있었기 때문이었다. 하트버튼을 Link로 감싸진 영역 밖으로 꺼내서 다시 포지션을 조절하니 해결됐다.

프로젝트를 진행하며 느낀점

  1. 코드리뷰의 중요성
    기능이 구현됐다는 사실만으로 끝이 아니고 더 나아가 좀 더 가독성 좋고 좀 더 효율적인 방식으로 코드를 리팩토링하는 과정이 꼭 수반 되어야 한다는 것이다.
    멘토분들께 리뷰를 받으면서 어떻게 하면 더 가독성 좋고 반복되는 하드코딩을 줄일 수 있을지 피드백을 많이 주셔서 많은 도움이 됐고 다음 프로젝트때 는 더 다양한 방법으로 시도해 보고 싶다
    완성코드도 다시보자!

  2. 깃헙 기초의 중요성
    깃헙은 정말 기초적이지만 개발자에게 있어서 정말 중요한 스킬인 것 같다.
    이번 프로젝트때만 해도 브랜치를 master로 이동후 생성하는 것을 계속 까먹고 진행하던 브랜치에서 다른 브랜치를 만든 적이 많았다. 그래서 새로 만든 파일들을 다 따로 저장후 삭제하고 push하는 귀찮은 작업들로 시간을 허비했는데 이러한 실수들을 하나씩 줄여 나가는 것이 발전되는 과정인 것 같다.
    또 깃 process들을 이해하고 다양한 터미널 git 단축키들을 사용해 보았다. 팀원들과 협업을 하며 내가 작업하는 브랜치에서 작업했던 브랜치로 이동하여 코드를 보여줄 일이 많았는데, 작업하다가 다른 브랜치로 이동할때 git stash 라는 명령어로 임시저장을 해주는 기능이 있어 사용해 보았는데 굉장히 유용했다.
    그리고 브랜치를 컴포넌트 단위로 쪼개서 만들면 작업속도가 한결 빨라질것 같다는 생각도 들었다. 이번프로젝트때는 git도 익숙치 않고 branch를 만드는 것이 더 귀찮다는 생각에 브랜치를 페이지별로만 만들고 컴포넌트는 컴포넌트가 쓰이는 해당 페이지 브랜치에서 만들어 머지를 했는데, 나중에 한꺼번에 머지가 되는것보다 기능별로 머지가 되면 다른 페이지 작업시에도 머지된 기능을 재사용 할 수 있어 협업에 용이할 것 같다.

  3. 백엔드 소통의 중요성
    초반에 백엔드 용어도 모르고 데이터 구조가 아직짜여 있지 않은 상태에서 기능구현을 하기 위해 목데이터를 사용해 기능을 구현했는데 백엔드와 통신하는 과정에서 계속 데이터가 안받아와져 고생을 했었다. 프론트에서는 변수명을 주로 카멜케이스를 사용하는데 백엔드분들은 스네이크케이스를 사용하는 경우가 있어 변수명의 차이가 주 원인이었다.
    이를 초반에 적극적으로 소통하여 변수명을 통일하거나 따로 변수명들이 저장된 파일들을 만들어 언제든 볼 수 있도록 관리했으면 좋았을것 같다는 생각을 했다.

🍺 마무리

첫 팀부터 너무 좋은 팀원분들을 만나 프로젝트를 잘 마무리 할 수 있었던것 같다. 다들 짧은 기간동안 프로젝트를 완성해야한다는 데드라인에 쫓겨 예민해져 있고 툭 건들면 터질 정도로 스트레스도 많이 받았을 것이다.

백엔드와 프론트간의 소통에 있어서도 마찰없이 수월하게 진행된것도 다 서로간의 배려와 실수를 하더라도 탓하기 보다 같이 고쳐가려는 노력이 있었기에 이러한 화합이 가능했던 것 같다.

첫 프로젝트이다 보니 업무 배분이나 시간할당도 모르기때문에 팀원들과 작업 속도가 차이가 나는 부분도 있었는데, 다들 꿋꿋이 포기하지 않고 조바심 내지 않고 모두 해낸것에 굉장히 뿌듯함을 느낀다.

다음 프로젝트때는 기술보다는 소통에 더 중점을 두어서 적극적인 소통을 하며 상대방과 같이 성장할 수 있는 기회가 되기를 기대한다

profile
프론트엔드 개발자

0개의 댓글