본문 바로가기
Spring Boot

스프링 부트(Spring Boot) JPA 게시판 - 게시글 등록 페이지 구현하기 (With. MySQL, Thymeleaf)

by 도뎡 2021. 10. 2.
반응형

본 JPA 게시판 프로젝트는 단계별(step by step)로 진행됩니다.


이전 글에서는 타임리프(Thymeleaf) 템플릿 엔진과 부트스트랩(Bootstrap) 프레임워크를 이용해서,

모든 페이지에 사용될 레이아웃(layout.html)과 게시글 리스트 페이지(list.html)를 구현해 보았습니다.

이번 글에서는 게시글 등록 페이지를 구현하게 되는데요.

이전 글에서 레이아웃 작업을 미리 해두었기에 지금까지 보다 훨씬 수월하게 진행하실 수 있을 거예요 :)

그럼, 시작합니다!

 

1. 게시글 등록 HTML 생성하기

templates/board 폴더에 write.html을 생성해 주세요.

templates/board 폴더 구조

 

다음으로 write.html을 다음과 같이 변경해 주세요.

설명이 필요하지 않을 만큼 심플한 코드네요 :)

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">

    <th:block layout:fragment="content">
    <div class="card-content">
		<form class="form-horizontal">
			<div class="form-group">
				<label for="title" class="col-sm-2 control-label">제목</label>
				<div class="col-sm-10">
					<input type="text" id="title" class="form-control" placeholder="제목을 입력해 주세요." />
				</div>
			</div>

			<div class="form-group">
				<label for="writer" class="col-sm-2 control-label">이름</label>
				<div class="col-sm-10">
					<input type="text" id="writer" class="form-control" placeholder="이름을 입력해 주세요." />
				</div>
			</div>

			<div class="form-group">
				<label for="content" class="col-sm-2 control-label">내용</label>
				<div class="col-sm-10">
					<textarea id="content" class="form-control" placeholder="내용을 입력해 주세요."></textarea>
				</div>
			</div>

			<div class="btn_wrap text-center">
				<a th:href="@{/board/list}" class="btn btn-default waves-effect waves-light">뒤로가기</a>
				<button type="button" class="btn btn-primary waves-effect waves-light">저장하기</button>
			</div>
		</form>
	</div>
    </th:block>


    <th:block layout:fragment="script">
    <script th:inline="javascript">
	/*<![CDATA[*/

		

	/*]]>*/
    </script>
    </th:block>

</html>

 

2. 게시글 등록 페이지 매핑(Mapping)하기

당연히, 게시글 등록 페이지(write.html)와 URI를 연결할 메서드가 필요하겠지요?

BoardPageController에 openBoardWrite( )를 추가해 주세요.

package com.study.board.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/board")
public class BoardPageController {

    /**
     * 게시글 리스트 페이지
     */
    @GetMapping("/list")
    public String openBoardList() {
        return "board/list";
    }

    /**
     * 게시글 등록 페이지
     */
    @GetMapping("/write")
    public String openBoardWrite() {
        return "board/write";
    }

}

 

이제, App을 실행하고 게시글 등록 페이지로 접근해보면 다음과 같은 화면을 보실 수 있습니다.

게시글 등록 페이지

 

2. 게시글 등록 API 호출하기

여기서 게시글 등록 API를 구현해 뒀으니, API를 호출해 주기만 하면 됩니다.

BoardApiController의 save( )를 호출하는 JS 함수를 작성해 보도록 할 건데요.

함수 작성에 앞서, write.html의 <form> 태그에 id 속성을 부여해 주도록 하겠습니다.

<form id="form" class="form-horizontal">

 

다음은 데이터의 유효성 검사와 게시글 등록(생성)을 처리하는 함수들입니다.

자세한 내용은 코드를 작성한 뒤에 설명드리도록 할게요 :)

/**
 * 유효성 검사
 */
function isValid() {

  	const form = document.getElementById('form');

	if (!form.title.value.trim()) {
		alert('제목을 입력해 주세요.');
		form.title.value = '';
		form.title.focus();
		return false;
	}

	if (!form.writer.value.trim()) {
		alert('작성자를 입력해 주세요.');
		form.writer.value = '';
		form.writer.focus();
		return false;
	}

	if (!form.content.value.trim()) {
		alert('내용을 입력해 주세요.');
		form.content.value = '';
		form.content.focus();
		return false;
	}

	return true;
}

/**
 * 게시글 등록(생성/수정)
 */
function save() {

	if ( !isValid() ) {
		return false;
	}

	const form = document.getElementById('form');
	const params = {
		title: form.title.value,
		writer: form.writer.value,
		content: form.content.value,
		deleteYn: 'N'
	};

	fetch('/api/boards', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
		},
		body: JSON.stringify(params)

	}).then(response => {
		if (!response.ok) {
			throw new Error('Request failed...');
		}

		alert('저장되었습니다.');
		location.href = '/board/list';

	}).catch(error => {
		alert('오류가 발생하였습니다.');
	});
}

 

isValid( ) 함수

게시판 테이블 구조

 

보시는 것처럼, 게시판 테이블은 수정일(modified_date)을 제외하고 모두 Null을 허용하지 않습니다.

만약의 실수로 제목, 작성자, 내용 중 하나라도 입력되지 않은 경우에는 오류가 발생하겠지요?

