User API 전체 연동 완료 및 로그아웃 에러 처리 개선

## 주요 변경사항

### 1. FSD 아키텍처 기반 API 레이어 구축
- entities/user: User 엔티티 (타입, API)
- features/auth: 인증 기능 (useAuth, AuthProvider)
- shared/api: 공통 API 클라이언트 (Axios, 인터셉터)

### 2. 전체 User API 화면 연동 완료
-  POST /api/v1/users/login → login/page.tsx
-  POST /api/v1/users/register → register/page.tsx
-  POST /api/v1/users/logout → profile/page.tsx
-  GET /api/v1/users/profile → profile/page.tsx
-  PUT /api/v1/users/profile → profile/page.tsx
-  PUT /api/v1/users/password → profile/page.tsx

### 3. 로그인 페이지 API 연동
- useAuthStore → useAuthContext 변경
- 실제 로그인 API 호출
- 비밀번호 검증 완화 (API 스펙에 맞춤)
- 상세 로깅 추가

### 4. 프로필 페이지 API 연동
- 프로필 자동 로드 (GET /profile)
- 프로필 수정 (PUT /profile)
- 비밀번호 변경 (PUT /password)
- 로그아웃 (POST /logout)
- 전화번호 형식 변환 (01012345678 ↔ 010-1234-5678)

### 5. 로그아웃 에러 처리 개선
- 백엔드 500 에러 발생해도 로컬 상태 정리 후 로그아웃 진행
- 사용자 경험 우선: 정상 로그아웃으로 처리
- 개발자용 상세 에러 로그 출력

### 6. 문서화
- docs/api-integration-complete.md: 전체 연동 완료 보고서
- docs/api-server-issue.md: 백엔드 이슈 상세 보고 (회원가입 타임아웃, 로그아웃 500 에러)
- docs/user-api-integration.md: User API 통합 가이드
- docs/register-api-guide.md: 회원가입 API 가이드

### 7. 에러 처리 강화
- 서버 응답 에러 / 네트워크 에러 / 요청 설정 에러 구분
- 사용자 친화적 에러 메시지
- 전체 프로세스 상세 로깅

## 기술 스택
- FSD Architecture
- React Context API (AuthProvider)
- Axios (인터셉터, 90초 타임아웃)
- Zod (폼 검증)
- TypeScript (엄격한 타입)

## 테스트
-  빌드 성공
-  백엔드 안정화 후 전체 플로우 테스트 필요

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cherry2250
2025-10-28 13:18:23 +09:00
parent a90fd81c46
commit 10c728dbaf
19 changed files with 2126 additions and 87 deletions
+103
View File
@@ -0,0 +1,103 @@
import { apiClient } from '@/shared/api';
import type {
LoginRequest,
LoginResponse,
RegisterRequest,
RegisterResponse,
LogoutResponse,
ProfileResponse,
UpdateProfileRequest,
ChangePasswordRequest,
} from '../model/types';
const USER_API_BASE = '/api/v1/users';
/**
* User API Service
* 사용자 인증 및 프로필 관리 API
*/
export const userApi = {
/**
* 로그인
*/
login: async (data: LoginRequest): Promise<LoginResponse> => {
const response = await apiClient.post<LoginResponse>(
`${USER_API_BASE}/login`,
data
);
return response.data;
},
/**
* 회원가입
*/
register: async (data: RegisterRequest): Promise<RegisterResponse> => {
console.log('📞 userApi.register 호출');
console.log('🎯 URL:', `${USER_API_BASE}/register`);
console.log('📦 요청 데이터:', {
...data,
password: '***'
});
try {
const response = await apiClient.post<RegisterResponse>(
`${USER_API_BASE}/register`,
data
);
console.log('✅ userApi.register 성공:', response.data);
return response.data;
} catch (error) {
console.error('❌ userApi.register 실패:', error);
throw error;
}
},
/**
* 로그아웃
*/
logout: async (): Promise<LogoutResponse> => {
const token = localStorage.getItem('accessToken');
const response = await apiClient.post<LogoutResponse>(
`${USER_API_BASE}/logout`,
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
return response.data;
},
/**
* 프로필 조회
*/
getProfile: async (): Promise<ProfileResponse> => {
const response = await apiClient.get<ProfileResponse>(
`${USER_API_BASE}/profile`
);
return response.data;
},
/**
* 프로필 수정
*/
updateProfile: async (
data: UpdateProfileRequest
): Promise<ProfileResponse> => {
const response = await apiClient.put<ProfileResponse>(
`${USER_API_BASE}/profile`,
data
);
return response.data;
},
/**
* 비밀번호 변경
*/
changePassword: async (data: ChangePasswordRequest): Promise<void> => {
await apiClient.put(`${USER_API_BASE}/password`, data);
},
};
export default userApi;
+16
View File
@@ -0,0 +1,16 @@
// Types
export type {
LoginRequest,
LoginResponse,
RegisterRequest,
RegisterResponse,
LogoutResponse,
ProfileResponse,
UpdateProfileRequest,
ChangePasswordRequest,
User,
AuthState,
} from './model/types';
// API
export { userApi } from './api/userApi';
+98
View File
@@ -0,0 +1,98 @@
/**
* User Entity Types
* API 스펙 기반 타입 정의
*/
// 로그인 요청/응답
export interface LoginRequest {
email: string;
password: string;
}
export interface LoginResponse {
token: string;
userId: number;
userName: string;
role: string;
email: string;
}
// 회원가입 요청/응답
export interface RegisterRequest {
name: string;
phoneNumber: string;
email: string;
password: string;
storeName: string;
industry?: string;
address: string;
businessHours?: string;
}
export interface RegisterResponse {
token: string;
userId: number;
userName: string;
storeId: number;
storeName: string;
}
// 로그아웃 응답
export interface LogoutResponse {
success: boolean;
message: string;
}
// 프로필 조회/수정
export interface ProfileResponse {
userId: number;
userName: string;
phoneNumber: string;
email: string;
role: string;
storeId: number;
storeName: string;
industry: string;
address: string;
businessHours: string;
createdAt: string;
lastLoginAt: string;
}
export interface UpdateProfileRequest {
name?: string;
phoneNumber?: string;
email?: string;
storeName?: string;
industry?: string;
address?: string;
businessHours?: string;
}
// 비밀번호 변경
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
}
// User 상태
export interface User {
userId: number;
userName: string;
email: string;
role: string;
phoneNumber?: string;
storeId?: number;
storeName?: string;
industry?: string;
address?: string;
businessHours?: string;
}
// 인증 상태
export interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
}