Skip to content

Conversation

@lecto17
Copy link

@lecto17 lecto17 commented Nov 10, 2025

과제 체크포인트

배포 링크

https://lecto17.github.io/front_7th_chapter2-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로 다시 구현한다.
  • 이 과정에서 직접 가공하는 것은 최대한 지양한다.

과제 셀프회고

기술적 성장

  • React가 vanila js에서 어떤 부분들을 개선해줬는지 조금이나마 알게 되고, React로 편하게 개발할 수 있던 것을 역체감

    • state, useEffect 같은 훅을 통해 dom의 변화되는 부분만 리렌더링 해주거나 callback을 실행하는 것.
      • -> state의 변화에 따른 화면 자동 리렌더링, useEffect의 dependency로 등록한 변수의 변화에 따른 callback 실행 등의 기능들을 직접 구현하였다. setTimeout을 걸어주고(dom 렌더링 이후 실행될 영역), 그 내부에서는 document 내 변화될 element를 찾은 뒤(값이 변경될 데이터가 있는 태그를 찾는 과정), 변화될 element의 코드를 삽입해주는 코드가 필요함(데이터의 값 변경).
      • -> 위와 같이 한 줄 한 줄 코드를 명령형 형식으로 작성하다보니 react가 왜 선언형 기반의 라이브러리이고, 왜 선언형을 권장하는지에 대해 알게 되었다. 그리고 그동안 사용했던 API들(eg, react-query의 useInfiniteQuery 등)이 얼마나 많은 것들을 뒷단에서 해주고 있는지, 이러한 API들이 선언형 형식으로 만들어져 있음을 생각해볼 수 있었다.
  • 라우터

    • 과제 구현 시 특정 경로마다 지정해 놓은 컴포넌트를 innerHTML, insertAdjacentHTML 같은 API를 사용하여 DOM을 교체하였는데, 실제 라우터에서는 어떤 방식으로 컴포넌트를 렌더링하는지 궁금해졌다. ('react-router도 DOM 바꾸는 거니까, 결국 이것도 innerHTML 쓸 것 같은데'라는 생각이 있었다.)
    • 하지만 실제로 react-router“어떤 React 컴포넌트 트리를 렌더링할지”만 결정하고 DOM 변경은 React 렌더러(react-dom)가 Virtual DOM diff를 통해 처리한다는 것을 알게 되었다. 그리고 React는 대부분 document.createElement, setAttribute, appendChild 같은 방식으로 패치하지 페이지 전체 innerHTML을 갈아엎는 식으로 동작하지 않는다는 것을 알게 되었다.
    • 그러다 다시 갖았던 의문은 '왜 굳이 DOM을 통째로 바꾸지 않고 일부만 달라진 곳만 일일히 찾아 변경하는지' 였다. DOM 내에서 달라진 부분을 하나씩 다 찾고 그걸 다시 바꿔주는 것, DOM 내에 달라진 것을 한 땀 한 땀 비교해 찾았다 한들 그 사이 사용자가 다른 화면으로 이동해버린다면..? DOM 비교하는데 불필요한 리소스를 낭비한 것은 아닐까 싶었다. 이런 과정 대비 DOM을 통쨰로 바꿔주는 것은 너무나 간단한 작업이면서도 성능도 챙길 수 있지 않을까? 하는 생각들을 하다 브라우저 렌더링 과정이 생각났다. 이 하나의 문서가 렌더링되기 위해선 여러 트리들을 만들고, 그것들을 결합해서 DOM 트리를 만드는 과정, 그리고 그 트리를 페인트, 플로우하는 과정이 매우 비싼 작업이란 사실을 되돌아 보게 되었다. 그렇다면 React에서 DOM을 비교하는 'diff 알고리즘'이 어떻게 작동되는 것이고, 성능이 얼마나 좋을까 싶을까 하는 궁금증이 들었지만, 이건 과제 제출 후 찾아봐야겠다.
    • window.history에 대한 내용(popstate Event), SPA에서 pushState를 통한 라우트 처리에 대해 학습

자랑하고 싶은 코드

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

학습 효과 분석

과제 피드백

AI 활용 경험 공유하기

리뷰 받고 싶은 내용

@JunilHwang JunilHwang force-pushed the main branch 2 times, most recently from c1cdc92 to 9b09aa3 Compare November 10, 2025 11:59
Copy link
Contributor

