본문 바로가기
프로그래밍/Springboot-토이프로젝트

【게시판-05】글 수정

by 코이킹 2022. 9. 11.
반응형

안녕하세요 코이킹입니다.
이번 포스트는 글 수정 기능을 구현하는 과정에 대한 내용이 되겠습니다. 


1. 목표

 - 스프링 부트를 사용해서 웹 어플리케이션을 만드는 흐름에 대해서 이해할 수 있다.  

 - 스프링 부트를 사용해서 웹 어플리케이션을 만들 때, 코드 복사 붙여 넣기를 통해 빠르게 원하는 기능을 구현할 수 있다.
 - 스프링 부트를 사용해서 기존 데이터를 갱신할 수 있는 웹 어플리케이션을 만들 수 있다. 

 

2. 어떻게 구현할지에 대한 설명 

글 수정 기능의 핵심기능을 생각해보면 '기존의 데이터를 새로운 데이터로 갱신하는 것'입니다. 

글 수정 기능을 구현하기 위해 해야할 일을 추려보면 다음과 같습니다.

스스로 질문해 보기 스스로 추려낸 답변
수정대상 데이터를 유저에게 표시할 것인가? 수정해야할 데이터를 확인한 후 수정을 해야하니 유저에게 수정대상 데이터를 표시해 주어야할 것 같다.
수정대상 데이터를 유저에게 어떻게 표시할것 인가? 기존데이터를 표시하고, 수정도 가능한 페이지를 추가해야겠다. 
글 수정페이지는 어떻게 이동해야할까? 글 상세보기에서 이동링크를 추가하자
특정 데이터를 가져오기 위한 키값을 어떻게 백엔드로 보낼 수 있을까? 요청 파라미터나 경로 파라미터에 특정 값을 담아서 요청하거나, POST요청이라면, 요청 바디에 특정 값을 담아서 넘길 수 있겠다. 
글 수정 페이지에서 입력받은 데이터를 어떻게 백엔드로 보낼 수 있을까? 폼 태그의 액션이나 자바스크립트를 사용해야겠다
글 수정 페이지 표시요청과, 글 수정 처리 요청은 어떤 Http메서드를 사용해야 할까? 글 수정페이지 표시요청은 단순한 데이터 표시이니 GET방식을, 글 수정 처리 요청은 POST방식을 하용하면 되겠다.
글 수정 페이지 표시요청과, 글 수정 처리 요청은 백엔드에서 어떻게 수신해야할까? 컨트롤러에 글 수정 페이지 표시 요청과 글 수정 처리 요청을 각각 매칭하는 메서드를 추가한다. 

위의 해야할 일을 보면, 이전 포스트에서 다루었던 글 작성과 글 상세보기 기능을 구현할 때 추려낸 것과 거의 동일한 내용이 나옵니다.  

글 작성, 글 상세보기에서 구현에서 작성한 코드는 추려낸 답변을 코드로 바꾸었다라고 할 수 있으므로, 해야 할 일이 글 작성, 글 상세보기와 거의 동일한 글 수정하기 기능의 구현은 이미 구현해둔 글 작성과 글 상세보기의 코드를 응용하는 것으로 쉽게 구현이 가능할 겁니다. 

 

3. 소스코드와 해석 

1) 글 상세보기 템플릿 : /template-springboot/src/main/resources/templates/board/read.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, inital-scale=1.0"/>
</head>
<body>

<div>

<article class="blog-post">
    <div class="board-read-header">
    <h1 class="blog-post-title">[[${board.title}]]</h1>

    <hr/>

    <p class="blog-post-meta mt-2">Created at [[${board.createdTime}]] / Last Updated : [[${board.updatedTime}]]</p>

    </div>
    <hr/>
    <div class="mt-4 mb-5 px-2 py-2" style="width: 100%;">
        <div th:text="${board.contents}"></div>
    </div>
</article>
<div>
<button class="btn btn-info" type="button" onclick="location.href='/board/list'">Back to List</button>
<button class="btn btn-warning" type="button" th:onclick="'location.href=\'' + @{/board/update/{boardNo}(boardNo=${board.boardNo})} +'\';'">Update</button>
</div>

</div>
</body>
</html>

-27행 : 글 수정 페이지로 이동하는 링크를 추가했습니다. 

글 상세보기 구현시에 글 목록 페이지에서 글 상세 페이지로 이동하는 링크를 추가한 것을 참조하여 작성했습니다.

 

