Skip to content

[4팀 오하늘] Chapter 1-1. 프레임워크 없이 SPA 만들기#59

Open
eveneul wants to merge 61 commits intohanghae-plus:mainfrom
eveneul:main
Open

[4팀 오하늘] Chapter 1-1. 프레임워크 없이 SPA 만들기#59
eveneul wants to merge 61 commits intohanghae-plus:mainfrom
eveneul:main

Conversation

@eveneul
Copy link

@eveneul eveneul commented Jul 9, 2025

과제 체크포인트

배포 링크

https://eveneul.github.io/front_6th_chapter1-1/

기본과제

상품목록

상품 목록 로딩

  • 페이지 접속 시 로딩 상태가 표시된다
  • 데이터 로드 완료 후 상품 목록이 렌더링된다
  • 로딩 실패 시 에러 상태가 표시된다
  • 에러 발생 시 재시도 버튼이 제공된다

상품 목록 조회

  • 각 상품의 기본 정보(이미지, 상품명, 가격)가 카드 형태로 표시된다

한 페이지에 보여질 상품 수 선택

  • 드롭다운에서 10, 20, 50, 100개 중 선택할 수 있으며 기본 값은 20개 이다.
  • 선택 변경 시 즉시 목록에 반영된다

상품 정렬 기능

  • 상품을 가격순/인기순으로 오름차순/내림차순 정렬을 할 수 있다.
  • 드롭다운을 통해 정렬 기준을 선택할 수 있다
  • 정렬 변경 시 즉시 목록에 반영된다

무한 스크롤 페이지네이션

  • 페이지 하단 근처 도달 시 다음 페이지 데이터가 자동 로드된다
  • 스크롤에 따라 계속해서 새로운 상품들이 목록에 추가된다
  • 새 데이터 로드 중일 때 로딩 인디케이터와 스켈레톤 UI가 표시된다
  • 홈 페이지에서만 무한 스크롤이 활성화된다

상품을 장바구니에 담기

  • 각 상품에 장바구니 추가 버튼이 있다
  • 버튼 클릭 시 해당 상품이 장바구니에 추가된다
  • 추가 완료 시 사용자에게 알림이 표시된다

상품 검색

  • 상품명 기반 검색을 위한 텍스트 입력 필드가 있다
  • 검색 버튼 클릭으로 검색이 수행된다
  • Enter 키로 검색이 수행된다
  • 검색어와 일치하는 상품들만 목록에 표시된다

카테고리 선택

  • 사용 가능한 카테고리들을 선택할 수 있는 UI가 제공된다
  • 선택된 카테고리에 해당하는 상품들만 표시된다
  • 전체 상품 보기로 돌아갈 수 있다
  • 2단계 카테고리 구조를 지원한다 (1depth, 2depth)

카테고리 네비게이션

  • 현재 선택된 카테고리 경로가 브레드크럼으로 표시된다
  • 브레드크럼의 각 단계를 클릭하여 상위 카테고리로 이동할 수 있다
  • "전체" > "1depth 카테고리" > "2depth 카테고리" 형태로 표시된다

현재 상품 수 표시

  • 현재 조건에서 조회된 총 상품 수가 화면에 표시된다
  • 검색이나 필터 적용 시 상품 수가 실시간으로 업데이트된다

장바구니

장바구니 모달

  • 장바구니 아이콘 클릭 시 모달 형태로 장바구니가 열린다
  • X 버튼이나 배경 클릭으로 모달을 닫을 수 있다
  • ESC 키로 모달을 닫을 수 있다
  • 모달에서 장바구니의 모든 기능을 사용할 수 있다

장바구니 수량 조절

  • 각 장바구니 상품의 수량을 증가할 수 있다
  • 각 장바구니 상품의 수량을 감소할 수 있다
  • 수량 변경 시 총 금액이 실시간으로 업데이트된다

장바구니 삭제

  • 각 상품에 삭제 버튼이 배치되어 있다
  • 삭제 버튼 클릭 시 해당 상품이 장바구니에서 제거된다

