안녕하세요 코이킹입니다.
이번 포스트에서는 글 작성 기능을 구현하는 과정에 대한 내용이 되겠습니다.
1. 목표
- 스프링 부트를 사용해서 웹 어플리케이션을 만드는 흐름에 대해서 이해할 수 있다.
- 스프링 부트를 사용해서 폼 데이터를 수신하여 DB에 저장하는 웹 어플리케이션을 만들 수 있다.
2. 어떻게 구현할지에 대한 설명
글 작성 기능의 핵심을 정의해보면 '유저로 부터 입력받은 데이터를 DB에 저장' 하는 것이 되겠습니다.
'글 작성 기능의 핵심정의'(간략/추상화된 사양)를 바탕으로 해야 할 일(상세 사양)을 추려내는 것이 개발의 시작이라고 생각합니다.
저의 경우 일단 생각나는대로 내 자신에게 질문을하는 것에서 부터 시작합니다. 질문에 대한 답변도 스스로 도출합니다.
답변은 가능한한 여러개를 근거를 들어서 도출하는게 좋습니다.
스스로 질문해보기 | 스스로 추려낸 답변 |
유저로 부터 어떻게 데이터를 입력받을 수 있을까? | 데이터를 입력받을 페이지나 모달이 필요하다. |
글 작성 페이지는 어디에서 이동이 가능할까? | 글 목록 페이지에서 데이터 입력창으로 이동하는 링크가 있어야겠다. |
글 작성 페이지에서 입력받은 데이터를 어떻게 백엔드로 보낼 수 있을 까? | 폼 태그의 액션이나 자바스크립트를 사용해야겠다 |
유저로 부터의 글 작성 페이지 표시 요청과, 글 작성 처리 요청을 어떻게 수신해야 할까? | 컨트롤러에 글 작성 페이지 표시 요청과 글 작성 처리 요청을 각각 매칭하는 메서드를 추가한다. |
글 작성 페이지 표시요청과, 글 작성 처리 요청은 어떤 Http메서드를 사용해야 할까? | 페이지 표시 요청은 단순 페이지 표시 이므로 GET 요청, 글 작성 처리 요청은 민감한 정보가 있을 수 있으니 POST 요청 |
여담으로 저의 경우 실무에서 질문과 답변 작업이 끝나면 간략화된 사양을 언급한 관계자나 상급자에게 리뷰를 받아 사양을 확정합니다.
위에서 말한 '글 작성 기능의 핵심정의'와 같은 요구사항이 문서나 회의 중에 몇 개씩 정해지는데,
저 연차의 개발자는 위의 해야 할 일(상세 사양)을 그대로 구현하는 데 일단 집중하며, 경력이 쌓여 여유가 생긴다면 사양이 정의되는 회의 등의 커뮤니케이션의 장에서 사양의 부족한 점이 있거나 추가되면 좋을 사양을 타당한 근거를 가지고 제안하면 더 많은 성과를 낼 수 있을 거라고 생각합니다.
설명은 여기서 마치고 지난 포스트에서 설명 드린대로 화면부터 구현한 후 백엔드 부분을 구현하도록 하겠습니다.
3. 소스코드와 해석
1) 글목록 템플릿 : /template-springboot/src/main/resources/templates/board/list.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>
<div class="nav justify-content-end px-5 pt-2">
<button class="btn btn-info text-white" id="write-board" type="button" onclick="location.href='/board/write'">
Contents Write
</button>
</div>
<hr/>
<div>
<table class="table" border="1">
<thead class="table-dark">
<tr>
<th>No</th>
<th>Contents Type</th>
<th>Title</th>
<th>CreatedTime</th>
<th>UpdatedTime</th>
<th>Likes</th>
</tr>
</thead>
<tbody>
<tr th:each="board: ${boardList}">
<td th:text="${board.boardNo}"></td>
<td th:text="${board.type}==1 ? 'Normal' : 'MemberShip'"></td>
<td th:text="'[' + ${board.title} + ']'"></td>
<td th:text="${board.createdTime}"></td>
<td th:text="${board.updatedTime}"></td>
<td th:text="${board.likes}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
- 10~14행 : 글 작성 페이지로 이동할 수 있는 버튼을 추가했습니다.
2) 글 작성 페이지 : /template-springboot/src/main/resources/templates/board/write.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 Write</title>
</head>
<body>
<div>
<form method="post" th:action=@{/board/write} class="form-horizontal">
<fieldset>
<legend class="text-center header">Write Board</legend>
<input th:if="${not #strings.isEmpty(session?.member?.memberId)}" type="hidden" name="memberId" th:value="${session.member.memberId}" />
<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: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">
</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"></textarea>
</div>
</div>
<hr/>
<div class="form-group py-2">
<input type="submit" value="Write Complete" class="btn btn-primary">
</div>
</fieldset>
</form>
<div>
<button class="btn btn-info" type="button" onclick="location.href='/board/list'">Back to List</button>
</div>
</div>
</body>
</html>
- 11행 : 폼 태그를 통해서 데이터를 백엔드로 전송할 때의, Http 메서드를 post로 설정, action에는 전송 시의 url정보를 설정.
- 15행 : 세션스코프에 들어있는 유저 정보를 가져오는 코드. 회원 기능을 구현 시에 다시 설명 예정.
- 19~21행 : 셀렉트 태그의 옵션 값을 boardTypes리스트의 데이터 수만큼 반복해서 추가한다.
- 19, 28, 35행 : name속성의 값을 Form데이터를 담을 클래스의 필드명과 일치시켜야 한다. 그래야 정상적으로 데이터를 백엔드로 보낼 수 있다.
3) 글의 타입을 정의한 enum : /template-springboot/src/main/java/com/sb/template/enums/BoardType.java
package com.sb.template.enums;
import java.util.ArrayList;
import java.util.List;
public enum BoardType {
NORMAL(1, "Normal"), MEMBERSHIP(2, "MemberShip");
public Integer value;
public String name;
private static List<BoardType> typeList = null;
BoardType(Integer value, String name) {
this.value = value;
this.name = name;
}
public static List<BoardType> getBoardTypes() {
if (typeList == null) {
typeList = new ArrayList<>();
typeList.add(NORMAL);
typeList.add(MEMBERSHIP);
}
return typeList;
}
}
- 8행 : 게시글의 타입을 상수로 정의해 둔다. 변하지 않는 값이 필요하다면 상수로 정의해 두어야 프로그램의 오작동을 방지할 수 있음.
4) 글 작성 처리 요청 시 폼 데이터가 들어갈 클래스 : /template-springboot/src/main/java/com/sb/template/forms/BoardForm.java
package com.sb.template.forms;
import com.sb.template.entity.Board;
import lombok.Data;
@Data
public class BoardForm {
private Integer no;
private Integer type;
private String title;
private String contents;
private String memberId;
public Board toEntity() {
Board board = new Board();
board.setType(this.type);
board.setTitle(this.title);
board.setContents(this.contents);
board.setMemberId(((this.memberId == null || (this.memberId.equals(""))) ? "" : this.memberId));
return board;
}
}
- 10~14행 : 글 작성 페이지의 input 태그의 name속성과 값이 일치하면, 스프링 부트가 자동으로 데이터를 바인딩해준다.
- 16~25행 : 유저로부터 입력받은 데이터를 사용하여 엔티티 객체를 생성하는 메서드
5) 컨트롤러 : /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.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";
}
}
- 33~39행 : 글 작성 페이지 템플릿을 유저에게 표시해 주는 메서드. 게시글 타입 리스트 데이터를 리퀘스트 스코프에 넣어서 템플릿에 표시한다.
- 42~49행 : 글 작성 처리 요청을 수신하는 메서드, 글 작성 처리 자체는 서비스에 위임한다. 리턴 값으로 boardNo변수에 값을 넣는 이유는 글 상세보기 구현 시에 사용하기 위해서이다.
6) 서비스 : /template-springboot/src/main/java/com/sb/template/service/BoardService.java
package com.sb.template.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sb.template.entity.Board;
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);
}
}
- 25~28행 : DB에 유저로부터 입력받은 게시글 데이터를 삽입한다.
4. 동작확인
5. 데이터 흐름
① 글 목록 페이지에서 글 작성 버튼을 클릭하여, 백엔드의 컨트롤러에 글 작성 페이지를 요청한다.
② 컨트롤러는 글 작성 페이지 템플릿을 브라우저에 리턴한다.
③ 글 작성 페이지에서 유저로부터 입력받은 폼 데이터를 컨트롤러에서 수신하여, 서비스 컴포넌트에 글 작성 처리를 위임한다. 컨트롤러에서 서비스로 처리 위임시에 폼 데이터가 담긴 폼 클래스에서 엔티티의 형태로 데이터를 성형하여 서비스 메서드의 파라미터로 넘긴다.
서비스에서는 게시글 데이터를 DB에 저장하는 처리를 리포지토리를 통해 수행한다.
④ DB에 신규 게시글 데이터의 저장이 확인되면, 컨트롤러에서는 리다이렉트 방식으로 갱신된 글목록 페이지를 표시하는 것으로 글작성 처리를 마친다.
※ 리다이렉트는 브라우저에서 리다이렉트로 지정된 경로로 다시 요청하는 방식으로 페이지를 새롭게 띄우는 것을 말함.
⑤ 리다이렉트로 게시글 목록 페이지 요청 발생하고, 컨트롤러는 이를 수신하여 게시글 목록 요청에 대한 일련의 처리를 실시한다.
⑥ 일련의 처리가 끝난 후 컨트롤러는 갱신된 글목록 페이지 템플릿을 브라우저로 리턴한다.
6. 전체 소스코드
https://github.com/leeyoungseung/template-springboot/tree/feature/03_basic_board_create
글 작성 구현은 이것으로 마치겠습니다.
다음 포스트는 글 상세보기 구현에 대한 내용이 되겠습니다.
'프로그래밍 > Springboot-토이프로젝트' 카테고리의 다른 글
【게시판-05】글 수정 (0) | 2022.09.11 |
---|---|
【게시판-04】글상세보기 (0) | 2022.09.09 |
【게시판-02】글목록 (0) | 2022.09.07 |
【게시판-번외02】설계 (0) | 2022.07.09 |
【게시판-번외01】요건정의 (0) | 2022.07.08 |
댓글