jtwjs Dev Wiki
  • DEV_ROAD
    • 💪🏻 생존하기
    • Week 1
      • 개발 환경 세팅
      • 타입스크립트
      • 리엑트
      • Testing Library
      • Parcel & ESLint
    • Week 2
      • JSX
      • Virtual DOM
    • Week 3
      • React Component
      • React State
    • Week 4
      • Express
      • Fetch API & CORS
      • React Hook
      • useRef & Custom Hook
    • Week 5
      • TDD
      • React Testing Library
      • MSW
      • Playwrite
      • Snapshot
    • Week 6
      • Separtion of Concerns
      • Principle
      • DI, (Dependency Injection)
      • Reflect-metadata
      • TSyringe
      • External Store
      • Follow Redux
      • usestore-ts
      • useSyncExternalStore
    • Week 7
      • Routing
      • Routes
      • Router
      • Navigation
    • Week 8
      • Design System
      • Style Basics
      • CSS-in-JS
      • Styled-Components
      • Global Style & Theme
    • Week 9
      • 개발하기 전 준비
      • 상품 목록 페이지
      • 상품 상세 페이지
      • 장바구니 페이지
    • Week 10
      • 로그인
      • 로그아웃
      • 회원가입
      • 주문 목록 & 주문 상세
    • Week 11
      • 배송 정보 입력
      • 포트원 결제 요청
      • 배송 및 결제 정보 전달
    • Week 12
      • 관리자 웹사이트개발시작
  • DEV_NOTE
    • TypeScript
      • 기본적 문법
        • Enum
        • 다형성
          • Untitled
        • 구조적 타이핑
        • 제너릭 타입
        • 컨디셔널 타입
        • 함수 메서드 타이핑
        • infer로 타입스크립트의 추론 직접 활용
        • 재귀 타입
        • 템플릿 리터럴 타입
        • 추가적인 타입 검사 satisfies 연산자
        • 타입스크립트 건망증
        • 원시 자료형에도 브랜딩 기법 사용 가능
        • 타입 좁히기
        • 유용한 타입 만들기
        • 데코레이터 함수
        • 앰비언트 선언도 선언 병합이 된다.
        • 앰비언트 선언도 선언병합이 된다.
    • Testing
      • Unit Testing
      • 단위 테스트의 두 분파
      • 좋은 단위 테스트를 구성하는 4대 요소
      • 테스트 대역과 식별할 수 있는 동작
      • 단위 테스트 스타일
      • 가치 있는 단위 테스트를 위한 리팩토링
      • 통합 테스트
      • Cross Browsing Testing
      • 기능 테스트 종류
      • React Testing Pattern
      • 프론트엔드 테스트 입문
        • 테스트 범위
        • 단위 테스트 검증
        • Mock
        • UI 컴포넌트 테스트
        • 테스트 커버리지
        • 웹 통합 테스트
        • MSW
        • 스토리북
        • 시각적 회귀 테스트
        • E2E 테스트
        • Github Actions 설정
        • 깃허브 액션에서 E2E
      • 시프트 레프트
        • 테스트 기본중의 기본
        • 단위 테스트
        • 코드 복잡도
        • 리팩터링
        • 코드 리뷰
        • 통합 테스트 패턴
        • 시스템 테스트의 자동화
        • 탐색적 테스트
      • Test Tip
      • vitest
      • playwright
      • Test Data Generator
      • MSW
    • Algorithm
      • coding test
      • Data Structure
    • Next.js
      • Data Fetching
      • Hydration
      • Next 13
      • Optimization
      • Next 15
    • Tailwind
      • Tailwind CSS
      • Theme
      • Directives
      • Tool
      • Design System
    • Storybook
      • Storybook
      • CSF3
      • CDD
      • Headless Component
    • Funtional Programming
      • 함수형 프로그래밍
      • 참조 투명성
      • 부수효과
      • 함수 합성
      • 제너릭 타입 활용하기
      • 암묵적 입출력
      • 액션과 계산, 데이터
      • 계층형 설계
      • 호출 그래프
      • 함수형 설계
      • 불변성
      • 일급 함수
      • 함수형 도구
    • Git
      • Github Actions
      • Conflict
      • Branch 전략
    • Contents Format
      • Audio
    • 3D Graphic
      • 3D keyword
      • Three.js
      • Geometry
      • Material
      • Light
      • Camera
      • Decal
      • Rotation
      • Text
      • Shadow
      • Fog
      • Post Processing
      • Animation
      • Math
        • Vector Space
        • 벡터의 연산
        • 회전 계산
      • 3D 컨텐츠가 만들어지는 과정
      • R3F
      • Env
      • Scene
      • Transform
      • R3F
      • Interaction & Raycast
      • Rendering Algorithnm
      • Blender
      • Blender
    • Accessibility
      • 접근성이란
    • Interactive Web
      • Parallax
      • Canvas
      • requestAnimationFrame
      • Effect
      • HSL
      • React.js + Canvas
      • Matter.js
    • AWS
      • DevOps
      • Amplify
      • S3
      • 클라우드 컴퓨팅
        • 온프레미스와 클라우드
        • 클라우드 도입효과
        • 클라우드 컴퓨팅의 범위
        • 컴퓨팅 옵션
          • EC2 - Virtual Machin
          • ECS, EKS - Container
          • Lambda - Serverless
        • 네트워크 가상화
        • 스토리지
        • 데이터베이스
        • 데이터 수집
        • 머신 러닝 영역
        • IoT 영역
        • 블록체인 영역
      • 클라우드 아키텍처 설계
    • Network
      • Web Server & WAS
    • System Design
      • System Design
      • Component
      • 의존성을 배제한 개발
      • Error Handling
      • Architecture
        • 모노로틱 아키텍처
        • Clean Architecture
        • Layered Architecture
        • 이벤트 기반 아키텍처
      • 상황을 파악하는 메타인지
      • 중복 문제 해결하기
      • Monorepo Arhitecture
        • 모노레포 운영과 트러블슈팅
        • Module Federation
      • 코드 병목지점
      • API 대응
      • 공통 코드
      • Infra 구축
      • 모듈 기반의 개발 방식
      • Design System
        • 최소 수준의 아키텍처 설정
        • 더 효율적인 디자인시스템 만들기
        • 디자인 시스템과 UI 라이브러리 목적
        • 디자인 토큰
      • 효율적인 업무
        • 업무 프로세스 병목 파악
      • Clean Code
      • Design Pattern
        • CQRS Pattern
        • Strangler Fig Pattern
        • 데코레이터 패턴
        • 커맨드 패턴
        • 전략 패턴
        • 옵저버 패턴
      • A/B 테스팅
      • 대규모 리엑트 웹앱 개발
        • 복잡성 관리
        • 모듈성
        • 성능
        • 디자인 시스템
        • 데이터 패칭
        • 상태 관리
        • 국제화
        • 코드 조직화하기
        • 개인화 A/B 테스팅
        • 확장 가능한 웹 아키텍처
        • 테스팅
        • 툴링
        • 기술적 마이그레이션
        • 타입스크립트
        • 라우팅
        • 사용자 중심 API 디자인
        • 리액트 미래
    • Performance
      • React DevTools
      • Component 최적화
      • Page Load
      • API
    • MFA
      • MSA
      • MFA 도입하기
      • Monorepo
        • Monorepo Tool
        • Yarn Berry Workspace
        • Turborepo
      • MFA Composition
      • SPA 통합
      • Design System
      • Package Manager
        • Yarn
        • pnpm
      • Transpiler & Bundler
        • Babel
        • Rollup
        • esbuild
        • swc
        • Webpack
        • Vite
      • 분해와 통합을 위한 여러 기술 비교
    • State Management
      • Zustand
    • React v18
      • Automatic batching
      • Suspense
      • Transition
    • SEO
      • Search Engine Optimization
      • Open Graph Element
      • Metadata
    • FE Develop
      • Scrubbing
      • Clipboard
    • Refactoring
      • 리팩토링 깊게 들여다보기
      • 긴 코드 조각내기
      • 타입 코드 처리하기
      • 유사한 코드 융합하기
      • 데이터 보호
      • 코드 추가 및 제거
    • OAuth 2.0
    • Analytics
      • Mixpanel
    • ETC
      • VSCode
    • React Hook In Action
      • useContext & Provider
      • 커스텀 훅
      • 코드 분할하기 with Suspense, lazy
      • Suspense와 이미지 적재하기
      • useTransition, uesDeferredValue
      • SuspenseList
