개발 지식/WEB 지식

🌐 λ¦¬μ†ŒμŠ€ μΊμ‹œλ‘œ μΈν•œ CORS μ—λŸ¬ ν˜„μƒ κ³ μ°°

인파_ 2023. 3. 6. 09:37

cors-image-cache

λΈŒλΌμš°μ € μΊμ‹œλ‘œ μΈν•œ CORS 문제

CORS(Cross-Origin Resource Sharing)λŠ” μ„œλ‘œ λ‹€λ₯Έ 좜처(Origin)의 λ¦¬μ†ŒμŠ€λ₯Ό κ³΅μœ ν•˜κ³  μ‹Άμ„λ•Œ μ‚¬μš©ν•˜λŠ” 정책을 λ§ν•œλ‹€.

기본적으둜 λΈŒλΌμš°μ €λŠ” SOP(Same Origin Policy) 정책을 λ”°λ₯΄κΈ° λ•Œλ¬Έμ— μ™ΈλΆ€ λ¦¬μ†ŒμŠ€μ— λŒ€ν•΄μ„œ μ°¨λ‹¨ν•œλ‹€. ν•˜μ§€λ§Œ 인터넷은 μ—¬λŸ¬ μ‚¬λžŒλ“€μ—κ²Œ μ˜€ν”ˆλœ ν™˜κ²½μ΄κ³ , 이런 ν™˜κ²½μ—μ„œ μ›ΉνŽ˜μ΄μ§€μ—μ„œ λ‹€λ₯Έ μΆœμ²˜μ— μžˆλŠ” λ¦¬μ†ŒμŠ€λ₯Ό 가져와 μ‚¬μš©ν•˜λŠ” 일은 맀우 ν”ν•œ 일이라 λͺ¨λ“  μ™ΈλΆ€ λ¦¬μ†ŒμŠ€λ₯Ό  λ¬΄ν„±λŒ€κ³  λ§‰μœΌλ©΄ μ§€κΈˆμ²˜λŸΌ 웹이 λ°œμ „ν•˜μ§€ μ•Šμ•˜μ„ 것이닀. λ”°λΌμ„œ μ™ΈλΆ€ λ¦¬μ†ŒμŠ€λΌλ„ ν—ˆμš© κ°€λŠ₯ν•œ μ˜ˆμ™Έ 사항을 λ‘μ—ˆλŠ”λ° 그것이 CORS 정책이닀.

 

[WEB] πŸ“š μ•…λͺ… 높은 CORS κ°œλ… & 해결법 - 정리 λνŒμ™• πŸ‘

μ•…λͺ… 높은 CORS μ—λŸ¬ 메세지 μ›Ή κ°œλ°œμ„ ν•˜λ‹€λ³΄λ©΄ λ°˜λ“œμ‹œ λ§ˆμ£ΌμΉ˜λŠ” 멍멍 같은 μ—λŸ¬κ°€ λ°”λ‘œ CORS 이닀. μ›Ή 개발의 μ‹ μž… 신고식이라고 ν•  μ •λ„λ‘œ, CORSλŠ” λˆ„κ΅¬λ‚˜ ν•œ 번 μ •λ„λŠ” κ²ͺ게 λœλ‹€κ³  해도 과언이

inpa.tistory.com


μš”μ²­ 방식에 따라 λ‹€λ₯Έ CORS λ°œμƒ μ—¬λΆ€

μ΄λ•Œ λΈŒλΌμš°μ €λŠ” HTTP μš”μ²­μ— λŒ€ν•΄μ„œ μ–΄λ–€ λ°©μ‹μœΌλ‘œ μš”μ²­μ„ ν•˜λŠλƒμ— 따라 CORSλ₯Ό μžλ™μœΌλ‘œ ν—ˆμš©ν•˜κΈ°λ„ 막기도 ν•œλ‹€. 예λ₯Όλ“€μ–΄ <img> νƒœκ·Έμ™€ 같은 인라인으둜 λ¦¬μ†ŒμŠ€λ₯Ό μš”μ²­ν•˜λ©΄ λ‹€λ₯Έ 좜처의 λ¦¬μ†ŒμŠ€λΌλ„ 검사 없이 μžλ™ ν†΅κ³Όλœλ‹€. κ·ΈλŸ¬λ‚˜ μžλ°”μŠ€ν¬λ¦½νŠΈ Ajax μš”μ²­μΌ 경우 어김없이 μ°¨λ‹¨λ˜μ–΄ 버린닀.

