From 2edde77baeab2a4ee9abdf43eb75a018cf1763a1 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Tue, 17 Jun 2025 14:45:31 +0900 Subject: [PATCH] refactor: add ErrorMessages Enum for handle api error --- src/app/api/qna/detail/route.ts | 6 +-- src/app/api/qna/file/route.ts | 47 +++-------------------- src/app/api/qna/list/route.ts | 11 +++--- src/app/api/qna/save/route.ts | 8 ++-- src/app/api/submission/admin-sub/route.ts | 14 +++++-- src/app/api/submission/admin/route.ts | 10 +++-- src/app/api/submission/builder/route.ts | 10 ++++- src/app/api/submission/super/route.ts | 40 ++++++------------- src/app/api/survey-sales/[id]/route.ts | 39 ++++++++----------- src/app/api/survey-sales/route.ts | 19 +++++---- src/utils/common-utils.js | 16 ++++++++ 11 files changed, 92 insertions(+), 128 deletions(-) diff --git a/src/app/api/qna/detail/route.ts b/src/app/api/qna/detail/route.ts index 1ac937c..d1f8078 100644 --- a/src/app/api/qna/detail/route.ts +++ b/src/app/api/qna/detail/route.ts @@ -1,7 +1,8 @@ -import { queryStringFormatter } from '@/utils/common-utils' +import { ERROR_MESSAGES, queryStringFormatter } from '@/utils/common-utils' import axios from 'axios' import { NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' +import { HttpStatusCode } from 'axios' async function getQnaDetail(request: Request): Promise { const { searchParams } = new URL(request.url) @@ -19,8 +20,7 @@ async function getQnaDetail(request: Request): Promise { } return NextResponse.json({ error: response.data.result }, { status: response.status }) } catch (error: any) { - console.error(error.response) - return NextResponse.json({ error: 'route error' }, { status: 500 }) + return NextResponse.json({ error: error.response.data.result ?? ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/qna/file/route.ts b/src/app/api/qna/file/route.ts index 49c7209..e050c3c 100644 --- a/src/app/api/qna/file/route.ts +++ b/src/app/api/qna/file/route.ts @@ -1,43 +1,7 @@ -import axios from 'axios' +import { HttpStatusCode } from 'axios' import { NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' - -// export async function GET(request: Request) { -// const { searchParams } = new URL(request.url) -// const encodeFileNo = searchParams.get('encodeFileNo') -// const srcFileNm = searchParams.get('srcFileNm') - -// if (!encodeFileNo) { -// return NextResponse.json({ error: 'encodeFileNo is required' }, { status: 400 }) -// } - -// try { -// const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/file/downloadFile2`, { -// params: { -// encodeFileNo, -// }, -// responseType: 'arraybuffer', -// }) - -// if (response.headers['content-type'] === 'text/html;charset=utf-8') { -// return NextResponse.json({ error: 'file not found' }, { status: 404 }) -// } - -// const contentType = response.headers['content-type'] || 'application/octet-stream' -// const contentDisposition = response.headers['content-disposition'] || 'inline' - -// return new NextResponse(response.data, { -// status: 200, -// headers: { -// 'Content-Type': contentType, -// 'Content-Disposition': contentDisposition, -// }, -// }) -// } catch (error: any) { -// console.error('File download error:', error) -// return NextResponse.json({ error: error.response?.data || 'Failed to download file' }, { status: 500 }) -// } -// } +import { ERROR_MESSAGES } from '@/utils/common-utils' async function downloadFile(request: Request): Promise { const { searchParams } = new URL(request.url) @@ -45,7 +9,7 @@ async function downloadFile(request: Request): Promise { const srcFileNm = searchParams.get('srcFileNm') || 'downloaded-file' if (!encodeFileNo) { - return NextResponse.json({ error: 'encodeFileNo is required' }, { status: 400 }) + return NextResponse.json({ error: ERROR_MESSAGES.BAD_REQUEST }, { status: HttpStatusCode.BadRequest }) } const url = `${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/file/downloadFile2?encodeFileNo=${encodeFileNo}` @@ -54,7 +18,7 @@ async function downloadFile(request: Request): Promise { const resp = await fetch(url) if (!resp.ok) { - return NextResponse.json({ error: 'Failed to download file' }, { status: 500 }) + return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } const contentType = resp.headers.get('content-type') || 'application/octet-stream' @@ -68,8 +32,7 @@ async function downloadFile(request: Request): Promise { }, }) } catch (error: any) { - console.error('File download error:', error) - return NextResponse.json({ error: error.response?.data || 'Failed to download file' }, { status: 500 }) + return NextResponse.json({ error: error.response?.data ?? ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/qna/list/route.ts b/src/app/api/qna/list/route.ts index 12cf96e..7e8807d 100644 --- a/src/app/api/qna/list/route.ts +++ b/src/app/api/qna/list/route.ts @@ -1,6 +1,6 @@ -import axios from 'axios' +import axios, { HttpStatusCode } from 'axios' import { NextResponse } from 'next/server' -import { queryStringFormatter } from '@/utils/common-utils' +import { ERROR_MESSAGES, queryStringFormatter } from '@/utils/common-utils' import { getIronSession } from 'iron-session' import { cookies } from 'next/headers' import { loggerWrapper } from '@/libs/api-wrapper' @@ -12,7 +12,7 @@ async function getQnaList(request: Request): Promise { const session = await getIronSession(cookieStore, sessionOptions) if (!session.isLoggedIn) { - return NextResponse.json({ error: 'ログインしていません。' }, { status: 401 }) + return NextResponse.json({ error: ERROR_MESSAGES.UNAUTHORIZED }, { status: HttpStatusCode.Unauthorized }) } const { searchParams } = new URL(request.url) @@ -32,10 +32,9 @@ async function getQnaList(request: Request): Promise { if (response.status === 200) { return NextResponse.json(response.data) } - return NextResponse.json({ error: 'Failed to fetch qna list' }, { status: response.status }) + return NextResponse.json({ error: response.data.result }, { status: response.status }) } catch (error: any) { - console.error('Error fetching qna list:', error.response.data) - return NextResponse.json({ error: 'route error' }, { status: 500 }) + return NextResponse.json({ error: error.response.data.result ?? ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/qna/save/route.ts b/src/app/api/qna/save/route.ts index 8cd1ad5..429db83 100644 --- a/src/app/api/qna/save/route.ts +++ b/src/app/api/qna/save/route.ts @@ -1,6 +1,7 @@ -import axios from 'axios' +import axios, { HttpStatusCode } from 'axios' import { NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' +import { ERROR_MESSAGES } from '@/utils/common-utils' async function setQna(request: Request): Promise { const formData = await request.formData() @@ -14,10 +15,9 @@ async function setQna(request: Request): Promise { if (response.status === 200) { return NextResponse.json(response.data) } - return NextResponse.json({ error: response.data }, { status: response.status }) + return NextResponse.json({ error: response.data.result }, { status: response.status }) } catch (error: any) { - console.error('error:: ', error.response) - return NextResponse.json({ error: 'Failed to save qna' }, { status: 500 }) + return NextResponse.json({ error: error.response.data.result ?? ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/submission/admin-sub/route.ts b/src/app/api/submission/admin-sub/route.ts index 345a3ff..53812f8 100644 --- a/src/app/api/submission/admin-sub/route.ts +++ b/src/app/api/submission/admin-sub/route.ts @@ -2,12 +2,18 @@ import { prisma } from '@/libs/prisma' import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' import { SubmitTargetResponse } from '@/types/Survey' +import { ERROR_MESSAGES } from '@/utils/common-utils' +import { HttpStatusCode } from 'axios' // 2차점이 자신에게 매핑 된 1차 판매점과 관리자 정보 조회 async function getSubMissionAdminSub(request: NextRequest): Promise { try { const { searchParams } = new URL(request.url) - const id = searchParams.get('id') + const storeId = searchParams.get('storeId') + + if (!storeId) { + return NextResponse.json({ error: ERROR_MESSAGES.BAD_REQUEST }, { status: HttpStatusCode.BadRequest }) + } const query = ` OPEN SYMMETRIC KEY SYMMETRICKEY DECRYPTION BY CERTIFICATE CERTI_QSPJP; @@ -23,7 +29,7 @@ async function getSubMissionAdminSub(request: NextRequest): Promise { try { const { searchParams } = new URL(request.url) - const id = searchParams.get('id') + const storeId = searchParams.get('storeId') const query = ` OPEN SYMMETRIC KEY SYMMETRICKEY DECRYPTION BY CERTIFICATE CERTI_QSPJP; @@ -32,7 +34,7 @@ async function getSubmissionAdmin(request: NextRequest): Promise { AND BCL.HEAD_CD = '103200' AND BCL.DEL_YN = 'N' WHERE MCSA.COMP_CD = '5200' - AND MCSA.STORE_ID = 'A03' + AND MCSA.STORE_ID = '${storeId}' AND MCSA.DEL_YN = 'N' ; CLOSE SYMMETRIC KEY SYMMETRICKEY; @@ -40,8 +42,8 @@ async function getSubmissionAdmin(request: NextRequest): Promise { const data: SuperPerson[] = await prisma.$queryRawUnsafe(query) return NextResponse.json(data) } catch (error) { - console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }) + console.error('❌ API ROUTE ERROR:', error) + return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/submission/builder/route.ts b/src/app/api/submission/builder/route.ts index cf43b08..6c831f6 100644 --- a/src/app/api/submission/builder/route.ts +++ b/src/app/api/submission/builder/route.ts @@ -2,6 +2,8 @@ import { prisma } from '@/libs/prisma' import { SubmitTargetResponse } from '@/types/Survey' import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' +import { ERROR_MESSAGES } from '@/utils/common-utils' +import { HttpStatusCode } from 'axios' // 2차점의 시공권한 user가 해당 판매점의 관리자 정보 조회 // N == 일반유저, S == 수퍼유저, B == 시공권한유저 @@ -10,6 +12,10 @@ async function getSubmissionBuilder(request: NextRequest): Promise const { searchParams } = new URL(request.url) const id = searchParams.get('id') + if (!id) { + return NextResponse.json({ error: ERROR_MESSAGES.BAD_REQUEST }, { status: HttpStatusCode.BadRequest }) + } + const query = ` OPEN SYMMETRIC KEY SYMMETRICKEY DECRYPTION BY CERTIFICATE CERTI_QSPJP; SELECT @@ -33,8 +39,8 @@ async function getSubmissionBuilder(request: NextRequest): Promise const data: SubmitTargetResponse[] = await prisma.$queryRawUnsafe(query) return NextResponse.json(data) } catch (error) { - console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }) + console.error('❌ API ROUTE ERROR:', error) + return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/submission/super/route.ts b/src/app/api/submission/super/route.ts index c950dfe..76c5fc9 100644 --- a/src/app/api/submission/super/route.ts +++ b/src/app/api/submission/super/route.ts @@ -1,40 +1,22 @@ -import { prisma } from '@/libs/prisma' import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' - -type SuperPerson = { - storeId: string - userId: string - eMail: string -} +import { ERROR_MESSAGES } from '@/utils/common-utils' +import { HttpStatusCode } from 'axios' +import { SubmissionService } from '../service' async function getSubmissionSuper(request: NextRequest): Promise { try { const { searchParams } = new URL(request.url) - const id = searchParams.get('id') - - const query = ` - OPEN SYMMETRIC KEY SYMMETRICKEY DECRYPTION BY CERTIFICATE CERTI_QSPJP; - SELECT - MCSA.STORE_ID - , BU.USER_ID - , CONVERT(NVARCHAR(100), DecryptByKey(BU.E_MAIL)) AS E_MAIL - FROM MS_CUST_STOREID_ADDITNL MCSA WITH(NOLOCK) - LEFT OUTER JOIN BC_USER bu WITH(NOLOCK) - ON MCSA.COMP_CD = BU.COMP_CD - AND MCSA.KAM_ID = BU.KAM_ID - AND BU.STAT_CD = 'A' - AND BU.DEL_YN = 'N' - WHERE MCSA.COMP_CD = '5200' - AND MCSA.STORE_ID = 'A03' - AND MCSA.DEL_YN = 'N'; - CLOSE SYMMETRIC KEY SYMMETRICKEY; - ` - const data: SuperPerson[] = await prisma.$queryRawUnsafe(query) + const storeId = searchParams.get('storeId') + if (!storeId) { + return NextResponse.json({ error: ERROR_MESSAGES.BAD_REQUEST }, { status: HttpStatusCode.BadRequest }) + } + const submissionService = new SubmissionService(storeId, 'Super') + const data = await submissionService.getSubmissionTarget() return NextResponse.json(data) } catch (error) { - console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error); - return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }); + console.error('❌ API ROUTE ERROR:', error) + return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index c1a76bc..a50c146 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -1,22 +1,13 @@ import { NextRequest, NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' -import { convertToSnakeCase } from '@/utils/common-utils' +import { convertToSnakeCase, ERROR_MESSAGES } from '@/utils/common-utils' import { getIronSession } from 'iron-session' import { sessionOptions } from '@/libs/session' import { cookies } from 'next/headers' import type { SessionData } from '@/types/Auth' import { Prisma } from '@prisma/client' import { loggerWrapper } from '@/libs/api-wrapper' - -/** - * @description 조사 매물 조회 에러 메시지 - */ -const ERROR_MESSAGES = { - NOT_FOUND: 'データが見つかりません。', - UNAUTHORIZED: 'Unauthorized', - NO_PERMISSION: '該当物件の照会権限がありません。', - FETCH_ERROR: 'データの取得に失敗しました。', -} as const +import { HttpStatusCode } from 'axios' /** * @description T01 조회 권한 체크 @@ -139,7 +130,7 @@ async function getSurveySaleDetail(request: NextRequest): Promise const survey = await fetchSurvey(Number(id)) if (!survey) { - return NextResponse.json({ error: ERROR_MESSAGES.NOT_FOUND }, { status: 404 }) + return NextResponse.json({ error: ERROR_MESSAGES.NOT_FOUND }, { status: HttpStatusCode.NotFound }) } /** pdf 데이터 요청 여부, 권한 여부 확인 */ @@ -149,14 +140,14 @@ async function getSurveySaleDetail(request: NextRequest): Promise /** 로그인 여부 확인 */ if (!session?.isLoggedIn || session?.role === null) { - return NextResponse.json({ error: ERROR_MESSAGES.UNAUTHORIZED }, { status: 401 }) + return NextResponse.json({ error: ERROR_MESSAGES.UNAUTHORIZED }, { status: HttpStatusCode.Unauthorized }) } /** 권한 없음 */ - return NextResponse.json({ error: ERROR_MESSAGES.NO_PERMISSION }, { status: 403 }) + return NextResponse.json({ error: ERROR_MESSAGES.NO_PERMISSION }, { status: HttpStatusCode.Forbidden }) } catch (error) { - console.error('Error fetching survey:', error) - return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: 500 }) + console.error('❌ API ROUTE ERROR:', error) + return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError }) } } @@ -254,8 +245,8 @@ async function updateSurveySaleDetail(request: NextRequest): Promise { return obj; } + +/** + * @description 조사 매물 조회 에러 메시지 + */ +export const ERROR_MESSAGES = { + /** 데이터를 찾을 수 없습니다. */ + NOT_FOUND: 'データが見つかりません。', + /** 승인되지 않았습니다. */ + UNAUTHORIZED: '承認されていません。', + /** 권한이 없습니다. */ + NO_PERMISSION: '権限がありません。', + /** 데이터의 조회에 실패했습니다. */ + FETCH_ERROR: 'データの取得に失敗しました。', + /** 잘못된 요청입니다. */ + BAD_REQUEST: '間違ったリクエストです。', +}