교차 출처 격리 검사기
아무 사이트의 URL을 입력해서 그 사이트가 교차 출처 격리(cross-origin isolated) 되어 있는지, 그리고 SharedArrayBuffer와 멀티스레드 WebAssembly가 거기서 동작하는지 확인하세요. 검사기는 해당 URL의 Cross-Origin-Opener-Policy와 Cross-Origin-Embedder-Policy 응답 헤더를 읽고 무엇이 빠졌는지 알려줍니다. 헤더만 읽을 뿐 그 외에는 아무것도 가져오거나 저장하지 않습니다.
교차 출처 격리가 웹 게임에 중요한 이유
가장 쓸모 있는 브라우저 기능 몇 가지는 교차 출처 격리 뒤에 잠겨 있는데, 게임에서 가장 큰 것이 바로 SharedArrayBuffer, 즉 멀티스레드 WebAssembly를 가능하게 하는 공유 메모리입니다. 엔진이 스레드를 쓰는 웹 빌드를 내보낸다면, 이것 없이는 실행되지 않습니다.
- Godot 웹 익스포트는 예전부터 SharedArrayBuffer를 필요로 했습니다(Godot 4.3+에서 헤더를 피할 수 있는 단일 스레드 익스포트가 추가됨).
- Unity 멀티스레드 WebGL 빌드도 필요합니다.
- Emscripten pthreads, ffmpeg.wasm, 그리고 많은 물리 및 코덱 라이브러리도 마찬가지로 필요합니다.
격리가 없으면 브라우저는 고해상도 타이머와 performance.measureUserAgentSpecificMemory()도 제공하지 않습니다. 그래서 스레드를 쓰는 게임이 아무 메시지 없이 로딩에 실패하거나, performance.now()의 정밀도가 수상할 만큼 거칠어 보인다면 가장 먼저 해 볼 일이 격리 검사입니다.
교차 출처 격리의 동작 방식
페이지는 HTML 문서에 다음 두 응답 헤더를 보낼 때 교차 출처 격리됩니다.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpCOOP: same-origin은 동일 출처가 아닌 다른 창들로부터 페이지를 차단합니다. COEP: require-corp는 모든 하위 리소스가 임베드되려면 명시적으로 동의해야 한다고 요구합니다. 둘 다 설정되면 self.crossOriginIsolated가 true가 되고 SharedArrayBuffer가 잠금 해제됩니다.
까다로운 부분은 COEP입니다. 일단 켜지면 모든 교차 출처 리소스(CDN 이미지, 폰트, 스크립트, iframe)가 Cross-Origin-Resource-Policy: cross-origin을 보내거나 CORS로 가져와져야 하며, 그렇지 않으면 브라우저가 차단합니다. 사이트가 보통 깨지는 지점이 바로 이 부분입니다. 서드파티 리소스를 직접 제어할 수 없다면 대신 Cross-Origin-Embedder-Policy: credentialless를 사용하세요. 이 방식은 교차 출처 리소스가 동의하도록 요구하는 대신 자격 증명 없이 로드합니다.
활성화 방법
Cloudflare Pages (_headers 파일):
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpVite 개발 서버 (vite.config.js):
export default {
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
}nginx:
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;itch.io: HTML 게임의 임베드 옵션에서 "SharedArrayBuffer support"를 켜면 헤더가 대신 설정됩니다.
배포한 뒤 다시 로드하고 검사기를 한 번 더 실행하세요. crossOriginIsolated가 true로 바뀌어야 합니다.
자주 묻는 질문
내 페이지가 교차 출처 격리되어 있는지 어떻게 확인하나요?
위 검사기에 사이트 URL을 입력하세요. 검사기가 서버 측에서 페이지의 Cross-Origin-Opener-Policy와 Cross-Origin-Embedder-Policy 헤더를 읽고 그 조합이 페이지를 격리시키는지 알려줍니다. 직접 브라우저에서 런타임으로 확인하려면 페이지를 열고 콘솔에서 crossOriginIsolated를 실행하세요. 격리가 활성화되어 있으면 true를 반환합니다.
SharedArrayBuffer가 왜 undefined인가요?
브라우저는 SharedArrayBuffer를 교차 출처 격리된 페이지에만 노출합니다. undefined라면 페이지에 Cross-Origin-Opener-Policy: same-origin와 Cross-Origin-Embedder-Policy: require-corp 헤더가 빠져 있거나, 어떤 교차 출처 리소스가 COEP 아래에서 격리를 막고 있는 것입니다. 헤더를 설정하고(위 참고) 다시 테스트하세요.
COOP와 COEP는 무엇인가요?
Cross-Origin-Opener-Policy(COOP)는 페이지가 다른 브라우징 창과 어떻게 상호작용하는지를 제어합니다. Cross-Origin-Embedder-Policy(COEP)는 어떤 하위 리소스를 임베드할 수 있는지를 제어합니다. COOP: same-origin과 COEP: require-corp를 함께 설정하면 페이지가 교차 출처 격리되어 SharedArrayBuffer를 비롯한 강력한 기능들이 풀립니다.
내 WebAssembly 게임이 브라우저에서 로드되지 않습니다. 이것 때문인가요?
대체로 그렇습니다. 스레드를 쓰는 WebAssembly 빌드(일부 Godot과 Unity 웹 익스포트, Emscripten pthreads, ffmpeg.wasm)는 SharedArrayBuffer가 필요하고, 이는 교차 출처 격리를 요구합니다. 위 검사기가 "격리 안 됨"으로 나오면 COOP/COEP 헤더를 설정하거나, 가능한 경우 엔진의 단일 스레드 빌드를 사용하세요.
Cross-Origin-Resource-Policy 헤더가 필요한가요?
격리하려는 페이지 자체에는 필요 없습니다. 문서를 교차 출처 격리시키는 것은 COOP와 COEP이며, 이들은 문서에 붙습니다. Cross-Origin-Resource-Policy(CORP)는 방향이 반대인 다른 헤더입니다. 이것은 교차 출처 리소스(이미지, 폰트, 스크립트)에 붙여서, COEP: require-corp를 쓰는 페이지가 그 리소스를 임베드할 수 있게 해 줍니다. 그래서 검사기가 당신 페이지에서 CORP "설정 안 됨"으로 표시한다면 그건 예상된 것이고 문제없습니다. CORP는 다른 격리된 페이지가 로드해야 하는 자산에만 추가하면 됩니다.
COEP를 켜면 내 서드파티 임베드가 깨지나요?
그럴 수 있습니다. COEP: require-corp를 쓰면 모든 교차 출처 리소스가 Cross-Origin-Resource-Policy를 보내거나 CORS를 사용해야 하며, 그렇지 않으면 차단됩니다. 직접 제어할 수 없는 CDN 자산이나 서드파티 스크립트를 임베드한다면 대신 COEP: credentialless를 사용하세요. 그러면 이 요구 사항을 피할 수 있습니다.
관련 자료
- COOP/COEP와 SharedArrayBuffer — 코드가 포함된 전체 튜토리얼
- WebGL & WebGPU 검사기 — 브라우저의 GPU 기능 테스트
- 게임 로직을 위한 Web Worker — 작업을 메인 스레드 밖으로 옮기기
- 2026년 최고의 웹 게임 엔진 — 어떤 엔진이 스레드를 필요로 하는지
- Cinevva 엔진 — 웹 우선 엔진