글 작성자: 개발자 올라프

프로젝트

 

  • 주제 : 쿡킷 사이트 클론 코딩 - 밀키트 판매 서비스 (e-commers)
  • 기간 : 2022/05/23 ~ 2022/06/02 (11일)

 

프로젝트 팀 🌼

 

  • 팀명 : 남바완(Namba1)
  • Front-end
    • 김혜수 : 회원가입 페이지, 로그인 페이지, Footer
    • 박주영 : 리뷰 페이지
    • 천은별 : 메뉴 리스트 페이지, 상품 상세 페이지
    • 최현민 : 메인 페이지, Nav
  • Back-end
    • 임한구 : 모든 기능 API 구현

 

개발 도구 🛠

 

  • Front-end : HTML / CSS / SCSS / JavaScript(ES6) / React.js
  • Back-end : Python / Django Web Framework /  MySQL / Bcrypt / JWT
  • 협업 도구 : Git / Github / Trello / Slack / AWS

 

 

  • Backlog : 이번 프로젝트 기간 중 구현할 사항을 작성한다.
  • This Sprint : 일주일 간 구현할 기능을 담는다.
  • In Progress : 현재 구현 중인 기능을 담는다.
  • In Review : 구현 후, 리뷰를 받고 있는 기능을 담는다.
  • Done : 구현 완료한 기능을 담는다.
  • Meeting : 매 회의 내용을 기록한다.
    • Planning Meeting : 프로젝트 진행 전 회의. Backlog 구현 목표 설정 및 프로젝트에 관련된 회의를 진행
    • Sprint Meeting : 매주 첫 번째에 진행하는 회의. 각자 이번 주 동안 구현할 기능들에 대해서 회의하는 시간
    • Retrospective Meeting : Sprint Meeting 종료 후, 한 주 동안 아쉬웠던 점 혹은 개선할 사항에 대해서 회고하는 시간
    • Daily Standup Meeting : 매일 30분 이내로 현재 프로젝트 진행 상황에 대해서 간단한 브리핑을 하는 시간

 


 

프로젝트 시연 영상 📺

 

 


 

구현 사항 💭

 

캐러셀 (메인, 리뷰 캐러셀)

메인 캐러셀 기능 구현 (이미지 제작 : 주영님)
한쪽은 opacity, absolute를 이용한 캐러셀, 한쪽은 사진 x축 이동을 이용한 캐러셀

 

// ******* 메인 캐러셀 코드 *******
const MainCarousel = () => {
  const totalSlide = MAIN_SLIDE.length - 1;
  const [currentSlide, setCurrentSlide] = useState(0);
  const slideRef = useRef(null);

  const nextSlide = () => {
    if (currentSlide >= totalSlide) {
      setCurrentSlide(0);
    } else {
      setCurrentSlide(currentSlide + 1);
    }
  };

  const prevSlide = () => {
    if (currentSlide === 0) {
      setCurrentSlide(totalSlide);
    } else {
      setCurrentSlide(currentSlide - 1);
    }
  };

  useEffect(() => {
    slideRef.current.style.transition = 'all 1.0s ease-in-out';
    slideRef.current.style.transform = `translateX(-${currentSlide}00%)`;
  }, [currentSlide]);

  return (
    <div className="mainCarousel">
      <div className="mainCarouselDisplay" ref={slideRef}>
        {MAIN_SLIDE.map(({ id, src, alt }) => (
          <img key={id} className="mainCarouselImg" src={src} alt={alt} />
        ))}
        ;
      </div>
      <button className="prevButton" type="button" onClick={prevSlide}>
        <FontAwesomeIcon icon={faArrowLeft} />
      </button>
      <button className="nextButton" type="button" onClick={nextSlide}>
        <FontAwesomeIcon icon={faArrowRight} />
      </button>
      <div className="currentSlide">
        {currentSlide + 1} / {MAIN_SLIDE.length}
      </div>
    </div>
  );
};

const MAIN_SLIDE = [
  { id: 1, src: '/images/main/banner1.png', alt: 'event' },
  { id: 2, src: '/images/main/banner2.png', alt: 'newMenu' },
  { id: 3, src: '/images/main/banner3.png', alt: 'delivery' },
  { id: 4, src: '/images/main/banner4.png', alt: 'present' },
];

export default MainCarousel;

 

