[React.js, 스트링부트, AWS로 배우는 웹개발 101] 2장 백엔드 개발-3
웹개발 101 - 2장 백엔드 개발
[React.js, 스트링부트, AWS로 배우는 웹개발 101] 2장 백엔드 개발-1
[React.js, 스트링부트, AWS로 배우는 웹개발 101] 2장 백엔드 개발-2
[React.js, 스트링부트, AWS로 배우는 웹개발 101] 2장 백엔드 개발-3
2.3 서비스 개발 및 실습
로그 어노테이션
가장 간단한 방법은 System.out.println이다. 하지만 이 방법은 유용하지만 기능이 제한적이다.
로그를 크게 그냥 정보를 위한 것인 info, 디버깅을 위한 자세한 정보인 debug, warn, 그리고 심각한 에러를 알려주는 로그인 error로 나누고, 이를 로그 레벨이라고 부른다.
이런 기능을 제공하는 라이브러리를 Slf4j(Simple Logging Façade for Java)라고 부른다.
스피링에 @Slf3j를 추가해준다면 이 작업을 알아서 해준다.
Create Todo 구현
퍼시스턴스 구현
우리가 이전에 작성했던 TodoRepository를 사용한다. 이는 JpaRepository를 상속하므로 JpaRepository가 제공하는 메서드를 사용할 수 있다. 엔티티 저장을 위해서 save 메서드를 사용하고, 새 Todo 리스트를 반환하기 위해 findByUserId() 메서드를 사용한다.
// TodoRepository.java
package com.example.demo.persistence;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.example.demo.model.TodoEntity;
@Repository
public interface TodoRepository extends JpaRepository<TodoEntity, String> {
// ?1은 메서드의 매개변수의 순서 위치이다.
@Query("select * from TodoEntity t where t.userId = ?1")
List<TodoEntity> findByUserId(String userId);
}
서비스 구현
서비스 추가를 위해 create 메서드를 작성하자. 이는 크게 세 단계로 구성된다.
검증 → Save() → findByUserId()
// TodoService.java 리팩토링한 create 메서드
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 method로 리팩토링한 메서드
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("Unknown user.");
throw new RuntimeException("Unknown user.");
}
}
컨트롤러 구현
HTTP 응답을 반환할 때, 비즈니스 로직을 캡슐화하거나 추가적인 정보를 함께 반환하기 위해 DTO를 사용한다고 했다.
TodoDTO.java에 DTO를 TodoEntity로 변환하기 위해 toEntity 메서드를 작성해보자.
// TodoDTO.java
package com.example.demo.dto;
import com.example.demo.model.TodoEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@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();
}
public static TodoEntity toEntity(final TodoDTO dto) {
return TodoEntity.builder()
.id(dto.getId())
.title(dto.getTitle())
.done(dto.isDone())
.build();
}
}
// TodoController.java
package com.example.demo.controller;
import java.util.ArrayList;
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.GetMapping;
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")
public class TodoController {
@Autowired
private TodoService service;
// testTodo 메서드 작성하기
@GetMapping("/test")
public ResponseEntity<?> testTodo(){
String str = service.testService(); // 테스트 서비스 사용
List<String> list = new ArrayList<>();
list.add(str);
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
return ResponseEntity.ok().body(response);
}
@PostMapping
public ResponseEntity<?> createTodo(@RequestBody TodoDTO dto){
try {
String temporaryUserId = "temporary-user"; // temporary user id.
TodoEntity entity = TodoDTO.toEntity(dto);
entity.setId(null);
entity.setUserId(temporaryUserId);
List<TodoEntity> entities = service.create(entity);
List<TodoDTO> dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().data(dtos).build();
return ResponseEntity.ok().body(response);
} catch(Exception e) {
String error = e.getMessage();
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().error(error).build();
return ResponseEntity.badRequest().body(response);
}
}
}
Retrieve Todo 구현, Todo 리스트 검색
퍼시스턴스 구현
TodoRepository를 사용한다.
Todo 리스트 반환을 위해 기존에 작성했던 TodoReository에서 findByUserId() 메서드를 사용한다.
서비스 구현
TodoService에 findAll이라는 메서드를 작성해보자.
// TodoService.java, Retrieve Todo 구현(Todo 리스트 검색 기능)
public List<TodoEntity> retrieve(final String userId){
return repository.findByUserId(userId);
}
컨트롤러 구현
TodoController.java에 새 GET 메서드를 만들어준다.
// TodoController.java의 retrieveTodoList 메서드
public ResponseEntity<?> retrieveTodoList(){
String temporaryUserId = "temporary-user"; // temporary user id.
List<TodoEntity> entities = service.retrieve(temporaryUserId);
List<TodoDTO> dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().data(dtos).build();
return ResponseEntity.ok().body(response);
}
Update Todo 구현, Todo를 업데이트
퍼시스턴스 구현
업데이트하기 위해 기존에 작성했던 TodoReository에서 save(), findByUserId() 메서드를 사용한다.
서비스 구현
Update 기능 구현을 위해 TodoService.java에 update() 메서드를 작성해보자.
// TodoService.java의 update() 메서드
// update 구현, Todo를 업데이트
public List<TodoEntity> update(final TodoEntity entity){
// (1) 저장할 엔티티가 유효한지 확인한다. 이 메서드는 2.3.1 Create Todo에서 구현했다.
validate(entity);
// (2) 넘겨받은 엔티티 id를 이용해 TodoEntity를 가져온다. 존재하지 않는 엔티티는 업데이트할 수 없기 때문
final Optional<TodoEntity> original = repository.findById(entity.getId());
original.ifPresent(todo -> {
// (3) 반환된 TodoEntity가 존재하면 값을 새 entity의 값으로 덮어 씌운다.
todo.setTitle(entity.getTitle());
todo.setDone(entity.isDone());
// (4) 데이터베이스에 새 값을 저장한다.
repository.save(todo);
});
// 2.3.2 Retrieve Todo에서 만든 메서드를 이용해 유저의 모든 Todo 리스트를 리턴한다.
return retrieve(entity.getUserId());
}
컨트롤러 구현
TodoController.java에 새 PUT 메서드를 만들어 준다.
// TodoController.java의 PUT 메서드
@PutMapping
public ResponseEntity<?> updateTodo(@RequestBody TodoDTO dto){
String temporaryUserId = "temporary-user"; //temporary user id.
TodoEntity entity = TodoDTO.toEntity(dto);
entity.setUserId(temporaryUserId);
List<TodoEntity> entities = service.update(entity);
List<TodoDTO> dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().data(dtos).build();
return ResponseEntity.ok().body(response);
}
Delete Todo 구현
퍼시스턴스 구현
업데이트하기 위해 기존에 작성했던 TodoReository에서 delete(), findByUserId() 메서드를 사용한다.
서비스 구현
Delete 기능을 구현하기 위해 TodoService에 delete() 메서드를 작성하자.
// delete 구현, Todo를 업데이트
public List<TodoEntity> delete(final TodoEntity entity){
validate(entity);
try {
repository.delete(entity);
} catch(Exception e) {
log.error("error deleting entity", entity.getId(), e);
throw new RuntimeException("error deleting entity" + entity.getId());
}
return retrieve(entity.getUserId());
}
컨트롤러 구현
TodoController.java에 새 Delete 메서드를 만들어준다.
// TodoController.java의 deleteTodo 메서드
@DeleteMapping
public ResponseEntity<?> deleteTodo(@RequestBody TodoDTO dto){
try{
String temporaryUserId = "temporary-user"; // temporary user id.
TodoEntity entity = TodoDTO.toEntity(dto);
entity.setUserId(temporaryUserId);
List<TodoEntity> entities = service.delete(entity);
List<TodoDTO> dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().data(dtos).build();
return ResponseEntity.ok().body(response);
}catch (Exception e){
String error = e.getMessage();
ResponseDTO<TodoDTO> response = ResponseDTO.<TodoDTO>builder().error(error).build();
return ResponseEntity.badRequest().body(response);
}
}