mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2026-06-13 12:59:11 +00:00
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:
@@ -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;
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user