so woon!

게시판 글쓰기 구현 + DB에 인서트 하기 본문

Spring Boot/구현해보기

게시판 글쓰기 구현 + DB에 인서트 하기

xowoony 2022. 11. 17. 14:57

학습일 : 2022. 11. 17


BbsMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dev.xowoony.studymemberbbs.mappers.IBbsMapper">
    <select id="selectBoardById"
            resultType="dev.xowoony.studymemberbbs.entities.bbs.BoardEntity">
        SELECT `id` AS `id`,
        `text` AS `text`
        FROM `study_bbs`.`boards`
        WHERE BINARY `id` = #{id}
        LIMIT 1
        <!--#{id} 에는 notice가 들어가게 된다. notice를 적어줄 경우-->
        <!--    abc 같은게 id에 들어왔다 => 없는 게시판일 경우  BbsService 전체가 null이 된다.-->
    </select>
    <!--게시글(제목, 내용) 작성후 DB에 인서트-->
    <insert id="insertArticle"
            parameterType="dev.xowoony.studymemberbbs.entities.bbs.ArticleEntity">
        INSERT INTO `study_bbs`.`articles` (`user_email`, `board_id`, `title`, `content`, `view`, `written_on`,`modified_on`)
        VALUES (#{userEmail}, #{boardId}, #{title}, #{content}, #{view},
                IFNULL(#{writtenOn}, DEFAULT(`written_on`)),
                IFNULL(#{modifiedOn}, DEFAULT(`modified_on`)));
    </insert>
</mapper>

IBbsMapper.java (Interface)

package dev.xowoony.studymemberbbs.mappers;

import dev.xowoony.studymemberbbs.entities.bbs.ArticleEntity;
import dev.xowoony.studymemberbbs.entities.bbs.BoardEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface IBbsMapper {
    int insertArticle(ArticleEntity article);
    BoardEntity selectBoardById(@Param(value="id") String id); 
}

WriteResult.java

package dev.xowoony.studymemberbbs.enums.bbs;

import dev.xowoony.studymemberbbs.interfaces.IResult;

public enum WriteResult implements IResult {
    NOT_ALLOWED,
    NO_SUCH_BOARD
}

BbsController.java

package dev.xowoony.studymemberbbs.controllers;

import dev.xowoony.studymemberbbs.entities.bbs.ArticleEntity;
import dev.xowoony.studymemberbbs.entities.bbs.BoardEntity;
import dev.xowoony.studymemberbbs.entities.member.UserEntity;
import dev.xowoony.studymemberbbs.enums.CommonResult;
import dev.xowoony.studymemberbbs.enums.bbs.WriteResult;
import dev.xowoony.studymemberbbs.services.BbsService;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

@Controller(value = "dev.xowoony.studymemberbbs.controllers.BbsController")
@RequestMapping(value = "/bbs")
public class BbsController {
    public final BbsService bbsService;

    // 글쓰기 페이지
    @Autowired
    public BbsController(BbsService bbsService) {
        this.bbsService = bbsService;
    }
    @RequestMapping(value = "write",
            method = RequestMethod.GET,
            produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView getWrite(@SessionAttribute(value = "user", required = false) UserEntity user,
                                 @RequestParam(value = "bid", required = false) String bid) {
        // required = false => 400 방지
        ModelAndView modelAndView;
        if (user == null) {         // 로그인 확인 (로그인이 안되었을 경우 다시 로그인 페이지로 돌려줌
            modelAndView = new ModelAndView("redirect:/member/login"); // ModelAndView 로 redirect 할 수 있다.
        } else {                    // 로그인이 되었을 경우 bbs/write 페이지로 데려다 줌
            modelAndView = new ModelAndView("bbs/write");
            // BoardEntity board = bid == null ? null : this.bbsService.getBoard(bid); // 참이면 null 반환, 거짓이면 getBoard(bid) 반환
            if (bid == null || this.bbsService.getBoard(bid) == null) {
                modelAndView.addObject("result", CommonResult.FAILURE.name());
            } else {
                modelAndView.addObject("result", CommonResult.SUCCESS.name());
                modelAndView.addObject("board", this.bbsService.getBoard(bid));
            }
        }
        return modelAndView;
    }
    // DB에 게시글 내용 insert 하기 (1116)
    @RequestMapping(value = "write",
            method = RequestMethod.POST,    // POST 방식으로 맵핑
            produces = MediaType.APPLICATION_JSON_VALUE) // JSON으로 돌려돌려준다.
    @ResponseBody
    public String postWrite(@SessionAttribute(value = "user", required = false) UserEntity user,
                            @RequestParam(value = "bid", required = false) String bid,
                                    ArticleEntity article) {
        Enum<?> result;
        if (user == null) {
            result = WriteResult.NOT_ALLOWED;
        } else if (bid == null) {
            result = WriteResult.NO_SUCH_BOARD;
        } else {
            article.setUserEmail(user.getEmail());
            article.setBoardId(bid);
            result = this.bbsService.write(article);
        }
        JSONObject responseObject = new JSONObject();
        responseObject.put("result", result.name().toLowerCase());
        return responseObject.toString();
    }
}

BbsService.java

package dev.xowoony.studymemberbbs.services;

import dev.xowoony.studymemberbbs.entities.bbs.ArticleEntity;
import dev.xowoony.studymemberbbs.entities.bbs.BoardEntity;
import dev.xowoony.studymemberbbs.enums.CommonResult;
import dev.xowoony.studymemberbbs.interfaces.IResult;
import dev.xowoony.studymemberbbs.mappers.IBbsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value = "dev.xowoony.studymemberbbs.services.BbsService")
public class BbsService {
    private final IBbsMapper bbsMapper;
    @Autowired
    public BbsService(IBbsMapper bbsMapper) {
        this.bbsMapper = bbsMapper;
    }
    public BoardEntity getBoard(String id) {
        return this.bbsMapper.selectBoardById(id);
    }
    public Enum<? extends IResult> write(ArticleEntity article) {
        BoardEntity board = this.bbsMapper.selectBoardById(article.getBoardId());
        if (board == null) {
            return CommonResult.NO_SUCH_BOARD;
        }
        return this.bbsMapper.insertArticle(article) > 0
                ? CommonResult.SUCCESS
                : CommonResult.FAILURE;
    }
}

write.html

<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title th:if="${board != null}" th:text="'또치뱅크 - ' + ${board.getText()} + ' 글쓰기'"></title>
    <!--<title th:text="${'스터디 -' + board.getText() + '글쓰기'}"></title>-->
    <script th:if="${board == null}">
        alert('존재하지 않는 게시판입니다.');
        if (window.history.length > 1) {    // 뒤로가기 할 수 있으면 뒤로 가고
            window.history.back();      // 없다면 창을 닫는다.
        } else {
            window.close();
        }
    </script>
    <th:block th:replace="~{fragments/head :: common}"></th:block>
    <link rel="stylesheet" th:href="@{/bbs/resources/stylesheets/write.css}">
    <script defer th:src="@{/resources/libraries/ckeditor/ckeditor.js}"></script>
    <script defer th:src="@{/bbs/resources/scripts/write.js}"></script>
</head>
<body th:if="${board != null}">
<!--board가 null일때 body가 안뜬다.-->
<th:block th:replace="~{fragments/body :: header}"></th:block>
<th:block th:replace="~{fragments/body :: cover}"></th:block>
<main class="--main main">
    <form id="form" class="--content content">
        <h1 class="title" th:if="${board!=null}" th:text="'글쓰기 - ' + ${board.getText()}"></h1>
        <!--board.getText()가 null 일때 500터짐-->
        <input name="bid" type="hidden" th:value="${ board.getId() }">
        <table class="table" id="table">
            <tbody>
            <tr>
                <th>제목</th>
                <td>
                    <label class="label">
                        <span hidden>제목</span>
                        <input class="--object-input input" maxlength="100" name="title" placeholder="제목을 입력해 주세요." type="text">
                    </label>
                </td>
            </tr>
            <tr>
                <th>내용</th>
                <td>
                    <label class="label">
                        <span hidden>내용</span>
                        <textarea name="content" class="--object-input input" placeholder="내용을 입력해 주세요." maxlength="10000"></textarea>
                    </label>
                </td>
            </tr>
            <tr class="warning-row" rel="warningRow">
                <th></th>
                <td>
                    <span class="warning">
                        <i class="icon fa-solid fa-triangle-exclamation"></i>
                        <span class="text">제목을 입력해주세요</span>
                    </span>
                </td>
            </tr>
            <tr>
                <th></th>
                <td>
                    <div class="button-container">
                        <input class="--object-button" name="back" type="button" value="돌아가기">
                        <input class="--object-button" name="submit" type="submit" value="작성하기">
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
    </form>
</main>
<th:block th:replace="~{fragments/body :: footer}"></th:block>
</body>
</html>

write.css

@charset "UTF-8";

body > .main > .content {
    margin: 5rem 0;

    align-items: stretch;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

body > .main > .content > .title {
    font-size: 2rem;
    font-weight: 500;
    margin-bottom: 2.5rem;
}

/*경고창*/
#form > .table .warning-row {
    display: none;
}

#form > .table .warning-row.visible {
    display: table-row;
}

#form > .table .warning-row .warning {
    background-color: rgb(231, 76, 60);
    border-radius: 0.375rem;
    color: rgb(255, 255, 255);
    font-size: 1rem;
    margin-top: 0.25rem;
    padding: 1rem 1.25rem;

    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#form > .table .warning-row .warning > .icon {
    font-size: 1.5rem;
    margin: 0 1.25rem 0 0.25rem;
}

#form > .table .warning-row .warning > .text {
    flex: 1;
    text-align: justify;
}

