mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 06:56:24 +00:00
- 이벤트 목록 페이지에 Mock 데이터 적용 (evt_2025012301 등 4개 이벤트) - 이벤트 상세 페이지 Analytics API 임시 주석처리 (서버 이슈) - Participation API 프록시 라우트 URL 구조 수정 (/events/ 제거) - EventID localStorage 저장 기능 추가 - 상세한 console.log 추가 (생성된 eventId, objective, timestamp) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
14 KiB
User API 연동 현황
📋 연동 완료 요약
User API는 완전히 구현되어 있으며, 로그인 및 회원가입 기능이 정상적으로 작동합니다.
✅ 구현 완료 항목
-
API 클라이언트 설정
- Gateway를 통한 백엔드 직접 연동
- 토큰 기반 인증 시스템
- Request/Response 인터셉터
-
타입 정의
- LoginRequest/Response
- RegisterRequest/Response
- ProfileResponse
- User 및 AuthState 인터페이스
-
인증 로직
- useAuth 커스텀 훅
- AuthProvider Context
- localStorage 기반 세션 관리
-
UI 페이지
- 로그인 페이지 (/login)
- 회원가입 페이지 (/register)
- 3단계 회원가입 플로우
🏗️ 아키텍처 구조
프론트엔드 Gateway 백엔드
┌─────────────┐ ┌────────┐ ┌──────────┐
│ │ HTTP │ │ HTTP │ │
│ Browser ├────────────>│Gateway ├─────────────>│ User API │
│ │<────────────┤ │<─────────────┤ │
└─────────────┘ JSON+JWT └────────┘ JSON+JWT └──────────┘
API 클라이언트 설정
// src/shared/api/client.ts
const GATEWAY_HOST = 'http://kt-event-marketing-api.20.214.196.128.nip.io';
const API_HOSTS = {
user: GATEWAY_HOST,
event: GATEWAY_HOST,
content: GATEWAY_HOST,
ai: GATEWAY_HOST,
participation: GATEWAY_HOST,
distribution: GATEWAY_HOST,
analytics: GATEWAY_HOST,
};
// User API는 apiClient를 통해 직접 Gateway에 연결
export const apiClient: AxiosInstance = axios.create({
baseURL: API_HOSTS.user,
timeout: 90000,
headers: {
'Content-Type': 'application/json',
},
});
💡 프록시 라우트 불필요: User API는 Next.js 프록시를 거치지 않고 브라우저에서 Gateway로 직접 요청합니다.
📡 User API 엔드포인트
1. 로그인
POST /api/v1/users/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
응답:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"userId": "550e8400-e29b-41d4-a716-446655440000",
"userName": "홍길동",
"role": "USER",
"email": "user@example.com"
}
2. 회원가입
POST /api/v1/users/register
Content-Type: application/json
{
"name": "홍길동",
"phoneNumber": "01012345678",
"email": "user@example.com",
"password": "password123",
"storeName": "홍길동 고깃집",
"industry": "restaurant",
"address": "서울특별시 강남구",
"businessHours": "09:00-18:00"
}
응답:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"userId": "550e8400-e29b-41d4-a716-446655440000",
"userName": "홍길동",
"storeId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"storeName": "홍길동 고깃집"
}
3. 로그아웃
POST /api/v1/users/logout
Authorization: Bearer {token}
응답:
{
"success": true,
"message": "로그아웃되었습니다"
}
4. 프로필 조회
GET /api/v1/users/profile
Authorization: Bearer {token}
응답:
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"userName": "홍길동",
"phoneNumber": "01012345678",
"email": "user@example.com",
"role": "USER",
"storeId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"storeName": "홍길동 고깃집",
"industry": "restaurant",
"address": "서울특별시 강남구",
"businessHours": "09:00-18:00",
"createdAt": "2025-01-01T00:00:00",
"lastLoginAt": "2025-01-10T12:00:00"
}
5. 프로필 수정
PUT /api/v1/users/profile
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "홍길동",
"phoneNumber": "01012345678",
"storeName": "홍길동 고깃집",
"industry": "restaurant",
"address": "서울특별시 강남구",
"businessHours": "09:00-18:00"
}
6. 비밀번호 변경
PUT /api/v1/users/password
Authorization: Bearer {token}
Content-Type: application/json
{
"currentPassword": "oldpassword",
"newPassword": "newpassword123"
}
🔐 인증 플로우
로그인 플로우
1. 사용자가 이메일/비밀번호 입력
2. userApi.login() 호출
3. 서버에서 JWT 토큰 발급
4. localStorage에 토큰 저장
5. userApi.getProfile() 호출 (storeId 포함된 전체 정보 획득)
6. localStorage에 User 정보 저장
7. AuthContext 상태 업데이트
8. 메인 페이지로 리디렉션
회원가입 플로우
1. 3단계 폼 작성
- Step 1: 계정 정보 (이메일, 비밀번호)
- Step 2: 개인 정보 (이름, 전화번호)
- Step 3: 사업장 정보 (상호명, 업종, 주소 등)
2. userApi.register() 호출
3. 서버에서 사용자 생성 및 JWT 토큰 발급
4. localStorage에 토큰 및 User 정보 저장
5. AuthContext 상태 업데이트
6. 회원가입 완료 다이얼로그 표시
7. 메인 페이지로 리디렉션
로그아웃 플로우
1. userApi.logout() 호출
2. 서버에서 세션 무효화 (실패해도 계속 진행)
3. localStorage에서 토큰 및 User 정보 삭제
4. AuthContext 상태 초기화
5. 로그인 페이지로 리디렉션
📂 파일 구조
API Layer
src/entities/user/
├── api/
│ └── userApi.ts # User API 서비스 함수
├── model/
│ └── types.ts # TypeScript 타입 정의
└── index.ts # Public exports
Features Layer
src/features/auth/
├── model/
│ ├── useAuth.ts # 인증 커스텀 훅
│ └── AuthProvider.tsx # Context Provider
└── index.ts # Public exports
Pages
src/app/(auth)/
├── login/
│ └── page.tsx # 로그인 페이지
└── register/
└── page.tsx # 회원가입 페이지 (3단계 플로우)
Shared
src/shared/api/
├── client.ts # Axios 클라이언트 설정
└── index.ts # Public exports
src/stores/
└── authStore.ts # Zustand 인증 스토어 (참고용)
🔑 주요 코드 구조
1. User API Service
src/entities/user/api/userApi.ts:
const USER_API_BASE = '/api/v1/users';
export const userApi = {
login: async (data: LoginRequest): Promise<LoginResponse> => {...},
register: async (data: RegisterRequest): Promise<RegisterResponse> => {...},
logout: async (): Promise<LogoutResponse> => {...},
getProfile: async (): Promise<ProfileResponse> => {...},
updateProfile: async (data: UpdateProfileRequest): Promise<ProfileResponse> => {...},
changePassword: async (data: ChangePasswordRequest): Promise<void> => {...},
};
2. useAuth Hook
src/features/auth/model/useAuth.ts:
export const useAuth = () => {
const [authState, setAuthState] = useState<AuthState>({
user: null,
token: null,
isAuthenticated: false,
isLoading: true,
});
// 초기 인증 상태 확인 (localStorage 기반)
useEffect(() => {
const token = localStorage.getItem(TOKEN_KEY);
const userStr = localStorage.getItem(USER_KEY);
if (token && userStr) {
const user = JSON.parse(userStr) as User;
setAuthState({
user,
token,
isAuthenticated: true,
isLoading: false,
});
}
}, []);
// 로그인 함수
const login = async (credentials: LoginRequest) => {
const response = await userApi.login(credentials);
localStorage.setItem(TOKEN_KEY, response.token);
const profile = await userApi.getProfile();
localStorage.setItem(USER_KEY, JSON.stringify(user));
setAuthState({...});
return { success: true, user };
};
// 회원가입, 로그아웃, 프로필 새로고침 함수들...
return {
...authState,
login,
register,
logout,
refreshProfile,
};
};
3. AuthProvider Context
src/features/auth/model/AuthProvider.tsx:
const AuthContext = createContext<AuthContextType | null>(null);
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const auth = useAuth();
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
};
export const useAuthContext = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuthContext must be used within AuthProvider');
}
return context;
};
4. RootLayout 적용
src/app/layout.tsx:
export default function RootLayout({ children }) {
return (
<html lang="ko">
<body>
<MUIThemeProvider>
<ReactQueryProvider>
<AuthProvider>
{children}
</AuthProvider>
</ReactQueryProvider>
</MUIThemeProvider>
</body>
</html>
);
}
🧪 테스트 방법
1. 회원가입 테스트
-
개발 서버 실행
npm run dev -
브라우저에서
/register접속 -
3단계 폼 작성:
- Step 1: 이메일 및 비밀번호 입력
- Step 2: 이름 및 전화번호 입력 (010-1234-5678 형식)
- Step 3: 사업장 정보 입력 및 약관 동의
-
"가입완료" 버튼 클릭
-
성공 시:
- 회원가입 완료 다이얼로그 표시
- localStorage에 토큰 및 사용자 정보 저장
- 메인 페이지로 리디렉션
2. 로그인 테스트
-
브라우저에서
/login접속 -
이메일 및 비밀번호 입력
-
"로그인" 버튼 클릭
-
성공 시:
- localStorage에 토큰 및 사용자 정보 저장
- 메인 페이지로 리디렉션
- 헤더에 사용자 정보 표시
3. 로그아웃 테스트
-
로그인된 상태에서 프로필 페이지 또는 헤더 메뉴 접근
-
"로그아웃" 버튼 클릭
-
성공 시:
- localStorage에서 토큰 및 사용자 정보 삭제
- 로그인 페이지로 리디렉션
4. 디버깅
브라우저 개발자 도구 Console에서 다음 로그 확인:
📞 전화번호 변환: 010-1234-5678 -> 01012345678
📦 회원가입 요청 데이터: {...}
🚀 registerUser 함수 호출
📥 registerUser 결과: {...}
✅ 회원가입 성공: {...}
💾 localStorage에 토큰과 사용자 정보 저장 완료
🚨 주의사항
1. 전화번호 형식 변환
- UI 입력:
010-1234-5678(하이픈 포함) - API 전송:
01012345678(하이픈 제거) - 회원가입 페이지에서 자동 변환 처리됨
2. 토큰 관리
- Access Token은 localStorage에
accessToken키로 저장 - User 정보는 localStorage에
user키로 저장 - 401 응답 시 자동으로 로그인 페이지로 리디렉션
3. 프록시 라우트 없음
- User API는 Next.js 프록시를 사용하지 않음
- 브라우저에서 Gateway로 직접 요청
- CORS 설정이 Gateway에서 처리되어야 함
4. 로그아웃 에러 처리
- 로그아웃 API 실패해도 로컬 상태는 정리됨
- 서버 에러 발생 시에도 사용자는 정상적으로 로그아웃됨
📝 타입 정의 요약
// 로그인
interface LoginRequest {
email: string;
password: string;
}
interface LoginResponse {
token: string;
userId: string; // UUID format
userName: string;
role: string;
email: string;
}
// 회원가입
interface RegisterRequest {
name: string;
phoneNumber: string; // "01012345678" 형식
email: string;
password: string;
storeName: string;
industry?: string;
address: string;
businessHours?: string;
}
interface RegisterResponse {
token: string;
userId: string; // UUID format
userName: string;
storeId: string; // UUID format
storeName: string;
}
// 프로필
interface ProfileResponse {
userId: string; // UUID format
userName: string;
phoneNumber: string;
email: string;
role: string;
storeId: string; // UUID format
storeName: string;
industry: string;
address: string;
businessHours: string;
createdAt: string;
lastLoginAt: string;
}
// User 상태
interface User {
userId: string; // UUID format
userName: string;
email: string;
role: string;
phoneNumber?: string;
storeId?: string; // UUID format
storeName?: string;
industry?: string;
address?: string;
businessHours?: string;
}
// 인증 상태
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
}
✅ 체크리스트
- API 클라이언트 설정 완료
- TypeScript 타입 정의 완료
- useAuth Hook 구현 완료
- AuthProvider Context 구현 완료
- 로그인 페이지 구현 완료
- 회원가입 페이지 (3단계) 구현 완료
- localStorage 세션 관리 완료
- Request/Response 인터셉터 설정 완료
- 401 에러 핸들링 완료
- RootLayout에 AuthProvider 적용 완료
- 빌드 테스트 통과 ✅
- 개발 서버 실행 및 실제 API 테스트 (사용자가 수행)
📚 관련 파일
핵심 파일
src/entities/user/api/userApi.ts- User API 서비스src/entities/user/model/types.ts- TypeScript 타입 정의src/features/auth/model/useAuth.ts- 인증 Hooksrc/features/auth/model/AuthProvider.tsx- Context Providersrc/app/(auth)/login/page.tsx- 로그인 페이지src/app/(auth)/register/page.tsx- 회원가입 페이지src/shared/api/client.ts- Axios 클라이언트 설정
참고 파일
src/stores/authStore.ts- Zustand 인증 스토어 (참고용, 현재 미사용)src/app/layout.tsx- RootLayout with AuthProvider
🎯 다음 단계
User API 연동은 완료되었으므로 다음 작업을 진행할 수 있습니다:
- 개발 서버 테스트:
npm run dev실행 후 실제 회원가입/로그인 테스트 - 프로필 페이지 개선: 사용자 정보 수정 기능 강화
- 비밀번호 찾기: 비밀번호 재설정 플로우 구현 (현재 미구현)
- 소셜 로그인: 카카오톡, 네이버 소셜 로그인 구현 (현재 준비 중)
- 권한 관리: Role 기반 접근 제어 (ADMIN, USER) 구현
- 세션 갱신: Refresh Token 로직 추가 (필요시)
📞 문의
User API 관련 문제나 개선사항은 프로젝트 팀에 문의하세요.
문서 작성일: 2025-01-30 마지막 업데이트: 2025-01-30