
1. 문제발생
Spring Boot 에서 CORS 에러 문제를 해결하는 방법입니다. 인계받은 프로젝트에서는 Spring Boot 를 미들웨어로 사용 중인데, 수정사항이 생겨 반영 후 첫 배포를 해보니 클라이언트에서 CORS에러가 발생하고 있었습니다.
우선 간단히 통신 구조를 설명 해보자면 위 그림과 같이 클라이언트의 요청을 받아 분석서버로 전송하고 응답을 클라이언트로 전달해주는 형태로 되어있습니다.
클라이언트에서는 403 에러가 발생하고 있었습니다. 403 에러는 지정된 URL에 액세스 할 수있는 권한이 없기 때문에 발생하는 에러로, URL이 유효하지만 사용자의 요청을 수행할 수 없다는 것을 의미
합니다.
2. 원인 파악
이제 Spring Boot 앱에서 원인을 찾아봅니다. 우선 우리의 미들웨어는 SSL인증서를 사용중이며, https 프로토콜을 사용합니다. (클라이언트와 분석서버에는 http 프로토콜을 사용중입니다.)
보통은 개발환경에서 CORS문제를 해결하기 위해 전체 Origin에 대해 허용하는 경우가 많습니다. 아래 코드처럼요.
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 WebMvcConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*")
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE").maxAge(MAX_AGE_SECS);
}
}
우리 코드도 마찬가지였습니다. (그동안으 왜 된거지..)
왜 이게 갑자기 문제가 되었을까? 의문이 생겼지만 아마도 이전 배포버전에서는 SSL인증서 적용이 안되었거나, 배포할때 다른 설정을 적용했기 때문일 것 같습니다. 제가 인계받은건 git레포지토리라 통신에 영향을 줄만한 수정사항이 없었는데 이런 문제가 발생했기 때문입니다.
하지만 구글링을 해보니 SSL이 적용되어 https를 사용할땐 allowdOrigins에 특정 오리진을 지정해주어야 한다고 합니다. 이제 문제를 해결해봅시다.
3. 문제 해결
기존의 전체를 허용하던 allowedOrigins
함수의 인자를 변경해주면 됩니다.
allowedOrigins(”*”) => allowedOrigins(“클라이언트 URL”)
만약 허용할 Origin 이 여러개라면 ,로 구분하여 나열해주면 됩니다.
ex) allowedOrigins("클라이언트URL1", "클라이언트URL2", ..., "클라이언트URLn")
아래는 수정한 코드 입니다.
// WebMvcConfig.java
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 WebMvcConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("클라이언트 URL")
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
.allowedHeaders("Authorization", "Content-Type", "X-Requested-With")
.exposedHeaders("Authorization", "Content-Type") // 브라우저에 노출할 HTTP 헤더 설정
.allowCredentials(true) // 쿠키 허용 설정
.maxAge(MAX_AGE_SECS);
}
}
코드를 수정하고 다시 배포했더니 요청이 잘 수행되었습니다.
4. 느낀점
이번 문제는 원인을 파악하고 난 이후에 조치는 쉬웠지만, 문제의 원인을 찾기가 어려웠습니다. 내가 작성한 코드가 아닌데다 이전 배포판은 잘 돌아가고 있기 때문에 내가 무언가를 놓치거나 실수했을거라고 생각한게 컸던것 같습니다. 하지만 클라이언트의 요청부터 하나하나 뜯어보고 분석해보니 결국엔 원인을 찾을 수 있었습니다. 통신흐름을 파악하고 Postman으로 요청해서 클라이언트의 요청과 비교해보고 어느부분이 문제인지 파악하는 과정도 중요한것 같습니다.
인수인계받을때 문서를 많이 참조하였지만 최신화가 안된 부분도 있어서 오히려 헷갈리기도 했습니다. (이전 담당자에게 연락해봤지만 뾰족한 해답을 얻지는 못했습니다..)
읽어주셔서 감사합니다.