빌드 오류 수정

This commit is contained in:
2025-12-12 19:09:20 +09:00
parent b746db5638
commit d4424c8fa1
9 changed files with 272 additions and 249 deletions
+2 -1
View File
@@ -23,4 +23,5 @@ apps/fo/.next/**/*
.qodo
pnpm-lock.yaml
settings.json
settings.json
.npmrc
+8 -6
View File
@@ -14,8 +14,8 @@ const serverApiUrl =
const serverApiUrl2 =
process.env.NEXT_PUBLIC_SERVER_API_URL2 ?? serverApiUrl;
// const serverApiUrl3 =
// process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl;
const serverApiUrl3 =
process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl;
// const serverApiUrl4 =
// process.env.NEXT_PUBLIC_SERVER_API_URL4 ?? serverApiUrl;
@@ -110,11 +110,12 @@ export function middleware(req: NextRequest) {
});
}
if (pathname.startsWith("/ic-service")) {
if (pathname.startsWith("/server/ic-service")) {
const requestHeaders = new Headers(req.headers);
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${serverApiUrl5}${pathname}`;
const newUrl = `${serverApiUrl3}${pathname.replace("/server/", "")}${searchParams ? `?${searchParams}` : ""}`;
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
@@ -128,11 +129,12 @@ export function middleware(req: NextRequest) {
});
}
if (pathname.startsWith("/dc-service")) {
if (pathname.startsWith("/server/dc-service")) {
const requestHeaders = new Headers(req.headers);
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${serverApiUrl5}${pathname}`;
const newUrl = `${serverApiUrl3}${pathname.replace("/server/", "")}${searchParams ? `?${searchParams}` : ""}`;
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
+15 -35
View File
@@ -15,41 +15,13 @@ export interface Banner {
}
export const getBannerList = async (siteId: string) => {
const url = `/api/proxy${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`;
console.log("[getBannerList] API 호출 시작 (프록시, 인증 없음):", url);
try {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log("[getBannerList] API 응답 성공:", data);
return data;
} catch (error: any) {
console.error("[getBannerList] API 호출 실패:", {
url,
error,
status: error?.status,
message: error?.message,
});
throw error;
}
const response = await client.get<Banner[]>(
`${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`,
);
return response.data;
};
export const getImageUrlByAtflCd = async (atflCd: string): Promise<string> => {
if (!atflCd) {
console.warn("[getImageUrlByAtflCd] atflCd가 없습니다");
return "";
}
console.log("[getImageUrlByAtflCd] 이미지 URL 조회 시작:", `${ENDPOINT.ATTACH_SERVICE.IMAGES}/atflCd/${atflCd}`);
try {
const response = await client.get<Blob>(
`${ENDPOINT.ATTACH_SERVICE.IMAGES}/atflCd/${atflCd}`,
@@ -61,8 +33,16 @@ export const getImageUrlByAtflCd = async (atflCd: string): Promise<string> => {
const url = URL.createObjectURL(blob);
console.log("[getImageUrlByAtflCd] 이미지 URL 생성 성공:", url);
return url;
} catch (error) {
console.error("[getImageUrlByAtflCd] 이미지 URL 조회 실패:", error);
} catch (error: unknown) {
const err = error as
| { status?: number; message?: string; response?: { status?: number } }
| undefined;
console.error("[getImageUrlByAtflCd] 이미지 URL 조회 실패:", {
atflCd,
error: err,
status: err?.status ?? err?.response?.status,
message: err instanceof Error ? err.message : String(err),
});
throw error;
}
};
};
+8 -6
View File
@@ -1,4 +1,3 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
export interface Support {
@@ -30,12 +29,15 @@ export const getSupportList = async () => {
const data = await response.json();
console.log("[getSupportList] API 응답 성공:", data);
return data;
} catch (error: any) {
console.error("[getSupportList] API 호출 실패:", {
} catch (error: unknown) {
const err = error as
| { status?: number; message?: string; response?: { status?: number } }
| undefined;
console.error("[getBannerList] API 호출 실패:", {
url,
error,
status: error?.status,
message: error?.message,
error: err,
status: err?.status ?? err?.response?.status,
message: err instanceof Error ? err.message : String(err),
});
throw error;
}
+174 -149
View File
@@ -7,155 +7,180 @@ import { getImageUrlByAtflCd, getBannerList, type Banner } from "../api/banner";
import { getCookie } from "@/shared/lib";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
Autoplay,
} from "@common/components";
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
Autoplay,
} from "@common/components";
type BannerImageProps = {
atflCd: string;
bnrSn: number;
lnkgUrlAddr: string;
};
const BannerList: React.FC = () => {
const siteId = getCookie("site-id-key") ?? "";
const BannerImage = ({ atflCd, bnrSn, lnkgUrlAddr }: BannerImageProps) => {
const { data: imageUrl, isLoading } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd],
queryFn: () => getImageUrlByAtflCd(atflCd),
enabled: !!atflCd,
});
const handleClick = () => {
if (lnkgUrlAddr) {
window.open(lnkgUrlAddr, '_blank');
}
};
if (isLoading) {
return (
<div className="relative w-full h-[280px] flex items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">로딩 중...</div>
</div>
);
}
if (!imageUrl) {
return (
<div className="relative w-full h-[280px] flex items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">이미지 없음</div>
</div>
);
}
return (
<div
className="relative w-full h-[280px] cursor-pointer hover:opacity-80 transition-opacity overflow-hidden"
onClick={handleClick}
>
<img src={imageUrl} className="w-full h-full object-cover" alt="배너 이미지" />
</div>
);
};
const { data: bannerList, error: bannerError, isLoading: isLoadingBanners } = useQuery<Banner[]>({
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, siteId],
queryFn: () => getBannerList(siteId),
enabled: !!siteId,
retry: false,
});
const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));
// MAIN 타입 배너만 필터링
const mainBanners = bannerList?.filter((banner) => banner.bnrTypeCd === "BNRMAIN") ?? [];
// 401 에러 처리
if (bannerError) {
const is401Error = bannerError instanceof Error &&
(bannerError.message.includes("401") ||
(bannerError as any)?.response?.status === 401 ||
(bannerError as any)?.status === 401);
if (is401Error) {
console.error("[BannerList] 401 인증 오류:", bannerError);
return (
<div className="card card-border">
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-red-500">인증이 필요합니다. 로그인해주세요.</div>
</div>
</div>
);
}
console.error("[BannerList] 배너 조회 오류:", bannerError);
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">배너를 불러올 수 없습니다</div>
</div>
</div>
);
}
// 로딩 중
if (isLoadingBanners) {
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">배너 로딩 중...</div>
</div>
</div>
);
}
// siteId가 없을 때
if (!siteId) {
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">사이트 ID가 없습니다</div>
</div>
</div>
);
}
return (
<div className="card card-border">
<Carousel
plugins={[plugin.current]}
opts={{ loop: true }}
className="relative"
>
<CarouselContent className="-ml-0">
{mainBanners.length > 0 ? (
mainBanners.map((banner) => (
<CarouselItem key={`banner-${banner.bnrSn}`} className="pl-0 basis-full">
<BannerImage
atflCd={banner.atflCd}
bnrSn={banner.bnrSn}
lnkgUrlAddr={banner.lnkgUrlAddr}
/>
</CarouselItem>
))
) : (
<CarouselItem className="pl-0 basis-full">
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">배너가 없습니다</div>
</div>
</CarouselItem>
)}
</CarouselContent>
<CarouselPrevious className="absolute left-2 top-1/2 !bg-[rgba(255,255,255,0.8)] text-accent-foreground z-10" />
<CarouselNext className="absolute right-2 top-1/2 !bg-[rgba(255,255,255,0.8)] text-accent-foreground z-10" />
</Carousel>
</div>
);
type BannerImageProps = {
atflCd: string;
lnkgUrlAddr: string;
};
export default BannerList;
const BannerList: React.FC = () => {
const siteId = getCookie("site-id-key") ?? "";
const BannerImage = ({ atflCd, lnkgUrlAddr }: BannerImageProps) => {
const { data: imageUrl, isLoading } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd],
queryFn: () => getImageUrlByAtflCd(atflCd),
enabled: !!atflCd,
});
const handleClick = () => {
if (lnkgUrlAddr) {
window.open(lnkgUrlAddr, "_blank");
}
};
if (isLoading) {
return (
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">로딩 중...</div>
</div>
);
}
if (!imageUrl) {
return (
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">이미지 없음</div>
</div>
);
}
return (
<div
className="relative h-[280px] w-full cursor-pointer overflow-hidden transition-opacity hover:opacity-80"
onClick={handleClick}
>
<div
className="h-full w-full bg-cover bg-center"
style={{ backgroundImage: `url(${imageUrl})` }}
role="img"
aria-label="배너 이미지"
/>
</div>
);
};
const {
data: bannerList,
error: bannerError,
isLoading: isLoadingBanners,
} = useQuery<Banner[]>({
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, siteId],
queryFn: () => getBannerList(siteId),
enabled: !!siteId,
retry: false,
});
const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));
// MAIN 타입 배너만 필터링
const mainBanners =
bannerList?.filter((banner) => banner.bnrTypeCd === "BNRMAIN") ?? [];
// 에러 상태 판별 헬퍼 함수
const getErrorStatus = (e: unknown): number | undefined => {
if (!e || typeof e !== "object") return undefined;
const maybe = e as { status?: number; response?: { status?: number } };
return maybe.status ?? maybe.response?.status;
};
// 401 에러 처리
if (bannerError) {
const is401Error =
(bannerError instanceof Error && bannerError.message.includes("401")) ||
getErrorStatus(bannerError) === 401;
if (is401Error) {
console.error("[BannerList] 401 인증 오류:", bannerError);
return (
<div className="card card-border">
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-red-500">
인증이 필요합니다. 로그인해주세요.
</div>
</div>
</div>
);
}
console.error("[BannerList] 배너 조회 오류:", bannerError);
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">
배너를 불러올 수 없습니다
</div>
</div>
</div>
);
}
// 로딩 중
if (isLoadingBanners) {
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">배너 로딩 중...</div>
</div>
</div>
);
}
// siteId가 없을 때
if (!siteId) {
return (
<div className="card card-border">
<div className="relative flex h-[240px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">
사이트 ID가 없습니다
</div>
</div>
</div>
);
}
return (
<div className="card card-border">
<Carousel
plugins={[plugin.current]}
opts={{ loop: true }}
className="relative"
>
<CarouselContent className="-ml-0">
{mainBanners.length > 0 ? (
mainBanners.map((banner) => (
<CarouselItem
key={`banner-${banner.bnrSn}`}
className="basis-full pl-0"
>
<BannerImage
atflCd={banner.atflCd}
lnkgUrlAddr={banner.lnkgUrlAddr}
/>
</CarouselItem>
))
) : (
<CarouselItem className="basis-full pl-0">
<div className="relative flex h-[280px] w-full items-center justify-center bg-accent">
<div className="text-sm text-muted-foreground">
배너가 없습니다
</div>
</div>
</CarouselItem>
)}
</CarouselContent>
<CarouselPrevious className="absolute left-2 top-1/2 z-10 !bg-[rgba(255,255,255,0.8)] text-accent-foreground" />
<CarouselNext className="absolute right-2 top-1/2 z-10 !bg-[rgba(255,255,255,0.8)] text-accent-foreground" />
</Carousel>
</div>
);
};
export default BannerList;
@@ -9,10 +9,14 @@ import { useDataTable } from "@/shared/lib";
import {
noticePostTableColumns,
} from "../model/table-columns";
import { Button } from "@common/components";
const NoticeList: React.FC = () => {
const router = useRouter();
// #region agent log
fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:16',message:'Router initialized',data:{routerExists:!!router},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'E'})}).catch(()=>{});
// #endregion
const noticePostQuery = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
queryFn: () =>
@@ -28,6 +32,25 @@ const NoticeList: React.FC = () => {
router.push(`/bbs/bbsnotice/${row.bbsId}/${row.pstNo}`);
};
const handleMoreClick = () => {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:32',message:'Button click handler called',data:{targetPath:'/cms/bbsnotice/7'},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
// #endregion
try {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:35',message:'Before router.push',data:{routerExists:!!router,path:'/cms/bbsnotice/7'},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
// #endregion
router.push('/cms/bbsnotice/7');
// #region agent log
fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:43',message:'After router.push',data:{navigationCalled:true},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
// #endregion
} catch (error) {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:41',message:'Router.push error',data:{error:error instanceof Error?error.message:String(error)},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
// #endregion
}
};
const { tableComponent: noticePostTableComponent } = useDataTable({
query: noticePostQuery,
tableOptions: {
@@ -44,6 +67,7 @@ const NoticeList: React.FC = () => {
<div className="card card-border !p-0">
<div className="card-header !m-0 !border-none text-foreground">
<h3 className="text-2xl font-medium">공지사항</h3>
<Button className="h-[3.2rem]" onClick={handleMoreClick}>더보기</Button>
</div>
{noticePostTableComponent}
</div>
+16 -22
View File
@@ -1,34 +1,28 @@
"use client";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { getCoreRowModel } from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import {
supportTableColumns,
} from "../model/support-columns";
import { supportTableColumns } from "../model/support-columns";
import { getSupportList } from "../api/support";
const SupportList: React.FC = () => {
const router = useRouter();
const supportQuery = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
queryFn: () =>
getSupportList(),
});
const supportQuery = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
queryFn: () => getSupportList(),
});
const { tableComponent: supportTableComponent } = useDataTable({
query: supportQuery,
tableOptions: {
columns: supportTableColumns,
getCoreRowModel: getCoreRowModel(),
},
dataTableOptions: {
variant: "line",
},
});
const { tableComponent: supportTableComponent } = useDataTable({
query: supportQuery,
tableOptions: {
columns: supportTableColumns,
getCoreRowModel: getCoreRowModel(),
},
dataTableOptions: {
variant: "line",
},
});
return (
<div className="card card-border !p-0">
@@ -40,4 +34,4 @@ const SupportList: React.FC = () => {
);
};
export default SupportList;
export default SupportList;
+1 -6
View File
@@ -1,4 +1,3 @@
import { PageTitle } from "@common/components";
import { useMenuStore } from "../lib/use-menu-store";
function PageLayout({ children }: { children: React.ReactNode }) {
@@ -8,11 +7,7 @@ function PageLayout({ children }: { children: React.ReactNode }) {
?.split(">")
.map((path) => path.trim());
return (
<div className="h-auto min-h-full bg-container pb-2">
{children}
</div>
);
return <div className="h-auto min-h-full bg-container pb-2">{children}</div>;
}
export { PageLayout };
+24 -24
View File
@@ -170,10 +170,10 @@ importers:
version: 5.28.5
eslint:
specifier: ^9.0.0
version: 9.39.1(jiti@1.21.7)
version: 9.39.1(jiti@2.6.1)
eslint-config-next:
specifier: 15.4.0
version: 15.4.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
version: 15.4.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
postcss:
specifier: ^8.4.49
version: 8.5.6
@@ -300,10 +300,10 @@ importers:
version: 5.28.5
eslint:
specifier: ^9
version: 9.39.1(jiti@2.6.1)
version: 9.39.1(jiti@1.21.7)
eslint-config-next:
specifier: 15.1.7
version: 15.1.7(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
version: 15.1.7(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
postcss:
specifier: ^8
version: 8.5.6
@@ -10080,6 +10080,26 @@ snapshots:
optionalDependencies:
source-map: 0.7.6
eslint-config-next@15.1.7(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 15.1.7
'@rushstack/eslint-patch': 1.15.0
'@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
eslint: 9.39.1(jiti@1.21.7)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@1.21.7))
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- eslint-import-resolver-webpack
- eslint-plugin-import-x
- supports-color
eslint-config-next@15.1.7(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 15.1.7
@@ -10100,26 +10120,6 @@ snapshots:
- eslint-plugin-import-x
- supports-color
eslint-config-next@15.4.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 15.4.0
'@rushstack/eslint-patch': 1.15.0
'@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
eslint: 9.39.1(jiti@1.21.7)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@1.21.7))
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- eslint-import-resolver-webpack
- eslint-plugin-import-x
- supports-color
eslint-config-next@15.4.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 15.4.0