GrowMe

[Spring] DTO의 개념과 그 활용 방법 본문

About Spring

[Spring] DTO의 개념과 그 활용 방법

오늘도 타는중 2022. 6. 27. 08:01
DTO(Data Transfer Object)
# DTO
# DTO의 역할
# DTO의 활용
# 유효성 검증

*DTO란?

  • 데이터 전송을 위해, 데이터를 저장하는 객체
  • 전송이 목적이므로, 로직을 가지지 않으며, 데이터값 외 getter & setter만을 가집니다.

*DTO의 구조

public class MemberPatchDto {
    private long memberId;
    private String name;
    private String phone;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public long getMemberId() {
        return memberId;
    }

    public void setMemberId(long memberId) {
        this.memberId = memberId;
    }
}

 

*DTO의 역할

1. 계층 간 데이터의 교환

@RequiredArgsConstructor
@RestController
public class PostsApiController {

    private final PostsService postsService;

    @PostMapping("/api/v1/posts")
    public Long save(@RequestBody PostsSaveRequestDto requestDto) {
        return postsService.save(requestDto);
    }
}
@RequiredArgsConstructor
@Service
public class PostsService {
    private final PostsRepository postsRepository;

    @Transactional
    public Long save(PostsSaveRequestDto requestDto) {
        return postsRepository.save(requestDto.toEntity()).getId();
    }
}
  • 컨트롤러 계층에서 서비스 계층을 호출 시, 요청받은 데이터를 Dto에 담아 서비스 계층으로 전송한다.
  • 서비스 계층에서 또한, save메서드를 통해 레퍼지토리 계층으로 데이터 처리 요청 시, Dto를 통해 데이터를 전송한다.
  • 컨트롤러가 받는 데이터 또한, Dto로 받을 수 있다. (여기서는 Long 타입을 반환하였다.)

 

2. API 요청에 대한 응답 코드의 간결화

- DTO 사용하기 전, 컨트롤러의 레거시 코드

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name,
                                     @RequestParam("phone") String phone) {
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
        map.put("phone", phone);

        return new ResponseEntity<Map>(map, HttpStatus.CREATED);
    }

		...
		...
}

- DTO 사용 시, 훨씬 간결하고 처리가 용이해진 코드

RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }

		...
		...
}
  • @RequestParm을 통해 모든 데이터를 일일이 받지 않고, Dto를 통해 한번에 요청 데이터를 받을 수 있다.
  • 응답을 위한 코드 또한, Map을 새로 만들지 않고, 매개변수에 저장된 Dto를 그대로 활용해 응답 객체로 활용할 수 있다.
  • (주의) Response의 Body로 포함시키려면, 해당 멤버 변수의 getter 메서드가 있어야 한다.
    (Dto를 응답 데이터로 저장 시, getter 메서드를 활용하기 때문)

 

3. 데이터 유효성 검증 처리의 분리

- DTO 사용하기 전, 컨트롤러 상에서의 유효성 검증 코드

@RestController
@RequestMapping("/no-dto-validation/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name,
                                     @RequestParam("phone") String phone) {
				// (1) email 유효성 검증
        if (!email.matches("^[a-zA-Z0-9_!#$%&'\\*+/=?{|}~^.-]+@[a-zA-Z0-9.-]+$")) {
            throw new InvalidParameterException();
        }
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
        map.put("phone", phone);

        return new ResponseEntity<Map>(map, HttpStatus.CREATED);
    }
		...
		...
}

- DTO 사용 시, DTO에서 유효성 검증을 분할하여 처리

DTO에 유효성 검증을 사용하려면, build.gradle 파일에 아래 코드를 추가해야한다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	...
	...
}
public class MemberDto {
    @Email
    private String email;
    private String name;
    private String phone;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}
@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@Valid MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }

		...
		...
}
  • @Email : 유효한 이메일인지 검증한다. null 값을 허용한다.
  • @Valid : 요청 Dto 데이터가, 저장된 Dto 클래스의 유효성의 위반을 검증하며, 유효하지 않을 시 에러를 호출한다.
  • 이메일 단 한가지의 유효성만 검증했는데도, 코드의 간결성이 확연하게 차이남을 알 수 있다.
Comments