From 16ce99f9eabe74378ea3b7eb680d26853ccd3a0f Mon Sep 17 00:00:00 2001 From: joona95 Date: Tue, 7 Jul 2026 23:01:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20cors=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/common/config/WebSecurityConfig.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/recipe/app/src/common/config/WebSecurityConfig.java b/src/main/java/com/recipe/app/src/common/config/WebSecurityConfig.java index c06f9f3d..557e61a1 100644 --- a/src/main/java/com/recipe/app/src/common/config/WebSecurityConfig.java +++ b/src/main/java/com/recipe/app/src/common/config/WebSecurityConfig.java @@ -15,6 +15,11 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; @Configuration @EnableWebSecurity @@ -43,11 +48,13 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration a public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authorize -> authorize + .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/fridges/**").authenticated() .requestMatchers("/fridges/basket/**").authenticated() .requestMatchers("/ingredients/**").authenticated() @@ -59,6 +66,19 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .addFilterBefore(new JwtFilter(jwtUtil, userDetailsService), UsernamePasswordAuthenticationFilter.class) .build(); } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of("*")); + config.setAllowedMethods(List.of("GET", "OPTIONS")); + config.setAllowedHeaders(List.of("Content-Type", "Accept", "Authorization")); + config.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/recipes/**", config); + return source; + } } From d4d3f25cca17b2a9397cacb58010c687051e7c97 Mon Sep 17 00:00:00 2001 From: joona95 Date: Tue, 7 Jul 2026 23:14:14 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20MVC=20=ED=91=9C=EC=A4=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/GeneralExceptionHandler.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/java/com/recipe/app/src/common/config/GeneralExceptionHandler.java b/src/main/java/com/recipe/app/src/common/config/GeneralExceptionHandler.java index 0ec13c69..cd2f4ddc 100644 --- a/src/main/java/com/recipe/app/src/common/config/GeneralExceptionHandler.java +++ b/src/main/java/com/recipe/app/src/common/config/GeneralExceptionHandler.java @@ -18,11 +18,20 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.security.web.firewall.RequestRejectedException; import org.springframework.util.StringUtils; +import org.springframework.web.ErrorResponse; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.resource.NoResourceFoundException; import java.util.Map; @@ -81,6 +90,26 @@ public ResponseEntity handleRequestRejected(RequestRejectedException e, HttpS return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("message", "잘못된 요청입니다.")); } + // Spring MVC 표준 예외(파라미터 누락·타입 불일치·미지원 메서드 등)는 예외가 가진 상태코드(400/405/415…)를 + // 그대로 내려준다. 클라이언트 잘못이므로 500 으로 뭉개지 않고 알림도 보내지 않는다. + // ErrorResponse 는 인터페이스(Throwable 아님)라 @ExceptionHandler 로 직접 잡을 수 없어, + // 실제 예외 타입을 나열해 잡고 상태코드는 런타임에 ErrorResponse 로 읽는다. + @ExceptionHandler({ + ServletRequestBindingException.class, + MethodArgumentTypeMismatchException.class, + MethodArgumentNotValidException.class, + HttpMessageNotReadableException.class, + HttpRequestMethodNotSupportedException.class, + HttpMediaTypeNotSupportedException.class, + HttpMediaTypeNotAcceptableException.class + }) + public ResponseEntity handleSpringMvcException(Exception e, HttpServletRequest request) { + HttpStatusCode status = (e instanceof ErrorResponse er) ? er.getStatusCode() : HttpStatus.BAD_REQUEST; + log.warn("{} {} {} from {} - {}", status.value(), request.getMethod(), request.getRequestURI(), + clientIp(request), e.getMessage()); + return ResponseEntity.status(status).body(Map.of("message", "잘못된 요청입니다.")); + } + // 위에서 처리되지 않은 모든 예외 = 500. 로그를 남기고 (webhook 설정 시) Discord 로 알림을 보낸다. @ExceptionHandler(Exception.class) public ResponseEntity handleInternalServerError(Exception e, HttpServletRequest request) {