api link to page

This commit is contained in:
2025-11-29 13:00:50 +09:00
parent 32e9fed5cd
commit 39d21a475b
11 changed files with 288 additions and 447 deletions

View File

@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import MainLogoSvg from "./svgs/mainlogosvg";
import ChevronDownSvg from "./svgs/chevrondownsvg";
import apiService from "./lib/apiService";
const NAV_ITEMS = [
{ label: "교육 과정 목록", href: "/course-list" },
@@ -54,32 +55,20 @@ export default function NavBar() {
localStorage.setItem('token', cookieToken);
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
if (response.status === 401) {
// 토큰이 만료되었거나 유효하지 않은 경우
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
// 로그인 페이지가 아닐 때만 리다이렉트
if (isMounted && pathname !== '/login') {
router.push('/login');
}
if (response.status === 401) {
// 토큰이 만료되었거나 유효하지 않은 경우
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
// 로그인 페이지가 아닐 때만 리다이렉트
if (isMounted && pathname !== '/login') {
router.push('/login');
}
return;
}
const data = await response.json();
const data = response.data;
// 계정 상태 확인
const userStatus = data.status || data.userStatus;

View File

@@ -42,33 +42,9 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet
setIsLoadingInstructors(true);
try {
const token = localStorage.getItem('token') || document.cookie
.split('; ')
.find(row => row.startsWith('token='))
?.split('=')[1];
// 외부 API 호출
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/users/compact`
: 'https://hrdi.coconutmeet.net/admin/users/compact';
// 쿼리 파라미터 추가: type=ADMIN
const apiUrl = new URL(baseUrl);
apiUrl.searchParams.set('type', 'ADMIN');
const response = await fetch(apiUrl.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (!response.ok) {
throw new Error(`강사 목록을 가져오는데 실패했습니다. (${response.status})`);
}
const data = await response.json();
const response = await apiService.getUsersCompact();
const data = response.data;
// API 응답이 배열이 아닌 경우 처리 (예: { items: [...] } 형태)
let usersArray: any[] = [];
@@ -293,15 +269,6 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet
}
}
const token = localStorage.getItem('token') || document.cookie
.split('; ')
.find(row => row.startsWith('token='))
?.split('=')[1];
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? process.env.NEXT_PUBLIC_API_BASE_URL
: 'https://hrdi.coconutmeet.net';
const requestBody: {
title: string;
instructor: string;
@@ -354,52 +321,24 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet
}
} else {
// 등록 모드: POST /subjects
const response = await fetch(`${baseUrl}/subjects`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
body: JSON.stringify(requestBody),
});
try {
await apiService.createSubject({
courseName: courseName.trim(),
instructorName: selectedInstructor.name,
});
if (!response.ok) {
let errorMessage = `과목 등록 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
}
} catch (parseError) {
// JSON 파싱 실패 시 기본 메시지 사용
// 성공 시 onSave 콜백 호출 및 모달 닫기
if (onSave && selectedInstructor) {
onSave(courseName.trim(), selectedInstructor.name);
}
onClose(); // 모달 닫기
} catch (createError) {
const errorMessage = createError instanceof Error ? createError.message : '과목 등록 중 오류가 발생했습니다.';
console.error('과목 등록 실패:', errorMessage);
setErrors({ submit: errorMessage });
setIsSaving(false);
return;
}
// 응답에서 id 추출하여 저장
try {
const responseData = await response.json();
// 응답에서 id 추출 (다양한 가능한 필드명 확인)
const subjectId = responseData.id
|| responseData.data?.id
|| responseData.subjectId
|| responseData.data?.subjectId
|| null;
} catch (parseError) {
// 응답 파싱 실패 시 무시
}
// 성공 시 onSave 콜백 호출 및 모달 닫기
if (onSave && selectedInstructor) {
onSave(courseName.trim(), selectedInstructor.name);
}
onClose(); // 모달 닫기
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '네트워크 오류가 발생했습니다.';

View File

@@ -1,3 +1,5 @@
import apiService from "@/app/lib/apiService";
type RoleType = 'learner' | 'instructor' | 'admin';
type AccountStatus = 'active' | 'inactive';
@@ -22,43 +24,8 @@ export async function getInstructors(): Promise<UserRow[]> {
?.split('=')[1])
: null;
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/users/compact`
: 'https://hrdi.coconutmeet.net/admin/users/compact';
// 쿼리 파라미터 추가: type=ADMIN, limit=10
const apiUrl = new URL(baseUrl);
apiUrl.searchParams.set('type', 'ADMIN');
apiUrl.searchParams.set('limit', '10');
console.log('🔍 [getInstructors] API 호출 정보:', {
url: apiUrl.toString(),
hasToken: !!token,
tokenLength: token?.length || 0
});
const response = await fetch(apiUrl.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
console.log('📡 [getInstructors] API 응답 상태:', {
status: response.status,
statusText: response.statusText,
ok: response.ok
});
if (!response.ok) {
const errorText = await response.text();
console.error('❌ [getInstructors] API 에러 응답:', errorText);
console.error('강사 목록 가져오기 실패:', response.status);
return [];
}
const data = await response.json();
const response = await apiService.getUsersCompact();
const data = response.data;
console.log('📦 [getInstructors] 원본 API 응답 데이터:', {
type: typeof data,

View File

@@ -4,6 +4,7 @@ import { useState, useEffect, useRef, useMemo } from "react";
import AdminSidebar from "@/app/components/AdminSidebar";
import ChevronDownSvg from "@/app/svgs/chevrondownsvg";
import { type UserRow } from "./mockData";
import apiService from "@/app/lib/apiService";
type TabType = 'all' | 'learner' | 'instructor' | 'admin';
type RoleType = 'learner' | 'instructor' | 'admin';
@@ -45,35 +46,9 @@ export default function AdminIdPage() {
setIsLoading(true);
setError(null);
const token = localStorage.getItem('token') || document.cookie
.split('; ')
.find(row => row.startsWith('token='))
?.split('=')[1];
// 외부 API 호출
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/users/compact`
: 'https://hrdi.coconutmeet.net/admin/users/compact';
// 쿼리 파라미터 추가: type=STUDENT, limit=10, page=currentPage
const apiUrl = new URL(baseUrl);
apiUrl.searchParams.set('type', 'STUDENT');
apiUrl.searchParams.set('limit', '10');
apiUrl.searchParams.set('page', String(currentPage));
const response = await fetch(apiUrl.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (!response.ok) {
throw new Error(`사용자 데이터를 가져오는데 실패했습니다. (${response.status})`);
}
const data = await response.json();
const response = await apiService.getUsersCompact();
const data = response.data;
// API 응답이 배열이 아닌 경우 처리 (예: { items: [...] } 형태)
let usersArray: any[] = [];
@@ -206,48 +181,7 @@ export default function AdminIdPage() {
return;
}
const token = localStorage.getItem('token') || document.cookie
.split('; ')
.find(row => row.startsWith('token='))
?.split('=')[1];
if (!token) {
setToastMessage('로그인이 필요합니다.');
setShowToast(true);
setTimeout(() => {
setShowToast(false);
}, 3000);
setIsActivateModalOpen(false);
setSelectedUserId(null);
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/users/${selectedUserId}/unsuspend`
: `https://hrdi.coconutmeet.net/admin/users/${selectedUserId}/unsuspend`;
const response = await fetch(apiUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
let errorMessage = `계정 활성화 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
}
} catch (parseError) {
// ignore
}
throw new Error(errorMessage);
}
await apiService.unsuspendUser(selectedUserId);
// API 호출 성공 시 로컬 상태 업데이트
setUsers(prevUsers =>
@@ -295,52 +229,7 @@ export default function AdminIdPage() {
}
try {
const token = localStorage.getItem('token') || document.cookie
.split('; ')
.find(row => row.startsWith('token='))
?.split('=')[1];
if (!token) {
setToastMessage('로그인이 필요합니다.');
setShowToast(true);
setTimeout(() => {
setShowToast(false);
}, 3000);
setIsDeactivateModalOpen(false);
setSelectedUserId(null);
setDeactivateReason('');
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/admin/users/${selectedUserId}/suspend`
: `https://hrdi.coconutmeet.net/admin/users/${selectedUserId}/suspend`;
const response = await fetch(apiUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
reason: deactivateReason,
}),
});
if (!response.ok) {
let errorMessage = `계정 비활성화 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
}
} catch (parseError) {
// ignore
}
throw new Error(errorMessage);
}
await apiService.suspendUser(selectedUserId);
// API 호출 성공 시 로컬 상태 업데이트
setUsers(prevUsers =>

View File

@@ -158,23 +158,10 @@ export default function AdminLessonsPage() {
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (response.ok) {
const data = await response.json();
if (data.name) {
setCurrentUser(data.name);
}
if (response.data && response.data.name) {
setCurrentUser(response.data.name);
}
} catch (error) {
console.error('사용자 정보 조회 오류:', error);

View File

@@ -3,6 +3,7 @@
import { useEffect, useState, useMemo } from 'react';
import { useRouter } from 'next/navigation';
import ChevronDownSvg from '../../svgs/chevrondownsvg';
import apiService from '../../lib/apiService';
// 드롭다운 아이콘 컴포넌트
function ArrowDownIcon({ className }: { className?: string }) {
@@ -139,30 +140,18 @@ export default function InstructorCoursesPage() {
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
if (response.status === 401) {
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
if (isMounted) {
router.push('/login');
}
if (response.status === 401) {
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
if (isMounted) {
router.push('/login');
}
return;
}
const data = await response.json();
const data = response.data;
if (isMounted) {
const role = data.role || data.userRole || '';
@@ -198,26 +187,14 @@ export default function InstructorCoursesPage() {
.find(row => row.startsWith('token='))
?.split('=')[1];
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/subjects`
: 'https://hrdi.coconutmeet.net/subjects';
const response = await apiService.getSubjects();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (response.ok) {
const data = await response.json();
const coursesArray = Array.isArray(data) ? data : (data.items || data.courses || data.data || []);
setCourses(coursesArray.map((item: any) => ({
id: String(item.id || item.subjectId || ''),
name: item.courseName || item.name || item.subjectName || '',
})));
}
const data = response.data;
const coursesArray = Array.isArray(data) ? data : (data.items || data.courses || data.data || []);
setCourses(coursesArray.map((item: any) => ({
id: String(item.id || item.subjectId || ''),
name: item.courseName || item.name || item.subjectName || '',
})));
} catch (error) {
console.error('교육 과정 목록 조회 오류:', error);
}
@@ -391,7 +368,7 @@ export default function InstructorCoursesPage() {
return (
<div className="bg-white min-h-screen flex flex-col">
<div className="flex-1 max-w-[1440px] w-full mx-auto px-0">
<div className="flex flex-col gap-[10px] h-[100px] items-start px-[32px]">
<div className="flex flex-col h-[100px] items-start justify-center px-[32px] ">
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
</h1>
@@ -625,9 +602,14 @@ export default function InstructorCoursesPage() {
{/* 테이블 바디 */}
<div className="bg-white">
{paginatedData.map((item) => (
<div
<button
key={item.id}
className="h-[48px] flex items-center border-b border-[#dee1e6] last:border-b-0"
type="button"
onClick={() => {
// 상세 페이지로 이동 (API 연동 시 item.id 사용)
router.push(`/instructor/courses/${item.id}`);
}}
className="h-[48px] w-full flex items-center border-b border-[#dee1e6] last:border-b-0 cursor-pointer hover:bg-[#F5F7FF] transition-colors text-left"
>
<div className="flex-1 border-r border-[#dee1e6] px-[16px] py-[12px]">
<span className="text-[15px] font-medium leading-[1.5] text-[#1b2027]">{item.courseName}</span>
@@ -666,7 +648,7 @@ export default function InstructorCoursesPage() {
<StatusTag text="미완료" type="default" color="gray" />
)}
</div>
</div>
</button>
))}
</div>
</>

View File

@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import Link from 'next/link';
import MainLogoSvg from '../svgs/mainlogosvg';
import ChevronDownSvg from '../svgs/chevrondownsvg';
import apiService from '../lib/apiService';
// 아이콘 컴포넌트들
function BookIcon({ className }: { className?: string }) {
@@ -194,30 +195,18 @@ export default function InstructorPage() {
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
if (response.status === 401) {
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
if (isMounted) {
router.push('/login');
}
if (response.status === 401) {
localStorage.removeItem('token');
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
if (isMounted) {
router.push('/login');
}
return;
}
const data = await response.json();
const data = response.data;
if (isMounted) {
const role = data.role || data.userRole || '';

View File

@@ -339,15 +339,86 @@ class ApiService {
});
}
/**
* 회원 리스트 조회 (컴팩트) - 관리자 전용
*/
async getUsersCompact() {
return this.request('/admin/users/compact');
}
/**
* 단일 회원 정지 (ID 기준) - 관리자 전용
*/
async suspendUser(userId: string | number) {
return this.request(`/admin/users/${userId}/suspend`, {
method: 'PATCH',
});
}
/**
* 단일 회원 정지 해제 (ID 기준) - 관리자 전용
*/
async unsuspendUser(userId: string | number) {
return this.request(`/admin/users/${userId}/unsuspend`, {
method: 'PATCH',
});
}
/**
* 대량 정지 (ID 배열) - 관리자 전용
*/
async suspendUsers(userIds: (string | number)[]) {
return this.request('/admin/users/suspend', {
method: 'POST',
body: { userIds },
});
}
// ===== 기타 API =====
/**
* 공지사항 조회
* 공지사항 목록 조회 (페이징)
*/
async getNotices() {
return this.request('/notices');
}
/**
* 공지 단건 조회
*/
async getNotice(id: string | number) {
return this.request(`/notices/${id}`);
}
/**
* 공지 등록 (ADMIN)
*/
async createNotice(noticeData: any) {
return this.request('/notices', {
method: 'POST',
body: noticeData,
});
}
/**
* 공지 수정 (ADMIN)
*/
async updateNotice(id: string | number, noticeData: any) {
return this.request(`/notices/${id}`, {
method: 'PATCH',
body: noticeData,
});
}
/**
* 공지 삭제 (ADMIN)
*/
async deleteNotice(id: string | number) {
return this.request(`/notices/${id}`, {
method: 'DELETE',
});
}
/**
* 강좌 조회
*/
@@ -424,6 +495,135 @@ class ApiService {
return this.request('/resources');
}
// ===== 학습 자료실 (Library) 관련 API =====
/**
* 학습 자료실 목록 조회 (로그인 필요, 페이징)
*/
async getLibrary() {
return this.request('/library');
}
/**
* 학습 자료 단건 조회
*/
async getLibraryItem(id: string | number) {
return this.request(`/library/${id}`);
}
/**
* 학습 자료 등록 (ADMIN)
*/
async createLibraryItem(libraryData: any) {
return this.request('/library', {
method: 'POST',
body: libraryData,
});
}
/**
* 학습 자료 수정 (ADMIN)
*/
async updateLibraryItem(id: string | number, libraryData: any) {
return this.request(`/library/${id}`, {
method: 'PATCH',
body: libraryData,
});
}
/**
* 학습 자료 삭제 (ADMIN)
*/
async deleteLibraryItem(id: string | number) {
return this.request(`/library/${id}`, {
method: 'DELETE',
});
}
// ===== 진행률 (Progress) 관련 API =====
/**
* 특정 강의에서 "내" 진행률 조회
*/
async getLectureProgress(lectureId: string | number) {
return this.request(`/progress/lectures/${lectureId}`);
}
/**
* 강의 진행(Heartbeat) 업서트
*/
async updateLectureProgress(progressData: any) {
return this.request('/progress/lectures/progress', {
method: 'POST',
body: progressData,
});
}
/**
* 과목 진행률 요약(내 기준)
*/
async getSubjectProgressSummary(subjectId: string | number) {
return this.request(`/progress/subjects/${subjectId}/summary`);
}
// ===== 평가 (Evaluation) 관련 API =====
/**
* 강의 평가 제출 (60점 이상만 저장)
*/
async submitEvaluation(evaluationData: any) {
return this.request('/evaluations', {
method: 'POST',
body: evaluationData,
});
}
/**
* 특정 강의에서 "내" 마지막 평가 결과 조회
*/
async getMyEvaluation(lectureId: string | number) {
return this.request(`/evaluations/lectures/${lectureId}/me`);
}
/**
* 특정 강의에서 특정 수강생의 마지막 평가 결과 조회 (ADMIN/INSTRUCTOR)
*/
async getUserEvaluation(lectureId: string | number, userId: string | number) {
return this.request(`/evaluations/lectures/${lectureId}/users/${userId}`);
}
// ===== 관리자 강의 현황 관련 API =====
/**
* 강좌별 학습/문제 제출/수료 현황 리스트 (관리자/강사용)
*/
async getLecturesStatus() {
return this.request('/admin/lectures/status');
}
/**
* 특정 강좌 + 특정 학습자의 수강/문제 풀이 상세 (관리자/강사용)
*/
async getLectureStudentDetail(lectureId: string | number, userId: string | number) {
return this.request(`/admin/lectures/${lectureId}/students/${userId}/detail`);
}
// ===== 수료증 및 결과 관련 API =====
/**
* 특정 과목 수료증용 정보 조회 (학생 본인)
*/
async getCertificate(subjectId: string | number) {
return this.request(`/certificates/subjects/${subjectId}`);
}
/**
* 학생 기준 학습 결과(수료 과목 목록)
*/
async getMyResults() {
return this.request('/results/my-subjects');
}
// ===== 파일 업로드 관련 API =====
/**

View File

@@ -3,6 +3,7 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import ModalCloseSvg from "../svgs/closexsvg";
import apiService from "../lib/apiService";
type Props = {
open: boolean;
@@ -22,54 +23,7 @@ export default function AccountDeleteModal({ open, onClose, onConfirm }: Props)
setIsLoading(true);
try {
const token = localStorage.getItem('token');
if (!token) {
alert('로그인이 필요합니다.');
setIsLoading(false);
onClose();
router.push('/login');
return;
}
console.log('회원 탈퇴 요청 시작, 토큰 존재:', !!token);
console.log('토큰 길이:', token?.length);
console.log('토큰 시작 부분:', token?.substring(0, 20));
const response = await fetch('https://hrdi.coconutmeet.net/auth/delete/me', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
console.log('회원 탈퇴 응답 상태:', response.status);
if (!response.ok) {
let errorMessage = `회원 탈퇴 실패 (${response.status})`;
try {
const errorData = await response.json();
console.error('회원 탈퇴 API 오류 응답:', errorData);
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (errorData.errorMessage) {
errorMessage = errorData.errorMessage;
} else if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
} catch (parseError) {
console.error('응답 파싱 오류:', parseError);
if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
}
console.error('회원 탈퇴 실패:', errorMessage, '상태 코드:', response.status);
alert(errorMessage);
setIsLoading(false);
return;
}
await apiService.deleteAccount();
// 성공 시 토큰 제거 및 로그인 페이지로 이동
localStorage.removeItem('token');

View File

@@ -6,6 +6,7 @@ import ChangePasswordModal from "../ChangePasswordModal";
import PasswordChangeDoneModal from "../PasswordChangeDoneModal";
import AccountDeleteModal from "../AccountDeleteModal";
import MenuAccountOption from "@/app/menu/account/MenuAccountOption";
import apiService from "@/app/lib/apiService";
type VerificationState = 'initial' | 'sent' | 'verified' | 'failed' | 'changed';
@@ -51,38 +52,19 @@ export default function AccountPage() {
localStorage.setItem('token', cookieToken);
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (response.status === 401) {
// 토큰이 만료되었거나 유효하지 않은 경우
localStorage.removeItem('token');
if (isMounted) {
router.push('/login');
}
return;
}
if (!response.ok) {
if (response.status === 401) {
// 토큰이 만료되었거나 유효하지 않은 경우
localStorage.removeItem('token');
if (isMounted) {
router.push('/login');
}
return;
}
let errorMessage = `사용자 정보 조회 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
}
} catch (parseError) {
// ignore
}
if (response.status !== 200) {
const errorMessage = response.message || `사용자 정보 조회 실패 (${response.status})`;
console.error('사용자 정보 조회 실패:', errorMessage);
if (isMounted) {
setIsLoading(false);
@@ -90,7 +72,7 @@ export default function AccountPage() {
return;
}
const data = await response.json();
const data = response.data;
if (isMounted) {
setUserInfo(data);
setIsLoading(false);
@@ -191,35 +173,7 @@ export default function AccountPage() {
onClose={() => setDeleteOpen(false)}
onConfirm={async () => {
try {
const token = localStorage.getItem('token');
const response = await fetch('https://hrdi.coconutmeet.net/auth/delete/me', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (!response.ok) {
let errorMessage = `회원 탈퇴 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
} catch (parseError) {
if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
}
console.error('회원 탈퇴 실패:', errorMessage);
alert(errorMessage);
return;
}
await apiService.deleteAccount();
// 성공 시 토큰 제거 및 로그인 페이지로 이동
localStorage.removeItem('token');

View File

@@ -4,6 +4,7 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import MainLogoSvg from './svgs/mainlogosvg';
import apiService from './lib/apiService';
export default function Home() {
const router = useRouter();
@@ -133,23 +134,13 @@ export default function Home() {
return;
}
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me`
: 'https://hrdi.coconutmeet.net/auth/me';
const response = await apiService.getCurrentUser();
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
if (response.status !== 200) {
return;
}
const data = await response.json();
const data = response.data;
if (isMounted) {
// 사용자 권한 확인