본문 바로가기

Project

Spring boot, react 이용한 블로그 개발 (4) - Post API 개발

이번 포스트에서는 포스트를 등록하고 수정하고 삭제하고 읽는 포스트 관련 CRUD API를 개발할 것이다. 이를 통해 블로그 개발에 있어 아주 기본적인 기능을 추가하게 되고 react를 통해 개발하는 프론트엔드와 연동하여 유의미한 화면을 만들 수 있을 것이다.


1. PostDto 객체 생성


- Post와 관련된 데이터를 CRUD를 통해 받을 때는 기존에 생성한 Post 도메인 객체를 사용하지 않을 것이다. 물론 @jsonIgnore 어노테이션을 이용하는 방법으로 원하는 데이터만 노출할 수도 있겠지만 Post 객체를 직접 이용하기 보단 데이터 이동간의 필수 필드만 가지고 있는 DTO 객체를 별도로 생성하여 사용할 것이다. 


@Data
public class PostDto {

private Long id;
private String title;
private String body;
private Long userId;
private String userName;
private String createdBy;
private LocalDateTime createdDate;
private String lastModifiedBy;
private LocalDateTime lastModifiedDate;

public PostDto () {}

public PostDto (Post post) {
this.id = post.getId();
this.title = post.getTitle();
this.body = post.getBody();
this.createdBy = post.getCreatedBy();
this.createdDate = post.getCreatedDate();
this.lastModifiedBy = post.getLastModifiedBy();
this.lastModifiedDate = post.getLastModifiedDate();
this.userId = post.getUser().getId();
this.userName = post.getUser().getUserName();
}
}


2. PostService 클래스 생성


- 이전 포스트에서 만든 PostRepository를 이용하여 포스트를 등록하고 수정하고 조회하고 삭제하는 코드를 작성하였다. 아직 User 관련 코드를 작성하지 않은 관계를 post 등록시 User는 임의로 생성하였다.

@Service
@Transactional
public class PostService {

@Autowired
private PostRepository postRepository;

public Optional<Post> findForId(Long id) {
return postRepository.findById(id);
}

public PostDto registerPost(PostDto postDto) {
Post newPost = new Post();
newPost.setTitle(postDto.getTitle());
newPost.setBody(postDto.getBody());
newPost.setUser(new User(1L)); // temporary code
return new PostDto(postRepository.saveAndFlush(newPost));
}

public Optional<PostDto> editPost(PostDto editPostDto) {
return this.findForId(editPostDto.getId())
.map(p -> {
p.setTitle(editPostDto.getTitle());
p.setBody(editPostDto.getBody());
return p;
})
.map(PostDto::new);
}

public Page<Post> findByUserOrderedByCreatedDatePageable(User user, Pageable pageable) {
return postRepository.findByUserOrderByCreatedDateDesc(user, pageable);
}

public Page<Post> findAllByOrderByCreatedDateDescPageable(Pageable pageable) {
return postRepository.findAllByOrderByCreatedDateDesc(pageable);
}

public void delete(Post post) {
postRepository.delete(post);
}
}


3. PostController 생성


- Post와 관련된 CRUD를 위한 컨트롤러를 작성하였다. API 행동에 맞는 HTTP 메소드를 선언하고 stateless 한 REST API 형태로 작성였고 기본적인 데이터 교환은 PostDto 객체를 이용하였다. 그리고 getPostList 메소드에서는 스프링의 Pageable 객체를 이용하여 손쉽게 페이징을 구현한 것을 확인할 수 있다. 기본적으로 모든 Response는 Spring의 ResponseEntity를 사용하였는데 ResponseEntity를 사용하면 body 메시지와 함께 명시적으로 적절한 HTTP 상태코드를 지정할 수 있기 때문이다.

@RestController
@RequestMapping("/api")
public class PostController {

private final Logger log = LoggerFactory.getLogger(PostController.class);

@Autowired
private PostService postService;

@GetMapping(value = "/posts/{id}")
public ResponseEntity<PostDto> getPost(@PathVariable Long id) {
log.debug("REST request to get Post : {}", id);
Post post = postService.findForId(id).orElseThrow(() -> new ApiException("Post does not exist", HttpStatus.NOT_FOUND));
return new ResponseEntity<>(new PostDto(post), HttpStatus.OK);
}

@GetMapping(value = "/posts")
public ResponseEntity<List<PostDto>> getPostList(Pageable pageable) {
log.debug("REST request to get Posts : {}", pageable);
Page<Post> posts = postService.findAllByOrderByCreatedDateDescPageable(pageable);
Page<PostDto> postDto = posts.map(post -> new PostDto((post)));
return new ResponseEntity<>(postDto.getContent(), HttpStatus.OK);
}

@PostMapping(value = "/posts")
public ResponseEntity<PostDto> registerPost(@RequestBody PostDto postDto) {
log.debug("REST request to save Post : {}", postDto);
if (postDto.getId() != null) {
throw new ApiException("A new post cannot already have an ID", HttpStatus.CONFLICT);
} else {
PostDto returnPost = postService.registerPost(postDto);
return new ResponseEntity<PostDto>(returnPost, HttpStatus.CREATED);
}
}

@PutMapping(value = "/posts/{id}")
public ResponseEntity<PostDto> editPost(@PathVariable Long id,
@RequestBody PostDto postDto) {
log.debug("REST request to edit Post : {}", postDto);
Optional<Post> post = postService.findForId(id);
if (!post.isPresent()) {
throw new ApiException("Post could not be found", HttpStatus.NOT_FOUND);
}
Optional<PostDto> returnPost = postService.editPost(postDto);
return returnPost.map(response -> {
return new ResponseEntity<PostDto>(response, HttpStatus.OK);
}).orElse(new ResponseEntity(HttpStatus.NOT_FOUND));
}
}

NEXT 

- 다음 포스트에서는 create-react-app을 통해 프론트엔드 프로젝트를 생성하고 기본 UI를 작성할 것이다.


프로젝트 github

https://github.com/keumtae-kim/spring-boot-react-blog