[프로젝트] 개발자 포트폴리오 - 2. [백엔드] Swagger, Response Model, 예외 처리 세팅

개발자 포트폴리오 프로젝트

2. [백엔드] Swagger, Response Model, 예외 처리 세팅

전체 소스 - https://github.com/vividswan/Portfolio-For-Developers


Swagger Setting

source commit - 062756b

백엔드 초기 세팅 때 build.gradle에 설정했던 Swagger를 세팅했다.
Swagger에 대한 기본 세팅을 해주는 SwaggerConfig.java를 만들고, Security에서도 web.ignoring() 설정을 해주었다.

SwaggerConfig.java


package com.portfolio.backend.config.swagger;

// import 생략

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket swaggerApi() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(swaggerInfo()).select()
                .apis(RequestHandlerSelectors.basePackage("com.portfolio.backend"))
                .paths(PathSelectors.any())
                .build()
                .useDefaultResponseMessages(false);
    }

    private ApiInfo swaggerInfo() {
        return new ApiInfoBuilder().title("Portfolio For Developers API Documentation")
                .description("개발자를 위한 포트폴리오 프로젝트 API 연동 문서")
                .license("vividswan").licenseUrl("https://vividswan.github.io/").version("1").build();
    }

} 

SecurityConfig.java

package com.portfolio.backend.config.security;

// import 생략

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override // ignore check swagger resource
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**",
                "/swagger-ui.html", "/webjars/**", "/swagger/**");

    }

} 

Api Response Model Setting

source commit - 062756b

Response 형식을 통일하기 위해 Response Model 객체를 만들었다.
Response의 성공 여부, 코드번호, 메시지를 담는 CommonResponse와 이를 상속하여 단일 Data를 Return 하는 SingleResponse<T>와 리스트 Data를 Return 하는 ListResponse<T> 객체를 만들었다.
이를 상황에 맞게 Return 해줄 수 있는 ResponseService라는 Service 객체를 만들었다.

CommonResponse.java

package com.portfolio.backend.model.response;

// import 생략

@Getter
@Setter
public class CommonResponse {

    @ApiModelProperty(value = "Response 성공 여부")
    private boolean isSuccess;

    @ApiModelProperty(value = "Response 코드 번호")
    private int code;

    @ApiModelProperty(value = "Response Message")
    private String message;

    public CommonResponse(){}
    public CommonResponse(String msg){
        this.message = msg;
    };

} 

SingleResponse.java

package com.portfolio.backend.model.response;

// import 생략

@Setter
@Getter
public class SingleResponse<T> extends CommonResponse{

    @ApiModelProperty(value = "Single Response Data")
    private T data;

}

ListResponse.java

package com.portfolio.backend.model.response;

// import 생략

import java.util.List;

@Getter
@Setter
public class ListResponse<T> extends CommonResponse{

    @ApiModelProperty(value = "Response Data List")
    private List<T> list;

}

ResponseService.java

package com.portfolio.backend.model.response;

// import 생략

@Service
public class ResponseService {

    public <T> SingleResponse<T> getSingleResponse(T data){
        SingleResponse<T> res = new SingleResponse<>();
        res.setData(data);
        res.setCode(0);
        res.setMessage("Success");
        res.setSuccess(true);
        return res;
    }

    public <T> ListResponse<T> getListResponse(List<T> list){
        ListResponse<T> res = new ListResponse<>();
        res.setList(list);
        res.setCode(0);
        res.setMessage("Success");
        res.setSuccess(true);
        return res;
    }

    public CommonResponse getSuccessResponse(){
        CommonResponse res = new CommonResponse();
        res.setSuccess(true);
        res.setCode(0);
        res.setMessage("Success");
        return res;
    }

    public CommonResponse getFailResponse(String msg){
        CommonResponse res = new CommonResponse(msg);
        res.setSuccess(false);
        res.setCode(-1);
        res.setMessage(msg);
        return res;
    }

}

Exception Handler Setting

source commit - a096fef

우선 두 가지 예외 클래스를 만들었다.

그 후 전역으로 예외 처리를 컨트롤(@RestControllerAdvice 어노테이션) 할 수 있는 ExceptionAdvice을 선언하여 예외 처리 고도화를 해준다.

CustomValidationException

package com.portfolio.backend.config.advice.exception;

public class CustomValidationException extends RuntimeException{
    public CustomValidationException() {
        super();
    }

    public CustomValidationException(String message) {
        super(message);
    }

    public CustomValidationException(String message, Throwable cause) {
        super(message, cause);
    }
}

CustomUserNotFoundException

package com.portfolio.backend.config.advice.exception;

public class CustomUserNotFoundException extends  RuntimeException{
    public CustomUserNotFoundException() {
        super();
    }

    public CustomUserNotFoundException(String message) {
        super(message);
    }

    public CustomUserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

ExceptionAdvice

package com.portfolio.backend.config.advice;

// import 생략

@RequiredArgsConstructor
@RestControllerAdvice
public class ExceptionAdvice {

    private final ResponseService responseService;

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResponse defaultException(HttpServletRequest request, Exception e) {
        return responseService.getFailResponse("Default Error");
    }

    @ExceptionHandler(CustomUserNotFoundException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResponse userNotfoundException(HttpServletRequest req, CustomUserNotFoundException e){
        return responseService.getFailResponse("잘 못 된 User 정보 입니다.");
    }

    @ExceptionHandler(CustomValidationException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResponse validationException(HttpServletRequest req, CustomValidationException e){
        return responseService.getFailResponse("잘 못 된 입력 값입니다.");
    }
}