프로젝트/react

React와 Vite를 사용한 프로젝트에서 Spring 서버와의 CORS 오류 해결하기

코딩연습중 2024. 10. 13. 21:09

최근에 React와 Vite를 사용하여 웹 애플리케이션을 개발하는 과정에서 Spring 서버와의 통신 중 CORS(Cross-Origin Resource Sharing) 오류를 경험했다. 이 포스트에서는 CORS 오류의 원인과 해결 방법에 대해 작성해보겠다.

CORS란?

CORS는 웹 브라우저가 다른 출처의 리소스에 접근할 수 있도록 허용하는 메커니즘. 기본적으로 웹 브라우저는 보안상의 이유로 다른 출처의 리소스에 대한 요청을 제한한다. 따라서, React 애플리케이션이 Spring 서버와 통신할 때, 서로 다른 출처로 인식되면 CORS 오류가 발생할 수 있다.

 

이렇게 인증 버튼을 누르면 서버에 사용자의 이메일을 전달하여 해당 이메일에 인증번호를 보내줘야 한다.

const SIGNUP_API_BASE_URL = '/api/member';
//const SIGNUP_API_BASE_URL = 'http://localhost:8080/member';

//이메일 전송
export const SendEmailApi = async (email: string) => {
  try {
    const data = { email: email };
    const response = await axios.post(SIGNUP_API_BASE_URL + '/sendEmail', data,);
    console.log('response', response);
    return response.data;
    //console.log(response);
  } catch (error) {
    console.error('Error fetching boards:', error);
    alert('이메일 전송 문제');
    return; // 에러 발생 시 빈 배열 반환
  }
};

 

위와 같이 axios를 통해 서버와 통신한다. 

 

이제 CORS 오류를 피하기 위해 내가 썼던 방법들을 열거하겠다.

 

1. vite.config.ts 파일 server proxy 설정

import react from '@vitejs/plugin-react-swc';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080', // Spring Boot 서버 URL
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''), // '/api' 경로를 제거,
        secure: false,
        ws: true,
      },
    },
  },
});

 

이렇게 설정해두면 '/api'가 포함된 경로의 origin(protocol + host + port)을 http://localhost:8080으로 변경해주고,

'/api' 경로를 제거하여 결론적으로 localhost:5137/api/member/sendEmail이 localhost:8080/member/sendEmail 이렇게 변경돼서 요청을 보낸다. 

 

그리고 사실 CORS 설정은 server에서 하는 게 맞기 때문에 spring에서 처리하는 방법을 살펴보았다.

 

스프링에서 cors 설정을 하는 방법은 두가지가 있다.

1. CrossOrigin annotation을 사용하여 컨트롤러에 직접 도메인을 허용해주는 방식

     - 이건 controller가 많아질 수록 비효율적이라고 생각이 듦.

2. WebMvcConfigurer을 이용하여 global하게 허용 도메인 설정  

 

나는 두번째 방법이 더 효율적이라고 생각이 들어 2번을 선택했다.

2. Spring WebMvcConfigurer 이용하여 WebConfig 파일 생성

spring에서 WebConfig.java 파일을 생성해서 다음과 같이 작성하였다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // API 경로에 대한 CORS 설정
                .allowedOrigins("http://localhost:5137") // Vite의 개발 서버 포트
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(6000L);
    }
}

 

allowedOrigins에 허용할 origin을 넣고,

allowedMethods에는 허용할 HTTP method를 넣으면 된다.

(중간중간에 찾아보니 allowedOrigins 말고 allowedOriginPatterns도 있는 것 같은데 모든 도메인을 허용하고 싶을 때 allowedOrigins(*) 이런 식으로 사용하지 말고 allowedOriginPatterns(*) 이렇게 사용해야 하는 듯하다.)

 

 

3. Spring Security 파일 Cors 설정

그리고 spring Security를 쓰기 때문에 거기에서도 또 cors 설정을 해주어야 한다.

 

...
http
        .cors((cors) -> cors.configurationSource(request -> {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowedOrigins(List.of("http://localhost:5137")); // 허용할 출처
            config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 허용할 HTTP 메서드
            config.setAllowedHeaders(List.of("*")); // 허용할 헤더
            config.setAllowCredentials(true); // 자격 증명 허용
            return config;
        }));
...
http
                .authorizeHttpRequests((auth) -> auth
                        //.requestMatchers("/login", "/api/member/register", "/", "/member/sendEmail").permitAll()
                        .anyRequest().permitAll());
                        //.requestMatchers("/admin").hasRole("ADMIN")
                        //.anyRequest().authenticated());

 

위와 같이 security 파일에도 cors 설정을 해주어야 한다.

(근데 그냥 cors()만 해줘도 되는건지 저렇게 작성해야하는건지는 잘 모르겠음. 알아보기)

원래는 requestMatchers로 허용할 path를 따로 지정해줘야하는데 잠깐 테스트를 위해 그냥 anyRequest().permitAll()이라고 작성했다.

 

또 csrf은 disable 해놨다.

 

이렇게 cors 설정을 마쳤으니 잘 될 거라고 생각했고 잘 돼야 하는데 왜인지 모르게 자꾸 문제가 생겼다.

 

이렇게 오류가 나서 네트워크 창을 확인해보니

 

요청 헤더의 origin에는 문제가 없어보이고

응답헤더에 Acces-Controll-Allow-Origin: *이라고 되어있고 문제도 없어보이는데 왜 오류가 나는 것인지 모르겠다.

 

 

 

이것 때문에 계속 찾아보고 했는데 알고 보니까 오타로 인한 문제였다......

아무튼 덕분에 CORS에 대한 공부가 됐다

 

 

참고: https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F

https://velog.io/@leehyunho2001/CORS