1. CORS 설정

CORS란

CORS(Cross-Origin Resource Sharing)는 다른 출처(Origin)에서 온 요청을 허용할지 결정하는 브라우저 보안 정책입니다.

출처(Origin)는 프로토콜 + 도메인 + 포트의 조합입니다:

<http://localhost:3000>   ← React 개발 서버
<http://localhost:8080>   ← Spring Boot 서버

이 둘은 포트가 다르므로 다른 출처입니다. 브라우저는 보안상 다른 출처로의 요청을 기본적으로 차단합니다.

React에서 API를 호출하면:

// React (localhost:3000) → Spring Boot (localhost:8080)
axios.get('<http://localhost:8080/api/posts>')

CORS 설정이 없으면 브라우저 콘솔에 이런 에러가 나옵니다:

Access to XMLHttpRequest at '<http://localhost:8080/api/posts>' from origin
'<http://localhost:3000>' has been blocked by CORS policy

서버가 "localhost:3000에서 온 요청을 허용한다"고 명시해야 합니다.

SecurityConfig에 CORS 설정 추가

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final JwtAuthenticationEntryPoint authenticationEntryPoint;
    private final JwtAccessDeniedHandler accessDeniedHandler;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))    // 추가
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .formLogin(form -> form.disable())
            .httpBasic(basic -> basic.disable())
            .exceptionHandling(exception -> exception
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler)
            )
            .addFilterBefore(jwtAuthenticationFilter,
                    UsernamePasswordAuthenticationFilter.class)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll()
                .anyRequest().authenticated()
            );

        return http.build();
    }

    // CORS 설정
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of(
                "<http://localhost:3000>",     // React 개발 서버
                "<http://localhost:5173>"      // Vite 개발 서버
        ));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }

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

import가 필요합니다:

import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;

각 설정의 의미:

allowedOrigins: 허용할 출처 목록입니다. React 개발 서버의 주소를 넣습니다. Vite의 기본 포트는 5173이고, CRA(Create React App)는 3000입니다.

allowedMethods: 허용할 HTTP 메서드입니다. OPTIONS는 브라우저가 본 요청 전에 보내는 Preflight 요청에 사용됩니다.

allowedHeaders: 허용할 요청 헤더입니다. *는 모든 헤더를 허용합니다. Authorization 헤더(JWT 토큰)를 보내야 하므로 반드시 허용해야 합니다.

allowCredentials: 쿠키나 인증 헤더를 포함한 요청을 허용합니다.