1523 단어
8 분
CORS
2025-06-14

CORS (Cross-Origin Resource Sharing) 완전 정복#

1. CORS란?#

  • *CORS(Cross-Origin Resource Sharing)**는 직역하면 ‘교차 출처 리소스 공유 정책’

웹 생태계에서는 보안을 위해 기본적으로 다른 출처의 리소스를 제한하는데, CORS는 특정 조건 하에 이를 허용해주는 정책을 말함. 여기서 핵심은 ‘출처(Origin)’

Warning! 출처(Origin)인 Protocol + Hostname + Port를 비교하여 하나라도 다르면 브라우저는 이를 차단하고 아래와 같은 에러를 만남

Access to fetch at ‘...’ from origin ‘...’ has been blocked by CORS policy...

2. 출처 (Origin) 이해하기#

URL의 구성 요소#

URL은 다음과 같이 구성됨.

  • Protocol (Scheme): http, https
  • Host: 사이트 도메인 (예: www.domain.com)
  • Port: 포트 번호 (예: 3000, 8080)
  • Path: 내부 경로
  • Query String: 요청의 key-value
  • Fragment: 해시 태그

동일 출처(Same-Origin)의 기준#

브라우저는 Protocol, Host, Port 이 3가지가 모두 일치해야 ‘같은 출처’로 인식함. 하나라도 다르면 **다른 출처(Cross-Origin)**로 간주하여 차단

비교 예시 기준 URL: https://www.myshop.com (기본 포트 443)

URL접근 가능 여부 (SOP 준수)이유
https://www.myshop.com/user프로토콜, 호스트, 포트 일치
https://myshop.com/user(서브 도메인 정책에 따라 다를 수 있으나 통상 일치로 봄)
http://www.myshop.com/user프로토콜 불일치 (https vs http)
https://en.myshop.com/user호스트 불일치 (도메인 다름)
https://www.myshop.com:8080포트 불일치 (443 vs 8080)

참고: 출처 비교와 차단은 **‘브라우저’**가 수행함. 서버는 정상적으로 200 OK를 응답하더라도, 브라우저가 출처를 비교해 다르면 데이터를 버리고 에러를 띄움 (Server-to-Server 통신에서는 발생하지 않음)

3. 동일 출처 정책 (SOP: Same-Origin Policy)#

SOP는 “동일한 출처에 대해서만 리소스를 공유할 수 있다”는 보안 규칙 같은 출처의 리소스는 자유롭게 가져오지만, 다른 출처의 리소스(이미지, API 등)는 상호작용을 막는 것이 기본 원칙

SOP가 필요한 이유#

출처가 다른 애플리케이션끼리 자유롭게 통신하면 보안에 매우 취약해짐

  • CSRF (Cross-Site Request Forgery): 사용자가 악성 사이트에 접속했을 때, 해커가 심어둔 코드가 사용자의 브라우저를 통해 로그인이 되어 있는 다른 사이트(은행, 포털 등)에 요청을 보내 개인정보를 탈취하거나 조작할 수 있음
  • XSS (Cross-Site Scripting): 악성 스크립트 실행 방지

즉, SOP는 악의적인 스크립트가 실행되지 않도록 브라우저 차원에서 사전에 방지하는 안전장치임. 하지만 현대 웹에서는 외부 리소스 사용이 필수적이므로, CORS 정책을 지킨 요청에 한해 예외적으로 허용해주는 것

4. CORS 동작 원리 (기본 흐름)#

기본적으로 클라이언트와 서버가 헤더를 주고받으며 확인

  1. 클라이언트: 요청 헤더의 Origin 필드에 현재 출처를 담아 보냄
  2. 서버: 응답 헤더의 Access-Control-Allow-Origin 필드에 리소스 접근이 허용된 출처를 담아 보냄
  3. 브라우저: 자신이 보낸 Origin과 서버가 준 Access-Control-Allow-Origin을 비교. 유효하면 통과, 아니면 폐기(CORS 에러)

5. CORS 작동 시나리오 3가지#

① 예비 요청 (Preflight Request)#

가장 일반적인 시나리오. 브라우저가 본 요청을 보내기 전에 안전한지 확인하기 위해 OPTIONS 메서드로 미리 찔러보는 것

  • 과정:
    1. 브라우저 → 서버: OPTIONS 요청 (Origin, 사용할 메서드, 헤더 정보 포함)
    2. 서버 → 브라우저: 허용 여부 응답 (Access-Control-Allow-Origin 등)
    3. 브라우저: 안전하다 판단되면 실제 본 요청(Actual Request) 전송
  • 캐싱: 매번 예비 요청을 보내면 성능이 저하되므로 Access-Control-Max-Age 헤더로 결과를 일정 시간 캐싱할 수 있음

② 단순 요청 (Simple Request)#

예비 요청 없이 바로 본 요청을 보내는 방식. 하지만 조건이 매우 까다로워 잘 쓰이지 않음

  • 조건:
    • 메서드: GET, POST, HEAD 중 하나
    • 헤더: Accept, Content-Type 등 기본 헤더만 허용
    • Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain만 가능. (application/json은 불가능하므로 대부분 Preflight를 탐)

③ 인증된 요청 (Credentialed Request)#

클라이언트가 쿠키, 토큰 등 인증 정보를 포함해 요청을 보낼 때 사용함. 설정이 더 엄격

  • 클라이언트 설정:
    • fetch, axios, jQuery 등에서 credentials: 'include' 또는 withCredentials: true 설정 필수
  • 서버 설정 (중요):
    1. Access-Control-Allow-Credentials: true 로 설정해야 함
    2. Access-Control-Allow-Origin에 와일드카드()를 쓸 수 없음. 반드시 구체적인 출처(예: http://localhost:3000)를 명시해야 함

6. CORS 해결 방법 (솔루션)#

① 서버에서 헤더 설정 (백엔드)#

가장 정석적인 방법. 백엔드에서 허용할 출처를 명시

Spring Framework (Global 설정)

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080") // 허용할 출처
.allowedMethods("GET", "POST") // 허용할 메서드
.allowCredentials(true) // 인증 정보 허용
.maxAge(3000); // Preflight 캐싱 시간
}
}

Spring Framework (Controller 별 설정)

@CrossOrigin(origins = "*", methods = RequestMethod.GET)
@GetMapping("/url")
public ResponseEntity<?> findAll() { ... }

Nginx 설정

location /api/ {
# CORS 헤더 추가
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
# Preflight 요청 처리
if ($request_method = OPTIONS) {
return 204;
}
}

② 프록시(Proxy) 서버 이용#

브라우저의 CORS 정책은 브라우저와 서버 간의 통신에서만 적용됨. 서버와 서버 간 통신에는 적용되지 않는 점을 이용

Webpack Dev Server (React 개발 환경) 개발 중에는 로컬 프록시를 띄워 CORS를 우회할 수 있음. 브라우저는 같은 출처인 로컬 프록시로 요청을 보내고, 프록시가 실제 백엔드로 요청을 전달하는 방식

  • package.json 설정 예시:

    {
    "proxy": "https://myshop.com:8080"
    }

주의: React의 Proxy 설정은 개발 환경(Development)에서만 동작함. 배포 시에는 백엔드나 Nginx 등에서 실제 CORS 설정을 해줘야 함

CORS
https://devlog.jpstudy.org/posts/2025/cs/cors/
저자
SY
게시일
2025-06-14
라이선스
CC BY-NC-ND 4.0