# User API 연동 가이드
## 개요
FSD(Feature-Sliced Design) 아키텍처를 기반으로 User Service API 연동을 구현했습니다.
## 디렉토리 구조
```
src/
├── shared/
│ └── api/
│ ├── client.ts # Axios 클라이언트 설정
│ ├── types.ts # 공통 API 타입
│ └── index.ts
├── entities/
│ └── user/
│ ├── model/
│ │ └── types.ts # User 엔티티 타입
│ ├── api/
│ │ └── userApi.ts # User API 함수
│ └── index.ts
└── features/
├── auth/
│ ├── model/
│ │ ├── useAuth.ts # 인증 훅
│ │ └── AuthProvider.tsx # 인증 Context
│ └── index.ts
└── profile/
├── model/
│ └── useProfile.ts # 프로필 훅
└── index.ts
```
## API 명세
- **Base URL**: `http://20.196.65.160:8081`
- **Endpoints**:
- `POST /api/v1/users/login` - 로그인
- `POST /api/v1/users/register` - 회원가입
- `POST /api/v1/users/logout` - 로그아웃
- `GET /api/v1/users/profile` - 프로필 조회
- `PUT /api/v1/users/profile` - 프로필 수정
- `PUT /api/v1/users/password` - 비밀번호 변경
## 사용 방법
### 1. AuthProvider 설정
루트 레이아웃에 AuthProvider를 추가합니다:
```tsx
// app/layout.tsx
import { AuthProvider } from '@/features/auth';
export default function RootLayout({ children }) {
return (
{children}
);
}
```
### 2. 로그인 구현 예제
```tsx
'use client';
import { useAuthContext } from '@/features/auth';
import { useState } from 'react';
export default function LoginPage() {
const { login, isLoading } = useAuthContext();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
const result = await login({ email, password });
if (result.success) {
// 로그인 성공
console.log('로그인 성공:', result.user);
// 페이지 이동 등
} else {
// 로그인 실패
console.error('로그인 실패:', result.error);
}
};
return (
);
}
```
### 3. 회원가입 구현 예제
```tsx
'use client';
import { useAuthContext } from '@/features/auth';
import { useState } from 'react';
import type { RegisterRequest } from '@/entities/user';
export default function RegisterPage() {
const { register, isLoading } = useAuthContext();
const [formData, setFormData] = useState({
name: '',
phoneNumber: '',
email: '',
password: '',
storeName: '',
industry: '',
address: '',
businessHours: '',
});
const handleRegister = async (e: React.FormEvent) => {
e.preventDefault();
const result = await register(formData);
if (result.success) {
console.log('회원가입 성공:', result.user);
} else {
console.error('회원가입 실패:', result.error);
}
};
return (
);
}
```
### 4. 프로필 조회 및 수정 예제
```tsx
'use client';
import { useProfile } from '@/features/profile';
import { useEffect } from 'react';
export default function ProfilePage() {
const {
profile,
isLoading,
error,
fetchProfile,
updateProfile
} = useProfile();
useEffect(() => {
fetchProfile();
}, [fetchProfile]);
const handleUpdate = async () => {
const result = await updateProfile({
name: '새로운 이름',
storeName: '새로운 가게명',
});
if (result.success) {
console.log('프로필 수정 성공:', result.data);
}
};
if (isLoading) return 로딩 중...
;
if (error) return 에러: {error}
;
if (!profile) return 프로필 없음
;
return (
{profile.userName}
이메일: {profile.email}
가게명: {profile.storeName}
);
}
```
### 5. 인증 상태 확인 예제
```tsx
'use client';
import { useAuthContext } from '@/features/auth';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function ProtectedPage() {
const { user, isAuthenticated, isLoading } = useAuthContext();
const router = useRouter();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push('/login');
}
}, [isAuthenticated, isLoading, router]);
if (isLoading) return 로딩 중...
;
if (!isAuthenticated) return null;
return (
환영합니다, {user?.userName}님!
);
}
```
### 6. 로그아웃 구현 예제
```tsx
'use client';
import { useAuthContext } from '@/features/auth';
import { useRouter } from 'next/navigation';
export default function Header() {
const { user, isAuthenticated, logout } = useAuthContext();
const router = useRouter();
const handleLogout = async () => {
await logout();
router.push('/login');
};
if (!isAuthenticated) return null;
return (
);
}
```
## 타입 정의
### User 타입
```typescript
interface User {
userId: number;
userName: string;
email: string;
role: string;
phoneNumber?: string;
storeId?: number;
storeName?: string;
industry?: string;
address?: string;
businessHours?: string;
}
```
### LoginRequest 타입
```typescript
interface LoginRequest {
email: string;
password: string;
}
```
### RegisterRequest 타입
```typescript
interface RegisterRequest {
name: string;
phoneNumber: string; // 패턴: ^010\d{8}$
email: string;
password: string; // 최소 8자
storeName: string;
industry?: string;
address: string;
businessHours?: string;
}
```
## API Client 설정
API 클라이언트는 다음 기능을 자동으로 처리합니다:
1. **JWT 토큰 자동 추가**: localStorage의 `accessToken`을 자동으로 헤더에 포함
2. **401 인증 오류 처리**: 인증 실패 시 자동으로 토큰 삭제 및 로그인 페이지로 리다이렉트
3. **Base URL 설정**: 환경 변수로 API 서버 URL 관리
## 환경 변수
`.env.local` 파일에 다음 환경 변수를 설정하세요:
```env
NEXT_PUBLIC_API_BASE_URL=http://20.196.65.160:8081
```
## 주의사항
1. **토큰 관리**: 토큰은 localStorage에 저장되며, 로그아웃 시 자동으로 삭제됩니다.
2. **인증 상태**: AuthProvider로 감싼 컴포넌트에서만 useAuthContext 사용 가능합니다.
3. **에러 처리**: 모든 API 함수는 try-catch로 에러를 처리하며, 결과 객체에 success와 error를 포함합니다.
4. **비밀번호 검증**: 회원가입 시 비밀번호는 최소 8자 이상이어야 합니다.
5. **전화번호 형식**: 010으로 시작하는 11자리 숫자만 허용됩니다.
## 빌드 및 실행
```bash
# 빌드
npm run build
# 개발 서버 실행 (사용자가 직접 수행)
npm run dev
```