React | 술담화 클론 프로젝트

이형준·2022년 5월 5일
4

Project

https://github.com/leehyoungjoon/32-1st-sulsajo-frontend

Project Overview

내 인생에 첫 web 프로젝트를 시작하게 되었다. 사이트는 술담화였는데 이 사이트를 이용하지 않는 나에게는 생소한 부분이 많았지만, 사이트를 하나하나 보면서 술을 판매하는 커머스 사이트라는 것을 알았다.
술담화 같은 경우 레이아웃이 반복되는 부분이 많았는데 이 부분에서 리액트를 활용하기 좋은 사이트라고 생각이 들었다. 첫 프로젝트인 만큼 프론트엔드와 백엔드 소통이 쉽지는 않았다. 하지만 한 목표를 향해 달려와 준 팀원분들 덕분에 기한 안에 팀 프로젝트를 성공적으로 끝낼 수 있었다.

React를 사용한 클론

첫 React를 이용한 프로젝트였다. 혼자 자바스크립트를 이용하여 클론사이트를 만들어는 보았다. 이때마다 자바스크립트도 충분히 편한데 굳이 리액트를 사용해야 할까? 라는 생각이 들었다. 하지만 프로젝트를 하면서 왜 React를 사용하는가를 알게되었다. 그 이유를 몇가지로 나누어 볼 수 있을꺼 같다.

  • 백엔드에서 데이터 fetch를 사용할 수 있다
  • 팀프로젝트 중의 공동의 public 폴더 및 scss를 사용하여 관리
  • 리액트 컴포넌트를 공유하여 효율을 높일 수 있었다
  • state, props를 통해 데이터 변경
  • 라이프사이클를 통해 실제 데이터를 나타내고 사라지는 타이밍을 통한 데이터의 효율적인 관리

작업기간

2022.04.25 ~ 2022.05.06.

기술 스택

프론트엔드 4명

  • HTML/CSS
  • JavaScript(ES6+)
  • React
  • SASS

백엔드 2명

  • Django
  • Python
  • Postman
  • MySQL

협업도구

  • Git
  • Github
  • Trello
  • Slack
  • Notion

주요 구현 사항

🔥 => 내가 구현한 기능 & 참여한 기능

  • access token를 활용한 로그인,회원가입 기능 🔥
  • Main 페이지 구성
  • 상세페이지 구성
  • 장바구니페이지 구성
  • Mock 데이터를 이용한 장바구니 수량
  • 사이트 API를 이용한 Nav bar 구성
  • 구독페이지 케러셀 기능 구현 🔥
  • 구독페이지 버튼 클릭에 따른 모달창 🔥
  • 상세페이지에서 장바구니 담기 기능
  • 장바구니 수량 변경 기능
  • access token를 활용한 pages 이동 기능

결과화면

  1. 메인화면
    : 반복적인 레이아웃 구성을 컴포넌트화 시켜 구성하였다.

  2. 구독페이지
    : 구독페이지 내부에 버튼 클릭시 생성되는 모달창과 하단부에 캐러셀을 구성하였다.

  3. 로그인
    : 프로젝트 안에서 백엔드와 처음으로 데이터패치를 하고 성공하였다,
    토큰권한에 따라서 만약에 로그인이 되어 있지 않다면 로그인페이지로 넘어가게 구성하였다.

  4. 상세페이지
    : 제품리스트가 있는 메인페이지와 카트페이지를 연결해주고, 댓글입력, 삭제, 수량 조절 기능 & 장바구니 담기 기능을 구현하였다.

  5. 회원가입
    : 술담화 회원가입 유효성 검사에서 생각보다 까다로웠다. 이 부분 팀원들과 상의한 끝에 정규표현 식으로 해결을 하여 프론트쪽에서 1차 검사를 하여 양식에 맞지 않으면 회원가입을 불가하게 만들었다.

기억하고 싶은 코드 1

