From 5a08c9067f1f197c86c7b49307e39b007490b3e6 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Wed, 6 Aug 2025 16:59:47 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EA=B3=B5=ED=86=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B2=B0=EA=B3=BC=20=EB=82=B4?= =?UTF-8?q?=20ref=5Fchr1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/comm-code/route.ts | 17 ++++++----------- src/types/CommCode.ts | 1 + 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/app/api/comm-code/route.ts b/src/app/api/comm-code/route.ts index 71acdd1..5959fb0 100644 --- a/src/app/api/comm-code/route.ts +++ b/src/app/api/comm-code/route.ts @@ -7,9 +7,8 @@ async function getCommCode(request: NextRequest): Promise { try { const searchParams = request.nextUrl.searchParams const headId = searchParams.get('headId') - if (headId === 'QNA_CD') { - return getQnaCd() - } + + if (!headId) return NextResponse.json({ error: '필수 파라미터가 없습니다' }, { status: 400 }) // @ts-ignore const commHeadData = await prisma.BC_COMM_H.findFirst({ @@ -21,15 +20,10 @@ async function getCommCode(request: NextRequest): Promise { }, }) - if (!commHeadData) { - return NextResponse.json({ error: `${headId}를 찾을 수 없습니다` }, { status: 404 }) - } + if (!commHeadData) return NextResponse.json({ error: `${headId}를 찾을 수 없습니다` }, { status: 404 }) - if (headId === 'SALES_OFFICE_CD') { - return getSaleOffice(commHeadData.HEAD_CD) - } // @ts-ignore - const roofMaterials: CommCode[] = await prisma.BC_COMM_L.findMany({ + const results: CommCode[] = await prisma.BC_COMM_L.findMany({ where: { HEAD_CD: commHeadData.HEAD_CD, }, @@ -37,12 +31,13 @@ async function getCommCode(request: NextRequest): Promise { HEAD_CD: true, CODE: true, CODE_JP: true, + REF_CHR1: true, }, orderBy: { CODE: 'asc', }, }) - return NextResponse.json(roofMaterials) + return NextResponse.json(results) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) diff --git a/src/types/CommCode.ts b/src/types/CommCode.ts index 5847047..fc82431 100644 --- a/src/types/CommCode.ts +++ b/src/types/CommCode.ts @@ -2,4 +2,5 @@ export type CommCode = { headCd: string code: string codeJp: string + refChr1: string } From 5a49b862051353741963f963a4eac0cc18f89bca Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Thu, 7 Aug 2025 14:03:34 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EB=AC=B8=EC=9D=98=20=EC=9C=A0?= =?UTF-8?q?=ED=98=95=20=EA=B3=B5=ED=86=B5=EC=BD=94=EB=93=9C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 구현 되어 있던 공통코드 조회 api 사용 --- src/components/inquiry/RegistForm.tsx | 18 ++++++++--------- src/hooks/useInquiry.ts | 28 +++++++++++++-------------- src/types/Inquiry.ts | 22 ++++++++++++--------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/components/inquiry/RegistForm.tsx b/src/components/inquiry/RegistForm.tsx index 7cf5b9d..460a990 100644 --- a/src/components/inquiry/RegistForm.tsx +++ b/src/components/inquiry/RegistForm.tsx @@ -2,7 +2,7 @@ import { useInquiry } from '@/hooks/useInquiry' import { useSessionStore } from '@/store/session' -import { InquiryRequest } from '@/types/Inquiry' +import { InquiryCommCode, InquiryRequest } from '@/types/Inquiry' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { CONFIRM_MESSAGE, SUCCESS_MESSAGE, useAlertMsg, WARNING_MESSAGE } from '@/hooks/useAlertMsg' @@ -152,15 +152,15 @@ export default function RegistForm() { 選択してください {commonCodeList - .filter((code) => code.headId === 'QNA_CLS_LRG_CD') + .filter((code) => code.headCd === InquiryCommCode.QNA_CLS_LRG_CD.headCd) .map((code) => ( ))} - {commonCodeList.filter((code) => code.refChar1 === inquiryRequest.qnaClsLrgCd).length > 0 && inquiryRequest.qnaClsLrgCd && ( + {commonCodeList.filter((code) => code.refChr1 === inquiryRequest.qnaClsLrgCd).length > 0 && inquiryRequest.qnaClsLrgCd && (
)} - {commonCodeList.filter((code) => code.refChar1 === inquiryRequest.qnaClsMidCd).length > 0 && inquiryRequest.qnaClsMidCd && ( + {commonCodeList.filter((code) => code.refChr1 === inquiryRequest.qnaClsMidCd).length > 0 && inquiryRequest.qnaClsMidCd && (
diff --git a/src/hooks/useInquiry.ts b/src/hooks/useInquiry.ts index 25bb533..cd87401 100644 --- a/src/hooks/useInquiry.ts +++ b/src/hooks/useInquiry.ts @@ -1,11 +1,12 @@ -import { InquiryList, Inquiry, InquirySaveResponse, CommonCode } from '@/types/Inquiry' +import { InquiryList, Inquiry, InquirySaveResponse, InquiryCommCode } from '@/types/Inquiry' import { useAxios } from '@/hooks/useAxios' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useInquiryFilterStore } from '@/store/inquiryFilterStore' import { useMemo } from 'react' import { useRouter } from 'next/navigation' import { useAlertMsg } from '@/hooks/useAlertMsg' - +import { useCommCode } from './useCommCode' +import { CommCode } from '@/types/CommCode' /** * @description 문의사항 관련 기능을 제공하는 커스텀 훅 @@ -20,7 +21,7 @@ import { useAlertMsg } from '@/hooks/useAlertMsg' * @returns {boolean} isSavingInquiry - 문의사항 저장 중 상태 * @returns {Function} saveInquiry - 문의사항 저장 함수 * @returns {Function} downloadFile - 파일 다운로드 함수 - * @returns {CommonCode[]} commonCodeList - 공통 코드 목록 + * @returns {CommCode[]} commonCodeList - 공통 코드 목록 */ export function useInquiry( qnoNo?: number, @@ -33,7 +34,7 @@ export function useInquiry( isSavingInquiry: boolean saveInquiry: (formData: FormData) => Promise downloadFile: (encodeFileNo: number, srcFileNm: string) => Promise - commonCodeList: CommonCode[] + commonCodeList: CommCode[] } { const queryClient = useQueryClient() const { inquiryListRequest, offset, isMyInquiry } = useInquiryFilterStore() @@ -216,7 +217,8 @@ export function useInquiry( } /** - * @description 공통 코드 목록 조회 + * @description 문의 공통 코드 목록 조회 + * - 문의 유형 대분류, 중분류, 소분류 코드 조회 이후 데이터 리로드 하지 않음 * * @returns {Object} 공통 코드 목록 데이터 * @returns {CommonCode[]} data - 공통 코드 목록 @@ -225,15 +227,13 @@ export function useInquiry( const { data: commonCodeList, isLoading: isLoadingCommonCodeList } = useQuery({ queryKey: ['commonCodeList'], queryFn: async () => { - const isListQuery = false - const shouldThrowError = false - - const resp = await tryFunction( - () => axiosInstance(null).get('/api/comm-code', { params: { headId: 'QNA_CD' } }), - isListQuery, - shouldThrowError, - ) - return resp?.data ?? [] + const { getCommCode } = useCommCode() + const resp = await Promise.all([ + getCommCode(InquiryCommCode.QNA_CLS_LRG_CD.headId), + getCommCode(InquiryCommCode.QNA_CLS_MID_CD.headId), + getCommCode(InquiryCommCode.QNA_CLS_SML_CD.headId), + ]) + return resp.flat() }, staleTime: Infinity, gcTime: Infinity, diff --git a/src/types/Inquiry.ts b/src/types/Inquiry.ts index c54d9dc..888ef6e 100644 --- a/src/types/Inquiry.ts +++ b/src/types/Inquiry.ts @@ -180,13 +180,17 @@ export type InquirySaveResponse = { mailYn: string } -/** - * @description 공통 코드 타입 - */ -export type CommonCode = { - headCd: string - headId: string - code: string - name: string - refChar1: string +export const InquiryCommCode = { + QNA_CLS_LRG_CD: { + headCd: '204200', + headId: 'QNA_CLS_LRG_CD', + }, + QNA_CLS_MID_CD: { + headCd: '204300', + headId: 'QNA_CLS_MID_CD', + }, + QNA_CLS_SML_CD: { + headCd: '204400', + headId: 'QNA_CLS_SML_CD', + }, } From 870e3af59776af42e89b85596f782c675e8dd3b2 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Thu, 7 Aug 2025 14:05:21 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=EC=A1=B0=EC=82=AC=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EC=A0=9C=EC=B6=9C=20=EA=B3=B5=ED=86=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 제출 대상 지점의 구분값으로 REF_NUM1 값이 사용되므로 공통 코드 타입 정의에 refNum1 추가 --- src/components/popup/SurveySaleSubmitPopup.tsx | 6 +++--- src/types/CommCode.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/popup/SurveySaleSubmitPopup.tsx b/src/components/popup/SurveySaleSubmitPopup.tsx index 9104989..71b531b 100644 --- a/src/components/popup/SurveySaleSubmitPopup.tsx +++ b/src/components/popup/SurveySaleSubmitPopup.tsx @@ -63,7 +63,8 @@ export default function SurveySaleSubmitPopup() { /** Admin 제출 폼 데이터 삽입 - 1차 판매점*/ if (session?.role === 'Admin') { getCommCode('SALES_OFFICE_CD').then((codes) => { - setCommCodeList(codes) + const filteredCodes = codes.filter((code) => code.refNum1 === '1') + setCommCodeList(filteredCodes) }) setSubmitData((prev) => ({ ...prev, @@ -221,8 +222,7 @@ export default function SurveySaleSubmitPopup() { onChange={(e) => { const selectedOffice = commCodeList.find((item) => item.code === e.target.value) if (selectedOffice) { - //@ts-ignore - const receiver = selectedOffice.REF_CHR1.split(';') + const receiver = selectedOffice.refChr1?.split(';') || [] setSubmitData((prev) => ({ ...prev, receiver: receiver, diff --git a/src/types/CommCode.ts b/src/types/CommCode.ts index fc82431..d12ea08 100644 --- a/src/types/CommCode.ts +++ b/src/types/CommCode.ts @@ -3,4 +3,5 @@ export type CommCode = { code: string codeJp: string refChr1: string + refNum1: string } From 728dcfe928982ec2650f2245f11486b85d96de72 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Thu, 7 Aug 2025 14:05:51 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EA=B3=B5=ED=86=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=ED=95=A8=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/comm-code/route.ts | 67 +--------------------------------- 1 file changed, 2 insertions(+), 65 deletions(-) diff --git a/src/app/api/comm-code/route.ts b/src/app/api/comm-code/route.ts index 5959fb0..d91adf6 100644 --- a/src/app/api/comm-code/route.ts +++ b/src/app/api/comm-code/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' import { prisma } from '@/libs/prisma' -import { CommonCode } from '@/types/Inquiry' +import { CommCode } from '@/types/CommCode' async function getCommCode(request: NextRequest): Promise { try { @@ -32,6 +32,7 @@ async function getCommCode(request: NextRequest): Promise { CODE: true, CODE_JP: true, REF_CHR1: true, + REF_NUM1: true, }, orderBy: { CODE: 'asc', @@ -43,68 +44,4 @@ async function getCommCode(request: NextRequest): Promise { return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) } } - -const getSaleOffice = async (headCode: string) => { - // @ts-ignore - const commCodeSaleOffice: CommCode[] = await prisma.BC_COMM_L.findMany({ - where: { - HEAD_CD: headCode, - REF_NUM1: 1, - }, - select: { - CODE: true, - CODE_JP: true, - REF_CHR1: true, - REF_NUM1: true, - }, - }) - return NextResponse.json(commCodeSaleOffice) -} - -/** - * @description QNA 공통 코드 조회 - * @returns {CommonCode[]} QNA 공통 코드 목록 - */ -const getQnaCd = async () => { - // @ts-ignore - const headCdList: { HEAD_CD: string; HEAD_ID: string }[] = await prisma.BC_COMM_H.findMany({ - where: { - OR: [{ HEAD_ID: { in: ['QNA_CLS_LRG_CD'] } }, { HEAD_ID: { in: ['QNA_CLS_MID_CD'] } }, { HEAD_ID: { in: ['QNA_CLS_SML_CD'] } }], - }, - select: { - HEAD_CD: true, - HEAD_ID: true, - }, - }) - const result: CommonCode[] = [] - // @ts-ignore - const commCodeQna: CommCode[] = await prisma.BC_COMM_L.findMany({ - where: { - HEAD_CD: { - in: headCdList.map((item) => item.HEAD_CD).filter(Boolean), - }, - }, - select: { - HEAD_CD: true, - CODE: true, - CODE_JP: true, - REF_CHR1: true, - }, - }) - commCodeQna.forEach((item) => { - result.push({ - // @ts-ignore - headCd: item.HEAD_CD, - // @ts-ignore - headId: headCdList.find((headCd) => headCd.HEAD_CD === item.HEAD_CD)?.HEAD_ID ?? '', - // @ts-ignore - code: item.CODE, - // @ts-ignore - name: item.CODE_JP, - // @ts-ignore - refChar1: item.REF_CHR1 ?? '', - }) - }) - return NextResponse.json(result) -} export const GET = loggerWrapper(getCommCode) From 7574dffb0b0428f400ceb6e446516e44a89941fa Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Thu, 7 Aug 2025 14:07:41 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20snakecase=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=A0=95=EA=B7=9C=EC=8B=9D=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통코드의 REF_CHR1, REF_NUM1 필드가 정규식 조건에 걸리지 않는 문제로 정규식 조건 수정 --- src/hooks/useAxios.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hooks/useAxios.ts b/src/hooks/useAxios.ts index 27b04a8..b905511 100644 --- a/src/hooks/useAxios.ts +++ b/src/hooks/useAxios.ts @@ -91,15 +91,13 @@ export function useAxios() { // Handle uppercase snake_case (e.g., USER_NAME -> userName) // Handle lowercase snake_case (e.g., user_name -> userName) - if (/^[A-Z_]+$/.test(key) || /^[a-z_]+$/.test(key)) { + if (/^[A-Z0-9_]+$/.test(key) || /^[a-z0-9_]+$/.test(key)) { transformedKey = snakeToCamel(key) } // Handle single uppercase word (e.g., ROLE -> role) - else if (/^[A-Z]+$/.test(key)) { + else if (/^[A-Z0-9]+$/.test(key)) { transformedKey = key.toLowerCase() } - // Preserve existing camelCase - acc[transformedKey] = transformObjectKeys(obj[key]) return acc }, {}) @@ -109,7 +107,7 @@ export function useAxios() { } const snakeToCamel = (str: string): string => { - return str.toLowerCase().replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', '')) + return str.toLowerCase().replace(/_([a-z])/g, (match, letter) => letter.toUpperCase()) } return { From a24b2f338b2d4382f1997e9582e0989c5ff0e27b Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Thu, 7 Aug 2025 14:08:31 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EC=A1=B0=EC=82=AC=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=ED=95=84=EC=88=98=EA=B0=92=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EC=97=90=20=EA=B3=B5=ED=86=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../survey-sale/detail/ButtonForm.tsx | 8 +++---- src/hooks/useSurvey.ts | 23 ++++++++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/components/survey-sale/detail/ButtonForm.tsx b/src/components/survey-sale/detail/ButtonForm.tsx index c5ef64e..31c326c 100644 --- a/src/components/survey-sale/detail/ButtonForm.tsx +++ b/src/components/survey-sale/detail/ButtonForm.tsx @@ -97,12 +97,10 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) { /** 저장 로직 */ const handleSave = (isTemporary: boolean) => { - const emptyField = validateSurveyDetail(data.roof) - if (isTemporary) { tempSaveProcess() } else { - saveProcess(emptyField) + saveProcess() } } @@ -134,7 +132,9 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) { } /** 저장 로직 */ - const saveProcess = async (emptyField: string | null) => { + const saveProcess = async () => { + const emptyField = validateSurveyDetail(data.roof) + if (emptyField?.trim() === '') { showConfirm(CONFIRM_MESSAGE.SAVE_CONFIRM, async () => { await handleSuccessfulSave() diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 28193d0..e61e42c 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -1,4 +1,11 @@ -import type { SubmitTargetResponse, SurveyBasicInfo, SurveyDetailRequest, SurveyRegistRequest } from '@/types/Survey' +import { + radioEtcData, + selectBoxOptions, + type SubmitTargetResponse, + type SurveyBasicInfo, + type SurveyDetailRequest, + type SurveyRegistRequest, +} from '@/types/Survey' import { useMemo } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useSurveyFilterStore } from '@/store/surveyFilterStore' @@ -380,7 +387,7 @@ export function useSurvey( }) /** - * @description 조사 매물 상세 데이터 유효성 검사 + * @description 조사 매물 상세 데이터 필수값 검증 * * @param {SurveyDetailRequest} surveyDetail 검사할 조사 매물 상세 데이터 * @returns {string} 빈 필드 이름 또는 빈 문자열 @@ -395,15 +402,25 @@ export function useSurvey( } const checkRequiredField = (field: string): string => { + /** 기타 (직접입력) 항목이 있는 필드 체크 */ if (ETC_FIELDS.includes(field as (typeof ETC_FIELDS)[number])) { + /** 기타 (직접입력) 항목이 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 */ if ( isEmptyValue(surveyDetail[field as keyof SurveyDetailRequest]) && isEmptyValue(surveyDetail[`${field}Etc` as keyof SurveyDetailRequest]) ) { return field } + /** 직접입력 필드가 기타 선택이 아닌 필드 체크 - 건축연수, 단열재 유무 + * 건축연수 필드가 기축(O) 선택이고 직접입력 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 + * 단열재 유무 필드가 2(있음) 선택이고 직접입력 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 + */ } else if (SPECIAL_CONDITIONS.includes(field as (typeof SPECIAL_CONDITIONS)[number])) { - if (surveyDetail[field as keyof SurveyDetailRequest] === '2' && isEmptyValue(surveyDetail[`${field}Etc` as keyof SurveyDetailRequest])) { + if ( + (surveyDetail.constructionYear === selectBoxOptions.constructionYear[1].code && isEmptyValue(surveyDetail.constructionYearEtc)) || + (surveyDetail.insulationPresence === '2' && isEmptyValue(surveyDetail.insulationPresenceEtc)) + ) { + /** 건축연수, 단열재 유무 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 */ return `${field}Etc` } else if (isEmptyValue(surveyDetail[field as keyof SurveyDetailRequest])) { return field