본문 바로가기

WEB

[Spring] CORS policy에러, 'localhost:3000' has been blocked by CORS policy

두번쨰 프로젝트가 끝이났습니다... 배운것도 많고 아쉬움도 많은 프로젝트였습니다. 프로젝트에서 겪은 문제들을 정리해보자 합니다.
먼저 프론트엔드 1명, 백엔드 3명 풀스택 2명으로 구성되어 있으며

저는 풀스택을 맡게되었습니다

프로젝트의 주제는 여행용 모임통장입니다.

다음과 같이 진행중입니다.

  • Java 17
  • Spring Boot 3.2.0
  • Gradle
  • React
문제

문제는 Cors에러 였습니다. 저희 개발 순서는 다음과 같습니다.

백엔드 API 개발 -> Swagger 테스트 -> 프론트에서 적용

Swagger에서는 정상작동을 확인했기에 로직의 문제 보다는 설정이나 프론트 axios가 잘못됐다고 판단하였습니다.

 

 

에러를 보니 Cors 에러가 맞는것 같습니다. 

저희 프로젝트는 로그인 이후에는 JWT 토큰을 header에 담아서 요청을 보내야 응답을 하게 되어있습니다. 또한 axios 설정에서 header에 응답을 보내게 설정해 놨습니다.

import axios from "axios";

const axiosAPI = axios.create({ baseURL: process.env.REACT_APP_URL });

axiosAPI.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("accessToken");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    return config;
  },
  (error) => {
    console.log(error);
    return Promise.reject(error);
  }
);

axiosAPI.defaults.withCredentials = true;

export default axiosAPI;

 

 

 

 

 

로컬스토리지에 토큰이 저장되는것을 보니 백엔드에 문제가 있는것 같습니다. 

 

WebConfig 파일

package com.noah.backend.global.config;

import com.noah.backend.global.interceptor.NicknameValidInterceptor;
import com.noah.backend.global.resolver.AccessTokenArgumentResolver;


import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final AccessTokenArgumentResolver accessTokenArgumentResolver;
    private final NicknameValidInterceptor nicknameValidInterceptor;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000", "https://localhost:3000", "http://localhost:8080", "https://localhost:8080", "https://j10b106.p.ssafy.io/", "http://j10b106.p.ssafy.io") // 허용할 출처를 여기에 명시
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowCredentials(true);
    }

}

 

리액트의 포트인 localhost:3000이 제대로 allow 되어있었습니다. 문제는 SecurityConfig파일에 있엇습니다.

 

SecurityConfig 파일

package com.noah.backend.global.config;

import com.noah.backend.global.entrypoint.JwtAuthenticationEntryPoint;
import com.noah.backend.global.filter.EmailVerificationFilter;
import com.noah.backend.global.filter.JwtAuthenticationFilter;
import com.noah.backend.global.filter.TokenExceptionFilter;
import com.noah.backend.global.filter.TokenRefreshRequestFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final TokenExceptionFilter tokenExceptionFilter;
    private final EmailVerificationFilter emailVerificationFilter;
    private final TokenRefreshRequestFilter tokenRefreshRequestFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {

        security
                .httpBasic(basic -> basic.disable())
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
        ;

        security
            .authorizeHttpRequests((authorize ->
            {
                authorize.requestMatchers(
                    "/api-docs/**",
                    "/v2/api-docs/**",
                    "/v3/api-docs/**",
                    "/webjars/**",
                    "/swagger/**",
                    "/swagger-ui/**",
                    "/swagger-config/**",
                    "/swagger-resources/**",
                    "/api/v1/member",
                    "/api/v1/member/social",
                    "/api/v1/member/login/**",
                    "/api/v1/member/nickname/**",
                    "/api/v1/member/email/**",
                    "/api/v1/member/password-reset",
                    "/api/v1/exchange/rateinfo",
                    "/api/v1/bank/qr/withdraw",
                    "/api/v1/Suggest/nonlogin",
                    "/ws/**",
                    "/pub/**",
                    "/sub/**"
                ).permitAll();
                authorize.anyRequest().authenticated();
            }))
        ;

        security
                .sessionManagement(sessionManager -> {
                    sessionManager.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })
        ;

        security
                .addFilterBefore(emailVerificationFilter,
                        UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(tokenRefreshRequestFilter,
                        UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationFilter,
                        UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(tokenExceptionFilter,
                        UsernamePasswordAuthenticationFilter.class)
        ;

        security.exceptionHandling(handlingConfigurer -> {
            handlingConfigurer.authenticationEntryPoint(jwtAuthenticationEntryPoint);
        });

        return security.build();
    }

}

 

문제 해결

 

 

 

이미 WebConfig파일의 addCorsMapping에서 cors에 대한 설정을 끝냈는데 이후 SecurityConfig에서 이를 사용하지 않는다고 해서 생긴 문제였습니다.

 

수정 전

@Bean
    public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {

        security
                .httpBasic(basic -> basic.disable())
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
        ;

 

수정 후

@Bean
    public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {

        security
                .httpBasic(basic -> basic.disable())
                .csrf(csrf -> csrf.disable())

        ;

 


초보 개발자의 글이라 부족한 점이 많습니다. 잘못된 점 등을 말씀해주시면 감사히 받겠습니다.