게시글 저장 API를 호출하기 전에 isValid( )를 이용해서 게시글 데이터의 유효성을 검사합니다.

 

save( ) 함수

게시글 등록 API를 호출하는 용도의 함수입니다.

Fetch API에 대해서는 이전에 간략히 설명드렸었는데요.

list.html의 findAll( ) 함수처럼, 단순히 데이터를 조회하는 경우에는

fetch( ) 함수에 URI만 인자로 전달해주면 되지만,

GET 방식이 아닌, POST, PUT, PATCH, DELETE 등의 요청(Request)에는

필수적으로 옵션(method, headers, body 등)을 전달해 주어야 합니다.

 

save( ) 함수

 

77~79번 라인

isValid( ) 함수를 이용해서 게시글 데이터의 유효성을 검사하고,

제목, 작성자, 내용 중 하나라도 입력되지 않은 경우에는 로직을 종료합니다.

 

form 변수

폼 안에 있는 제목(title), 작성자(writer), 내용(content)에 접근하기 위한 폼 엘리먼트입니다.

 

params 변수

제목(title), 작성자(writer), 내용(content), 사용 여부(deleteYn) 데이터를 전달하기 위한 객체입니다.

 

89~96번 라인

BoardApiController - save( )

게시글 생성을 처리하는 BoardApiContorller의 첫 번째 save( ) 메서드를 호출하는 함수로,

눈여겨보셔야 할 것은 fetch( )의 두 번째 인자로 전달한 옵션(Options)입니다.


method: 데이터 생성(Create)은 무조건 POST 방식을 이용한다.

headers: API 호출 시, GET 방식이 아닌 요청은 Content-Type을 application/json으로 설정한다.

body: 데이터 전달에 사용되는 옵션으로, params 객체에 담긴 게시글 정보를 API 서버로 전달한다.


 

96~106번 라인

list.html의 findAll( )과 마찬가지로 96번 라인의 then( ) 안의 response는 Promise 객체입니다.

먼저, 게시글 생성에 실패했을 경우에 실행되는 97~99번 라인입니다.

Promise 객체의 ok 상태가 false인 경우, Error를 throw 하며, 104~106번 라인의 catch로 빠지게 됩니다.

JAVA의 try/catch와 동일하다고 생각하셔도 무관할 것 같네요 :)

 

다음은 게시글 생성에 성공했을 경우에 실행되는 101~102번 라인입니다.

list.html의 findAll( )은 응답으로 내려받은 데이터를 이용해서 HTML을 렌더링 하는 작업이 필요했지만,

save( )는 저장 완료 메시지를 보여주고 게시글 리스트 페이지로 이동해주는 정도면 충분할 듯합니다.

 

3. 게시글 등록해보기

게시글 리스트 페이지 하단의 Write 버튼을 클릭하면 아직은 아무런 액션도 일어나지 않을 겁니다.

게시글 리스트 페이지

 

Write 버튼을 클릭하면 글쓰기 페이지로 이동하도록 href 속성을 추가해 주세요.

<a th:href="@{/board/write}" class="btn btn-primary waves-effect waves-light">Write</a>

 

다시 Write 버튼을 클릭해서 글쓰기 페이지로 이동해 주세요.

글쓰기 페이지

 

저장하기 버튼에도 클릭 이벤트를 지정해 주어야겠죠?

버튼을 클릭했을 때 save( ) 함수가 실행되도록 onclick 속성을 추가해 주세요.

<button type="button" onclick="save();" class="btn btn-primary waves-effect waves-light">저장하기</button>

 

이제, 저장하기를 클릭해 볼까요?

보시는 것처럼, 모든 항목이 입력되지 않은 상태에서는 API가 호출되지 않습니다.

유효성 검사에 실패한 경우

 

이번엔 모든 항목을 정상적인 케이스로 입력하고 저장하기를 클릭한 결과입니다.

게시글 등록에 성공한 경우

 

Alert 창의 확인 버튼을 클릭하면, 게시글 리스트 페이지로 이동합니다.

4번 게시글이 정상적으로 생성되었네요.

게시글 리스트 페이지

 

마무리

여기까지, 게시글 등록 페이지 구현이 완료되었는데요.

게시글 수정 기능은 아직 처리되지 않은 상황입니다.

일반적으로 게시글 수정은 다음과 같은 시나리오로 진행됩니다.


1. 게시글 리스트 페이지에서 게시글 제목을 클릭해서 상세 페이지로 이동한다.

2. 게시글 상세 페이지에서 수정 버튼을 클릭해서 게시글 등록(수정) 페이지로 이동한다.

3. 게시글 등록(수정) 페이지에서 제목, 내용을 변경하고 저장하기 버튼을 클릭한다.


하지만, 우리는 아직 게시글 상세 페이지를 구현하지 않은 상황입니다.

다음 글에서 게시글 상세 페이지를 구현하고, 게시글 수정/삭제/조회 수 증가까지

총 세 개의 기능을 추가해 보도록 하겠습니다.

오늘도 방문해 주셔서 감사드리고, 다음 글에서 뵙겠습니다 :)

 


진행에 어려움을 겪으시는 분들이 계실 수 있으니, 프로젝트를 첨부해 드리도록 하겠습니다.

application.properties의 데이터베이스 정보만 내 PC 환경과 일치하도록 변경해서 사용해 주세요 :)


Board.zip
2.14MB

반응형

댓글