Skip to content

프론트엔드 테스트 도입 — 설계 문서 ​

작성일: 2026-06-10 상태: 범위 승인 완료, 구현 계획 수립 대기 룰 문서: .claude/rules/frontend/testing.md (테스트 작성 규칙 — 본 문서는 무엇을 먼저 만들지를 결정)

1. 목표 ​

전 앱에 프론트엔드 테스트를 도입하되, 단일 원칙으로 조직한다: 조용한 깨짐(silent breakage)을 잡는다 — throw 없이 그럴듯한 오답을 내는 로직(급여 숫자, 날짜/타임존, 이스케이프, URL/탭 라우팅, 필드 매핑). 커버리지 퍼센트는 명시적으로 목표가 아니다.

사용자와 합의된 범위 결정:

  • 파일럿이 아닌 전 앱 도입, 유닛 우선 + 선별적 컴포넌트 테스트. e2e는 임계 여정만.
  • 이번 작업의 산출물은 러너 + 테스트 코드. 공유 vitest 프리셋 패키지, happy-dom/Testing Library 셋업, CI 연동, 커버리지 게이트는 이후 인프라 작업으로 분리.
  • 타 앱으로의 E2E 확장은 이번 작업에 포함.

2. 근거 자료 ​

세 가지 조사가 대상 선정의 근거다:

  1. 코드 전수조사 — apps/*/src와 packages/components/src의 export 심볼 166개 + 거대 app.ts 내부의 비공개 헬퍼(team-manager 6,069줄, admin 5,852줄 + scheduler 7,725줄, hr-system 6,028줄)까지 확인. 현재 프론트엔드 테스트 도구는 전무: vitest는 packages/test에만 존재(v2.1.9), happy-dom/jsdom/@testing-library는 어디에도 미설치.
  2. 버그 히스토리 마이닝 — 프론트엔드 fix 커밋 ~95건(2025-11 이후)의 클러스터: 탭 라우팅 회귀(6건+, PR 251/252), Messenger 정렬/타임존(9건+), GA의 API 인터페이스 불일치(PR 227/249), positionFloatingMenu 동일 버그 2회 수정, importer 컬럼 중복 제거 수정, 로그인 식별자 trim, API base URL 파싱(PR 253).
  3. 중복 감사 — kr-holidays.ts가 admin/team-manager에 동일 사본으로 존재; toDateStr(UTC 기반, 하루 밀림 의심 버그)이 team-manager app.ts와 admin scheduler.ts에 중복; positionFloatingMenu는 components/admin 사본이 이미 드리프트; router.ts 변형이 7개 앱에 존재.

3. 유닛 테스트 — Wave 1 (테스트 파일 21개) ​

러너: 8개 패키지(hr-system, importer, admin, team-manager, employee-app, general-affairs, management-dashboard, packages/components)에 vitest devDependency + "test": "vitest run" / "test:watch": "vitest" 스크립트 추가. zero-config(기본 env node, 코로케이션 *.test.ts 자동 탐색). 이번 wave에서는 vitest.config 파일을 만들지 않는다.

패키지테스트 파일대상과 근거
hr-system (6)escape-html.test.ts수제 XSS 이스케이프 — 케이스 하나 누락이 조용히 XSS를 연다
selection.test.ts셀 선택 + TSV 클립보드 export
history.test.tsundo/redo 트랜잭션 스택
styles.test.tsbuildSelectionCss/buildAnchorCss 순수 문자열 빌더
router.test.tsstripBasePath/withBasePath — 탭 라우팅 버그군
layout-persist.test.tslocalStorage 레이아웃 ↔ 기본 컬럼 병합(storage 스텁)
importer (5)excel-reader.test.tsexcelDateToDate/parseString/parseNumber — 급여 숫자 파싱. 날짜 serial 특이점은 characterization
sheet-tab.parser.test.ts47개 시트가 타는 제네릭 엔진(parseGenericTab, inferColumnFormat) — 컬럼 중복 수정 이력
payroll-ledger.parser.test.ts급여대장 매핑(characterization)
overtime.parser.test.ts시간외 파싱(characterization)
salary-grade.parser.test.ts호봉 파싱(export 3개)
admin (2)kr-holidays.test.tsisHoliday/getHolidayName 동작
scheduler-helpers.test.tstoDateStr(characterization — UTC 하루 밀림 의심 버그, 별도 보고), getAssignmentLabels, initialTabFromUrl
team-manager (2)kr-holidays-sync.test.tsadmin/team-manager 공휴일 사본 deep-equal 동기화 가드
app-helpers.test.tstoDateStr 쌍둥이 + 탭 헬퍼
employee-app (1)api-environment-store.test.tsnormalizeApiBaseUrl/getStartupApiBaseUrl(Capacitor 모킹) — PR 253 버그군
general-affairs (1)app-helpers.test.tssafeNumber, formatDateTimeLocal(타임존), formatDateForInput
management-dashboard (1)month-helpers.test.tspreviousMonthOf(연 경계), formatYearMonth, generateYearRange, currentMonth
components (3)positionFloatingMenu.test.ts위치 기하 계산 — 동일 버그 2회 수정 + 사본 드리프트
messenger-helpers.test.ts대화 정렬 + 시간 표시(순수 헬퍼로 추출 후) — 정렬·타임존 모두 수정 이력 보유
LoginForm.test.ts식별자 정규화(trim) — 팩토리 객체 메서드 직접 호출

동반 소스 변경 (동작 무변경) ​

  • 현재 비공개인 헬퍼 ~9개에 export 추가: team-manager/admin의 toDateStr, getAssignmentLabels, initialTabFromUrl; general-affairs의 safeNumber/formatDateTimeLocal/formatDateForInput; management-dashboard의 월 산술 헬퍼들.
  • Messenger의 대화 정렬·시간 표시 로직을 순수 헬퍼 함수로 추출(같은 파일 또는 형제 모듈) — DOM 없이 테스트 가능하게.
  • characterization 테스트는 현재 동작을 고정한다. 동작이 틀려 보이는 경우(toDateStr의 UTC 변환)에도 테스트는 현재 동작을 기록하고 발견 사항만 사용자에게 보고 — 이번 작업에서 동작 수정은 하지 않는다(사용자가 요청하면 별도 진행).

4. E2E 확장 (Playwright, packages/test) ​

인프라: packages/test/playwright.config.ts에 앱별 project 추가(admin :4001, employee-app :4002, team-manager :4004, general-affairs :4005) — 각자 baseURL과 storageState 보유. global-setup을 확장해 필요한 페르소나를 API로 로그인(packages/test/src/scenarios/personas.ts의 시드 계정 재사용). 데이터는 API로 시드, 역할 기반 로케이터, 테스트 완전 격리 — .claude/rules/frontend/testing.md 준수.

신규 spec 5개 (임계도·버그 히스토리 순):

  1. e2e/tests/apps/leave-approval.spec.ts — employee-app에서 휴가 신청 → team-manager에서 승인 → employee-app에서 상태 반영 확인 (크로스앱 핵심 루프).
  2. e2e/tests/apps/overtime-approval.spec.ts — 같은 루프를 시간외로 (#1과 헬퍼 공유).
  3. e2e/tests/apps/admin-tab-routing.spec.ts — admin: 모든 탭 클릭 시 URL 갱신 → 새로고침 시 같은 탭+데이터 복원 → 뒤로가기 동작 (회귀 6건+ 클러스터 방어, PR 251/252).
  4. e2e/tests/apps/admin-add-employee.spec.ts — AddEmployeeWizard 끝까지 진행 → 직원 목록에 표시 (공유 컴포넌트, 온보딩 임계).
  5. e2e/tests/apps/ga-task-training.spec.ts — general-affairs: task 생성 → 실행 → 이수 기록; 법정의무교육 생성/이수 (PR 227/249 QA 클러스터 방어).

기존 hr-system 워크북 spec 15개는 그대로 유지.

5. 비범위 (연기 — 폐기 아님) ​

  • 공유 @hereby/vitest-config 프리셋 패키지, happy-dom + Testing Library 설치, Alpine 마운트 헬퍼 — Wave 1 이후 인프라 작업.
  • Wave 2 유닛: 나머지 bestrh 파서 8개, engine.ts/workbook-adapter.ts characterization, events.ts, Alpine 컴포넌트 레퍼런스 테스트, 나머지 router 변형들.
  • CI 파이프라인 연동과 커버리지 임계값.
  • 중복 모듈 통합(kr-holidays, toDateStr, positionFloatingMenu, router 패밀리) — 별도 리팩터링으로 표시. 통합 전까지는 동기화/characterization 테스트가 중복을 가드.

6. 검증·완료 기준 ​

  • 신규 테스트 파일 전부가 각 패키지의 pnpm --filter <pkg> test로 green.
  • repo 전체 pnpm lint:check && pnpm build 통과.
  • e2e spec이 로컬에서 make dev + 시드 DB 기준 green (pnpm test:e2e).
  • 산출 보고: 패키지별 추가 테스트, 표면화된 의심 버그(예: toDateStr), 후속 항목(Wave 2, 인프라 작업, 중복 통합 리팩터링).

7. Wave 1 구현 중 표면화된 의심 버그 (수정 안 함 — 별도 결정 필요) ​

  • toDateStr (team-manager app.ts / admin scheduler.ts 중복): Date 입력 시 toISOString() 기반이라 KST 자정~09:00 시각이 전날 날짜로 변환됨. 간호사 근무표 날짜 계산에 영향 가능. characterization 테스트로 고정됨 (toDateStr(new Date("2026-06-10T00:00:00+09:00")) → "2026-06-09" 실측).
  • formatDateForInput (general-affairs app.ts): 동일한 UTC 변환 패턴 — KST 자정 입력이 전날로 밀림 (테스트로 실측 고정).
  • parseNumber (importer excel-reader.ts): 회계식 음수 "(1,000)"이 조용히 0이 됨 — 엑셀 원본에 회계 서식 음수가 있으면 데이터 소실 가능.
  • loadLayout (hr-system layout-persist.ts): 신규 기본 컬럼이 저장 레이아웃과 병합될 때 사용자 순서와 교차하면 직관과 다른 순서가 나올 수 있음 (실측: 저장 [c,a] + 신규 b(기본 1번 위치) → [b,c,a] — b가 맨 앞에 끼어듦).

참고: repo 전체 pnpm lint:check는 mockups/option-*.html 2건의 기존 에러로 실패 상태였음(이번 작업과 무관, merge-base 시점부터 존재). 신규 테스트 파일은 린트 에러 0건.