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
24 changes: 0 additions & 24 deletions .github/workflows/chromatic.yml

This file was deleted.

31 changes: 31 additions & 0 deletions .github/workflows/code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Claude Code Review

on:
pull_request:
types: [opened, synchronize]

permissions:
contents: read
pull-requests: write
id-token: write

jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
이 PR을 리뷰해줘. 아래 항목을 중심으로 분석하고 PR에 리뷰 코멘트를 남겨줘.

## 체크 항목
- 코드 품질 및 가독성
- FSD 레이어 규칙 준수 (shared → entities → features → widgets → pages → app)
- Tailwind 사용 (Emotion 신규 작성 금지)
- 버그 가능성 (타입 오류, 엣지 케이스 등)
- 웹 접근성 (aria 속성, 키보드 네비게이션)
- 보안 취약점 (XSS, 민감 정보 노출 등)
claude_args: "--max-turns 5"
204 changes: 204 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# MOING Frontend - 리팩토링 가이드라인

## 프로젝트 개요
여행 매칭 플랫폼. React → Next.js 긴급 마이그레이션 이후 체계적인 리팩토링 진행 중.
- **Framework**: Next.js 14 (App Router) + React 18 + TypeScript
- **현재 상태**: 448개 파일, ~40,460줄, 대부분 Client Component

---

## 리팩토링 목표
1. **Next.js 방식으로 개편** - Server Component 전략 (Option B: 하이브리드)
2. **FSD 폴더 구조** - Feature-Sliced Design 완전 적용
3. **성능 최적화** - 테스트 기반 측정 후 개선
4. **TDD + 에러 핸들링** - Vitest (단위/통합) + Playwright (E2E)
5. **Tailwind CSS 전환** - Emotion 제거
6. **UX 개편** - 진행 중 별도 지시

---

## 확정된 기술 결정

| 항목 | AS-IS | TO-BE |
|------|-------|-------|
| 스타일링 | Emotion (CSS-in-JS) | Tailwind CSS |
| 폴더 구조 | 도메인 분리 구조 | FSD (Feature-Sliced Design) |
| 테스트 | 없음 | Vitest + Playwright |
| Server/Client | 전부 Client Component | 하이브리드 (Option B) |
| 타입 강화 | 후순위 (나중에) | - |
| 라이브러리 | Zustand / React Query / Axios | 유지 |

---

## FSD 목표 구조

```
src/
├── app/ # Next.js App Router (라우팅만)
│ ├── layout.tsx
│ ├── (auth)/
│ ├── trip/
│ └── ...
├── pages/ # 페이지 조합 레이어
├── widgets/ # 독립적 UI 블록
├── features/ # 사용자 시나리오 단위 기능
├── entities/ # 도메인 모델 (trip, user, comment...)
└── shared/ # 공통 재사용 요소
├── ui/ # 디자인 시스템 (버튼, 인풋, 모달...)
├── api/ # axios 인스턴스, 공통 fetch
├── hooks/ # 범용 훅
├── lib/ # 외부 라이브러리 래핑
├── constants/ # 전역 상수
└── types/ # 전역 타입
```

---

## 진행 단계 (Phase)

### Phase 0: 기반 구축 ✅
- [x] Vitest 설정
- [x] Playwright 설정
- [x] Tailwind 설치 (Emotion과 공존)
- [x] FSD 디렉토리 스캐폴딩

### Phase 1: shared 레이어
- `components/designSystem/` → `shared/ui/` 이전
- Tailwind 첫 마이그레이션
- 각 컴포넌트 단위 테스트 작성

### Phase 2: entities 레이어
- `model/` + `api/` → `entities/{domain}/`
- 도메인별 타입, API, 기본 모델 정의

### Phase 3: features 레이어
- `hooks/` + `components/` → `features/{feature}/`
- 각 feature마다 TDD로 진행

### Phase 4: pages / widgets 레이어
- `page/` → `pages/` + `widgets/`
- Server Component 전환 (하이브리드)
- 성능 테스트 → 최적화

---

## 협업 프로토콜

### 모든 작업은 아래 절차를 따른다
1. **계획 제시** → 사용자 검수 및 승인
2. **작업 실행**
3. **결과 보고** → 사용자 검수 및 승인
4. 승인 없이 다음 단계로 넘어가지 않는다

### 문서화 규칙
> **목적**: 블로그 포스팅 및 이력서 작성 재료로 활용. 단순 작업 기록이 아닌 **문제 → 원인 → 결정 → 결과** 서술 형식을 유지한다.

- 각 Phase 완료 시 `docs/refactoring/phase-{N}.md` 작성
- 기술 결정 사항은 `docs/decisions/` 에 ADR 형식으로 기록
- 변경된 파일 목록과 이유를 항상 기록