const goToLogin = e => {
    e.preventDefault();
    if (idInput && pwInput) {
      fetch('http://10.58.2.197:8000/users/login', {
        method: 'post',
        body: JSON.stringify({
          email: idInput,
          password: pwInput,
        }),
      })
        .then(response => response.json())
        .then(result => {
          if (result.message === 'SUCCESS') {
            localStorage.setItem('token', result.token);
            alert('환영합니다!');
            navigate('/detail/14');
          } else {
            alert('확인해주세요');
          }
        });
    } else {
      alert('이메일 및 비밀번호를 확인해 주세요');
    }
  };

기억하고 싶은 첫번째 코드는 로그인 로직이다.
idInput 에 담긴 값과 pwInput 에 모두 값이 담기면 서버로 그 값을 보내서 서버에 저장된 id값과 pw값과 일치되는지 확인 후 그 값이 맞다면 토큰값을 부여하고 같지 않다면 alert 창을 통해 유저에게 입력된 값이 맞는지 확인하게 해주는 로직이다.

기억하고 싶은 코드 2

라이브러리를 사용하지 않고 순수 리액트를 통해서 구현한 모달창 역시 꽤 기억에 남는 코드이다.

 const CardBox = () => {
 const [modalOpen, setModalOpen] = useState(false);
 const outModal = useRef();
 const [product, setProduct] = useState([]);
 const [modalId, setModalId] = useState();

 const fetchData = () => {
   fetch(`http://10.58.2.197:8000/products/${modalId}/subscribe`)
     .then(response => response.json())
     .then(data => {
       setProduct(data.subscribe_detail[0]);
     });
 };

 useEffect(() => {
   typeof modalId !== 'undefined' && fetchData();
 }, [modalId]);

const openModalFn = id => {
   setModalOpen(true);
   setModalId(id);
 };

modalOpen state 를 초기값을 false 로 설정해주고 openModalFn 을 통해서 true 로 변경시켜서 모달창을 띄운다.
또한, modal 에서 사용할 데이터를 setProduct 에 담아서 저장하였다

useEffect(() => {
  const handleEscapeKey = event => {
    if (event.code === 'Escape') {
      setModalOpen(false);
    }
  };
  document.addEventListener('keydown', handleEscapeKey);

  const handleClickOutside = e => {
    if (outModal.current && !outModal.current.contains(e.target)) {
      setModalOpen(false);
    }
  };
  document.addEventListener('mousedown', handleClickOutside);

  return () => {
    document.removeEventListener('mousedown', handleClickOutside);
    document.removeEventListener('keydown', handleEscapeKey);
  };
}, []);

      return (
        <CardBoxComponent
          key={id}
          id={id}
          openModal={() => setModalOpen(true)
          openModalFn={openModalFn}
          modalOpen={modalOpen}
        />
      );
    })}
    {modalOpen && (
      <Modal
        closeModal={() => setModalOpen(false)}
        outModal={outModal}
        product={product}
      />
    )}
  </div>
);
};

event.code. 를 통해서 "esc" 버튼을 눌렀을때 setModalOpen(false) 로 변경시켜서 모달창을 꺼주게 구현하였고,
"esc" 로직과 비슷한 로직으로 addEventListner 을 이용해서 모달 외부 클릭시 혹은 "X" 버튼을 클릭시 setModalOpen(false) 로 변경시켜서 모달창을 꺼주게 구현하였다.

const Modal = ({ outModal, closeModal, product }) => {
 return (
   <div className="modal" ref={outModal}>
     <div className="madalBack" onClick={closeModal} />
     <div className="modalPage">
       <button className="modalBtn" onClick={closeModal}>
         X
       </button>
       <div className="modalbox">
         <div className="modalOpen">
           <img
             className="modalImage"
             src={product.product_image}
             alt="이미지"
           />
           <div className="descriptionDetail">
             {product.description_detail}
           </div>
           <div className="descriptionTag">{product.description_tag}</div>
           <div className="alcholPercentage">
             도수 : {product.alcohol_percentage}%
           </div>
           <div className="price">
             가격 : {parseInt(product.price).toLocaleString()}</div>
         </div>
       </div>
       <div>
         <style jsx="true">{`
           body {
             overflow: hidden;
           }
         `}</style>
       </div>
     </div>
   </div>
 );
};