1차 프로젝트 기간에는 라이브러리 사용이 금지되었다. 처음에는 캐러셀 기능을 어떻게 구현해야 할지 감을 잡지 못했다. 캐러셀이라는 키워드조차 몰랐으며 슬라이드로 알고 있던 나였다. 한 동료에게 "슬라이드 기능 어떻게 구현할지 감이 안 잡힌다." 했더니 "캐러셀이요?"라는 대답이 돌아왔다. 그렇게 캐러셀이라는 것에 대하여 처음 알게 되었고, 그대로 멘토님에게 캐러셀을 어떠한 방식으로 구현해야 하는지 질문을 했더니 아래와 같은 그림을 그려주셨다.

 

 

처음에는 정말 그림을 보고 난감했다. 그림 하나만 그려주시고 알아서 구현하라는 느낌에 당혹스러움을 느꼈지만 구현하고 나서는 모든 내용을 담은 그림이었음을 느꼈다. 구현하기 전에는 정말 이해하기 어려웠지만, 구글링을 통해서 한 번 구현하고 나서는 간단한 내용이었고, 모든 캐러셀이 비슷한 원리로 돌아가고 있음을 알게 되었다.

 

맛에 따른 음식 추천

 

 

useEffect(() => {
	location.search === '' ? navigate('/?themeId=8&sort=-id') : navigate('/');
}, []);

useEffect(() => {
	fetch(`http://${ip}/products${location.search}`)
		.then(res => res.json())
		.then(data => {
			setTasteImg(data.product_list);
   	});
}, [tasteOption, ip, location]);

const getTasteBtn = tasteOption => {
    const queryString = `?themeId=${tasteOption}&sort=-id`;
    navigate(`${queryString}`);
}; // 맛 버튼 코드에서 보내온 id 값으로 쿼리파라미터로 만든다.
    
// ******* 맛 버튼 코드 일부 *******
<ul>
  {TASTE.map(({ id, taste }) => {
    return (
      <li
        key={id}
        onClick={e => {
          setTaste(taste);
          activateDropdown();
          getTasteBtn(id); // 여기서 맛을 선택할 때마다 해당 id 값을 쿼리파라미터로 전송한다.
        }}
      >
        {taste}
      </li>
    );
  })}
</ul>

const TASTE = [
  { id: 8, taste: '매콤한맛' },
  { id: 7, taste: '짭짤한맛' },
  { id: 6, taste: '새콤한맛' },
  { id: 5, taste: '담백한맛' },
  { id: 9, taste: '이국적인맛' },
  { id: 4, taste: '얼큰한맛' },
];

 

queryParameter를 사용해서 처음에는 매콤한 맛에 해당하는 음식들을 기본 값으로 보여주고, 다른 맛을 클릭할 때마다

queryParameter값이 바뀌면서 재 렌더링 되어서 해당 맛 음식들 사진들로 대체되면서 추천한다. 이 기능을 구현하면서 useState의 유용성에 대해서 정말 감탄하게 되었다.

 

음식 리스트 구현하기

 

 

메뉴를 구현하는 부분에선 큰 어려움은 없었지만 새로 알게 된 grid를 사용해서 만들었기에 기억에 남는다. flex밖에 모르던 나는 끙끙대면서 신메뉴 부분 비율을 맞추고 있었는데, 같은 팀 은별님께서 메뉴 리스트를 너무 잘 만드셔서 어떻게 구현했는지 조언을 구했다. 본인은 grid를 사용해서 구현했다고 말씀하셨다. 나는 처음 들어보는 속성에 호기심을 가졌고 어떤 기능인지 간단한 설명을 부탁드렸는데 정말 내가 찾던 것임을 느꼈다. 작성한 CSS를 모두 지워버리고 w3school에서 grid를 잠깐 흝어보고 구현하게 되었다. 정말 좋은 속성을  같은 프로젝트를 진행하는 팀원 덕분에 알게 되어서 기억에 남는다. 메뉴의 추가적인 기능으로는 상품 id를 가져와서 useNavigate를 사용하여 해당 상품 상세 페이지로 이동할 수 있도록 하였다.

 

추가적으로 리스트를 구현하면서 고민했던 것이 하나 있는데 신메뉴 같은 경우는 컴포넌트로 만들어서 map함수를 사용하면 됐었지만, 남바완 메뉴 보기와 한정 수량 MD의 추천 구조가 비슷하여 다른 내용을 나타내면서 컴포넌트를 어떻게 재사용할지 고민을 많이 했다.

 

