트러블슈팅 - CORS 에러

김동헌·2023년 12월 5일
0

CS

목록 보기
3/9
post-thumbnail

친구랑 프로젝트를 진행하는 도중에 아래 사진을 받았습니다.
이전의 프로젝트에서도 경험한 CORS의 에러인데 해결 방법(django)만 알고 어떠한 에러인지 자세히 알지 못해서 포스팅했습니다.

백엔드 개발자가 포스트맨으로도 확인하고 테스트 코드가 다 통과하더라고 발생할 수 있는 에러입니다. 왜일까요 ?

아래 포스팅을 읽고 답을 얻으셨으면 좋겠습니다.

스프링 부트에서는 처음 해결해보네요 : >


CORS

Cross-Origin Resource Sharing

교차 출처 리소스 공유를 뜻하는 CORS는
서로 다른 출처에서 리소스를 공유 한다는 뜻입니다.

여기서 Origin 감이 오시나요 ?

  • CORS에서 Origin이란 웹 컨텐츠의 출처를 의미한다고 합니다.
    그림으로 한번 살펴보겠습니다.

URL은 상단의 형태로 구성되어 잇습니다.

이 중 Protocol + Host + Port 를 합쳐 부르는 말이 Origin(출처)입니다.

즉, CORS 허용 설정을 하지 않았다면 !

  • 서버와 다른 Origin(출처)를 가진 곳에서 서버의 Origin에 요청을 보낼 때 CORS 에러가 발생하는 것입니다.

개발 중인 서버의 Originhttp://localhost:8080

서버에 요청을 보낸 React의 Originhttp://localhost:3000

가정할 때 OriginPort 부분이 동일하지 않기 때문에 문제가 발생하는 것입니다.



교차 출처를 왜 제한할까 ?

CORS에 대한 개념은 알겠습니다. 그렇다면 왜 제한할까요 ?

서로 다른 출처를 가진 두 애플리케이션이 자유롭게 서로를 접근할 수 있는건 매우 위험합니다. 보안을 위해서죠

크롬에서 F12를 눌러 개발자 도구를 통해서도 어떤 서버와 통신하고, 어떤 정보를 주고 받는지 등등 여러 정보를 확인할 수 있습니다.

만약 다른 출처를 가진 애플리케이션에 접근 제한이 없다면 XSSCSRF 등을 통해 중요한 데이터를 빼갈 수 있는거죠 !

중요한 부분은 이 CORS를 확인하는 로직이 서버가 아닌 브라우저에 구현되어 있습니다. 그렇게 때문에 Postman과 같은 툴을 이용해 API요청을 보낼때는 CORS에러가 발생하지 않았던거죠 !


동작 방식

기본적으로 웹 클라이언트 애플리케이션이 다른 출처의 리소스 요청을 할 때 HTTP 프로토콜을 사용합니다.

(1) 요청 헤더에 Origin 추가

  • 브라우저가 다른 출처의 리소스에 요청을 보낼 때, HTTP 헤더에 Origin 필드가 포함되어 해당 리소스를 요청한 출처가 전송됩니다.

(2) 응답 헤더에 Access-Control-Allow-Origin 추가

  • 서버는 해당 리소스에 접근하는 것을 허용하는 출처를 나타내는 Access-Control-Allow-Origin 헤더를 응답에 포함시킵니다.

    이 헤더는 클라이언트의 출처와 일치하는 경우에만 브라우저에서 리소스에 접근이 허용됩니다.

(3) 출처 비교

  • 브라우저서버로부터 받은 Access-Control-Allow-Origin 값과 요청 시 보낸 Origin 값을 비교합니다.

    이 두 값이 일치하면 브라우저는 리소스에 접근을 허용하고, 그렇지 않으면 차단합니다.

(4) 응답 유효성 판단

  • 브라우저는 비교를 통해 출처가 일치하는지 여부를 확인하고, 이를 기반으로 응답이 유효한지를 판단합니다.

    만약 출처가 일치하지 않거나 허용되지 않은 경우, 브라우저는 CORS 오류를 발생시킵니다.

동작방식 참고하기 좋은 자료


해결 방법

서버에서 http://localhost:3000 오리진의 요청에 대해 CORS를 허용해주면 되겠죠 ?

파일구조입니다 ! MvcConfiguration 파일을 생성하고 아래 코드를 작성했습니다.


@Configuration
@EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("*")
                .allowCredentials(false)
                .maxAge(3000);
    }
}

addCorsMappings 메서드를 오버라이딩해서 모든 RequestMapping에 대하여, 통신을 주고 받은 React Origin(port:3000)의 요청에 대해 CORS를 허용해주었습니다.

allowCredentials(false) - 기본 설정

  • 자격 증명을 허용하지 않음을 의미합니다.
    자격 증명이란 쿠키, HTTP 인증 등의 정보를 의미합니다.
    이 설정을 false로 하면 브라우저가 요청 시에 자격 증명을 포함하지 않습니다.

maxAge(3000);

  • Preflight 요청의 최대 유효 기간을 설정합니다.
    Preflight 요청은 실제 요청을 보내기 전에 브라우저가 서버에게 허용 여부를 물어보는 사전 요청입니다.

    이를 통해 브라우저와 서버 간의 허용 여부를 확인하고, 그 정보를 기억하는 시간을 maxAge로 설정할 수 있습니다. 여기서는 3000초(5분) 동안 Preflight 요청의 결과를 기억하게 됩니다.

  • Preflight의 자세한 동작방식은 아래 참고 자료가 잘 설명해주셨습니다 !
    동작방식 참고하기 좋은 자료 evan-moon

아래는 Spring Secutiry 참고 자료 혹시 몰라서 첨부합니다 : >
Spring Security 참고자료

profile
백엔드 기록 공간😁

0개의 댓글