[Project]/Favor 프로젝트

API 응답 ResponseDto 에서 ResponseEntity로 바꾸기

응파카 2023. 3. 22. 14:07

DB의 User 를 삭제하는 deleteUser 메서드가 Controller 계층, Service 계층에 아래와같이 정의되어 있고 이와 관련된 메서드들도 다음과 같다고 하자.

 

@ApiOperation("회원 탈퇴")
@DeleteMapping("/{userNo}")
public UserDetailResponseDto deleteUser(@PathVariable Long userNo){

    return userService.deleteUser(userNo);
}

Controller 단 deleteUser 메서드

 

@Transactional
public UserDetailResponseDto deleteUser(Long userNo){
    User user = findUserByUserNo(userNo);
    userRepository.deleteById(userNo);

    return returnUserDetailDto(user);

}

Service 단 deleteUser 메서드

 

@Transactional
public UserDetailResponseDto returnUserDetailDto(User user){
    List<ReminderResponseDto> r_list = new ArrayList<>();
    List<Reminder> reminderList = user.getReminderList();
    for(Reminder r : reminderList){
        ReminderResponseDto dto = new ReminderResponseDto(r);
        r_list.add(dto);
    }
    List<GiftResponseDto> g_list = new ArrayList<>();
    List<Gift> giftList = user.getGiftList();
    for(Gift g : giftList){
        GiftResponseDto dto = new GiftResponseDto(g);
        g_list.add(dto);
    }

    List<FriendResponseDto> f_list = new ArrayList<>();
    List<Friend> friendList = user.getFriendList();
    for(Friend f : friendList){
        FriendResponseDto dto = new FriendResponseDto(f);
        f_list.add(dto);
    }

    List<Favor> favor_List = new ArrayList<>();
    for(Integer favorType : user.getFavorList()){
        favor_List.add(Favor.valueOf(favorType));
    }

    UserDetailResponseDto dto = new UserDetailResponseDto(user, r_list, g_list, f_list, favor_List);
    return dto;
}

순환참조를 막기 위해 DTO를 만들어주는 returnUserDetailDto 메서드

 

public void isExistingUserNo (Long userNo){
    Boolean isExistingUser = null;
    try{
        isExistingUser = userRepository.existsByUserNo(userNo);
    } catch(RuntimeException e){
        throw new CustomException(e, SERVER_ERROR);
    }
    if(!isExistingUser){
        throw new CustomException(null, USER_NOT_FOUND);
    }
}

userNo 식별자로 존재하는 회원인지 여부를 반환하는 isExistingUser 메서드

 

"페이버" 라는 한 명의 회원이 존재할 때,

그 회원을 삭제하는 deleteUser 메서드의 반환값은 지금까지 다음과 같았다.

 

 

이는 반환타입을 일반 객체로 받은 것으로, HTTP의 Response 표준 규약을 지키지 않은 것이기에 클라이언트 사이드가 정형화되어있지 않은 환경에서 개발속도가 저하될 수 있다.

 

 

HTTP 규약을 지키게 된다면 클라이언트 입장에서도 어떤 요청을 하였고 어떤 상황인지 이해가 쉬워진다.

ResponseEntity (Spring Framework 6.0.7 API)

 

ResponseEntity (Spring Framework 6.0.7 API)

Create a ResponseEntity with a body, headers, and a raw status code.

docs.spring.io

 

이 때 사용하는 ResponseEntity는 HttpEntity를 상속하여
HttpHeaders headers와 T body, HttpStatus status 를 포함한 클래스이다.
즉, HTTP 요청에 응답 Entity로 사용 가능한 클래스이다.
headers에는 웹서버가 웹브라우저에 응답하는 메세지가 담겨있다.
body에는 데이터 값이 들어간다.

ResponseEntity를 사용하면 에러코드를 설정하고, 결과값과 상태코드, 헤더값을 넘겨줄 수 있다.

 

 

ResponseEntity.status(200).body(dto)

ResponseEntity를 생성하는 기본 방법은 status와 body를 이용해서

status : status code와

body : JSON으로 변환할 객체를 지정하는 것이다.

 

 

이 때 사용할 객체 DTO인 DefaultResponseDto 클래스를 만들었다.

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;

@Getter
@Builder
@AllArgsConstructor
@ApiModel(value = "기본 응답")
public class DefaultResponseDto<T> {

    @ApiModelProperty(position = 1, value = "응답 코드", example = "RESPONSE_CODE")
    private String responseCode;
    @ApiModelProperty(position = 2, value = "응답 메세지", example = "응답 메세지")
    private String responseMessage;
    @ApiModelProperty(position = 3, value = "응답 데이터", example = "응답 데이터")
    private T data;

    public DefaultResponseDto(final String responseCode, final String responseMessage){
        this.responseCode = responseCode;
        this.responseMessage = responseMessage;
        this.data = null;
    }

    public static <T> DefaultResponseDto<T> response(final String responseCode, final String responseMessage){
        return response(responseCode, responseMessage, null);
    }

    public static <T> DefaultResponseDto<T> response(final String responseCode, final String responseMessage, final T data){
        return DefaultResponseDto.<T>builder()
                .responseCode(responseCode)
                .responseMessage(responseMessage)
                .data(data)
                .build();
    }
}

 

 

 

DefaultResponseDto를 ResponseEntity에서 다음과 같이 적용하면 된다.

@Transactional
@DeleteMapping("/{userNo}")
public ResponseEntity<DefaultResponseDto<Object>> deleteUser(
        @PathVariable Long userNo){

    userService.isExistingUserNo(userNo);

    User user = userService.findUserByUserNo(userNo);
    UserDetailResponseDto dto = userService.returnUserDetailDto(user);

    userService.deleteUser(userNo);

    return ResponseEntity.status(200)
            .body(DefaultResponseDto.builder()
                    .responseCode("USER_DELETED")
                    .responseMessage("회원 탈퇴 완료 (임시)")
                    .data(dto)
                    .build());
}

Controller 단  deleteUser 메서드

 

 

@Transactional
public User deleteUser(Long userNo){
    User user = findUserByUserNo(userNo);
    userRepository.deleteByUserNo(userNo);

    return user;
}

Service 단 deleteUser 메서드

 

 

 

 

실행 결과 반환 타입 ResponseEntity가 적용되었음을 알 수 있다.

나중에는 로그인에 사용된 인증받은 토큰에서 로그인중인 회원의 정보를 찾아내 해당 회원을 삭제하는 방식으로 진행할 것이기 때문에 임시를 붙여놓았다.