From 78388d347e9639059777311d20c7a2ec6532b376 Mon Sep 17 00:00:00 2001 From: tkrmagid Date: Thu, 21 May 2026 21:52:09 +0900 Subject: [PATCH] =?UTF-8?q?fix(web):=20API=20base=20=EB=A5=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20host=20=EB=A1=9C=20=EB=8F=99=EC=A0=81=20?= =?UTF-8?q?=ED=95=B4=EC=84=9D=20(LAN=20=EC=A0=91=EC=86=8D=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 리뷰어 지적: docker-compose 가 NEXT_PUBLIC_API_BASE=http://localhost:8000 을 주입해서 클라이언트 번들에 localhost 가 inline 됨. 사금향 게임컴 (192.168.10.13) 브라우저에선 동작하지만, 같은 내부망의 다른 PC 또는 외부 검증자가 http://192.168.10.13:3000 에 접속하면 fetch 가 그 PC 의 localhost:8000 으로 가서 연결 실패. 백엔드는 정상인데도 '검색 결과 없음' 으로 보임. 해석 우선순위: 1) NEXT_PUBLIC_API_BASE 가 비 localhost 값 → 그대로 (프로덕션 도메인 케이스) 2) 브라우저 → window.location.hostname:8000 (LAN/localhost 자동 대응) 3) SSR 폴백 → localhost:8000 localhost/127.0.0.1 판별은 //(localhost|127.0.0.1)(?::|$) 정규식 — 'localhost.evil.com' 같은 서브도메인 우회는 매치 안 됨. node 로 7케이스 검증 완료. web/app 코드만이라 사금향 PC 는 restart.bat 으로 반영 (next dev hot-reload). --- web/lib/api.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/web/lib/api.ts b/web/lib/api.ts index 88bb940..e8b57f2 100644 --- a/web/lib/api.ts +++ b/web/lib/api.ts @@ -1,8 +1,31 @@ // Backend API client. -// NEXT_PUBLIC_API_BASE 는 docker-compose 에서 http://localhost:8000 으로 주입됨. +// +// API 베이스 해석 우선순위: +// 1) NEXT_PUBLIC_API_BASE 가 localhost/127.0.0.1 이 아닌 명시값 → 그대로 사용 +// (예: 프로덕션 https://api.example.com) +// 2) 브라우저 환경 → window.location.hostname:8000 (LAN 접속도 자동 대응) +// 3) SSR 폴백 → http://localhost:8000 +// +// docker-compose 가 NEXT_PUBLIC_API_BASE=http://localhost:8000 을 주입하는 경우가 흔한데, +// LAN 의 다른 PC 에서 http://:3000 으로 접속하면 inline 된 localhost 가 그쪽 PC 의 +// localhost 를 가리켜 깨진다. 그래서 localhost/127.0.0.1 값은 신뢰하지 않고 페이지 host 로 +// 폴백. -const RAW_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8000"; -export const API_BASE = RAW_BASE.replace(/\/$/, ""); +function resolveApiBase(): string { + const raw = process.env.NEXT_PUBLIC_API_BASE; + const env = raw && raw.length > 0 ? raw.replace(/\/$/, "") : ""; + const envIsLocal = !env || /\/\/(localhost|127\.0\.0\.1)(?::|$)/.test(env); + if (typeof window !== "undefined") { + if (envIsLocal) { + return `${window.location.protocol}//${window.location.hostname}:8000`; + } + return env; + } + // SSR + return env || "http://localhost:8000"; +} + +export const API_BASE = resolveApiBase(); export type Symbol = { code: string;