@JunilHwang JunilHwang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 피드백은 n8n + ai (gpt-5-mini)를 활용하여 자동으로 생성된 내용입니다.

이번 PR에서는 기본적인 SPA 쇼핑몰 기능의 구현 요구사항을 충실히 충족하는 구조로 잘 작성되었으며,
상품 목록부터 상세 페이지, 장바구니, 검색, 필터, 무한 스크롤, URL 동기화 및 SPA 라우팅까지 폭넓은 영역을 다루었습니다.

코드는 기능별로 컴포넌트와 유틸리티 모듈로 적절히 분리되어 있고, 이벤트 위임을 적절히 사용하여 동적 요소도 잘 처리하고 있습니다.

하지만 향후 "확장성"을 고려할 때 다음과 같은 개선 포인트가 있습니다.

  • 상태 관리와 URL 관리의 분리: 필터 상태를 전역 객체 하나로 관리하면서 동시에 URL 갱신까지 담당하고 있는데, 상태와 URL 관리를 분리해 이벤트나 옵저버 패턴으로 상태 영향도를 낮출 필요가 있습니다.
  • 무한 스크롤 로딩 로직 분리: API 호출, 상태 갱신, UI 업데이트가 한 함수에 몰려 있어, 앞으로 오류 처리나 다른 UI 대응을 확장하는 데 어려울 수 있습니다.
  • 장바구니 모달 이벤트 관리 개선: 개별 DOM 노드에 이벤트를 붙이는 부분을 이벤트 위임과 상태 중심 설계로 개선하면 더 간결하고 효율적으로 관리할 수 있습니다.
  • 상품 상세 페이지 이벤트 위임: 이벤트 처리 방식을 DOM 특정 엘리먼트 직접 바인딩 대신 위임으로 전환하면 생산성과 확장성 향상에 도움이 됩니다.
  • SPA 네비게이션 이벤트 위임 범위 최적화: 링크 클릭 인터셉터의 이벤트 위임 범위를 너무 넓게 하지 않고, 특정 영역으로 제한하는 것이 성능과 유지보수에 유익합니다.
  • 컴포넌트 함수 내부의 역할 분리: 특히 필터 및 토스트 컴포넌트는 렌더링과 로직 분리를 명확히 하여 재사용성과 테스트 용이성을 개선할 수 있습니다.
  • 브레드크럼 URL 인코딩 및 SPA 연동: 브레드크럼 네비게이션에 SPA 라우팅 기능과 URL 인코딩 적용이 필요합니다.
  • 토스트 메시지 컴포넌트 설계: 단일 메시지 처리 컴포넌트와 별도의 토스트 매니저 분리로 유연성을 확보할 수 있습니다.

이처럼 기본 기능 구현은 탄탄하지만, 향후 요구사항 증가 및 유지보수성을 고려하여 상태 관리와 이벤트 처리 구조를 한 단계 업그레이드하는 방향을 추천드립니다.

