From 2d6b0fc7ceaad90d97b41fc2e2e46bd1155dc4ea Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 11:46:42 +0900 Subject: [PATCH 1/8] =?UTF-8?q?ip=20adress=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend-node/src/app.ts | 2 +- backend-node/src/config/environment.ts | 4 ++-- frontend/lib/api/client.ts | 27 ++++++++++++++++++++++++-- frontend/next.config.mjs | 2 +- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/backend-node/src/app.ts b/backend-node/src/app.ts index 3b281d19..be12bcad 100644 --- a/backend-node/src/app.ts +++ b/backend-node/src/app.ts @@ -29,7 +29,7 @@ app.use(express.urlencoded({ extended: true, limit: "10mb" })); // CORS 설정 app.use( cors({ - origin: config.cors.origin, + origin: config.cors.origin.split(',').map(url => url.trim()), credentials: true, methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"], diff --git a/backend-node/src/config/environment.ts b/backend-node/src/config/environment.ts index be936f76..56478ae3 100644 --- a/backend-node/src/config/environment.ts +++ b/backend-node/src/config/environment.ts @@ -82,8 +82,8 @@ const config: Config = { // CORS 설정 cors: { - origin: process.env.CORS_ORIGIN || "http://localhost:9771", - credentials: process.env.CORS_CREDENTIALS === "true", + origin: process.env.CORS_ORIGIN || "http://localhost:9771,http://192.168.0.70:5555,http://39.117.244.52:5555", + credentials: process.env.CORS_CREDENTIALS === "true" || true, }, // 로깅 설정 diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index e3eef389..5404774a 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -1,7 +1,30 @@ import axios, { AxiosResponse, AxiosError } from "axios"; -// API 기본 URL 설정 -export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080/api"; +// API 기본 URL 동적 설정 (클라이언트 사이드에서 현재 도메인 기반으로 결정) +const getApiBaseUrl = (): string => { + if (typeof window !== 'undefined') { + const currentHost = window.location.hostname; + const currentPort = window.location.port; + + // 외부 IP로 접근한 경우 + if (currentHost === '39.117.244.52') { + return 'http://39.117.244.52:8080/api'; + } + // 내부 IP로 접근한 경우 + else if (currentHost === '192.168.0.70') { + return 'http://192.168.0.70:8080/api'; + } + // localhost로 접근한 경우 + else if (currentHost === 'localhost' || currentHost === '127.0.0.1') { + return 'http://localhost:8080/api'; + } + } + + // 서버 사이드 렌더링이나 기본값 + return process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api"; +}; + +export const API_BASE_URL = getApiBaseUrl(); // JWT 토큰 관리 유틸리티 const TokenManager = { diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 286faf4a..5323571a 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -43,7 +43,7 @@ const nextConfig = { // 환경 변수 (런타임에 읽기) env: { - NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://192.168.0.70:8080/api", + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api", }, }; From a03db24ab9c6522457a3afef81495118ab08cd73 Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 12:02:35 +0900 Subject: [PATCH 2/8] =?UTF-8?q?=EB=8F=99=EC=A0=81=20API=20URL=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend-node/src/app.ts | 10 ++++++---- frontend/lib/api/client.ts | 18 +++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/backend-node/src/app.ts b/backend-node/src/app.ts index be12bcad..4a07ae1a 100644 --- a/backend-node/src/app.ts +++ b/backend-node/src/app.ts @@ -29,7 +29,7 @@ app.use(express.urlencoded({ extended: true, limit: "10mb" })); // CORS 설정 app.use( cors({ - origin: config.cors.origin.split(',').map(url => url.trim()), + origin: config.cors.origin.split(",").map((url) => url.trim()), credentials: true, methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"], @@ -84,11 +84,13 @@ app.use(errorHandler); // 서버 시작 const PORT = config.port; +const HOST = config.host; -app.listen(PORT, () => { - logger.info(`🚀 Server is running on port ${PORT}`); +app.listen(PORT, HOST, () => { + logger.info(`🚀 Server is running on ${HOST}:${PORT}`); logger.info(`📊 Environment: ${config.nodeEnv}`); - logger.info(`🔗 Health check: http://localhost:${PORT}/health`); + logger.info(`🔗 Health check: http://${HOST}:${PORT}/health`); + logger.info(`🌐 External access: http://39.117.244.52:${PORT}/health`); }); export default app; diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index 5404774a..f58319af 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -2,24 +2,24 @@ import axios, { AxiosResponse, AxiosError } from "axios"; // API 기본 URL 동적 설정 (클라이언트 사이드에서 현재 도메인 기반으로 결정) const getApiBaseUrl = (): string => { - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { const currentHost = window.location.hostname; const currentPort = window.location.port; - + // 외부 IP로 접근한 경우 - if (currentHost === '39.117.244.52') { - return 'http://39.117.244.52:8080/api'; + if (currentHost === "39.117.244.52") { + return "http://39.117.244.52:8080/api"; } // 내부 IP로 접근한 경우 - else if (currentHost === '192.168.0.70') { - return 'http://192.168.0.70:8080/api'; + else if (currentHost === "192.168.0.70") { + return "http://192.168.0.70:8080/api"; } // localhost로 접근한 경우 - else if (currentHost === 'localhost' || currentHost === '127.0.0.1') { - return 'http://localhost:8080/api'; + else if (currentHost === "localhost" || currentHost === "127.0.0.1") { + return "http://localhost:8080/api"; } } - + // 서버 사이드 렌더링이나 기본값 return process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api"; }; From 8d76df1cfe9c19c95715a8f16e4489ed6e221fda Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 12:14:06 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=EB=8F=84=EC=BB=A4=20yml=EC=97=90=20cors=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/docker-compose.backend.prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/prod/docker-compose.backend.prod.yml b/docker/prod/docker-compose.backend.prod.yml index cd29965c..78e5e994 100644 --- a/docker/prod/docker-compose.backend.prod.yml +++ b/docker/prod/docker-compose.backend.prod.yml @@ -13,7 +13,7 @@ services: - DATABASE_URL=postgresql://postgres:ph0909!!@39.117.244.52:11132/plm - JWT_SECRET=ilshin-plm-super-secret-jwt-key-2024 - JWT_EXPIRES_IN=24h - - CORS_ORIGIN=http://192.168.0.70:5555 + - CORS_ORIGIN=http://192.168.0.70:5555,http://39.117.244.52:5555 - CORS_CREDENTIALS=true - LOG_LEVEL=info # 운영용에서는 볼륨 마운트 없음 (보안상 이유) From 22b8bdd4002608cf4be3cdf86e5eebafdb284190 Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 12:27:45 +0900 Subject: [PATCH 4/8] =?UTF-8?q?=EB=82=B4=EB=B6=80=20IP=EB=A1=9C=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/api/client.ts | 27 ++------------------------- frontend/next.config.mjs | 4 ++-- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index f58319af..a141be3f 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -1,30 +1,7 @@ import axios, { AxiosResponse, AxiosError } from "axios"; -// API 기본 URL 동적 설정 (클라이언트 사이드에서 현재 도메인 기반으로 결정) -const getApiBaseUrl = (): string => { - if (typeof window !== "undefined") { - const currentHost = window.location.hostname; - const currentPort = window.location.port; - - // 외부 IP로 접근한 경우 - if (currentHost === "39.117.244.52") { - return "http://39.117.244.52:8080/api"; - } - // 내부 IP로 접근한 경우 - else if (currentHost === "192.168.0.70") { - return "http://192.168.0.70:8080/api"; - } - // localhost로 접근한 경우 - else if (currentHost === "localhost" || currentHost === "127.0.0.1") { - return "http://localhost:8080/api"; - } - } - - // 서버 사이드 렌더링이나 기본값 - return process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api"; -}; - -export const API_BASE_URL = getApiBaseUrl(); +// API 기본 URL 설정 - 모든 접근에서 내부 IP 사용 (Option B) +export const API_BASE_URL = "http://192.168.0.70:8080/api"; // JWT 토큰 관리 유틸리티 const TokenManager = { diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 5323571a..d9ef4ce9 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -41,9 +41,9 @@ const nextConfig = { ]; }, - // 환경 변수 (런타임에 읽기) + // 환경 변수 (런타임에 읽기) - 내부 IP로 통일 env: { - NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api", + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://192.168.0.70:8080/api", }, }; From b4e01641a040af24720511d9917c420667e19edb Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 13:56:26 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EB=B6=80=20API=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/api/client.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index a141be3f..c73c53eb 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -1,7 +1,29 @@ import axios, { AxiosResponse, AxiosError } from "axios"; -// API 기본 URL 설정 - 모든 접근에서 내부 IP 사용 (Option B) -export const API_BASE_URL = "http://192.168.0.70:8080/api"; +// API 기본 URL 동적 설정 - 접속 위치에 따라 최적 경로 선택 +const getApiBaseUrl = (): string => { + if (typeof window !== "undefined") { + const currentHost = window.location.hostname; + + // 외부 IP로 접근한 경우 - 외부 IP로 API 호출 + if (currentHost === "39.117.244.52") { + return "http://39.117.244.52:8080/api"; + } + // 내부 IP로 접근한 경우 - 내부 IP로 API 호출 + else if (currentHost === "192.168.0.70") { + return "http://192.168.0.70:8080/api"; + } + // localhost로 접근한 경우 - localhost로 API 호출 + else if (currentHost === "localhost" || currentHost === "127.0.0.1") { + return "http://localhost:8080/api"; + } + } + + // 서버 사이드 렌더링이나 기본값 - 외부 IP 사용 + return process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api"; +}; + +export const API_BASE_URL = getApiBaseUrl(); // JWT 토큰 관리 유틸리티 const TokenManager = { @@ -43,6 +65,7 @@ apiClient.interceptors.request.use( tokenStart: token ? token.substring(0, 30) + "..." : "없음", url: config.url, method: config.method, + baseURL: config.baseURL, }); if (token && !TokenManager.isTokenExpired(token)) { @@ -92,7 +115,9 @@ apiClient.interceptors.request.use( } } - console.log("📡 API 요청:", config.method?.toUpperCase(), config.url, config.params, config.data); + // TypeScript 안전성을 위한 null 체크 + const fullUrl = `${config.baseURL || ""}${config.url || ""}`; + console.log("📡 API 요청:", config.method?.toUpperCase(), fullUrl, config.params, config.data); console.log("📋 요청 헤더:", config.headers); return config; }, From 22b0f839c6b4e9a5349ddb10d9212d95869220bc Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 14:11:20 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=EB=AA=85=EC=8B=9C=EC=A0=81=20HOST=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/docker-compose.backend.prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/prod/docker-compose.backend.prod.yml b/docker/prod/docker-compose.backend.prod.yml index 78e5e994..4bd7a8be 100644 --- a/docker/prod/docker-compose.backend.prod.yml +++ b/docker/prod/docker-compose.backend.prod.yml @@ -10,6 +10,7 @@ services: environment: - NODE_ENV=production - PORT=8080 + - HOST=0.0.0.0 - DATABASE_URL=postgresql://postgres:ph0909!!@39.117.244.52:11132/plm - JWT_SECRET=ilshin-plm-super-secret-jwt-key-2024 - JWT_EXPIRES_IN=24h From c78b239db2d2af5d29fcd246344c119d83603317 Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 14:15:25 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=90=98=EB=8F=8C?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/docker-compose.backend.prod.yml | 1 - frontend/lib/api/client.ts | 31 ++------------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/docker/prod/docker-compose.backend.prod.yml b/docker/prod/docker-compose.backend.prod.yml index 4bd7a8be..78e5e994 100644 --- a/docker/prod/docker-compose.backend.prod.yml +++ b/docker/prod/docker-compose.backend.prod.yml @@ -10,7 +10,6 @@ services: environment: - NODE_ENV=production - PORT=8080 - - HOST=0.0.0.0 - DATABASE_URL=postgresql://postgres:ph0909!!@39.117.244.52:11132/plm - JWT_SECRET=ilshin-plm-super-secret-jwt-key-2024 - JWT_EXPIRES_IN=24h diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index c73c53eb..a141be3f 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -1,29 +1,7 @@ import axios, { AxiosResponse, AxiosError } from "axios"; -// API 기본 URL 동적 설정 - 접속 위치에 따라 최적 경로 선택 -const getApiBaseUrl = (): string => { - if (typeof window !== "undefined") { - const currentHost = window.location.hostname; - - // 외부 IP로 접근한 경우 - 외부 IP로 API 호출 - if (currentHost === "39.117.244.52") { - return "http://39.117.244.52:8080/api"; - } - // 내부 IP로 접근한 경우 - 내부 IP로 API 호출 - else if (currentHost === "192.168.0.70") { - return "http://192.168.0.70:8080/api"; - } - // localhost로 접근한 경우 - localhost로 API 호출 - else if (currentHost === "localhost" || currentHost === "127.0.0.1") { - return "http://localhost:8080/api"; - } - } - - // 서버 사이드 렌더링이나 기본값 - 외부 IP 사용 - return process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api"; -}; - -export const API_BASE_URL = getApiBaseUrl(); +// API 기본 URL 설정 - 모든 접근에서 내부 IP 사용 (Option B) +export const API_BASE_URL = "http://192.168.0.70:8080/api"; // JWT 토큰 관리 유틸리티 const TokenManager = { @@ -65,7 +43,6 @@ apiClient.interceptors.request.use( tokenStart: token ? token.substring(0, 30) + "..." : "없음", url: config.url, method: config.method, - baseURL: config.baseURL, }); if (token && !TokenManager.isTokenExpired(token)) { @@ -115,9 +92,7 @@ apiClient.interceptors.request.use( } } - // TypeScript 안전성을 위한 null 체크 - const fullUrl = `${config.baseURL || ""}${config.url || ""}`; - console.log("📡 API 요청:", config.method?.toUpperCase(), fullUrl, config.params, config.data); + console.log("📡 API 요청:", config.method?.toUpperCase(), config.url, config.params, config.data); console.log("📋 요청 헤더:", config.headers); return config; }, From 103dd9907d55225d4648a904c00ca2fb78738297 Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Thu, 4 Sep 2025 14:45:58 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=EC=99=B8=EB=B6=80=20ip=20=EB=A1=9C=20=20ap?= =?UTF-8?q?i=20=ED=98=B8=EC=B6=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/docker-compose.backend.prod.yml | 7 ++----- frontend/lib/api/client.ts | 3 +-- frontend/next.config.mjs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docker/prod/docker-compose.backend.prod.yml b/docker/prod/docker-compose.backend.prod.yml index 78e5e994..3cded048 100644 --- a/docker/prod/docker-compose.backend.prod.yml +++ b/docker/prod/docker-compose.backend.prod.yml @@ -5,20 +5,17 @@ services: context: ../../backend-node dockerfile: ../docker/prod/backend.Dockerfile # 운영용 Dockerfile container_name: pms-backend-prod - ports: - - "8080:8080" + network_mode: "host" # 호스트 네트워크 모드 environment: - NODE_ENV=production - PORT=8080 + - HOST=0.0.0.0 # 모든 인터페이스에서 바인딩 - DATABASE_URL=postgresql://postgres:ph0909!!@39.117.244.52:11132/plm - JWT_SECRET=ilshin-plm-super-secret-jwt-key-2024 - JWT_EXPIRES_IN=24h - CORS_ORIGIN=http://192.168.0.70:5555,http://39.117.244.52:5555 - CORS_CREDENTIALS=true - LOG_LEVEL=info - # 운영용에서는 볼륨 마운트 없음 (보안상 이유) - networks: - - pms-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] diff --git a/frontend/lib/api/client.ts b/frontend/lib/api/client.ts index a141be3f..659a512a 100644 --- a/frontend/lib/api/client.ts +++ b/frontend/lib/api/client.ts @@ -1,7 +1,6 @@ import axios, { AxiosResponse, AxiosError } from "axios"; -// API 기본 URL 설정 - 모든 접근에서 내부 IP 사용 (Option B) -export const API_BASE_URL = "http://192.168.0.70:8080/api"; +export const API_BASE_URL = "http://39.117.244.52:8080/api"; // JWT 토큰 관리 유틸리티 const TokenManager = { diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index d9ef4ce9..50f2755d 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -43,7 +43,7 @@ const nextConfig = { // 환경 변수 (런타임에 읽기) - 내부 IP로 통일 env: { - NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://192.168.0.70:8080/api", + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://39.117.244.52:8080/api", }, };