Post

(코딩 자율학습 스프링 부트3) 4장


게시글 Update

게시판에 작성한 글을 수정하기 위해서는 해당 글을 불러와 수정할 수 있는 입력 상태로 만들어야 한다 이후 내용을 입력하고 다시 전송하면 DB에 반영돼 최종적으로 수정된다

[수정 단계]

  • <상세 페이지>에서 [Edit] 버튼을 클릭한다
  • 요청을 받은 컨트롤러는 해당 글의 id로 DB에서 데이터를 찾아 가져온다
  • 컨트롤러는 가져온 데이터를 뷰에서 사용할 수 있도록 모델에 등록한다
  • 모델에 등록된 데이터를 <수정 페이지>에서 보여 준다 그러면 사용자가 내용을 수정할 수 있는 상태가 된다
  • 폼 데이터(수정 요청 데이터)를 DTO에 담아 컨트롤러에서 받는다
  • DTO를 엔티티로 변환한다
  • DB에서 기존 데이터를 수정 데이터로 갱신한다
  • 수정 데이터를 <상세 페이지>로 리다이렉트 한다

<상세 페이지>에서 Edit 버튼 생성

  • show.mustache 파일
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
{{>layouts/header}}
{{#article}}
<table class="table">
    <thead>
    <tr>
        <th scope="col">Id</th>
        <th scope="col">Title</th>
        <th scope="col">Content</th>
        <th scope="col">Handle</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th>{{id}}</th>
        <td>{{title}}</td>
        <td>{{content}}</td>
    </tr>
    </tbody>
</table>
<a href="/articles/{{article.id}}/edit" class="btn btn-primary">Edit</a>
<a href="/articles/{{article.id}}/delete" class="btn btn-danger">Delete</a>
<a href="/articles">Go to Article List</a>
{{/article}}
{{>layouts/footer}}

href 속성 값의 URL을 보면 id가 article의 속성이므로 {{article.id}}로 사용했는데 표 보통 article의 사용 범위를 {{#article}}{{/article}} 형식으로 지정한 경우에는 {{id}}만 써도 되지만 범위를 따로 지정하지 않았다면 점(.)을 사용해 {{article.id}}라고 표시해야 한다

<상세 페이지>에서 Edit 버튼을 누를시 <수정 페이지>로 이동할 수 있도록 Controller에서 맵핑 라우팅을 추가한다

  • ArticleController 파일
1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/articles/{id}/edit")
public String edit(@PathVariable Long id, Model model) {
//수정할 데이터 가져오기
Article articleEntity = articleRepository.findById(id).orElse(null); // db에서 수정할 데이터 가져오기
//모델에 데이터 등록하기
model.addAttribute("article", articleEntity);//articleEntity를 article로 등록
// 뷰페이지 설정하기
return "articles/edit";
}

[Tip]

  • public String edit(@PathVariable Long id, Model model) 부분에서 @PathVariable 는 id변수가 URL 주소에 있는 id변수를 받아 오는 것으로 어노테이션을 특정 지정해야한다 Model model은 모델을 사용하기 위해 model 객체를 받아온다
  • addAttribute() 메서드로 모델에 데이터를 등록한다 article이라는 이름으로 앞에서 가져온 articleEntity를 등록한다(이렇게 하면 DB에서 가져온 데이터를 article이라는 이름으로 뷰 페이지에서 사용할 수 있다)

<수정 페이지> 파일 생성

  • edit.mustache 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{{>layouts/header}}
{{#article}}
<form class="container" action="/articles/update" method="post"> <!-- 부트스트랩 css코드 -->
<input name="id" type="hidden" value="{{id}}">
<div class="mb-3">
    <label class="form-label">제목</label>
    <input type="text" class="form-control" name="title" value="{{title}}"> <!-- DTO의 title 필드와 연결 -->
</div>
<div class="mb-3">
    <label class="form-label">내용</label>
    <textarea class="form-control" rows="3" name="content">{{content}}</textarea> <!-- DTO의 content 필드와 연결 -->
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/articles/{{id}}">Back</a>
</form>
{{/article}}
{{>layouts/footer}}

<수정 페이지>에서 Submit 버튼을 누를시 DB에 수정된 값으로 반영하고 리다이렉션을 위한 라우팅 추가

  • ArticleController 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@PostMapping("/articles/update")

public String update(ArticleForm form) {// 매개변수로 DTO 받아 오기
log.info(form.toString()); // DTO 데이터가 잘들어있는지 로그 찍기

Article articleEntity = form.toEntity(); //DTO(form)를 엔티티(articleEntity)로 변환하기
log.info(articleEntity.toString()); //엔티티로 잘 변환됐는지 로그 찍기

//DB에서 기존 데이터 가져오기
Article target = articleRepository.findById(articleEntity.getId()).orElse(null);

//기존 데이터 값을 갱신하기
if (target != null) {
    articleRepository.save(articleEntity); // 엔티티를 DB에 저장(갱신)
}
return "redirect:/articles/" + articleEntity.getId();
}

sequenceDiagram edit(수정페이지)->>+DTO(컨트롤러): 데이터 수정 요청 (HTTP) DTO(컨트롤러)->>+엔티티(리파지터리): DTO를 엔티티로 변환 엔티티(리파지터리)->>+SQL: DB 갱신 (JPA) SQL ->>+edit(수정페이지): 리다이렉트
  • MVC(Model-View-Controller): 서버 역할을 분담해 처리하는 기법
  • JPA(Java Persisitence API): 서버와 DB 간 소통에 관여하는 기술
  • SQL(Structured Query Language): DB 데이터를 관리하는 언어
  • HTTP(HyperText Transfer Protocol): 데이터를 주고받기 위한 통신 규약

게시글 Delete

[삭제 단계]

  • 클라이언트가 http 메서드로 특정 게시글의 삭제를 요청
  • 삭제 요청을 받은 컨트롤러는 리파지터리를 통해 db에 저장된 데이터를 찾아 삭제한다 이 작업은 기존 데이터가 있는 경우에만 수행된다
  • 삭제가 완료되면 클라이언트를 결과 페이지로 리다이렉트한다
sequenceDiagram show(상세페이지)->>+컨트롤러: /articles/{id}/delete 컨트롤러->>+리파지터리: delete(id) 컨트롤러->>+show(상세페이지): redirect:/articles

[Tip] 결과 페이지로 리다이렉트할 때 클라이언트에게 삭제 완료 메시지를 띄워주고 싶다면 사용되는 클래스가 RedirectAttributes 이다 RedirectAttributes 객체의 addFlashAttribute()라는 메서드는 리다이렉트된 페이지에서 사용할 일회성 데이터를 등록할 수 있다

<상세 페이지> 삭제 버튼 추가하기

  • show.mustache 파일
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
{{>layouts/header}}
{{#article}}
<table class="table">
    <thead>
    <tr>
        <th scope="col">Id</th>
        <th scope="col">Title</th>
        <th scope="col">Content</th>
        <th scope="col">Handle</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th>{{id}}</th>
        <td>{{title}}</td>
        <td>{{content}}</td>
    </tr>
    </tbody>
</table>
<a href="/articles/{{article.id}}/edit" class="btn btn-primary">Edit</a>
<a href="/articles/{{article.id}}/delete" class="btn btn-danger">Delete</a>
<a href="/articles">Go to Article List</a>
{{/article}}
{{>layouts/footer}}

컨트롤러에서 삭제 요청에 따른 처리 만들기

  • ArticleController 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    @GetMapping("/articles/{id}/delete") //URL 요청 접수
    public String delete(@PathVariable Long id, RedirectAttributes rttr) { //메서드 생성 및 null 값 반환
        log.info("삭제 요청이 들어왔습니다!!");
        // 1. 삭제할 대상 가져오기
        Article target = articleRepository.findById(id).orElse(null);
        log.info(target.toString());
        // 2. 대상 엔티티 삭제하기
        if (target != null) { //삭제할 대상이 있는지 확인
            articleRepository.delete(target); //delete() 메서드로 대상 삭제
            rttr.addFlashAttribute("msg", "삭제됐습니다!");
        }
        // 3. 결과 페이지로 리다이렉트하기
        return "redirect:/articles";
    }

[Tip]

  • 삭제할 대상 가져오기 DB에 접근해 데이터를 처리할 때는 JPA의 리파지터리를 이용한다
    1. articleRepository.findById(id) 메서드로 DB에 해당 id를 가진 데이터가 있는지 찾아서 만약 찾으면 Article 타입의 target 변수에 저장하고, 찾지 못하면 null을 반환
    2. findById(id)를 호출할 때 사용한 id 변수는 delete() 메서드에 선언되지 않았다 이 id는 @GetMapping("/articles/{id}/delete")의 URL 주소에서 가져오므로 delete() 메서드의 매개변수로 @PathVariable Long id를 써준다
This post is licensed under CC BY 4.0 by the author.