* @returns {Object} 현재 필터 상태
*/
export const getCurrentFilters = () => {
return { ...currentFilters };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
필터 상태 관리를 전역 객체 하나로 관리하고, UI와 URL 업데이트 로직을 결합하면서 확장성 측면에서 부담이 될 수 있습니다.

현재 코드의 한계:

  • 현재 구조는 필터 상태가 한 곳에 집중되어 한 페이지에서는 문제 없지만, 다른 페이지나 기능 추가 시 관리가 어려워집니다.
  • URL 동기화와 상태 업데이트가 함께 처리되어 테스트 및 유지보수가 어렵습니다.
  • 복잡한 필터 추가 시 모든 곳에 영향을 미칠 가능성이 높습니다.

2. 근본 원인

핵심 문제:
"단일 전역 상태 객체에 UI와 URL 조작 로직이 결합되어 있어 상태 관리와 뷰 업데이트가 분리되지 않음"

왜 문제인가:
상태와 UI/URL 관리가 밀접하게 결합되면 새로운 필터 조건 추가, 분기 처리, 또는 비동기 업데이트 시 복잡해지고 오류 발생 가능성이 증가합니다.

3. 개선 구조

현재 구조:

  • state: currentFilters 전역 객체
  • updateFilters: 상태 및 URL 동시 업데이트

개선된 구조:

  • 상태 관리와 URL 관리 분리
  • 상태 변경은 이벤트나 옵저버 패턴으로 처리
  • URL 업데이트는 별도의 모듈에서 상태 구독 후 처리

개선 사항:

  • 상태 변경 알림을 받는 옵저버 패턴 혹은 이벤트 시스템 도입
  • URL 반영은 별도 함수에서 상태 구독 후 처리
  • 컴포넌트들이 상태를 구독하여 필요한 UI만 업데이트

코드 비교:

// ❌ 현재 방식
export const updateFilters = (newFilters) => {
  currentFilters = {
    ...currentFilters,
    ...newFilters,
  };

  // URL도 함께 업데이트
  updateURL(currentFilters);

  return { ...currentFilters };
};

// ✅ 개선 방식 예시 (상태와 URL 업데이트 분리)
const listeners = [];
export const subscribeFilters = (listener) => {
  listeners.push(listener);
};

export const updateFilters = (newFilters) => {
  currentFilters = {
    ...currentFilters,
    ...newFilters,
  };

  listeners.forEach((l) => l(currentFilters));

  return { ...currentFilters };
};

// 별도 모듈이나 위치에서
subscribeFilters((filters) => {
  updateURL(filters);
});

// 다음 페이지 번호
const nextPage = state.currentPage + 1;

// API 호출
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
무한 스크롤 로딩과 상품 추가 로직이 하나의 단일 함수에 모두 구현되어 있어, 앞으로 새로운 요구사항(예: 필터 조건 변경 시 무한 스크롤 초기화, 스크롤 위치 제어 등)이 더해질 때 확장성이 떨어집니다.

현재 코드의 한계:

  • 로딩 중 UI 표시, 오류 핸들링, 상품 추가 로직이 섞여 있어 유지보수가 복잡합니다.
  • 무한 스크롤 활성화/비활성화 상태를 내부에서 관리하지만, 외부 상태 변경에 대응하는 구조가 미흡합니다.
  • 추후 다른 위치에 더 많은 기능이 추가되면 코드가 장황해질 가능성이 큽니다.

2. 근본 원인

핵심 문제:
"무한 스크롤 데이터 로딩과 UI 조작 로직이 단일 함수에 결합되어 있어 기능 확장 및 분리 관리를 어렵게 만듦"

왜 문제인가:
이 구조는 테스트가 어려워지고, 오류 발생 시 트러블슈팅이 힘들며 재사용성이 낮아 다양한 상황에 대응하기 어렵습니다.

3. 개선 구조

현재 구조:

  • loadMoreProducts() 함수 안에 API 호출, 로딩 UI 처리, DOM 조작이 통합되어 있음

개선된 구조:

  • API 호출과 데이터 처리 로직 분리
  • UI 상태 관리(로딩 표시/숨기기, 완료 메시지 등) 별도 함수 또는 컴포넌트에 위임
  • 상태 변화에 따른 사이드 이펙트는 옵저버나 상태 관리 모듈에서 처리

개선 사항:

  • API 요청 함수와 UI 업데이트 함수를 분리하여 재사용성을 늘림
  • 에러처리 로직 별도 함수 분리
  • 무한 스크롤 상태를 외부에서 구독 및 제어하도록 설계

코드 비교:

// ❌ 기존 loadMoreProducts 내 로딩 UI 직접 제어
try {
  const response = await getProducts(...);
  appendProductsToGrid(response.products);
  updateInfiniteScrollState(...);
  showCompletionMessage();
} catch (error) {
  hideLoadingIndicator();
}

// ✅ 개선: API 호출은 별도 함수
async function fetchNextProducts(page, filters) {
  const response = await getProducts({ ...filters, page });
  return response.products;
}

// UI 업데이트와 상태 변경은 콜백 혹은 이벤트로 분리
async function loadMoreProducts() {
  setLoading(true);

  try {
    const products = await fetchNextProducts(currentPage + 1, currentFilters);
    addProductsUI(products);
    updateStateAfterLoad(...);
  } catch(err) {
    handleLoadError(err);
  } finally {
    setLoading(false);
  }
}

checkbox.addEventListener("change", handleItemCheckboxChange);
});

// 수량 증가 버튼
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
장바구니 모달의 이벤트 리스너 등록과 DOM 조작 로직이 매우 길고 복잡하게 단일 파일에 모여 있어, 유지보수 시 혼란스러울 수 있고, 확장성에도 제약이 있습니다.

