Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

김쥬르에 개발일지

Spring Boot 수정 삭제 본문

Spring boot

Spring Boot 수정 삭제

김쥬르 2023. 12. 28. 22:50

이번엔 수정 삭제를 구현해보겠습니다.

 

삭제부터 해보겠습니다.

BlogService.java 파일을 delete() 메서드를 추가합니다.

메서드는 블로그 글의 ID를 받은 뒤 JPA에서 제공하는 deleteById() 메서드를 이용해

데이터베이스에서 데이터를 삭제합니다.

public void delete(long id) {
    blogRepository.deleteById(id);
}

 

/api/articles/{id} DELETE 요청이 오면 글을 삭제하기 위한

findArticles() 메서드를 BlogApiController에 추가하겠습니다.

 

@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Void> deleteArticle(@PathVariable long id) {
    blogService.delete(id);

    return ResponseEntity.ok()
            .build();
}

 

/api/articles/{id} DELETE 요청이 오면 {id}에 해당하는 값이

@PathVariable 어노테이션을 통해 들어옵니다.

 

로직이 모두 완성되었으니 실제로 테스트해보겠습니다. 포스트맨에서

HTTP 메서드 : DELETE

URL : http://localhost:8080/api/articles/1

로 설정한후 send를 누릅니다.

 

이후 전체 글을 조회하여 삭제가 잘됐는지 확인해봅시다.

HTTP 메서드 : GET

URL : http://localhost:8080/api/articles

요청을 보냅시다.

 

 

ID가 1인 글이 삭제된걸 확인되었네요.

 

이번엔 테스트 코드를 작성해보겠습니다.

 

Given : 블로그 글을 저장합니다.

When : 저장한 블로그 글의 id값으로 삭제 API를 호출합니다.

Then : 응답 코드가 200 OK이고 , 블로그 글 리스트를 전체 조회해 조회한 배열 크기가 0인지 확인합니다.

 

@DisplayName("deleteArticle: 블로그 글 삭제에 성공한다.")
@Test
public void deleteArticle() throws Exception {
    // given
    final String url = "/api/articles/{id}";
    final String title = "title";
    final String content = "content";

    Article savedArticle = blogRepository.save(Article.builder()
            .title(title)
            .content(content)
            .build());

    // when
    mockMvc.perform(delete(url, savedArticle.getId()))
            .andExpect(status().isOk());

    // then
    List<Article> articles = blogRepository.findAll();

    assertThat(articles).isEmpty();
}

 

테스트를 실행해봅시다.

 

 

이제 수정 API를 구현해보겠습니다.

 

엔티티에 요청받은 내용으로 값을

수정하는 메서드를 하나 작성하겠습니다

 

Article.java 파일을 열어 다음과 같이 작성합니다.

 

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article {

---------생략----------

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }

 

 그 다음 블로그 글 수정 요청을 받을 DTO를 작성해야합니다.

dto 디렉터리에 UpdateArticleRequest.java 파일을 만든후 코드를 작성하세요.

 

@NoArgsConstructor
@AllArgsConstructor
@Getter
public class UpdateArticleRequest {


    private String title;
    private String content;
}

 

글에서 수정해야 하는 내용은 제목과 내용이므로

그에 맞게 제목과 내용 필드로 구성했습니다.

 

다음 BlogService.java 파일을 열어 리포지터리를

사용해 글을 수정하는 update() 메서드를 추가하겠습니다.

 

@RequiredArgsConstructor // final이 붙거나 @NotNull이 붙은 필드의 생성자 추가
@Service // 빈으로 등록
public class BlogService {

---------생략------------

    @Transactional // 트랜잭션 메서드
    public Article update(long id, UpdateArticleRequest request) {
        Article article = blogRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("not found : " + id));
        
        article.update(request.getTitle(), request.getContent());

        return article;
    }
}

 

@Transactional

매칭한 메서드를 하나의 트랜잭션으로 묶는 역할을 합니다

트랜잭션?

데이터베이스의 데이터를 바꾸기 위해 묶은 작업의 단위

 

이해를 위해 예를 한번 들어보겠습니다.

계좌 이체를 할 때 이런 과정을 거친다고 가정해봅시다.

 

1. A 계좌에서 출금

2. B 계좌에 입금

 

그런데 A 계좌에선 출금을 성공하고 B 계좌에선 입금을 진행하는 도중

실패하면 어떻게 될까요? 고객에 입장에서는 출금은 됐는데 입금이 안 된 심각한 상황이 벌어집니다.

이런 상황이 발생하지 않으려면 출금과 입금을 하나의 작업단위로 묶어서

