Secure Context 완전 정리 — HTTP에서 crypto.randomUUID가 안 되는 이유와 해결법

@JavaPark · April 06, 2026 · 8 min read

안녕하세요, 자바파커입니다.

"분명 로컬에서는 잘 되는데, 개발서버에 올리니까 crypto.randomUUID is not a function 에러가 나요."

저도 최근에 정확히 이 상황을 겪었습니다. 로컬(localhost)에서는 멀쩡하게 동작하던 코드가 내부 IP로 접속하는 개발서버에서만 터지는 거죠. 원인은 단순합니다 — Secure Context 때문입니다.

결론부터 말씀드리면 — HTTP 환경에서는 브라우저가 일부 Web API를 아예 차단합니다. crypto.randomUUID()도 그 중 하나이고, 이것 말고도 꽤 많습니다. 오늘은 Secure Context가 뭔지, 어떤 API가 영향을 받는지, 그리고 어떻게 해결하는지 정리하겠습니다.


Secure Context란?

Secure Context는 브라우저가 "이 환경은 보안이 보장된다"고 판단하는 실행 컨텍스트입니다. 쉽게 말하면, 브라우저가 "여기서는 민감한 API를 써도 안전하다"고 허용하는 조건이라고 보면 됩니다.

핵심 판단 기준은 단 하나 — HTTPS로 서비스되고 있는가?


HTTP vs HTTPS — 환경별 Secure Context 동작 차이

환경 Secure Context crypto.randomUUID()
https://... ✅ 사용 가능
http://localhost ✅ (브라우저 예외 허용) ✅ 사용 가능
http://127.0.0.1 ✅ (브라우저 예외 허용) ✅ 사용 가능
http://192.168.x.x ❌ TypeError
http://개발서버도메인 ❌ TypeError

여기서 함정이 있습니다. localhost127.0.0.1은 HTTP여도 Secure Context로 인정됩니다. 브라우저가 로컬 개발 편의를 위해 예외를 두고 있기 때문입니다.

그래서 로컬에서는 문제없이 동작하다가, 내부 IP(192.168.x.x)나 HTTP 도메인으로 서비스하는 개발서버에 배포하는 순간 터지는 겁니다. "제 PC에서는 되는데요?"의 전형적인 원인 중 하나입니다.


Secure Context 전용 Web API 목록 — crypto.randomUUID만 있는 게 아닙니다

crypto.randomUUID() 외에도 HTTP 환경에서 차단되는 API가 꽤 많습니다.

API 용도 HTTP에서
crypto.randomUUID() UUID 생성 ❌ 차단
crypto.subtle.* 암호화 (AES, RSA 등) ❌ 차단
navigator.geolocation 위치 정보 ❌ 차단
navigator.mediaDevices 카메라 / 마이크 ❌ 차단
navigator.clipboard 클립보드 읽기 / 쓰기 ❌ 차단
ServiceWorker PWA, 오프라인 캐시 ❌ 차단
WebAuthn (FIDO2) 생체 인증 ❌ 차단

이 목록을 보면 알 수 있듯이, 하나의 API에서 문제가 발생했다면 다른 API에서도 같은 문제가 터질 가능성이 높습니다. 하나씩 고치기보다는 근본적인 원인을 해결하는 게 맞습니다.


해결 방법 3가지

방법 1. 개발서버 HTTPS 적용 (권장)

가장 근본적인 해결책입니다. HTTPS를 적용하면 모든 Secure Context API 문제가 한 번에 해결됩니다.

# Kubernetes Ingress 예시
annotations:
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
tls:
  - hosts:
      - dev.example.com
    secretName: dev-tls-secret

내부 개발서버라면 자체 서명 인증서(self-signed)나 mkcert 같은 도구로 간단하게 적용할 수 있습니다.

방법 2. 코드 레벨 폴백 — 간단하지만 실용적

UUID 형식이 반드시 필요하지 않은 경우, 간단한 폴백으로 해결할 수 있습니다.

