Spring

json error stack trace print 여부 커스텀 프로퍼티

ybs 2021. 11. 22. 01:16
반응형

먼저 application.yml 에 원하는 커스텀 프로퍼티를 추가한다. 보통 deploy phase 에 따라 다르게 설정된다.

response:
  print-stack-trace: true

 

다음으로 /resources/META-INF/spring-configuration-metadata.json 파일에 프로퍼티에 대한 메타데이터를 추가한다.

{
  "groups": [
    {
      "name": "response",
      "type": "com.toy.config.ResponseProperties",
      "sourceType": "com.toy.config.ResponseProperties"
    }
  ],
  "properties": [
    {
      "name": "response.print-stack-trace",
      "type": "java.lang.Boolean",
      "description": "에러 스택 트레이스.",
      "sourceType": "com.toy.config.ResponseProperties"
    }
  ]
}

 

다음으로 application.yml 에 설정된 값이 맵핑될 ResponseProperties 를 추가한다.

package com.toy.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@ConfigurationProperties(prefix = "response")
@ConstructorBinding
@Getter
@RequiredArgsConstructor
public class ResponseProperties {
	private final boolean printStackTrace;
}

 

다음으로 ConfigurationPropertiesScan 애노테이션을 추가한다.

@ConfigurationPropertiesScan(basePackages = "com.toy.config")
@SpringBootApplication
public class MainApplication {
	public static void main(String[] args) {
		SpringApplication.run(MainApplication.class, args);
	}
    
    ...
}

 

다음으로 JsonConfig 에서 빈으로 만드는 ObjectMapper 에서 ResponseProperties 를 활용한다.

@Configuration
public class JsonConfig {
  @Bean
  ObjectMapper jacksonObjectMapper(ResponseProperties responseProperties) {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper
      .registerModule(
        // HTTP 응답에 에러 StackTrace 포함 여부
        new ProblemModule().withStackTraces(responseProperties.isPrintStackTrace())
      );
  }
}

 

cf) RFC7807 application/problem+json 구현체로 zalando problem 을 사용했다.

<dependency>
  <groupId>org.zalando</groupId>
  <artifactId>problem-spring-web</artifactId>
  <version>0.26.2</version>
</dependency>

 

마지막으로 ExceptionHandler에서 ProblemBuilder 를 통해 Problem을 만들어 처리한다. 설명을 위해 편의상 ProblemHandling 을 컨트롤러에서 직접 implements 했지만 GlobalExceptionHandler 를 따로 만들고 거기서 implements 하는게 좋다.

@RestController
public class TestController implements ProblemHandling {

	...

	@ExceptionHandler(Exception.class)
	public ResponseEntity<Problem> handle(NativeWebRequest request, Exception ex) {
		ProblemBuilder builder = Problem.builder()
			.withTitle(Status.BAD_REQUEST.getReasonPhrase())
			.withStatus(Status.BAD_REQUEST)
			.withDetail("wrong request param")
			.with("name", "ybs")
			.withType(URI.create("https://yangbongsoo.tistory.com"));

		return create(ex, builder.build(), request);
	}
}

 

cf) 예외가 발생했을 때 response body는 아래와 같다. 

{
  "type": "https://yangbongsoo.tistory.com",
  "title": "Bad Request",
  "status": 400,
  "detail": "wrong request param",
  "stacktrace": [
    "org.zalando.problem.ProblemBuilder.build(ProblemBuilder.java:83)",
    ...
  ],
  "name": "ybs"
}
반응형