[Spring Boot] Day12 - 서비스 계층

Sarah·2025년 12월 13일

Spring Boot

목록 보기
11/17

오늘의 목표

Service 계층을 따로 만들어 ApiController를 더 간단한 코드로 리팩터링하기


서비스 계층

  • 서비스 : 컨트롤러와 리파지터리 사이에 위치하여, 핵심 비즈니스 로직 담당. Repository를 호출하여 결과를 받아옴.
  • 컨트롤러 : 클라이언트로부터 요청을 받고 응답을 하는 역할, 서비스 계층을 호출하여 결과를 받아옴.

    서비스 계층을 만들어서 서비스와 컨트롤러의 역할을 분리하면 코드가 깔끔해지고 유지보수가 쉬워진다.


서비스 계층 만들기

  • main -> java -> 프로젝트 패키지 안에 Service 이름의 새로운 패키지 생성
  • ArticleService 이름의 자바 클래스 생성
@Service
@Slf4j
public class ArticleService {
    @Autowired
    ArticleRepository articleRepository;
  • @Service : 서비스 계층임을 알리는 어노테이션
  • @Slf4j : 로그 사용시 필요한 어노테이션
  • 서비스 계층에서는 리파지터리 호출을 통해 데이터를 다루기 때문에 @Autowired 로 리파지터리 의존성 주입
public class ArticleApiController {
    @Autowired
    private ArticleService articleService;
  • 기존에 리파지터리 의존성 주입 했던 ApiController에서는 이제 서비스 계층을 호출할 것이기 때문에 ArticleService 객체 주입으로 수정

Read 조회 기능 분리

  • ApiController
    @GetMapping("/api/articles")
    public List<Article> index() {
        return articleService.index();
    }

    @GetMapping("/api/articles/{id}")
    public Article show(@PathVariable Long id) {
        return articleService.show(id);
    }
  • apicontroller에서는 return 값으로 서비스 계층의 index 메서드 호출

  • Service
    public List<Article> index() {
        return articleRepository.findAll();
    }

    public Article show(Long id) {
        return articleRepository.findById(id).orElse(null);
    }

Create 생성 기능 분리

  • ApiController
    @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();
    }
  • 서비스의 create 메소드 호출하여 가져온 값을 created에 저장.
  • 만약 created가 null이 아니라면 Http 상태 200, body는 created 내용으로 리턴
  • null이라면 Bad_request로 리턴

  • Service
    public Article create(ArticleForm dto) {
        Article article = dto.toEntity();
        if(article.getId() != null) {
            return null;
        }
        return articleRepository.save(article);
    }
  • 컨트롤러에서 호출됐을 때 받아온 매개변수 dto를 엔티티로 변환.
  • 새로운 데이터를 추가할 때, id를 포함해서 받아왔다면 (article.getId()!=null) null을 반환
  • 아니라면, 리파지터리 사용해서 해당 엔티티를 저장

Update 수정 기능 분리

  • ApiController
    @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();
    }
  • 서비스의 update 메소드 호출하여 가져온 값을 updated 저장.
  • 만약 updated가 null이 아니라면 Http 상태 200, body는 updated 내용으로 리턴
  • null이라면 Bad_request로 리턴

  • Service
    public Article update(Long id, ArticleForm dto) {
        Article article = dto.toEntity();
        log.info("id: {}, article: {}", id, article.toString());
        Article target = articleRepository.findById(id).orElse(null);
        if(target == null || id != article.getId()) {
            log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
            return null;
        }
        target.patch(article);
        Article updated = articleRepository.save(target);
        return updated;
    }
  • 매개변수로 받아온 dto를 엔티티로 변환하여 article에 저장. (새로 받아온 데이터들)
  • url에서 받아온 id로 찾은 데이터를 target에 저장. (해당 id에 존재하는 기존 데이터)
  • 해당 id에 존재하는 기존 데이터가 없거나, 새로 받아온 데이터의 id와 url에서 받아온 id가 일치하지 않는다면 -> null 리턴
  • 아니라면, 기존데이터 (target)에서 patch 메서드 호출하여 데이터 내용을 article로 변경
  • 덮어쓰기 된 target 데이터를 저장하고 이를 updated에 저장하고 리턴

Delete 삭제 기능 분리

  • ApiController
    @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() ;
    }
  • 서비스의 delete 메소드 호출하여 가져온 값(target)을 deleted 저장.
  • 만약 deleted가 null이 아니라면 No_Content로 리턴
  • null이라면(id에 해당하는 데이터가 없음) Bad_request로 리턴

  • Service
    public Article delete(Long id) {
        Article target = articleRepository.findById(id).orElse(null);
        if(target == null) return null;
        articleRepository.delete(target);
        return target;
    }
  • url에서 받아온 id로 찾은 데이터를 target에 저장.
  • 해당 데이터가 없다면? (id에 해당하는 데이터가 없음) null 리턴
  • 아니라면 리파지터리로 delete호출하여 target 삭제
  • 삭제 대상 엔티티 (target) 반환

profile
헤맨 만큼 내 땅

0개의 댓글