[ Spring boot ] CORS란? 스프링부트에서의 CORS 설정
반응형

처음으로 back과 front를 다른 서버에서 구현을 하는 작업을 하여 테스트를 해보려고 하니, CORS에러가 떴다.. CORS가 뭔지, 어떻게 해결해줄지 적어보기..

 

CORS란?

Cross-Origing Resource Sharing의 줄임말로, 교차 출처 리소스 공유라는 뜻이다. -> 다른 출처의 리소스를 공유

여기서 말하는 출처는 protocol, host, port를 합친 것을 의미한다. 즉...

이렇게 브라우저에 접속한 위치가 출처라고 보면 된다.

 

같은 출처? 다른 출처?

현재의 웹페이지 주소가 https://diddl.tistory.com/manage일 때, 같은 출처는 무엇이고 어떤 것이 다른 출처인지 보자

URL 같은 출처 여부 이유
https://diddl.tistory.com/manage/newpost 같은 출처 Protocol, Host, Port 동일
https://diddl.tistory.com/manage/newpost/?type=post 같은 출처 Protocol, Host, Port 동일
http://diddl.tistory.com/manage 다른 출처 https가 아님, Protocol 다름
https://diddl.tistory.com:81 다른 출처 Port가 다름
http://diddl.naver.com/manage 다른 출처 Host가 다름

이런 식으로 같은 출처란 Protocol, Host, Port가 모두 동일해야 한다.

 

SOP(Same-Origin Policy), 동일 출처 정책이란?

웹 생태계의 보안 정책으로 "같은 출처에서만 리소스를 공유할 수 있다"라는 정책이다.

Postman이나 ARC로 API 테스트를 했을 때는 동일 출처이기 때문에 문제가 없었을 것이다. 하지만 React App과 Back Spring App을 돌리면 다음과 같은 문제가 발생한다.

Spring App의 Port는 8080인데 React App은 3000번 Port를 사용하기 때문에 브라우저에서 다른 출처를 허용하지 않아 발생하는 문제이다.

이러한 SOP 정책이 등장한 이유는 서로 다른 두 개의 어플리케이션이 마음대로 소통할 수 있다면, 보안상 매우 위험하기 때문이다. 브라우저의 개발자 도구창만 열어봐도 어떤 서버와 통신하는지, 리소스의 출처가 어딘지 쉽게 열람할 수 있기 때문에, 다른 출처의 어플리케이션이 서로 통신하는 것에 제약이 없으면, CSRF나 XSS와 같은 공격으로 정보 탈취가 쉬워진다.

하지만 웹에서는 다른 출처의 리소스를 가져와 사용하는 일이 빈번하다. 따라서 몇 가지 예외 조항을 두고 출처가 다른 리소스 요청도 허용할 수 있도록 해주었는데, 그것이 바로 CORS 정책을 지킨 리소스 요청이다.

 

출처를 비교하는 곳은 사실 서버가 아니라 브라우저에서 구현된다. 그래서 CORS 정책을 위반하는 요청을 하더라도 서버에서는 정상적으로 응답을 하지만, 브라우저에서 그 응답을 분석하고 출처를 비교하여 CORS 정책에 위반된다면 해당 응답을 버려버린다.

서버에서는 정상적으로 응답했다 하는데 브라우저에서는 에러가 나니.. 처음 접했을 땐 오류 해결이 힘들었다..

이 문제를 해결하기 위해서는 Back이나 Front 둘 중에 한 군데에서 CORS 설정을 해주면 된다. 이전에 먼저 동작 원리를 알아보자.

 

CORS 동작원리

CORS의 동작 방식은 Simple Request(단순 요청 방법)과 Preflight request(예비 요청을 먼저 보내는 방법)가 있다.

 

Simple Request

단순 요청 방법은 서버에게 그냥 바로 요청을 보내는 방법이다. 아래 그림과 같다.

서버에 API를 요청하고 서버에서는 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에게 보내면 브라우저는 해당 헤더를 확인하고 CORS를 수행할지 판단한다.

여기에도 조건이 있는데, Simple Request의 조건은 아래와 같다.

  • 요청 메서드(method)는 GET, HEAD, POST 중 하나여야 한다.
  • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
  • Content-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나를 사용해야 한다.

사실 이 조건들은 REST API를 사용한다면 만족하기 어렵다.. 많은 REST API들이 Content-Type을 application/json로 사용하기 때문이다..ㅠㅠ

 

Preflight Request

예비 요청을 먼저 보내는 방법은 예비 요청을 보내서 안전한지 판단한 후 본 요청을 보내는 방법이다. 아래 그림과 같다.

OPTIONS 메서드로 서버에 예비 요청을 보낸 다음 서버는 응답으로 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단한다. 후에 확인이 완료되었다면 본 요청인 GET 요청을 통해 리소스를 받아온다.

 

위에서 Back이나 Front 둘 중에 한 군데에서 CORS 설정을 해주면 된다고 했다. 나는 SpringBoot을 사용하기 때문에 Back에서 처리해주는 방법을 간단히 소개한다..

 

SpringBoot CORS 에러 해결

스프링 부트에서 CORS를 해결하는 방법은 4가지 정도가 있다. Filter, CrossOrigin, WebMvcConfigure, Spring Security corsConfigurationSource 설정 방법이 있는데 나는 이중 corsConfigurationSource방식을 사용했다.

 

해당 프로젝트에서 Spring Security를 사용했기에 여기서 한 번에 설정해주어야 했다.. 처음에 WebMvcConfigure방식으로 따로 하려했으나 동작을 안했다.. 여튼 Spring Security 설정파일인 WebSecurityConfig.java 파일에 다음과 같이 설정해주었다.

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // ... 중략 
   
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // cors설정 추가
        http
                .cors()
                .configurationSource(corsConfigurationSource());

       // ... 중략
    }

    // ... 중략

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://localhost:3000"); // local 테스트 시
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        configuration.addExposedHeader("Authorization");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

 

CorsConfigurationSource 메소드를 보자면...

  • addAllowedOrigin :  허용 출처 설정해주기 -> 교차 출처를 허용할 출처를 적어준다.. Front 출처를 적어두었다.
  • addAllowedMethod : 요청 시 오는 Http Method 허용을 말한다. -> 나는 모두 허용해주었다.
  • addAllowedHeader : 헤더 허용 -> 역시 모두 허용
  • addExposedHeader : 노출시킬 헤더 -> 응답 시에 프론트에서 받았을 때 노출될 헤더를 설정
    -> 이부분 힘들었다.. 분명 프론트에서 응답은 200 OK로 뜨는데 헤더에 JWT 토큰만 없는 이상한 상황..ㅠㅠ Authorization 헤더를 노출시키도록 설정해주니 해결되었다.

 

SpringBoot에서의 CORS 설정은 이렇게 간단하게 끝..!

반응형