refactor: add ErrorMessages Enum for handle api error

This commit is contained in:
Dayoung 2025-06-17 14:45:31 +09:00
parent 17d306bb11
commit 2edde77bae
11 changed files with 92 additions and 128 deletions

View File

@ -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<NextResponse> {
const { searchParams } = new URL(request.url)
@ -19,8 +20,7 @@ async function getQnaDetail(request: Request): Promise<NextResponse> {
}
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 })
}
}

View File

@ -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<NextResponse> {
const { searchParams } = new URL(request.url)
@ -45,7 +9,7 @@ async function downloadFile(request: Request): Promise<NextResponse> {
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<NextResponse> {
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<NextResponse> {
},
})
} 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 })
}
}

View File

@ -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<NextResponse> {
const session = await getIronSession<SessionData>(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<NextResponse> {
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 })
}
}

View File

@ -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<NextResponse> {
const formData = await request.formData()
@ -14,10 +15,9 @@ async function setQna(request: Request): Promise<NextResponse> {
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 })
}
}

View File

@ -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<NextResponse> {
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<NextResponse
AND MCS.STORE_ID = MCP.STORE_ID
AND MCP.DEL_YN = 'N'
WHERE MCS.COMP_CD = '5200'
AND MCS.STORE_ID = (SELECT STORE_ID FROM MS_CUST_AGENCY_STOREID WHERE AGENCY_STORE_ID = '${id}' AND DEL_YN = 'N')
AND MCS.STORE_ID = (SELECT STORE_ID FROM MS_CUST_AGENCY_STOREID WHERE AGENCY_STORE_ID = '${storeId}' AND DEL_YN = 'N')
AND MCP.EMAIL IS NOT NULL
AND MCS.DEL_YN = 'N';
CLOSE SYMMETRIC KEY SYMMETRICKEY;
@ -31,8 +37,8 @@ async function getSubMissionAdminSub(request: NextRequest): Promise<NextResponse
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 })
}
}

View File

@ -1,6 +1,8 @@
import { prisma } from '@/libs/prisma'
import { NextRequest, NextResponse } from 'next/server'
import { loggerWrapper } from '@/libs/api-wrapper'
import { ERROR_MESSAGES } from '@/utils/common-utils'
import { HttpStatusCode } from 'axios'
type SuperPerson = {
storeId: string
@ -12,7 +14,7 @@ type SuperPerson = {
async function getSubmissionAdmin(request: NextRequest): Promise<NextResponse> {
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<NextResponse> {
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<NextResponse> {
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 })
}
}

View File

@ -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<NextResponse>
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<NextResponse>
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 })
}
}

View File

@ -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<NextResponse> {
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 })
}
}

View File

@ -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<NextResponse>
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<NextResponse>
/** 로그인 여부 확인 */
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<NextRespons
})
return NextResponse.json(survey)
} catch (error) {
console.error('Error updating survey:', error)
return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 })
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
@ -303,10 +294,10 @@ async function deleteSurveySaleDetail(request: NextRequest): Promise<NextRespons
})
})
return NextResponse.json({ message: 'success' })
return NextResponse.json({ status: HttpStatusCode.Ok })
} catch (error) {
console.error('Error deleting survey:', error)
return NextResponse.json({ error: 'データ削除に失敗しました。' }, { status: 500 })
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
@ -360,10 +351,10 @@ async function submitSurveySaleDetail(request: NextRequest): Promise<NextRespons
UPT_DT: new Date(),
},
})
return NextResponse.json({ message: 'Survey confirmed successfully', data: survey })
return NextResponse.json({ status: HttpStatusCode.Ok, data: survey })
} catch (error) {
console.error('Error updating survey:', error)
return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 })
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}

View File

@ -1,7 +1,8 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
import { convertToSnakeCase } from '@/utils/common-utils'
import { convertToSnakeCase, ERROR_MESSAGES } from '@/utils/common-utils'
import { loggerWrapper } from '@/libs/api-wrapper'
import { HttpStatusCode } from 'axios'
/**
* @description
*/
@ -245,8 +246,8 @@ async function getSurveySales(request: Request) {
const count = await prisma.SD_SURVEY_SALES_BASIC_INFO.count({ where })
return NextResponse.json({ data: { data: surveys, count: count } })
} 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 })
}
}
@ -291,12 +292,10 @@ async function updateSurveySales(request: Request) {
data: detailInfo,
})
return NextResponse.json({
message: 'Success Update Survey',
})
return NextResponse.json({ status: HttpStatusCode.Ok })
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Fail Update Survey' }, { status: 500 })
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
@ -390,8 +389,8 @@ async function createSurveySales(request: Request) {
})
return NextResponse.json(result)
} 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 })
}
}

View File

@ -210,3 +210,19 @@ export const convertToSnakeCase = (obj) => {
return obj;
}
/**
* @description 조사 매물 조회 에러 메시지
*/
export const ERROR_MESSAGES = {
/** 데이터를 찾을 수 없습니다. */
NOT_FOUND: 'データが見つかりません。',
/** 승인되지 않았습니다. */
UNAUTHORIZED: '承認されていません。',
/** 권한이 없습니다. */
NO_PERMISSION: '権限がありません。',
/** 데이터의 조회에 실패했습니다. */
FETCH_ERROR: 'データの取得に失敗しました。',
/** 잘못된 요청입니다. */
BAD_REQUEST: '間違ったリクエストです。',
}