Powered by GitBook
On this page
  • 상품 목록 보기 기능
  • User Scenario
  • 상품 카테고리 목록
  • User Scenario
  1. DEV_ROAD
  2. Week 9

상품 목록 페이지

Previous개발하기 전 준비Next상품 상세 페이지

Last updated 1 year ago

상품 목록 보기 기능

상품 목록을 얻어서 표시하는 화면

  1. 상품 목록 패칭

  2. 상품 목록 리스팅

User Scenario

  • 카테고리 별로 상품 목록이 보여져야 한다.

  • 카테고리를 선택하지 않으면 전체 상품이 노출된다.

  • 각 상품은 상품명과 가격이 노출된다.

  • 상품을 클릭하면 상품 상세 페이지로 넘어간다.

  • 객체지향 디자인패턴 중 하나로 객체가 null 값을 반환할 때 생길 수 있는 문제를 해결하기 위해 사용됨

  • NullPointerException 같은 예외를 피하기 위해 null 대신 객체의 인터페이스를 충족하지만 값이 없는 객체를 반환한다.

다양한 나라의 언어에 맞는 문자열, 숫자, 시간, 날짜 비교와 관련된 포맷팅 기능을 제공한다.

Unit Test

ProductList.test.tsx

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Route, useParams } from "react-router-dom";

import { products } from "@/fixtures";
import { withAllContexts, withRouter } from "@/tests/utils";

import ProductList from "./ProductList";

const context = describe;