2) 글 수정 템플릿 : /template-springboot/src/main/resources/templates/board/update.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, inital-scale=1.0"/>
<title>Board Update</title>

</head>
<body>
<div>

    <form method="post" th:action=@{/board/update/}+${board.boardNo}>
    <fieldset>
    	<legend class="text-center header">Update Board</legend>

      <div class="col-md-4">
        <h5><label> Type </label></h5>
	    <select class="form-select" name="type">
	        <option th:each="boardType : ${boardTypes}" th:value="${boardType.value}" th:selected="${boardType.value == board.type}" th:inline="text">[[${boardType.name}]]</option>
	    </select>
	  </div>

    <div class="form-group py-2">
      <div class="col-md-8">
        <h5><label>Title</label></h5>
        <input id="title" name="title" type="text" placeholder="Input Title" class="form-control" th:value="${board.title}">
      </div>
    </div>

    <div class="form-group py-2">
      <div class="col-md-8">
        <h5><label>  Contents </label></h5>
        <textarea name="contents" placeholder="Input Contents" class="form-control" rows="6" th:text="${board.contents}"></textarea>
      </div>
    </div>
    <hr/>

    <div class="form-group py-2">
       <input type="submit" value="Update Complete" class="btn btn-primary">
    </div>
    </fieldset>
    </form>

	<div>
	    <button class="btn btn-info py-2 my-2" type="button" onclick="location.href='/board/list'">Back to List</button>
	    <button class="btn btn-warning py-2 my-2" type="button" th:onclick="'location.href=\'' + @{/board/read/{boardNo}(boardNo=${board.boardNo})} +'\';'">Back to [[${board.boardNo}]]</button>
	</div>
</div>

</body>
</html>

- 12행 : 글 작성 구현에 사용한 코드를 조금 변경하여, 글 수정의 요청을 할 수 있도록 action속성의 값을 수정했습니다.

- 18, 26, 33행 : 템플릿 자체는 글 작성의 것을 거의 그대로 사용하였습니다.

글 수정 페이지를 출력시 기존 데이터가 표시되게 하는 것은, 글 상세보기에서의 데이터 출력을 응용하였습니다.

 

3) 컨트롤러 : /template-springboot/src/main/java/com/sb/template/controller/BoardController.java

package com.sb.template.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.sb.template.entity.Board;
import com.sb.template.enums.BoardType;
import com.sb.template.forms.BoardForm;
import com.sb.template.service.BoardService;

@Controller
@RequestMapping(path = "/board")
public class BoardController {

	@Autowired
	private BoardService boardService;

	@RequestMapping(method = RequestMethod.GET, path = "list")
	public String viewBoardList(Model model) {

		List<Board> boardList = boardService.getAllBoard();
		model.addAttribute("boardList", boardList);

		return "board/list";
	}


	@RequestMapping(method = RequestMethod.GET, path = "write")
	public String writeBoard(Model model) {

		model.addAttribute("boardTypes", BoardType.getBoardTypes());

		return "board/write";
	}


	@RequestMapping(method = RequestMethod.POST, path = "write")
	public String writeCompleteBoard(BoardForm form, Model model) {

		Integer boardNo = null;
		boardNo = boardService.createBoard(form.toEntity()).getBoardNo();

		return "redirect:/board/list";
	}


	@RequestMapping(method = RequestMethod.GET, path = "read/{boardNo}")
	public String viewBoardOne(@PathVariable int boardNo, Model model) {

        model.addAttribute("board", boardService.getBoardOne(boardNo));

		return "board/read";
	}


	@RequestMapping(method = RequestMethod.GET, path = "update/{boardNo}")
	public String updateBoard(@PathVariable int boardNo, Model model) {

		boardService.updateBoardForm(boardNo, model);

		return "board/update";
	}


	@RequestMapping(method = RequestMethod.POST, path = "update/{boardNo}")
	public String updateCompleteBoard(@PathVariable int boardNo, BoardForm form, Model model) {

		boardService.updateBoard(boardNo, form.toEntity());

		model.addAttribute("message", "Update Success");

		return "redirect:/board/read/"+boardNo;
	}


}

- 62행 : 글 상세보기 구현에서처럼 '/{파라미터명}'으로 path의 값을 설정했습니다. 

- 65행 : 글 수정 페이지에 표시할 데이터를 설정하는 처리를 서비스에 위임했습니다. 

- 72행 : 글 작성 구현에서처럼 유저로부터 수정받은 데이터를 폼 클래스로 바인딩하고 있습니다. 

