본 시리즈는 메타 코딩님의 Junit 강의를 학습한 내용을 바탕으로 정리하였습니다.
Service layer에서 지금까지 책 목록보기와 책 등록. 즉, Create
와 Read
를 구현했다. 이제 Read
의 또 다른 기능인 책 한건보기를 구현하고 나머지 삭제와 수정을 모두 한번에 구현할 것이다.
BookService.java
// 1. 책 등록하기
public BookRespDto 책등록하기(BookSaveReqDto dto) {
// (...생략)
}
// 2. 책 목록보기
public List<BookRespDto> 책목록보기() {
// (...생략)
}
// 3. 책 한건보기
public BookRespDto 책한건보기(Long id) {
Optional<Book> bookOP = bookRepository.findById(id);
if(bookOP.isPresent()) { //찾았다면
return new BookRespDto().toDto(bookOP.get());
} else {
throw new RuntimeException("해당 아이디를 찾을 수 없습니다.");
}
}
책 한건보기는 쉽게 구현할 수 있다. Java8의 Optional
클래스를 통해 bookRepository
에서의 null 값을 허용시키고, 만약 찾았다면 Dto
에서 get으로 리턴. 못 찾았다면 RuntimeException
을 터뜨려주면 된다.
❓
Optional
클래스 정확히 어떤 것일까?
저의 블로그 시리즈 중 spring 입문 강의를 들으면서 정리한 포스팅을 참고해주세요...! 😀
https://velog.io/@kjh950330/TIL-JAVA-spring-DAY-6
BookService.java
// 1. 책 등록하기
public BookRespDto 책등록하기(BookSaveReqDto dto) {
// (...생략)
}
// 2. 책 목록보기
public List<BookRespDto> 책목록보기() {
// (...생략)
}
// 3. 책 한건보기
public BookRespDto 책한건보기(Long id) {
// (...생략)
}
// 4. 책 삭제하기
@Transactional(rollbackOn = RuntimeException.class)
public void 책삭제하기(Long id) {
bookRepository.deleteById(id);
}
책 삭제하기도 마찬가지로 위와 같이 간단하게 구현할 수 있다.
그런데 한 가지 의문점이 있다.
BookRepository
는 ID를 통해서 삭제하는 구조이다. 테이블에 record ID가 1
, 2
, 3
까지만 들어와있다고 가정하자. 그런데 ID가 4
인 record를 삭제하려고 한다면 즉, 쿼리문으로 delete from book where id = 4;
를 실행한다면 오류가 발생하게 될까?
정답은 "오류가 발생하지 않는다." 이다. DB에서 찾지 못했다고 해서 오류가 발생하는 것은 아니다. Null이면 당연히 IllegalArgumentException
이 터지겠지만 4
의 경우는 DB에 없는 번호일 뿐, Null은 아닌 것이다. 사실 이 경우에는 DB쪽에서 삭제도 되지 않았기 때문에 Rollback
또한 되지 않았을 것이다.
마지막으로 책 수정하기를 살펴보자.
BookService.java
// 1. 책 등록하기
public BookRespDto 책등록하기(BookSaveReqDto dto) {
// (...생략)
}
// 2. 책 목록보기
public List<BookRespDto> 책목록보기() {
// (...생략)
}
// 3. 책 한건보기
public BookRespDto 책한건보기(Long id) {
// (...생략)
}
// 4. 책 삭제하기
@Transactional(rollbackOn = RuntimeException.class)
public void 책삭제하기(Long id) {
// (...생략)
}
// 5. 책 수정하기
@Transactional(rollbackOn = RuntimeException.class)
public void 책수정하기(Long id, BookSaveReqDto dto) { // id, title, author
Optional<Book> bookOP = bookRepository.findById(id); // 1, 2, 3
if(bookOP.isPresent()) {
Book bookPS = bookOP.get();
bookPS.update(dto.getTitle(), dto.getAuthor());
}else {
throw new RuntimeException("해당 아이디를 찾을 수 없습니다.");
}
}
책을 수정하기 위해서는 인자로 id
, title
, author
를 모두 받아야 한다. id
를 통해 책이 실제로 있는지 찾고, title
과 author
를 수정할 것이기 때문이다. 그런데 title
과 author
는 BookSaveDto
로 구현해놓았기 때문에 BookSaveDto
를 가져다 쓴다.
책 수정도 책 한건보기와 마찬가지로 책을 찾았을 때와 못 찾았을 때로 나누어서 생각한다. 책을 찾았을 경우, 영속화된 bookPS
객체에 실제로 업데이트를 진행하면 된다. 이렇게 한다면 메서드가 종료될 때, 더티체킹으로 업데이트가 실행된다. 즉, DB쪽으로 flush가 되고 커밋이 되는 것이다.
반면, 못 찾았을 경우, RuntimeException
을 터뜨려 주면 된다.
마침내.. 드디어 Service layer의 모든 구현이 끝났다!
이제 이 코드들이 잘 동작하는지 테스트코드를 다음 포스트에서 구현해보자.