diff --git a/apps/common/features/.gitignore b/apps/common/features/.gitignore
index e8d8437ee..b660a552e 100644
--- a/apps/common/features/.gitignore
+++ b/apps/common/features/.gitignore
@@ -37,4 +37,5 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
-.env*
\ No newline at end of file
+.env*
+__ENV__.js
\ No newline at end of file
diff --git a/apps/common/features/cli.js b/apps/common/features/cli.js
new file mode 100644
index 000000000..9f165f497
--- /dev/null
+++ b/apps/common/features/cli.js
@@ -0,0 +1,44 @@
+import { findUp } from "find-up";
+import { config } from "dotenv";
+import fs from "fs";
+import yargs from "yargs";
+import { hideBin } from "yargs/helpers";
+
+async function parseDotenv(nodeEnv) {
+ const envFilePath = await findUp(`.env.${nodeEnv}`);
+
+ const parsedEnv = config({ path: envFilePath }).parsed || {};
+
+ return parsedEnv;
+}
+
+function writeEnv(parsedEnv) {
+ const scriptFilePath = `${fs.realpathSync(process.cwd())}/public/__ENV__.js`;
+
+ fs.writeFileSync(
+ scriptFilePath,
+ `window.__ENV__ = ${JSON.stringify(parsedEnv)}`,
+ );
+}
+
+yargs(hideBin(process.argv))
+ .command(
+ "next-env",
+ "Create Next.js runtime environment js",
+ function builder(y) {
+ return y.option("env", {
+ alias: "e",
+ type: "string",
+ description: "Environment name(ex: development, production)",
+ });
+ },
+ async function handler(args) {
+ const nodeEnv = args.e || args.env || "development";
+
+ const parsedEnv = await parseDotenv(nodeEnv); // dotenv 파싱
+ writeEnv(parsedEnv); // 환경 변수 스크립트 파일 생성
+
+ return parsedEnv;
+ },
+ )
+ .parse();
diff --git a/apps/common/features/package.json b/apps/common/features/package.json
index 64051045c..8b62e9ffa 100644
--- a/apps/common/features/package.json
+++ b/apps/common/features/package.json
@@ -1,11 +1,12 @@
{
"name": "features",
"version": "0.1.0",
+ "type": "module",
"private": true,
"scripts": {
- "dev": "next dev -p 13000",
+ "dev": "node cli.js next-env -e ${NODE_ENV:-development} && next dev -p 13000",
"build": "next build",
- "start": "next start -p 13000",
+ "start": "node cli.js next-env -e ${NODE_ENV:-production} && next start -p 13000",
"lint": "next lint"
},
"dependencies": {
@@ -27,6 +28,8 @@
"axios": "^1.8.1",
"chart.js": "^4.4.8",
"dayjs": "^1.11.13",
+ "dotenv": "^17.2.1",
+ "find-up": "^7.0.0",
"js-cookie": "^3.0.5",
"next": "15.3.0",
"next-themes": "^0.4.6",
@@ -44,6 +47,7 @@
"@types/js-cookie": "^3.0.6",
"@types/node": "^22.13.1",
"@types/webpack": "^5.28.5",
+ "@types/yargs": "^17.0.33",
"eslint": "^9",
"eslint-config-next": "15.1.7",
"postcss": "^8",
diff --git a/apps/common/features/src/app/index.tsx b/apps/common/features/src/app/index.tsx
index 692b55ef0..eb6e75d00 100644
--- a/apps/common/features/src/app/index.tsx
+++ b/apps/common/features/src/app/index.tsx
@@ -4,12 +4,16 @@ import "@fontsource-variable/noto-sans-kr";
import { AppProvider } from "./providers";
import { cookies } from "next/headers";
+import Script from "next/script";
const App = async ({ children }: Readonly<{ children: React.ReactNode }>) => {
const theme = (await cookies()).get("site-theme-key");
return (
+
+
+
{children}
diff --git a/apps/common/features/src/shared/config/api.ts b/apps/common/features/src/shared/config/api.ts
index 67b8948d7..df4c0da7d 100644
--- a/apps/common/features/src/shared/config/api.ts
+++ b/apps/common/features/src/shared/config/api.ts
@@ -1,6 +1,10 @@
-import { PROXY_HOST } from "./env";
+"use client";
-export const BASE_URL = `${PROXY_HOST}/server`;
+import { getCookie, getEnv } from "../lib";
+
+const SITE_ID = getCookie("site-id-key") ?? "";
+const PROXY_HOST_KEY = `NEXT_PUBLIC_PROXY_HOST_${SITE_ID}`;
+export const BASE_URL = `${getEnv(PROXY_HOST_KEY)}/server`;
export const ENDPOINT = {
ATTACH_SERVICE: {
diff --git a/apps/common/features/src/shared/lib/env.ts b/apps/common/features/src/shared/lib/env.ts
new file mode 100644
index 000000000..4ad7ad5a2
--- /dev/null
+++ b/apps/common/features/src/shared/lib/env.ts
@@ -0,0 +1,14 @@
+declare global {
+ interface Window {
+ __ENV__?: Record;
+ }
+}
+
+export function getEnvConfig(): Record {
+ if (typeof window === "undefined") return {};
+ return window.__ENV__ ?? {};
+}
+
+export function getEnv(key: string): string | undefined {
+ return getEnvConfig()[key];
+}
diff --git a/apps/common/features/src/shared/lib/index.ts b/apps/common/features/src/shared/lib/index.ts
index 42ec7d509..82bb6c8b7 100644
--- a/apps/common/features/src/shared/lib/index.ts
+++ b/apps/common/features/src/shared/lib/index.ts
@@ -3,3 +3,4 @@ export { useAlertStore } from "./use-alert-store";
export { useMenuStore } from "./use-menu-store";
export { useUserStore } from "./use-user-store";
export { usePrompt } from "./use-prompt";
+export { getEnv } from "./env";
diff --git a/apps/cs/src/entities/authorization/model/authorization-interface.ts b/apps/cs/src/entities/authorization/model/authorization-interface.ts
index 2230f5226..8c79a4a69 100644
--- a/apps/cs/src/entities/authorization/model/authorization-interface.ts
+++ b/apps/cs/src/entities/authorization/model/authorization-interface.ts
@@ -16,6 +16,7 @@ export interface Authorizations {
httpMethodCode: string;
sortSeq: number;
useYn: string;
+ siteId: string;
mdfr: string;
mdfcnDt: string;
}
@@ -27,6 +28,7 @@ export interface RoleAuthorizations {
urlPatternValue: string;
httpMethodCode: string;
sortSeq: number;
+ siteId: string;
createdAt: string;
}
@@ -40,6 +42,7 @@ export const authorizationFormSchema = z.object({
httpMethodCode: z.string(),
sortSeq: z.number(),
useYn: z.string(),
+ siteId: z.string(),
});
export type AuthorizationsList = PageableList;
diff --git a/apps/cs/src/entities/link/model/link-interface.ts b/apps/cs/src/entities/link/model/link-interface.ts
index 57d1b4582..86960f195 100644
--- a/apps/cs/src/entities/link/model/link-interface.ts
+++ b/apps/cs/src/entities/link/model/link-interface.ts
@@ -161,12 +161,7 @@ export const LinkTableColumnsSchema = z.object({
}, {
message: '최대 14자리까지 입력 가능합니다.',
}),
- col_seq: z.coerce.number().nullable().optional().refine((val) => {
- if (val == null) return true;
- return val > 0;
- }, {
- message: "컬럼 매칭 순서는 0 이상이어야 합니다."
- }),
+ col_seq: z.coerce.number().nullable().optional(),
mdfcn_dt: z.string().nullable().optional(),
mdfr_id: z.string().nullable().optional(),
});
diff --git a/apps/cs/src/entities/role-authorization/model/role-authorization-interface.ts b/apps/cs/src/entities/role-authorization/model/role-authorization-interface.ts
index cf18ae582..16d80de53 100644
--- a/apps/cs/src/entities/role-authorization/model/role-authorization-interface.ts
+++ b/apps/cs/src/entities/role-authorization/model/role-authorization-interface.ts
@@ -11,6 +11,7 @@ export interface WithRoleIdRoleAuthorizationsParams extends PageableParams {
keywordType?: string;
keyword?: string;
roleId?: string;
+ siteId?: string;
}
export interface RoleAuthorizations {
diff --git a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
index b217531dd..65fd0030a 100644
--- a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
+++ b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
@@ -134,7 +134,7 @@ function DashboardPage() {
{/* 전체 게시물 카드 */}
-
+
전체 게시물
{data?.pstTotCnt ? data.pstTotCnt.toLocaleString() : "-"}
@@ -157,7 +157,7 @@ function DashboardPage() {
{/* 오늘 접속자 수 카드 */}
-
+
오늘 접속자 수
{data?.accessTodayCnt
@@ -184,7 +184,7 @@ function DashboardPage() {
{/* API 호출 건수 카드 */}
-
+
API 호출 건수
{data?.apiTodayCnt ? data.apiTodayCnt.toLocaleString() : "-"}
@@ -206,13 +206,13 @@ function DashboardPage() {
{/* 승인 대기 알림 */}
-
+
승인 대기 알림
{/* 사용자 승인 */}
-
+
{/* 매뉴 승인 */}
-
+
-
{/* 컨텐츠 승인 */}
-
+