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:
@@ -2,11 +2,10 @@
|
||||
|
||||
|
||||
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import type { Board, BoardApiData, BoardFormData } from './types';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ===== 변환 =====
|
||||
function transformApiToFrontend(apiData: BoardApiData): Board {
|
||||
const extraSettings = apiData.extra_settings || {};
|
||||
@@ -50,12 +49,11 @@ function transformFrontendToApi(data: BoardFormData & { boardCode?: string; desc
|
||||
export async function getBoards(filters?: {
|
||||
board_type?: string; search?: string;
|
||||
}): Promise<ActionResult<Board[]>> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.board_type) params.append('board_type', filters.board_type);
|
||||
if (filters?.search) params.append('search', filters.search);
|
||||
const queryString = params.toString();
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/tenant${queryString ? `?${queryString}` : ''}`,
|
||||
url: buildApiUrl('/api/v1/boards/tenant', {
|
||||
board_type: filters?.board_type,
|
||||
search: filters?.search,
|
||||
}),
|
||||
transform: (data: BoardApiData[]) => (Array.isArray(data) ? data : []).map(transformApiToFrontend),
|
||||
errorMessage: '게시판 목록 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -65,12 +63,11 @@ export async function getBoards(filters?: {
|
||||
export async function getTenantBoards(filters?: {
|
||||
board_type?: string; search?: string;
|
||||
}): Promise<ActionResult<Board[]>> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.board_type) params.append('board_type', filters.board_type);
|
||||
if (filters?.search) params.append('search', filters.search);
|
||||
const queryString = params.toString();
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/tenant${queryString ? `?${queryString}` : ''}`,
|
||||
url: buildApiUrl('/api/v1/boards/tenant', {
|
||||
board_type: filters?.board_type,
|
||||
search: filters?.search,
|
||||
}),
|
||||
transform: (data: BoardApiData[]) => data.map(transformApiToFrontend),
|
||||
errorMessage: '테넌트 게시판 목록 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -79,7 +76,7 @@ export async function getTenantBoards(filters?: {
|
||||
// ===== 게시판 상세 조회 (코드 기반) =====
|
||||
export async function getBoardByCode(code: string): Promise<ActionResult<Board>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${code}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${code}`),
|
||||
transform: (data: BoardApiData) => transformApiToFrontend(data),
|
||||
errorMessage: '게시판을 찾을 수 없습니다.',
|
||||
});
|
||||
@@ -88,7 +85,7 @@ export async function getBoardByCode(code: string): Promise<ActionResult<Board>>
|
||||
// ===== 게시판 상세 조회 (ID 기반) =====
|
||||
export async function getBoardById(id: string): Promise<ActionResult<Board>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${id}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${id}`),
|
||||
transform: (data: BoardApiData) => transformApiToFrontend(data),
|
||||
errorMessage: '게시판을 찾을 수 없습니다.',
|
||||
});
|
||||
@@ -99,7 +96,7 @@ export async function createBoard(
|
||||
data: BoardFormData & { boardCode: string; description?: string }
|
||||
): Promise<ActionResult<Board>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards`,
|
||||
url: buildApiUrl('/api/v1/boards'),
|
||||
method: 'POST',
|
||||
body: transformFrontendToApi(data),
|
||||
transform: (d: BoardApiData) => transformApiToFrontend(d),
|
||||
@@ -113,7 +110,7 @@ export async function updateBoard(
|
||||
data: BoardFormData & { boardCode?: string; description?: string }
|
||||
): Promise<ActionResult<Board>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${id}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${id}`),
|
||||
method: 'PUT',
|
||||
body: transformFrontendToApi(data, true),
|
||||
transform: (d: BoardApiData) => transformApiToFrontend(d),
|
||||
@@ -124,7 +121,7 @@ export async function updateBoard(
|
||||
// ===== 게시판 삭제 =====
|
||||
export async function deleteBoard(id: string): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${id}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${id}`),
|
||||
method: 'DELETE',
|
||||
errorMessage: '게시판 삭제에 실패했습니다.',
|
||||
});
|
||||
@@ -143,4 +140,4 @@ export async function deleteBoardsBulk(ids: string[]): Promise<ActionResult> {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import type {
|
||||
PostApiData,
|
||||
PostPaginationResponse,
|
||||
@@ -10,22 +11,19 @@ import type {
|
||||
CommentsApiResponse,
|
||||
} from '@/components/customer-center/shared/types';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ===== 게시글 API =====
|
||||
|
||||
export async function getDynamicBoardPosts(
|
||||
boardCode: string, filters?: PostFilters
|
||||
): Promise<ActionResult<PostPaginationResponse>> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.search) params.append('search', filters.search);
|
||||
if (filters?.is_notice !== undefined) params.append('is_notice', String(filters.is_notice));
|
||||
if (filters?.status) params.append('status', filters.status);
|
||||
if (filters?.per_page) params.append('per_page', String(filters.per_page));
|
||||
if (filters?.page) params.append('page', String(filters.page));
|
||||
const queryString = params.toString();
|
||||
return executeServerAction<PostPaginationResponse>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts${queryString ? `?${queryString}` : ''}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts`, {
|
||||
search: filters?.search,
|
||||
is_notice: filters?.is_notice,
|
||||
status: filters?.status,
|
||||
per_page: filters?.per_page,
|
||||
page: filters?.page,
|
||||
}),
|
||||
errorMessage: '게시글 목록 조회에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
@@ -34,7 +32,7 @@ export async function getDynamicBoardPost(
|
||||
boardCode: string, postId: number | string
|
||||
): Promise<ActionResult<PostApiData>> {
|
||||
return executeServerAction<PostApiData>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
errorMessage: '게시글을 찾을 수 없습니다.',
|
||||
});
|
||||
}
|
||||
@@ -44,7 +42,7 @@ export async function createDynamicBoardPost(
|
||||
data: { title: string; content: string; is_secret?: boolean; is_notice?: boolean; custom_fields?: Record<string, string> }
|
||||
): Promise<ActionResult<PostApiData>> {
|
||||
return executeServerAction<PostApiData>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts`),
|
||||
method: 'POST',
|
||||
body: data,
|
||||
errorMessage: '게시글 등록에 실패했습니다.',
|
||||
@@ -56,7 +54,7 @@ export async function updateDynamicBoardPost(
|
||||
data: { title?: string; content?: string; is_secret?: boolean; is_notice?: boolean; custom_fields?: Record<string, string> }
|
||||
): Promise<ActionResult<PostApiData>> {
|
||||
return executeServerAction<PostApiData>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
errorMessage: '게시글 수정에 실패했습니다.',
|
||||
@@ -67,7 +65,7 @@ export async function deleteDynamicBoardPost(
|
||||
boardCode: string, postId: number | string
|
||||
): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
method: 'DELETE',
|
||||
errorMessage: '게시글 삭제에 실패했습니다.',
|
||||
});
|
||||
@@ -79,7 +77,7 @@ export async function getDynamicBoardComments(
|
||||
boardCode: string, postId: number | string
|
||||
): Promise<ActionResult<CommentsApiResponse>> {
|
||||
return executeServerAction<CommentsApiResponse>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}/comments`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}/comments`),
|
||||
errorMessage: '댓글 목록 조회에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
@@ -88,7 +86,7 @@ export async function createDynamicBoardComment(
|
||||
boardCode: string, postId: number | string, content: string
|
||||
): Promise<ActionResult<CommentApiData>> {
|
||||
return executeServerAction<CommentApiData>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}/comments`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}/comments`),
|
||||
method: 'POST',
|
||||
body: { content },
|
||||
errorMessage: '댓글 등록에 실패했습니다.',
|
||||
@@ -99,7 +97,7 @@ export async function updateDynamicBoardComment(
|
||||
boardCode: string, postId: number | string, commentId: number | string, content: string
|
||||
): Promise<ActionResult<CommentApiData>> {
|
||||
return executeServerAction<CommentApiData>({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}/comments/${commentId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}/comments/${commentId}`),
|
||||
method: 'PUT',
|
||||
body: { content },
|
||||
errorMessage: '댓글 수정에 실패했습니다.',
|
||||
@@ -110,8 +108,8 @@ export async function deleteDynamicBoardComment(
|
||||
boardCode: string, postId: number | string, commentId: number | string
|
||||
): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}/comments/${commentId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}/comments/${commentId}`),
|
||||
method: 'DELETE',
|
||||
errorMessage: '댓글 삭제에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import type {
|
||||
PostApiData,
|
||||
PostPaginationResponse,
|
||||
@@ -9,8 +10,6 @@ import type {
|
||||
Post,
|
||||
} from './types';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ===== 변환 =====
|
||||
function transformApiToPost(apiData: PostApiData, boardName?: string): Post {
|
||||
return {
|
||||
@@ -41,25 +40,20 @@ function transformApiToPost(apiData: PostApiData, boardName?: string): Post {
|
||||
};
|
||||
}
|
||||
|
||||
function buildPostFilterParams(filters?: PostFilters): string {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.search) params.append('search', filters.search);
|
||||
if (filters?.is_notice !== undefined) params.append('is_notice', String(filters.is_notice));
|
||||
if (filters?.status) params.append('status', filters.status);
|
||||
if (filters?.per_page) params.append('per_page', String(filters.per_page));
|
||||
if (filters?.page) params.append('page', String(filters.page));
|
||||
if (filters?.board_code) params.append('board_code', filters.board_code);
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
// ===== 게시글 목록 조회 =====
|
||||
export async function getPosts(
|
||||
boardCode: string,
|
||||
filters?: PostFilters
|
||||
): Promise<{ success: boolean; data?: PostPaginationResponse; posts?: Post[]; error?: string; __authError?: boolean }> {
|
||||
const queryString = buildPostFilterParams(filters);
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts${queryString ? `?${queryString}` : ''}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts`, {
|
||||
search: filters?.search,
|
||||
is_notice: filters?.is_notice,
|
||||
status: filters?.status,
|
||||
per_page: filters?.per_page,
|
||||
page: filters?.page,
|
||||
board_code: filters?.board_code,
|
||||
}),
|
||||
transform: (data: PostPaginationResponse) => ({
|
||||
raw: data,
|
||||
posts: data.data.map(post => transformApiToPost(post)),
|
||||
@@ -73,9 +67,15 @@ export async function getPosts(
|
||||
export async function getMyPosts(
|
||||
filters?: PostFilters
|
||||
): Promise<{ success: boolean; data?: PostPaginationResponse; posts?: Post[]; error?: string; __authError?: boolean }> {
|
||||
const queryString = buildPostFilterParams(filters);
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/my-posts${queryString ? `?${queryString}` : ''}`,
|
||||
url: buildApiUrl('/api/v1/my-posts', {
|
||||
search: filters?.search,
|
||||
is_notice: filters?.is_notice,
|
||||
status: filters?.status,
|
||||
per_page: filters?.per_page,
|
||||
page: filters?.page,
|
||||
board_code: filters?.board_code,
|
||||
}),
|
||||
transform: (data: PostPaginationResponse) => ({
|
||||
raw: data,
|
||||
posts: data.data.map(post => transformApiToPost(post)),
|
||||
@@ -88,7 +88,7 @@ export async function getMyPosts(
|
||||
// ===== 게시글 상세 조회 =====
|
||||
export async function getPost(boardCode: string, postId: number | string): Promise<ActionResult<Post>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
transform: (data: PostApiData) => transformApiToPost(data),
|
||||
errorMessage: '게시글을 찾을 수 없습니다.',
|
||||
});
|
||||
@@ -100,7 +100,7 @@ export async function createPost(
|
||||
data: { title: string; content: string; is_notice?: boolean; is_secret?: boolean; custom_fields?: Record<string, string> }
|
||||
): Promise<ActionResult<Post>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts`),
|
||||
method: 'POST',
|
||||
body: data,
|
||||
transform: (d: PostApiData) => transformApiToPost(d),
|
||||
@@ -115,7 +115,7 @@ export async function updatePost(
|
||||
data: { title?: string; content?: string; is_notice?: boolean; is_secret?: boolean; custom_fields?: Record<string, string> }
|
||||
): Promise<ActionResult<Post>> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
transform: (d: PostApiData) => transformApiToPost(d),
|
||||
@@ -126,7 +126,7 @@ export async function updatePost(
|
||||
// ===== 게시글 삭제 =====
|
||||
export async function deletePost(boardCode: string, postId: number | string): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/boards/${boardCode}/posts/${postId}`,
|
||||
url: buildApiUrl(`/api/v1/boards/${boardCode}/posts/${postId}`),
|
||||
method: 'DELETE',
|
||||
errorMessage: '게시글 삭제에 실패했습니다.',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user