좌: Main.js, 우: Menu.js

 

Main.js에는 컴포넌트만 남겨놓고 싶었는데 어떻게 재사용하면 좋을까 고민하면서 그 목표는 깨졌던 것 같다. 일단 백엔드에서 fetch로 두 내용들을 남바완 메뉴 보기는 allMenuList에, 한정 수량 MD  추천 메뉴는 productList로 나누어 useState를 사용해서 저장하여 삼항연산자로 클래스명에 따라서 컴포넌트를 재사용해서 다른 내용을 불러오도록 했다.

 


 

프로젝트 후기

 

프로젝트를 진행하는 6팀 중에서 우리팀을 제외하면 모두 6~7명으로 구성되어 있었다. 비록 적은 인원이었지만 회의록과 Readme를 꼼꼼히 작성해주신 주영님🍋, 항상 늦은 시간까지 코딩하시고 깔끔한 코드로 배울게 많았던 은별님🌟, 항상 쳐지지 않도록 팀원 분위기를 밝게 만들어 주신 혜수님🐷, 유일하게 백엔드가 혼자인 팀이지만 묵묵히 프론트엔드의 쏟아지는 요청에 잘 맞춰주신 한구님🎅🏻 덕분에 1차 프로젝트를 좋게 마무리 할 수 있었다. 얼떨결에 PM을 맡게 되었지만, 모두 각자의 자리에서 최선을 다해 주셨기 때문에 프로젝트를 진행하는 내내 누가 이끌어 간다는 느낌 없이 함께 간다는 느낌을 받을 수 있었다.

 

처음에는 과연 한 달이라는 기간동안 학습한 내용과 11일이라는 짧은 시간내에 하나의 사이트를 클론코딩하는 프로젝트를 해낼 수 있을까 걱정이 앞섰지만, 막상 프로젝트가 진행되는 동안은 어떻게 좋은 코드를 작성할 수 있을지, 어떻게 기능 구현을 해낼 수 있을지에 대한 고민을 하면서 걱정 없이 개발에 집중하는 시간을 보낼 수 있었다. 프로젝트를 진행하는 동안 다른 사람들은 어떻게 코드를 작성 했는지 공유하면서 새로운 사실들을 익히며 하루하루 발전하는 나를 볼 수 있었다. 개발에 입문하는 모두에게 너무나 짧은 시간이어서 완벽한 코드를 작성했다고 할 수는 없었지만, 2주동안 각자 최대한의 시간과 고민을 쏟아 부어서 만들어진 코드가 담겨있는 프로젝트이지 않을까 싶다.

 

2주일 동안 함께한 남바완 팀

 


 

아쉬운 점, 개선하고 싶은 점 👨‍🔧

 

  1. 백엔드와 통신하는 부분에서 fetch를 사용할 때 GET 방식 밖에 없었던 점. 다음 기회에 GET 이외의 방식인 POST 방식으로 통신할 기회가 있었으면 좋겠다.
  2. github 메인 브랜치에 merge할 때 CSS가 깨지는 현상이 발생했고, 겹치는 클래스명이 있기에 누군가는 수정해야 했다. 다음 번에는 조금 더 페이지와 기능의 특징을 잘 나타내는 클래스명을 사용하여 이러한 문제가 발생되지 않도록 하고 싶다. 혹은 기능을 개발할 때 소통에 조금 더 신경을 써서 이러한 문제가 발생하지 않도록 하고 싶다.
  3. 캐러셀을 구현하는데 3일 ~ 4일 시간을 허비했는데 과연 옳은 행동 이었는지 의문이다. 정답을 빨리 알아내서 해결하고서 다른 기능을 조금 더 구현 해보는게 옳았을지, 길게 고민하고 넘어가는 것인지 옳은 것인지 모르겠다.
  4. 회의를 진행할 때, 백엔드 내용이 이해되지 않더라도 넘어갔던 경향이 있다. 다음 프로젝트 때는 백엔드 내용에 조금 더 집중해서 소통하고, 회의록을 꼼꼼히 작성해서 프로젝트의 흐름을 더욱 깊게 이해하고 싶다.
  5. 기능별로 branch를 생성하여 작업하는 방식이 익숙하지 않았다. 다음 프로젝트에는 기능별로 branch를 생성하여 작업하는 방식에 익숙해지고자 한다.