Spring 프레임워크를 2.x 버전에서 3.x로 마이그레이션하는 과정에서 예상치 못하게 여러 이슈가 발생해 롤백을 했던 경험이 있었습니다. 이번 블로그 포스트에서는 실제로 발생했던 CORS 이슈 및 기타 주요 문제와 이를 해결했던 방법에 대해 공유하고자 합니다.
1. CORS 이슈 배경
Spring 3.x로 마이그레이션한 이후, 프론트엔드와 백엔드 간 통신을 시도할 때 CORS 관련 오류가 발생했습니다. 브라우저 콘솔에는 다음과 같은 오류 메시지가 나타났습니다:
CORS redirect is not allowed for a preflight request.
Spring 2.x 였을 때는 특별한 설정없이 cors 이슈가 없었는데, 버젼 문제일 거라고 생각을 하고 어떻게 해결할 것인지 생각을 해보았습니다.
2. CORS 프리플라이트 요청의 원리
브라우저에서 다른 도메인으로의 요청이 발생하면 보안상의 이유로 먼저 프리플라이트 요청(preflight request)을 보냅니다. 이는 주로 OPTIONS 메서드를 사용하여 서버가 실제 요청을 허용할 수 있는지 확인하는 과정입니다.
프리플라이트 요청의 과정은 다음과 같습니다:
- 브라우저가 프리플라이트 요청을 전송: 실제 요청(GET, POST 등)을 보내기 전에 OPTIONS 메서드를 사용해 서버에 허용 여부를 확인합니다.
- 서버가 응답: 허용된 출처(origin), 메서드, 헤더 등이 포함된 CORS 헤더를 반환해야 합니다.
- 프리플라이트 요청이 성공하면 실제 요청 전송: 서버가 허용을 명확히 응답한 경우에만 브라우저는 본 요청을 실행합니다.
만약 서버가 정확한 CORS 설정으로 응답하지 않거나 리디렉션이 발생하면 CORS 에러가 발생하게 됩니다
3. Spring 2.x에서의 CORS 설정 방법
Spring 2.x에서는 주로 WebMvcConfigurer를 사용하여 CORS 설정을 간단히 구성할 수 있었습니다:
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://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
}
이 설정은 잘 작동했으나, Spring 3.x로 마이그레이션 후 문제가 발생했습니다.
4. Spring 3.x에서의 변화
Spring 3.x에서는 CORS 관련 설정 방식에 몇 가지 주요 변경점이 생겼습니다:
- 기본 CORS 설정 적용 범위의 변화: Spring Security와의 통합으로 인해 CORS 설정이 더 정교해졌고, Spring Security 설정에서 별도로 추가 설정이 필요할 수 있습니다.
- Spring Boot 기본 설정 변경: Spring Boot 3.x에서는 일부 기본 CORS 정책이 강화되어 추가적인 커스터마이징이 필요합니다.
5. CORS 이슈 해결 방법
Spring 3.x로의 마이그레이션 과정에서 발생했던 CORS 문제는 CorsFilter를 직접 구현하고 OPTIONS 메서드에 대한 응답을 명시적으로 처리하여 해결했습니다. 다른 방식은 효과가 없었기 때문에 최종적으로 아래와 같은 필터를 사용했습니다:
import org.springframework.stereotype.Component;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomCorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
해결 방법 설명:
- 모든 요청에 대해 CORS 헤더를 설정합니다.
- OPTIONS 메서드로 들어오는 프리플라이트 요청은 명시적으로 200 OK 상태를 반환하도록 처리합니다.
- 이 필터를 사용함으로써 브라우저의 프리플라이트 요청이 정상적으로 처리되고, 리디렉션 문제나 CORS 오류가 더 이상 발생하지 않았습니다.
6. 문제 해결 확인
각 문제를 해결한 후에는 단위 테스트와 통합 테스트를 통해 CORS 문제, Redirect 405 오류 등이 재발하지 않는 것을 확인했습니다. 브라우저와 서버 간 통신, 데이터베이스 쿼리, JSON 직렬화 등 모든 핵심 기능이 정상적으로 동작함을 최종적으로 확인했습니다.
7. 마무리
Spring 3.x로의 마이그레이션은 단순히 버전 업그레이드가 아니라 다양한 설정과 라이브러리 호환성을 종합적으로 점검해야 하는 작업입니다. 이 블로그 포스트가 비슷한 문제를 겪는 개발자들에게 도움이 되길 바라며, 마이그레이션을 준비할 때 발생할 수 있는 잠재적 문제들을 미리 검토하는 것이 중요합니다.
'Java' 카테고리의 다른 글
Java 멀티스레딩: Runnable과 Callable 인터페이스의 차이점 (0) | 2024.04.07 |
---|---|
일반 재귀 대 테일리 커전: 깊은 이해와 올바른 선택 (0) | 2024.04.01 |
@Schedule 통해서 스케줄러 돌 때 데이터가 많아질 때 상황의 생각 (0) | 2024.03.27 |
Java 8 스트림(Stream) 이해하기: 데이터 처리의 혁신 (0) | 2024.01.29 |
Java에서 hashCode()와 equals()를 함께 오버라이드하는 이유 (1) | 2024.01.22 |