Java

[Spring Boot] REST API 구현(2) DTO, Service, Controller 개념 및 작성 방법

cob 2022. 10. 9. 10:48

 

DTO

 

DTO란?
 계층 간 데이터 교환을 하기 위해 사용하는 객체로, DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)이다.

HTTP 응답을 반환할 때 비즈니스 로직을 캡슐화하거나 추가적인 정보를 함께 반환하려고 DTO를 사용한다.
따라서 컨트롤러는 사용자에게 DTO를 요청 바디로 넘겨받고 이를 Entity로 변환 저장해야 한다. 
즉, 서비스의 메서드에서 리턴하는 Entity를 DTO로 변환해 리턴해야 한다

 

 

1. DTO 구현

import com.example.demo.model.TodoEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TodoDTO {
	private String id; 			
	private String title; 	
	private boolean done; 	
	
	public TodoDTO(final TodoEntity entity) {
		this.id 	= entity.getId();
		this.title 	= entity.getTitle();
		this.done 	= entity.isDone();
	}
	
     // lombok을 사용한 Entity 객체 생성(builder) 방법
	public static TodoEntity toEntity(final TodoDTO dto) {
		return TodoEntity.builder()
				.id(dto.getId())
				.title(dto.getTitle())
				.done(dto.isDone())
				.build();
	}
}
  • @Builder : Builder 패턴을 사용하는 것은 생성자를 이용해 오브젝트를 생성하는 것과 비슷하다.
    장점이 있다면 생성자 매개변수의 순서를 기억할 필요가 없다는 점이다.
  • @NoArgsConstructor : 매개변수 없는 생성자를 구현해준다. 아래와 같이 생성자를 구현하는 것과 같다.
  • @AllArgsConstructor : 클래스의 모든 멤버 변수를 매개변수로 받는 생성자를 구현해준다. 아래와 같다
  • @DATA : 클래스 멤버 변수의 Getter/Setter 메서드를 구현해준다.
* Lombok 개념 및 사용법 *
https://cocococo.tistory.com/37

* Entity 개념 및 사용법 *
https://cocococo.tistory.com/38

 

 


 

Service

 

Service란?
비즈니스 로직을 처리하는 객체를 말하고, DAO 여러 개를 묶어서 사용하는 객체를 가리키기도 한다. 
주로 DAO를 통해 데이터에 접근하고, DTO로 전달받은 데이터를 처리하는 데 필요한 로직을 구현한다. 
Controller가 Request를 받으면 적절한 Service에 전달하고, 전달받은 Service는 비즈니스 로직을 처리한다. 

 

 

2. Service 구현

2-1) 구현 조건
1) 검증(validation) : 넘어온 Entity가 유효한지 검사하는 로직으로 이 부분의 코드가 더 커지면 클래스로 분리시킬 수 있다.
2) save() : Entity를 데이터베이스에 저장하는 로그를 남긴다.
3) findByUserId() : 저장된 Entity를 포함하는 새 리스트를 리턴한다.

 

2-2) 로직 구현

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.model.TodoEntity;
import com.example.demo.persistence.TodoRespository;
import lombok.extern.slf4j.Slf4j;

@Slf4j   // 로그 어노테이션
@Service
public class TodoService {
	
	@Autowired
	private TodoRespository repository;
	
	public List<TodoEntity> create(final TodoEntity entity){
		// Validations
		validate(entity);
		
		repository.save(entity);
		
		log.info("Entity Id : {} is saved.", entity.getId());
		
		return repository.findByUserId(entity.getUserId());
	}
	
	// 검증 메서드
	private void validate(final TodoEntity entity) {
		if(entity == null) {
			log.warn("Entity cannot be null.");
			throw new RuntimeException("entity cannot be null.");
		}
		
		if(entity.getUserId() == null) {
			log.warn("Unkown user.");
			throw new RuntimeException("Unknown user");
		}
	}
}
  • @Slf4j : 로그 어노테이션으로 용도에 따라 로그를 크게 info, debug, warn, error로 나누고 이를 로그 레벨이라고 부른다. 로깅은 웹서비스에서 반드시 필요하다.

 


 

Controller

 

Controller란?
View(Client)에서 보낸 요청 URL에 따라 적절한 응답을 한다. 
View의 Response Body에 담긴 데이터(DTO)를 Service에 넘겨 적절하게 처리한다. 
Service에서 처리하고 반환된 데이터(DTO)를 Response Body에 담아 View에 전달한다.  

 

3. Controller 구현

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.dto.ResponseDTO;
import com.example.demo.dto.TodoDTO;
import com.example.demo.model.TodoEntity;
import com.example.demo.service.TodoService;

@RestController
@RequestMapping("todo") // DB 맵핑
public class TodoController {
	
	@Autowired
	private TodoService service;
	
	@PostMapping
	public ResponseEntity<?> createTodo(@RequestBody TodoDTO dto){
		try {
			String temporaryUserId = "temporary-user"; // temporary user id.
			
			// (1) TodoEntity로 변환한다.
			TodoEntity entity = TodoDTO.toEntity(dto);
			
			// (2) id를 null로 초기화한다. 생성 당시에는 id가 없어야하기 때문이다.
			entity.setId(null);

			// (3) 임시 사용자 아이디를 설정해 준다. 인증과 인가 기능이 없으므로 현재는 temporary-user만 로그인 없이 사용할 수 있는 애플리케이션이다.
			entity.setUserId(temporaryUserId);
			
			// (4) 서비스를 이용해 Todo 엔티티를 생성한다.
			List<TodoEntity> entities = service.create(entity);
			
			// (5) 자바 스트림을 이용해 리턴된 엔티티 리스트를 TodoDTO 리스트로 변환한다.
			List<TodoDTO> dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
			
			// (6) 변환된 TodoDTO 리스트를 이용해 ResponseDTO를 초기화한다.
			ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().data(dtos).build();
			
			// (7) ResponseDTO를 리턴한다.
			return ResponseEntity.ok().body(response);
		} catch (Exception e) {
			// (8) ,혹시 예외가 있는 경우 dto 대신 error에 메시지를 넣어 리턴한다.
			String error = e.getMessage();
			ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().error(error).build();
			return ResponseEntity.badRequest().body(response);
		}
	}
}
반응형