CORS란?

Louie·2022년 12월 3일
1

프로젝트

목록 보기
4/6
  • 프로젝트를 진행하면서 발생했던 CORS 에러를 해결하기 위해 학습했던 내용을 정리했다.
  • CORS를 알아보기 전에 먼저 SOP에 대해 설명하겠다.

SOP(Smae Origin Policy)

  • 같은 출처를 가진 요청에 대한 응답을 허용하는 정책이다.
  • 여기서 출처(Origin)는 Protocol, Host, Port가 모두 같아야 같은 출처라고 한다.

CORS란?

  • CORS는 Cross-Origin Resource Sharing의 약자로 그대로 해석하면 다른 출처 간에 자원을 공유한다는 뜻이다.
    • 출처가 다른 경우를 cross origin이라고 부른다.
  • 조금 더 자세히 설명하면 CORS는 추가적인 HTTP 헤더를 사용해서 한 출처에서 다른 출처의 자원에 접근할 수 있도록 알려주는 체제이다.
  • SOP로 인해 기본적으로 다른 출처 간의 정상적인 응답은 불가능하지만 CORS 정책을 지킨다면 예외적으로 다른 출처 간의 리소스를 사용할 수 있다.

CORS 요청 방식

Preflight Request

  • 브라우저는 기본적으로 cross-origin 요청(진짜 요청)을 보내기 전에 OPTIONS 메소드를 통해 preflight 요청을 전송한다.
  • Preflight 요청시 아래과 같이 Access-Control-Request-Headers, Access-Control-Request-Method, Origin 헤더를 통해 cross-origin 요청에 대한 출처, 메서드, 추가 헤더를 나타낸다.

  • cross-origin 요청에 대한 정보를 서버에서 허용한다면 서버는 아래와 같이 Preflight 요청이 성공한다.
    • 서버에서 2xx번 응답 코드를 반환하면 Preflight 요청이 성공했다고 한다.
  • Preflight 요청이 성공하면 응답으로 Access-Control-Allow-Origin, Access-Control-Allow-Methods 등 서버에서 허용하는 자원들을 나타내는 헤더들을 반환하고 cross-origin 요청을 전송하게 된다.

  • 하지만 Preflight 요청이 실패한다면 cross-origin 요청을 전송하지 않으며 아래와 같이 개발자 도구에서 CORS error라는 내용을 확인할 수 있다.

Simple Request

  • Preflight Request 없이 바로 실제 요청을 보내면서 것이다.
  • Simple Request는 아래에 나열된 조건을 모두 만족해야 사용할 수 있다.

Simple Request 조건

  • GET, HEAD, POST 중의 한 가지 방식을 사용해야 한다.
  • Content-type 헤더의 값이 아래중에 하나여야 한다.
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 커스텀 헤더를 전송하지 말아야 한다.

Credentials Request

  • 인증 관련 헤더를 포함할 때 사용하는 요청이다.
  • 클라이언트 / 서버 모두 Credentials Request에 대한 설정을 해줘야 정상적으로 통신할 수 있다.
    • 클라이언트 : credentials 옵션을 include로 설정
    • 서버 : 응답 헤더에 Access-Control-Allow-Credentials를 true로 반환
  • Credentials Request를 사용할 경우 서버에서 와일드 카드(*)를 통해 모든 Origin을 허용했다면 에러가 발생하기 때문에 반드시 허용할 Origin을 특정지어서 적어줘야 한다.

스프링에서의 CORS 설정 방법

@CrossOrigin 사용

  • 아래와 같이 @CrossOrigin을 사용한 컨트롤러에만 cors 관련 설정을 적용할 수 있다.
@RequestMapping("/chats")
@RestController
@CrossOrigin(origins = "http://cors.com", allowedHeaders = "* ")
public class ChatController {
	// 생략
}

WebMvcConfigurer를 상속한 설정 파일 구현

  • 아래와 같이 WebMvcConfigurer를 상속 받고 addCorsMappings 메서드를 override 한 다음 해당 클래스를 빈으로 등록한다.
  • 해당 방법을 사용하면 @CrossOrigin과 다르게 CORS 설정을 전역적으로 설정해줄 수 있다.
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*")
            .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
            .exposedHeaders("*")
            .allowedHeaders("*");
    }

}

스프링에서 Preflight Request 처리 방법

  • 필자는 검증 관련 인터셉터에서 Preflight Request가 들어왔고 인터셉터는 4xx번 상태 코드를 반환하여 결국 Preflight Request가 실패한 것을 경험했다.
  • 아래와 같이 메서드가 OPTIONS인 경우 인터셉터를 통과하도록 구현하여 해당 문제를 해결했다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
    Object handler) {

    if (request.getMethod().equals(HttpMethod.OPTIONS.name())) {
        return true;
    } 
		// 생략
}

마무리

  • 단순히 CORS 설정만 적용해주면 CORS 에러가 발생하지 않는줄 알았지만 Preflight Request가 실패하여 CORS 에러가 발생했다.
  • CORS의 동작 방식에 대한 이해도가 낮아서 문제를 해결하는데 시간이 오래 걸린 부분이 아쉬웠고 앞으로는 기본적인 원리를 학습해야겠다는 생각이 들었다.

Reference

https://jerry-hs.tistory.com/43

https://www.youtube.com/watch?v=-2TgkKYmJt4

profile
백엔드 개발자를 준비하고 있는 Louie입니다.

0개의 댓글