Spring boot

ResponseEntity Code Convention

Subi 2023. 7. 26. 10:53

ResponseEntity를 Custom 하여 ResopnseResult 사용하여 이에 맞는 Convention 을 작성합니다.

Template

ResponseResult 의 Template은 다음을 유지한다.

  1. List Template
  • contents : Parameter Contents (List)
  • pageable : Paging 정보
    • pageSize : 페이지 사이즈 (int)
    • pageNumber : 현재 페이지 번호 (int) / 1 부터 시작
    • totalElement : Total Contents Size (Long)
{
  "code": int,
  "message": "string",
  "content": [
    {list1 : data1},
	  {list2 : data2}
  ],
	  "pageable": {
	    "pageSize": int,
	    "pageNumber": int,
	    "totalElement": Long
  }
}
  1. Single Templte
  • contents : Parameter Contents (Object)
{
  "code": int,
  "message": "string",
  "content": {
	   parameter1 : data1 ,
	   parameter2 : data2
  }
  1. Create Templte , Update Templte, Delete Templte
  • contents : Parameter Id (Long) / 실행을 처리한 ID
{
  "code": int,
  "message": "string",
  "content": id(Long)
}

ResponseStatus

  • ResponseResult 에서 사용하기 위한 Response의 상태값을 관리하는 Enum Class
public enum ResponseStatus {

    SUCCESS(200, "SUCCESS"),
    CREATE(201, "SUCCESS CREATED"),
    NO_CONTENTS(204, "SUCCESS NO_CONTENTS");

    private final int code;
    private final String message;

    ResponseStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

Enum의 변수로 code, message 값을 가지고 Service에서 값을 넣어 사용한다.

  • 기본 code 의 값은 Http.Status의 값을 사용한다.
  • 추가가 필요할 시 다음의 형태를 유지한다.
    • Enum 의 Name 은 대문자와 언더바( _ ) 를 사용한다.
    • code 의 값 중 200번 대의 값은 Http 통신이 성공했을 시에 대한 값으로 유지한다.
    • 특이한 별도의 Case를 추가 할 시 기존 Http.stauts 에서 사용하지 않는 값으로 추가한다.

Service

  • Service 에서는 Repository에서 return 받은 값을 ResponseResult 로 retrun 시켜준다.
  1. List Data
public ResponseResult<Page<EntityDTO>> listEntity (EntitySearchCondition entitySearchCondition, Pageable pageable){
        return ResponseResult.response(ResponseStatus.SUCCESS , repository.listEntity(entitySearchCondition,pageable));
 }
  • List Data 에 대한 Retrun 은 ResponseStatus.SUCCESS 로 설정한다. (”200”,”SUCCESS”)
  1. Single Data
public ResponseResult<EntityDTO> getEntity (Long id){
        return ResponseResult.response(ResponseStatus.SUCCESS , repository.findById(id).get());
}
  • Single Data 에 대한 Retrun 은 ResponseStatus.SUCCESS 로 설정한다. (”200”,”SUCCESS”)
  1. Create
public ResponseResult<Long> createEntity (EntityRequest entityRequest){
        Entity saveEntity = repository.save(entityRequest.toEntity());
        return ResponseResult.response(ResponseStatus.CREATE , saveEntity .getId());
}
  • Create 에 대한 Return 은 ResponseStatus.CREATE 로 설정한다. (”201” , “SUCCESS CREATED”)
  • 저장된 Entity에 대한 id 값 (Long) 을 같이 ResponseResult 에 보낸다.
  1. Update
public ResponseResult<Long> updateEntity (Long id , EntityDTO entityDTO) {
        Entity result = repository.findById(id).orElseThrow(()-> new IllegalArgumentException("해당 게시글이 없습니다.") );
        result.updateEntity(entityDTO);
				return ResponseResult.response(ResponseStatus.SUCCESS, result.getId());
}
  • Update 에 대한 Retrun 은 ResponseStatus.SUCCESS 로 설정한다. (”200”,”SUCCESS”)
  1. Delete
public ResponseResult<Long> deleteEntity (Long id) {
        Entity entity = repository.findById(id).orElseThrow( () -> new IllegalArgumentException("해당 게시글이 없습니다."));
        entity.delete();
       return ResponseResult.response(ResponseStatus.SUCCESS, id);
    }
  • Delete 에 대한 Retrun 은 ResponseStatus.SUCCESS 로 설정한다. (”200”,”SUCCESS”)
    • 형식적으로 Delete 에 대한 Http.Status 에 대한 값은 204 (NO_CONTENT) 이지만, 현재 Soft-Delete (논리 삭제) 방식을 사용하고 있으므로, ResponseStatus.SUCCESS 으로 사용한다.

Controller

  • Controller 에서는 전달받은 ResponseResult 의 객체와 Header Http.Status 를 설정해서 Retrun 한다.
  1. List Data
@GetMapping("/domain/list")
public ResponseEntity<ResponseResult<Page<EntityResponse>>> listEntity(EntitySearchCondition condition, @ParameterObject @PageableDefault(page = 1) Pageable pageable) {
    return new ResponseEntity<>(entityService.listEntity(condition, pageable),HttpStatus.OK);
}
  • List Data 에 대한 Header의 상태 값은 HttpStatus.OK로 설정한다.
  1. Single Data
@GetMapping("/domain/{id}")
public ResponseEntity<ResponseResult<EntityResponse>> getEntity(@PathVariable @NotBlank Long id) {
    return new ResponseEntity<>(entityService.getEntity(id),HttpStatus.OK);
}
  • Single Data 에 대한 Header의 상태 값은 HttpStatus.OK로 설정한다.
  1. Create
@PostMapping("/domain/create")
public ResponseEntity<ResponseResult<Long>> createEntity(@RequestBody @Valid EntityRequest entityRequest) {
   return new ResponseEntity<>(entityService.createEntity(entityRequest) , HttpStatus.CREATED);
}
  • Create 에 대한 Header의 상태 값은 HttpStatus.CREATED로 설정한다.
  • 저장된 Entity에 대한 id 값 (Long) 을 같이 ResponseResult 에 보낸다.
  1. Update
@PutMapping("/domain/update/{id}")
public ResponseEntity<ResponseResult<Long>> updateEntity(@RequestBody @Valid EntityRequest entityRequest) {
   return new ResponseEntity<>(entityService.updateEntity(entityRequest) , HttpStatus.OK);
}
  • Update 에 대한 Header의 상태 값은 HttpStatus.OK로 설정한다.
  1. Delete
@DeleteMapping("/domain/delete/{id}")
public ResponseEntity<ResponseResult<Long>> deleteEntity(@PathVariable @NotBlank Long id) 
     return new ResponseEntity<>(entityService.deleteEntity(entityRequest) , HttpStatus.OK);
}
  • Delete 에 대한 Header의 상태 값은 HttpStatus.OK로 설정한다.
    • 형식적으로 Delete 에 대한 Http.Status 에 대한 값은 204 (NO_CONTENT) 이지만, 현재 Soft-Delete (논리 삭제) 방식을 사용하고 있으므로, HttpStatus.OK 로 사용한다.

Test Code

  • Cotroller Test는 아직 R&D 가 필요합니다
  • Service 에서 ResponseResult 로 return 하면서 Service Test 대한 부분이 수정이 되었습니다.

Service Test 의 대한 데이터는 크게 3가지로 분류한다.

  1. List Data (Page 로 감싸진 형태)
ResponseResult<Page<TestResponse>> pageResponseResult = testService.listTest(getTestSearchCondition(), getPageable(0, 10));

List<TestResponse> list = pageResponseResult.getResultToList();

//getResultToList
public <T>List<T> getResultToList() {
        return (List<T>) this.content;
}
  • Service 단에서 Method를 호출하게 되면 ResponseResult<Page<TestResponse>> 이 자료형으로 나오게 된다.
  • 이 데이터에서 Content 를 추출할려고 getContent 를 쓰게 되면 Page라는 자료형으로 캐스팅 오류가 발생하게 된다.
  • 그러므로 따로 생성해 놓은 getResultTOList() 를 사용해 Content를 추출해야 List 에 대한 형태로 나오게 된다.
  1. Single Data
ResponseResult<TestResponse> responseResult = testService.getTest(id);

TestResponse response= pageResponseResult.getResultToData();

//getResultToData
public T getResultToData() {
        return this.content;
}
  • Single Data는 getContent() 를 호출해도 문제가 없지만 , Code Convention과 추후의 리팩토링을 위해 다른 Method 와의 Convention을 유지한다.
  1. Long Data
ResponseResult testResult = testService.createTest(testRequest);
Long resultToLong = testResult.getResultToLong();

//getResultToLong
public Long getResultToLong() {
        return (Long) this.content;
}
  • Create, Update, Delete 같은 Long 으로 retrun 해주는 method 같은 경우에도 getContetns를 하게 될 경우에 Object로 반환 하기 때문에 Test Code 에서 캐스팅이 필요하다.
  • 그렇게 때문에 getResultToLong 이라는 Method를 통해 Long 형으로 content를 추출해야 된다.