- 74행 : 서비스에 글 수정 처리를 위임합니다. 폼 클래스로 바인딩된 데이터를 바탕으로 엔티티 객체를 생성하여, 서비스의 메서드의 파라미터로 넘겨주고 있습니다. 

- 78행 : 글 상세보기 페이지로 리다이렉트 하여 수정된 데이터를 확인할 수 있도록 하고 있습니다.

 

4) 서비스 : /template-springboot/src/main/java/com/sb/template/service/BoardService.java

package com.sb.template.service;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;

import com.sb.template.entity.Board;
import com.sb.template.enums.BoardType;
import com.sb.template.repo.BoardRepository;

@Service
public class BoardService {

	@Autowired
	private BoardRepository boardRepository;

	public List<Board> getAllBoard() {

		List<Board> res = boardRepository.findAll();
		if (res == null) return null;

		return res;
	}

	public Board createBoard(Board board) {

		return boardRepository.save(board);
	}

	public Board getBoardOne(int boardNo) {

		Optional<Board> board = boardRepository.findById(boardNo);

		if (board.isEmpty()) {
			return null;
		}

		return board.get();
	}

	public void updateBoardForm(int boardNo, Model model) {
		Optional<Board> board = boardRepository.findById(boardNo);

		if (board.isEmpty()) {
			return ;
		}

		model.addAttribute("boardTypes", BoardType.getBoardTypes());
		model.addAttribute("board", board.get());
	}

	@Transactional
	public Board updateBoard(int boardNo, Board updatedBoard) {

		Optional<Board> res = boardRepository.findById(boardNo);

		if (res.isEmpty()) {
			return null;
		}

		Board board = res.get();
		board.setType(updatedBoard.getType());
		board.setTitle(updatedBoard.getTitle());
		board.setContents(updatedBoard.getContents());

		Board endUpdatedBoard = boardRepository.save(board);
		return endUpdatedBoard;
	}
}

- 45~54행 : 게시글 수정 페이지에 표시해야할 데이터를 설정하는 메서드입니다.

 글 번호를 키값으로 게시글 데이터를 가지고 오며, model객체에 게시글 데이터와 글 타입의 리스트 데이터를 넣어줍니다.

- 56~72행 : 게시글 수정을 처리하는 메서드입니다. 

findById메서드를 실행하여 수정하려고하는 게시글 데이터 존재하는지를 확인한 후 존재한다면, 게시글 데이터를 수정합니다. 

4. 동작확인 

5. 데이터 흐름

① 글 상세보기 페이지에서 글 수정 페이지 버튼을 클릭하면, 글 수정 페이지 요청을 백엔드로 보내게 된다. 

 요청을 보낼때 경로 파라미터로 글 번호를 같이 보내, 수정할 글의 데이터를 가져올 키 값으로 사용한다.

② DB에서 수정할 게시글의 데이터를 가져오고, 게시글 데이터 수정 폼에 필요한 게시글 타입 리스트를 요청 스코프에 담아서, 글 수정 템플릿을 브라우저에 리턴한다. 

③ 글 수정이 완료된 후 글 수정처리 버튼을 클릭하면, 글 수정 처리 요청을 백엔드로 보내게 된다.

요청 시에 요청 바디에 수정된 데이터를 넣어서 보내며, 폼 클래스에서 요청 바디에 담긴 수정된 게시글 데이터를 바인딩하여 게시글의 엔티티 객체로 변환한다. 

서비스의 메서드를 호출할 때에는 수정할 게시글 번호와 게시글의 엔티티 객체를 파라미터로 넘기며, 게시글번호를 키로 수정할 데이터가 존재하는지를 먼저 확인한다. 

④ 수정할 데이터가 존재하는 경우 글 수정처리가 실행된다. 

⑤ 수정이 완료된 후 글 상세보기 페이지를 리다이렉트 URL로 설정한 후 요청에 대한 응답을 브라우저로 보낸다. 

⑥ 브라우져에서 글 상세보기 페이지로 리다이렉트 요청을 보낸다. 

⑦ 백엔드에서 글 상세보기 요청에 해당하는 응답을 브라우저로 리턴한다.

 

6. 전체 소스코드 

https://github.com/leeyoungseung/template-springboot/tree/feature/05_basic_board_update

 

GitHub - leeyoungseung/template-springboot

Contribute to leeyoungseung/template-springboot development by creating an account on GitHub.

github.com


글 수정하기 구현은 이것으로 마치겠습니다.

다음 포스트는 글 삭제하기 구현에 대한 내용이 되겠습니다.

반응형

댓글