본문 바로가기
springboot로 API만들어보기

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

by alwaysone 2021. 1. 3.

지금까지 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 PostDto addPost(String title, String body) {
        Post post = this.postRepository.save(new Post(title, body));
        return new PostDto(post.getTitle(), post.getBody(), post.getViews());
    }

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

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

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

Exception

> post/exception/exceptions/PostNotFoundException.java

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

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

Response

> post/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();
    }
}

> post/response/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 GetPostResponse addPost(
            @RequestBody updatePostRequest updatePostRequest
    ) {
        PostDto post = this.postService.addPost(updatePostRequest.getTitle(), updatePostRequest.getBody());
        return new GetPostResponse(post);
    }

    @PutMapping("/{postId}")
    @ResponseStatus(HttpStatus.OK)
    public GetPostResponse updatePost(
            @PathVariable long postId,
            @RequestBody updatePostRequest updatePostRequest
    ) {
        PostDto post = this.postService.updatePost(postId, updatePostRequest.getTitle(), updatePostRequest.getBody());
        return new GetPostResponse(post);
    }

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

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

현재까지의 패키지 구조

마무리

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

댓글