[스프링부트로 API 만들기] Api 구현해보기

황상일·2021년 1월 2일
0

지금까지 API기능을 만들기 위한 모든 부분들을 간단하게 만들어봤다. 이제 이들을 조합해서 간단한 블로그 API를 만들어보자

Service

매개변수로 받은 변수들을 활용해서 상황에 맞는 응답의 데이터를 조작한다.

@Service
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository;

    @Transactional(readOnly = true)
    public List<PostDto> getPosts() {
        return this.postRepository.findAll().stream()
                .map(post -> new PostDto(post.getTitle(), post.getBody(), post.getViews()))
                .collect(Collectors.toList());
    }

    @Transactional
    public PostDto getPost(long postId) {
        Post post = this.postRepository.findById(postId).orElseThrow(PostNotFoundException::new);
        post.increaseViews();
        return new PostDto(post.getTitle(), post.getBody(), post.getViews());
    }

    @Transactional
    public void addPost(String title, String body) {
        this.postRepository.save(new Post(title, body));
    }

    @Transactional
    public void updatePost(long postId, String title, String body) {
        Post post = this.postRepository.findById(postId).orElseThrow(PostNotFoundException::new);
        post.updatePost(title, body);
    }

    @Transactional
    public void deletePost(long postId) {
        this.postRepository.deleteById(postId);
    }
}

repository를 이용하여 데이터를 조회 한 후, 가공하여 DTO를 사용하여 반환한다. 데이터의 트랜잭션을 보장하기 위햐여 @Transactional어노테이션을 붙여주고 상황에 맞게 옵션을 준다. 이부분은 JPA시리즈에서 자세히 설명하겠다.
Spring Data JPA를 사용해서 데이터를 조회할 때 기본으로 제공되는 기능은 Optional자료형을 사용한다. Null체크를 일일이 하지 않고 제공되는 orElse~구문을 사용해서 예외처리를 하면 된다. 자세한 내용은 따로 포스팅에서 설명하겠다.

Exception

public class PostNotFoundException extends RuntimeException{
    public PostNotFoundException() {
        super("존재하지 않는 게시글입니다.");
    }
}

java에서 예외처리를 직접 구현하는거보다 예외를 던지고 handler에서 처리하는것이 훨씬 더 깔끔한 구현이니 위와같은 예외를 만들어서 문제가 생겼을 때 바로 던지자.

Request

@Getter
public class updatePostRequest {
    String title;
    String body;
}

요청받을 데이터를 선언하고 컨트롤러에서 서비스에 데이터를 넘길수 있도록 getter정도 선언해주면 좋다.

Response

> GetPostResponse.java

@Getter
public class GetPostResponse {
    private String title;
    private String body;
    private Long views;

    public GetPostResponse(PostDto postDto){
        this.title = postDto.getTitle();
        this.body = postDto.getBody();
        this.views = postDto.getViews();
    }
}

> GetPostsResponse.java

@Getter
@AllArgsConstructor
public class GetPostsResponse {
    List<PostDto> posts;
}

서비스에서 받은 데이터를 컨트롤러에서 생성할 수 있도록 생성자를 만들어 주면 되고, 스프링이 responsebody를 구성할 수 있도록 Getter를 생성해 주어야 한다.

Controller

@RestController
@RequestMapping("/posts")
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    @GetMapping
    @ResponseStatus(HttpStatus.OK)
    public GetPostsResponse getPosts() {
        List<PostDto> posts = this.postService.getPosts();
        return new GetPostsResponse(posts);
    }


    @GetMapping("/{postId}")
    @ResponseStatus(HttpStatus.OK)
    public GetPostResponse getPost(
            @PathVariable long postId
    ) {
        PostDto post = this.postService.getPost(postId);
        return new GetPostResponse(post);
    }


    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void addPost(
            @RequestBody updatePostRequest updatePostRequest
    ) {
        this.postService.addPost(updatePostRequest.getTitle(), updatePostRequest.getBody());
    }

    @PutMapping("/{postId}")
    @ResponseStatus(HttpStatus.OK)
    public void updatePost(
            @PathVariable long postId,
            @RequestBody updatePostRequest updatePostRequest
    ) {
        this.postService.updatePost(postId, updatePostRequest.getTitle(), updatePostRequest.getBody());
    }

    @DeleteMapping("/{postId}")
    @ResponseStatus(HttpStatus.OK)
    public void deletePost(
            @PathVariable long postId
    ) {
        this.postService.deletePost(postId);
    }
}

서비스로부터 받은 데이터를 지정한 Response에 맞게 포장하여 반환한다.

현재까지의 패키지 구조

마무리

간단하다고 하면 간단하고, 복잡하다고 하면 복잡하다고 할 수 있는 Api 기능에 대한 구현은 우선 전부 마쳤다. 다음시간에는 예외처리에 대해서 알아보자

profile
받은만큼 나눠주자

0개의 댓글