SSR, 테스트 서버, 운영 서버를 운영하면서 CORS를 피할 수 없었습니다. 경험이 부족했을 때 학자형 스타일로 문제를 직면하기보다 전투형으로 개발하면서 무지했던 상황들을 기록하고자 합니다.
누구나 웹 개발을 하며 개발자 도구를 열어볼텐데 시뻘건 줄을 마구마구 마주합니다.
CORS ERROR, 정확히 이해해보자!
자바스크립트에서 요청은 기본적으로 서로 다른 도메인에 대한 요청을 보안상 제한합니다. 브라우저는 또한 기본적으로 하나의 서버 연결만 허용되도록 설정되어 있습니다.
Same Origin과 Cross Origin 정책 -> 바로 이러한 정책이 존재하기 때문에 CORS ERROR가 발생하는 것입니다. Cross-Origin Resource Sharing이라는 단어로서 "교차 출처 리소스 공유 정책" 을 의미합니다.
기본적으로 요청에 의해 에러가 발생하면 서버 문제라고 생각하지만 출처를 비교하는 로직 자체는 브라우저에서 구현된 스펙입니다. 따라서 서버에서는 요청에 대한 응답을 해주는데 브라우저가 응답을 분석해 에러를 뿜는다고 생각하면 됩니다.
필자가 작성한 쿠키의 도메인 설정과 관련해서도 출처, 즉 Origin에 대해 이해 해야 합니다.
Same Origin이라는 의미는 URL의 구성 요소 중에서도 Protocol, Host, Port 3가지가 동일하면 Same Origin이라고 판단합니다.
브라우저 정책이라면 서버끼리 통신할때는 문제가 없지 않을까?
실제로, 브라우저가 아닌 서버 간에 통신을 할때는 정책이 적용되지 않습니다. 그래서, 서버 단 코드에서 서버로 API를 요청하면 CORS 에러로부터 자유로워질 수 있습니다.
실제로, Next JS를 활용해 SSR을 하게 되면 API 서버와 서버 간의 통신을 하기 때문에 CORS와 같은 정책 문제를 해결할 수 있습니다.
브라우저가 동일 출처 정책에 따라서 다른 출처의 리소스를 차단하며 에러가 발생했더라도 CORS, 다른 출처 리소스 공유에 대한 허용과 비허용 정책에 따라서 SOP 정책을 회피하면 된다.
브라우저는 HTTP 요청 Header에 Origin을 담아서 전달합니다.
그렇다면, 서버는?
서버 또한 Access-Control-Allow-Origin을 담아서 클라이언트로 전달합니다.
그렇다면 클라이언트는 Origin과 서버가 보내준 Accss-Control-Allow-Origin을 비교하여 유효성을 판단합니다.
필자는 Spring Boot 프레임워크를 활용해서 개발하였다.
따라서 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers를 통해 브라우저에게 요청이 안전하다는 것을 알려줍니다.
if (CorsUtils.isPreFlightRequest(request)) {
return true;
}
위와 같이, 브라우저는 예비 요청을 통해 서버와 잘 통신되는지 확인하는데 이 역할은 안전한 요청인지 미리 확인하는 것이다.
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3001")
.allowedOrigins("http://www.ssafsound.com")
.allowedOrigins("https://www.ssafsound.com")
.allowedOrigins("https://test.ssafsound.com")
.allowedOrigins("https://api.ssafsound.com")
.allowedOrigins("http://ssafsound.com")
.allowedOrigins("https://ssafsound.com")
.allowedMethods(ALLOWED_METHOD_NAMES.split(","))
.allowedHeaders("*")
.allowCredentials(true)
.exposedHeaders("*");
}