μš”μ²­ 방식에 따라 λ‹€λ₯Έ CORS λ°œμƒ μ—¬λΆ€λ₯Ό μ’€ 더 μ΄ν•΄ν•˜κΈ° μ‰½κ²Œ μ•„λž˜ html μ½”λ“œλ₯Ό 직접 μž‘μ„±ν•˜κ³  ν…ŒμŠ€νŠΈ ν•΄λ³΄μž. λ˜‘κ°™μ€ μ„œλ²„ λ„λ©”μΈμœΌλ‘œ λΆ€ν„° check.svg 이미지λ₯Ό κ°€μ Έμ˜€μ§€λ§Œ κ²°κ³ΌλŠ” λ‹€λ₯΄κ²Œ λœλ‹€.

β€» 참고둜 ν•΄λ‹Ή ν˜ΈμŠ€νŒ… μ„œλ²„λŠ” cors 섀정이 λ˜μ–΄μžˆμ§€ μ•ŠλŠ” μ„œλ²„λΌκ³  κ°€μ •ν•΄λ³΄μž.

<img src="https://third-party-test.glitch.me/check.svg" alt="이미지">

<script>
    fetch('https://third-party-test.glitch.me/check.svg')
        .then(response => response.blob())
        .then(imgBlob => {
            const imageObjectURL = URL.createObjectURL(imgBlob); // 응닡 받은 이미지λ₯Ό blob 객체둜 λ³€ν™˜
            const img = document.createElement('img'); // 이미지 νƒœκ·Έλ₯Ό μƒμ„±ν•˜κ³ 
            img.src = imageObjectURL; // 이미지 경둜λ₯Ό μ„€μ •ν•œλ’€
            document.body.append(img); // html에 μΆ”κ°€
        })
</script>

 

νŽ˜μ΄μ§€ κ²°κ³Όλ₯Ό 보면 이미지가 ν•˜λ‚˜λ§Œ λ‚˜νƒ€λ‚˜λŠ” κ±Έ λ³Ό 수 μžˆμ„ 것이닀.

cors-image-cache

λΈŒλΌμš°μ € 개발자 λ„κ΅¬μ˜ Network 창을 보면 check.svg 이미지에 λŒ€ν•΄μ„œ λ‘λ²ˆ μš”μ²­μ€ ν–ˆμ§€λ§Œ μžλ°”μŠ€ν¬λ¦½νŠΈ fetch Type으둜 μš”μ²­ν•œ 것이 Statusκ°€ CORS error μž„을 λ³Όμˆ˜κ°€ μžˆλ‹€.

cors-image-cache
cors-image-cache

fetch μš”μ²­ μ²˜λ¦¬μ— λŒ€ν•œ 헀더 λͺ©λ‘μ„ 보면 κ·Έ 이유λ₯Ό μ•Œ 수 μžˆλŠ”λ°, ν΄λΌμ΄μ–ΈνŠΈκ°€ μžμ‹ μ˜ Origin을 μš”μ²­ 헀더에 λ„£μ–΄ μ„œλ²„μ— μ „λ‹¬ν–ˆμ§€λ§Œ, μ„œλ²„μ—μ„  별닀λ₯Έ μ•‘μ…˜ 없이 Access-Control-Allow-Origin 헀더λ₯Ό 응닡해주지 μ•Šμ•˜κ³ , 이λ₯Ό λΈŒλΌμš°μ €κ°€ κ°μ§€ν•˜μ—¬ 사단에 μ°¨λ‹¨ν•œ 것이닀. μ—¬κΈ°μ„œ μ˜€ν•΄ν•˜μ§€ 말아야 ν•  점이 μ„œλ²„λŠ” λ¦¬μ†ŒμŠ€λ₯Ό 정상 μ‘λ‹΅ν–ˆμ§€λ§Œ λΈŒλΌμš°μ €κ°€ cors κ΄€λ ¨ 헀더 μ—†λ‹€κ³  차단해 버린 것이닀.

 

