본문으로 건너뛰기
개발 머꼬
개발 노트Nuxt.js
hohyeon.dev16

runtimeConfig에 넣은 API 키가 브라우저 소스에 보인 이유

  • #Engineering Note
  • #Nuxt.js
  • #Security

문제 발생

외부 API 키를 runtimeConfig에 넣고 컴포넌트에서 꺼내 썼는데, 배포 후 브라우저 소스에서 그 키가 그대로 보였습니다.

// nuxt.config.ts
runtimeConfig: {
  apiSecret: process.env.API_SECRET,
}
<script setup>
const config = useRuntimeConfig();
const data = await $fetch("https://api.example.com", {
  headers: { Authorization: config.apiSecret },   // ❌
});
</script>

원인 분석

runtimeConfig는 두 층입니다.

  • 최상위 키 — 서버에서만 접근 가능한 값입니다. API 시크릿처럼 민감한 데이터가 여기 들어갑니다.
  • public 객체 — 클라이언트에 노출되며 브라우저에서도 읽을 수 있습니다. 공개 API base URL처럼 민감하지 않은 값만 둡니다.

문제는 "서버 전용"이 접근 위치의 문제라는 점입니다. <script setup>은 서버에서도 클라이언트에서도 실행됩니다. 서버에서 읽은 값을 렌더 결과나 상태에 실으면 그대로 브라우저까지 갑니다.

Nuxt 공식 문서가 이걸 경고로 적습니다 — 런타임 설정 키를 렌더링하거나 useState에 넘겨서 클라이언트에 노출하지 않도록 주의하십시오.

해결 방안

  1. 비밀값을 쓰는 요청은 서버 라우트로 옮깁니다. 브라우저는 우리 서버만 부르고, 외부 API 호출과 키 사용은 서버에서 일어납니다.
// server/api/quotes.get.ts
export default defineEventHandler(async () => {
  const config = useRuntimeConfig();
  return await $fetch("https://api.example.com/quotes", {
    headers: { Authorization: config.apiSecret },
  });
});
<script setup>
const { data } = await useFetch("/api/quotes");   // 키가 브라우저로 가지 않음
</script>
  1. 공개해도 되는 값만 public에 둡니다.
runtimeConfig: {
  apiSecret: "",                       // 서버 전용
  public: {
    apiBase: "https://meokko.com/api", // 클라이언트 노출 OK
  },
}
  1. 환경변수 이름 규칙을 지킵니다. 접두사가 곧 층을 정합니다.
설정 키환경변수
apiSecretNUXT_API_SECRET
public.apiBaseNUXT_PUBLIC_API_BASE

값은 destr로 자동 형변환됩니다 — 문자열로 유지해야 하면 따옴표를 포함해 적습니다.

  1. nuxt.config에 기본값을 빈 문자열로 둡니다. 실제 값은 환경변수로 주입합니다. 코드에 진짜 키를 적어두면 저장소에 남습니다.

  2. 호출 위치가 정해져 있습니다. useRuntimeConfig()는 컴포넌트 setup, 플러그인, 미들웨어, 서버 라우트 핸들러 안에서만 부를 수 있습니다. 모듈 최상단에서 부르면 동작하지 않습니다.

  3. useState에 담지 않습니다. useState 값은 payload로 직렬화되어 브라우저로 전달됩니다. 경고가 콕 집어 언급하는 경로입니다.

  4. 이미 유출됐다면 키를 폐기합니다. 코드를 고치는 것으로 이미 배포된 번들과 브라우저 캐시에서 회수되지 않습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.