const generateId = () =>
  globalThis.crypto?.randomUUID?.() ??
  `${Date.now()}-${Math.random().toString(36).slice(2)}`
  • HTTPS / HTTP 모두 동작
  • 고유성은 보장되지만 RFC 4122 UUID 형식은 아님
  • 임시 ID, 클라이언트 사이드 키 등에 적합

방법 3. crypto.getRandomValues() 사용 — UUID 형식이 필요할 때

randomUUID()는 Secure Context 전용이지만, getRandomValues()는 HTTP에서도 동작합니다. RFC 4122 v4 형식의 UUID가 필요하다면 이 방법을 씁니다.

const generateUUIDv4 = (): string => {
  if (globalThis.crypto?.randomUUID) {
    return globalThis.crypto.randomUUID()
  }
  const bytes = new Uint8Array(16)
  globalThis.crypto.getRandomValues(bytes)
  bytes[6] = (bytes[6] & 0x0f) | 0x40 // version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80 // variant
  return [...bytes]
    .map((b, i) =>
      [4, 6, 8, 10].includes(i)
        ? `-${b.toString(16).padStart(2, "0")}`
        : b.toString(16).padStart(2, "0")
    )
    .join("")
}

어떤 방법을 써야 할까?

상황 추천 방법
개발서버/스테이징 환경 전체에 적용 방법 1 (HTTPS 적용)
빠르게 에러만 해결하고 싶을 때 방법 2 (폴백)
UUID v4 형식이 반드시 필요한 로직 방법 3 (getRandomValues)
장기적으로는? 방법 1 + 방법 2 또는 3 조합

Secure Context 확인하는 방법

현재 환경이 Secure Context인지 브라우저 콘솔에서 바로 확인할 수 있습니다.

console.log(window.isSecureContext) // true 또는 false

개발 중 "이 API가 왜 안 되지?" 싶을 때, 가장 먼저 확인해볼 값입니다.


FAQ — 자주 묻는 질문

Q. localhost는 HTTP인데 왜 Secure Context인가요?

브라우저가 로컬 개발 편의를 위해 localhost127.0.0.1명시적으로 예외 처리하고 있습니다. W3C Secure Contexts 스펙에 정의된 동작이며, 모든 주요 브라우저(Chrome, Firefox, Safari, Edge)에서 동일하게 적용됩니다.

Q. 자체 서명 인증서(self-signed)로도 Secure Context가 되나요?

네, HTTPS로 서비스되면 인증서 종류와 관계없이 Secure Context입니다. 다만 브라우저에서 "안전하지 않음" 경고가 뜹니다. 내부 개발용이라면 mkcert로 로컬 CA 인증서를 만들면 경고 없이 사용할 수 있습니다.

Q. crypto.getRandomValues()는 왜 HTTP에서도 되나요?

getRandomValues()는 Secure Context 이전부터 존재하던 API이고, 난수 생성 자체는 보안 위협이 아니기 때문입니다. 반면 randomUUID()는 나중에 추가된 API로, 처음부터 Secure Context 전용으로 설계되었습니다.


정리하면 이렇습니다.

개발서버  →  HTTPS 적용 (Ingress TLS, mkcert 등)
코드 내   →  폴백 패턴 유지 (방어적 코딩)

HTTP 환경에서 발생하는 Secure Context 오류는 하나씩 발견되기보다 개발서버에 HTTPS를 적용하는 것이 가장 확실하고 포괄적인 해결책입니다. 한 번 적용해두면 crypto.randomUUID() 뿐 아니라 클립보드, 위치 정보, Service Worker 등에서도 같은 문제가 재발하지 않습니다.

여러분도 혹시 "로컬에서는 되는데 서버에서는 안 되는" 경험이 있으신가요? 댓글로 공유해주세요!

@JavaPark
AI 시대의 개발자 도구, 실전 경험을 공유합니다