HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🍗
[New] 조규현팀
/
🏗️
Tech Store
/
👻
CORS 설정
👻

CORS 설정

담당자들
카테고리
Skill
주제
CORS
나의 블로그
완료율%
프로젝트
인스타뀨램
상태
완료
⚙️ 설정 방법⁉️ DELETE, PATCH 요청 오류 발생그 이유는…? 🤔그래서 어쩔..? 🐶🪬 설정 정보 분리 3가지 방법[공통] YAML 파일 설정1. 설정 정보를 클래스 파일로 분리2. @Value 사용3. @ConstructorBinding 사용 (By 진형님)🐶 Origin만 설정하고 모두 허용하기YAML 파일 설정

⚙️ 설정 방법

  • 더미 컨트롤러 생성
    • @RestController @RequestMapping("/api/cors") public class CorsRestController { @GetMapping public ApiResponse<String> get() { return new ApiResponse<>("Accept Get Method!"); } @PostMapping public ApiResponse<String> post() { return new ApiResponse<>("Accept Post Method!"); } @PatchMapping public ApiResponse<String> patch() { return new ApiResponse<>("Accept Patch Method!"); } @DeleteMapping public ApiResponse<String> delete() { return new ApiResponse<>("Accept Delete Method!"); } }
  • 더미 프론트 프로젝트 생성
    • notion image
      해당 프로젝트는 에서 다운로드 후 손쉽게 사용 가능합니다. (필요하신 분들은 README에서 소개된 내용대로 사용하면 됩니다. 😁 )
       
  • 설정 파일 추가
    • @Configuration public class WebMvcConfig implements WebMvcConfigurer { public WebMvcConfig(CorsConfig corsConfig) { this.corsConfig = corsConfig; } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http:localhost:3000"); } }
 

⁉️ DELETE, PATCH 요청 오류 발생

notion image
로그 내역을 보면 GET, POST 는 성공 했지만! DELETE, PATCH 는 실패한 것을 확인할 수 있습니다.
 

그 이유는…? 🤔

// ✨ 첫 번째 클래스 public class CorsRegistry { private final List<CorsRegistration> registrations = new ArrayList<>(); public CorsRegistration addMapping(String pathPattern) { // ✨ 여기서 설정 → 두 번째 클래스 CorsRegistration registration = new CorsRegistration(pathPattern); this.registrations.add(registration); return registration; } } // ✨ 두 번째 클래스 public class CorsRegistration { private final String pathPattern; private CorsConfiguration config; public CorsRegistration(String pathPattern) { this.pathPattern = pathPattern; // Same implicit default values as the @CrossOrigin annotation + allows simple methods // ✨ 여기서 Default 설정을 해주게 됩니다. → 마지막 클래스 this.config = new CorsConfiguration().applyPermitDefaultValues(); } } // ✨ 마지막 클래스 public class CorsConfiguration { // ✨ 여기서 Default Methods 값이 GET, POST 밖에 없는 것을 확인할 수 있습니다. private static final List<String> DEFAULT_PERMIT_METHODS = Collections.unmodifiableList( Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name())); public CorsConfiguration applyPermitDefaultValues() { if (this.allowedOrigins == null && this.allowedOriginPatterns == null) { this.allowedOrigins = DEFAULT_PERMIT_ALL; } if (this.allowedMethods == null) { this.allowedMethods = DEFAULT_PERMIT_METHODS; // ✨ 여기서 설정!! this.resolvedMethods = DEFAULT_PERMIT_METHODS .stream().map(HttpMethod::resolve).collect(Collectors.toList()); } if (this.allowedHeaders == null) { this.allowedHeaders = DEFAULT_PERMIT_ALL; } if (this.maxAge == null) { this.maxAge = 1800L; } return this; } }
 

그래서 어쩔..? 🐶

  • allowedMethods를 통해 추가 설정 해주면 됩니다.
    • @Configuration public class WebMvcConfig implements WebMvcConfigurer { public WebMvcConfig(CorsConfig corsConfig) { this.corsConfig = corsConfig; } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http:localhost:3000") .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PATCH.name(), HttpMethod.DELETE.name()); } }
      이렇게 해주고 나면…?
      notion image
      정상적으로 4개의 메서드가 모두 성공적으로 응답이 됩니다.
 

🪬 설정 정보 분리 3가지 방법

[공통] YAML 파일 설정

cors: allowed: api: /api/cors url: http://localhost:3000 method: GET, POST, PATCH, DELETE

1. 설정 정보를 클래스 파일로 분리

  • CorsConfig 설정 파일 생성
    • @Configuration @ConfigurationProperties(prefix = "cors.allowed") public class CorsConfig { private String api; private String[] url; private String[] method; // Getter, Setter }
  • 해당 설정 클래스를 이용해서 설정 여기서 두가지 어노테이션을 사용하고 있습니다. 1. @AutoConfigureAfter : CorsConfig 빈이 등록되고 난 후에 설정되도록 사용 2. @ConditionalOnBean : 해당 빈이 존재하면 설정 되도록 추가
    • @AutoConfigureAfter(CorsConfig.class) @ConditionalOnBean(CorsConfig.class) @Configuration public class WebMvcConfig implements WebMvcConfigurer { private final CorsConfig corsConfig; public WebMvcConfig(CorsConfig corsConfig) { this.corsConfig = corsConfig; } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(api) .allowedOrigins(corsConfig.getUrl()) .allowedMethods(corsConfig.getMethod()); } }

2. @Value 사용

@Configuration public class WebMvcConfig implements WebMvcConfigurer { private final CorsConfig corsConfig; @Value("${cors.allowed.api}") private String api; @Value("${cors.allowed.url}") private String[] url; @Value("${cors.allowed.method}") private String[] method; public WebMvcConfig(CorsConfig corsConfig) { this.corsConfig = corsConfig; } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(api) .allowedOrigins(url) .allowedMethods(method); } }

3. @ConstructorBinding 사용 (By 진형님)

  • 설정 파일 생성
@ConstructorBinding // ✨ Setter 메서드를 사용하지 않고 생성자를 통해서 주입!! @ConfigurationProperties(prefix = "cors.v2.allowed") public record CorsConfigV2 ( String api, String[] url, String[] method ) { }
  • ConfigurationPropertiesScan 을 사용해서 빈 등록 후 해당 클래스로 설정
@ConfigurationPropertiesScan("com.kdt.instakyuram.config") @Configuration public class WebMvcConfigV4 implements WebMvcConfigurer { CorsConfigV2 corsConfig; public WebMvcConfigV4(CorsConfigV2 corsConfig) { this.corsConfig = corsConfig; } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(corsConfig.api()) .allowedOrigins(corsConfig.url()) .allowedMethods(corsConfig.method()); } }
 

🐶 Origin만 설정하고 모두 허용하기

YAML 파일 설정

cors: allowed: api: /** origins: - http://localhost:3000 - http://127.0.0.1:3000 methods: *
나머지는 위의 [설정 정보 분리 3가지 방법]과 동일