지금까지 API기능을 만들기 위한 모든 부분들을 간단하게 만들어봤다. 이제 이들을 조합해서 간단한 블로그 API를 만들어보자
매개변수로 받은 변수들을 활용해서 상황에 맞는 응답의 데이터를 조작한다.
@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~
구문을 사용해서 예외처리를 하면 된다. 자세한 내용은 따로 포스팅에서 설명하겠다.
public class PostNotFoundException extends RuntimeException{
public PostNotFoundException() {
super("존재하지 않는 게시글입니다.");
}
}
java에서 예외처리를 직접 구현하는거보다 예외를 던지고 handler에서 처리하는것이 훨씬 더 깔끔한 구현이니 위와같은 예외를 만들어서 문제가 생겼을 때 바로 던지자.
@Getter
public class updatePostRequest {
String title;
String body;
}
요청받을 데이터를 선언하고 컨트롤러에서 서비스에 데이터를 넘길수 있도록 getter정도 선언해주면 좋다.
> 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를 생성해 주어야 한다.
@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 기능에 대한 구현은 우선 전부 마쳤다. 다음시간에는 예외처리에 대해서 알아보자