반면 λΈŒλΌμš°μ €λŠ” <img>, <video> 와 같은 일뢀 λ―Έλ””μ–΄ νƒœκ·Έλ₯Ό 톡해 λ¦¬μ†ŒμŠ€λ₯Ό μš”μ²­ν•  CORSλ₯Ό μ œν•œν•˜μ§€ μ•ŠλŠ”λ‹€.κ·Έλž˜μ„œ HTTP μš”μ²­μ‹œ Origin 헀더λ₯Ό μΆ”κ°€ν•˜μ§€ μ•Šκ³ , λΈŒλΌμš°μ €λ„ 별닀λ₯Έ 검사 없이 ν†΅κ³Όν•˜κ²Œ ν•΄μ£ΌλŠ” 것이닀.

cors-image-cache


μΊμ‹œμ— μ €μž₯된 λ¦¬μ†ŒμŠ€λ₯Ό κ·ΈλŒ€λ‘œ 뢈러올 경우

그런데 이 CORSλ₯Ό μ œν•œν•˜μ§€ μ•Šκ³  받은 λ¦¬μ†ŒμŠ€λ₯Ό λΈŒλΌμš°μ €κ°€ 둜컬 μΊμ‹œμ— μ €μž₯ν•˜κ³  μž¬ν™œμš© ν•  경우 λ¬Έμ œκ°€ λ°œμƒν•˜κ²Œ λœλ‹€.

예λ₯Ό λ“€μ–΄ μ•„λž˜μ™€ 같이 이미지λ₯Ό μš”μ²­ ν• κ²½μš° λ¬Έμ œμ—†μ΄ μ„œλ²„λ‘œ λΆ€ν„° μ™ΈλΆ€ λ¦¬μ†ŒμŠ€λ₯Ό λ°›λŠ”λ‹€.

β€» μ°Έκ³ λ‘œ ν•΄λ‹Ή ν˜ΈμŠ€νŒ… μ„œλ²„λŠ” cors 섀정이 λ˜μ–΄μžˆλŠ” μ„œλ²„μ΄κΈ° λ•Œλ¬Έμ— μžλ°”μŠ€ν¬λ¦½νŠΈ ajax μš”μ²­μ—λ„ μ •μƒμ μœΌλ‘œ μ‘λ‹΅ν•˜κ²Œ λœλ‹€.

<img src="https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png" alt="이미지">

<script>
    fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
        .then(response => response.blob())
        .then(imgBlob => {
            const imageObjectURL = URL.createObjectURL(imgBlob);
            const img = document.createElement('img');
            img.src = imageObjectURL;
            document.body.append(img);
        })
</script>

cors-image-cache
cors-image-cache

 

μ΄λ²ˆμ—λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ fetch μš”μ²­μ„ 3초 뒀에 ν•˜λΌκ³  setTimeout() λ©”μ„œλ“œλ₯Ό 톡해 μ„€μ •ν•΄λ³΄μž. μ΄λ ‡κ²Œ ν•˜λŠ” μ΄μœ λŠ” 이미지 νƒœκ·Έλ‘œ μš”μ²­ν•œ sample.png 이미지가 λΈŒλΌμš°μ € 둜컬 μΊμ‹œμ— μ €μž₯ν•˜λ„λ‘ μ‹œκ°„μ„ μ£ΌκΈ° μœ„ν•΄μ„œ 이닀. 그러면 3초 뒀에 λ˜‘κ°™μ€ 이미지 URL을 μš”μ²­ν•  경우 λΈŒλΌμš°μ €λŠ” μžλ™μœΌλ‘œ μ΅œμ ν™”λ₯Ό μœ„ν•΄ μ„œλ²„μ— μš”μ²­μ„ λ•Œλ¦¬λŠ” 것이 μ•„λ‹Œ μΊμ‹œ μ €μž₯μ†Œμ—μ„œ λ¦¬μ†ŒμŠ€λ₯Ό κ°€μ Έμ˜€κ²Œ λœλ‹€.

<img src="https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png" alt="이미지">

