feat: fetchWrapper 마이그레이션 및 토큰 리프레시 캐싱 구현

- 40+ actions.ts 파일을 fetchWrapper 패턴으로 마이그레이션
- 토큰 리프레시 캐싱 로직 추가 (refresh-token.ts)
- ApiErrorContext 추가로 전역 에러 처리 개선
- HR EmployeeForm 컴포넌트 개선
- 참조함(ReferenceBox) 기능 수정
- juil 테스트 URL 페이지 추가
- claudedocs 문서 업데이트

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-30 17:00:18 +09:00
parent 0e5307f7a3
commit d38b1242d7
82 changed files with 7434 additions and 4775 deletions

View File

@@ -1,21 +1,9 @@
'use server';
import { cookies } from 'next/headers';
import { serverFetch } from '@/lib/api/fetch-wrapper';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://sam.kr:8080';
// ===== API Helper =====
async function getAuthHeaders() {
const cookieStore = await cookies();
const token = cookieStore.get('access_token')?.value;
return {
'Content-Type': 'application/json',
'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '',
...(token && { Authorization: `Bearer ${token}` }),
};
}
// ===== 타입 정의 =====
// API 응답 타입
@@ -99,21 +87,27 @@ export async function getWorkSetting(): Promise<{
success: boolean;
data?: WorkSettingFormData;
error?: string;
__authError?: boolean;
}> {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE_URL}/api/v1/settings/work`, {
const { response, error } = await serverFetch(`${API_BASE_URL}/api/v1/settings/work`, {
method: 'GET',
headers,
cache: 'no-store',
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
if (error) {
return {
success: false,
error: errorData.message || `API 오류: ${response.status}`,
error: error.message,
__authError: error.code === 'UNAUTHORIZED',
};
}
if (!response || !response.ok) {
const errorData = await response?.json().catch(() => ({}));
return {
success: false,
error: errorData?.message || `API 오류: ${response?.status}`,
};
}
@@ -141,21 +135,27 @@ export async function updateWorkSetting(
success: boolean;
data?: WorkSettingFormData;
error?: string;
__authError?: boolean;
}> {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE_URL}/api/v1/settings/work`, {
const { response, error } = await serverFetch(`${API_BASE_URL}/api/v1/settings/work`, {
method: 'PUT',
headers,
body: JSON.stringify(transformToApi(data)),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
if (error) {
return {
success: false,
error: errorData.message || `API 오류: ${response.status}`,
error: error.message,
__authError: error.code === 'UNAUTHORIZED',
};
}
if (!response || !response.ok) {
const errorData = await response?.json().catch(() => ({}));
return {
success: false,
error: errorData?.message || `API 오류: ${response?.status}`,
};
}