Files
frontend/apps/cs/tests/unit/walkthrough.md
T

14 KiB
Raw Blame History

cs 앱 단위(Unit) 및 통합(Integration) 테스트 구축 결과 보고서

cs 애플리케이션 내의 컴포넌트 로직 검증 및 렌더링 검사 기능을 수행할 수 있도록 Vitest와 React Testing Library (RTL) 설정을 완료하고 샘플 코드를 생성했습니다.

변경 및 추가 사항 요약

1. 의존성 설치 및 스크립트 추가

  • apps/cs/package.json의 개발용 라이브러리로 다음 패키지들을 설치했습니다:
    • vitest, @testing-library/react, @testing-library/jest-dom, jsdom, @vitejs/plugin-react, vite-tsconfig-paths
  • scripts 항목에 단위 테스트 실행 스크립트를 정의했습니다:
    • "test:unit": "vitest run" (1회 실행)
    • "test:unit:watch": "vitest" (실시간 Watch 모드)

2. 설정 파일 정의

  • vitest.config.ts:
    • React 플러그인 및 tsconfig alias 경로 파싱을 연결했습니다.
    • 테스트 환경으로 가상 브라우저 돔(jsdom)을 연결했습니다.
    • Playwright E2E 테스트 스펙(*.spec.ts)과 충돌이 나지 않도록, 단위 테스트 범위를 tests/unit/**/*.{test,spec}.{ts,tsx} 경로로 제한했습니다.
  • setup-tests.ts:
    • DOM 전역 단언식인 @testing-library/jest-dom을 확장했습니다.

3. 샘플 및 페이지 테스트 코드 생성

  • sample-counter.tsx:
    • 상태값 관리(useState) 및 이벤트 처리, 5 초과 시의 경고문 조건부 렌더링 등이 구현된 React 컴포넌트입니다.
  • sample-counter.test.tsx:
    • 기본 렌더링 검사 (초기 값 및 제목 텍스트 검증)
    • 상태 변경 검사 (증가/감소 버튼 동작에 따른 값 업데이트 검증)
    • 통합 조건부 검사 (5 초과 시의 경고 노출 여부 검증)
  • login-page.test.tsx:
    • 실제 로그인 컴포넌트(LoginPage)를 대상으로 한 단위/통합 테스트 스위트입니다.
    • UI 요소 검증: 타이틀 렌더링, ID/PW 입력 필드 및 로그인 버튼 존재 확인.
    • 로그인 실패 케이스: authService.login API가 에러를 발생시킬 때 화면에 한글 에러 문구(아이디와 비밀번호가 일치하지 않습니다.)가 정상 표출되는지 검증.
    • 로그인 성공 케이스: 정상적으로 로그인이 완료되었을 때 window.location.href 가 "/dashboard" 로 셋업되어 이동되는지 검증.
    • Next.js next/image 컴포넌트 및 글로벌 window.location을 가상 돔 환경에 맞추어 적절하게 모킹 처리하였습니다.
  • use-session-extend-confirm.test.ts:
    • 세션 연장 팝업 제어용 공통 훅(useSessionExtendConfirm)에 대한 단위 테스트 스위트입니다.
    • 기본 타이머 대기 검증: 토큰 만료 버퍼 도달 전까지는 컨펌창이 나타나지 않고, 해당 시점(예: 10초) 경과 후에 confirm이 정상 호출되는지 검증.
    • 연장 승인 시나리오: 팝업에서 승인 시 silentRefresh()가 호출되고 갱신된 신규 토큰으로 재스케줄링되는지 검증.
    • 연장 거절 시나리오: 팝업에서 거절 시 declineSessionExtend()가 호출되고, 해당 토큰에 대해서는 더 이상 팝업을 띄우지 않도록 무한 루프가 방지되는지 검증.
    • 즉시 노출 시나리오: 이미 만료 버퍼 내인 경우 타이머 대기 없이 즉각적으로 컨펌창이 실행되는지 검증.
    • 미동작 예외 시나리오: 비활성화 상태(enabled: false)이거나 이미 토큰이 완전히 만료된 경우 어떠한 스케줄도 잡지 않는지 검증.
    • 가상 타이머(vi.useFakeTimers()) 및 마이크로태스크 큐 제어를 결합하여 비동기 훅의 작동 방식을 정밀하고 빠르게 시뮬레이션하였습니다.