현재 코드의 한계:

  • 이벤트 위임 대신 개별 노드에 일일이 이벤트를 붙이는 방식이라 동적 요소 관리에 효율성이 떨어집니다.
  • 체크박스 상태 복원을 로컬스토리지에서 직접 DOM 조작으로 구현하여 재사용성과 테스트가 어렵습니다.
  • 전반적으로 책임 분리가 모호하며, 함수들이 너무 많아 단일 책임 원칙 위반 가능성

2. 근본 원인

핵심 문제:
"모달 UI 상태 관리, 이벤트 처리, 상태 동기화 로직이 한 파일 내에서 꼬여 있어 기능 단위 분리가 부족함"

왜 문제인가:
이런 구조는 수정 시 영향 범위 파악이 힘들고, 새로운 기능 추가나 리팩토링에 비용을 많이 요구합니다.

3. 개선 구조

현재 구조:

  • DOM 삽입 후 모든 이벤트를 구체적으로 개별 노드에 부착
  • 상태 저장/복원도 직접 DOM 쿼리 및 조작으로 처리

개선된 구조:

  • 이벤트 위임 기법 활용해 상위 컨테이너에 이벤트를 한 번만 붙임
  • 상태 관리는 별도의 훅 혹은 상태 매니저로 분리
  • 체크박스 상태 저장/복원과 UI 업데이트를 명확한 함수로 분리

개선 사항:

  • 모달 DOM 이벤트 리스너를 하나 또는 적은 수의 핸들러로 처리
  • 상태 변경 시 UI 업데이트 분리 함수 호출
  • 공유 가능한 체크박스 상태 관리 모듈 도입

코드 예시:

// ❌ 개별 노드 이벤트 등록
itemCheckboxes.forEach(cb => cb.addEventListener('change', onCheckboxChange));

// ✅ 이벤트 위임
modalElement.addEventListener('change', (e) => {
  if(e.target.matches('.cart-item-checkbox')) {
    onCheckboxChange(e.target);
  }
});

/**
* 장바구니 추가 버튼 이벤트 핸들러 초기화
* 이벤트 위임을 사용하여 동적으로 추가되는 상품에도 대응
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
장바구니 핸들러가 단일 플래그 isInitialized를 통해 한번만 초기화하지만, 만약 페이지 내에서 재렌더링 되는 경우나 SPA에서 상태를 잃을 가능성에 대비한 재초기화 로직이 부족합니다.

현재 코드의 한계:

  • 상태 또는 DOM의 변화가 있을 때 대응할 재초기화 메커니즘 미탑재
  • 이벤트 위임을 문서(document)에 붙이지만, 이벤트 핸들러 정리 기능은 없어서 메모리 누수 가능성 낮지 않음
  • 에러 발생 시 복구 기능이 부족

2. 근본 원인

핵심 문제:
"SPA 환경에서 동적 렌더링과 상태 관리 시 이벤트 핸들러 등록과 해제가 체계적으로 관리되지 않음"

왜 문제인가:
이로 인해 여러 번 이벤트가 등록되거나 누수 발생시 앱 성능 저하, 예상치 못한 동작 발생 가능성이 있습니다.

3. 개선 구조

개선 사항:

  • 이벤트 핸들러 등록 시 해제 로직 추가
  • SPA 내에서 경로 변경 등으로 필요 시 재초기화 가능하도록 개선
  • 이벤트 위임 범위를 필요 최소 범위로 줄이고, doctype과 scope 고려

예시:

// 이벤트 핸들러 등록
const onClick = event => { /* ... */ };
document.addEventListener('click', onClick);

// 해제 함수
const cleanup = () => {
  document.removeEventListener('click', onClick);
  isInitialized = false;
};

// SPA 라우팅 시 cleanup 호출 후 initCartHandler 재호출

});
}

if (quantityDecreaseBtn && quantityInput) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
상품 상세 페이지 내 수량 증감 버튼에 대한 이벤트 핸들링이 DOM 요소 이벤트에 직접 붙여져 있어, 장기적인 관리와 확장성에 부담이 있습니다.

