CORS(Cross-Origin Resource Sharing)는 웹 브라우저에서 도메인 간 리소스 요청을 제어하는 보안 메커니즘입니다. 즉, 브라우저는 한 도메인(origin)에서 다른 도메인(origin)의 리소스를 요청할 때 보안 규칙을 적용하는데, 이 과정에서 동작하는 규칙이 CORS입니다.
기본 원리
웹 애플리케이션은 기본적으로 동일 출처 정책(Same-Origin Policy)에 의해 보호됩니다. 동일 출처 정책은 다음 세 가지가 모두 일치하는 경우에만 리소스를 자유롭게 공유하도록 허용합니다:
CORS 요청이 일어날 때, 브라우저는 서버에 HTTP 요청을 보내며 "이 요청이 안전한지" 확인합니다. 서버는 특정 헤더를 응답에 포함하여 브라우저에게 "이 요청은 허용된다"고 알려줍니다. 브라우저는 이를 바탕으로 자원을 사용하도록 허가받습니다.
예: CORS 요청 흐름
클라이언트(브라우저)에서 API 요청을 보냅니다 (예: https://frontend.com에서 https://api.backend.com으로 요청).
서버(api.backend.com)는 응답에서 Access-Control-Allow-Origin 헤더를 포함시켜, 어느 도메인에서 접근이 가능한지 명시합니다.
Access-Control-Allow-Origin: https://frontend.com
브라우저는 응답에 포함된 Access-Control-Allow-Origin 값을 확인하고, 요청을 허용할지 결정합니다.
CORS 요청에는 두 가지 주요 유형이 있습니다: 단순(Simple) 요청과 예비(Preflight) 요청.
1) 단순 요청(Simple Request)
HTTP 메서드가 GET, POST, HEAD이고, Content-Type이 일반적인 값(application/x-www-form-urlencoded, text/plain, multipart/form-data)인 경우에는 단순 요청으로 처리됩니다. 이 경우, 브라우저는 CORS 정책을 자동으로 처리하고, 서버는 Access-Control-Allow-Origin 헤더만 포함시키면 됩니다.
2) 예비 요청(Preflight Request)
CORS 요청이 안전하지 않은 메서드(PUT, DELETE 등) 또는 커스텀 헤더를 포함하는 경우, 브라우저는 먼저 서버에 OPTIONS 메서드로 예비 요청(Preflight Request)을 보내 서버가 이 요청을 허용할지 확인합니다. 서버는 이를 처리하고 CORS 정책에 맞는 응답을 반환해야 합니다.
예비 요청의 응답 헤더 예시:
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
스프링 부트(Spring Boot)에서는 CORS를 쉽게 설정할 수 있습니다. 주로 두 가지 방법으로 설정 가능합니다.
1) Controller 레벨에서 CORS 설정
특정 컨트롤러나 핸들러 메서드에 대해서만 CORS를 허용하고 싶다면 @CrossOrigin 어노테이션을 사용할 수 있습니다.
@RestController
@RequestMapping("/api")
public class MyController {
@CrossOrigin(origins = "https://frontend.com") // 특정 도메인에만 허용
@GetMapping("/data")
public ResponseEntity<String> getData() {
return ResponseEntity.ok("Hello, CORS!");
}
}
이 설정은 특정 경로와 특정 메서드에만 적용되며, https://frontend.com 도메인에서만 접근할 수 있습니다.
2) Global CORS 설정 (전역 설정)
애플리케이션 전체에 대한 CORS 정책을 설정하고 싶다면 WebMvcConfigurer를 사용해 전역적으로 설정할 수 있습니다.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해 CORS 허용
.allowedOrigins("https://frontend.com") // 특정 Origin 허용
.allowedMethods("GET", "POST", "PUT", "DELETE") // 허용할 HTTP 메서드
.allowedHeaders("Content-Type", "Authorization") // 허용할 헤더
.allowCredentials(true); // 쿠키나 인증 정보도 함께 전송 허용
}
}
이 방식은 애플리케이션 전체에 적용되며, 모든 API 경로에서 지정한 도메인에 대해서만 CORS를 허용하게 됩니다.
1) CORS 정책 오류
브라우저에서 "CORS 정책 오류"가 발생할 수 있습니다. 이는 주로 서버가 요청한 Origin을 허용하지 않았기 때문입니다. 서버 응답에 Access-Control-Allow-Origin 헤더가 없거나 잘못된 경우에 이런 오류가 발생합니다.
Access to XMLHttpRequest at 'https://api.backend.com' from origin 'https://frontend.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
해결 방법:
서버에서 올바르게 Access-Control-Allow-Origin 헤더를 설정했는지 확인해야 합니다.
필요하다면, 예비 요청에 대한 처리를 추가로 설정해야 할 수 있습니다.
2) 쿠키 인증 관련 문제
만약 클라이언트가 쿠키나 인증 정보를 함께 보내려면, Access-Control-Allow-Credentials: true 설정을 추가로 해야 합니다. 이때 allowedOrigins는 와일드카드(*)를 사용할 수 없습니다.
registry.addMapping("/**")
.allowedOrigins("https://frontend.com")
.allowCredentials(true); // 인증 정보 허용
보안 강화: CORS는 기본적으로 보안을 강화하는 기술이므로, 가능한 한 최소한의 도메인만 허용하는 것이 중요합니다. allowedOrigins에 모든 도메인(*)을 허용하는 것은 위험할 수 있습니다.
동적 CORS 처리: 복잡한 시스템에서는 사용자의 권한이나 상황에 따라 동적으로 CORS 정책을 적용할 수 있습니다.
HTTPS 사용: CORS는 보안과 밀접한 관련이 있으므로, 특히 민감한 데이터가 다뤄지는 경우에는 반드시 HTTPS 프로토콜을 통해 CORS 요청을 처리하는 것이 권장됩니다.
CORS는 도메인 간 리소스 공유를 안전하게 허용하는 중요한 보안 메커니즘입니다. 스프링 부트에서는 @CrossOrigin 어노테이션이나 WebMvcConfigurer를 통해 쉽게 설정할 수 있으며, 올바르게 설정하지 않으면 클라이언트에서 CORS 오류가 발생할 수 있습니다.