Tech Blog of Pinomaker
Published 2022. 5. 30. 11:34
[Spring] Book Api Project B.E/Spring

Book Project

  • Create Book : 생성 요청 처리
  • Find Book : 조회 요청 처리
  • Find Books 전체 조회 요청 처리
API Method URL Request DATA Response DATA
Create Book POST "/api/book" {
  title : "String",
  authorId : "Long"
}
{
  id : "Long"
}
Find Books GET "/api/book" none [
  {
    id : "Long",
    title : "String",
    authodId : "Long"
  }
]
Find Book GET "/api/book/:id" none {
  id : "Long",
  title : "String",
  authodId : "Long"
}

 

폴더 구조

Package Class
controller BookController
domain Book
dto BookResponse
CreateBookRequest
repository BookRepository (interface)
BookInMemoryRepository
service BookService

 

Domain

domain는 모델의 정보 처리의 파일이다.

//domain

package com.example.demo.domain;

public class Book {
    //Book 객체는 고유 id, title, autorId를 가지고 있음
    private Long id;
    private String title;
    private Long authorId;

    //고유 ID Getter
    public Long getId() {
        return id;
    }
    
    //Title Getter
    public String getTitle() {
        return title;
    }

    //AuthorId Getter
    public Long getAuthorId() {
        return authorId;
    }
    
    //생성자 1
    public Book(Long id, String title, Long authorId) {
        this.id = id;
        this.title = title;
        this.authorId = authorId;
    }

    //생성자 2
    public Book(String title, Long authorId) {
        this.title = title;
        this.authorId = authorId;
    }
}

Controller

controller는 유저의 요청을 처리해주는 파일이다.

//controller
package com.example.demo.controller;

import com.example.demo.domain.Book;
import com.example.demo.dto.BookResponse;
import com.example.demo.dto.CreateBookRequest;
import com.example.demo.service.BookService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;

@RestController
public class BookController {
    private final BookService bookService;

    //생성자
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    //GET : "/api/book"
    @GetMapping("/api/book")
    public List<BookResponse> findAll(){
        //bookService.findAll 값을 BookResponse 객체로 생성
        return this.bookService.findAll().stream().map(book -> new BookResponse(book)).collect(Collectors.toList());
    }

    //GET : "/api/book/:id"
    @GetMapping("/api/book/{id}")
    //PathVariable를 이용하여 {id} 값을 추출하여 매개 변수로 전달
    public BookResponse findById(@PathVariable(name = "id") Long id) {
        //ID 값으로 Data를 찾아 book 객체 생성 후 저장
        Book book = this.bookService.findById(id);
        
        //BookRespnse에 book을 넣어 생성 후 리턴
        return new BookResponse(book);
    }

    @PostMapping("/api/book")
    //@RequestBody를 이용하여, 요청 값 읽기
    public Long save(@RequestBody CreateBookRequest request) {
        return this.bookService.save(request);
    }
}

Repository

인터페이스를 사용하여 만들어준다.

//interface : BookRepository

package com.example.demo.repository;

import java.util.*;
import com.example.demo.domain.Book;

public interface BookRepository {
    //Find Books
    List<Book> findAll();
    
    //Find Book
    Optional<Book> findById(Long id);

    //Create Book
    Book save(Book book);
}

 

BookRepository 인터페이스를 상속 받아 저장소를 생성한다.

//BookInMemoryRepository

package com.example.demo.repository;

import com.example.demo.domain.Book;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Repository
public class BookInMemoryRepository implements BookRepository {
    //Book 객체를 담은 ArrayList, bookList를 생성
    private final List<Book> bookList = new ArrayList<>();

    //Book 고유 ID 값 지정    private Long bookNextId = 1L;
    private Long bookNextId = 1L;

    //Book List 반환 기능
    @Override
    public List<Book> findAll() {
        return this.bookList;
    }

    //ID 값을 받아 해당 ID의 Book 반환
    @Override
    public Optional<Book> findById(Long id) {
        // stream을 이용하여, filter로 List를 돌리고, 입력 받은 ID와 List의 Book ID가 같으면 선택 후 첫 번째 값 반환
        return this.bookList.stream()
                .filter(book -> book.getId().equals(id))
                .findFirst();
    }

    //Book 객체를 받아 저장 및 업데이트
    @Override
    public Book save(Book book) {
        //Book 객체인, bookOptional에 입력 받은 book의 ID 값을 이용하여 List에 해당 ID 값 있는 지 여부 저장
        Optional<Book> bookOptional = this.findById(book.getId());

        //만약에 값이 있다면 => 중복 데이터
        if (bookOptional.isPresent()) {
            //수정한 book 객체를 가져와서 targetBook에 저장
            Book targetBook = bookOptional.get();
            //updateInfo 사용하여, 타이틀과 ID 값 변경
            targetBook.updateInfo(book.getTitle(), book.getAuthorId());
            return targetBook;
        }
        //savedBook 객체 생성자 이용하여 생성, 고유 ID 값과 타이틀, ID 값을 가짐
        Book savedBook = new Book(bookNextId++, book.getTitle(), book.getAuthorId());

        //add를 이용하여 bookList에 추가
        this.bookList.add(savedBook);
        //Return 해줌
       return savedBook;
    }
}

Service

서비스는 로직이 담긴 곳으로, controller와 연결된다.

//BookService

package com.example.demo.service;

import com.example.demo.domain.Book;
import com.example.demo.dto.CreateBookRequest;
import com.example.demo.repository.BookRepository;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class BookService {
    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }
    public List<Book> findAll(){
        return this.bookRepository.findAll();
    }

    public Book findById(Long id) {
        return this.bookRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("not exist book id : '" + id + "'"));
    }

    public Long save(CreateBookRequest request) {
        Book book = new Book(request.getTitle(), request.getAuthorId());
        Book savedBook = this.bookRepository.save(book);
        return savedBook.getId();
    }
}

DTO

DTO(Data Transfer Object)는 계층 간 데이터 교환을 하기 위해 사용하는 객체로, 로직을 가지고 있지 않으며, getter, setter만 가지고 있는 클래스다.

 

요청 받을 때 데이터와 응답을 보낼 때 사용한다.

 

//Book Response

package com.example.demo.dto;

import com.example.demo.domain.Book;

public class BookResponse {
    private Long id;
    private String title;
    private Long authorId;

    public BookResponse(Book book) {
        this.id = book.getId();

        this.title = book.getTitle();
        this.authorId = book.getAuthorId();
    }

    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public Long getAuthorId() {
        return authorId;
    }
}

 

//CreateBookRequest

package com.example.demo.dto;

import com.example.demo.domain.Book;

public class CreateBookRequest {
    private String title;
    private Long authorId;

    public CreateBookRequest(String title, Long authorId) {
        this.title = title;
        this.authorId = authorId;
    }

    public String getTitle() {
        return title;
    }

    public Long getAuthorId() {
        return authorId;
    }
}
profile

Tech Blog of Pinomaker

@pinomaker

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!