현재 코드의 한계:

  • 이벤트 핸들러가 요소마다 직접 붙어 있어, 신규 동적 컴포넌트나 변경에 대응하기 어렵습니다.
  • 수량 검증 로직이 입력 변경 이벤트에서만 처리되어 수량 입력 비정상 변경에 취약
  • 상품 상세 페이지 내 관련 상품 클릭도 개별 이벤트 리스너 부착으로 비용 발생

2. 근본 원인

핵심 문제:
"구체적 DOM 요소에 이벤트 핸들러가 직접 결합되어 있어 동적 컨텐츠에 유연하게 대응하기 어려움"

왜 문제인가:
서로 다수의 이벤트 핸들러 중복 등록 및 삭제가 복잡해지고, 테스트 및 유지보수가 어려워집니다.

3. 개선 구조

개선 사항:

  • 이벤트 위임 방식을 적용하여 상위 컨테이너에 핸들러를 한 번만 등록
  • 수량 입력 검증을 중앙집중화하여 중복 방지 및 정확성 향상
  • 관련 상품 영역에 한 번만 이벤트 리스너 등록하여 위임 방식 적용

코드 예시:

// 이벤트 위임
const container = document.querySelector('.product-detail-container');
container.addEventListener('click', (e) => {
  if(e.target.matches('#quantity-increase')) { /* 증가 로직 */ }
  else if(e.target.matches('#quantity-decrease')) { /* 감소 로직 */ }
  else if(e.target.closest('.related-product-card')) { /* 관련 상품 이동 */ }
});

// 입력 검증은 input 이벤트에 위임
container.addEventListener('input', (e) => {
  if(e.target.matches('#quantity-input')) {
    // 유효성 검사
  }
});

* data-link 속성이 있는 a 태그 클릭 시 SPA 방식으로 처리
* @param {Event} e - 클릭 이벤트
*/
export const handleLinkClick = (e) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
SPA 네비게이션을 위해 링크 클릭 인터셉터를 설정하였으나, 현재 사용자 클릭 이벤트를 전체 문서에 리스닝하고, data-link 속성 있는 링크만 처리하는 방식입니다.

현재 코드의 한계:

  • 대규모 DOM에서 모든 클릭을 듣기 때문에 성능 이슈 가능성 존재
  • 정적 링크로만 동작하며, 동적 생성 엘리먼트에 대한 처리 방법이 명확하지 않음
  • 중첩된 a 태그나 버튼 등 클릭 이벤트 예외 처리 코드가 없음

2. 근본 원인

핵심 문제:
"이벤트 위임 범위가 지나치게 광범위하고, 동적 엘리먼트 및 예외 상황 대응이 부족함"

왜 문제인가:
비효율적인 이벤트 처리로 인한 퍼포먼스 저하와 클릭 이벤트 예외 상황 발생 가능성이 있습니다.

3. 개선 구조

개선 사항:

  • 이벤트 위임 범위를 라우팅이 필요한 특정 영역으로 제한
  • 동적 엘리먼트 추가 시 필터링 강화
  • 중첩된 링크 등의 예외 처리 고려

예시 코드:

// 특정 컨테이너에만 이벤트 위임
const container = document.getElementById('root');
container.addEventListener('click', e => {
  const link = e.target.closest('a[data-link]');
  if (!link) return;

  // 중첩 a 태그 예외 처리 등
  if (link.contains(e.target)) {
    e.preventDefault();
    navigateTo(link.getAttribute('href'));
  }
});

<!-- 기존 필터들 -->
<div class="flex gap-2 items-center justify-between">
<!-- 페이지당 상품 수 -->
<div class="flex items-center gap-2">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
필터 UI와 관련 이벤트 핸들러가 긴 함수 안에 모두 구현되어 있어 코드 가독성과 유지보수가 어렵습니다.

현재 코드의 한계:

  • 로직이 너무 긴 함수에 몰려있어 역할 분리 부족
  • 이벤트 핸들러도 복잡하게 중첩되어 있어 추후 기능 확장 시 어려움
  • DOM 엘리먼트 탐색이 반복적으로 이루어짐

2. 근본 원인

핵심 문제:
"컴포넌트의 렌더링과 이벤트 처리가 하나의 함수에 집중되어 역할이 명확히 분리되어 있지 않음"

왜 문제인가:
복잡도가 증가하면 실수하거나 버그가 생기기 쉽고, 유지보수 및 테스트가 어려워지며, 재사용이 힘듭니다.

