Spring
Pattern
봉투패턴

Error Process

에러가 발생함

var ticket = ticketJpaRepository.findByLectureIdAndEmployeeId(command.getLectureId(),
                command.getEmployeeId());
        if (ticket != null && ticket.isPresent()) {
            throw new SimpleLogicException("이미 수강신청 하였습니다.");
        }

var ticket = ticketJpaRepository.findByLectureIdAndEmployeeId(command.getLectureId(),
                command.getEmployeeId());
        if (ticket != null && ticket.isPresent()) {
            throw new SimpleLogicException("이미 수강신청 하였습니다.");
        }

Explain

RestControllerWithEnvelopPattern Annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@RestController
public @interface RestControllerWithEnvelopPattern {
}
  • @Documented : @RestControllerWithEnvelopPattern Annotation을 사용한 코드를 JavaDoc으로 만들 때, JavaDoc에 포함시킬지 여부를 결정
  • @Inherited : 상속을 통해서 서브클래스에도 Annotation을 전달할지 여부를 결정

RestControllerAdvice

RestControllerAdvice란 @ControllerAdvice와 @ResponseBody가 결합된 Annotation이다.

  • @ControllerAdvice : @ExceptionHandler, @ModelAttribute, @InitBinder 가 적용된 메서드들에 AOP를 적용해 Controller 단에 적용하기 위해 고안된 어노테이션이라고 한다.
  • @ResponseBody : 자바객체를 HTTP 요청의 Body 내용으로 매핑하여 클라이언트로 전송하는 어노테이션이라고 한다.

즉, ExceptionHandler, ModelAttribute, InitBinder에 AOP를 적용하여 Controller 단에 적용하고, 그 결과를 HTTP 요청의 Body 내용으로 매핑하여 클라이언트로 전송하는 어노테이션이다.

intrastructure.web.common.dto.ApiResponseWrappingResponseBodyAdvisor.java
@RestControllerAdvice(annotations = {RestControllerWithEnvelopPattern.class})
public class ApiResponseWrappingResponseBodyAdvisor implements ResponseBodyAdvice<Object> {
    private static final Logger LOG =
            LoggerFactory.getLogger(ApiResponseWrappingResponseBodyAdvisor.class);
 
    private Type getGenericType(MethodParameter returnType) {
 
        if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) {
            return ResolvableType.forType(returnType.getGenericParameterType())
                    .getGeneric()
                    .getType();
        } else {
            return returnType.getGenericParameterType();
        }
    }
 
    @Override
    public boolean supports(
            MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        Type type =
                GenericTypeResolver.resolveType(
                        getGenericType(returnType), returnType.getContainingClass());
 
        if (returnType.getExecutable().getName().toUpperCase().contains("HEALTH")) { // healthCheck
            return false;
        }
 
        if (Void.class.getName().equals(type.getTypeName())) {
            return false;
        }
 
        return !converterType.isAssignableFrom(ByteArrayHttpMessageConverter.class)
                && !converterType.isAssignableFrom(ResourceHttpMessageConverter.class);
    }
 
    @Override
    public Object beforeBodyWrite(
            Object body,
            MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request,
            ServerHttpResponse response) {
 
        HttpStatus responseStatus =
                HttpStatus.valueOf(
                        ((ServletServerHttpResponse) response).getServletResponse().getStatus());
 
        if (Objects.isNull(body)) {
            return responseStatus.isError()
                    ? ApiResponseGenerator.fail()
                    : ApiResponseGenerator.success();
        }
 
        var apiResponse =
                responseStatus.isError()
                        ? ApiResponseGenerator.fail(body)
                        : ApiResponseGenerator.success(body);
        LOG.trace("[ApiResponse] {}", apiResponse);
 
        if (selectedConverterType.isAssignableFrom(StringHttpMessageConverter.class)) {
            try {
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                return JsonUtils.toJson(apiResponse);
            } catch (JsonUtils.JsonEncodeException jpe) {
                throw new ApiResponseJsonProcessingException(jpe);
            }
        }
 
        return apiResponse;
    }
}