dew's CSE Studying

스프링부트3 백엔드 12~14장 본문

3-1/[inflearn]스프링입문

스프링부트3 백엔드 12~14장

dew₍ᐢ.ˬ.⑅ᐢ₎ 2024. 11. 30. 01:15

12장 서비스 계층과 트랜잭션

12.1 서비스와 트랜잭션의 개념

서비스(service): 컨트롤러와 리파지터리 사이에 위치하는 계층

-서버의 핵심 기능(비즈니스 로직)을 처리하는 순서를 총괄한다

 

트랜잭션(transaction): 모두 성공해야 하는 일련의 과정

 

롤백(rollback): 트랜잭션이 실패로 돌아갈 경우 진행 초기 단계로 돌리는 것

 

12.2 서비스 계층 만들기

service package를 만들어주고
ArticleApiController은 모두 주석처리 후 바꿔보자!

@Autowired
    private ArticleRepository articleRepository; //게시글 리파지터리 객체 주입
}

일단 게시글 리파지터리 객체를 주입해주었다

 

12.2.1. 게시글 조회 요청 개선하기

  • 모든 게시글 조회 요청 개선하기
// GET
    @GetMapping("/api/articles")
    public List<Article> index() {
        return articleService.index();
    }
public List<Article> index() {
        return articleRepository.findAll();
    }

DB에서 조회한 결과를 반환한다

 

  • 단일 게시글 조회 요청 개선하기
@GetMapping("/api/articles/{id}")
    public Article show(@PathVariable Long id) {
        return articleService.show(id);
    }
 @GetMapping("/api/articles/{id}")
    public Article show(@PathVariable Long id) {
        return articleService.show(id);
    }

 

 

12.2.2 게시글 생성 요청 개선하기

이번엔 create 부분을 수정해보자

@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();
    }
public Article create(ArticleForm dto) {
        Article article = dto.toEntity();
        return articleRepository.save(article);
    }

 

 

12.2.3 게시글 수정 요청 개선하기

update 부분 수정하기

 @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();
    }
public Article update(Long id, ArticleForm dto) {
        // 1. DTO -> 엔터티 변환하기
        Article article = articleRepository.findById(id).orElse(null);
        log.info("id: {}, article: {}", id, article.toString());
        // 2. 타깃 조회하기
        Article target=articleRepository.findById(id).orElse(null);
        // 3. 잘못된 요청 처리하기
        if (target == null || id != article.getId()) {
            // 400, 잘못된 요청 응답!
            log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
            return null;
        }
        // 4. 업데이트 및 정상 응답(200)하기
        target.patch(article);
        Article updated= articleRepository.save(target);
        return updated;
    }

 

12.2.4 게시글 삭제 요청 개선하기

delete 수정하기

// DELETE
    @DeleteMapping("/api/articles/{id}")
    public ResponseEntity<Article> delete(@PathVariable Long id) {
        Article deleted = articleService.delete(id);
        return (deleted != null) ?
                ResponseEntity.status(HttpStatus.OK).body() :
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
    }
public Article delete(Long id) {
        // 1. 대상 찾기
        Article target = articleRepository.findById(id).orElse(null);
        // 2. 잘못된 요청 처리하기
        if (target == null) {
            return null;
        }
        // 3. 대상 삭제하기
        articleRepository.delete(target);
        return target;
    }

 

12.3 트랜잭션 맛보기

  1. 게시판에 데이터 3개를 한꺼번에 생성 요청하기
  2. 데이터를 DB에 저장하는 과정에서 의도적으로 오류 발생시키기
  3. 어떻게 롤백되는지 확인하기
public List<Article> createArticles(List<ArticleForm> dtos) {
        // 1. dto 묶음을 엔티티 묶음으로 변환하기
        List<Article> articleList = dtos.stream()
                .map(dto->dto.toEntity())
                .collect(Collectors.toList());
        // 2. 엔티티 묶음을 DB에 저장시키기
        articleList.stream()
                .forEach(article -> articleRepository.save(article));
        // 3. 강제 예외 발생시키기
        articleRepository.findById(-1L)
                .orElseThrow(() -> new IllegalArgumentException("결제 실패!"));
        // 4. 결과 값 반환하기
        return articleList;
    }