장바구니 선택 삭제

  • 각 상품에 선택을 위한 체크박스가 제공된다
  • 선택 삭제 버튼이 있다
  • 체크된 상품들만 일괄 삭제된다

장바구니 전체 선택

  • 모든 상품을 한 번에 선택할 수 있는 마스터 체크박스가 있다
  • 전체 선택 시 모든 상품의 체크박스가 선택된다
  • 전체 해제 시 모든 상품의 체크박스가 해제된다

장바구니 비우기

  • 장바구니에 있는 모든 상품을 한 번에 삭제할 수 있다

상품 상세

상품 클릭시 상세 페이지 이동

  • 상품 목록에서 상품 이미지나 상품 정보 클릭 시 상세 페이지로 이동한다
  • URL이 /product/{productId} 형태로 변경된다
  • 상품의 자세한 정보가 전용 페이지에서 표시된다

상품 상세 페이지 기능

  • 상품 이미지, 설명, 가격 등의 상세 정보가 표시된다
  • 전체 화면을 활용한 상세 정보 레이아웃이 제공된다

상품 상세 - 장바구니 담기

  • 상품 상세 페이지에서 해당 상품을 장바구니에 추가할 수 있다
  • 페이지 내에서 수량을 선택하여 장바구니에 추가할 수 있다
  • 수량 증가/감소 버튼이 제공된다

관련 상품 기능

  • 상품 상세 페이지에서 관련 상품들이 표시된다
  • 같은 카테고리(category2)의 다른 상품들이 관련 상품으로 표시된다
  • 관련 상품 클릭 시 해당 상품의 상세 페이지로 이동한다
  • 현재 보고 있는 상품은 관련 상품에서 제외된다

상품 상세 페이지 내 네비게이션

  • 상품 상세에서 상품 목록으로 돌아가는 버튼이 제공된다
  • 브레드크럼을 통해 카테고리별 상품 목록으로 이동할 수 있다
  • SPA 방식으로 페이지 간 이동이 부드럽게 처리된다

사용자 피드백 시스템

토스트 메시지

  • 장바구니 추가 시 성공 메시지가 토스트로 표시된다
  • 장바구니 삭제, 선택 삭제, 전체 삭제 시 알림 메시지가 표시된다
  • 토스트는 3초 후 자동으로 사라진다
  • 토스트에 닫기 버튼이 제공된다
  • 토스트 타입별로 다른 스타일이 적용된다 (success, info, error)

심화과제

SPA 네비게이션 및 URL 관리

페이지 이동

  • 어플리케이션 내의 모든 페이지 이동(뒤로가기/앞으로가기를 포함)은 하여 새로고침이 발생하지 않아야 한다.

상품 목록 - URL 쿼리 반영

  • 검색어가 URL 쿼리 파라미터에 저장된다
  • 카테고리 선택이 URL 쿼리 파라미터에 저장된다
  • 상품 옵션이 URL 쿼리 파라미터에 저장된다
  • 정렬 조건이 URL 쿼리 파라미터에 저장된다
  • 조건 변경 시 URL이 자동으로 업데이트된다
  • URL을 통해 현재 검색/필터 상태를 공유할 수 있다

상품 목록 - 새로고침 시 상태 유지

  • 새로고침 후 URL 쿼리에서 검색어가 복원된다
  • 새로고침 후 URL 쿼리에서 카테고리가 복원된다
  • 새로고침 후 URL 쿼리에서 옵션 설정이 복원된다
  • 새로고침 후 URL 쿼리에서 정렬 조건이 복원된다
  • 복원된 조건에 맞는 상품 데이터가 다시 로드된다

장바구니 - 새로고침 시 데이터 유지

  • 장바구니 내용이 브라우저에 저장된다
  • 새로고침 후에도 이전 장바구니 내용이 유지된다
  • 장바구니의 선택 상태도 함께 유지된다

상품 상세 - URL에 ID 반영

  • 상품 상세 페이지 이동 시 상품 ID가 URL 경로에 포함된다 (/product/{productId})
  • URL로 직접 접근 시 해당 상품의 상세 페이지가 자동으로 로드된다

