
최근에 저는 REAT API를 설계했습니다.
이번에는 역할을 분할하기 위해 서비스 계층을 만들었습니다.
@Service //서비스 선언 (서비스 객체를 스프링부트에 생성)
public class ArticleService {
@Autowired
private ArticleRepository articleRepository;
public List<Article> index() {
return articleRepository.findAll();
}
public Article show(Long id) {
return articleRepository.findById(id).orElse(null);
}
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
if(article.getId() != null){
return null;
}
return articleRepository.save(article);
}
public Article update(Long id, ArticleForm dto) {
// 1. 수정용 엔티티 생성
Article article = dto.toEntity();
// 2. 대상 엔티티 찾기
Article target = articleRepository.findById(id).orElse(null);
// 3. 잘못된 요청 처리
if(target == null || id != article.getId()){
return null;
}
// 4. 업데이트
target.patch(article);
Article updated = articleRepository.save(target);
return updated;
}
public Article delete(Long id) {
// 대상 찾기
Article target = articleRepository.findById(id).orElse(null);
// 잘못된 요청 처리
if(target == null){
return null;
}
// 대상 삭제
articleRepository.delete(target);
return target;
}
@Transactional // 해당 메소드를 트랙잭션으로 묶는다!
public List<Article> createArticle(List<ArticleForm> dtos) {
// dto 묶음을 entity 묶음으로 변환
List<Article> articlesList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());
// entity 묶음을 DB로 저장
articlesList.stream().forEach(article -> articleRepository.save(article));
// 강제 예외 발생
articleRepository.findById(-1L).orElseThrow(
() -> new IllegalArgumentException("결재 실패")
) ;
// 결과값변환
return null;
}
@Slf4j
@RestController // RestAPI용 컨트롤러 데이터 JSON을 반환
public class ArticleApiController {
@Autowired // DI,생성 객체를 가져와 생성
private ArticleService articleService;
//GET
@GetMapping("/api/articles")
public List<Article> index() {
return articleService.index();
}
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id) {
return articleService.show(id);
}
//POST
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto) { //JSON 데이터 받기
Article created = articleService.create(dto);
return (created != null) ?
ResponseEntity.status(HttpStatus.OK).body(created) :
ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
//PATCH
@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();
}
//delete
@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();
}
// 트랙잭션 -> 실패 -> 롤백!
@PostMapping("/api/transcation-test")
public ResponseEntity<List<Article>> trascatuonTest(@RequestBody List<ArticleForm> dtos){
List<Article> createdList = articleService.createArticle(dtos);
return (createdList != null) ? ResponseEntity.status(HttpStatus.OK).body(createdList):
ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
이전 코드에 비해 코드가 늘어났지만 각 역할이 명확하게 유지되고 있는 것을 확인할 수 있습니다.
———————–
값과 코드로 결과를 쓰고 싶은데 마우스를 집에 두고 나서 쓰기가 힘듭니다.