[Spring] DTO를 꼭 사용해야하는가

성 우·2023년 2월 6일
0

DTO 란 무엇인가?

DTO란 Data Transfer Object의 약자로, 계층 간 데이터 전송을 위해 도메인 모델 대신 사용되는 객체이다. 이때, 계층이란 Presentation(View, Controller), Business(Service), Persistence(DAO, Repository) 등을 의미한다.

DTO 왜 써야되는건데?

아래는 현재 스프링에 입문 2주차 토이프로젝트 코드이다.

Entity

@Data
@Entity
public class BoardVo {

    @Id
    @GeneratedValue
    private int id;
    private int categoryId;
    private String title;
    private String content;
    private boolean approval;
    private String creator;
    private LocalDateTime regDt;
}

Service

@Override
public ResponseEntity<List<BoardVo>> getCategoryPostList(int categoryId) {
        try {
            return new ResponseEntity<>(boardRepository.findByCategoryIdAndApproval(categoryId,true), HttpStatus.OK);
        } catch (RuntimeException e) {
            return ResponseEntity.internalServerError().build();
        } catch (Exception e) {
            return ResponseEntity.internalServerError().build();
        }
    }
}

현재 작성된 Board에서 작성된 카테고리와 게시글정보를 불러오는 메서드이다.

이렇게 사용하면서 사실 클라이언트쪽에서 기능동작에는 아무 문제가 없었기 때문에 'DTO를 왜쓰는거지 그냥 이대로 리턴해주면 편하고 좋은데?' 라는 생각을 가지고 있었다.

그래서 DTO에 대해서 좀 공부를 하기 시작했다.

'맞다' 사실 도메인 객체를 지금처럼 View로 그대로 전달해도 된다. 하지만 메인 객체를 View에 직접 전달할 경우 도메인의 비즈니스 기능이 노출될 수 있으며 Model과 View 사이에 의존성이 생긴다고한다.

그놈의 의존성.. 작은 프로젝트에서 감이 잘안온다..

뷰와의 의존성 문제
예를 들어 프론트엔드에서 백엔드쪽으로 받고자하는 데이터는 자주 바뀐다. 이때 Entity 정보를 모두 넘기고있었다면 상당히 귀찮은 요소가 될 수 있다.

비즈니스 기능의 노출
메인 객체를 View에 직접 전달할 경우 도메인의 비즈니스 기능이 노출될 수 있는 부분에서 깊은 영감을 깨달았다.

만약 위에서 내코드가 Board 객체가 아닌 User 객체 였다면? Entity에 있는 사용자 모든정보가 반환된다.(password 까지) 모든 정보를 반환을 원하지않다면 비즈니스 로직에 해당 정보들을 제거한 하는 로직을 거친 후 반환해야하는데 DTO 만들어놓으면 그냥 맵핑시켜서 넘기면된다.

도메인 Model을 캡슐화하고, UI 화면에서 사용하는 데이터만 선택적으로 보낼 수 있다.

정답이 있는건 아니지만 상황에 따라 DTO를 사용해 변환 해서 쓰고 필요한 방법을 선택해서 사용하는게 맞는거같다.

Resonpnse의 대한 DTO 뿐만 아닌 Request요청의 대한 DTO도 만들어서 쓰시는분들이 있는거같다.

[실습] 그럼 DTO 를 만들어보자

Controller

    @GetMapping("/api/v1/board")
    //@RequestParam(value = "category",required = false, defaultValue = "0") category가 없어도 들어오게
    public ResponseEntity<List<BoardResponseDto>> getCategoryPostInfo(
            @RequestParam(value = "category", required = false , defaultValue = "0") int categoryId,
            @RequestParam(value = "postId", required = false, defaultValue = "0") int postId) {
        /**
         * case 1 : 카테고리와 게시글번호가 둘다 없는 경우 요청자체가 잘못됨.
         * case 2 : 카테고리 아이디만 요청했을 경우 -> 해당 카테고리 모든 게시글 정보 반환
         * case 3 : 카테고리 아이디와 게시글 아이디를 요청했을 경우 -> 해당 카테고리에 있는 게시글 정보만 반환
         */

        if (categoryId == 0) { //카테고리와 게시글번호가 둘다 없는 경우
            System.out.println("category prams required");
        } else if(categoryId != 0 && postId == 0) { //카테고리 아이디만 왔을 경우 해당 카테고리 모든 게시글 정보 반환
            return boardService.getCategoryPostList(categoryId); //getByAll -> List<Board>
        } else if(categoryId != 0 && postId != 0) {
            return boardService.getPost(postId); //getById -> Optinal<Board>
        }
        return null;
    }

Serivce

    @Override
    public ResponseEntity<List<BoardResponseDto>> getPost(int id) {
        List<BoardResponseDto> boardList = new ArrayList<BoardResponseDto>();
        BoardVo board = boardRepository.findById(id).get();
        boardList.add(new BoardResponseDto(board));
        return new ResponseEntity<>(boardList, HttpStatus.OK);
    }
    @Override
    public ResponseEntity<List<BoardResponseDto>> getCategoryPostList(int categoryId) {
        try {
            return new ResponseEntity<>(boardRepository.findByCategoryIdAndApproval(categoryId,true), HttpStatus.OK);
        } catch (RuntimeException e) {
            return ResponseEntity.internalServerError().build();
        } catch (Exception e) {
            return ResponseEntity.internalServerError().build();
        }
    }

현재 View에서는 단건 객체라도 리스트 안에 넣어서 넘겨주는 구조이기 때문에 find해온 borad entity객체를 boardList 라는 빈 List 안에 넣어준다. 이때 DTO
생성자를 호출해서 DTO로 맵핑 시켜서 리스트에 넣고 리턴 시킨다

        BoardVo board = boardRepository.findById(id).get();
        boardList.add(new BoardResponseDto(board));

결과는 ?

아주 이뿌게 잘 온다.

실제 앱 화면에서도 한번 보자

다음은 View에서 사용할 Response 공통 DTO를 한번 만들어보는걸로 복습해야겠다!

profile
풀스택 개발자가 되고싶은 개발자

0개의 댓글