상품 상세 - 새로고침시 유지

  • 새로고침 후에도 URL의 상품 ID를 읽어서 해당 상품 상세 페이지가 유지된다

404 페이지

  • 존재하지 않는 경로 접근 시 404 에러 페이지가 표시된다
  • 홈으로 돌아가기 버튼이 제공된다

AI로 한 번 더 구현하기

  • 기존에 구현한 기능을 AI로 다시 구현한다.
  • 이 과정에서 직접 가공하는 것은 최대한 지양한다.

과제 셀프회고

기술적 성장

  • 폴더 구조 · 설계에 대한 고찰
    • 처음부터 구조를 잘 잡는 사람이 개발을 잘한다는 생각이 있었다. 지금도 그 생각은 변하지 않았지만! 실제로 과제를 받고 나서 머릿속으로 구조화를 하는 게 내 생각보다 너무 힘들었다.
    • 금요일, 김성호 코치님의 멘토링을 앞두고 나 포함 팀원분들이 다 같은 생각을 하고 있었다. 처음부터 잘 짜여진 코드를 작성하고 싶은데, 개발을 하다 보면 이것도 필요하고 저것도 필요하다는 생각이 많아지는 게 걱정이라고.. (나만 그런 생각 한 게 아니어서 내심 다행이었다.)
    • 코치님은 주니어 때는 그런 생각이 당연히 드는 게 당연하고, 처음부터 잘하는 사람은 없다고 하셨다. 많이 만들어 가면서 익히는 거라고.. 일단 큰 덩어리로 만들고 코드가 신호를 보내 주면 그때서야 나누면 된다는 말씀을 듣고, 앞으로의 구현 방식은 그렇게 해야겠다는 생각이 들었다.
  • Class VS Fuction
    • 1차 시도 - 함수형 + 커스텀 훅 느낌
      • 처음에는 리액트처럼 onClick=”myFunction” 식 바인딩을 꿈꾸며 전역 함수 & 커스텀 훅 스타일로 시작
      • 그렇게 되면 코드가 예쁘지 않게(?) 작성될 것 같아서 저장소를 삭제하고(!) 다시 Fork 받아서 진행
    • 2차 시도 - 클래스로 재구성
      • BaseComponent, BasePage를 만들고 상속 구조로 재편하려고 했으나………
      • 잦은 override와 불필요한 추상화가 오히려 가독성을 해치고, 재미도 없어져서 다시 함수형으로 변경했다.
    • 3차 시도 - Function으로 복귀
      • MyComponent.mount = () ⇒ { … } 패턴 도입하여 “DOM 생성 후 실행”을 보장
      • 결과적으로 컴포넌트를 잘게 나누어도 예측 가능해서 잘 맞았고, mount뿐만 아닌 init, unmount 등 다양한 메서드들을 추가로 도입할 수 있을 것 같음.
  • 다른 분과 이야기하면서 **“저 지금 국비 이제 막 졸업한 사람처럼 짜고 있어요”**라고 했다. 솔직히 지금도 그런 것 같다. 그래서 코치님들께 수정하고, 지웠던 코드를 보여 주기 싫어서 저장소를 삭제하고 fork를 받았다. 하지만 현빈 매니저님께서 실패한 것도 경험이니 저장소 삭제하지 말고 실패한 과정이든, 성공한 과정이든 모두 commit에 남겨 놓으라고 하셨다. 기록의 중요성을 체감하는 요즈음, 커밋도 하나의 기록이니까 앞으로는 지울 코드라도 커밋에 남겨 놓아야겠다. (갑자기 감성적.. 🖋️)

자랑하고 싶은 코드

core/useStore.js로 전역에 상태하는 훅을 만들었다.