outModal, closeModal, product 를 props 로 받아와서
모달 내부에 사용하는 데이터를 뿌려주었고, 각 버튼에 onClick event 를 활용해서 모달을 닫는 기능을 부여하였다.

기억하고 싶은 코드 3

캐러셀 또한 라이브러리 없이 순수 리액트를 사용해서 구현하였다.

const Subscribe = () => {
 const [index, setIndex] = useState(0);

 const leftclickhandler = () => {
   return index !== 0 && setIndex(index - 1);
 };

 const rightclickhandler = () => {
   return index !== 2 && setIndex(index + 1);
 };

 const changeBtn = idx => {
   setIndex(idx);
 };

상위 components 인 Subscribe 에서 유저 눈에 가시화된 회전하는 화면단을 index로 저장하고 최초값을 0으로 설정해주었고,
좌측, 우측을 막는 동시에 좌우로 회전하는 함수를 index - 1, index + 1 를 통해서 구현하였다.
그리고, changeBtn 함수에 setIndex를 설정해주는 값을 걸어서 해당 index 에 해당하는 버튼을 클릭시 해당된 화면으로 넘어가게 구현하였다.

const Carousel = ({
 leftclickhandler,
 rightclickhandler,
 index,
 changeBtn,
}) => {
 return (
   <div className="Carousel">
     <div className="dontWorryWrapper">
       <div className="leftIcon">
         <button onClick={leftclickhandler} />
       </div>
       <div className="dontWorrySlideBox">
         <div
           className="dontWorrySubSlideBox"
           style={{
             transform: `translateX(${index * -375}px)`,
             transition: `transform 1s`,
           }}
         >
         <div className="rightIcon">
           <button onClick={rightclickhandler} />
         </div>

캐러셀 좌, 우측에 버튼에 onClick event에 leftclickhandler, rightclickhandler 를 걸어주었다
또한 dontWorryBox 박스에 style을 적용하여서 전체 캐러셀에 1/3 만큼 유저눈에 보이게 구현하였다.

         
         
          <ul className="buttonWrapper">
            <li className="slideButton" onClick={() => changeBtn(0)} />
            <li className="slideButton" onClick={() => changeBtn(1)} />
            <li className="slideButton" onClick={() => changeBtn(2)} />
          </ul>
    

changeBtn 을 이용해서 index를 변환하여 각 버튼 클릭시 해당하는 화면단으로 이동시키게 구현하였다.



Project Review

프론트 - 백엔드의 Communication

프론트엔드, 백엔드 등 다양한 분야의 사람들이 프로젝트를 하는 첫 경험이었다. 백엔드 분야의 팀원들은 프론트에서 데이터를 어떤 방식으로 사용하는지 몰랐고, 마찬가지로 프론트 분야의 팀원들은 백엔드에서 어떤 방식으로 데이터를 넘겨주는지에 대한 지식이 너무 부족한 상황이었다. 프로젝트를 진행하는 과정에서 프론트엔드 - 백엔드의 팀원들이 어떤 방식으로 데이터를 주고 받아야하는지에 대한 대화가 많이 이루어졌다. 이 과정에서 시간적인 낭비가 분명히 있었기 때문에 더 간결하고 가독성이 좋은 코드나 기능구현 부분에서 미흡했을 수 있지만, 팀원들 간의 의사소통이 얼마나 중요한 첫 단추인지를 충분히 배우는 시간이 되었다고 생각한다.

실수를 통해 배워가는 Git

개인 프로젝트에서 Git을 활용할 때와 팀 프로젝트에서 Git을 활용할 때 많은 차이가 있다는 것을 배우는 시간이었다. 혼자 프로젝트를 관리할 때는 코드가 꼬인다거나 컨플릭이 발생하는 일이 없었지만, 팀 프로젝트는 코드가 꼬이는 경우도 잦았고 크리티컬한 컨플릭을 발견하여 초반에 상당한 시행착오를 겪었다. issue를 해결하기 위하여 팀원들과 소통을 통해 Git에 대한 활용법을 많이 배우는 시간이었다.

시간관리

프로젝트를 진행하는 2주라는 기간 중 실질적으로 프로젝트에 집중할 수 있는 시간은 매우 한정적이었다.
팀원들과 함께 작업의 중요도, 우선도 등을 정하여 확실하게 진행할 수 있는 작업 등을 우선적으로 진행하였다. 이 때 기간 내 작업 등을 효율적으로 관리하기 위해 사용했던게 Trello였다. 내가 계획하였던 작업의 진행도 등을 객관적으로 판단하고, 진행되고 있는 부분을 보완하여 좀 더 탄탄하게 프로젝트를 마무리 할 수 있게 일정 관리를 한 것이 많은 도움이 되었다.

아쉬운 부분

  1. 소통
    프로젝트를 하면서 개인적으로 아쉬운 부분이 많다
    먼저, 백엔드와 소통할때 그 용어와 개념들이 생소해서 완벽한 소통을 하지 못했다 서로 같은 내용의 대화를 다른 언어로 하는 느낌이었다 하지만 프로젝트에 더 심도있게 들어가고, 젖어 들 때 쯤 백엔드에서 하는 말들과 내용을 어느 정도 이해 할 수 있었다 조금 더 빨리 잘 이해했다면 착오없이 더 좋은 성과를 낼 수 있을까 라는 생각을 한다.

  2. 몰입의 부작용, 나무만 보는 시야
    어는 작은 목표에 집중해서 코드를 치고 있으면 문뜩문뜩 내가 뭘 하고 있는지 란 생각을 할 때가 있었다.
    독서를 할 때 목차를 보면서 독서하는 습관이 있는데 그 이유는 내가 지금 집중해서 읽는 책의 내용이 이 책 전체에서 어디에 해당되는지 무엇을 가르키는지 그 방향을 체크 하기 위해서다.
    하지만 개발을 할때는 그 목표를 설정하고 설계없이 달려들기 바빴다 그래서 그 몰입의 끝에 문뜩 내가 뭘 하고 있는지 놓칠때가 종종 있었다 다음 프로젝트 혹은 앞으로 개발을 할 때는 나무를 보고 달리는 것도 너무 중요하지만 숲을 주기적으로 보는 시야를 가져야 겠다는 생각을 했다.

  3. 코드
    2주가 지난 시점에서 내가 쓴 코드를 쭉 들여다 보고 있으면 정말 솔직히 아직 이해하지 못한 부분들이 있다.
    동기분들의 도움을 받은 부분, 구글에서 긁어서 붙인 부분, 디버깅 중 우연히 구현된 부분 등 내 코드, 로직이라고 말하기 너무 부끄러운 부분들이 많다 하지만, 내 로직이 아니라고, 내 머리속에서 나오지 않았다고 나를 자책하고 몰아세워도 봤지만 그럴수록 나만 더 괴롭고, 오히려 개발하는데 있어서 동력을 떨어지게 했다.
    아직 개념이 부족해서, 경험이 부족해서, 공부한 기간이 얼마되지 않았는데 너무 많은 부분을 나에게 바란거 같기도 하다 조급해 하지 말고 조금씩 하루하루 깔끔한 코드를 작성하고 싶다.

프로젝트 후기

촉박한 기간내에 팀원들과 공동의 목표를 이뤘기에 굉장히 만족스러웠다.

하지만 개인적으로는 아쉬운 부분이 너무 많았다
최초 기획에 없던 구독페이지를 개인적인 욕심으로 만들었다 어찌보면 팀 보다 내 경험을 우선시 했던 거 같다 그로 인해서 백엔드에서 구축해야할 데이터도 추가되어서 부담을 드린 거 같다 다음 프로젝트 할때에는 팀을 우선시해서 공동의 목표에 더 크게 기여하고 싶다.

그렇지만 좋은 팀원들과 함께 일하고, 그 속에서 성장 할 수 있어서 너무 감사하고 행복했다

조금 더디고 느려도 올바른 방향으로 꾸준히 성장하고싶다.

profile
프론트엔드 개발자 이형준입니다.

0개의 댓글