<script>
    setTimeout(() => {
        fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
            .then(response => response.blob())
            .then(imgBlob => {
                const imageObjectURL = URL.createObjectURL(imgBlob);
                const img = document.createElement('img');
                img.src = imageObjectURL;
                document.body.append(img);
            })
    }, 3000);
</script>

cors-image-cache
cors-image-cache

그런데 κ²°κ³ΌλŠ” CORS μ—λŸ¬κ°€ λœ¬λ‹€. λΆ„λͺ… μ„œλ²„μ—μ„œ λͺ¨λ“  좜처λ₯Ό ν—ˆμš©ν•˜λŠ” access-control-allow-origin: * 헀더λ₯Ό μ„€μ •ν•˜μ˜€κ³ , setTimeout() λ©”μ„œλ“œλ₯Ό μ „ κΉŒμ§€λŠ” λ¬Έμ œμ—†μ΄ 잘 λ°›μ•„μ™”λŠ”λ° 말이닀.

 

λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ μš”μ²­ / 응닡 헀더λ₯Ό 보면 μ™œ κ·ΈλŸ°μ§€μ— λŒ€ν•œ μœ μΆ”κ°€ κ°€λŠ₯ν•˜λ‹€.

μ•žμ„œ λ§ν–ˆλ“―μ΄ <img> νƒœκ·Έλ‘œ λΆˆλŸ¬μ˜€λŠ” 인라인 λ¦¬μ†ŒμŠ€ μš”μ²­μΌ κ²½μš°μ—” μš”μ²­ ν—€λ”μ—λŠ” Origin 헀더가 μ—†λ‹€. λ”°λΌμ„œ μ„œλ²„μ—μ„œλ„ Access-Control-Allow-Origin 헀더λ₯Ό μ‹€μ–΄ 보내지 μ•Šκ²Œλ˜κ³ , 이 μƒνƒœ κ·ΈλŒ€λ‘œ μΊμ‹œμ— 적재되게 λœλ‹€.

cors-image-cache

그리고 3초 뒀에 λ˜‘κ°™μ€ λ¦¬μ†ŒμŠ€ URL을 μš”μ²­ν–ˆμ„λ•Œ μΊμ‹œ μ €μž₯μ†Œμ— μžˆλŠ” λ¦¬μ†ŒμŠ€λ₯Ό κ°€μ Έμ˜€λŠ”λ°, 응닡 헀더에 Access-Control-Allow-Origin 헀더가 μ—†κΈ° λ•Œλ¬Έμ— μ•„λž˜μ™€ 같이 No ' Access-Control-Allow-Origin' header 라고 μ—λŸ¬ 메세지λ₯Ό λ‚΄λΏœλŠ” 것이닀.

cors-image-cache
cors-image-cache


μΊμ‹œλ‘œ μΈν•œ CORS μ—λŸ¬ 해결방법

 

crossorigin 속성 ν• λ‹Ήν•˜μ—¬ κ°•μ œ CORS 검사 ν–‰ν•˜κΈ°

ν•΄κ²° 방법은 μ•„λž˜μ™€ 같이 <img> νƒœκ·Έμ˜ μ†μ„±μœΌλ‘œ crossorigin 을 ν• λ‹Ήν•΄ μ£Όλ©΄ λœλ‹€.

<img crossorigin src="https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png" alt="이미지">

<script>
    setTimeout(() => {
        fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
            .then(response => response.blob())
            .then(imgBlob => {
                const imageObjectURL = URL.createObjectURL(imgBlob);
                const img = document.createElement('img');
                img.src = imageObjectURL;
                document.body.append(img);
            })
    }, 3000);
</script>

 

crossorigin 속성