Recoil, Zustand가 어떻게 돌아가는지도 모르고 생각없이 막 사용하던 지난 날….. 처음 전역 상태 관리를 도입해야겠다고 생각하고, Zustand 코드를 참고했다.

 const listener = [];
 
 const get = (key) => {
    if (!key) {
      return globalState;
    } else {
      return globalState[key];
    }
  };

  // 전역 상태를 만드는 함수
  const set = (key, value) => {
    setNestedValue(globalState, key, value);

    listener.forEach(({ callback, targetKey }) => {
      if (!targetKey || key === targetKey || key.startsWith(`${targetKey}.`)) {
        const valueToReturn = getNestedValue(globalState, targetKey);
        callback(valueToReturn, globalState);
      }
    });
  };

  // 전역 상태가 변경됐음을 감지해 주는 함수
  const watch = (callback, targetKey = null) => {
    listener.push({ callback, targetKey });

    return () => {
      const index = listener.findIndex((l) => l.callback === callback && l.targetKey === targetKey);
      if (index !== -1) listener.splice(index, 1);
    };
  };

  return () => ({ get, set, watch });
  • get으로 미리 선언해둔 globalState에 있는 객체 중 해당하는 key만 정보를 추출하여 리턴한다.
  • 처음에는 1뎁스로만 되어 있는 (params 정보만 있는) globalState를 구성했으나, 카트라든지 카테고리라든지 다른 상태도 관리할 일이 생겨서 2뎁스까지 들어가서 value를 바꾸는 함수를 구성했다. (여담으로 저때 좀 신 들렸나 보다. AI 도움 안 받고 갑자기 생각나는 대로 작성했다. 지금 하라고 하면 못 할 듯)
  • watchset으로 객체를 변경하면 그것을 감지하고, 새롭게 변경한 state를 알려 주고, 콜백을 제공한다. 나 같은 경우, 하위 컴포넌트 또는 다른 컴포넌트에서 store.set으로 객체를 변경하면, 상위 컴포넌트에서 변경을 감지해 다시 dom에 그려주는 render를 진행하는 식으로 구현했다.

개선이 필요하다고 생각하는 코드

  • return 안에 있는 template에 넣을 생각으로 각 컴포넌트마다 state 객체를 만들었다.
const state = {
  isLoading: true
};
  • 그런데 구현하다 보면 isLoading 같은 것도, 전역 상태에서 관리할 만한 것도 state에 작성되거나, 전역 상태에서 관리할 것들을 state에 작성하는 경우도 있었고,
  • 부모 컴포넌트에서 인자로 넘겨 줘서 바인딩 해 줄 것도 괜히(?) state에 작성했다.
  • 이런 중복된 코드를 줄이고, 명확한 역할을 부여하고 싶다. 그런데 괜히 건드렸다가 빵꾸 터질까 봐 겁남.

학습 효과 분석

  • 처음부터 모든 것을 다 해내려고 하는 것이 좋은 건 아니구나 생각이 많이 들었다. 앞서 이야기했듯이 더러운 내 코드 (ㅠㅠ) 코치님들이 보시기에 정색할 만한 코드 같으면 과감없이 지웠다. (실제로도 개인 공부 할 때 그랬음)
  • 멘토링을 받고 다음부터는 큰 덩어리부터 시작해, 점차 분리해 나가는 과정으로 다음 과제를 맞이할 것 같다.
  • 왜인지 Intersection Observer를 쓸 수가 없었다. 그러면 테스트에 통과를 못 해서, 어쩔 수 없이 scroll event를 작성했다.
    • 현재 스크롤 값에 + view (현재 모니터에 보이는 화면) height와, body height - trigger height로 비교를 해서 하단에 스크롤이 도달하면 데이터 패칭을 이루어지는 아이디어로 작성했는데, 이게 맞는 건지 모르겠다.
    • 하지만 observer를 쓰지 못하니 스크롤로 데이터를 패칭하는 아이디어를 생각한 내가 참 기특한 것 같다. ^^

