refactor(WEB): Server Action 공통화 및 보안 강화
- executeServerAction 공통 유틸 도입으로 actions.ts 대폭 간소화 (50+개 파일) - sanitize 유틸 추가 (XSS 방지) - middleware CSP 헤더 추가 및 Open Redirect 방지 - 프록시 라우트 로깅 개발환경 한정으로 변경 - 프로덕션 불필요 console.log 제거 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,116 +1,40 @@
|
||||
'use server';
|
||||
|
||||
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { serverFetch } from '@/lib/api/fetch-wrapper';
|
||||
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
|
||||
import type { NotificationSettings } from './types';
|
||||
import { DEFAULT_NOTIFICATION_SETTINGS } from './types';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ===== 알림 설정 조회 =====
|
||||
export async function getNotificationSettings(): Promise<{
|
||||
success: boolean;
|
||||
data: NotificationSettings;
|
||||
error?: string;
|
||||
__authError?: boolean;
|
||||
success: boolean; data: NotificationSettings; error?: string; __authError?: boolean;
|
||||
}> {
|
||||
try {
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/settings/notifications`,
|
||||
{
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
}
|
||||
);
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/settings/notifications`,
|
||||
transform: (data: Record<string, unknown>) => transformApiToFrontend(data),
|
||||
errorMessage: '알림 설정 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
data: DEFAULT_NOTIFICATION_SETTINGS,
|
||||
error: error.message,
|
||||
__authError: error.code === 'UNAUTHORIZED',
|
||||
};
|
||||
}
|
||||
|
||||
if (!response || !response.ok) {
|
||||
console.warn('[NotificationActions] GET settings error:', response?.status);
|
||||
return {
|
||||
success: true,
|
||||
data: DEFAULT_NOTIFICATION_SETTINGS,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return {
|
||||
success: true,
|
||||
data: DEFAULT_NOTIFICATION_SETTINGS,
|
||||
};
|
||||
}
|
||||
|
||||
// API → Frontend 변환
|
||||
return {
|
||||
success: true,
|
||||
data: transformApiToFrontend(result.data),
|
||||
};
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[NotificationActions] getNotificationSettings error:', error);
|
||||
return {
|
||||
success: true,
|
||||
data: DEFAULT_NOTIFICATION_SETTINGS,
|
||||
};
|
||||
// 인증 에러는 전파, 그 외 에러는 기본값 반환 (설정 미존재 시 정상 동작)
|
||||
if (result.__authError) {
|
||||
return { success: false, data: DEFAULT_NOTIFICATION_SETTINGS, error: result.error, __authError: true };
|
||||
}
|
||||
if (!result.success || !result.data) {
|
||||
return { success: true, data: DEFAULT_NOTIFICATION_SETTINGS };
|
||||
}
|
||||
return { success: true, data: result.data };
|
||||
}
|
||||
|
||||
// ===== 알림 설정 저장 =====
|
||||
export async function saveNotificationSettings(
|
||||
settings: NotificationSettings
|
||||
): Promise<{ success: boolean; error?: string; __authError?: boolean }> {
|
||||
try {
|
||||
const apiData = transformFrontendToApi(settings);
|
||||
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/settings/notifications`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(apiData),
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
__authError: error.code === 'UNAUTHORIZED',
|
||||
};
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return {
|
||||
success: false,
|
||||
error: '알림 설정 저장에 실패했습니다.',
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: result.message || '알림 설정 저장에 실패했습니다.',
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[NotificationActions] saveNotificationSettings error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.',
|
||||
};
|
||||
}
|
||||
export async function saveNotificationSettings(settings: NotificationSettings): Promise<ActionResult> {
|
||||
return executeServerAction({
|
||||
url: `${API_URL}/api/v1/settings/notifications`,
|
||||
method: 'PUT',
|
||||
body: transformFrontendToApi(settings),
|
||||
errorMessage: '알림 설정 저장에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== API → Frontend 변환 (기본값과 병합) =====
|
||||
|
||||
Reference in New Issue
Block a user