CORS란?

CORS(Cross-Origin Resource Sharing , 교차 출처 리소스 공유)는 웹 브라우저에서 보안상의 이유로 다른 출처(도메인, 프로토콜, 포트)의 리소스를 요청할 때 발생하는 보안 정책입니다. 이를 통해 악의적인 도메인이 사용자의 데이터를 무단으로 요청하는 것을 방지합니다.
출처(Origin)란?
CORS(Cross-Origin Resource Sharing)는 번역하면 교차 출처 리소스 공유로 여기서 Origin은 출처를 말합니다. 그럼 과연 이 출처(Origin)은 뭘까요? 출처(Origin)는 프로토콜(Protocol), 도메인(Domain, Hostname), 포트(Port)로 구성되어 있습니다.
따라서 교차 출처의 의미는 두 출처가 서로 다르다는 것을 의미하며, CORS(Cross-Origin Resource Sharing)는 출처가 다른 서버간에 리소스 공유를 허용한다는 의미입니다.

[참고] URL에서 포트 번호를 명시하지 않으면, 기본적으로 프로토콜(스킴)에 따라 기본 포트가 자동으로 설정
HTTP (http://) → 기본 포트 80
HTTPS (https://) → 기본 포트 443
FTP (ftp://) → 기본 포트 21
MySQL (mysql://) → 기본 포트 3306
PostgreSQL (postgresql://) → 기본 포트 5432
MongoDB (mongodb://) → 기본 포트 27017
SOP(Same-Origin Policy)와의 관계
CSRF(Cross-Site Request Forgery, 크로스 사이트 요청 위조)는 웹 보안 취약점 중 하나로, 사용자가 의도하지 않은 요청을 강제로 실행하도록 하는 공격 기법입니다. 공격자는 사용자의 인증된 세션을 악용하여 원하지 않는 요청을 서버에 보내도록 유도할 수 있습니다. 따라서 공격자의 요청이 사용자의 요청인 것 처럼 속이면서 공격하게 됩니다.
이러한 CSRF를 예방하기 위해 브라우저는 서로 다른 출처의 리소스 요청과 응답을 차단하는 SOP(Same-Origin Policy, 동일 출처 정책)을 구현했습니다.
SOP란?
- SOP(Same-Origin Policy, 동일 출처 정책)는 보안 정책으로, 웹 애플리케이션이 클라이언트와 다른 출처(Origin)의 리소스에 접근하지 못하도록 합니다.
- 동일 출처(Same-Origin) 기준: 출처(도메인, 프로토콜, 포트)가 동일
- SOP(동일 출처 정책)이 없으면 악성 사이트에서 사용자의 쿠키를 탈취하거나 민감한 정보를 요청할 수 있습니다.
CORS의 필요성
SOP는 보안상 중요한 역할을 하지만, 다른 출처의 리소스를 많이 사용하는 현대의 웹 어플리케이션에서 다양한 웹 서비스 간의 데이터 공유를 어렵게 만듭니다.
과거에는 프론트와 백엔드를 따로 구성하지 않아 모든 처리를 같은 도메인 안에서 모든 처리가 가능했습니다. 하지만 현대의 웹 어플리케이션에서는 프론트와 백엔드을 나누어 클라이언트가 API를 직접 호출하는 경우가 많습니다. 이런 경우 클라이언트와 API는 도메인(Domain, Hostname)이 다른 경우가 많기 때문에, SOP(동일 출처 정책)의 한계가 있습니다.
따라서 CORS로 특정 조건을 만족할 경우, 다른 출처의 리소스 요청과 응답을 허용하는 역할을 합니다.
CORS 에러가 발생하는 경우
CORS 에러는 백엔드와 프론트엔드가 분리된 환경에서 자주 발생하는 문제입니다. 이 외에도 API 요청의 특성, 브라우저의 보안 정책, 서버의 응답 헤더 구성 등도 CORS 문제를 일으킬 수 있습니다.
1) 프론트엔드와 백엔드가 다른 출처에 위치할 때
- 프론트엔드: http://localhost:3000
- 백엔드: http://localhost:8080
웹 애플리케이션이 클라이언트-서버 구조로 개발되는 경우, 프론트엔드와 백엔드가 서로 다른 출처에서 운영될 수 있습니다. 위와 같이 도메인 또는 포트가 다르면 다른 출처(Cross-Origin) 로 간주되어 브라우저에서 기본적으로 요청을 차단합니다.
2) Ajax 요청에서 다른 출처의 API를 호출할 때
웹 애플리케이션에서 fetch 또는 Axios와 같은 라이브러리를 사용하여 API를 호출할 때, 해당 API가 다른 출처에 위치해 있다면 브라우저는 CORS 정책을 적용하여 요청을 차단할 수 있습니다. 아래 예제 코드를 실행하면, api.example.com에서 CORS 설정을 하지 않았다면 브라우저가 요청을 차단하여 CORS 에러가 발생합니다.
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS Error:', error));
3) 웹 브라우저가 CORS 정책을 적용하여 요청을 차단할 때
- 인증 정보를 포함한 요청 (credentials: include 설정이 필요한 경우)
- 프리플라이트 요청(OPTIONS 메서드) 을 요구하는 경우
- 허용되지 않은 HTTP 메서드 (예: DELETE, PUT 등)
- 허용되지 않은 헤더 (예: Custom-Header, Authorization 등)
브라우저는 기본적으로 보안 강화를 위해 다른 출처에서의 리소스 요청을 막습니다. 특히 위와 같은 요청이 차단될 수 있습니다. 이와 같은 상황에서 서버에서 적절한 CORS 설정을 하지 않으면 요청이 실패하게 됩니다.
CORS 해결 방법
1. 서버에서 Access-Control-Allow-Origin 헤더 설정
Access-Control-Allow-Origin: <origin> | *
백엔드 서버에서 Access-Control-Allow-Origin 헤더를 설정하여 요청을 수락할 출처를 명시적으로 지정할 수 있습니다. 하지만 위와 같이 와일드카드(*)를 설정하면 모든 출처에서의 요청을 허용하지만, 보안상 취약할 수 있습니다. 따라서 아래와 같이 특정 출처만 허용하면 보안성을 높일 수 있습니다.
Access-Control-Allow-Origin: https://rimeir24.tistory.com
2. Spring Boot에서 CORS 설정
Spring Boot에서는 @CrossOrigin 어노테이션을 사용하거나 WebMvcConfigurer를 통해 설정할 수 있습니다. 만약 Spring Security가 활성화된 경우, WebMvcConfigurer 설정만으로는 CORS가 적용되지 않을 수도 있습니다. 따라서 이런 경우에는 SecurityFilterChain을 사용하여 설정해야 합니다.
(1) 컨트롤러에서 특정 엔드포인트에 CORS 허용
@CrossOrigin(origins = "https://example.com")
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/data")
public ResponseEntity getData() {
return ResponseEntity.ok("CORS 요청 성공");
}
}
(2) 전역적으로 CORS 허용 (WebMvcConfigurer 사용)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}
(3) Spring Boot + Spring Security 환경 (SecurityFilterChain 사용)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("https://frontend.example.com"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
3. 프론트엔드에서 프록시 서버 사용하기
CORS 문제를 피하기 위해 웹 애플리케이션이 직접적으로 리소스를 요청하는 대신, 프록시 서버를 사용하여 요청을 우회할 수도 있습니다. 이렇게 하면 웹 애플리케이션이 프록시 서버를 통해 요청을 보내므로, 동일 출처로 인식되어 CORS 에러를 방지할 수 있습니다.
[웹 애플리케이션] → [프록시 서버] → [API 서버]
http://example.com http://example-proxy.com http://api.example.com
React (Vite) 환경에서 개발용 프록시 설정
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
secure: false,
},
},
},
});
4. Nginx에 CORS 허용하기
Nginx를 리버스 프록시로 사용하여 add_header 설정을 추가해 CORS를 허용합니다. 운영 환경에서 보안성을 고려하면서 CORS 문제를 해결하는 데 유용합니다.
server {
location /api/ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
CORS 설정 시 주의사항
- 모든 출처(*)를 허용하면 보안 문제가 발생할 수 있음
- 민감한 요청(예: 인증 정보 포함 요청)에는 allowCredentials: true를 설정해야 함
- 프리플라이트 요청(OPTIONS 메서드)을 고려해야 함
- 환경별로 다르게 설정 (로컬 개발 vs 운영 서버)
[출처 및 참고자료]
'CS > 네트워크' 카테고리의 다른 글
| HTTP 헤더 - 인증, 쿠키, 캐시 (0) | 2025.02.11 |
|---|---|
| HTTP 헤더 - 개요 및 자주 사용되는 헤더 (0) | 2025.02.06 |
| HTTP 상태 코드 (1) | 2025.02.01 |
| HTTP 메서드 (1) | 2025.01.29 |
| HTTP 개요 (0) | 2025.01.24 |