#form > .table .button-container {
    align-items: stretch;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    margin-top: 0.5rem;
}

#form > .table .button-container > [name="back"] {
    background-color: rgb(16, 68, 108);
}
#form > .table .button-container > [name="back"]:hover{
    background-color: rgb(130, 170, 204);
}
#form > .table .button-container > [name="back"]:active{
    background-color: rgb(44, 62, 80);
}

write.js

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

const Warning = {
    getElementById: () => form.querySelector('[rel="warningRow"]'),
    show: (text) => {
        const warningRow = Warning.getElementById();
        warningRow.querySelector('.text').innerText = text;
        warningRow.classList.add('visible');
    },
    hide: () => Warning.getElementById().classList.remove('visible')
};

let editor; //체인메서드
ClassicEditor
    .create(form['content'])
    .then( e => editor = e );       // e = 에디터 전체   / 얘가 뱉어낸 e를

form['back'].addEventListener('click', () => window.history.length < 2 ? window.close() : window.history.back());

form.onsubmit = e => {
    e.preventDefault();
    Warning.hide();
    if (form['title'].value === '') {
        Warning.show('제목을 입력해 주세요.');
        form['title'].focus();
        return false;
    }
    if (editor.getData() === '') {
        Warning.show('내용을 입력해 주세요.');
        editor.focus();
        return false;
    }
    <!--게시글(제목, 내용) 작성후 DB에 인서트-->
    Cover.show('게시글을 작성하고 있어요. 잠시만 기다려주세여.')
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('bid', form['bid'].value);
    // formData.append('POST', window.location.href);해도됨
    formData.append('title', form['title'].value);
    formData.append('content', editor.getData());

    xhr.open('POST', './write')
    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            Cover.hide();
            if (xhr.status >= 200 && xhr.status < 300) {
                const responseObject = JSON.parse(xhr.responseText);
                switch (responseObject['result']) {
                    case 'not_allowed':
                        Warning.show('게시글 작성권한이 없거나 로그아웃되었ㅅㅂ니다,, 확인후 다시 시도 ㄱ');
                        break;
                    case 'success':
                       //?
                        break;
                    default:
                        Warning.show('알 수 없는 이유로 게시글 작성 못했어요, 다시 시도하세여');
                }
            } else {
                Warning.show('서버와 통신하지 못하였습니다. 잠시 후 다시 시도해 주세요.');
            }
        }
    };
    xhr.send(formData);
};

insert 과정

 

 

아래와 같이 `study_bbs`.`articles` 테이블에 insert 된다.


그리고 오늘의 과제ㅆ

SUCCESS 일 때

/bbs/read?aid=인덱스 

로 이동하도록 조치하기

 

 

투 비 컨티뉴.

'Spring Boot > 구현해보기' 카테고리의 다른 글

게시글에 댓글 달기 구현  (5) 2022.11.27
게시글 읽기 + DB에 인서트 하기  (0) 2022.11.21
게시판 글쓰기 구현  (0) 2022.11.14
이메일 찾기 구현  (0) 2022.11.13
비밀번호 재설정 구현2  (0) 2022.11.13
Comments