Files
BASE_REACT_PROJECT/README.md
2026-01-30 09:01:27 +09:00

1177 lines
33 KiB
Markdown

# Frontend 프로젝트 가이드
Vite + React + TypeScript 기반 프론트엔드 프로젝트 개발 가이드입니다.
---
## 목차
1. [개발 규칙](#1-개발-규칙)
2. [개발 환경 및 옵션 리스팅](#2-개발-환경-및-옵션-리스팅)
3. [설치 및 동작 방법](#3-설치-및-동작-방법)
4. [아키텍처 & 책임 분리 규칙](#4-아키텍처--책임-분리-규칙)
5. [네이밍 & 패키지 규칙](#5-네이밍--패키지-규칙)
6. [예외 처리 & 에러 응답 규칙](#6-예외-처리--에러-응답-규칙)
7. [로그 & 감사(Audit) 규칙](#7-로그--감사audit-규칙)
8. [설정 관리 규칙](#8-설정-관리-규칙)
9. [보안 관련 추가 규칙](#9-보안-관련-추가-규칙)
10. [테스트 & 검증 규칙](#10-테스트--검증-규칙)
11. [금지 패턴 / 안티 패턴 목록](#11-금지-패턴--안티-패턴-목록)
12. [성능 최적화 가이드](#12-성능-최적화-가이드)
13. [의존성 관리](#13-의존성-관리)
14. [트러블슈팅 가이드](#14-트러블슈팅-가이드)
---
## 1. 개발 규칙
### 1.1 시큐어 코딩 규칙 준수
- **OWASP Top 10 기반 보안 규칙 준수 필수**
- XSS 방지: `dangerouslySetInnerHTML` 금지, 필요 시 DOMPurify 사용
- 인증 정보 저장 금지: `localStorage`/`sessionStorage`에 토큰 저장 금지
- URL 인코딩: `encodeURIComponent` 사용 필수
- `eval` / `new Function` 사용 금지
- 환경 변수에 비밀키 저장 금지
**상세 규칙은 `base_arcitectures_md/SECURE_RULE.md` 참조**
### 1.2 공통 소스 기능 및 활용 방법
#### 공통 API (`common/api/`)
- **`http.ts`**: Axios 인스턴스
- `baseURL`, `timeout`, `withCredentials` 설정
- 전역 헤더 설정
- **`endpoints.ts`**: API 엔드포인트 상수
```typescript
import { API } from "@/common/api/endpoints";
const response = await http.get(API.AUTH.ME);
```
- **`interceptors.ts`**: 전역 인터셉터
- 요청: 전역 로딩 시작
- 응답: 전역 로딩 종료, 401/403 처리
#### 공통 인증 (`common/auth/`)
- **`AuthContext`**: 인증 상태 Context
- **`AuthProvider`**: 인증 상태 제공자
- **`useAuth`**: 인증 훅
```typescript
const { user, login, logout, refreshMe } = useAuth();
```
- **`ProtectedRoute`**: 인증 필요 라우트 보호
```typescript
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
```
- **`authService`**: 인증 API 호출
- `login()`, `logout()`, `me()`
#### 공통 컴포넌트 (`common/components/`)
**디렉터리 구조:**
```
common/components/
├── form/ # 폼 입력 컴포넌트 (Input, Select, Textarea, Checkbox, Radio)
├── action/ # 액션 컴포넌트 (Button)
├── display/ # 표시 컴포넌트 (Table)
├── system/ # 시스템 컴포넌트 (LoadingOverlay, LoadingProvider, Toast, CommonModal)
└── index.ts # 통합 export
```
**공통 원칙:**
- 모든 공통 컴포넌트는 `id`, `name`, `className` 전달 옵션 지원
- 스타일 관련 세부 구현은 CSS로 처리 가능한 영역은 최대한 CSS에 위임
- 컴포넌트는 범용성 위주로 설계하되, 과도한 추상화는 지양
- 동작이 필요한 부분은 props 옵션으로만 제어
**입력 / 액션 계열 컴포넌트:**
- **`Button.tsx`**: 버튼 컴포넌트
```typescript
import { Button } from "@/common/components";
<Button
disabled={false}
loading={false}
size="md" // "sm" | "md" | "lg"
variant="primary" // "primary" | "secondary" | "danger" | "outline" | "ghost"
onClick={() => {}}
>
버튼 텍스트
</Button>
```
- `disabled`: 비활성화
- `loading`: 로딩 상태 (중복 클릭 방지)
- `size`: 버튼 크기 (`sm`, `md`, `lg`)
- `variant`: 버튼 스타일 변형
- **`Input.tsx`**: 입력 컴포넌트 (검색 기능 포함)
```typescript
import { Input } from "@/common/components";
// 기본 텍스트 입력
<Input
type="text"
value={value}
onChange={setValue}
format={{ formatEmail: true }} // 이메일 포맷
format={{ formatPhone: true }} // 전화번호 포맷 (010-1234-5678)
format={{ validationRegex: /^[a-z]+$/ }} // 정규식 검증
search={{
enabled: true,
debounceMs: 300,
commitOnEnter: true,
clearOnEscape: true,
onCommit: (value) => {}
}}
/>
// 비밀번호 입력
<Input type="password" value={value} onChange={setValue} />
// 숫자 입력 (숫자 이외 문자 자동 제거)
<Input type="number" value={value} onChange={setValue} />
```
- 지원 타입: `text`, `password`, `number`
- `format`: 이메일/전화번호 자동 포맷, 정규식 검증
- `search`: debounce, commitOnEnter, clearOnEscape 옵션
- **`Select.tsx`**: 선택 컴포넌트
```typescript
import { Select } from "@/common/components";
<Select
options={[
{ value: "1", label: "옵션 1" },
{ value: "2", label: "옵션 2", disabled: true }
]}
value={selectedValue}
onChange={(value) => {}}
placeholder="선택하세요"
/>
```
- **`Textarea.tsx`**: 텍스트 영역 컴포넌트
```typescript
import { Textarea } from "@/common/components";
<Textarea
value={value}
onChange={setValue}
rows={5}
/>
```
- 기본 속성: `resize: none`, `width: 100%`
- **`Checkbox.tsx`**: 체크박스 컴포넌트
```typescript
import { Checkbox } from "@/common/components";
<Checkbox
checked={checked}
onChange={setChecked}
label="체크박스 레이블"
/>
```
- **`Radio.tsx`**: 라디오 버튼 컴포넌트
```typescript
import { Radio } from "@/common/components";
<Radio
name="radio-group"
checked={selected === "value"}
onChange={(checked) => {}}
label="라디오 레이블"
/>
```
**표시 계열 컴포넌트:**
- **`Table.tsx`**: 표준 테이블 컴포넌트
```typescript
import { Table } from "@/common/components";
<Table
columns={columns}
rows={rows}
getRowId={(row) => row.id}
loading={isLoading}
emptyText="데이터가 없습니다"
onRowClick={(row) => {}}
selection={selectionState}
sort={sortValue}
onSortChange={setSortValue}
/>
```
- 로딩, 빈 상태, 선택, 정렬, 페이징 지원
- **`CommonModal.tsx`**: 공통 모달 컴포넌트
```typescript
import { CommonModal } from "@/common/components";
<CommonModal
open={isOpen}
onClose={() => setIsOpen(false)}
title="모달 제목"
showFooter={true}
onConfirm={() => {}} // 전달 시 "확인", "닫기" 버튼 모두 표시
confirmText="확인"
cancelText="닫기"
width="50vw" // 기본값
height="50vh" // 기본값
>
모달 컨텐츠
</CommonModal>
```
- 구조: 헤더(타이틀, X 닫기) / 컨텐츠 / 푸터(확인/닫기 버튼)
- `onConfirm` 전달 시: "확인", "닫기" 버튼 모두 표시
- `onConfirm` 없을 시: "확인" 버튼만 표시 (단순 닫기)
- ESC 키로 닫기 지원
- 기본 사이즈: `width: 50vw`, `height: 50vh`
**시스템 컴포넌트:**
- **`LoadingOverlay.tsx`**: 전역 로딩 오버레이
```typescript
import { LoadingOverlay } from "@/common/components";
<LoadingOverlay visible={isLoading} />
```
- **`LoadingProvider.tsx`**: 전역 로딩 상태 관리
```typescript
import { LoadingProvider, useLoading } from "@/common/components";
const { visible } = useLoading(); // API 호출 시 자동으로 로딩 표시
```
- **`Toast.tsx`**: 토스트 알림 시스템
```typescript
import { useToast } from "@/common/components";
const toast = useToast();
toast.push("작업이 완료되었습니다.", "success"); // "success" | "error" | "warning" | "info"
```
**통합 Import:**
```typescript
// 통합 import (권장) - 모든 컴포넌트를 한 번에 import
import {
Button,
Input,
Select,
Textarea,
Checkbox,
Radio,
CommonModal,
Table,
LoadingOverlay,
LoadingProvider,
useLoading,
useToast
} from "@/common/components";
// 또는 디렉터리별 import
import { Button } from "@/common/components/action";
import { Input, Select, Textarea, Checkbox, Radio } from "@/common/components/form";
import { Table } from "@/common/components/display/table";
import { CommonModal, LoadingOverlay, LoadingProvider, useToast } from "@/common/components/system";
```
#### 공통 훅 (`common/hooks/`)
- **`useFilterQuery`**: URL 검색 파라미터 관리
- 뒤로가기/앞으로가기/새로고침 시 상태 유지
```typescript
const { state, update } = useFilterQuery();
// state: { q, page, size, sort, filters }
// update({ q: "검색어" }): URL 업데이트
```
- **`useAppError`**: 에러 처리 훅
```typescript
const { handle } = useAppError();
try {
await someApiCall();
} catch (error) {
handle(error); // 자동으로 Toast 표시
}
```
#### 공통 Query (`common/query/`)
- **`queryClient.ts`**: TanStack Query 클라이언트 설정
- **`keys.ts`**: Query Key 표준화
- **`QueryProvider`**: TanStack Query 제공자 (옵션)
### 1.3 공통소스 활용해서 서비스 구현하는 규칙
1. **Pages (`features/<domain>/pages/`)**: 화면 구성만 담당
- `useFilterQuery`로 URL 상태 관리
- `useAuth`로 인증 상태 확인
- `Table`, `Input` 등 공통 컴포넌트 사용
2. **Components (`features/<domain>/components/`)**: 도메인 특화 컴포넌트
- 공통 컴포넌트를 조합하여 도메인 특화 UI 구성
3. **API 호출**: `http` 인스턴스 사용
- `endpoints.ts`의 상수 활용
- 인터셉터가 자동으로 로딩/에러 처리
4. **상태 관리**:
- 전역 상태: Context API (`AuthContext`)
- 서버 상태: TanStack Query (옵션)
- URL 상태: `useFilterQuery`
### 1.4 공통 소스 수정 규칙
- **공통 소스는 불가피한 경우가 아니면 절대 수정 금지**
- **추가(Extension)는 허용**: 새로운 훅, 유틸 함수 추가 가능
- 수정이 필요한 경우 반드시 팀 리뷰 및 승인 필요
- 공통 소스 수정 시 모든 도메인에 미치는 영향 검토 필수
---
## 2. 개발 환경 및 옵션 리스팅
### 2.1 필수 환경
- **Node.js**: 18.x 이상
- **npm**: 9.x 이상 (또는 yarn, pnpm)
- **브라우저**: Chrome, Firefox, Safari, Edge 최신 버전
### 2.2 주요 의존성
- `react`: ^19.2.0
- `react-dom`: ^19.2.0
- `react-router-dom`: ^6.25.1
- `axios`: ^1.7.9
- `typescript`: ~5.9.3
- `vite`: ^7.2.4
### 2.3 옵션 기능
- **TanStack Query**: 서버 상태 관리 (옵션)
- 활성화: `.env`에 `VITE_USE_REACT_QUERY=true`
- 비활성화: `VITE_USE_REACT_QUERY=false` 또는 미설정
### 2.4 개발 도구
- **ESLint**: 코드 품질 검사
- **TypeScript**: 타입 안정성
- **Vite**: 빠른 개발 서버 및 빌드
---
## 3. 설치 및 동작 방법
### 3.1 사전 준비
1. **Node.js 설치 확인**
```bash
node -v
npm -v
```
2. **환경 변수 파일 생성**
- `.env.development`: 개발 환경
- `.env.production`: 운영 환경
- 자세한 내용은 `ENV_SETUP.md` 참조
### 3.2 프로젝트 설정
1. **의존성 설치**
```bash
cd frontend
npm install
```
또는
```bash
npm ci
```
2. **환경 변수 설정**
- `.env.development` 파일 생성 및 설정
```env
VITE_API_BASE_URL=http://localhost:8080
VITE_API_PREFIX=/api
VITE_USE_REACT_QUERY=false
```
### 3.3 실행
1. **개발 서버 실행**
```bash
npm run dev
```
- 기본 주소: `http://localhost:5173`
2. **빌드**
```bash
npm run build
```
- 빌드 결과: `dist/` 디렉터리
3. **프리뷰 (빌드 결과 확인)**
```bash
npm run preview
```
### 3.4 확인
- 개발 서버 실행 후 브라우저에서 `http://localhost:5173` 접속
- 콘솔에서 에러 없이 로드되는지 확인
---
## 4. 아키텍처 & 책임 분리 규칙
### 4.1 계층별 책임 명확화
#### Pages (`features/<domain>/pages/`)
- **책임**: 화면 구성 및 조합
- **역할**:
- 라우팅 대상 컴포넌트
- 공통 컴포넌트 조합
- URL 상태 관리 (`useFilterQuery`)
- 인증 상태 확인 (`useAuth`)
- **금지**:
- 직접 API 호출 (Service 계층 사용 권장)
- 복잡한 비즈니스 로직 포함 ❌
#### Components (`features/<domain>/components/`)
- **책임**: 도메인 특화 UI 컴포넌트
- **역할**:
- 공통 컴포넌트 조합
- 도메인 특화 로직 (프레젠테이션 로직만)
- **금지**:
- 직접 API 호출 ❌
- 전역 상태 직접 조작 ❌
#### Common (`common/`)
- **책임**: 공통 기능 제공
- **구성**:
- `api/`: API 통신 (http, endpoints, interceptors)
- `auth/`: 인증 관리 (Context, Provider, Service)
- `components/`: 공통 UI 컴포넌트
- `hooks/`: 공통 훅
- `query/`: TanStack Query 설정 (옵션)
- `utils/`: 유틸리티 함수
- `types/`: 공통 타입
### 4.2 비즈니스 로직 위치 규칙
| 위치 | 비즈니스 로직 허용 여부 |
|------|----------------------|
| Pages | ❌ 금지 (프레젠테이션 로직만) |
| Components | ❌ 금지 (프레젠테이션 로직만) |
| Common/Utils | ❌ 금지 (순수 함수만) |
| Service (선택) | ⭕ 허용 (API 호출 및 데이터 변환) |
**참고**: 프론트엔드는 주로 프레젠테이션 로직만 포함하며, 복잡한 비즈니스 로직은 백엔드에서 처리.
**예시**:
```typescript
// ❌ 잘못된 예: Page에 복잡한 로직
function UserListPage() {
const [users, setUsers] = useState([]);
useEffect(() => {
// 복잡한 필터링/정렬 로직
const filtered = users.filter(...).sort(...);
setUsers(filtered);
}, []);
}
// ⭕ 올바른 예: 백엔드에서 필터링/정렬 처리
function UserListPage() {
const { state, update } = useFilterQuery();
const { data } = useQuery({
queryKey: ["users", state],
queryFn: () => fetchUsers(state), // 백엔드에 필터링/정렬 파라미터 전달
});
}
```
### 4.3 공통 모듈과 도메인 모듈의 경계 규칙
- **공통 모듈 (`common/`)**: 모든 도메인에서 공통으로 사용하는 기능
- 도메인 특화 로직 포함 금지
- 재사용 가능한 순수 함수/컴포넌트만
- **도메인 모듈 (`features/<domain>/`)**: 특정 도메인 전용 기능
- 다른 도메인에서 직접 참조 금지
- 도메인 간 공통 기능은 `common/`으로 이동
### 4.4 "이 로직은 어디에 두어야 하는가" 판단 기준
1. **URL 상태 관리**: `useFilterQuery` (Pages에서 사용)
2. **인증 상태**: `useAuth` (Pages/Components에서 사용)
3. **API 호출**: `http` 인스턴스 또는 TanStack Query
4. **전역 로딩**: 자동 처리 (interceptors)
5. **에러 처리**: `useAppError` 또는 TanStack Query 에러 핸들링
6. **공통 UI**: `common/components/`의 컴포넌트 사용
---
## 5. 네이밍 & 패키지 규칙
### 5.1 디렉터리 구조 규칙
- **`app/`**: 앱 레벨 설정 (라우팅, 레이아웃)
- **`common/`**: 공통 기능
- **`features/`**: 도메인별 기능
- `features/<domain>/pages/`: 페이지 컴포넌트
- `features/<domain>/components/`: 도메인 특화 컴포넌트
### 5.2 파일 / 컴포넌트 / 함수 네이밍 기준
#### 파일
- **컴포넌트**: `PascalCase.tsx` (예: `UserList.tsx`, `LoginForm.tsx`)
- **훅**: `camelCase.ts` + `use` 접두사 (예: `useAuth.ts`, `useFilterQuery.ts`)
- **유틸**: `camelCase.ts` (예: `errorMapper.ts`)
- **타입**: `camelCase.ts` (예: `api.ts`, `error.ts`)
#### 컴포넌트
- **PascalCase** 사용
- 명확한 의미 전달 (예: `UserList`, `DocumentTable`, `LoginForm`)
#### 함수/변수
- **camelCase** 사용
- **boolean**: `is`, `has`, `can` 접두사 (예: `isLoading`, `hasPermission`)
- **이벤트 핸들러**: `handle` 접두사 (예: `handleSubmit`, `handleClick`)
#### 상수
- **UPPER_SNAKE_CASE** 사용 (예: `API_BASE_URL`, `MAX_FILE_SIZE`)
### 5.3 약어 사용 허용 / 금지 리스트
#### 허용 약어
- `api`, `auth`, `config`, `util`, `props`, `ctx`, `ref`
#### 금지 약어
- `usr` (→ `user`), `svc` (→ `service`), `cmp` (→ `component`)
- `mgr` (→ `manager`), `info` (→ `information`), `num` (→ `number`)
### 5.4 API 필드 ↔ 컴포넌트 Props ↔ 상태 변수 매핑 규칙
#### API 응답 → 컴포넌트
- **카멜 케이스 유지** (JSON 기본)
- API: `{"userId": 1, "email": "user@example.com"}`
- TypeScript: `{ userId: number; email: string }`
#### 컴포넌트 Props
- **카멜 케이스** 사용
```typescript
type UserCardProps = {
userId: number;
email: string;
displayName: string;
};
```
#### 상태 변수
- **카멜 케이스** 사용
```typescript
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(false);
```
---
## 6. 예외 처리 & 에러 응답 규칙
### 6.1 공통 Exception 구조
#### AppError 타입
```typescript
type AppError = {
kind: "network" | "timeout" | "unauthorized" | "forbidden" | "notfound" | "validation" | "server" | "unknown";
status?: number;
code?: string;
message: string;
details?: unknown;
};
```
#### errorMapper
- `mapAxiosError()`: `AxiosError` → `AppError` 변환
- 자동으로 에러 종류 분류 및 사용자 친화적 메시지 제공
### 6.2 비즈니스 예외 vs 시스템 예외 구분
| 예외 타입 | 발생 위치 | 처리 방법 | 사용자 메시지 |
|----------|---------|----------|-------------|
| **비즈니스 예외** | API 응답 (4xx) | `useAppError.handle()` | 서버 메시지 또는 기본 메시지 |
| **시스템 예외** | 네트워크/타임아웃 | `useAppError.handle()` | 일반화된 메시지 |
**예시**:
```typescript
// useAppError로 자동 처리
const { handle } = useAppError();
try {
await someApiCall();
} catch (error) {
handle(error); // 자동으로 Toast 표시 및 AppError 반환
}
```
### 6.3 API 응답 포맷 통일 규칙
모든 API 응답은 다음 형식:
```typescript
type ApiResponse<T> = {
success: boolean;
data: T | null;
error: {
code: string;
message: string;
} | null;
};
```
**사용 예시**:
```typescript
const response = await http.get<ApiResponse<User>>(API.AUTH.ME);
if (response.data.success) {
const user = response.data.data; // User 타입
} else {
const error = response.data.error; // { code, message }
}
```
### 6.4 로그 기록 기준과 사용자 노출 메시지 분리 규칙
- **콘솔 로그**: 개발 환경에서만 (`import.meta.env.DEV`)
- **사용자 메시지**: Toast 또는 에러 페이지에 표시
- **민감 정보**: 절대 로그/메시지에 포함하지 않음
**예시**:
```typescript
// ❌ 잘못된 예: 민감 정보 콘솔 출력
console.log("User login:", { email, password });
// ⭕ 올바른 예: 개발 환경에서만 제한적 로깅
if (import.meta.env.DEV && import.meta.env.VITE_HTTP_LOGGING === "true") {
console.log("User login:", { email: maskEmail(email) });
}
```
---
## 7. 로그 & 감사(Audit) 규칙
### 7.1 로그 레벨 사용 기준
프론트엔드는 주로 `console.log`, `console.warn`, `console.error` 사용:
| 레벨 | 사용 시기 | 예시 |
|------|---------|------|
| **console.log** | 개발 중 디버깅 | 파라미터 값, 상태 변화 |
| **console.warn** | 예상 가능한 문제 | API 응답 경고, 사용자 입력 경고 |
| **console.error** | 예상치 못한 오류 | 예외 발생, 시스템 오류 |
**주의**: 운영 빌드에서는 `console.log` 제거 (Vite가 자동 처리)
### 7.2 개인정보 및 민감정보 마스킹 규칙
**마스킹 대상**:
- 비밀번호, 토큰
- 이메일 (일부 마스킹 가능)
- 전화번호 (일부 마스킹 가능)
**마스킹 방법**:
- 직접 구현 또는 라이브러리 사용
- 로그/콘솔 출력 전 마스킹 필수
**예시**:
```typescript
// ❌ 잘못된 예: 민감 정보 콘솔 출력
console.log("User data:", user);
// ⭕ 올바른 예: 마스킹 후 출력
console.log("User data:", {
...user,
email: maskEmail(user.email),
});
```
### 7.3 공통 로깅 유틸 사용 규칙
- **Axios Interceptor**: 자동 요청/응답 로깅 (개발 환경)
- **수동 로깅**: 필요한 경우에만 `console.log` 사용
- **운영 빌드**: Vite가 자동으로 `console.log` 제거
### 7.4 요청 추적용 식별자(Request ID 등) 사용 여부
현재는 기본 구조만 제공. 필요 시 다음 추가 가능:
- Axios Interceptor에서 Request ID 생성/추가
- 응답 헤더에서 Request ID 추출
- 로그에 Request ID 포함
---
## 8. 설정 관리 규칙
### 8.1 환경 변수 파일 분리 전략
- **`.env.development`**: 개발 환경
- **`.env.production`**: 운영 환경
- **`.env.local`**: 로컬 오버라이드 (Git 제외)
**환경 변수 접두사**: `VITE_` (Vite 요구사항)
### 8.2 환경별 설정 원칙
- **공통 설정**: 코드에 하드코딩 (예: 기본 타임아웃)
- **환경별 설정**: 환경 변수 사용
- **민감 정보**: 환경 변수 사용 (절대 코드에 포함 금지)
### 8.3 옵션 처리 기준 (enable / disable 방식)
옵션 기능은 환경 변수로 제어:
```env
# TanStack Query 사용 여부
VITE_USE_REACT_QUERY=true # 또는 false
```
**코드에서 사용**:
```typescript
const useReactQuery = import.meta.env.VITE_USE_REACT_QUERY === "true";
```
### 8.4 TanStack Query 사용 여부를 옵션으로 제어하는 규칙
- **활성화**: `VITE_USE_REACT_QUERY=true`
- `QueryProvider`가 `QueryClientProvider`로 래핑
- TanStack Query 훅 사용 가능
- **비활성화**: `VITE_USE_REACT_QUERY=false` 또는 미설정
- `QueryProvider`가 children만 반환
- 직접 `http` 인스턴스로 API 호출
**예시**:
```typescript
// TanStack Query 사용 (옵션 활성화 시)
const { data } = useQuery({
queryKey: ["users"],
queryFn: () => fetchUsers(),
});
// 직접 API 호출 (옵션 비활성화 시)
const [users, setUsers] = useState([]);
useEffect(() => {
http.get(API.USER.LIST).then(res => setUsers(res.data.data));
}, []);
```
---
## 9. 보안 관련 추가 규칙 (시큐어 코딩 보강)
### 9.1 인증 / 인가 흐름 준수 규칙
#### 인증 (Authentication)
- **세션 기반 인증** 사용
- `AuthProvider`에서 `/api/auth/me` 호출로 인증 상태 확인
- 로그인 성공 시 세션 쿠키 자동 설정 (백엔드)
#### 인가 (Authorization)
- **ProtectedRoute**: 인증 필요 라우트 보호
- 백엔드에서 권한 검증 (403 Forbidden)
**예시**:
```typescript
// ProtectedRoute 사용
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
```
### 9.2 세션 / 토큰 사용 시 주의 사항
- **세션 쿠키**: `withCredentials: true` 설정 (Axios)
- **토큰 저장 금지**: `localStorage`/`sessionStorage`에 토큰 저장 절대 금지
- **자동 갱신**: 백엔드에서 세션 관리, 프론트엔드는 `/api/auth/me`로 상태 확인
### 9.3 파일 업로드 / 다운로드 처리 규칙
#### 업로드
1. **파일 크기 제한**: 클라이언트에서 사전 검증
2. **파일 타입 검증**: 확장자 및 MIME 타입 확인
3. **FormData 사용**: `multipart/form-data` 전송
#### 다운로드
1. **안전한 다운로드**: 백엔드에서 권한 검증 후 다운로드 URL 제공
2. **Content-Disposition**: 안전한 파일명 설정
### 9.4 외부 API 연동 시 보안 체크리스트
- [ ] CORS 설정 확인 (백엔드)
- [ ] HTTPS만 사용 (HTTP 금지)
- [ ] 타임아웃 설정 (무한 대기 방지)
- [ ] 입력값 검증 (외부 API로 전송 전)
- [ ] 응답 검증 (예상 형식 확인)
### 9.5 XSS 방지 규칙
- **`dangerouslySetInnerHTML` 금지**: HTML 직접 삽입 금지
- **필요 시 DOMPurify**: HTML 삽입이 불가피한 경우 DOMPurify로 sanitize
- **URL 인코딩**: `encodeURIComponent` 사용
- **`eval` / `new Function` 금지**: 동적 코드 실행 금지
**예시**:
```typescript
// ❌ 잘못된 예: XSS 위험
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ⭕ 올바른 예: DOMPurify 사용
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
// ⭕ 더 안전한 예: 텍스트로 렌더링
<div>{userInput}</div>
```
---
## 10. 테스트 & 검증 규칙
### 10.1 단위 테스트 작성 기준 (필수 / 선택 구분)
#### 선택 테스트
- **컴포넌트 테스트**: React Testing Library 사용 (선택)
- **훅 테스트**: 커스텀 훅 테스트 (선택)
- **유틸 테스트**: 순수 함수 테스트 (선택)
**예시**:
```typescript
import { render, screen } from "@testing-library/react";
import { UserList } from "./UserList";
test("renders user list", () => {
render(<UserList users={mockUsers} />);
expect(screen.getByText("User 1")).toBeInTheDocument();
});
```
### 10.2 테스트용 데이터 작성 규칙
- **Mock 데이터**: `__mocks__/` 디렉터리에 정의
- **격리**: 각 테스트는 독립적으로 실행 가능해야 함
### 10.3 로컬 테스트 → 통합 테스트 흐름
1. **로컬 컴포넌트 테스트**: 단위 테스트
2. **로컬 통합 테스트**: 여러 컴포넌트 조합 테스트
3. **E2E 테스트**: 실제 브라우저 테스트 (선택, Playwright/Cypress)
### 10.4 테스트 미수행 시 병합 제한 여부
- 현재는 권고 사항 (필수 아님)
- 향후 CI/CD 파이프라인에서 테스트 실패 시 병합 차단 가능
---
## 11. 금지 패턴 / 안티 패턴 목록
### 11.1 공통 Util에 비즈니스 로직 포함 ❌
```typescript
// ❌ 잘못된 예
export function isAdminUser(email: string): boolean {
return email.includes("@admin.com");
}
// ⭕ 올바른 예: 도메인 로직은 해당 도메인에 위치
// (또는 백엔드에서 처리)
```
### 11.2 직접 API 호출 (인터셉터 우회) ❌
```typescript
// ❌ 잘못된 예: axios 직접 import
import axios from "axios";
const response = await axios.get("http://localhost:8080/api/users");
// ⭕ 올바른 예: 공통 http 인스턴스 사용
import { http } from "@/common/api/http";
import { API } from "@/common/api/endpoints";
const response = await http.get(API.USER.LIST);
```
### 11.3 옵션 무시 후 하드코딩 ❌
```typescript
// ❌ 잘못된 예: 옵션 무시하고 하드코딩
import { useQuery } from "@tanstack/react-query";
const { data } = useQuery({ ... });
// ⭕ 올바른 예: 옵션 확인
const useReactQuery = import.meta.env.VITE_USE_REACT_QUERY === "true";
if (useReactQuery) {
const { data } = useQuery({ ... });
} else {
const [data, setData] = useState(null);
useEffect(() => {
http.get(API.USER.LIST).then(res => setData(res.data.data));
}, []);
}
```
### 11.4 공통 코드 복사 후 개별 컴포넌트에 포함 ❌
```typescript
// ❌ 잘못된 예: 공통 로직을 각 컴포넌트에 복사
function UserList() {
const [loading, setLoading] = useState(false);
// 로딩 처리 로직 복사
}
function DocumentList() {
const [loading, setLoading] = useState(false);
// 동일한 로딩 처리 로직 복사
}
// ⭕ 올바른 예: 공통 훅/컴포넌트 사용
function UserList() {
// 인터셉터가 자동으로 로딩 처리
const { data } = useQuery({ ... });
}
```
### 11.5 localStorage/sessionStorage에 인증 정보 저장 ❌
```typescript
// ❌ 잘못된 예: 토큰을 localStorage에 저장
localStorage.setItem("token", token);
// ⭕ 올바른 예: 세션 쿠키 사용 (백엔드에서 관리)
// 프론트엔드는 withCredentials만 설정
```
### 11.6 dangerouslySetInnerHTML 사용 ❌
```typescript
// ❌ 잘못된 예: XSS 위험
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ⭕ 올바른 예: 텍스트로 렌더링 또는 DOMPurify
<div>{userInput}</div>
// 또는
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
```
### 11.7 eval / new Function 사용 ❌
```typescript
// ❌ 잘못된 예: 동적 코드 실행
eval(userInput);
new Function(userInput)();
// ⭕ 올바른 예: 안전한 방법 사용
// (동적 코드 실행이 필요한 경우는 거의 없음)
```
### 11.8 URL 상태 관리 무시 (useFilterQuery 미사용) ❌
```typescript
// ❌ 잘못된 예: useState로만 상태 관리
const [q, setQ] = useState("");
const [page, setPage] = useState(1);
// ⭕ 올바른 예: useFilterQuery 사용 (뒤로가기/새로고침 대응)
const { state, update } = useFilterQuery();
// state: { q, page, size, sort, filters }
// update({ q: "검색어" }): URL 업데이트
```
### 11.9 전역 로딩 중복 처리 ❌
```typescript
// ❌ 잘못된 예: 수동 로딩 처리
const [loading, setLoading] = useState(false);
const fetchData = async () => {
setLoading(true);
try {
await http.get(API.USER.LIST);
} finally {
setLoading(false);
}
};
// ⭕ 올바른 예: 인터셉터가 자동 처리
const fetchData = async () => {
await http.get(API.USER.LIST); // 인터셉터가 자동으로 로딩 처리
};
```
---
## 12. 성능 최적화 가이드
### 12.1 번들 크기 최적화
#### 코드 스플리팅
- **라우트 기반 스플리팅**: React.lazy 사용
```typescript
const UserListPage = lazy(() => import("@/features/user/pages/UserListPage"));
```
#### 불필요한 의존성 제거
- **트리 쉐이킹**: 사용하지 않는 import 제거
- **대안 라이브러리**: 가벼운 라이브러리 선택 (예: `date-fns` vs `moment`)
### 12.2 렌더링 최적화
#### React 최적화
- **useMemo / useCallback**: 불필요한 재계산 방지
- **React.memo**: 컴포넌트 메모이제이션
- **가상화**: 대량 리스트는 `react-window` 사용
**예시**:
```typescript
// ❌ 잘못된 예: 매번 새로운 객체 생성
function UserCard({ user }) {
const style = { color: "blue" }; // 매 렌더링마다 새 객체
return <div style={style}>{user.name}</div>;
}
// ⭕ 올바른 예: useMemo 사용
function UserCard({ user }) {
const style = useMemo(() => ({ color: "blue" }), []);
return <div style={style}>{user.name}</div>;
}
```
### 12.3 API 호출 최적화
#### 중복 호출 방지
- **TanStack Query**: 자동 캐싱 및 중복 제거
- **Debounce**: 검색 입력 시 debounce 적용
#### 데이터 페이징
- **서버 사이드 페이징**: 대량 데이터는 페이징 필수
- **무한 스크롤**: 필요 시 `react-infinite-scroll` 활용
### 12.4 이미지 최적화
- **이미지 포맷**: WebP 사용 권장
- **이미지 크기**: 적절한 해상도로 리사이즈
- **Lazy Loading**: `loading="lazy"` 속성 사용
---
## 13. 의존성 관리
### 13.1 버전 관리 원칙
- **명시적 버전 지정**: `package.json`에 버전 명시
- **보안 패치 우선**: 취약점 발견 시 즉시 업데이트
- **마이너 버전 업데이트**: 정기적으로 검토 및 업데이트
### 13.2 보안 취약점 점검
**정기 점검**:
```bash
npm audit
npm audit fix
```
**수동 점검**:
- [npm audit](https://docs.npmjs.com/cli/v8/commands/npm-audit) 활용
- GitHub Dependabot 설정 권장
### 13.3 업데이트 프로세스
1. **의존성 업데이트**: `package.json` 수정
2. **로컬 테스트**: 업데이트 후 빌드 및 테스트
3. **통합 테스트**: 개발 환경에서 검증
4. **운영 배포**: 검증 완료 후 배포
### 13.4 lock 파일 관리
- **package-lock.json**: Git에 커밋 필수
- **npm ci 사용**: CI/CD에서는 `npm ci` 사용 (lock 파일 기반 설치)
---
## 14. 트러블슈팅 가이드
### 14.1 자주 발생하는 문제
#### 문제 1: CORS 오류
**증상**: `Access to XMLHttpRequest has been blocked by CORS policy`
**해결**:
1. 백엔드 CORS 설정 확인 (`WebMvcConfig.addCorsMappings`)
2. `withCredentials: true` 설정 확인 (프론트엔드)
3. 백엔드에서 `allowCredentials: true` 확인
4. 프록시 설정 확인 (개발 환경)
#### 문제 2: 세션이 유지되지 않음
**증상**: 로그인 후 요청 시 401 Unauthorized
**해결**:
1. `withCredentials: true` 설정 확인 (Axios)
2. CORS 설정에서 `allowCredentials: true` 확인 (백엔드)
3. 쿠키 도메인/경로 설정 확인
4. 브라우저 쿠키 차단 여부 확인
#### 문제 3: 환경 변수가 적용되지 않음
**증상**: `import.meta.env.VITE_*`가 `undefined`
**해결**:
1. 환경 변수 파일 이름 확인 (`.env.development`, `.env.production`)
2. `VITE_` 접두사 확인
3. 개발 서버 재시작
4. 빌드 시 환경 변수 파일 위치 확인
#### 문제 4: 빌드 실패 (TypeScript 오류)
**증상**: `npm run build` 실패
**해결**:
1. `npm run lint`로 오류 확인
2. TypeScript 타입 오류 수정
3. `tsconfig.json` 설정 확인
### 14.2 디버깅 팁
#### React DevTools
- **컴포넌트 트리**: 컴포넌트 상태 확인
- **Profiler**: 성능 분석
#### 브라우저 DevTools
- **Network 탭**: API 호출 확인
- **Console 탭**: 에러 메시지 확인
- **Application 탭**: 쿠키/로컬 스토리지 확인
#### Vite DevTools
- **HMR (Hot Module Replacement)**: 빠른 개발 피드백
- **에러 오버레이**: 빌드 에러 즉시 확인
---
## 참고 문서
- **아키텍처 문서**: `base_arcitectures_md/FRONT_ARCHITECTURE_V1.md`
- **보안 규칙**: `base_arcitectures_md/FRONTEND_SECURE_RULE.md`
- **공통 보안 규칙**: `../SECURE_RULE.md`
- **환경 변수 설정**: `ENV_SETUP.md`
---
**마지막 업데이트**: 2024년