[common/features] feat: 환경 변수 URL 방식 도입

This commit is contained in:
2025-08-08 17:22:58 +09:00
parent 2b3475a626
commit e4d52611d7
8 changed files with 202 additions and 6 deletions
+2 -1
View File
@@ -37,4 +37,5 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
.env*
.env*
__ENV__.js
+44
View File
@@ -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();
+6 -2
View File
@@ -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",
+4
View File
@@ -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 (
<html lang="ko" data-theme={theme?.value}>
<head>
<Script src="/__ENV__.js" strategy="beforeInteractive" />
</head>
<body>
<AppProvider>{children}</AppProvider>
</body>
@@ -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: {
@@ -0,0 +1,14 @@
declare global {
interface Window {
__ENV__?: Record<string, string>;
}
}
export function getEnvConfig(): Record<string, string> {
if (typeof window === "undefined") return {};
return window.__ENV__ ?? {};
}
export function getEnv(key: string): string | undefined {
return getEnvConfig()[key];
}
@@ -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";
+125 -1
View File
@@ -247,6 +247,12 @@ importers:
dayjs:
specifier: ^1.11.13
version: 1.11.13
dotenv:
specifier: ^17.2.1
version: 17.2.1
find-up:
specifier: ^7.0.0
version: 7.0.0
js-cookie:
specifier: ^3.0.5
version: 3.0.5
@@ -274,6 +280,9 @@ importers:
tus-js-client:
specifier: ^4.3.1
version: 4.3.1
yargs:
specifier: ^18.0.0
version: 18.0.0
zod:
specifier: ^3.24.2
version: 3.24.2
@@ -293,6 +302,9 @@ importers:
'@types/webpack':
specifier: ^5.28.5
version: 5.28.5
'@types/yargs':
specifier: ^17.0.33
version: 17.0.33
eslint:
specifier: ^9
version: 9.21.0
@@ -4898,6 +4910,16 @@ packages:
- webpack-cli
dev: true
/@types/yargs-parser@21.0.3:
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
dev: true
/@types/yargs@17.0.33:
resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
dependencies:
'@types/yargs-parser': 21.0.3
dev: true
/@typescript-eslint/eslint-plugin@8.25.0(@typescript-eslint/parser@8.25.0)(eslint@9.21.0)(typescript@5.6.3):
resolution: {integrity: sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5747,6 +5769,15 @@ packages:
wrap-ansi: 7.0.0
dev: true
/cliui@9.0.1:
resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
engines: {node: '>=20'}
dependencies:
string-width: 7.2.0
strip-ansi: 7.1.0
wrap-ansi: 9.0.0
dev: false
/clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@@ -6236,6 +6267,11 @@ packages:
csstype: 3.1.3
dev: false
/dotenv@17.2.1:
resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==}
engines: {node: '>=12'}
dev: false
/dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -6289,6 +6325,10 @@ packages:
resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==}
dev: false
/emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
dev: false
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -7015,6 +7055,15 @@ packages:
path-exists: 4.0.0
dev: true
/find-up@7.0.0:
resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
engines: {node: '>=18'}
dependencies:
locate-path: 7.2.0
path-exists: 5.0.0
unicorn-magic: 0.1.0
dev: false
/flat-cache@4.0.1:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
@@ -7148,6 +7197,11 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
/get-east-asian-width@1.3.0:
resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
engines: {node: '>=18'}
dev: false
/get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
@@ -7968,6 +8022,13 @@ packages:
p-locate: 5.0.0
dev: true
/locate-path@7.2.0:
resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
p-locate: 6.0.0
dev: false
/lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
dev: true
@@ -8486,6 +8547,13 @@ packages:
yocto-queue: 0.1.0
dev: true
/p-limit@4.0.0:
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
yocto-queue: 1.2.1
dev: false
/p-locate@4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
@@ -8500,6 +8568,13 @@ packages:
p-limit: 3.1.0
dev: true
/p-locate@6.0.0:
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
p-limit: 4.0.0
dev: false
/p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
@@ -8541,6 +8616,11 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
/path-exists@5.0.0:
resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -9812,6 +9892,15 @@ packages:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
/string-width@7.2.0:
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
engines: {node: '>=18'}
dependencies:
emoji-regex: 10.4.0
get-east-asian-width: 1.3.0
strip-ansi: 7.1.0
dev: false
/string.prototype.includes@2.0.1:
resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
engines: {node: '>= 0.4'}
@@ -10400,6 +10489,11 @@ packages:
/undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
/unicorn-magic@0.1.0:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
dev: false
/unicorn-magic@0.3.0:
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
engines: {node: '>=18'}
@@ -10848,6 +10942,15 @@ packages:
string-width: 5.1.2
strip-ansi: 7.1.0
/wrap-ansi@9.0.0:
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
engines: {node: '>=18'}
dependencies:
ansi-styles: 6.2.1
string-width: 7.2.0
strip-ansi: 7.1.0
dev: false
/ws@8.18.1:
resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
engines: {node: '>=10.0.0'}
@@ -10895,7 +10998,6 @@ packages:
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -10924,6 +11026,11 @@ packages:
engines: {node: '>=12'}
dev: true
/yargs-parser@22.0.0:
resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
dev: false
/yargs@15.4.1:
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
engines: {node: '>=8'}
@@ -10954,11 +11061,28 @@ packages:
yargs-parser: 21.1.1
dev: true
/yargs@18.0.0:
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
dependencies:
cliui: 9.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
string-width: 7.2.0
y18n: 5.0.8
yargs-parser: 22.0.0
dev: false
/yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
/yocto-queue@1.2.1:
resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
engines: {node: '>=12.20'}
dev: false
/zod-validation-error@3.4.0(zod@3.24.2):
resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==}
engines: {node: '>=18.0.0'}