과제 피드백

  • 처음에 과제 타이틀만 보고 쉬운 거 아닌가? 했던 나를 후려치고 싶을 정도로 생각보다 너무 어려웠다. 할 게 너무 많았고, 어떤 식으로 구현을 해야 잘 굴러갈까 생각이 많았다. (날개가 아니라 목으로 돌아가는 비둘기마냥..)
  • 그런데 하면 할수록 리액트에게 너무 감사함을 느낀다. 그런데 지금은 이런 과제를 내 준 코치님께 더 충성충성하게 된다. 이번 기수에서 과제가 바뀌어서 코치님도 수정하시느라 바쁘셨는데, 결론적으로 선택 과제로 요구사항들이 많이 빠져서 좋다. ^^
  • 리액트 같은 프레임워크에서 제공하는 Hook 없이 생짜로 구현해 볼 수 있어서 했던 고민들이 좋았던 것 같다.

AI 활용 경험 공유하기

  • Cursor AI 썼다. 처음 개강날에 팀원분들께 AI는줘패야말을들어요 라고 했지만, 정말 줘패야 말을 듣는다는 건 오히려 인공지능이 아니라 나일 수도?
  • 이거 해 줘. 이거 구현할 거야가 아니라 내 아이디어를 먼저 제시했다. 가장 많이 한 말은 “나는 이렇게 생각했는데 너는 어때?” 였다. 그러면 인공지능에게 내가 구현해야 할 방식을 설명하면서도 거기에서 힌트를 얻어 오히려 AI 없이 코드를 작성한 적이 꽤 많았다.
  • 그리고 커밋 메시지를 Cursor AI 시켰다. 생각보다 많이 괜찮다. 가끔 오글거리는 말은 지우는 둥 한 번 정도 검수가 필요하지만, 커밋 메시지와 깃모지로 고민했던 지난 날은 안녕..

리뷰 받고 싶은 내용

  • 아무리 생각해도 잘 짜여진 Route를 모르겠습니다. 제 코드에서는 미리 나눠 놓은 routes를 순회 돌면서 현재 주소와 매치한 뒤, init을 한 뒤, mount를 실행시킵니다.
  const view = async () => {
    for (const route of routes) {
      const match = getAppPath().match(route.path);
      if (!match) continue;
      const Page = route.component;
      Page.init?.(match?.[1]);
      draw("main", Page({}));
      await Page.mount?.();
      return; // 매치된 첫 번째 라우트만 실행하고 멈추게
    }
  };
  • 지금은 init과 mount밖에 없지만 vue에서는 이런 기능들이 훨 많은데, vue처럼 나중에 unmount라든지 다른 메서드들을 추가하면 이런 로직은 계속 아래로 작성하는 것밖에 답이 없나? 라는 생각이 듭니다. 이를 테면 아래처럼요…
  const view = async () => {
    for (const route of routes) {
      ...
      if(Page.unmount) await Page.unmount?.();
      return; // 매치된 첫 번째 라우트만 실행하고 멈추게
    }
  };
  • 제가 생각한 건, Page를 순회하면서 해당 메서드가 있으면 실행시키고, 아니면 실행시키지 않는 것이었는데 그럴 경우 if 문 지옥에 빠질 것 같습니다.
  • 회고가 넘 길어서 죄송합니다. 🙇‍♀️