describe("ProductList", () => {
  it("render correctly", async () => {
    render(
      withAllContexts(withRouter(<Route path="/" element={<ProductList />} />))
    );

    await Promise.all(
      products.map(async (product) => {
        await waitFor(() =>
          expect(screen.getByText(product.name)).toBeInTheDocument()
        );
        await waitFor(() =>
          expect(screen.getByText(product.price)).toBeInTheDocument()
        );
      })
    );

    await waitFor(() => expect(screen.getAllByRole("link")).toHaveLength(2));
  });

  context("when product item is clicked", () => {
    const product = products[0];

    it("navigate to the product detail page", async () => {
      function DetailPage() {
        const params = useParams();
        return <pre>{JSON.stringify(params)}</pre>;
      }

      render(
        withAllContexts(
          withRouter(
            <>
              <Route path="/" element={<ProductList />} />
              <Route path="/:id" element={<DetailPage />} />
            </>
          )
        )
      );

      await waitFor(() => screen.getByText(product.name));
      const linkBtn = screen.getAllByRole("link")[0];

      userEvent.click(linkBtn);

      await waitFor(() =>
        expect(screen.getByText(new RegExp(product.id))).toBeInTheDocument()
      );
    });
  });
});

Product.test.tsx

import { render, screen } from "@testing-library/react";

import { products } from "@/fixtures";
import { withAllContexts } from "@/tests/utils";

import Product from "./Product";

describe("Product", () => {
  const product = products[0];
  it("render correctly", () => {
    render(withAllContexts(<Product product={product} />));

    expect(screen.getByRole("img")).toHaveAttribute(
      "src",
      product.thumbnail.url
    );

    expect(screen.getByText(product.name)).toBeInTheDocument();
    expect(screen.getByText(product.price)).toBeInTheDocument();
  });
})
E2E Test
/// <reference types="cypress" />
import "@testing-library/cypress/add-commands";

describe("Product List", () => {
  beforeEach(() => {
    cy.visit("http://localhost:8081");
  });

  context("when product link btn is clicked", () => {
    it("display products from all categories", () => {
      cy.findByRole("link", { name: "Products" }).click();

      cy.findByText(/CBCL 하트자수맨투맨/).should("exist");
      cy.findByText(/밴딩스커트/).should("exist");
      cy.findByText(/CBCL EARRING Silver/).should("exist");
    });
  });

  context("when product is clicked", () => {
    it("navigate product detail page", () => {
      cy.findByRole("link", { name: "Products" }).click();

      cy.findByRole("link", { name: /CBCL 하트자수맨투맨/ }).click();

      cy.findByText("상품 상세 페이지").should("exist");
    });
  });
});

상품 카테고리 목록

외부에서 상품 카테고리를 주입받아(API Fetching) 헤더에 표시

해당 데이터 관리를 어디서 하느냐에 따라 외부(API)에서 주입받는 데이터, 내부에서 관리하는 데이터로 구분할 수 있다.

즉, 어드민 페이지에서 상품 카테고리를 관리한다면 외부에서 데이터를 주입받아 표시하는 형태로 진행한다.

자주 바뀌지 않고 클라이언트에서 즉시 수정하는게 편한 데이터는 클라이언트 내부에서 관리하는 것이 좋다.

User Scenario

  • 어드민에서 지정한 카테고리 목록들이 표시된다.

  • 카테고리 버튼을 클릭하면 해당하는 상품 목록 페이지로 넘어간다.

Unit Test
import { render, screen, waitFor } from "@testing-library/react";
import { Route } from "react-router-dom";

import { categories } from "@/fixtures";
import { withAllContexts, withRouter } from "@/tests/utils";

import Header from "./Header";

describe("Header", () => {
  function renderHeader() {
    return render(
      withAllContexts(withRouter(<Route path="/" element={<Header />} />))
    );
  }

  it("render correctly", () => {
    renderHeader();

    expect(
      screen.getByRole("heading", { level: 1, name: "Shop" })
    ).toBeInTheDocument();
    expect(screen.getByRole("link", { name: "Products" })).toBeInTheDocument();
    expect(screen.getByRole("link", { name: "Cart" })).toBeInTheDocument();
  });

  it("render categories", async () => {
    renderHeader();

    await Promise.all(
      categories.map(async (ctg) => {
        await waitFor(() =>
          expect(
            screen.getByRole("link", { name: ctg.name })
          ).toBeInTheDocument()
        );
      })
    );
  });
});
E2E Test
/// <reference types="cypress" />
import "@testing-library/cypress/add-commands";

describe("Product List", () => {
  beforeEach(() => {
    cy.visit("http://localhost:8081");
  });
  
  context("when category btn is clicked", () => {
    it("display product from  categories", () => {
      cy.findByRole("link", { name: "top" }).click();

      cy.findByText(/밴딩스커트/).should("not.exist");
      cy.findByText(/CBCL 하트자수맨투맨/).should("exist");

      cy.findByRole("link", { name: "acc" }).click();

      cy.findByText(/CBCL 하트자수맨투맨/).should("not.exist");
      cy.findByText(/CBCL EARRING Silver/).should("exist");
      cy.findByText(/62000/).should("exist");
    });
  });
});
Null Object Pattern
Intl Object