# API Route 타입 안전성 가이드

## 📋 개요

Next.js API Route에서 백엔드 API 응답 데이터를 프론트엔드로 전달할 때, TypeScript 타입 정의를 통해 데이터 누락을 방지하는 방법

---

## 🎯 문제 사례

### 발생한 이슈
로그인 API를 테스트할 때, API 테스트 도구에서는 `roles` 데이터가 정상적으로 나오지만, 프론트엔드에서는 빈 배열로 나오는 현상 발생

### 원인 분석
```typescript
// ❌ 타입 정의 없이 데이터 전달 (문제 코드)
const responseData = {
  message: data.message,
  user: data.user,
  tenant: data.tenant,
  menus: data.menus,
  // roles: data.roles, ← 누락됨!
  token_type: data.token_type,
  expires_in: data.expires_in,
  expires_at: data.expires_at,
};
```

**문제점:**
- 백엔드에서 `roles` 데이터를 반환했지만
- Next.js API Route에서 프론트로 전달할 때 `roles` 필드를 포함하지 않음
- 타입 정의가 없어서 컴파일 타임에 감지 불가

---

## ✅ 해결 방법

### 1. 백엔드 응답 타입 정의

```typescript
/**
 * 백엔드 API 로그인 응답 타입
 */
interface BackendLoginResponse {
  message: string;
  access_token: string;
  refresh_token: string;
  token_type: string;
  expires_in: number;
  expires_at: string;
  user: {
    id: number;
    user_id: string;
    name: string;
    email: string;
    phone: string;
  };
  tenant: {
    id: number;
    company_name: string;
    business_num: string;
    tenant_st_code: string;
    other_tenants: any[];
  };
  menus: Array<{
    id: number;
    parent_id: number | null;
    name: string;
    url: string;
    icon: string;
    sort_order: number;
    is_external: number;
    external_url: string | null;
  }>;
  roles: Array<{
    id: number;
    name: string;
    description: string;
  }>;
}
```

### 2. 프론트엔드 응답 타입 정의

```typescript
/**
 * 프론트엔드로 전달할 응답 타입 (토큰 제외)
 */
interface FrontendLoginResponse {
  message: string;
  user: BackendLoginResponse['user'];
  tenant: BackendLoginResponse['tenant'];
  menus: BackendLoginResponse['menus'];
  roles: BackendLoginResponse['roles']; // ✅ 명시적으로 포함
  token_type: string;
  expires_in: number;
  expires_at: string;
}
```

### 3. 타입 적용

```typescript
export async function POST(request: NextRequest) {
  try {
    // ... 백엔드 API 호출

    // ✅ 타입 지정
    const data: BackendLoginResponse = await backendResponse.json();

    // ✅ 타입 지정 + 모든 필드 포함
    const responseData: FrontendLoginResponse = {
      message: data.message,
      user: data.user,
      tenant: data.tenant,
      menus: data.menus,
      roles: data.roles, // ✅ 누락 방지
      token_type: data.token_type,
      expires_in: data.expires_in,
      expires_at: data.expires_at,
    };

    return NextResponse.json(responseData, { status: 200 });
  } catch (error) {
    // ... 에러 처리
  }
}
```

---

## 🎁 타입 정의의 장점

### 1. 컴파일 타임 에러 감지
```typescript
// ❌ roles 누락 시 TypeScript 에러 발생
const responseData: FrontendLoginResponse = {
  message: data.message,
  user: data.user,
  // ... roles 필드 빠짐
  // ⚠️ Type Error: Property 'roles' is missing in type
};
```

### 2. 자동 완성 지원
- IDE에서 필드명 자동 완성
- 오타 방지
- 개발 생산성 향상

### 3. API 문서 역할
- 백엔드 API 스펙이 코드에 명시됨
- 별도 문서 없이도 데이터 구조 파악 가능
- 팀원 간 커뮤니케이션 비용 절감