4. Playwright E2E 테스트 코드 및 격리 스텁 뷰 추가

  • session-extend-confirm.spec.ts:
    • 세션 연장 팝업의 만료 전 타이머 동작과 실제 화면 조작 및 훅 상호작용을 검증하는 브라우저 E2E 테스트 스펙입니다.
    • 승인 E2E 시나리오: 만료 5분(버퍼 시간) 전 진입 시 리액트 HTML 모달 팝업이 노출되며, "확인" 버튼 클릭 시 리액트의 silentRefresh()가 트리거되고 모달이 닫히는 동작을 실 브라우저 상에서 검증.
    • 거절 E2E 시나리오: 모달 팝업 노출 상태에서 "취소" 버튼 클릭 시 세션 연장 요청이 거절되고 모달이 닫히며, 이후 시간이 더 지나도 모달이 중복 재노출되지 않는 동작을 검증.
  • page.tsx:
    • 실시간 API와 계정/시간 몽키패칭 부작용으로 인한 브라우저 락(Lock) 및 로그인 튕김 현상을 완벽하게 예방하기 위해 구축한 격리된 E2E 스텁 라우트입니다.
    • /test-session 경로는 ConfirmProvider와 ConfirmDialog 래퍼가 마운트되는 기본 레이아웃 하위에서 작동하므로, 실 브라우저가 직접 마운트된 모달 HTML 요소를 안정적으로 감지하고 클릭할 수 있는 신뢰성 높은 환경을 제공합니다.

5. Playwright E2E 테스트 개념 및 실전 사용법 가이드

5.1 Playwright 핵심 개념 (What is Playwright?)

Playwright는 Microsoft에서 개발한 차세대 End-to-End(E2E) 브라우저 자동화 및 테스트 프레임워크입니다. 사용자 관점에서 브라우저를 실제로 조작하며 전체 애플리케이션 플로우(인증, 페이지 이동, 폼 입력, 서버 연동)를 검증합니다.

Vitest(단위 테스트) vs Playwright(E2E 테스트) 비교

비교 항목 Vitest + React Testing Library Playwright (E2E)
실행 환경 Node.js + 가상 DOM (jsdom) 실제 브라우저 (Chromium, Firefox, WebKit)
속도 초고속 (밀리초 단위) 상대적으로 무거움 (수 초 ~ 수십 초)
검증 대상 개별 함수, 훅, 단일 컴포넌트 렌더링 로직 브라우저 렌더링, 실제 네트워크, 전체 사용자 시나리오
주요 역할 회귀 방지, 비즈니스 계산/매퍼 검증 최종 배포 전 시스템 통합 동작 보증

Playwright의 핵심 메커니즘

  1. 브라우저 컨텍스트 격리 (Browser Context Isolation):
    • 새 브라우저 프로세스를 매번 띄우는 대신, 가벼운 브라우저 컨텍스트를 생성하여 테스트 간 쿠키/스토리지 오염 없이 100% 독립된 환경을 제공합니다.
  2. 자동 대기 (Auto-waiting):
    • 요소를 클릭하거나 타이핑하기 전, 요소가 DOM에 부착(Attached), 표시(Visible), 활성화(Enabled), 애니메이션 정지 상태인지 자동으로 기다려 Flaky(불안정한) 테스트를 원천 차단합니다.
  3. Web-first Assertions:
    • expect(locator).toBeVisible()과 같은 단언문은 조건이 충족될 때까지 지정된 시간(기본 5초) 동안 자동으로 재시도합니다.

5.2 Playwright 실행 명령어 치트시트

apps/cs 폴더 또는 루트 디렉터리에서 실행합니다:

# 1. 헤드리스 모드로 전체 E2E 테스트 실행 (CLI 모드)
pnpm --filter cs test:playwright

# 2. 대화형 UI 모드 실행 (테스트 단계별 타임라인 탐색, 실시간 렌더링 시각화)
pnpm --filter cs test:playwright:ui

# 3. 브라우저 화면을 직접 보면서 실행 (--headed)
pnpm --filter cs exec playwright test --headed

# 4. 특정 테스트 파일만 지정 실행
pnpm --filter cs exec playwright test tests/session-extend-confirm.spec.ts
pnpm --filter cs exec playwright test tests/login.spec.ts

# 5. 특정 브라우저(엔진)만 지정 실행
pnpm --filter cs exec playwright test --project=chromium

