refactor(WEB): 전체 actions.ts에 공통 API 유틸 적용

- buildApiUrl / executePaginatedAction 패턴으로 전환 (40+ actions 파일)
- 직접 URLSearchParams 조립 → buildApiUrl 유틸 사용
- 수동 페이지네이션 메타 변환 → executePaginatedAction 자동 처리
- HandoverReportDocumentModal, OrderDocumentModal 개선
- 급여관리 SalaryManagement 코드 개선
- CLAUDE.md Server Action 공통 유틸 규칙 정리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-12 20:59:59 +09:00
parent 31be9d4a25
commit cbb38d48b9
51 changed files with 1050 additions and 1405 deletions

View File

@@ -15,6 +15,7 @@
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
import { buildApiUrl } from '@/lib/api/query-params';
// ============================================
// 타입 정의
@@ -87,8 +88,6 @@ export interface UpdateDepartmentRequest {
// 헬퍼 함수
// ============================================
const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api`;
/**
* API 응답을 프론트엔드 형식으로 변환 (재귀)
*/
@@ -124,14 +123,10 @@ function transformApiToFrontend(apiData: ApiDepartment, depth: number = 0): Depa
export async function getDepartmentTree(params?: {
withUsers?: boolean;
}): Promise<ActionResult<DepartmentRecord[]>> {
const queryParams = new URLSearchParams();
if (params?.withUsers) {
queryParams.append('with_users', '1');
}
const queryString = queryParams.toString();
return executeServerAction({
url: `${API_URL}/v1/departments/tree${queryString ? `?${queryString}` : ''}`,
url: buildApiUrl('/api/v1/departments/tree', {
with_users: params?.withUsers ? '1' : undefined,
}),
transform: (data: ApiDepartment[]) => data.map((dept) => transformApiToFrontend(dept, 0)),
errorMessage: '부서 트리 조회에 실패했습니다.',
});
@@ -145,7 +140,7 @@ export async function getDepartmentById(
id: number
): Promise<ActionResult<DepartmentRecord>> {
return executeServerAction({
url: `${API_URL}/v1/departments/${id}`,
url: buildApiUrl(`/api/v1/departments/${id}`),
transform: (data: ApiDepartment) => transformApiToFrontend(data),
errorMessage: '부서 조회에 실패했습니다.',
});
@@ -159,7 +154,7 @@ export async function createDepartment(
data: CreateDepartmentRequest
): Promise<ActionResult<DepartmentRecord>> {
return executeServerAction({
url: `${API_URL}/v1/departments`,
url: buildApiUrl('/api/v1/departments'),
method: 'POST',
body: {
parent_id: data.parentId,
@@ -183,7 +178,7 @@ export async function updateDepartment(
data: UpdateDepartmentRequest
): Promise<ActionResult<DepartmentRecord>> {
return executeServerAction({
url: `${API_URL}/v1/departments/${id}`,
url: buildApiUrl(`/api/v1/departments/${id}`),
method: 'PATCH',
body: {
parent_id: data.parentId === null ? 0 : data.parentId,
@@ -206,7 +201,7 @@ export async function deleteDepartment(
id: number
): Promise<ActionResult> {
return executeServerAction({
url: `${API_URL}/v1/departments/${id}`,
url: buildApiUrl(`/api/v1/departments/${id}`),
method: 'DELETE',
errorMessage: '부서 삭제에 실패했습니다.',
});