Access to XMLHttpRequest at 'http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTradeDev?_wadl&type=xml&serviceKey=서비스키&LAWD_CD=11110&DEAL_YMD=201512' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
막상 공공 API라고 해서 사용하려고 했는데 안된다면 대략 난감이다. 이런 문제는 보안상 이유로 서버에서 OpenAPI를 만들면서 자바스크립트를 이용한 직접 접근을 차단한 것이기 때문에 뭐라 할 수도 없다.
이번 포스트에서는 이 오류의 의미가 무엇인지, 어떻게 대처해야 하는지 살펴보자.
SOP(Same Origin Policy: 동일 근원 정책)
자바스크립트는 웹에서 심각한 보안상의 문제를 발생시키곤 한다. 그래서 다른 사이트에서 자바 스크립트에서 접근하지 못하도록 막아 놓았다. 즉 동일 근원(Same Origin)에서만 접근이 가능하게 하는 정책이다.
위 그럼에서 보듯이 동일 도메인에서 ajax 호출은 전혀 제한이 없다. 하지만 별도의 도메인에 ajax 요청을 하게 되면 SOP 위반에 의해 오류를 발생시킨다.
여기서 동일 도메인이란 프로토콜과 호스트명(ip), 포트가 같다는 것을 의미한다.
진행 과정
SOP를 처리하기 위해 브라우저는 요청 URL이 다른 도메인일 경우 본 요청 전에 preflight라는 사전 요청을 보내 호출 가능한 권한이 있는지 확인한다. 이를 위해 OPTIONS 메서드로 본 요청과 동일한 경로로 요청을 날려본다. 이 과정에서 권한이 없으면 본 요청을 날리지 않고 오류를 뿜어내게 된다.
2의 요청이 잘 처리된다면 3의 응답에 Access-Control-Allow-Origin 속성에 서버의 리소스를 사용할 수 있는 origin 정보가 넘어오는데 여기에 요청 도메인이 있다면 본 요청을 할 수 있고 없다면 오류가 발생하게 된다.
처리 방법 - 비추
CORS 문제에 대한 처리 방법은 여러가지가 있는데 먼저 비추 방법을 살펴보자. 이를 먼저 다루는 이유는 무책임한 블러그들에서 이 방법을 소개하고 생각없는 개발자들이 따라하기 때문에 이렇게 하지 말라는 취지로 먼저 정리한다.
브라우저에서 옵션 처리
앞서 설명했듯이 SOP를 확인하기 위해 브라우저 차원에서 preflight를 날리는데 브라우저의 실행 옵션에서 이 과정을 생략할 수 있다. 그러면 당연히 위 그림의 2, 3 과정이 생략되고 바로 본 요청을 진행하게 된다.
크롬의 경우 --disable-web-security 옵션을 주고 실행하면 사용이 가능하다. 하지만 오류가 나는 고객에게 브라우저 실행 옵션을 변경하라는건 말이 안된다.
크롬 플러그인 설치
크롬의 플러그인을 이용하면 위 그림의 3 부분처럼 응답 헤더에 임의로 Accsss-Control-Allow-Origin:*을 추가해서 요청 가능한 것 처럼 만들어줄 수 있다.
@CrossOrigin은 Method나 클래스 레벨에 사용할 수 있는 애너테이션으로 접속을 허용할 origin 목록을 설정해주면 된다.
// 클래스 레벨 선언: 여기 선언된 모든 mapping에 적용된다.
@CrossOrigin(origins = {"http://192.168.0.1:8080", "http://192.168.0.2:8080" })
@RestController
public class CorssampleApplication {
// 이 메서드에만 한적적으로 적용한다.
@CrossOrigin(origins = {"*" })
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
WebMvcConfigurer 이용
웹과 관련된 설정을 담당하는 WebMvcConfigurer 구현체의 addCrosMappings 메서드를 사용하면 중앙 집중적으로 관련 설정을 처리할 수 있다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 모든 요청에 대해 사이트의 접속을 허용한다.
registry.addMapping("/**")
.allowedOrigins("http://192.168.0.1:8080","http://192.168.0.2:8080");
}
}