# 6. 코드젠 (Codegen): 브라우저 조작 시 테스트 코드를 자동 생성해주는 도구
pnpm --filter cs exec playwright codegen http://localhost:3000

# 7. 마지막 테스트 실행 결과 HTML 리포트 열기
pnpm --filter cs exec playwright show-report

5.3 테스트 작성 표준 패턴 및 베스트 프랙티스

① 권장 로케이터(Locator) 사용 순서

Playwright는 사용자가 인지하는 방식을 반영하는 시맨틱 로케이터를 권장합니다:

  1. page.getByRole('button', { name: '확인' }) (접근성 및 의미 기반 - 최우선 권장)
  2. page.getByLabel('아이디') (폼 레이블 기반)
  3. page.getByPlaceholder('비밀번호를 입력하세요')
  4. page.getByText('저장되었습니다')
  5. page.locator('button.login_btn') (CSS 선택자 - 최후의 수단)

② 표준 테스트 구조 예시

import { test, expect } from "@playwright/test";

test.describe("로그인 및 대시보드 플로우", () => {
  test.beforeEach(async ({ page }) => {
    // 1. 사전 진입
    await page.goto("/auth/login");
  });

  test("아이디/비밀번호 입력 후 정상 대시보드로 이동해야 한다", async ({ page }) => {
    // 2. 사용자 조작 (자동 대기 내장)
    await page.getByPlaceholder("아이디").fill("admin");
    await page.getByPlaceholder("비밀번호").fill("secret123!");
    await page.getByRole("button", { name: "로그인" }).click();

    // 3. Web-first 단언문
    await expect(page).toHaveURL(/\/dashboard/);
    await expect(page.getByText("관리자님 환영합니다")).toBeVisible();
  });
});

5.4 프로젝트 설정 해설 (playwright.config.ts)

apps/cs/playwright.config.ts의 주요 설정 포인트:

  • testIgnore: "**/unit/**": Vitest로 실행되어야 할 jsdom 단위 테스트 파일이 중복 실행되지 않도록 격리.
  • workers: 1 & fullyParallel: false: 공유 세션(Redis, DB)을 사용하는 통합 인증 테스트 간의 충돌 방지.
  • webServer: 테스트 실행 전 개발 서버(http://localhost:3000)가 구동되어 있지 않으면 자동으로 구동하여 테스트 완료 후 종료.
  • trace: "retain-on-failure": 테스트가 실패한 경우에만 DOM 스냅샷, 콘솔 로그, 네트워크 요청이 담긴 Trace 파일을 저장하여 디버깅을 지원.

6. 검증 결과

1. Vitest 단위 테스트 결과

터미널에서 pnpm --filter cs run test:unit 명령을 실행하여 모든 단위 테스트(총 13개)가 정상 통과함을 검증하였습니다:

 RUN  v4.1.10 /Users/changwng/workspace.ona/frontend/apps/cs

 ✓ tests/unit/use-session-extend-confirm.test.ts (6 tests) 16ms
 ✓ tests/unit/sample-counter.test.tsx (4 tests) 94ms
 ✓ tests/unit/login-page.test.tsx (3 tests) 152ms

 Test Files  3 passed (3)
      Tests  13 passed (13)
   Start at  14:21:52
   Duration  819ms (transform 217ms, setup 109ms, import 367ms, tests 257ms, environment 1.04s)

속도 면에서 아주 빠르고 안정적으로 가상 돔 유닛 테스트 환경이 준비되었습니다.

2. Playwright E2E 테스트 결과

터미널에서 pnpm --filter cs exec playwright test tests/session-extend-confirm.spec.ts 명령을 실행하여, chromium, firefox, webkit 3대 주요 실 브라우저 환경에서 연장 성공 시 다음 팝업 억제 검증을 포함한 세션 연장 E2E 테스트(총 6개 스위트)가 100% 정상 통과함을 확인하였습니다:

Running 6 tests using 1 worker

  ✓  1 [chromium] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (11.6s)
  ✓  2 [chromium] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (9.8s)
  ✓  3 [firefox] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (16.4s)
  ✓  4 [firefox] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (15.3s)
  ✓  5 [webkit] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (11.1s)
  ✓  6 [webkit] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (9.9s)

  6 passed (1.3m)

이로써 단위/통합 테스트 환경과 연장 처리 성공 후 중복 팝업 억제 시나리오가 포함된 E2E 테스트 환경 구축 임무가 성공적으로 완결되었습니다.