api를 작성하기 위해 각 패키지를 만들어 패키지에 맞게 작동하도록 그리고 테스트 코드도 추가하여 작업할 수 있게 구조를 만드려고 한다.
우선 내가 지금 당장 필요하다고 생각되는 패키지들을 정의해놨다. 각각 패키지는
controller
api 호출시 client의 요청과 parameter를 받아오는 곳
service
실제 서비스 로직이 실행되어지는 곳
repository
서비스 로직 중 db와 작업이 필요한 곳에서 db의 데이터를 가져오거나 넣어주는 곳
domain
db테이블에 컬럼을 정의하거나 내가 사용할 모델을 사용하는 곳
exception
exception을 정의하는 곳
advice
exception처리를 하기 위한 곳
이렇게 사용하기 위해 작성했다.
api를 사용하면서 공통적으로 response하는 형태나 공통으로 반환되는 exception을 처리해야할 경우가 있다. 또한 이 공통 response나 exeception를 상속받아 다른 response나 exception을 추가하면 되므로 기본 뼈대가 되는 공통 처리 모델들이다.
@Getter
@Builder
@AllArgsConstructor
public class CommonV1<T> {
private String result; //success, fail 구분
private String code; //api 고유 상태코드
private String msg; //반환 메세지
private T data; //반환 데이터
}
먼저 공통 response를 만들었다. response할 데이터들을 정의하였는데
result
api 통신이 성공했는지 실패했는지 결과 값
code
api 고유 상태코드
msg
필요하다면 메세지 전달
data
반환되는 데이터
로 정의 하였고 lombok 어노테이션의 경우
@Getter
변수들의 getter생성
@Builder
빌더 패턴 생성
@AllArgsConstructor
모든 변수들을 포함한 생성자 생성
이다.
위의 response대로 정말 반환되는지 확인해보기 위해 test controller를 작성해보자
@RestController
public class TestController {
@GetMapping("/v1/test")
public ResponseEntity test(){
CommonV1.CommonV1Builder<Object> builder = CommonV1.builder();
builder.result("success");
builder.code("200");
builder.data(null);
builder.msg("성공");
CommonV1 result = builder.build();
return new ResponseEntity(result, HttpStatus.OK);
}
}
test controller를 만들어 테스트해보면
내가 생각했던 결과를 return 받을 수 있다.
다음으로는 exception이 발생했을 때 error에 대한 처리가 필요한데 이를 처리해보자.
우선 CommonErrorV1 class를 만든다. response의 CommonV1과 마찬가지로 반환하기 위한 객체를 담을 클래스인데.
@Getter
@AllArgsConstructor
public class CommonErrorV1<T> {
private T error;
}
다음과 같이 error들을 담을 것이기 때문에 이렇게 작성했다.
그리고 response와 다르게 error를 담아야하기 때문에 error 객체를 정의한 클래스도 만들어야하는데
@Getter
@AllArgsConstructor
public class CommonError {
private String msg;
}
CommonError라는 클래스명으로 msg만 전달하도록 작성했다. 다른 전달하고 싶은 정보가 있다면 변수를 추가해서 전달하면 된다.
그러면 우리가 작성한 error를 반환할 수 있도록 설정을 해보자!
간단한 exception을 하나 만들어보자.
@AllArgsConstructor
@Getter
public class CommonException extends RuntimeException{
private String msg;
private HttpStatus status;
}
Exception은 msg와 status를 전달하기위해 getter를 추가하여 작성했다.
@RestControllerAdvice
public class CommonAdvice {
@ExceptionHandler(CommonException.class)
public ResponseEntity commonExceptionAdvice(CommonException ce){
List<CommonError> errorList = new ArrayList<>();
CommonError commonError = new CommonError(ce.getMsg());
errorList.add(commonError);
CommonErrorV1<List<CommonError>> result = new CommonErrorV1<>(errorList);
return new ResponseEntity<CommonErrorV1>(result, ce.getStatus());
}
}
에러 처리를 위해 다음과 같이 설정해주었다. 에러가 일어날 경우 여러 정보를 전송하기 위해 배열 형태로 처리하는 경우가 보편적이라 배열 형태로 생성했다.
@GetMapping("/v1/error")
public ResponseEntity error(){
CommonV1.CommonV1Builder<Object> builder = CommonV1.builder();
builder.result("success");
builder.code("200");
builder.data(null);
builder.msg("성공");
CommonV1 result = builder.build();
if(true) throw new CommonException("내가 만든 exception", HttpStatus.FOUND);
return new ResponseEntity(result, HttpStatus.OK);
}
아까 만들어 두었던 test controller에 다음과 같이 코드를 추가하자.
코드를 보면 아까 전에 테스트와 거의 동일한데 마지막 return 전에 exception을 발생시키도록 처리해두었다.
exception이 정상적으로 에러로 처리되었고 상태 코드값과 메세지도 내가 작성한 내용 그대로 정상적으로 전달되었다!
이제 api를 작업할때 위 공통 class들을 상속받아 필요한 정보를 더 추가해서 작업하면 될것이다!
다음 작업은 rest docs를 추가해보자. api는 문서화가 필수니까!
builder 패턴으로 작업할때 참고하면 좋은 글을 찾았다.