<audio> , <img> , <link> , <script> 및 <video> 와 같은 λ―Έλ””μ–΄ μš”μ†Œ 의 crossorigin 속성은 μš”μ†Œκ°€ ꡐ차 좜처 μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” 방법을 μ •μ˜ν•˜λŠ” 속성이닀. 보톡은 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μΏ ν‚€λ‚˜ 인증 헀더λ₯Ό μ‹€μ–΄ μ„œλ²„μ— λ³΄λ‚Όλ•Œ μ‚¬μš©λ˜λŠ” μ†μ„±μ΄μ§€λ§Œ, 이것을 νƒœκ·Έ μ†μ„±μœΌλ‘œ λͺ…μ‹œν•˜λ©΄ 인라인 λ¦¬μ†ŒμŠ€ μš”μ²­μ΄λ“  뭐든 κ°•μ œμ μœΌλ‘œ CORS 정책을 λ”°λ₯΄κ²Œ ν•˜μ—¬ λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ μ—‘μ„ΈμŠ€ κΆŒν•œμ„ μš”μ²­ν•˜κ²Œ ν•œλ‹€.

개발자 λ„κ΅¬μ˜ 헀더 λͺ©λ‘μ„ 보면 더 감이 작힐 것이닀. 비둝 인라인 λ¦¬μ†ŒμŠ€ μš”μ²­μ΄μ§€λ§Œ μš”μ²­ 헀더에 Origin 헀더가 λ‹΄κΈ΄κ±Έ λ³Ό 수 있고 μ„œλ²„λ„ 이에 λ°˜μ‘ν•˜μ—¬ cors 헀더λ₯Ό 응닡함을 λ³Ό 수 κ°€ μžˆλ‹€.

cors-image-cache

λ”°λΌμ„œ 3초 뒀에 ν–‰ν•΄μ§€λ§Œ μžλ°”μŠ€ν¬λ¦½νŠΈ Ajax μš”μ²­ μ—­μ‹œ μΊμ‹œμ— λ¦¬μ†ŒμŠ€λ₯Ό κ°€μ Έμ˜€λ”λΌλ„, 이미 μΊμ‹œ μ €μž₯μ†Œμ— μžˆλŠ” λ¦¬μ†ŒμŠ€λŠ” Access-Control-Allow-Origin 헀더가 μ„€μ •λœ λ¦¬μ†ŒμŠ€ 이기 λ•Œλ¬Έμ— CORS κ΄€λ¬Έ 검사에 ν†΅κ³Όλ˜λŠ” 것이닀.

cors-image-cache


crossorigin 속성이 μžˆλŠ”λ° μ„œλ²„ CORS ν—ˆμš©μ΄ μ•ˆλ  경우

λ°˜λŒ€λ‘œ 만일 μ„œλ²„μ—μ„œ 별닀λ₯Έ cors 헀더 섀정을 ν•˜μ§€ μ•Šμ•˜μ„ 경우 <img crossorigin src> 인라인 μš”μ²­λ„ κ²°κ΅­ cors μ—λŸ¬ λ‘œκ·Έκ°€ λ‚˜νƒ€λ‚˜κ²Œ λœλ‹€. μ™œλƒν•˜λ©΄ crossorigin 속성은 κ°•μ œλ‘œ CORS 검사 관문을 ν–‰ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

cors-image-cache


μ •λ¦¬ν•˜μžλ©΄, μ„œλ²„μ— Origin ν—ˆμš©μ΄ 된 μƒνƒœμ—μ„œ λ™μΌν•œ λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ 쀑볡 μš”μ²­μ„ corsλ₯Ό 톡해 κ°€μ Έμ˜¬ 경우 μœ„μ˜ μ—‡κ°ˆλ¦Όμ„ μ‘°μ‹¬ν•˜λ©΄ λœλ‹€. λ˜ν•œ λΈŒλΌμš°μ € 둜컬 μΊμ‹œ 뿐만 μ•„λ‹ˆλΌ 이밖에도 ν”„λ‘μ‹œ(Proxy)λ‚˜ CloudFront와 같은 CDN μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν• λ•Œλ„ CORS μΊμ‹œ λ¬Έμ œκ°€ λ”°λΌμ˜€κ²Œ λœλ‹€. 보톡 ν΄λΌμš°λ“œμ˜ CDN μ„œλΉ„μŠ€μΌ 경우 μ΄λŸ¬ν•œ 경우λ₯Ό λŒ€λΉ„ν•΄ λ³„λ„λ‘œ Cache Key 섀정을 μ œκ³΅ν•˜λ‹ˆ 이에 λŒ€ν•΄μ„œ 검색해보길 λ°”λž€λ‹€.