354 lines
8.9 KiB
Markdown
354 lines
8.9 KiB
Markdown
|
|
# 코드 품질 및 일관성 검사 결과
|
||
|
|
|
||
|
|
**검사 일자**: 2025-11-07
|
||
|
|
**검사자**: Claude Code
|
||
|
|
|
||
|
|
## 📊 전체 요약
|
||
|
|
|
||
|
|
**프로젝트**: Next.js 15 + TypeScript + next-intl (다국어 지원)
|
||
|
|
**언어**: TypeScript/TSX
|
||
|
|
**린트**: ESLint 9 (Next.js config)
|
||
|
|
**타입 체크**: ✅ 통과 (에러 없음)
|
||
|
|
**린트 상태**: ⚠️ 12개 문제 (9 errors, 3 warnings)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔴 Critical Issues (즉시 수정 필요)
|
||
|
|
|
||
|
|
### 1. **src/lib/api/client.ts** - Type 정의 누락 (5 errors)
|
||
|
|
|
||
|
|
**문제**:
|
||
|
|
- `RequestInit`, `Response`, `fetch`, `URL` 등 글로벌 타입이 인식되지 않음
|
||
|
|
- 브라우저/Node.js 환경 타입 정의 누락
|
||
|
|
|
||
|
|
**수정 방법**:
|
||
|
|
```typescript
|
||
|
|
// 파일 상단에 타입 선언 추가
|
||
|
|
/// <reference lib="dom" />
|
||
|
|
|
||
|
|
// 또는 tsconfig.json에서 lib 설정 확인
|
||
|
|
"lib": ["dom", "dom.iterable", "esnext"]
|
||
|
|
```
|
||
|
|
|
||
|
|
**위치**:
|
||
|
|
- src/lib/api/client.ts:50 - `token` 변수 선언 (case block)
|
||
|
|
- src/lib/api/client.ts:70 - `RequestInit` 타입 미정의
|
||
|
|
- src/lib/api/client.ts:78 - `RequestInit` 타입 미정의
|
||
|
|
- src/lib/api/client.ts:88 - `fetch` 미정의
|
||
|
|
- src/lib/api/client.ts:139 - `Response` 타입 미정의
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. **src/middleware.ts** - 미사용 함수/변수 (2 errors)
|
||
|
|
|
||
|
|
**문제 1**: `isProtectedRoute` 함수 정의되었으나 사용되지 않음
|
||
|
|
```typescript
|
||
|
|
// Line 161
|
||
|
|
function isProtectedRoute(pathname: string): boolean {
|
||
|
|
return AUTH_CONFIG.protectedRoutes.some(route =>
|
||
|
|
pathname.startsWith(route)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**문제 2**: `URL` 글로벌 타입 인식 안됨
|
||
|
|
```typescript
|
||
|
|
// Line 231, 247
|
||
|
|
new URL(AUTH_CONFIG.redirects.afterLogin, request.url)
|
||
|
|
new URL('/login', request.url)
|
||
|
|
```
|
||
|
|
|
||
|
|
**수정 방법**:
|
||
|
|
- `isProtectedRoute` 함수 앞에 `_` 추가 (unused 규칙 준수) 또는 삭제
|
||
|
|
- tsconfig.json lib 설정 확인
|
||
|
|
|
||
|
|
**위치**:
|
||
|
|
- src/middleware.ts:161 - `isProtectedRoute` 미사용
|
||
|
|
- src/middleware.ts:231 - `URL` 타입 미정의
|
||
|
|
- src/middleware.ts:247 - `URL` 타입 미정의
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. **src/components/auth/LoginPage.tsx** (2 issues)
|
||
|
|
|
||
|
|
**Error**: 미사용 변수 `response`
|
||
|
|
```typescript
|
||
|
|
// Line 43
|
||
|
|
const response = await sanctumClient.login({
|
||
|
|
user_id: userId,
|
||
|
|
user_pwd: password,
|
||
|
|
});
|
||
|
|
// response 변수가 사용되지 않음
|
||
|
|
```
|
||
|
|
|
||
|
|
**Warning**: `any` 타입 사용
|
||
|
|
```typescript
|
||
|
|
// Line 55
|
||
|
|
} catch (err: any) {
|
||
|
|
// any 대신 구체적인 타입 필요
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**수정 방법**:
|
||
|
|
```typescript
|
||
|
|
// Option 1: response 사용하지 않으면 제거
|
||
|
|
await sanctumClient.login({ user_id: userId, user_pwd: password });
|
||
|
|
|
||
|
|
// Option 2: 타입 개선
|
||
|
|
} catch (err: unknown) {
|
||
|
|
const error = err as { status?: number; message?: string };
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**위치**:
|
||
|
|
- src/components/auth/LoginPage.tsx:43 - `response` 미사용
|
||
|
|
- src/components/auth/LoginPage.tsx:55 - `any` 타입 사용
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🟡 Warnings (개선 권장)
|
||
|
|
|
||
|
|
### 4. **src/lib/api/auth/token-storage.ts** - any 타입 사용 (2 warnings)
|
||
|
|
|
||
|
|
**위치**: Line 30, 38
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Line 30, 38
|
||
|
|
} catch (e: any) {
|
||
|
|
// any 대신 unknown 사용 권장
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**개선 방법**:
|
||
|
|
```typescript
|
||
|
|
} catch (e: unknown) {
|
||
|
|
console.error('Token parse error:', e);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**위치**:
|
||
|
|
- src/lib/api/auth/token-storage.ts:30 - `any` 타입 사용
|
||
|
|
- src/lib/api/auth/token-storage.ts:38 - `any` 타입 사용
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ 긍정적인 부분
|
||
|
|
|
||
|
|
1. **TypeScript 타입 체크 통과** - 타입 시스템이 올바르게 작동 중
|
||
|
|
2. **명확한 디렉토리 구조**:
|
||
|
|
```
|
||
|
|
src/
|
||
|
|
├── app/[locale]/ # Next.js 15 App Router
|
||
|
|
├── components/ # 재사용 컴포넌트
|
||
|
|
│ ├── ui/ # UI 컴포넌트 (shadcn/ui)
|
||
|
|
│ └── auth/ # 인증 관련
|
||
|
|
├── contexts/ # React Context
|
||
|
|
├── lib/ # 유틸리티/API
|
||
|
|
│ ├── api/
|
||
|
|
│ │ └── auth/ # 인증 API 로직
|
||
|
|
│ └── validations/ # Zod 스키마
|
||
|
|
└── i18n/ # 다국어 설정
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Zod 검증 사용** - 런타임 타입 안전성 확보
|
||
|
|
4. **일관된 명명 규칙**:
|
||
|
|
- 컴포넌트: PascalCase (`LoginPage.tsx`)
|
||
|
|
- 유틸: camelCase (`auth-config.ts`)
|
||
|
|
- 상수: UPPER_SNAKE_CASE (`AUTH_CONFIG`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 스타일 일관성
|
||
|
|
|
||
|
|
### ✅ 긍정적 패턴
|
||
|
|
- **Import 순서**: 외부 라이브러리 → 내부 모듈 → 컴포넌트 순서 일관됨
|
||
|
|
- **"use client" 지시자**: 클라이언트 컴포넌트에 올바르게 적용
|
||
|
|
- **경로 별칭**: `@/*` 패턴 일관되게 사용
|
||
|
|
- **함수형 컴포넌트**: 모든 컴포넌트가 함수형으로 작성됨
|
||
|
|
|
||
|
|
### ⚠️ 개선 필요
|
||
|
|
1. **하드코딩된 한글 텍스트**:
|
||
|
|
```tsx
|
||
|
|
// SignupPage.tsx:148
|
||
|
|
<p className="text-xs text-muted-foreground">회원가입</p>
|
||
|
|
|
||
|
|
// 다국어 지원 누락 (LoginPage는 useTranslations 사용)
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **인라인 스타일 사용**:
|
||
|
|
```tsx
|
||
|
|
// LoginPage.tsx:79
|
||
|
|
<div style={{ backgroundColor: '#3B82F6' }}>
|
||
|
|
|
||
|
|
// Tailwind 클래스 사용 권장: bg-blue-500
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **주석 처리된 코드**:
|
||
|
|
```tsx
|
||
|
|
// SignupPage.tsx:448-521
|
||
|
|
// 대량의 주석 처리된 플랜 선택 UI (73줄)
|
||
|
|
|
||
|
|
// 제거 또는 별도 파일로 분리 권장
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔧 추천 개선 사항
|
||
|
|
|
||
|
|
### 우선순위 1 (High) - 즉시 수정
|
||
|
|
1. ✅ **tsconfig.json** lib 설정 확인 (DOM 타입 포함)
|
||
|
|
2. ✅ **any 타입 제거** → `unknown` 또는 구체적 타입으로 변경
|
||
|
|
3. ✅ **미사용 변수 제거** (response, isProtectedRoute)
|
||
|
|
|
||
|
|
### 우선순위 2 (Medium) - 단기 개선
|
||
|
|
4. **하드코딩 텍스트 다국어화**:
|
||
|
|
```typescript
|
||
|
|
// messages/ko.json에 추가
|
||
|
|
{
|
||
|
|
"signup": {
|
||
|
|
"title": "회원가입",
|
||
|
|
"companyInfo": "회사 정보를 입력해주세요"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
5. **인라인 스타일 → Tailwind 클래스**:
|
||
|
|
```tsx
|
||
|
|
// Before
|
||
|
|
<div style={{ backgroundColor: '#3B82F6' }}>
|
||
|
|
|
||
|
|
// After
|
||
|
|
<div className="bg-blue-500">
|
||
|
|
```
|
||
|
|
|
||
|
|
6. **주석 처리된 코드 정리**:
|
||
|
|
- 필요 시 별도 브랜치로 보존
|
||
|
|
- 불필요하면 삭제
|
||
|
|
|
||
|
|
### 우선순위 3 (Low) - 장기 개선
|
||
|
|
7. **에러 타입 정의**:
|
||
|
|
```typescript
|
||
|
|
// lib/api/types.ts
|
||
|
|
export interface ApiError {
|
||
|
|
status: number;
|
||
|
|
message: string;
|
||
|
|
errors?: Record<string, string[]>;
|
||
|
|
code?: string;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
8. **ESLint 규칙 커스터마이징**:
|
||
|
|
```json
|
||
|
|
// .eslintrc.json 생성
|
||
|
|
{
|
||
|
|
"extends": "next/core-web-vitals",
|
||
|
|
"rules": {
|
||
|
|
"@typescript-eslint/no-unused-vars": ["error", {
|
||
|
|
"argsIgnorePattern": "^_"
|
||
|
|
}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📈 메트릭스
|
||
|
|
|
||
|
|
| 항목 | 상태 | 점수 |
|
||
|
|
|------|------|------|
|
||
|
|
| TypeScript 타입 체크 | ✅ 통과 | 100% |
|
||
|
|
| ESLint 오류 | ⚠️ 9개 | 65% |
|
||
|
|
| 코드 구조 | ✅ 우수 | 90% |
|
||
|
|
| 명명 규칙 | ✅ 일관됨 | 95% |
|
||
|
|
| 다국어 적용 | ⚠️ 부분적 | 75% |
|
||
|
|
| 스타일 일관성 | ✅ 양호 | 85% |
|
||
|
|
|
||
|
|
**전체 코드 품질**: **82/100** (양호)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 빠른 수정 가이드
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 1. tsconfig.json 확인 (이미 올바르게 설정됨)
|
||
|
|
cat tsconfig.json | grep -A5 "lib"
|
||
|
|
|
||
|
|
# 2. ESLint 오류 확인
|
||
|
|
npm run lint
|
||
|
|
|
||
|
|
# 3. 자동 수정 가능한 항목 수정
|
||
|
|
npm run lint -- --fix
|
||
|
|
|
||
|
|
# 4. TypeScript 타입 체크
|
||
|
|
npx tsc --noEmit
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 상세 에러 목록
|
||
|
|
|
||
|
|
### ESLint Errors (9개)
|
||
|
|
|
||
|
|
1. **src/components/auth/LoginPage.tsx:43:13**
|
||
|
|
- `response` is assigned a value but never used
|
||
|
|
- Rule: `@typescript-eslint/no-unused-vars`
|
||
|
|
|
||
|
|
2. **src/lib/api/client.ts:50:9**
|
||
|
|
- Unexpected lexical declaration in case block
|
||
|
|
- Rule: `no-case-declarations`
|
||
|
|
|
||
|
|
3. **src/lib/api/client.ts:70:15**
|
||
|
|
- `RequestInit` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
4. **src/lib/api/client.ts:78:19**
|
||
|
|
- `RequestInit` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
5. **src/lib/api/client.ts:88:28**
|
||
|
|
- `fetch` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
6. **src/lib/api/client.ts:139:39**
|
||
|
|
- `Response` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
7. **src/middleware.ts:161:10**
|
||
|
|
- `isProtectedRoute` is defined but never used
|
||
|
|
- Rule: `@typescript-eslint/no-unused-vars`
|
||
|
|
|
||
|
|
8. **src/middleware.ts:231:40**
|
||
|
|
- `URL` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
9. **src/middleware.ts:247:21**
|
||
|
|
- `URL` is not defined
|
||
|
|
- Rule: `no-undef`
|
||
|
|
|
||
|
|
### ESLint Warnings (3개)
|
||
|
|
|
||
|
|
1. **src/components/auth/LoginPage.tsx:55:19**
|
||
|
|
- Unexpected any. Specify a different type
|
||
|
|
- Rule: `@typescript-eslint/no-explicit-any`
|
||
|
|
|
||
|
|
2. **src/lib/api/auth/token-storage.ts:30:17**
|
||
|
|
- Unexpected any. Specify a different type
|
||
|
|
- Rule: `@typescript-eslint/no-explicit-any`
|
||
|
|
|
||
|
|
3. **src/lib/api/auth/token-storage.ts:38:14**
|
||
|
|
- Unexpected any. Specify a different type
|
||
|
|
- Rule: `@typescript-eslint/no-explicit-any`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 💡 결론
|
||
|
|
|
||
|
|
프로젝트는 전반적으로 **양호한 품질**을 유지하고 있으나, 위 9개 ESLint 오류를 수정하면 더욱 견고한 코드베이스가 될 것입니다.
|
||
|
|
|
||
|
|
주요 개선 포인트:
|
||
|
|
1. 타입 정의 완성도 향상 (no-undef 에러 해결)
|
||
|
|
2. any 타입 제거로 타입 안전성 강화
|
||
|
|
3. 미사용 변수/함수 정리로 코드 가독성 향상
|
||
|
|
4. 다국어 지원 일관성 개선
|
||
|
|
5. 스타일 일관성 유지 (인라인 스타일 제거)
|