서비스(service)컨트롤러와 리파지터리 사이에 위치한 계층.컨트롤러 - 서비스 - 리파지터리의 관계(출처)

컨트롤러(클라이언트의 요청 받기) -> 서비스(리파지터리에게 클라이언트의 요청에 대한 데이터를 반환해 달라고 요청) -> 리파지터리 -> 서비스 -> 컨트롤러(클라이언트에 응답)트랜잭션(transaction)원자성이라 함.)롤백(rollback)이라 함.@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository; // ArticleRepository 객체 주입.
}
@RestController
@Slf4j
public class ArticleApiController {
@Autowired
private ArticleService articleService; // @Service로 인해 에러표시 사라짐.
}
@Service(@Autowired) private ArticleService articleService;@Service를 사용함으로써 에러표시 사라짐.(DI)// controller
@GetMapping("/api/articles")
public List<Article> index() { // 모든 데이터 조회.
return articleService.index();
}
// service
public List<Article> index() {
return articleRepository.findAll();
}
controllerservice에 있는 index() 메서드 호출.servicearticleRepository의 findAll() 메서드를 통해 DB에서 조회한 데이터들을 반환.// controller
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id) { // 단일 데이터 조회.
return articleService.show(id);
}
// service
public Article show(Long id) {
return articleRepository.findById(id).orElse(null);
}
controller{id} 변수를 @PathVariable을 이용해 매개변수로 받아서, service에 있는 show() 메서드 호출.serviceid를 이용해서 repository의 findById()메서드 호출함.있으면 해당 데이터를 반환.없으면 null을 반환.// controller
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto) { // 새로운 데이터 생성.
Article created = articleService.create(dto);
return created != null ? ResponseEntity.status(HttpStatus.OK).body(created) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
if (article.getId() != null) {
return null;
}
return articleRepository.save(article);
}
controllerREST API에서 데이터를 생성할 때는 JSON 데이터를 받아와야 하므로, @RequestBody를 통해 요청 Body(본문)에 실어 보낸 데이터를 매개변수(dto)에 매핑시켜줌.service의 create() 메서드 호출.service에서 반환된 값!= null 이면 HTTP 상태 코드(200), 응답 Body에 생성한 데이터를 담아서 반환.= null 이면 HTTP 상태 코드(400), 응답 Body에 null을 담아서 반환servicedto를 Entity로 변환.id값은 DB에서 auto_increment로 자동 생성되기 때문에 id가 값이 있을 경우 null을 반환.repository의 save()메서드를 통해 article엔터티를 저장하고 반환.정상 처리

비정상 처리

// controller
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) { // 데이터 수정.
Article updated = articleService.update(id, dto);
return updated != null ? ResponseEntity.status(HttpStatus.OK).body(updated) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article update(Long id, ArticleForm dto) {
Article article = dto.toEntity();
Article target = articleRepository.findById(id).orElse(null);
if (target == null || id != article.getId()) {
return null;
}
target.patch(article);
return articleRepository.save(target);
}
controller{id} 변수를 @PathVariable을 이용해 매개변수 id에 매핑.JSON 데이터를 받아와야 하므로, @RequestBody를 통해 요청 Body(본문)에 실어 보낸 데이터를 매개변수(dto)에 매핑.service에 있는 update()메서드 호출.!= null이면 HTTP 상태 코드(200), 응답 Body에 수정된 데이터를 담아서 반환.= null이면 HTTP 상태 코드(400), 응답 Body에 null을 담아서 반환.service매개변수로 받은 dto를 Entity로 변환.매개변수로 받은 id와 repository의 findById()메서드를 이용해서 값을 조회.있다면, 해당 데이터를 저장.없다면, null을 저장.id와 요청Body에 실려온 id값이 다를 경우 null반환.if문이 false여서 지나쳐왔다면 patch()메서드를 호출해서 수정된 값이 있는 필드만 값을 갱신하고, save메서드를 통해 저장한 뒤 반환.URL 요청id와 요청Body의 id가 다를 경우

일부 데이터만 수정한 경우

모든 데이터를 수정

// controller
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id) { // 데이터 삭제.
Article deleted = articleService.delete(id);
return deleted != null ? ResponseEntity.status(HttpStatus.NO_CONTENT).build() : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article delete(Long id) {
Article target = articleRepository.findById(id).orElse(null);
if (target == null) {
return null;
}
articleRepository.delete(target);
return target;
}
controller{id} 변수를 @PathVariable을 이용해 매개변수로 받아서, service에 있는 delete() 메서드 호출.service의 반환값.!= null : HTTP 상태 코드(204) 반환.= null : HTTP 상태 코드(400) 반환.service매개변수로 받은 id와 repository의 findById()를 이용해서 값을 조회.if문이 false일 경우, repository의 delete()메서드를 호출해서 삭제한 뒤 반환.일련의 과정.롤백됨.// controller
@PostMapping("/api/transaction-test")
public ResponseEntity<List<Article>> transactionTest(@RequestBody List<ArticleForm> dtos) {
List<Article> createdList = articleService.createArticles(dtos);
return createdList != null ? ResponseEntity.status(HttpStatus.OK).body(createdList) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
@Transactional
public List<Article> createArticles(List<ArticleForm> dtos) {
List<Article> articleList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());
// List<Article> articleList = new ArrayList<>();
// for (int i = 0; i < dtos.size(); i++) {
// ArticleForm dto = dtos.get(i);
// Article entity = dto.toEntity();
// articleList.add(entity);
// }
articleList.stream().forEach(article -> articleRepository.save(article));
// for (int i = 0; i < articleList.size(); i++) {
// Article article = articleList.get(i);
// articleRepository.save(article)
// }
articleRepository.findById(-1L).orElseThrow(() -> new IllegalArgumentException("실패!")); // 강제로 예외 발생.
return articleList;
}
Controller@PostMapping("/api/transaction-test")@PostMapping으로 /api/transaction-test요청 URL 받음.public ResponseEntity<List<Article>> transactionTest(@RequestBody List<ArticleForm> dtos)List<>, 메서드 반환타입도 List<>로 해줌으로써 응답시 List형태로 보냄.ResponseEntityResponseEntity<List<Article>>이러한 형태.REST API방식의 Post요청을 받으므로 @RequestBody어노테이션 추가.Post 요청 시 Body에 실려있는 데이터를 메서드 매개변수로 매핑시킴.Service@TransactionalcreateArticles 메서드는 하나의 트랜잭션으로 묶임.롤백됨.List<Article> articleList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());dtos 묶음을 entity 묶음으로 변환.dtos.stream()dtos를 stream으로 변환시킴..map(dto -> dto.toEntity())map은 스트림의 각 요소를 다른 값으로 변환하는 메서드.dto 객체 하나하나를 dto.toEntity() 메서드를 호출하여 변환..collect(Collectors.toList())stream에서 다시 리스트 형태로.스트림(stream)문법은 List와 같은 자료구조에 저장된 요소를 하나씩 순회하면서 처리하는 코드 패턴.articleList.stream().forEach(article -> articleRepository.save(article));articleList.stream()List를 Stream으로 변환.forEach(article -> articleRepository.save(article))forEach는 스트림의 각 요소를 순회하는 메서드. article -> articleRepository.save(article)을 사용하여 각 Article 객체를 articleRepository를 통해 저장하는 작업을 수행.articleRepository.findById(-1L).orElseThrow(() -> new IllegalArgumentException("실패!"));id값에 -1(없는 값)을 넣어서 강제로 예외 발생시킴.