Blog
NEXT_PUBLIC_ 환경변수는 왜 env인데 클라이언트에 노출될까
env인데 왜 노출되나요?#
환경변수(env)는 보통 "비밀값"이라는 인식이 강하다.
그래서 NEXT_PUBLIC_ 환경변수가 클라이언트에서 보인다는 사실을 처음 접하면,
"env가 노출되면 보안 사고 아닌가?"라는 의문이 자연스럽게 든다.
하지만 이 의문은 env의 역할과 Next.js의 실행 구조를 분리해서 보지 않았기 때문에 생긴다.
환경변수는 본질적으로 "비밀"이 아니라, 실행 환경에 따라 값을 분기하기 위한 설정값이다.
Next.js는 이 설정값 중 일부를 의도적으로 클라이언트에 공개할 수 있게 설계했고,
그 선언이 바로 NEXT_PUBLIC_ 접두사다.
환경변수는 언제, 어디서 값이 결정되는가#
Next.js에서 NEXT_PUBLIC_ 환경변수는
클라이언트 기준으로 런타임에 읽히는 값이 아니라,
빌드 시점에 코드에 직접 치환된다.
process.env.NEXT_PUBLIC_API_URL;
이 코드는 브라우저에서 process.env를 실제로 읽는 것이 아니라,
next build 시점에 문자열 값으로 대체되어 번들에 포함된다.
즉, 클라이언트 번들에 포함된 env는 "나중에 가져오는 값"이 아니라,
이미 코드의 일부가 된 상수다.
왜 NEXT_PUBLIC_만 클라이언트 번들에 들어갈까#
Next.js는 서버 코드와 클라이언트 코드를 명확히 분리한다.
그리고 이 경계에서 중요한 원칙 하나를 강제한다.
개발자가 명시적으로 공개를 선언한 env만 클라이언트에 포함한다.
그 선언이 바로 NEXT_PUBLIC_다.
NEXT_PUBLIC_가 붙은 env > 클라이언트 코드에서 사용 가능- 붙지 않은 env > 서버 전용
이는 단순한 네이밍 규칙이 아니라,
보안 경계를 개발자에게 명확히 인식시키기 위한 장치다.
NEXT_PUBLIC_이 없는 env는 왜 번들에 포함되지 않는가#
NEXT_PUBLIC_이 없는 env는 클라이언트 코드에서 접근할 수 없다.
접근할 수 없다는 말은, 번들에 포함될 이유가 없다는 뜻이다.
Next.js는 빌드 과정에서:
- 클라이언트 코드에서 사용되지 않는 env를 제거하고
- 서버에서만 사용 가능한 값은 서버에만 남긴다.
이 덕분에,
- API Secret
- DB 접근 키
- JWT Secret
같은 값이 실수로 클라이언트 번들에 들어가는 구조 자체를 차단한다.
번들에 포함된 env != 비밀키#
여기서 가장 큰 오해가 발생한다.
"번들에 포함되면, 그건 이미 털린 거 아닌가?"
이 질문은 값의 노출과 위험성을 동일시하고 있다.
하지만 보안에서 중요한 건 "보였는가"가 아니라 "그 값만으로 무엇을 할 수 있는가"이다.
비유를 들면 이렇다.
-
NEXT_PUBLIC_env를 공개했다는 것은 길을 가다가 누군가에게 집 열쇠를 하나 건네준 상황이다. -
하지만 그 사람은:
- 집 주소, 위치를 모르고 열쇠가 어디에 쓰이는지도 모른다.
열쇠는 손에 있지만, 공격 가능한 맥락이 없다.
중요한 점은, 그 열쇠는
서버 검증 없이는 어떤 문도 열 수 없도록 설계된 열쇠라는 점이다.
진짜 비밀키와의 차이#
진짜 비밀키는 다르다.
- API Secret
- Service Role Key
- JWT Secret
이 값들은 그 자체만으로 권한을 가진다.
주소, 경로를 몰라도, 열쇠만 있으면 바로 문을 열 수 있는 구조다.
그래서 이 값들은:
- 절대 클라이언트로 내려가면 안 되고
- env라는 이유만으로 안전하다고 착각해서도 안 된다.
보안의 기준은 단순하다.
값 하나로 단독 행동이 가능한가?
이 질문에 "예"라면, 그건 비밀키에 해당한다.
NEXT_PUBLIC_ env의 올바른 사용 기준#
NEXT_PUBLIC_ env는 다음 조건을 만족할 때만 사용해야 한다.
- 공개되어도 무방한 값일 것
- 단독으로 의미를 가지지 않을 것
- 반드시 서버 검증을 거쳐야만 동작할 것
대표적인 예시는:
- 공개 API 엔드포인트
- 서비스 식별자
- 클라이언트 설정용 플래그
반대로,
- 권한을 직접 부여하는 값
- 서버 자원을 직접 조작할 수 있는 값
이런 것들은 env 여부와 상관없이 절대 클라이언트로 가면 안 된다.
정리#
NEXT_PUBLIC_는 보안을 제공하는 기능이 아니다.
"이 값은 공개해도 괜찮다"라고 개발자가 선언하는 장치일 뿐이다.
관련 게시글
5개
Next.js에서 보안 헤더를 설정한다는 것의 의미와 한계
Next.js에서 설정할 수 있는 보안 헤더들의 역할과 한계를 정리합니다. 보안 헤더가 무엇을 막아주지 않는지, 프레임워크와 개발자의 책임 경계를 중심으로 설명합니다.
React는 왜 기본적으로 XSS에 강할까?
React가 XSS에 강해 보이는 이유를 렌더링 방식 관점에서 설명하고, dangerouslySetInnerHTML, 속성 기반 주입, DOM 직접 조작 시 다시 취약해지는 지점을 정리합니다.
innerHTML을 사용할 때 조심해야 하는 이유
innerHTML이 왜 보안상 주의가 필요한 API인지, XSS 취약점과 안전한 대체 방법(textContent, sanitize)을 통해 정리합니다.
env는 보안인가? 많은 개발자들이 착각하는 이유
env는 보안 기능일까? 이 글에서는 env의 역할과 한계, NEXT_PUBLIC_ 환경 변수의 노출 특성, 프론트엔드에서 노출돼도 되는 값의 기준을 정리합니다.
XSS란 무엇인가: 브라우저에서 실행되는 공격
XSS(Cross-Site Scripting)는 서버가 아닌 사용자의 브라우저를 공격하는 웹 보안 취약점입니다. 이 글에서는 XSS의 개념, 발생 원인, 주요 유형과 위험성을 정리합니다.