PROJECT/게시판 프로젝트
게시판프로젝트[KyuBoard_ver_2.0]
regularity
2022. 2. 20. 16:14
728x90
게시판프로젝트 제작영상
게시판프로젝트 파일 GitHub QR코드
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
[에러 추가 내용]
23.
24.
홈 화면
게시글 목록
게시글 조회
게시글 작성
표지 이미지
홈 HTML 코드
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<title>KyuBoard</title>
</head>
<script>
function loginCheck() {
if (document.loginForm.userId.value == '') {
alert('아이디를 입력하세요!!!');
document.loginForm.userId.focus(); //로그인 폼의 id 이름을 가진 곳에 포커스를 맞춘다
return false;
}
var Passwd = document.getElementById('userPass').value;
if (Passwd == '') {
alert('비밀번호를 입력하세요!!!');
document.loginForm.userPass.focus();
return false;
}
document.loginForm.action = '/member/login';
document.loginForm.submit();
}
function press() {
if (event.keyCode == 13) {
loginCheck();
}
}
</script>
<Style>
/*@import url('http://fonts.googleapis.com/earlyaccess/nanumgothic.css');*/
@import
url('https://cdn.pixabay.com/photo/2017/08/01/01/17/beach-2562563_960_720.jpg')
;
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 100;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Thin.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Thin.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Thin.otf)
format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 300;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Light.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Light.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Light.otf)
format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 400;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Regular.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Regular.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Regular.otf)
format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 500;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Medium.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Medium.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Medium.otf)
format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 700;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Bold.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Bold.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Bold.otf)
format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 900;
src: url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Black.woff2)
format('woff2'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Black.woff)
format('woff'),
url(//fonts.gstatic.com/ea/notosanskr/v2/NotoSansKR-Black.otf)
format('opentype');
}
body {
margin: 0 auto;
/* body, table, div, p, span{font-family:'Nanum Gothic';}*/
body
,
table,
div,
p,
span{font-family
:
'Noto Sans KR';
}
a {
text-decoration: none;
color: #333;
}
}
#con {
width: 40%;
height: 100vh;
background-color: #4B4453;
background-image:
url('https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKyJ08%2FbtruokJIFda%2F8yF55aKLZid8roQuaHuHx1%2Fimg.png');
/*url('https://cdn.pixabay.com/photo/2022/01/31/15/18/coffee-6984075_960_720.jpg'); */
background-position: left left; 1
background-repeat: no-repeat;
background-size: cover;
margin: 0px 0px;
padding: 0;
}
#login {
border-radius: 60px;
padding: 100px;
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.9;
}
#login_form {
width: 150%;
border-radius: 10px;
padding: 30px;
background: #fff;
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#id {
text-align: center;
font-weight: 100px;
font-size: 20px;
color: #666
}
#pass {
text-align: center;
font-weight: 100px;
font-size: 20px;
color: #666
}
#userId {
width: 300px;
height: 30px;
padding-left: 10px;
background-color: #f4f4f4;
border: none;
border-radius: 1px;
font-style: italic;
}
#userPass {
width: 300px;
height: 30px;
padding-left: 10px;
background-color: #f4f4f4;
border: none;
border-radius: 1px;
font-style: italic;
}
.size {
width: 300px;
height: 30px;
padding-left: 10px;
background-color: #fff;
}
.btn {
width: 310px;
height: 40px;
font-size: 15px;
background-color: #0073C2;
color: #fff;
border: none;
cursor: pointer;
font-family: Noto Sans KR;
}
.join {
width: 610px;
height: 40px;
font-size: 15px;
background-color: none;
border: none;
cursor: pointer;
font-family: Noto Sans KR;
}
</Style>
<body>
<div id="con">
<div id="login">
<div id="login_form">
<h1>Sign into KyuBoard</h1>
<!--<hr>
<P>The time on the server is ${serverTime}.</P>
<p>
<a href="board/list">게시물 목록</a>
</p>
<p>
<a href="board/write">게시물 작성</a>
</p> -->
<!-- 내용 추가 (로그인) -->
<c:if test="${member == null}">
<form name="loginForm" method="post" class="loginForm"
onsubmit="loginCheck()">
<!-- 원본 <form role="form" method="post" autocomplete="off" action="/member/login"> -->
<!-- 아이디 -->
<p style="text-align: left; font-size: 15px; color: #666">
<label for="userId" id="id">User ID</label>
</p>
<input type="text" placeholder="아이디 입력" id="userId" name="userId" />
<!-- 비밀번호 -->
<p style="text-align: left; font-size: 15px; color: #666">
<label for="userPass" id="pass">Password</label>
</p>
<input type="password" placeholder="비밀번호 입력" id="userPass"
name="userPass" />
<p>
<button type="submit" class="btn">로그인</button>
</p>
<hr>
<p>
<a href="/member/register" class="join"> 회원가입</a>
</p>
</form>
</c:if>
<c:if test="${msg == false}">
<p style="color: #f00;">
로그인에 실패했습니다. <br>아이디 또는 패스워드를 다시 <br>입력해주십시오.
</p>
</c:if>
<c:if test="${member != null}">
<p>${member.userName}님!환영합니다!</p>
<p>
<a href="/board/listPageSearch?num=1">게시물 목록</a>
</p>
<p>
<a href="board/write">게시물 작성</a>
</p>
<a href="main/">메인페이지 이동</a>
<a href="member/modify">회원정보 수정</a>, <a href="member/withdrawal">회원탈퇴</a>
<br />
<a href="member/logout">로그아웃</a>
</c:if>
</div>
</div>
</div>
</body>
</html>
Controller 코드
package com.kyuboard.controller;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 org.springframework.web.bind.annotation.RequestParam;
import com.kyuboard.domain.KyuBoardVO;
import com.kyuboard.domain.KyuReplyVO;
import com.kyuboard.domain.Page;
import com.kyuboard.service.KyuBoardService;
import com.kyuboard.service.KyuReplyService;
@Controller
@RequestMapping("/board/*")
public class KyuBoardController {
private static final Logger logger = LoggerFactory.getLogger(KyuBoardController.class);
@Inject
private KyuBoardService service;
@Inject
private KyuReplyService replyService;
//게시물 목록
@RequestMapping(value="/list", method=RequestMethod.GET)
public void getList(Model model) throws Exception {
List<KyuBoardVO> list = null;
list = service.list();
model.addAttribute("listJsp", list);
}
//게시물 작성(GET)
@RequestMapping(value = "/write", method = RequestMethod.GET)
public void getWrite(HttpSession session, Model model) throws Exception {
logger.info("get write");
Object loginInfo = session.getAttribute("member");
if(loginInfo == null) {
model.addAttribute("msg", false);
}
}
//게시물 작성(POST)
@RequestMapping(value = "/write", method = RequestMethod.POST)
public String postWrite(KyuBoardVO vo) throws Exception {
logger.info("post write");
service.write(vo);
return "redirect:/board/list";
}
//게시물 조회
@RequestMapping(value = "/view", method = RequestMethod.GET)
public void getView(@RequestParam("bno") int bno, Model model) throws Exception {
KyuBoardVO vo = service.view(bno);
model.addAttribute("view", vo);
//댓글 조회
List<KyuReplyVO> reply = null;
reply = replyService.list(bno);
model.addAttribute("reply", reply);
}
//게시물 수정(GET)
@RequestMapping(value = "/modify", method = RequestMethod.GET)
public void getModify(@RequestParam("bno") int bno, Model model) throws Exception {
KyuBoardVO vo = service.view(bno);
model.addAttribute("view", vo);
}
//게시물 수정(POST)
@RequestMapping(value = "/modify", method = RequestMethod.POST)
public String postModify(KyuBoardVO vo) throws Exception {
service.modify(vo);
return "redirect:/board/view?bno=" + vo.getBno();
}
//게시물 삭제
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDelete(@RequestParam("bno")int bno) throws Exception {
service.delete(bno);
return "redirect:/board/list";
}
//게시물 목록 + 페이징 추가
@RequestMapping(value="/listPage", method=RequestMethod.GET)
public void getListPage(Model model, @RequestParam("num") int num) throws Exception {
Page page = new Page();
page.setNum(num);
page.setCount(service.count());
List<KyuBoardVO> list = null;
list = service.listPage(page.getDisplayPost(), page.getPostNum());
model.addAttribute("list", list);
/*
model.addAttribute("pageNum", page.getPageNum());
model.addAttribute("startPageNum", page.getStartPageNum());
model.addAttribute("endPageNum", page.getEndPageNum());
model.addAttribute("prev", page.getPrev());
model.addAttribute("next", page.getPrev());
*/
model.addAttribute("page", page);
model.addAttribute("select", num);
/*
//게시물 총 갯수
int count = service.count();
//한페이지에 출력할 게시물 갯수
int postNum = 10;
//하단 페이징 번호 ([게시물 총 갯수 / 한 페이지에 출력할 갯수]의 올림)
int pageNum = (int)Math.ceil((double)count/postNum);
//출력할 게시물
int displayPost = (num - 1) * postNum;
//한번에 표시할 페이징 번호의 갯수
int pageNum_cnt = 10;
//표시되는 페이지 번호 중 마지막 번호
int endPageNum = (int)(Math.ceil((double)num / (double)pageNum_cnt) * pageNum_cnt);
//표시되는 페이지 번호 중 첫번쨰 번호
int startPageNum = endPageNum - (pageNum_cnt - 1);
//마지막 번호 재계산 (why?->[ ((올림)(11 / 10)) * 10 => (올림)1.1 * 10 => 2 * 10 = 20 ] 이 됨 즉, 13 ~ 20까지 없어야할 페이지 번호가 출력
int endPageNum_tmp = (int)(Math.ceil((double)count / (double)pageNum_cnt)); //count ->게시물 총 갯수
if(endPageNum > endPageNum_tmp) {
endPageNum = endPageNum_tmp;
}
boolean prev = startPageNum == 1 ? false : true;
boolean next = endPageNum * pageNum_cnt >= count ? false : true;
List<KyuBoardVO> list = null;
list = service.listPage(displayPost, postNum);
model.addAttribute("list", list);
model.addAttribute("pageNum", pageNum);
//시작 및 끝 번호
model.addAttribute("startPageNum", startPageNum);
model.addAttribute("endPageNum", endPageNum);
// 이전 및 다음
model.addAttribute("prev", prev);
model.addAttribute("next", next);
//현재 페이지
model.addAttribute("select", num);
*/
//memo
// 매개변수로 num 은 페이지 번호
//1. 게시물의 총 갯수를 구하고
//한 페이지당 출력할 게시물 갯수를 정하고(10개)
//하단에 표시할 페이징 번호의 갯수를 구하고(소수점은 올림)
//현재 페이지를 기준으로 10개의 데이터를 출력
}
// 게시물 목록 + 페이징 추가 + 검색
@RequestMapping(value = "/listPageSearch", method = RequestMethod.GET)
public void getListPageSearch(Model model, @RequestParam("num") int num,
@RequestParam(value = "searchType", required = false, defaultValue = "title") String searchType,
@RequestParam(value = "keyword", required = false, defaultValue = "") String keyword
) throws Exception {
Page page = new Page();
page.setNum(num);
//page.setCount(service.count());
page.setCount(service.searchCount(searchType, keyword));
//검색 타입과 검색어
//page.setSearchTypeKeyword(searchType, keyword);
page.setSearchType(searchType);
page.setKeyword(keyword);
List<KyuBoardVO> list = null;
//list = service.listPage(page.getDisplayPost(), page.getPostNum());
list = service.listPageSearch(page.getDisplayPost(), page.getPostNum(), searchType, keyword);
model.addAttribute("list", list);
model.addAttribute("page", page);
model.addAttribute("select", num);
//model.addAttribute("searchType", searchType);
//model.addAttribute("keyword", keyword);
}
}
Mapper 코드
<?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="com.KyuBoard.mappers.board">
<!-- 게시물 목록 -->
<select id="list" resultType="com.kyuboard.domain.KyuBoardVO">
select
bno, title, content, writer, regDate, viewCnt
from tbl_board
</select>
<!-- 게시물 작성 -->
<insert id="write" parameterType="com.kyuboard.domain.KyuBoardVO">
insert into
tbl_board(title, content, writer)
value(#{title}, #{content}, #{writer})
</insert>
<!-- 게시물 조회 -->
<select id="view" parameterType="int" resultType="com.kyuboard.domain.KyuBoardVO">
select
bno, title, content, writer, regDate, viewCnt
from
tbl_board
where
bno = #{bno}
</select>
<!-- 게시물 수정 -->
<update id="modify" parameterType="com.kyuboard.domain.KyuBoardVO">
update tbl_board
set
title = #{title},
content = #{content},
writer = #{writer}
where bno = #{bno}
</update>
<!-- 게시물 삭제 -->
<delete id="delete" parameterType="int">
delete
from tbl_board
where bno = #{bno}
</delete>
<!-- 게시물 총 갯수 -->
<select id="count" resultType="int">
select count(bno) from tbl_board
</select>
<!-- 게시물 목록 + 페이징-->
<select id="listPage" parameterType="hashMap" resultType="com.kyuboard.domain.KyuBoardVO">
select
bno, title, writer, regDate, viewCnt
from tbl_board
order by bno desc
limit #{displayPost}, #{postNum}
</select>
<!-- 게시물 목록 + 페이징 + 검색 -->
<select id="listPageSearch" parameterType="hashMap" resultType="com.kyuboard.domain.KyuBoardVO">
select
bno, title, writer, regDate, viewCnt
from tbl_board
<if test='searchType.equals("title")'>
WHERE title LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("content")'>
WHERE content LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("title_content")'>
WHERE title LIKE concat('%', #{keyword}, '%')
or content LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("writer")'>
WHERE writer LIKE concat('%', #{keyword}, '%')
</if>
order by bno desc
limit #{displayPost}, #{postNum}
</select>
<!-- 게시물 총 갯수 + 검색적용-->
<select id="searchCount" parameterType="hashMap" resultType="int">
SELECT COUNT(bno) FROM tbl_board
<if test='searchType.equals("title")'>
WHERE title LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("content")'>
WHERE content LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("title_content")'>
WHERE title LIKE concat('%', #{keyword}, '%')
or content LIKE concat('%', #{keyword}, '%')
</if>
<if test='searchType.equals("writer")'>
WHERE writer LIKE concat('%', #{keyword}, '%')
</if>
</select>
</mapper>
728x90