본문 바로가기
개발

JPA CRUD(2)

by 효방 2022. 12. 31.

이전글에서는 조회 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