import { config } from "dotenv"; import { existsSync } from "fs"; import { resolve } from "path"; import { z } from "zod"; /** * 1) dotenv 로드 순서 * - .env (공통) * - .env.{NODE_ENV}.local (환경별 override, 있으면 덮어씀) */ config(); // .env const nodeEnv = process.env.NODE_ENV || "development"; const layerPath = resolve(process.cwd(), `.env.${nodeEnv}.local`); if (existsSync(layerPath)) { config({ path: layerPath }); } /** * 2) Zod 스키마 정의 * - 필수/선택/기본값 정책은 필요에 맞게 수정 가능 */ const EnvSchema = z .object({ NODE_ENV: z .enum(["development", "production", "test"]) .default("development"), PORT: z.coerce.number().int().positive().optional(), // 기본값은 app.ts에서 3000 처리 SECRET_KEY: z.string().min(1), LOG_FORMAT: z.string().min(1).optional(), // 기본값은 app.ts에서 'dev' LOG_DIR: z.string().min(1), LOG_LEVEL: z.string().min(1), ORIGIN: z.string().min(1), // 필요시 배열화 가능 CREDENTIALS: z.coerce.boolean(), // 'true'/'false' 문자열 → boolean CORS_ORIGINS: z.string().optional(), // "http://a.com,http://b.com" API_SERVER_URL: z.string().url().optional(), SENTRY_DSN: z.string().default(""), REDIS_URL: z.string().url().default("redis://localhost:6379"), // Database configuration for Drizzle DATABASE_URL: z.string().url().min(1), }) .strip(); /** * 3) 검증(모듈 import 시점에 실행) */ const parsed = EnvSchema.safeParse(process.env); if (!parsed.success) { console.error("\n❌ Invalid environment variables:\n"); console.error(parsed.error.format()); process.exit(1); } const env = parsed.data; /** * 4) 타입 안전한 상수 export * - 다른 파일에서는 process.env 직접 쓰지 말고 여기서만 가져가세요. */ export const NODE_ENV = env.NODE_ENV; export const PORT = env.PORT; // app.ts에서 PORT || 3000 export const SECRET_KEY = env.SECRET_KEY; export const LOG_FORMAT = env.LOG_FORMAT; // app.ts에서 LOG_FORMAT || 'dev' export const LOG_DIR = env.LOG_DIR; export const LOG_LEVEL = env.LOG_LEVEL; export const ORIGIN = env.ORIGIN; export const CREDENTIALS = env.CREDENTIALS; export const SENTRY_DSN = env.SENTRY_DSN; export const REDIS_URL = env.REDIS_URL; export const API_SERVER_URL = env.API_SERVER_URL; export const DATABASE_URL = env.DATABASE_URL; // CORS Origins를 배열로도 제공 (없으면 []) export const CORS_ORIGIN_LIST = env.CORS_ORIGINS?.split(",") .map((s) => s.trim()) .filter(Boolean) ?? [];