#### Phase 문서 필수 포함 항목
1. **배경 / 문제 정의** - 왜 이 작업이 필요했는가 (Before 상태, 수치 포함)
2. **선택지와 의사결정** - 어떤 옵션을 검토했고 왜 이것을 골랐는가
3. **구현 과정** - 핵심 기술적 도전과 해결 방법
4. **Before / After 비교** - 코드 예시 포함
5. **결과 및 수치** - 파일 수, 라인 수, 테스트 커버리지, 성능 지표 등
6. **트러블슈팅** - 겪은 문제와 해결 과정 (블로그 소재로 가장 중요)
7. **회고 / 배운 점** - 다음에 다르게 할 것, 인사이트

### 코딩 규칙
- 새로 작성하는 코드는 반드시 Tailwind 사용 (Emotion 신규 작성 금지)
- 새로 작성하는 컴포넌트는 FSD 구조에 맞게 위치
- 모든 새 기능은 테스트 먼저 작성 (TDD)
- console.log 사용 금지 (Sentry 또는 logger 사용)
- `any` 타입 신규 사용 금지

---

## 웹 접근성 (Web Accessibility) 가이드라인

> Phase 1.5 이후 모든 새 컴포넌트 및 수정 컴포넌트에 적용한다.

### 기본 원칙

**컴포넌트 고정값 vs 컨텍스트 의존값**

| 구분 | 적용 위치 | 예시 |
|------|-----------|------|
| **고정값** (디자인 시스템에서 처리) | 컴포넌트 내부 하드코딩 | `role="dialog"`, `aria-modal="true"`, `aria-expanded`, `aria-haspopup` |
| **컨텍스트 의존값** (호출부에서 주입) | prop으로 받기 | `aria-label`, `aria-describedby`, `aria-labelledby` |

### 컴포넌트별 접근성 규칙

#### 버튼 / 인터랙티브 요소
- `CloseButton`: `aria-label="닫기"` 기본값 (prop으로 override 허용)
- `RemoveButton`: `aria-label="삭제"` 기본값
- 아이콘만 있는 버튼은 반드시 `aria-label` 또는 `<span className="sr-only">` 포함
- `type="button"` 명시 (form submit 방지)

#### 폼 / 입력 요소
- `StateInputField`: 에러 메시지와 `aria-describedby`로 연결 (ID 기반)
- 에러 메시지 `<p id="{inputId}-error">`, 입력 `aria-describedby="{inputId}-error"`
- `CodeInput`: 각 셀에 `aria-label="n번째 숫자"` (1~6)
- `aria-invalid="true"` — 에러 상태 시 추가
- `aria-required` — 필수 필드에 명시

#### 선택 / 드롭다운
- `Select`: `role="combobox"`, `aria-expanded`, `aria-haspopup="listbox"`
- 옵션 목록: `role="listbox"`, 각 옵션: `role="option"`, `aria-selected`

#### 모달 / 다이얼로그
- `BaseModal`: `role="dialog"`, `aria-modal="true"`, `aria-labelledby` (제목 ID 연결)
- `BottomSheetModal`: 동일
- **Focus Trap 필수**: 모달 열릴 때 포커스 진입, 닫힐 때 트리거 버튼으로 복귀
- `Escape` 키로 모달 닫기 지원

#### 이미지
- `RoundedImage`: Phase 1에서 `div + background-image` 사용 → Phase 1.5에서 `<img>` + `alt` prop 전환 고려

### 키보드 네비게이션 체크리스트
- [ ] `Tab` / `Shift+Tab` 으로 모든 인터랙티브 요소 접근 가능
- [ ] `Enter` / `Space` 로 버튼 활성화
- [ ] `Escape` 로 모달/드롭다운 닫기
- [ ] focus outline 항상 표시 (`outline-none` 사용 금지, `focus-visible` 활용)
- [ ] 모달 내부 focus trap 적용

### 자동 접근성 테스트 (jest-axe)

```typescript
// 모든 shared/ui 컴포넌트 테스트에 포함할 패턴
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

it('접근성 위반이 없어야 한다', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
```

### Tailwind 접근성 유틸리티
- `sr-only`: 시각적으로 숨기되 스크린리더에 읽힘 (아이콘 버튼 텍스트)
- `focus-visible:ring-2`: 키보드 포커스 표시 (마우스 클릭 시 ring 미표시)
- `not-sr-only`: sr-only 해제

### 참고 기준
- WCAG 2.1 AA 준수 목표
- ARIA Authoring Practices Guide (APG) 패턴 참조

---

## 문서 구조