3. 개선 구조

개선 사항:

  • 렌더링과 이벤트 바인딩을 명확히 분리하여 모듈화
  • 카테고리 렌더링, 이벤트 처리, 브레드크럼 업데이트 등을 개별 함수나 클래스로 분리
  • 이벤트 위임을 적극 활용해 핸들러 수 최소화
  • DOM 접근을 줄이고 상태 기반 UI 업데이트 적용

예시:

// 렌더링 함수들
function renderCategory1() { ... }
function renderCategory2() { ... }

// 이벤트 핸들러 분리
function onCategory1Click() { ... }
function onCategory2Click() { ... }
function onBreadcrumbClick() { ... }

// 초기화 함수
function init() {
  renderCategory1();
  bindEvents();
}

</a>
`);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
브레드크럼 링크 URL 생성 시 별도의 URL 인코딩 처리와 SPA 전환 이벤트 위임이 부족하여, URL 직접 입력 또는 링크 클릭 시 SPA 네비게이션 흐름이 올바르게 작동하지 않을 수 있습니다.

현재 코드의 한계:

  • 브레드크럼 링크 생성 시 하드코딩된 href만 존재, SPA router에 의한 비동기 페이지 전환 미지원
  • 인코딩 처리 부족으로 카테고리명에 따라 URI 충돌 우려
  • 데이터 링크 어트리뷰트 미사용으로 클릭 시 새로고침 발생 가능

2. 근본 원인

핵심 문제:
"브레드크럼 네비게이션에 SPA 이벤트 전파 및 URL 인코딩 미처리가 결합되지 않아 사용자 경험 저하"

왜 문제인가:
SPA 전환 경험이 매끄럽지 않고, 잘못된 URL을 통한 네비게이션 시 에러나 페이지 로딩 오류가 발생합니다.

3. 개선 구조

개선 사항:

  • 브레드크럼 링크에 data-link 속성을 추가하여 SPA 라우터 인터셉터에 의해 네비게이션되도록 변경
  • encodeURIComponent를 사용해 카테고리명 인코딩 처리
  • SPA router의 navigateTo 함수를 사용한 네비게이션 연동

코드 비교:

// ❌ 현재 브레드크럼 링크
<a href="${category1Query}" data-link>${category1}</a>

// ✅ 개선 후
<a href="${category1Query}" data-link>${encodeURIComponent(category1)}</a>

// 더 나아가 클릭 이벤트 핸들링을 router에서 처리

@@ -0,0 +1,47 @@
export const Toast = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. 문제상황 제시

구체적인 문제 상황:
Toast 컴포넌트가 모든 토스트 메시지를 한번에 렌더링해 두고 있습니다. 이로 인해, 메시지 타입에 따라 보여주는 메시지와 스타일을 매번 변경하기 불편하고, 토스트 메시지 관리가 어렵습니다.

현재 코드의 한계:

  • 하나의 컴포넌트 내에 모든 타입의 메시지가 포함되어 불필요한 DOM이 생성됨
  • 동적으로 생성 및 제거하지 않고 항상 렌더링되어 있어 가독성과 유지보수가 어려움
  • 토스트 메시지 상태 관리 및 애니메이션 구현이 독립적이지 못함

2. 근본 원인

핵심 문제:
"하나의 컴포넌트가 여러 토스트 메시지를 모두 렌더링하는 구조로, 단일 메시지 관리에 부적합함"

왜 문제인가:
동적 토스트 메시지 생성과 제거를 효율적으로 지원하지 않아, 사용자 경험과 유지 보수성이 떨어집니다.

3. 개선 구조

개선 사항:

  • Toast 컴포넌트는 단일 메시지 렌더링을 수행하고, 생성 시 메시지 타입과 내용을 동적으로 받아서 렌더링
  • 스타일과 애니메이션은 토스트 생성 시 적용하고, 메시지별 생성과 제거를 담당하는 매니저를 별도로 구현
  • 토스트 메시지들의 동적 생성/소멸로 리소스 관리 개선

코드 예시:

export const Toast = (message, type) => {
  const styles = {
    success: 'bg-green-600',
    error: 'bg-red-600',
    info: 'bg-blue-600',
  };
  return `<div class="toast ${styles[type]}">${message}</div>`;
};

// 별도 ToastManager에서 생성, 삭제 담당

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