현의 개발 블로그

JPA로 도서관리 API 개발하기 본문

스프링부트 실습/도서관리 웹 개발

JPA로 도서관리 API 개발하기

hyun2371 2023. 4. 17. 16:25

책 생성 API 개발

API 명세서

HTTP Method POST
HTTP Path /book
HTTP Body {"name" : String}
결과 반환 X

 

book 테이블 설계하기

create table book
(
    id bigint auto_increment,
    name varchar(255),
    primary key (id)
);

@Column length 기본값이 255라서 varchar(255)로 지정했다.

 

 

Book 객체 생성

@Entity
@NoArgsConstructor(access = PROTECTED)
public class Book {
    @Id @GeneratedValue
    private Long id;
    
    @Column(nullable = false)
    private String name;
    
    public Book(String name){
    	if (name == null || name.isBlank())
        	throw new IllegalArgumentException();
        this.name = name;
    }
}

 

Book Repository 생성

public interface BookRepository extends JpaRepository<Book, Long>{}

 

Book DTO 생성

@Getter
public class BookCreateRequest {
    private String name;
}

 

 

Book Controller 생성

@RestController
@RequiredArgsConstructor
public class BookController {
    private final BookService bookService;
    
    @PostMapping("/book")
    public void saveBook(@RequestBody BookCreateRequest request){
    	bookService.saveBook(request);
    }
}

@RestController는 @ResponseBody + @Controller이다.

@ResponseBody는 자바 객체를 http body 객체로 변환한다.

@RequestBody는 http body에 있던 JSON 객체를 자바 객체로 변환한다.

 

@RequiredArgsConstructor는 final 변수를 생성자 주입해준다.

 

 

Book service 생성

@Service @RequiredArgsConstructor
public class BookService {
    private final BookRepository bookRepository;
    
    @Transactional
    public void saveBook(BookCreaterequest request){
    	bookRepository.save(new Book(request.getName());
    }
}

 

 

 

 

대출 API 개발

API 명세서

HTTP method POST
HTTP path /book/loan
HTTP Body {"userName" : String,
"bookName" : String}
결과 반환x

 

유저 대출 기록을 저장하는 새로운 테이블을 생성한다.

create table user_loan_history(
    id bigint auto_increment,
    user_id bigint,
    book_name varchar(255),
    is_return tinyint(1),
    primary key(id)
);

 

엔티티 생성

@Entity
@NoArgsConstructor 
public class UserLoanHistory{
    @Id @GeneratedValue
    private Long id;
    private Long userId;
    private String bookName;
    private boolean isReturn;
    
    public UserLoanHistory(Long userId, String bookName){
    	this.userId = userId;
        this.bookname = bookName;
        this.isReturn = false;
    }
}

 

DTO 생성

@Getter
public class BookLoanRequest {
    private String userName;
    private String bookName;
}

 

 

Controller 생성

@PostMapping("/book/loan")
public void loanBook(@RequestBody BookLoanRequest request){
	bookService.loanBook(request);
}

 

 

Service 생성

@Transactional
public void loanBook(BookLoanRequest request){
    Book book = bookRepository.findByName(request.getBookName())
    	.orElseThrow(IllegalArgumentException::new);
        
    if (userLoanHistoryRepository.existByBookNameAndIsReturn(book.getName(), false))
    	throw new IllegalArgumentException("이미 대출중인 책입니다.");
 	
    User user = userRepository.findByName(request.getUserName())
    				.orElseThrow(IllegalArgumentException::new);
   
    userLoanHistoryRepository.save(new UserLoanHistory(user.getId(),book.getName());
}

책 이름에 맞는 책을 찾는데, 없으면 예외를 던진다.

해당 책이 존재하는데 이미 대출 중이라면 예외를 던진다.

유저 이름과 일치하는 유저를 찾는데 없다면 예외를 던진다.

위의 조건들을 다 만족하면, 유저 이름과 책을 대출 기록에 저장한다.

 

 

Repository 인터페이스에 함수 추가

public interface BookRepository extends JpaRepository<Book, Long>{
    Optional<Book> findByName(String name);
}
public interface UserLoanHistoryRepository extends JpaRepository<UserLoanHistory,Long>{
	boolean existsByBookNameAndIsReturn(String name, boolean isReturn);
}
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByName(String name);
}

 

 

 

도서 반납 API 개발

HTTP Method PUT
HTTP path /book/return
HTTP Body {"userName" : String, "bookName": String}
결과 반환 x

앞의 도서 대출 API와 HTTP Body가 동일하다. 그렇다고 기존 DTO를 재활용하기보다는 새로 DTO를 생성하는 것이 좋다.

두 기능 중 한 기능에 변화가 생겼을 때 유지 및 보수가 용이하기 때문이다.

DTO 코드는 편의상 생략하겠다.

 

 

Controller 생성

@PutMapping("/book/return")
public void returnBook(@RequestBody BookReturnRequest request){
    bookService.returnBook(request);
}

 

service 생성

@Transactional
public void returnBook(BookReturnRequest request){
    User user = UserRepository.findByName(request.getUserName())
    		.orElseThrow(IllegalArgumentException::new);
            
    UserLocalHistory history = userLoanHistoryRepository
    	.findByUserIdAndBookNameAndIsReturn(user.getId(), request.getBookName())
        .orElseThrow(IllegalArgumentException::new);
     
    history.doReturn();
}

유저를 조회한다. 해당 유저가 없으면 예외를 던진다.

유저 아이디, 책 이름으로 기록 조회한다. 없으면 예외를 던진다.

UserLocalHistory 엔티티 내의 함수를 호출해 반납여부를 true로 변경한다.

 

 

 

 

마무리

이번에는 JPA로 도서관리 API를 개발하였다.

그러나 해당 코드는 객체 지향적이기 보다는 절차 지향적에 더 가깝다.

다음 글에서는 객체 지향적으로 개발하기 위해 연관관계 매핑을 살펴볼 것이다.

 

2023.04.19 - [스프링부트/도서관리 웹 개발] - 객체 지향적 개발 - 연관관계 매핑

 

 

 


참고

https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90

 

자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인

Java와 Spring Boot, JPA, MySQL, AWS를 이용해 서버를 개발하고 배포합니다. 웹 애플리케이션을 개발하며 서버 개발에 필요한 배경지식과 이론, 다양한 기술들을 모두 학습할 뿐 아니라, 다양한 옵션들

www.inflearn.com

 

Comments