```
docs/
├── refactoring/
│ ├── phase-0.md
│ ├── phase-1.md
│ └── ...
├── decisions/ # Architecture Decision Records
│ ├── 001-tailwind.md
│ ├── 002-fsd.md
│ └── ...
└── progress.md # 전체 진행 현황
```
28 changes: 28 additions & 0 deletions docs/decisions/001-tailwind.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ADR 001: Emotion → Tailwind CSS 전환

## 상태
확정 (2026-03-20)

## 배경
기존 프로젝트는 Emotion (CSS-in-JS)을 사용 중이었다.
Next.js App Router 환경에서 Emotion은 런타임에 CSS를 생성하기 때문에
Server Component와 호환되지 않아 모든 컴포넌트가 Client Component로 강제된다.

## 결정
Emotion을 Tailwind CSS로 전환한다.

## 근거
- Tailwind는 빌드타임에 CSS를 생성 → Server Component와 완벽 호환
- 번들 사이즈 감소 (런타임 CSS 생성 제거)
- Next.js 공식 권장 스타일링 방식 중 하나
- 하이브리드 Server Component 전략(Option B) 실현을 위한 필수 조건

## 마이그레이션 전략
- Emotion과 Tailwind 설치 공존 (Phase 0)
- Phase 1부터 신규 코드는 Tailwind만 사용
- 기존 컴포넌트는 FSD 이전 시 Tailwind로 교체
- Emotion 신규 작성 금지

## 영향
- 모든 styled-component 패턴 제거 필요
- className 기반 스타일링으로 전환
42 changes: 42 additions & 0 deletions docs/decisions/002-fsd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ADR 002: FSD (Feature-Sliced Design) 완전 적용

## 상태
확정 (2026-03-20)

## 배경
React → Next.js 긴급 마이그레이션으로 인해 폴더 구조가 도메인 분리 형태로
구성되어 있으나, 컴포넌트 간 의존성이 불명확하고 관심사 분리가 부족하다.

## 결정
FSD(Feature-Sliced Design) 아키텍처를 완전 적용한다.

## FSD 계층 구조

```
shared → entities → features → widgets → pages → app
(공통) (도메인) (기능) (UI블록) (페이지) (라우팅)
```

- 상위 레이어는 하위 레이어에 의존할 수 없다 (단방향 의존성)
- 같은 레이어 내 슬라이스 간 직접 참조 금지

## 각 레이어 책임

| 레이어 | 책임 | 현재 코드 출처 |
|--------|------|----------------|
| `app/` | Next.js 라우팅만 | `src/app/` (유지) |
| `pages/` | 페이지 조합 | `src/page/` |
| `widgets/` | 독립 UI 블록 | 대형 컴포넌트 분리 |
| `features/` | 사용자 시나리오 | `src/hooks/` + `src/components/` |
| `entities/` | 도메인 모델 | `src/model/` + `src/api/` |
| `shared/` | 공통 요소 | `src/components/designSystem/`, `src/utils/`, `src/constants/` |

## 근거
- 단방향 의존성으로 코드 복잡도 감소
- feature 단위로 독립적 개발/테스트 가능
- 대형 컴포넌트(862줄) 분할의 명확한 기준 제공

## 마이그레이션 전략
- Phase 0: 빈 디렉토리 스캐폴딩
- Phase 1: shared 레이어부터 시작
- Phase 2~4: 하위→상위 순서로 순차 이전
36 changes: 36 additions & 0 deletions docs/decisions/003-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# ADR 003: 테스트 전략 - Vitest + Playwright

## 상태
확정 (2026-03-20)

## 배경
현재 테스트 코드가 전혀 없는 상태에서 대규모 리팩토링을 진행한다.
테스트 없이 리팩토링하면 회귀 버그를 감지할 수 없다.

## 결정
- **단위/통합 테스트**: Vitest + React Testing Library
- **E2E 테스트**: Playwright
- **방식**: TDD (테스트 먼저 작성 후 구현)

## 테스트 레벨

| 레벨 | 도구 | 대상 |
|------|------|------|
| 단위 테스트 | Vitest | shared/ui 컴포넌트, utils, hooks |
| 통합 테스트 | Vitest + RTL | features, entities API |
| E2E 테스트 | Playwright | 주요 사용자 플로우 |

## 주요 E2E 시나리오 (Phase 4에서 작성)
- 로그인 / 소셜 로그인
- 여행 생성 플로우
- 여행 검색 및 신청
- 마이페이지 수정

## TDD 원칙
1. 실패하는 테스트 먼저 작성
2. 테스트를 통과하는 최소한의 코드 작성
3. 리팩토링

## 성능 테스트 (Phase 4)
- Lighthouse CI로 빌드 시 성능 점수 측정
- Core Web Vitals (LCP, FID, CLS) 기준점 설정 후 개선
Loading
Loading