Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: CI

on:
pull_request:
branches: [dev, main]
push:
branches: [dev, main]

# 같은 브랜치/PR의 이전 실행을 취소하여 리소스 절약
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# ─────────────────────────────────────────────
# Job 1: Lint & Type Check & Architecture
# ─────────────────────────────────────────────
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

# critical만 실패 처리 — steiger의 minimatch ReDoS(high)는 devDependency 한정이므로 제외
- name: Security audit
run: npm audit --audit-level=critical

- name: ESLint
run: npm run lint

- name: TypeScript
run: npx tsc --noEmit

- name: Architecture (FSD)
run: npm run lint:arch

# ─────────────────────────────────────────────
# Job 2: Test & Coverage Report
# ─────────────────────────────────────────────
test:
name: Test & Coverage
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # PR 코멘트 작성 권한
checks: write # Check run 생성 권한
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

# Storybook vitest 어댑터가 Playwright 브라우저를 사용
- name: Install Playwright browsers
run: npx playwright install chromium --with-deps

- name: Run tests with coverage
run: npm run test:coverage
env:
CI: true

# ── 테스트 결과 리포트 ──
# JUnit XML → PR Checks 탭에 테스트 목록 + 실패 상세 표시
- name: Publish test results
uses: dorny/test-reporter@v1
if: always()
with:
name: Vitest Results
path: test-results/junit.xml
reporter: java-junit
fail-on-error: false # 테스트 실패와 리포트 실패를 분리

# ── 커버리지 리포트 ──
# PR 코멘트에 파일별 커버리지 테이블 + 변화량(delta) 표시
- name: Coverage report
uses: davelosert/vitest-coverage-report-action@v2
if: always()
with:
json-summary-path: coverage/coverage-summary.json
json-final-path: coverage/coverage-final.json
# 커버리지가 낮은 파일만 PR 코멘트에 표시 (노이즈 감소)
file-coverage-mode: changes

# 아티팩트로 업로드 — 로컬 다운로드 및 추후 분석용
- name: Upload coverage artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: |
coverage/
test-results/
retention-days: 14

# ─────────────────────────────────────────────
# Job 3: Build
# ─────────────────────────────────────────────
build:
name: Build
runs-on: ubuntu-latest
needs: lint # lint 통과 후 빌드 (TypeScript 에러 중복 방지)
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build

# 빌드 결과물 업로드 — 배포 파이프라인 연동 또는 수동 검증용
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ storybook-static

# Generated reports
.reports
coverage
test-results

# Claude Code local settings
.claude/settings.json
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ React frontend for Payper payment platform.
## Tech Stack

- React 19 + TypeScript 5.9 + Vite 7
- TanStack Query v5 (서버 상태 관리)
- React Router v7
- Tailwind CSS 4 + Shadcn/ui (New York style)
- MSW 2 for API mocking
- Storybook 10 for component development
Expand Down
44 changes: 27 additions & 17 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from "eslint-plugin-storybook";
import tanstackQuery from "@tanstack/eslint-plugin-query";

import js from '@eslint/js'
import globals from 'globals'
Expand All @@ -8,22 +9,31 @@ import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([globalIgnores(['dist', 'public/mockServiceWorker.js']), {
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
export default defineConfig([
globalIgnores(['dist', 'public/mockServiceWorker.js']),
...tanstackQuery.configs['flat/recommended'],
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
rules: {
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
},
}, {
// Allow non-component exports in shared UI library (shadcn/ui pattern)
files: ['src/shared/ui/**/*.{ts,tsx}'],
rules: {
'react-refresh/only-export-components': 'off',
{
// Allow non-component exports in shared UI library (shadcn/ui pattern)
files: ['src/shared/ui/**/*.{ts,tsx}'],
rules: {
'react-refresh/only-export-components': 'off',
},
},
}, ...storybook.configs["flat/recommended"]])
...storybook.configs["flat/recommended"],
])
Loading