이전글에서는 조회 API를 만들어보았으니 이제 남은 건 상세조회, 등록, 수정, 삭제 이렇게 3개가 있다.
이것들 또한 JpaRepository에서 제공해주는 메서드를 이용해 기능을 만들어보자.
글을 등록 또는 수정하는 과정은 요청자에게 받아야 할 데이터가 있으므로 DTO를 추가로 생성해준다.
RegistPostsDto
@Getter
@Setter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class RegistPostsDto {
@NotBlank(message = "'author' is a required input value")
private String author;
@NotBlank(message = "'title' is a required input value")
private String title;
@NotBlank(message = "'content' is a required input value")
private String content;
public Posts toEntity() {
Posts build = Posts.builder()
.author(author)
.title(title)
.content(content)
.build();
return build;
}
}
ModifyPostsDto
@Getter
@Setter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class ModifyPostsDto {
@Min(1)
private Long id;
@NotBlank(message = "'author' is a required input value")
private String author;
@NotBlank(message = "'title' is a required input value")
private String title;
@NotBlank(message = "'content' is a required input value")
private String content;
public Posts toEntity() {
Posts build = Posts.builder()
.id(id)
.author(author)
.title(title)
.content(content)
.build();
return build;
}
}
그리고 컨트롤러와 서비스에 기능을 추가한다.
PostsService
@Service("postsService")
@AllArgsConstructor
public class PostsService {
private PostsRepository postsRepository;
private CustomModelMapper customModelMapper
// 글 목록 조회
public List<PostsResDto> getPostsService() {
List<Posts> entityList = postsRepository.findAll();
return entityList.stream()
.map(entity -> customModelMapper.toDto(entity, PostsResDto.class))
.collect(Collectors.toList());
}
// 글 등록
public Long regPostsService(RegistPostsDto regPosts) {
Long insertId = postsRepository.save(regPosts.toEntity()).getId();
return insertId;
}
// 글 상세 조회
public PostsResDto getPostsByIdService(Long id) {
Posts entity = postsRepository.findById(id)
.orElseThrow(() -> new ApiOtherException("Posts Result Not Found"));
return customModelMapper.toDto(entity, PostsResDto.class);
}
// 글 수정
public void setPostsService(ModifyPostsDto setPosts) {
postsRepository.save(setPosts.toEntity());
}
// 글 삭제
public void delPostsService(Long id) {
postsRepository.deleteById(id);
}
}
위 서비스에서 사용된 JpaRepository의 메서드를 살펴보자.
save() : 등록과 수정 두 작업에 사용된다. 아이디가 없다면 Transient 상태를 인지하고, persist() 메소드를 사용한다.
id가 있다면 Detached 상태를 인지하고, merge() 메소드를 사용한다.
delete() : 삭제 메소드이며 나는 ById(이것또한 제공됨)가 붙어있는데 id에 해당되는 레코드 삭제를 실행한다.
PostsController
@RestController
@RequestMapping(value = {"/posts"}, produces = MediaType.APPLICATION_JSON_VALUE)
@AllArgsConstructor
@CrossOrigin(origins = "*")
public class PostsController {
private PostsService postsService;
/**
* @method 설명 : 게시글 목록조회
* @param regPosts
* @throws Exception
*/
@GetMapping(value = {""})
public ResponseEntity<List<PostsResDto>> getPosts() {
return ResponseEntity.ok()
.body(postsService.getPostsService());
}
/**
* @method 설명 : 게시글 등록
* @param regPosts
* @throws Exception
*/
@PostMapping(value = {""})
public ResponseEntity<Long> regPosts(@Valid @RequestBody RegistPostsDto regPosts) throws Exception {
return ResponseEntity.ok()
.body(postsService.regPostsService(regPosts));
}
/**
* @method 설명 : 게시글 상세조회
* @param id
* @return
*/
@GetMapping(value = {"/{id}"})
public ResponseEntity<PostsResDto> getPostsDetail(@PathVariable Long id) {
return ResponseEntity.ok()
.body(postsService.getPostsByIdService(id));
}
/**
* @method 설명 : 게시글 수정
* @param id
* @param setPosts
* @throws Exception
*/
@PutMapping(value = {"/{id}"})
public ResponseEntity<String> setPosts(
@PathVariable Long id,
@Valid @RequestBody PostsSetDto setPosts) throws Exception {
postsService.setPostsService(setPosts);
return ResponseEntity.ok()
.body("UPDATE SUCCESS");
}
/**
* @method 설명 : 게시글 삭제
* @param id
* @throws Exception
*/
@DeleteMapping(value = {"/{id}"})
public ResponseEntity<String> delPosts(
@PathVariable Long id) throws Exception {
postsService.delPostsService(id);
return ResponseEntity.ok()
.body("DELETE SUCCESS");
}
}
CRUD의 기능이 완성된 서비스와 그에대한 요청을 받는 컨트롤러의 모습이다.
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@AutoConfigureMockMvc
class SpringJpaApplicationTests {
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private WebApplicationContext context;
@BeforeAll
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(new CharacterEncodingFilter("UTF-8", true))
.alwaysDo(print())
.build();
}
@Test
void contextLoads_게시판_글_목록조회() throws Exception{
mockMvc.perform(get("/posts"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
void contextLoads_게시판_글_상세조회() throws Exception {
mockMvc.perform(get("/posts/1"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
void contextLoads_게시판_글_등록() throws Exception {
PostsRegDto posts = new PostsRegDto("작성자", "테스트 제목", "테스트 본문");
String content = objectMapper.writeValueAsString(posts);
mockMvc.perform(post("/posts")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
@Test
void contextLoads_게시판_글_수정() throws Exception {
PostsSetDto posts = new PostsSetDto((long)1, "수정자", "제목 수정 테스트", "본문 수정 테스트");
String content = objectMapper.writeValueAsString(posts);
mockMvc.perform(put("/posts/1")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
}
@Test
void contextLoads_게시판_글_삭제() throws Exception {
mockMvc.perform(delete("/posts/1"))
.andDo(print())
.andExpect(status().isOk());
}
}
하나씩 차례차례 실행해보며 테스트를 진행해보면서 정상적으로 잘 동작하는지 확인해보면 끝!
'개발' 카테고리의 다른 글
Vue 게시판 (1) (0) | 2023.01.01 |
---|---|
박싱과 언박싱 (0) | 2023.01.01 |
JPA CRUD(1) (0) | 2022.12.31 |
객체지향주의 (0) | 2022.12.31 |
JWT(JSON Web Token) (0) | 2022.12.31 |