#architecture

CORS란?

웹 개발을 하다 보면 한 번쯤은 부딪히게 되는 주제가 CORS이다. 이 포스팅에서는 CORS가 무엇이며 CORS 예외를 어떻게 해결해야 하는지 알아보자.

배경 및 정의

기존 브라우저 정책은 서로 다른 도메인으로부터 리소스가 필요한 경우, 보안상의 이유로 다른 도메인의 리소스를 가져오는 것이 불가능했다. (SOP : Single-Origin-Policy) 하지만 어플리케이션을 개선하고 쉽게 개발하기 위해선 다른 도메인에 요청을 보내는 일은 필연적이다. 이를 해결하고자 등장한 표준 기술이 CORS이다.

CORS란 Cross Origin Resource Sharing 의 약자로 도메인이 다른 자원에 리소스를 요청할 때 접근 권한을 부여하는 메커니즘이다. 쉽게 말하자면 친구의 물건을 쓰려면 친구가 제한하는 규약안에서 사용해야 하듯, 다른 도메인의 자원을 쓰려면 자원의 주인이 허락한 규약을 지켜야 하는 것이고 이러한 규약을 표준화한 것이 CORS 이다.

  • 여기서 말하는 도메인이란 Resource를 의미하며 프로토콜, 호스트, 포트를 의미한다. (ex. http : 프로토콜, localhost : 호스트, 8080: 포트)

CORS가 발생하는 예시

Client와 Server라는 스프링부트 프로젝트를 시작하고, Client에서 Server로 fetch 요청을 쏘는 예제를 생각해보자.

  • Server Code
@RestController
public class ServerSideController {

    @GetMapping("/server")
    public String message() {
        return "server";
    }
}

서버에서는 단순히 String 을 리턴하는 RestController 를 하나 만들어준다.

  • Client Code
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Client - Server</title>
  </head>
  <body>
    <script>
      fetch('http://localhost:8080/server').then(response => {
        alert(response);
      });
    </script>
  </body>
</html>

클라이언트 쪽에서는 단순히 index.html 에서 서버로 요청을 쏘도록 설정하였다. 주의할 점은 CORS는 클라이언트가 다른 도메인으로 요청을 보내야 하기 때문에, 서로 다른 포트를 설정하였다(서버는 8080, 클라이언트는 7070으로 설정하였다.)

위와 같이 설정하고 [http://localhost:7070/](http://localhost:7070/) 으로 접근하면 아래와 같은 CORS 예외가 발생한다. 이는 클라이언트는 http://localhost:7070 도메인을 사용하고 서버는 [http://localhost:8080](http://localhost:8080) 을 사용하고 있기 때문에, 다른 도메인에 접근하지 못하도록(프로토콜과 호스트는 같지만, 포트가 다르기 때문에) 예외를 발생시키는 것이다.

스크린샷 2020-07-18 오후 2 06 32

해결책

가장 단순한 해결책은 @CrossOrigin 을 사용하는 것이다. 아래와 같이 @CrossOrigin 을 사용하면 다른 도메인의 클라이언트가 나의 서버에 요청 보내는 것을 허용하는 것이다.

@RestController
public class ServerSideController {

    @CrossOrigin("http://localhost:7070")
    @GetMapping("/server")
    public String message() {
        return "server";
    }
}

세부적으로 자신의 서버에서 CrossOrigin 을 설정하고 싶다면 아래의 방법과 같이 Configuration 을 추가하는 방법도 있다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}

PS

처음 CORS를 접했을 때 의문은 왜 서버는 되는데 브라우저에서만, 다른 도메인으로의 요청이 막혀있지?

였다. 애초에 Single Origin Policy가 브라우저의 정책이기 때문에 당연하겠지만 합리적인 이유에 대해서 찾으면 추가로 포스팅 할 예정이다.