### 4. 리팩토링 안정성
- 백엔드 API 변경 시 즉시 감지
- 영향 범위 파악 용이
- 안전한 코드 수정

---

## 📝 적용 체크리스트

### API Route 작성 시 필수 사항

- [ ] 백엔드 응답 타입 인터페이스 정의
- [ ] 프론트엔드 응답 타입 인터페이스 정의
- [ ] `await response.json()` 시 타입 지정
- [ ] 프론트 응답 객체에 타입 지정
- [ ] 모든 필수 필드 포함 확인

### 타입 정의 원칙

```typescript
// ✅ Good: 명시적 타입 지정
const data: BackendResponse = await response.json();
const result: FrontendResponse = {
  // ... 모든 필드 포함
};

// ❌ Bad: 타입 없이 작성
const data = await response.json();
const result = {
  // ... 필드 누락 가능성
};
```

---

## 🔍 실제 적용 예시

### 파일 위치
```
src/app/api/auth/login/route.ts
```

### Before (문제 코드)
```typescript
export async function POST(request: NextRequest) {
  // ...
  const data = await backendResponse.json(); // 타입 없음

  const responseData = {
    message: data.message,
    user: data.user,
    menus: data.menus,
    // roles 누락!
  };

  return NextResponse.json(responseData);
}
```

### After (개선 코드)
```typescript
interface BackendLoginResponse {
  // ... 전체 타입 정의
  roles: Array<{ id: number; name: string; description: string }>;
}

interface FrontendLoginResponse {
  // ... 전체 타입 정의
  roles: BackendLoginResponse['roles'];
}

export async function POST(request: NextRequest) {
  // ...
  const data: BackendLoginResponse = await backendResponse.json();

  const responseData: FrontendLoginResponse = {
    message: data.message,
    user: data.user,
    menus: data.menus,
    roles: data.roles, // ✅ 명시적 포함
    // ... 기타 필드
  };

  return NextResponse.json(responseData);
}
```

---

## 🚨 주의사항

### 1. 타입과 실제 데이터 불일치
```typescript
// ⚠️ 백엔드 API 스펙 변경 시
interface BackendResponse {
  // 타입 정의는 그대로인데
  user_name: string;
}

// 실제 응답은 변경됨
{
  "username": "홍길동" // 필드명 변경됨
}
```

**대응 방안:**
- 백엔드 API 스펙 변경 시 타입 정의도 함께 업데이트
- API 응답 검증 로직 추가 (런타임 체크)
- 백엔드 팀과 스펙 변경 사전 공유

### 2. Optional vs Required
```typescript
// 명확한 옵셔널 표시
interface Response {
  required_field: string;      // 필수
  optional_field?: string;     // 선택
  nullable_field: string | null; // null 가능
}
```

### 3. any 타입 남용 금지
```typescript
// ❌ Bad
interface Response {
  data: any; // 타입 안전성 상실
}

// ✅ Good
interface Response {
  data: {
    id: number;
    name: string;
  };
}
```

---

## 📚 관련 문서

- [Authentication Implementation Guide](./[IMPL-2025-11-07]%20authentication-implementation-guide.md)
- [Token Management Guide](./[IMPL-2025-11-10]%20token-management-guide.md)
- [API Requirements](./[REF]%20api-requirements.md)

---

## 📌 핵심 요약

1. **API Route는 백엔드와 프론트 사이의 중간 레이어**
   - 데이터 변환/필터링 역할 수행
   - 타입 정의로 누락 방지

2. **타입 정의의 3가지 핵심 가치**
   - 컴파일 타임 에러 감지
   - 개발 생산성 향상 (자동완성)
   - 리팩토링 안정성 보장

3. **실무 적용 원칙**
   - 백엔드 응답 타입 → 프론트 응답 타입 순서로 정의
   - 모든 API Route에 타입 적용
   - 백엔드 스펙 변경 시 타입도 함께 업데이트

---

**작성일:** 2025-11-11
**작성자:** Claude Code
**마지막 수정:** 2025-11-11