한 단위로 실행하면됩니다. 만약 중간에 실패한다면 트랜잭션의 처음 상태로 모두 되롤리는것이죠

 

이제 update() 메서드는 엔티티의 필드 값이 바뀌면 중간에

에러가 발생해도 제대로 된 수정을 보장하게 됩니다.

 

위 코드를 해석해보겠습니다.

Article update(long id, UpdateArticleRequest request)

글의 id 와 업데이트 할 내용을 담은

UpdateArticleRequest 를 받아들이고 업데이트된 글이 반환됩니다.

 

Article article = blogRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("not found : " + id));

주어진 id 에 해당하는 글을 blogReposutory 를 통해 찾습니다.

만약 글이 존재하지 않으면 orElseThrow 메서드가 호출되어 IllegalArgumentException

발생하며 메시지로 not found : id 를 오류에 포함합니다.

 

article.update(request.getTitle(), request.getContent());

찾아낸 글에 대해 update 메서드를 호출하여 주어진 request 객체의 제목과 내용을 글의 업데이트합니다.

 

 

BlogApiController.java 파일을 열어 코드를 추가하겠습니다.

 

@RequiredArgsConstructor
@RestController // HTTP Response Body에 객체 데이터를 JSON 형식으로 반환하는 컨트롤러
public class BlogApiController {

    private final BlogService blogService;

---------생략------------

    @PutMapping("/api/articles/{id}")
    public ResponseEntity<Article> updateArticle(@PathVariable long id, @RequestBody UpdateArticleRequest request) {
        Article updatedArticle = blogService.update(id, request);

        return ResponseEntity.ok()
                .body(updatedArticle);
    }
}

 

/api/articles/{id} PUT 요청이 오면 Request Body 정보가 request로 넘어옵니다.

그리고 다시 서비스 클래스의 update() 메서드에 {id}와 request를 넘겨줍니다.

응답값은 body에 담아 전송해줍니다.

 

이제 실행 테스트를 통해 작동이 잘되는지 확인해보겠습니다.

서버를 실행한후 포스트맨에서

HTTP 메서드 : PUT

URL : http://localhost:8080/api/articles/1

수정내용을 입력해야하는데 Body 탭을 누른후

 

{"title" : "새블로그" , "content" : "새로운 글 작성"}

 

작성해준후 send를 눌러 요청을 날려보냅니다.

 

 

수정이 됐는지 GET 요청을 통해

확인해봅시다

수정이 잘되었는지도 확인을 해봤습니다.

 

테스트 코드도 작성해보죠

 

Given : 블로그 글을 저장하고 , 블로그 글 수정에 필요한 요청 객체를 만듭니다.

When : UPDATE API로 수정 요청을 보냅니다. 이때 요청 타입은 JSON이며

GIVEN절에서 미리 만들어둔 객체를 요청 본문으로 함께 보냅니다.

 

@DisplayName("updateArticle: 블로그 글 수정에 성공한다.")
@Test
public void updateArticle() throws Exception {
    // given
    final String url = "/api/articles/{id}";
    final String title = "title";
    final String content = "content";

    Article savedArticle = blogRepository.save(Article.builder()
            .title(title)
            .content(content)
            .build());

    final String newTitle = "new title";
    final String newContent = "new content";

    UpdateArticleRequest request = new UpdateArticleRequest(newTitle, newContent);

    // when
    ResultActions result = mockMvc.perform(put(url, savedArticle.getId())
            .contentType(MediaType.APPLICATION_JSON_VALUE)
            .content(objectMapper.writeValueAsString(request)));

    // then
    result.andExpect(status().isOk());

    Article article = blogRepository.findById(savedArticle.getId()).get();

    assertThat(article.getTitle()).isEqualTo(newTitle);
    assertThat(article.getContent()).isEqualTo(newContent);
}

 

테스트가 잘되는지 확인해봅시다.

 

 

CRUD는 모든 기능의 기반이 되는 기능이므로

앞선 내용들을 숙지하고 이해하는건 기본입니다.

 

다음 포스팅엔 화면 구성을 통해 템플릿 엔진을

알아보고 더 직관적으로 값들에 변화를 봐보도록하겠습니다.

'Spring boot' 카테고리의 다른 글

Spring Boot 각종 뷰  (1) 2023.12.31
Spring Boot 화면구성  (1) 2023.12.29
Spring Boot 블로그 글 조회  (0) 2023.12.28
Spring Boot 블로그 글 추가하기  (1) 2023.12.27
Spring Boot REST API  (2) 2023.12.26