eveneul and others added 30 commits July 7, 2025 00:20
- 매치된 첫 번째 라우트만 렌더링되게 return 추가
- type(category, products, product)에 따른 분기 처리
- ProductList, ProductCard, Search 컴포넌트 생성
- Home 페이지에서 getProducts API 호출
- useStore.js 기본 전역 상태 추가
- 2 depth가 변경되어도 1 depth 전체를 반환해주는 코드로 변경 (gpt)
- globalStore에서 filter -> params로 변경 및 항목 추가
- ProductCard.js에서 상품 카드 이미지 경로를 데이터로 수정
- Search.js 컴포넌트 추가 및 이벤트 바인딩
- 🐛 검색 결과에 따른 상품 목록 리랜더링이 먹히지 않음
- Search.mount()로 Search에 관한 이벤트 다시 랜더링 해 주어야 함
- 스켈레톤 UI 노출 후 새로운 프로덕트 리스트 UI 노출 작업 중
- ProductCard.js에 상품 클릭 시 상세 페이지로 이동하는 기능 추가
- 장바구니 추가 버튼 클릭 시 콘솔 로그 출력 기능 추가
- Home.js에서 ProductCard.mount() 호출하여 이벤트 바인딩
- 상품 상세 페이지에서 로딩 상태 관리 기능 추가
- URL 변경 시 뷰를 리렌더링하는 이벤트 리스너 추가
- Loading 컴포넌트의 스타일 수정으로 화면 최적화
- 상품 목록에서 pagination.total과 products를 안전하게 접근하도록 수정
- 상품 상세 페이지에서 관련 상품을 가져오는 기능 추가
- 상품 정보 및 이미지 동적으로 렌더링하도록 수정
- package.json에 다양한 테스트 스크립트 추가: 기본, 쉬움, 어려움 레벨의 E2E 테스트를 위한 스크립트 추가
- CI 구성에서 새로운 테스트 작업 추가: 기본, 쉬움, 어려움 레벨의 E2E 테스트를 위한 GitHub Actions 워크플로우 설정
- 새로운 E2E 테스트 파일 추가: 쉬움 및 어려움 레벨의 E2E 테스트 시나리오 구현
- 앞으로/뒤로가기 시 데이터가 누적돼서 호출
- useNavigate 훅을 싱글톤으로 수정
- Home.js에서 fetchProducts와 fetchCategories 함수 호출로 카테고리 데이터 관리 방식 변경
- 무한 스크롤 함수 임시 주석 처리
- Search.js에서 불필요한 return 추가
- store에서 limit 및 sort 파라미터를 가져와서 선택 요소에 설정
- 중복된 주석 코드 제거
- Search.js에서 기본 선택 옵션을 20개 및 가격 낮은순으로 설정
- Home.js에서 카테고리 데이터를 포함하여 렌더링 방식 변경
- useNavigate 및 useStore 훅을 main.js에서 직접 가져와 사용하도록 변경
- render 초기화 및 Layout 마운트 방식 수정
- URL 변경 시 뷰 리렌더링 로직 개선
- ProductCard 및 Product 컴포넌트에서 navigate.push() 대신 window.history.pushState() 사용으로 변경
- Home.js에서 초기 로딩 상태 설정 및 fetchCategories 함수에서 로딩 상태 업데이트 추가
- Product.js에서 fetchProduct 및 fetchRelatedProducts 함수에서 로딩 상태 관리 제거 후 mount 메서드에서 로딩 상태 업데이트 추가
- Product.init 메서드 추가로 초기 로딩 상태 설정 방식 개선
eveneul added 27 commits July 11, 2025 03:58
- 모든 상품을 확인했습니다 메시지를 삭제하여 UI 간소화
- Toast.js 파일을 새로 생성하여 다양한 알림 메시지 템플릿 추가
- ProductCard.js에서 장바구니 추가 버튼 클릭 시 Toast.mount("addCart") 호출로 알림 표시 기능 추가
- ProductList 컴포넌트에서 props 구조 분해 할당을 적용하여 가독성 향상
- Search 컴포넌트에서 이벤트 핸들링 방식을 개선하고, 검색 입력값을 상태에서 가져오도록 수정
- Home 컴포넌트에서 fetchMoreProducts 함수를 비동기로 변경하고, 스크롤 이벤트 핸들러를 추가하여 무한 스크롤 기능 개선
- Search.js에서 불필요한 console.log 제거
- useNavigate.js에서 Header 컴포넌트 추가 및 렌더링 로직 개선
- Product.js에서 장바구니 추가 및 수량 조절 기능 구현
- handleDeleteToast 함수에 event 매개변수 추가
- Toast.mount에서 toastCloseBtn 클릭 이벤트 핸들러를 수정하여 콘솔 로그 추가
- Toast 메시지 삽입 위치를 document.body에서 main으로 변경
- 상품 이미지 클릭 시 window.history.pushState 대신 navigate.push로 변경하여 내비게이션 로직 개선
- Search.js에서 카테고리 필터링 기능 추가 및 이벤트 핸들링 개선
- Home.js에서 카테고리 상태를 store에 저장하고, Search 컴포넌트에 전달하여 검색 기능 향상
- useStore.js에서 categories 초기 상태 추가
- Breadcrumb.js 파일을 새로 생성하여 카테고리 내비게이션 기능 구현
- Search.js에서 Breadcrumb 컴포넌트를 통합하여 카테고리 필터링 UI 개선
- Home.js에서 Breadcrumb을 렌더링하고 mount하여 카테고리 상태 관리 기능 강화
- 가격 표시를 포맷팅하는 formatPrice 함수를 추가하여 천 단위 구분 기호 적용
- 브랜드 정보가 없을 경우 대체 텍스트로 maker 또는 mallName을 표시하도록 수정
- Search.js에서 URLSearchParams를 사용하여 쿼리 파라미터를 객체로 변환하고, 이를 통해 검색 및 정렬 기준을 설정하도록 수정
- Home.js에서 fetchProducts 호출 시 URL 파라미터를 전달하여 동적 검색 기능 강화
- Breadcrumb.js에서 카테고리 초기화 시 빈 문자열 대신 null로 설정하여 상태 관리 개선
- useRender.js에서 draw 함수에 tag 유효성 검사 추가
- NotFound.js 파일에 404 에러 페이지 추가
- 홈으로 돌아가는 링크를 추가
- fetchMoreProductsScroll 함수에서 현재 경로가 "/"일 때만 스크롤 이벤트를 처리하도록 수정하여 불필요한 호출 방지
- 중복된 경로 체크 로직을 함수 내에서 통합하여 코드 가독성 향상
- CartButton 컴포넌트를 새로 생성하여 장바구니 아이콘 및 수량 표시 기능 구현
- Header 컴포넌트에서 장바구니 상태를 관리하고 CartButton을 렌더링하도록 수정
- Layout 컴포넌트에서 Header.mount() 호출 추가하여 장바구니 상태 초기화
- ProductCard 및 ProductList 컴포넌트에서 장바구니 추가 기능을 위한 이벤트 핸들링 개선
- Toast 컴포넌트를 통해 장바구니 추가 시 사용자 피드백 제공
- package.json에 프로덕션 빌드 및 미리보기, 배포 스크립트 추가
- vite.config.js에서 프로덕션 환경에 맞는 기본 경로 및 빌드 설정 추가
- package.json에 gh-pages 패키지를 추가하고, 배포 스크립트를 수정하여 빌드 후 dist 디렉토리를 gh-pages로 배포하도록 변경
- pnpm-lock.yaml에서 gh-pages 의존성을 추가하여 패키지 관리 개선
- mockServiceWorker.js의 URL을 설정하는 serviceWorker 옵션을 추가하여 모킹 기능 개선
- Vite 설정 파일(vite.config.js)에서 프로덕션 환경에 맞는 기본 경로를 수정하여 배포 경로를 일관되게 유지
- 새로운 404.html 파일을 생성하여 사용자에게 페이지를 찾을 수 없음을 알리는 디자인을 추가
- 홈으로 돌아가는 링크와 함께 사용자 친화적인 메시지를 포함하여 사용자 경험 개선
- package.json의 배포 스크립트를 gh-pages에서 pnpm preview:prod로 변경하여 배포 방식 개선
- vite.config.js에서 프로덕션 환경에 맞는 기본 경로 설정을 수정
- GitHub Actions를 활용한 배포 자동화를 위한 deploy.yml 파일 추가
- index.html 파일에 로딩 중 표시 및 기본 콘텐츠 구조 추가
- main.js에서 모킹 기능을 개선하여 workerOptions를 사용하도록 수정
- browser.js에서 serviceWorker URL 설정을 환경 변수에 따라 동적으로 처리하도록 변경
- BASE_PATH를 제거한 앱 경로를 반환하는 getAppPath 함수 추가
- Header, Home, Product 컴포넌트에서 getAppPath 함수를 사용하여 경로 처리 개선
- useRender에서 경로 매칭 로직을 getAppPath로 변경하여 코드 일관성 향상
- deploy from a branch 설정으로 변경 테스트
- browser.js 롤백
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants