Skip to content

kyungmim/ohgoodcoffee

 
 

Repository files navigation

☕ Oh Good Coffee ☕

B2B(카페, 제조업체), B2C(소비자)를 하나로 연결하는 버티컬 커머스 플랫폼 🔗



새로 생긴 카페부터 탄탄한 브랜딩이 인상깊은 카페를 매거진을 통해 만나고 👀
멀리있어 직접 가기 힘들었던 카페의 커피와 굿즈를 집에서 손쉽게 🛒







🗝️ OGC 배포 링크 및 테스트 계정

👉 OGC 시작하기

<user 로그인>
ID: [email protected]
PW: 11111111
<seller 로그인>
ID: [email protected]
PW: 11111111

📑 프로젝트 관련 자료

👉 ☕ 피그마 디자인 시안
👉 ☕ 피그마 유저플로우
👉 ☕ 보람3조 노션 페이지



👼 1. OGC 팀원 소개

안녕하세요, 저희는 🦁멋쟁이사자처럼 9기 3조 보람3조🦁입니다 !

‘형설지공(螢雪之功)’이란 사자성어를 아시나요??
형설지공은 ‘반딧불’의 불빛과 눈빛으로 이루어 낸 성공이라는 뜻으로 어려움을 이겨 내고 공부하여 얻는 보람을 의미합니다.

각기 다른 분야에서 종사하던 사람들이 모여 같은 목표를 향해 나아가 어려움을 이겨내고 공부해 보람을 느끼자는 의미에서 팀명을 짓게 되었습니다!

📢 2. 서비스 소개

OGC는 ✨커피를 사랑하는 소비자와 열정적인 카페 판매자를 연결하는 혁신적인 버티컬 커머스 플랫폼✨입니다.
커피 문화가 확산되면서 대형 프렌차이즈 카페 뿐만 아니라 소규모 카페의 수요도 크게 늘어나고 있습니다. 그 결과, 지역에 관계없이 사람들은 다양한 카페를 방문하고 그들의 특별한 제품을 즐기고 싶어합니다.

그러나 종종 소비자들은 원하는 카페의 제품을 구매하기 위해 멀리 이동해야 하는 불편함을 겪곤 합니다. OGC는 이러한 불편함을 해소하기 위해 나섰습니다.
우리는 소규모 카페의 굿즈나 원두를 온라인으로 제공하여, 소비자들이 원하는 제품을 손쉽게 구매할 수 있도록 편의를 제공합니다. 더 나아가, 우리의 플랫폼은 다양한 카페의 제품을 방문없이도 체험할 수 있는 기회를 제공하여 더욱 다채로운 커피 경험을 선사합니다.

이 플랫폼은 판매자에게도 큰 장점을 제공합니다. 소비자들의 니즈와 수요를 파악할 수 있는 동시에, 브랜딩을 위한 다양한 아이템을 개발할 수 있는 기회를 제공합니다. 또한 매거진을 통해 다양한 카페를 소개함으로써 소비자들에게 폭넓은 커피 경험을 제공하고, 카페들에게는 더 많은 브랜딩 기회를 제공합니다.

OGC는 커피 열정가들을 위한 새로운 커머스 플랫폼으로, 소비자와 판매자 간의 소통과 경험을 풍부하게 만들어 나갈 것입니다.
함께 우리의 커피 여정을 더욱 특별하게 만들어보시죠! ☕️



📅 3. 개발 일정

🏃‍♂️ 2024.04.01 - 2024.04.25

  • 개발 일정은 노션의 캘린더에 일정을 만들어 관리



👩‍👩‍👧‍👦 4. 역할 분담

  • 이경민 : 조장🐥 / 로그인/회원가입 페이지 / 헤더/푸터 / 유저∙셀러 마이페이지 / 메인 및 로딩페이지/ 에러 처리 / QA/QC / 지도API / 댓글 / 페이지네이션 / 무한스크롤
  • 홍설아 : 스크럼 마스터 / 마켓/장바구니 마크업 및 기능구현 / db 데이터 문서화 / 에러 처리 / QA/QC / 결제프로세스 / CSS Module
  • 심호영 : db 데이터 문서화 / 매거진 페이지 기능구현
  • 김해리 : 기획 / 마켓/매거진 페이지 마크업 / 마켓/유저 마이페이지 기능구현 / db 데이터 문서화/ 리드미 문서화



⚙️ 5. 개발 환경 및 기술 스택

개발 환경 - FE: React, Axios, Zustand, Vanila CSS
- BE: 제공된 RESTful API, Bruno
버전 및 이슈 관리 - Git, Github, Notion
프로젝트 관리 - Github Pull Requests
커뮤니케이션 - Notion, Discord
배포 - Netlify 작성

라이브러리

  • ReactQuery
    • 같은 데이터에대한 여러번의 요청이 있는 페이지에 적용
    • 비동기 로직을 부드럽게 다룰 수 있게 해주고, server state를 효율적으로 관리할 수 있을뿐만아니라 잠재적으로는 메모리 성능을 높이는데 도움이 되기에 선택

  • Moment.js

    • 등록순, 신상품순의 정렬을 위해 원하는 날짜포맷으로 간단하게 변환하기 위해 사용

      moment.js 코드
      export default function useAlertModal() {
        const [isAlertModalOpen, setIsAlertModalOpen] = useState(false);
      
        const alertModalHandler = {
          openModal: () => {
            setIsAlertModalOpen(true);
          },
          closeModal: () => {
            setIsAlertModalOpen(false);
          },
        };
      
        return { isAlertModalOpen, alertModalHandler };
      }

  • Lottie
    • 에러페이지, 로딩페이지를 구현
    • Lottie는 GPU를 활용하여 애니메이션을 처리하기 때문에 CPU 부하를 줄이고, 높은 성능을 유지할 수 있음
    • 파일크기의 최적화된 라이브러리로 로딩시간을 단축시키고, 데이터 사용량을 줄이고 전반적인 기능향상
    • 백터기반으로 확대되어도 품질저하가 없음

  • Swiper

    • 메인페이지의 슬라이더 구현
    • 필요한 기능만을 로드하여 불필요한 자원을 최소화 하기때문에 로딩시간과 성능에 긍정적
  • React-Kakao-Maps-SDK

    • 매거진 페이지 해당 게시물 카페 위치 불러오기

      Map.jsx 코드
      import { Map, MapMarker, MapTypeControl, ZoomControl } from 'react-kakao-maps-sdk';
      
      import PropTypes from 'prop-types';
      
      Location.propTypes = {
        data: PropTypes.shape({
          extra: PropTypes.shape({
            coordinates: PropTypes.shape({
              lat: PropTypes.string.isRequired,
              lng: PropTypes.string.isRequired,
            }),
            address: PropTypes.string,
          }),
        }),
      };
      
      function Location({ data }) {
        if (!data || !data.extra || !data.extra.coordinates) {
          return null;
        }
      
        const { lat, lng } = data.extra.coordinates;
        return (
          <>
            <Map className="map" center={{ lat, lng }} style={{ width: '100%', height: '400px' }} level={1}>
              <MapTypeControl position={'TOPRIGHT'} />
              <ZoomControl position={'RIGHT'} />
              <MapMarker position={{ lat, lng }}></MapMarker>
            </Map>
          </>
        );
      }
      export default Location;



🔖 컨벤션

eslint

module.exports = {
  root: true,
  env: {
    browser: true,
    es2020: true,
  },
  extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended', 'prettier'],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  settings: { react: { version: '18.2' } },
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
  },
};

prettier

module.exports = {
  // 문자열에 single quote 사용(기본값 true)
  singleQuote: true,
  // 코드 마지막에 세미콜론 추가(기본값 true)
  semi: true,
  // 들여쓰기를 탭으로 지정할지 여부(기본값 false)
  useTabs: false,
  // 들여쓰기 너비 2칸(기본값 2)
  tabWidth: 2,
  // 여러 줄의 쉼표로 구분된 구문 구조에서 후행 쉼표를 추가(none: 설정 안함, es5: 객체,배열에 설정, all(기본값): 함수 정의나 호출 등 가능한 모든 곳에 설정)
  trailingComma: 'all',
  // 한줄에 200 글자가 넘어가면 줄바꿈(기본값 80)
  printWidth: 200,
  // 화살표 함수의 매개변수가 하나만 지정될 때 괄호 생략(always: 항상 괄호 명시, avoid: 가능하면 생략)
  arrowParens: 'always',
  // windows에 뜨는 'Delete cr' 에러 해결
  endOfLine: 'auto',
};

커밋 컨벤션

🎉 Init: 프로젝트 첫 개시
🚧 edit: 기능 및 코드 수정 중
✨ feat: 기능 및 코드 추가
🔥 Remove: 기능 및 코드 제거
🐛 fix: 버그, 오류 수정
📝 docs: readme.md, json 파일 등 수정, 라이브러리 설치 (문서 관련, 코드 수정 없음)
💄 style: CSS 등 사용자 UI 디자인 변경 (제품 코드 수정 발생, 코드 형식, 정렬 등의 변경)
♻️ refactor: 코드 리팩토링
🧪 test: 테스트 코드 추가, 삭제, 변경 등 (코드 수정 없음, 테스트 코드에 관련된 모든 변경에 해당)
🐎 Ci: npm 모듈 설치 등
🐳 Chore: 패키지 매니저 설정할 경우, etc 등 (ex. gitignore)
💬 comment: 필요한 주석 추가 및 변경
🚚 rename: 파일 또는 폴더 명을 수정하거나 옮기는 작업만인 경우
🚑 !HOTFIX: 치명적인 버그 수정
📱 Responsive: 반응형 작업
🚀 Deploy: 프로젝트 배포



🗂 6. 프로젝트 폴더 구조

🏠 OhGoodCoffee
├─ 📂 api     ───────────────────── 📲 Axios 인스턴스 및 API 관련 모듈 함수
├─ .env     ──────────────────── ⚙️ 환경변수 설정 파일
├─ .eslintrc     ──────────────────── ⚙️ eslint 설정 파일
├─ .gitignore
├─ .prettierrc   ──────────────────── ⚙️ prettier 설정 파일
├─ index.html
└─ 📦 src
  ├─ 📂 assets  ───────────────────── 🧸 이미지 등 정적 리소스
  ├─ 📂 components
  │  └─ 📂layout   ──────────────────── 🪅 공통으로 사용되는 레이아웃
  │     ├─ 📜Footer.jsx
  │     ├─ 📜Header.jsx
  │     ├─ 📜Index.jsx
  │     └─ 📜Modal.jsx
  ├─ 📂hooks ─────────────────────── ♻️ 커스텀 훅 폴더
  │  └─ 📜useCustomAxios.mjs
  ├─ 📂pages   ───────────────────── 📲 라우팅이 적용된 주로 API를 요청하는 페이지 폴더
  │  ├─ 📂cart
  │  │  ├─ 📜CartList.jsx
  │  │  ├─ 📜CartListItem.jsx
  │  │  └─ 📜CheckBox.jsx
  │  ├─ 📂magazine
  │  │  ├─ 📜MagazineDetail.jsx
  │  │  ├─ 📜MagazineList.jsx
  │  │  └─ 📜MagazineListItem.jsx
  │  ├─ 📂market
  │  │  ├─ 📜HeartIcon.jsx
  │  │  ├─ 📜MarketDetail.jsx
  │  │  ├─ 📜MarketList.jsx
  │  │  └─ 📜MarketListItem.jsx
  │  ├─ 📂mypage
  │  │  ├─ 📂seller
  │  │  │  ├─ 📜SellerInfo.jsx
  │  │  │  ├─ 📜SellerInfoEdit.jsx
  │  │  │  ├─ 📜SellerMyPageHeader.jsx
  │  │  │  ├─ 📜SellerSalesList.jsx
  │  │  │  ├─ 📜SellerSalesListEdit.jsx
  │  │  │  ├─ 📜SellerSalesListItem.jsx
  │  │  │  └─ 📜SellerUploadProduct.jsx
  │  │  └─ 📂user
  │  │     ├─ 📜userInfo.jsx
  │  │     ├─ 📜userInfoEdit.jsx
  │  │     ├─ 📜userMyPageHeader.jsx
  │  │     ├─ 📜UserOrderList.jsx
  │  │     ├─ 📜UserOrderListItem.jsx
  │  │     ├─ 📜UserOrderProductItem.jsx
  │  │     ├─ 📜UserReviewItem.jsx
  │  │     ├─ 📜UserReviewList.jsx
  │  │     ├─ 📜UserWishList.jsx
  │  │     └─ 📜UserWishListItem.jsx
  │  ├─ 📜About.jsx
  │  ├─ 📜ErrorPage.jsx
  │  ├─ 📜Loading.jsx
  │  ├─ 📜Login.jsx
  │  ├─ 📜Mainpage.jsx
  │  ├─ 📜PaymentComplete.jsx
  │  └─ 📜SignUp.jsx
  ├─ 📂zustand
  │  ├─ 📜store.js
  │  └─ 📜useModalStore.mjs
  ├─ 📜App.css
  ├─ 📜App.jsx
  ├─ 📜index.css
  ├─ 📜main.jsx
  └─ 📜routes.jsx

🧙 성능 최적화



✴️ 7. Git Branch 전략 - Github Flow

Git-flow에는 3가지 종류의 브랜치가 존재합니다.
항상 유지되는 메인 브랜치(main, dev)와 기능 개발 기간 동안 유지되는 보조 브랜치인 feature 가 있습니다.

  • main: 제품으로 배포 가능한 상태만을 위한 브랜치
  • dev: 기능 개발을 위한 브랜치들을 병합하는 브랜치
  • feature: 기능을 개발하는 브랜치
  1. 공통으로 사용하는 환경 구성해 github에 레포지토리 생성
  2. merge관련 오류를 대비해 Repository fork
  3. fork한 내용에서 각자 feature 브랜치를 생성하여 그 안에서 기능개발
  4. 기능개발 작업이 완료된 feature 브랜치를 개인 dev로 먼저 merge
  5. 문제 없이 병합이 완료되었다면 개인 dev 브랜치의 내용을 팀 main dev 브랜치로 PR 요청
  6. PR은 리더 1명이 관리, 내용에 이상이 없다면 main dev로 merge
  7. main dev에서 main으로 merge하여 배포



🙆 8. 팀 협업 방법

협업툴

  • 노션: 팀 노션을 통해 래퍼런스 및 정보 공유와 회의록, 일정 관리 및 컨벤션을 공유하여 원활한 팀 전체 커뮤니케이션 향상
  • 피그마: 사이트 컨셉 디자인, 전체 페이지 UI 작업 등 전반적인 공동 디자인 작업을 피그마를 통해 진행함
  • 커밋 컨벤션: 협업을 위한 공동 커밋 컨벤션을 지정. 커밋 목적을 한번에 파악하기 쉽고 효과적인 이력 추적이 가능

소통

  • 디스코드 : 팀원들과의 원격 소통에 대부분 활용 및 정보 공유
  • 매일 11시, 16시 진행상황 공유 및 피드백, 해야할 일 공유
  • 프로젝트 기간동안 대부분 팀원들과 직접 만나 소통하여 오류 발생시 즉각적인 피드백과 문제해결 가능



📍 9. 주요 기능 소개

💡 주요 기능

  • 로그인 / 회원가입 기능을 구현하였습니다.
  • 장바구니 담기 기능, 장바구니 수량 카운트 기능을 구현하였습니다.
  • 장바구니 전체 삭제, 선택 삭제, 클릭한 아이템 구매하기 기능을 구현하였습니다.
  • 마켓 디테일 페이지에서 북마크 추가, 삭제 기능을 구현하였습니다.
  • 셀러 마이페이지에서 상품 목록 조회 기능/ 상품 등록 및 수정 기능 구현하였습니다.
  • 유저 마이페이지에서 상세 주문 내역 조회시 아코디언으로 보여주기 기능을 구현하였습니다.
  • 유저 마이페이지에서 북마크한 아이템 리스트 보여주기, 회원정보 수정 기능을 구현하였습니다.
  • Zustand를 사용하여 상태관리를 하였습니다.
  • React Query를 사용하여 데이터 캐싱 작업을 하였습니다.
  • 반응형 모바일/데스크탑 작업하였습니다.
  • 매거진 페이지에서 해당 게시물에 위치를 지도에 불러왔습니다.

메인 풀페이지



로그인



회원가입



어바웃 페이지



매거진 페이지



마켓 페이지

  • 전체 리스트, sort 버튼 정렬

  • 위시리스트 추가/삭제

  • 아이템 수량 업다운



장바구니 담기 기능, 장바구니 페이지 수량 추가, 삭제 기능


  • 장바구니 담기

  • 장바구니 수량 1개 미만이면 삭제 모달 띄우기

  • 전체 주문

  • 아이템 선택 삭제

  • 아이템 전체 삭제



유저마이페이지

  • 주문 내역 조회

  • 위시리스트 목록 조회

  • 내가 쓴 리뷰

  • 내 정보 조회 및 수정



셀러마이페이지

  • 상품 수정



✨ 10. 핵심코드

1. zustand 상태관리

import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';

const useUserStore = create(
  persist(
    (set) => ({
      user: null,
      setUser: (user) => set({ user }),
      itemId: null,
      setItemId: (itemId) => set({ itemId }),
      product: null,
      setProduct: (product) => set({ product }),
      magazine: null,
      setMgazine: (magazine) => set({ magazine }),
    }),
    {
      name: 'Acctoken',
      storage: createJSONStorage(() => sessionStorage),
    },
  ),
);

export default useUserStore;

2. sort 정렬

const getParams = (sortType) => {
  const params = {
    page: searchParams.get('page') || 1, // 페이지 파라미터가 없는 경우 기본값 1
    limit: 8,
  };

  if (sortType === 'lowPrice' || sortType === 'highPrice') {
    params.sort = JSON.stringify({ price: sortType === 'lowPrice' ? 1 : -1 });
  } else if (sortType === 'isNew') {
    params.custom = JSON.stringify({ 'extra.isNew': true });
  } else if (sortType === 'isBest') {
    params.custom = JSON.stringify({ 'extra.isBest': true });
  }
  return params;
};

3. 카트 아이템 수량 조절 기능

const handleRequest = async (cart) => {
  setIsProcessing(true);
  await axios.post('/carts', cart);
  const response = await axios.get('/carts');
  if (response.data.item) {
    setItems(response.data.item);
    setIsProcessing(false);
  }
};



🔫 11. 트러블 슈팅

4/11 Git conflit 관련

1. 발생이슈

  • FRONTEND09에 있는 ohgoodcoffee의 레포지토리를 각 팀원이 fork로 가져와 작업을 했음
  • 원래 fork로 작업을 할 때에는 github에서 Sync를 맞추고 git pull하는 작업을 잊지말고 해주어야하는데 중간과정이 누락되면서 conflict 이슈 발생

2. 발생원인

  • main Repo dev branch에서 수정된 사항을 팀원의 개인 Repo dev branch에 Sync를 맞추지 않은 상태, 즉 main Repo dev branch와 Sync가 맞지 않는 상황에서 본인의 feature branch를 개인 Repo dev에 Push한 상황으로 추정됨

3. 해결방안

  • git merge conflict 날 경우

    • 브랜치에서 커밋할 내용이 생겼다

      -> 먼저 포크된 레포의 dev를 원본과 sync한다

      -> 브랜치에서 git pull origin dev로 최신 dev와 병합 (여기에서 컨플릭트가 없다면 커밋)

      -> 컨플릿트 발생시 소스 제어에서 변경점 확인 후 해당 팀원과 상의하며 컨플릭트 해결

      -> 이후 커밋


  • 같은 파일을 함께 수정하는 경우

    • 같이 한번에 작업하지 않고 한사람씩 작업이 끝난다음에 하기
    • 포크해서 사용하기때문에 포크뜬 레포의 dev는 원래 싱크 맞추고 최신버전을 유지하기 위해서 사용됨
    • 그렇기 때문에 이 dev가 원본 dev와 항상 동일한 버전을 가지고 있어야 함 → 매일 작업 전에 꼭 Sync 맞추고, git pull 하고 작업 시작하기

4/11 컴포넌트 중첩라우팅

1. 발생이슈

  • User와 Seller 두가지 타입의 사용자 유형에 따라 Mypage의 Header를 다르게 지정하고 싶었지만 하드코딩으로 입력해 두어 두 개의 Header가 중복으로 렌더링되는 이슈 발생

2. 발생원인

  • 하드코딩으로 입력된 Header코드와 Routes에 연결된 파일이 중복으로 렌더링 되는 것으로 추정

3. 해결방안

  • 중첩 라우팅 방법 사용하여 해결

  • 컴포넌트 Route 관련

    • 중첩라우팅 < Outlet /> 사용방법
      • 우리의 Mypage component는 user와 seller에 따라 다른 Header를 가지고있음 → 이 때 필요한게 중첩 라우팅, children이 표시될 공간에 < Outlet /> 을 넣고, outlet을 import해서 사용 → 이 경우 Header는 고정하고 아래부분만 원하는 페이지로 렌더링가능
  const router = createBrowserRouter([
    {
      path: '/',
      errorElement: <ErrorPage />,
      element: <Layout />,
      children: [
        {
        index: true,
        element: <Mainpage />,
        },
        {
        path: 'home',
        element: <Mainpage />,
        },
        {
        path: 'about',
        element: <About />,
        },
        {
        path: 'magazine',
        element: <MagazineList />,
        },
        {
        path: 'magazine/detail',
        element: <MagazineDetail />,
        },
        {
        path: 'market',
        element: <MarketList />,
        },
        {
        path: 'market/detail',
        element: <MarketDetail />,
        },
        {
        path: 'users/login',
        element: <Login />,
        },
        {
        path: 'users/signup',
        element: <SignUp />,
        },
        {
        path: 'carts',
        element: <Cart />,
        },
        {
        path: 'mypage',
        element: <UserMypageHeader />,
        children: [
          {
          index: true,
          element: <UserOrderList />,
          },
          {
          index: true,
          element: <UserReviewList />,
          },
          {
          index: true,
          element: <UserWishList />,
          },
          {
          index: true,
          element: <UserInfo />,
          },
        ],
      },
    )]

4/19 카트리스트 아이템 수량체크 트러블 슈팅

1. 발생이슈

  • 각 카트리스트 아이템의 수량을 추가, 감소 시킬 때 1씩 올라가지 않고 여러번 클릭되어 한번에 3개가 저장되는 이슈 발생
  • 더블클릭으로 해당 + 버튼을 누르게 되면, 최대 수량까지도 세팅이 되어 POST 요청을 보내는 이슈 발생

2. 발생원인

  • 카트리스트의 handleAddQuantity와 handleReduceQuantity의 로직은 구매 가능한 수량을 체크하고 해당 수량 이내이면 productQuantity의 State값을 변경하게되는데, productQuantity가 변경되면 useEffect가 실행되어 handleRequst 함수를 실행한다. handleRequst 함수는 State에 저장된 productQuantity 수량과 상품의 Id를 POST 하고, 변경점이 적용된 새로운 카트리스트를 GET 해오는 함수이다.
  • handleRequst 함수의 사이클 실행이 완료된 후에 다시 POST 요청을 보내야하는데, handleRequst 함수의 사이클이 마무리 되지 않은 상태에 추가적으로 POST 요청을 계속 보내게 되어 생긴 이슈로 추정된다.

3. 해결방안

  • isProcessing, setIsProcessing이라는 useState 상태를 만들어 handleRequst 함수의 사이클 중간에 API 요청이 되지 않도록 코드 수정

  • 아래와 같이 수정하여 클릭하면 1씩 증가하도록 수정 완료

    const handleRequest = async (cart) => {
      setIsProcessing(true);
      await axios.post('/carts', cart);
      const response = await axios.get('/carts');
      if (response.data.item) {
        setItems(response.data.item);
        setIsProcessing(false);
      }
    };

4/22 페이지 이동 시 최상단으로 이동하지 않는 이슈

1. 발생이슈

  • 메인페이지 하단의 매거진아이템과 마켓아이템을 클릭하면 이동 페이지의 최상단으로 이동하는 것이 아닌 클릭했던 버튼의 위치로 이동하는 이슈

2. 발생원인

  • 리액트라우터 특성 상, 페이지 이동 시, 이전페이지의 스크롤 위치를 기억하기 때문에 해당 이슈 발생

3. 해결방안

  • 이동되는 페이지에 아래의 코드를 삽입하여, 페이지가 최초 마운트 될 때, 스크롤을 최상단으로 위치시킴

    import { useLocation } from 'react-router-dom';
    
    const { pathname } = useLocation();
    useEffect(() => {
      window.scrollTo(0, 0);
    }, [pathname]);

4/22 북마크 API 변경 관련 이슈

1. 발생이슈

  • 21일 작업한 북마크 기능의 구현은 detailpage에서 받아온 응답값 중 bookmarks의 배열을 계산하여 해당 상품의 디테일 페이지 내 하트아이콘 옆의 숫자에 표시 될 수 있도록 함수를 생성.
  • 해당 배열에서 사용자의 userId가 확인되면 isBookmarked의 state 값을 true로 변경하여 빨간색으로 표시될 수 있도록 설정 + 해당 배열에서 북마크 아이디를 추출하여 북마크 POST

2. 발생원인

  • API 응답값 변경으로 이슈 발생
  • 22일 API 변경으로 응답값이 bookmarks의 배열이 아닌 해당 상품의 북마크 갯수, 또 로그인한 사용자가 해당 상품을 북마크 했을 때, 해당 상품의 응답값에 myBookmarkId의 북마크 아이디 값이 포함되어있음

3. 해결방안

  • 수정된 API로 보다 간단하게 상품 응답값에서 bookmarks 값을 하트 아이콘 옆으로 불러오고, 해당 상품의 응답값에 myBookmarkId의 북마크 아이디 값이 있을 때 하트 아이콘의 색상을 빨간색으로 변경할 수 있도록 수정완료
  • myBookmarkId 를 그대로 전송하여 삭제 기능 수정 완료

4/23 sort option 변경점 바로 적용되지 않는 이슈

1. 발생이슈

  • 오른쪽의 sort option dropdown을 선택할 때마다 변경점이 바로바로 적용되지 않고 한 박자 느리게 적용되는 이슈 발생

2. 발생원인

  • sortType이 변경 될 때 마다, UseQuery로 불러온 캐싱된 Stale 데이터를 참조하고 있어 변경점이 바로 적용되지 않았음.

  • sort는 직접적으로 배열을 수정하여 해당 이슈가 발생함

    useEffect(() => {
      if (data == undefined) {
        return;
      }
    
      let sortedArr;
      if (sortType == 'lowPrice') {
        sortedArr = data.sort((a, b) => b.price - a.price);
      } else if (sortType == 'highPrice') {
        sortedArr = data.sort((a, b) => a.price - b.price);
      } else if (sortType == 'new') {
        console.log('qqqqq', data);
        let arr = data.map((item) => ({
          ...item,
          createdAt: item.createdAt.substring(0, 16),
        }));
        sortedArr = arr.sort((a, b) => {
          const dateA = moment(a, 'YYYY.MM.DD HH:mm');
          const dateB = moment(b, 'YYYY.MM.DD HH:mm');
          return dateA - dateB;
        });
      } else if (sortType == 'sales') {
        sortedArr = data.sort((a, b) => b.buyQuantity - a.buyQuantity);
      } else if (sortType == 'registration') {
        let arr = data.map((item) => ({
          ...item,
          createdAt: item.createdAt.substring(0, 16),
        }));
        sortedArr = arr.sort((a, b) => {
          const dateA = moment(a, 'YYYY.MM.DD HH:mm');
          const dateB = moment(b, 'YYYY.MM.DD HH:mm');
          return dateA - dateB;
        });
      }
      setSortProductList(sortedArr);
    }, [sortType]);

    3. 해결방안

    • UseQuery의 stale한 data를 참조하지 않도록 refresh된 data를 복사
    • sort는 직접적으로 배열을 수정하기 때문에 refresh된 data를 복사하여 sort를 진행
    • 아래와 같이 코드 적용 후, 해당 이슈 해결 완료
    const sortData = (data, sortType) => {
      switch (sortType) {
        case 'lowPrice':
          return [...data].sort((a, b) => a.price - b.price);
        case 'highPrice':
          return [...data].sort((a, b) => b.price - a.price);
        case 'new':
        case 'registration':
          return sortDateDescending(data);
        case 'sales':
          return [...data].sort((a, b) => b.buyQuantity - a.buyQuantity);
      }
    };
    const sortDateDescending = (data) => {
      let arr = [...data].map((item) => ({
        ...item,
        createdAt: item.createdAt.substring(0, 16),
      }));
      return arr.sort((a, b) => {
        const dateA = moment(a.createdAt, 'YYYY.MM.DD HH:mm');
        const dateB = moment(b.createdAt, 'YYYY.MM.DD HH:mm');
        return dateA - dateB;
      });
    };
    
    //data 나 sortType이 실제로 변경될 때만 정렬된 리스트를 다시 계산하도록 메모이제이션
    const sortedData = useMemo(() => {
      if (!data) return [];
      return sortData([...data], sortType);
    }, [data, sortType]);
    
    useEffect(() => {
      setSortProductList(sortedData);
    }, [sortedData]);
    
    useEffect(() => {
      if (!data) {
        return;
      }
      setSortProductList((prevList) => {
        return sortData([...data], sortType);
      });
    }, [data, sortType]);


♻️ 12. 리팩토링

  • CSS 코드
    • 파일을 pure로 index.css 하나의 파일로 진행했으나, 이후의 유지보수를 위해 Module CSS로 각 페이지마다 적용되도록 수정 필요
  • 컴포넌트에도 UseQuery를 적용
    • 현재 mainpage, marketList, marketDetail 컴포넌트에서만 UseQuery를 사용하여 데이터 캐싱 작업이 되어있는데, 나머지 컴포넌트에도 UseQuery를 적용하여 데이터를 캐싱하도록 수정필요
  • 매거진 상세 페이지 텍스트 UI 가독성 개선



♻️ 13. 추가하고 싶은 기능

  • 검색 기능
  • 댓글 기능 완료
  • 페이지네이션 완료
  • 챗봇 채팅 기능
  • 이니시스 API 사용하여 결제
  • 지도 API를 사용하여 카페 위치 안내 완료
  • 관리자 계정 페이지 및 관리자 모드 개발
  • 마켓 리스트 페이지에서 페이지 이동없이 마우스 호버 시, 장바구니 or 구매하기



⏸️ 14. 프로젝트를 마무리하며..

🐤 경민

프로젝트 들어가기전에 제 자신 실력의 불신이 깊어 걱정이 많았는데 프로젝트를 하면서 공부하는 방법 코드를 쓰는 방법 등등 정말 알아가는것이 많은 프로젝트였고 덕분에 자신감도 많이 생겼던것같습니다. 처음해보는 프로젝트라 실수도 많고 못한것도 많지만 열심히하고 최선을 다해서 후회는 없는것같습니다!!! 보람3조 전부 프로젝트 한달동안 다들 멀리사는데 싫은 내색없이 매일 만나줘서 너무 고맙고 고생많으셨고 부족한 팀장이였지만 믿고 따라와줘서 너무 고맙습니다!! 프로젝트 끝나면 다시 으쌰으쌰해서 리팩토링도 열심히해봐여!! 다시 한번 고생많았고 보람3조 홧팅! 프엔9기 홧팅입니다!!

😺 설아

프로젝트가 처음이라 어려움이 많았지만 그 만큼 배웠던 부분들을 실제로 적용하면서 몰랐던 부분들을 배워나갈 수 있었고, 수업 시간에 이해했던 부분들 중에서도 실제 적용하니 이전에 이해했던 내용과 달라 제대로 다시 공부하는 과정에서 개발이 더 재밌게 느껴졌습니다. 작업하면서 궁금하거나 막히는 부분이 생길 때마다 찾아가서 여쭤볼 수 있는 길용쌤과 지혁멘토님이 계셔서 든든했습니다. 공간을 지원해주신 멋사 매니저님들까지 감사드립니다. 조원분들과 처음 만나 기획부터 배포까지 같이 작업하는 4주간 시간동안 배울 점이 정말 많았습니다. 다들 프로젝트가 처음이지만 맡은 바 최선을 다하는 모습을 보면서 동기부여가 많이되었습니다. 4주동안 모두 대면으로 만나 함께 작업하면서 더 프로젝트에 애정을 갖고 빡센일정을 소화할 수 있었습니다. 모두 경기도인이라 만나는 것이 어려울 수 있었지만 다들 열정으로 힘든 왕복 출퇴근 시간을 이겨낼 수 있지 않았나 생각이 듭니다. 모두 주말과 잠을 줄여가며 열심히 해주신 덕에 함께한 프로젝트를 더 즐겁고 완성도 있게 마무리할 수 있었고, 모두 최선을 다 했기에 후회없는 프로젝트가 된 것 같습니다! (물론 시간을 갖고 같이 업데이트를 하고싶지만요!) 항상 활기차게 우리조를 스트릭모드로 하드캐리 해주신 경민님, 유일한 J로 기획부터 일정뿐만아니라 프로젝트에 중요한 부분들을 캐리 해주신 해리님, 어려운 부분이 있어도 포기하지 않고 끝까지 맡은 바를 다 해주고 끝까지 불태워주신 호영님까지 모두 함께할 수 있어 즐거웠고 고생많으셨어요!

🐻‍❄️ 해리

멋사를 통해 또 좋은 동기분들 덕분에 정말 많은 기회를 얻은 것 같습니다! 처음의 제 모습을 생각하면 정말 아무것도 모르는 감자였는데 지금은 조금 깨우친 감자인 것 같아요.(맞겠지요..?)🤓 git을 사용하며 어떻게 소통하고 협업해야하는지에 대해 직접 느끼고, 사이트를 만드는 입장에서 어떤 부분을 고려해야좋을지, 또 프로젝트를 어떻게 진행하는지에 대해 배울 수 있었던 기회가 되었다고 생각합니다. 단기간이지만 정말 좋은 팀원분들을 만나서 혼자서는 어려웠을 문제들도 같이 해결해나가면서 집단지성의 힘을 또 다시 느끼게 되었고, 여전히 어렵고 무섭지만 코드를 어떻게 써야할지에 대한 고민을 하며 재미를 느끼게 되었던 것 같아요! 제 기획을 끝까지 믿어주시고, 응원해주시고, 현실로 만들어주신 팀원분들께 무한한 감사를...🙇🏻‍♀️ 프로젝트를 진행하면서 아쉬운 부분은 당연히 제 실력이었고, 조금 더 공부하고 잘 알았다면 프로젝트에서 다 같이 다양한 기능을 구현 해볼 수 있지 않았을까 하는 생각이 들어 아쉬움이 남습니다. 하지만, 팀원분들과 최선을 다했기에 후회는 없습니다! 한달이 채 되지 않는 시간동안 매일같이 만나서 보낸 시간들이 너무나 행복한 기억이 될 것 같고, 이 기간동안 함께 고생해준 팀원분들 너무나 고생많으셨습니다👍🩷

🐵 호영

멋쟁이사자처럼에서 진행한 처음이자 마지막 프로젝트 ! 개인적으로 준비가 너무 부족했던 프로젝트여서 너무 아쉽고 팀원들에게 미안한 마음이 듭니다 😿

부족한 저를 다독여주고 많이 도와준 팀원들이 없었다면 프로젝트를 진행하기 어려웠을 것 같습니다. 감사하고 고생 많으셨어요 다들 !!

실력이 많이 부족한 관계로 많은 기능 구현을 참여하지는 못했지만, 프로젝트 기획/디자인 부터 컨벤션을 통한 깃 활용, 사이트 구현의 a-z 까지 경험해볼 수 있는 소중한 기회가 되었던 것 같아서 좋았습니다.

이번 경험을 통해서 멋쟁이사자처럼 수료 이후에도 부족한 부분을 채워나가며 성장할 수 있게 되었다고 생각합니다. 좋은 강의를 해주신 강사님들과 많은 도움을 주신 매니저님과 멘토님, 프로젝트 팀원들과 회고조 동료를 너무 감사하고 모두 고생 많으셨습니다 🎉🎉



About

[3조] OhGoodCoffee

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 87.8%
  • CSS 12.0%
  • HTML 0.2%