CORS 오류 해결

ksngh·2024년 7월 11일

자바스프링

목록 보기
2/8

프론트엔드 서버와 백엔드 서버를 띄워서 카카오 로그인 API를 이용하던 중 CORS 오류가 발생했다.
생소한 오류였기에 CORS란 무엇인지, 그리고 해결 방법에 대해 찾아보았다.

CORS란 무엇인가

CORS는 Cross-Origin Resource Sharing의 약자로, 교차 출처 리소스 공유를 의미한다. 웹 페이지가 현재 자신의 출처(origin)와 다른 출처의 리소스를 요청할 때 CORS 정책이 적용된다. 출처는 프로토콜, 도메인, 포트를 포함한 URL의 일부로, 예를 들어 http://example.com과 https://example.com은 서로 다른 출처로 간주된다.

기본적으로 웹 브라우저는 보안상의 이유로 동일 출처 정책(Same-Origin Policy)을 따른다. 동일 출처 정책은 한 출처에서 로드된 문서나 스크립트가 다른 출처의 리소스에 접근하는 것을 제한한다. 이는 악의적인 웹 사이트가 사용자의 데이터에 무단으로 접근하거나 조작하는 것을 방지하기 위함이다.

하지만 현대 웹 애플리케이션에서는 다양한 출처의 리소스를 필요로 하는 경우가 많다. 예를 들어, 프론트엔드 애플리케이션이 API 서버와 통신하거나, 다른 도메인의 이미지나 글꼴을 사용하는 경우가 있다. 이런 상황에서 동일 출처 정책만으로는 충분하지 않기 때문에 CORS가 도입되었다.

CORS의 작동 방식

CORS는 서버가 특정 출처에서 오는 요청을 허용할지 말지를 결정하는 HTTP 헤더를 추가함으로써 작동한다. 기본적인 CORS 요청과 이를 허용하는 서버의 설정 예시는 다음과 같다.

간단한 요청(Simple Request)
간단한 요청은 다음과 같은 조건을 충족할 때 발생한다:

HTTP 메서드는 GET, POST, HEAD 중 하나입니다.
HTTP 헤더는 Accept, Accept-Language, Content-Language, Content-Type (단, Content-Type은 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나여야 함)만 사용한다.
브라우저는 간단한 요청을 보낼 때, 자동으로 요청 헤더에 Origin 헤더를 추가한다. 서버는 이 요청을 받을 때 Access-Control-Allow-Origin 헤더를 통해 어떤 출처의 요청을 허용할지를 응답한다.

http
GET /resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com

사전 요청(Preflight Request)
간단한 요청의 조건을 충족하지 않는 경우, 브라우저는 먼저 사전 요청(preflight request)을 보낸다. 이 요청은 OPTIONS 메서드를 사용하며, 실제 요청을 보내기 전에 서버가 요청을 허용하는지 확인한다.

http
OPTIONS /resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: X-Custom-Header

사전 요청에 대한 응답으로 서버가 허용하는 메서드와 헤더를 명시하면, 브라우저는 실제 요청을 진행한다.

CORS 설정 방법

스프링 부트에서 CORS 설정

스프링 부트에서는 @CrossOrigin 애너테이션이나 WebMvcConfigurer를 사용하여 CORS를 설정할 수 있다.


// @CrossOrigin 애너테이션 사용

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @CrossOrigin(origins = "http://example.com")
    @GetMapping("/resource")
    public String getResource() {
        return "Hello, CORS!";
    }
}

// WebMvcConfigurer 사용
import org.springframework.context.annotation.Bean;
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 {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("http://example.com")
                        .allowedMethods("GET", "POST", "PUT", "DELETE")
                        .allowedHeaders("*")
                        .allowCredentials(true);
            }
        };
    }
}

필자는 두 번째 방법을 택했고, 주의할 점은 스프링 시큐리티를 사용한다면 필터에 cors 옵션을 꼭 추가해주어야 한다는 것이다. (필터 뒤에 .cors() 붙이면 된다.)

profile
백엔드 개발자입니다.

0개의 댓글