이렇게 전송해보면 500 error가 뜨는데
결제 실패 에러가 뜬 것을 확인할 수 있다
데이터 생성이 실패했음에도 데이터가 남아있다

@Transactional

을 추가해주었다

 

13장 테스트 코드 작성하기

13.1 테스트란

테스트(test): 프로그램의 품질을 검증하는 것으로, 프로그램이 잘 동작하는지 확인하는 과정

 

테스트 코드의 3단계

  1. 예상 데이터 작성하기(=테스트 케이스 작성)
  2. 실제 데이터 획득하기
  3. 예상 데이터와 실제 데이터 비교해 검증하기

 

13.2 테스트 코드 작성하기

 

13.2.1 테스트 코드 기본 틀 만들기

 

13.2.2 index() 테스트하기

Arrays.asList() 메서드: 입력된 배열 또는 2개 이상의 동일한 타입 데이터를 정적 리스트로 만들어 반환한다

코드 작성
test passed 확인!

 

13.2.3 show() 테스트하기

게시글 조회에 성공하는 경우
게시글 조회에 실패하는 경우

 

13.2.4 create() 테스트하기

게시글 생성에 성공하는 경우
게시글 생성 실패

13.2.5 여러 테스트 케이스 한 번에 실행하기

데이터를 조회(read)하는 테스트를 제외하고 데이터를 생성, 수정, 삭제하는 테스트를 할 때는 반드시 해당 테스트르 ㄹ트랜잭션으로 묶어 테스트가 종료한 후 원래대로 돌아갈 수 있게 롤백 처리해주어야 한다!

 

 

Part 4 댓글 CRUD 만들기

14장 댓글 엔티티와 리파지터리 만들기

14.1 댓글 기능의 개요

14.1.1 댓글과 게시글의 관계

-일대다(1:n) 관계

-id같이 자신을 대표하는 속성인 대표키(pk) 존재

-article_id같이 연관 대상을 가리키는 속성인 외래키(fk) 존재

 

14.1.2 댓글 엔티티와 리파지터리 설계

  • 엔터티: DB 데이터를 담는 자바 객체로, 엔티티를 기반으로 테이블 생성
  • 리파지터리: 엔티티를 관리하는 인터페이스로, 데이터 CRUD 등의 기능 제공

각 인터세이스의 기능

  • Repository: 최상위 리파지터리 인터페이스
  • CrudRepository 및 ListCrudRepository: 엔티티의 CRUD 기능 제공
  • PagingAndSortingRepository 및 ListPagingAndSortingRepository: 엔티티의 페이징 및 정렬 기능 제공
  • JpaRepository: 엔티티의 CRUD 기능과 페이징 및 정렬 기능+JPA에 특화된 기능을 추가로 제공

 

14.2 댓글 엔티티 만들기

14.2.1 댓글 엔티티 만들기

코드를 작성해주고
FirstprojectApplication.java를 실행하면 로그에 create table comment가 찍힌 것을 확인할 수 있다

 

아직 아무것도 없긴 하지만 콘솔에서도 확인 가능!

 

14.2.2 더미 데이터 추가하기

이렇게 더미 데이터를 작성해주고
콘솔에 업데이트 된 거 확인함

14.2.3 댓글 조회 쿼리 연습하기

  • 특정 게시글의 모든 댓글 조회

 

  • 특정 닉네임의 모든 댓글 조회

14.3 댓글 리파지터리 만들기

14.3.1 댓글 리파지터리 만들기

네이티브 쿼리 메서드(native query method): 직접 작성한 SQL 쿼리를 리파지터리 메서드로 실행할 수 있게 해준다

방법: @Query / orm.xml 파일 이용

 

  • 특정 게시글의 모든 댓글 조회_@Query

  • 특정 닉네임의 모든 댓글 조회_orm.xml

 

14.3.2 댓글 리파지터리 테스트 코드 작성하기

  • findAllByArticleId()