day 11

유요한·2022년 12월 27일
0

Spring

목록 보기
14/15
post-thumbnail

처음부터 정리~

home.jsp

%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
	<title>Spring Board</title>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
	<meta name="description" content="" />
	<meta name="keywords" content="" />
	<link rel="stylesheet" href="/resources/assets/css/main.css" />
</head>
<body class="is-preload">

		<!-- Header -->
			<header id="header" class="alt">
				<a class="logo" href="/">Spring <span>Board</span></a>
				<nav id="nav">
					<ul>
						<c:choose>
							<c:when test="${loginUser == null}">
								<li class="current"><a href="/">Home</a></li>
								<li><a href="/user/join">Join</a></li>
								<li><a href="/user/login">Login</a></li>
							</c:when>
							<c:otherwise>
								<li>${loginUser}님 환영합니다!</li>
								<li class="current"><a href="/">Home</a></li>
								<li><a href="/board/list">Board</a></li>
								<li><a href="/user/logout">Logout</a></li>
							</c:otherwise>
						</c:choose>
					</ul>
				</nav>
			</header>

		<!-- Banner -->
			<div id="banner">
				<div class="wrapper style1 special">
					<div class="inner">
						<h1 class="heading alt">스프링 게시판</h1>
						<p>스프링 최종 예제</p>
						<div class="image fit special">
							<img src="https://images.unsplash.com/photo-1657497850516-de7b59ac243b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1160&q=80" alt="" />
						</div>
					</div>
				</div>
			</div>
		<!-- Scripts -->
			<script src="/resources/assets/js/jquery.min.js"></script>
			<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
			<script src="/resources/assets/js/browser.min.js"></script>
			<script src="/resources/assets/js/breakpoints.min.js"></script>
			<script src="/resources/assets/js/util.js"></script>
			<script src="/resources/assets/js/main.js"></script>

	</body>
</html>

/views/user/안에 만들어준다.

join.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
	<title>Spring Board</title>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
	<meta name="description" content="" />
	<meta name="keywords" content="" />
	<link rel="stylesheet" href="/resources/assets/css/main.css" />
<style>
	.heading{
		color:white;
	}
	.joinForm{
		width:50%;
		margin:0 auto;
	}
	.joinForm h3{
		float:left;
		width:30%;
		color:white;
		line-height:3.24rem;
	}
	.joinForm input[type=text], .joinForm input[type=password]{
		float:right;
		width:70%;
	}
	.joinForm>.col-12::after{
		display:block;
		content:" ";
		height:50px;
		clear:both;
	}

</style>
</head>
<body class="is-preload">

		<!-- Header -->
			<header id="header" class="alt">
				<a class="logo" href="/">Spring <span>Board</span></a>
				<nav id="nav">
					<ul>
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/user/join">Join</a></li>
						<li><a href="/user/login">Login</a></li>
					</ul>
				</nav>
			</header>

			<div id="main">
				<div class="wrapper style1 special">
					<div class="inner">
						<h2 class="heading alt">회원가입</h2>
						<br>
						<form class="joinForm" name="joinForm" id="joinForm" action="/user/join" method="post">
							<div class="col-12">
								<h3>아이디</h3>
								<input type="text" name="userid">
							</div>
							<div class="col-12">
								<h3>비밀번호</h3>
								<input type="password" name="userpw">
							</div>
							<div class="col-12">
								<h3>이름</h3>
								<input type="text" name="username">
							</div>
							<div class="col-12" style="text-align: center">
								<input type="submit" value="회원가입" class="primary">
							</div>
						</form>
					</div>
				</div>
			</div>
		<!-- Scripts -->
			<script src="/resources/assets/js/jquery.min.js"></script>
			<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
			<script src="/resources/assets/js/browser.min.js"></script>
			<script src="/resources/assets/js/breakpoints.min.js"></script>
			<script src="/resources/assets/js/util.js"></script>
			<script src="/resources/assets/js/main.js"></script>

	</body>
</html>

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
	<title>Spring Board</title>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
	<meta name="description" content="" />
	<meta name="keywords" content="" />
	<link rel="stylesheet" href="/resources/assets/css/main.css" />
<style>
	.heading{
		color:white;
	}
	.loginForm{
		width:50%;
		margin:0 auto;
	}
	.loginForm h3{
		float:left;
		width:30%;
		color:white;
		line-height:3.24rem;
	}
	.loginForm input[type=text], .loginForm input[type=password]{
		float:right;
		width:70%;
	}
	.loginForm>.col-12::after{
		display:block;
		content:" ";
		height:50px;
		clear:both;
	}

</style>
</head>
<body class="is-preload">

		<!-- Header -->
			<header id="header" class="alt">
				<a class="logo" href="/">Spring <span>Board</span></a>
				<nav id="nav">
					<ul>
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/user/join">Join</a></li>
						<li><a href="/user/login">Login</a></li>
					</ul>
				</nav>
			</header>

			<div id="main">
				<div class="wrapper style1 special">
					<div class="inner">
						<h2 class="heading alt">로그인</h2>
						<br>
						<form class="loginForm" name="loginForm" id="loginForm" action="/user/login" method="post">
							<div class="col-12">
								<h3>아이디</h3>
								<input type="text" name="userid">
							</div>
							<div class="col-12">
								<h3>비밀번호</h3>
								<input type="password" name="userpw">
							</div>
							<div class="col-12" style="text-align: center">
								<input type="submit" value="로그인" class="primary">
							</div>
						</form>
					</div>
				</div>
			</div>
		<!-- Scripts -->
			<script src="/resources/assets/js/jquery.min.js"></script>
			<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
			<script src="/resources/assets/js/browser.min.js"></script>
			<script src="/resources/assets/js/breakpoints.min.js"></script>
			<script src="/resources/assets/js/util.js"></script>
			<script src="/resources/assets/js/main.js"></script>

	</body>
</html>

UserController

package com.koreait.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.koreait.domain.UserDTO;
import com.koreait.service.UserService;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Controller
@Log4j
@RequestMapping("/user/*")
public class UserController {
	@Setter(onMethod_ = @Autowired)
	private UserService service;
//	@GetMapping("/join")
//	public void join() {
//		
//	}
//	@GetMapping("/login")
//	public void login() {
//		
//	}
	@GetMapping({"/join","/login"})
	public void replace() {	}
	
	@PostMapping("/join")
	public String join(UserDTO user, HttpServletResponse resp) {
		if(service.join(user)) {
			Cookie joinid = new Cookie("joinid",user.getUserid());
			joinid.setMaxAge(300);
			resp.addCookie(joinid);
		}
		return "redirect:/";
	}
	
	@PostMapping("/login")
	public String login(String userid, String userpw, HttpServletRequest req) {
		HttpSession session = req.getSession();
		UserDTO loginUser = service.login(userid, userpw);
		if(loginUser != null) {
			session.setAttribute("loginUser", loginUser.getUserid());
		}
		return "home";
	}
	
	@GetMapping("/logout")
	public String logout(HttpServletRequest req) {
		req.getSession().invalidate();
		return "home";
	}
	
}

UserMapper

package com.koreait.mapper;

import org.apache.ibatis.annotations.Param;

import com.koreait.domain.UserDTO;

public interface UserMapper {
	int join(UserDTO user);
	//MyBatis는 두개 이상의 데이터를 파라미터로 넘길 때 객체나 Map, List 등 혹은 @Param 을 이용한다.
	//정해진 파라미터는 MyBatis에서 #{param명}으로 사용 가능하다.
	UserDTO login(@Param("userid")String userid, @Param("userpw")String userpw);
}

UserMapper.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="com.koreait.mapper.UserMapper">
	<insert id="join">
		insert into spring_user values(#{userid},#{userpw},#{username})
	</insert>
	<select id="login" resultType="com.koreait.domain.UserDTO">
		select * from spring_user where userid=#{userid} and userpw=#{userpw}
	</select>
</mapper>

UserService

package com.koreait.service;

import com.koreait.domain.UserDTO;

public interface UserService {
	// true || false로 결과 값을 받는다.
	boolean join(UserDTO user);
	UserDTO login(String userid,String userpw);
}

UserServiceImpl

package com.koreait.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.koreait.domain.UserDTO;
import com.koreait.mapper.UserMapper;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
public class UserServiceImpl implements UserService{
	
	@Setter(onMethod_ = @Autowired)
	private UserMapper mapper;
	
	@Override
	public boolean join(UserDTO user) {
		return mapper.join(user) == 1;
	}
	
	@Override
	public UserDTO login(String userid, String userpw) {
		UserDTO loginUser = mapper.login(userid, userpw);
		return loginUser;
	}
}

UserMapperTests

package com.example.mapper;

import com.example.domain.UserDTO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class UserMapperTest {
    @Setter(onMethod_ = @Autowired)
    private UserMapper mapper;

//    @Test
//    public void joinTest() {
//        UserDTO user = new UserDTO();
//        user.setUserId("testId");
//        user.setUserPw("testPw");
//        user.setUserName("testName");
//
//        boolean result = 1 == mapper.join(user);
//        log.info("Result : " + result);
//    }

    @Test
    public void loginTest() {
        UserDTO loginUser = mapper.login("testid", "testpw");
        log.info(loginUser);
        System.out.println(loginUser);
    }
}

테스트는 하나하나 해줘야 한다.

UserServiceTests

package com.example.service;

import com.example.domain.UserDTO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class UserServiceTest {
    @Setter(onMethod_ = @Autowired)
    private UserService service;

    @Test
    public void joinTest() {
        UserDTO user = new UserDTO();
        user.setUserId("testId2");
        user.setUserPw("testPw2");
        user.setUserName("testName2");

        log.info("result : " + service.join(user));
    }

    @Test
    public void loginTest() {
        log.info("result : " + service.login("testId2", "testPw2"));
    }
}

여기까지가 user에 관한 view, controller, mapper, service 그리고 test다. 이제는 게시글에 해당하는 부분을 구현하려고 한다.


/views/board/~
list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BOARD</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/resources/assets/css/main.css" />
<style>
	#banner input, #banner select, #banner option{
		color:#000000;
	}
	select{
		width:15%;
		text-align: center;
		padding:0 20px;
		display: inline;
	}
	#keyword{
		width:55%;
		display: inline;	
	}
	#keyword+a{
		border:1px solid white;
	}
	th, td{
		text-align: center !important;
	}
	tbody tr:hover{
		background-color:rgba(255,255,255,0.3) !important;
	}
	.nowPage{
		background-color:#F9F8FF !important;
		color:#8086F1 !important;
		font-weight: 900;
	}
</style>
</head>
<body class="is-preload">
	<c:if test="${boardnum != null}">
		<script>alert("${boardnum}번 게시글 작성 완료!");</script>
	</c:if>
	<!-- Header -->
	<header id="header" class="alt">
		<a class="logo" href="/">Spring <span>Board</span></a>
		<nav id="nav">
			<ul>
				<c:choose>
					<c:when test="${loginUser == null}">
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/user/join">Join</a></li>
						<li><a href="/user/login">Login</a></li>
					</c:when>
					<c:otherwise>
						<li>${loginUser}님 환영합니다!</li>
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/board/list">Board</a></li>
						<li><a href="/user/logout">Logout</a></li>
					</c:otherwise>
				</c:choose>
			</ul>
		</nav>
	</header>

<!-- Banner -->
	<div id="banner">
		<div class="wrapper style1 special">
			<div class="inner">
				<h1 class="heading alt">Board</h1>
				<p>게시판 목록</p>
				<a href="/board/regist" class="button primary" style="float:right; border-radius:0px; border-bottom: 1px solid white; border-top: 1px solid white;">글 등록</a>
				<div class="table-wrapper" style="clear: both;">
					<table>
						<thead>
							<tr>
								<th style="width:10%;">번호</th>
								<th style="width:40%;">제목</th>
								<th style="width:15%;">작성자</th>
								<th style="width:35%;">작성시간</th>
							</tr>
						</thead>
						<tbody>
							<c:choose>
								<c:when test="${list != null and list.size()>0 }">
									<c:forEach items="${list}" var="board">
										<tr>
											<td>${board.boardnum }</td>
											<td><a href="${board.boardnum}" class="get">${board.boardtitle }</a></td>
											<td>${board.userid }</td>
											<td>${board.regdate }
												<c:if test="${board.regdate != board.updatedate}">
													(${board.updatedate})
												</c:if>
											</td>
										</tr>
									</c:forEach>
								</c:when>
								<c:otherwise>
									<tr>
										<td colspan="5">작성된 게시글이 없습니다.</td>
									</tr>
								</c:otherwise>
							</c:choose>
						</tbody>
					</table>
				</div>
				<form id="searchForm" action="/board/list" onsubmit="return sendit()">
					<div>
						<select name="type">
							<option value="" ${pageMaker.cri.type == null ? "selected" : ""}>검색</option>
							<option value="T" ${pageMaker.cri.type == "T" ? "selected" : ""}>제목</option>
							<option value="C" ${pageMaker.cri.type == "C" ? "selected" : ""}>내용</option>
							<option value="W" ${pageMaker.cri.type == "W" ? "selected" : ""}>작성자</option>
							<option value="TC" ${pageMaker.cri.type == "TC" ? "selected" : ""}>제목 또는 내용</option>
							<option value="TW" ${pageMaker.cri.type == "TW" ? "selected" : ""}>제목 또는 작성자</option>
							<option value="TCW" ${pageMaker.cri.type == "TCW" ? "selected" : ""}>제목 또는 내용 또는 작성자</option>
						</select>
						<input type="text" name="keyword" id="keyword" value="${pageMaker.cri.keyword}">
						<a href="#" class="button primary">검색</a>
					</div>
					<input type="hidden" value="1" name="pagenum">
					<input type="hidden" value="10" name="amount">
				</form>
				<div class="pagination center">
					<c:if test="${pageMaker.prev}">
						<a class="changePage" href="${pageMaker.startPage-1}"><code>&lt;</code></a>
					</c:if>
					<c:forEach begin="${pageMaker.startPage}" end="${pageMaker.endPage}" var="i">
						<c:choose>
							<c:when test="${i == pageMaker.cri.pagenum}">
								<code class="nowPage">${i}</code>
							</c:when>
							<c:otherwise>
								<a class="changePage" href="${i}"><code>${i}</code></a>
							</c:otherwise>
						</c:choose>
					</c:forEach>
					<c:if test="${pageMaker.next}">
						<a class="changePage" href="${pageMaker.endPage+1}"><code>&gt;</code></a>
					</c:if>
				</div>
			</div>
		</div>
	</div>
	<form name="pageForm" id="pageForm" action="/board/list">
		<input type="hidden" value="${pageMaker.cri.pagenum }" name="pagenum">
		<input type="hidden" value="${pageMaker.cri.amount }" name="amount">
		<input type="hidden" value="${pageMaker.cri.type}" name="type">
		<input type="hidden" value="${pageMaker.cri.keyword }" name="keyword">
	</form>
<!-- Scripts -->
	<script src="/resources/assets/js/jquery.min.js"></script>
	<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
	<script src="/resources/assets/js/browser.min.js"></script>
	<script src="/resources/assets/js/breakpoints.min.js"></script>
	<script src="/resources/assets/js/util.js"></script>
	<script src="/resources/assets/js/main.js"></script>

	</body>
<script>
	const pageForm = $("#pageForm");
	const searchForm = $("#searchForm")
	$(".get").on("click",function(e){
		//e(이벤트)의 기본 작동 막기
		e.preventDefault();
		let boardnum = $(this).attr("href");
		//				 <input type='hidden' name='boardnum' value='   5452780  '>
		pageForm.append("<input type='hidden' name='boardnum' value='"+boardnum+"'>");
		//jQuery 요소일때는
		pageForm.attr("action","/board/get");
		//바닐라 요소일때는
		//pageForm.setAttribute("action","/board/get");
		pageForm.submit();		
	})
	$(".changePage").on("click",function(e){
		e.preventDefault();
		let pagenum = $(this).attr("href");
		pageForm.find("input[name='pagenum']").val(pagenum);
		pageForm.submit();
	})
	$("#searchForm a").on("click",sendit)
	function sendit(e){
		//검색 조건을 선택하지 않았을 때
		/*
			if("") ---> if(false)
		*/
		if(!searchForm.find("option:selected").val()){
			alert("검색 기준을 선택하세요")
			return false;
		}
		if(!searchForm.find("input[name='keyword']").val()){
			alert("키워드를 입력하세요")
			return false;
		}
		if(searchForm.find("input[name='keyword']").val().length<2){
			alert("키워드는 두 글자 이상 입력하세요")
			return false;
		}
		searchForm.submit();
	}
</script>
</html>

BoardController

package com.koreait.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.koreait.domain.BoardDTO;
import com.koreait.domain.Criteria;
import com.koreait.domain.PageDTO;
import com.koreait.service.BoardService;

import lombok.Setter;

@Controller
@RequestMapping("/board/*")
public class BoardController {
	
	@Setter(onMethod_ = @Autowired)
	private BoardService service;
	
	@GetMapping("/list")
	public void list(Criteria cri, Model model) {
		model.addAttribute("list",service.getList(cri));
		model.addAttribute("pageMaker",new PageDTO(service.count(cri), cri));
	}
	@GetMapping("/regist")
	public void regist() {
		
	}
	@PostMapping("/regist")
	public String regist(BoardDTO board, RedirectAttributes ra) {
		service.regist(board);
		int boardnum = service.getMaxBoardnum(board.getUserid());
		ra.addFlashAttribute("boardnum", boardnum);
		return "redirect:/board/list";
	}
	
	@GetMapping(value = {"/get", "/modify"})
	public void get(Long boardnum, @ModelAttribute("cri") Criteria cri, Model model) {
		//@ModelAttribute("키값") : 데이터를 수집하자 마자 해당하는 키값으로 Model에 attribute로 추가
		model.addAttribute("board", service.get(boardnum));
		
	}
	
	@PostMapping("/modify")
	public String modify(BoardDTO board, Criteria cri, RedirectAttributes ra) {
		if(service.modify(board)) {
			ra.addFlashAttribute("mn",board.getBoardnum());
		}
		return "redirect:/board/get"+cri.getListLink()+"&boardnum="+board.getBoardnum();
	}
	
	@PostMapping("/remove")
	public String remove(Long boardnum,Criteria cri) {
		if(service.remove(boardnum)) {
			//
		}
		return "redirect:/board/list"+cri.getListLink();
	}
}

BoardDTO

package com.koreait.domain;

import lombok.Data;

@Data
public class BoardDTO {
	private Long boardnum;
	private String boardtitle;
	private String boardcontents;
	private String userid;
	private String regdate;
	private String updatedate;
}

regist.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BOARD</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/resources/assets/css/main.css" />
<style>
	#banner input, #banner select, #banner option{
		color:#000000;
	}
	input:not(.button), textarea{
		border:1px solid black !important;
		color:#000000 !important;
	}
	#banner .write_area ::placeholder{
		color:#888 !important;
	}
	.write_area{
		width:70%;
		margin:0 auto;	
	}
	.write_area input{
		margin-bottom: 32px;
	}
	.write_area > form > div{
		margin-bottom:32px;
	}
	.write_area > form > div:nth-of-type(odd) > h4 {
		float:left;
	}
	.write_area > form > div:nth-of-type(odd) > input {
		width:70%;
		float:right;
	}
	form h4{
		line-height:3.25rem;
	}
	hr{
		clear:both;	
	}
</style>
</head>
<body class="is-preload">
	<!-- Header -->
	<header id="header" class="alt">
		<a class="logo" href="/">Spring <span>Board</span></a>
		<nav id="nav">
			<ul>
				<c:choose>
					<c:when test="${loginUser == null}">
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/user/join">Join</a></li>
						<li><a href="/user/login">Login</a></li>
					</c:when>
					<c:otherwise>
						<li>${loginUser}님 환영합니다!</li>
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/board/list">Board</a></li>
						<li><a href="/user/logout">Logout</a></li>
					</c:otherwise>
				</c:choose>
			</ul>
		</nav>
	</header>

<!-- Banner -->
	<div id="banner">
		<div class="wrapper style1 special">
			<div class="inner">
				<h1 class="heading alt">Board</h1>
				<p>게시판 등록</p>
				<a href="/board/list" class="button primary" style="float:right; border-radius:0px; border-bottom: 1px solid white; border-top: 1px solid white;">목록 보기</a>
				<br>
				<br>
				<br>
				<div class="write_area" style="clear:both;">
					<form name="boardForm" method="post" action="/board/regist">
						<div class="col-12">
							<h4>제목</h4>
							<input name="boardtitle" type="text" placeholder="Title">
						</div>
						<hr>
						<div class="col-12">
							<h4>내용</h4>
							<textarea name="boardcontents" placeholder="Contents" rows="10"></textarea>
						</div>
						<hr>
						<div>
							<h4>작성자</h4>
							<input name="userid" type="text" value="${loginUser}" readonly>
						</div>
						<hr>
						<div>
							<input type="submit" value="등록" class="button primary">
						</div>
					</form>
				
				</div>
			</div>
		</div>
	</div>
<!-- Scripts -->
	<script src="/resources/assets/js/jquery.min.js"></script>
	<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
	<script src="/resources/assets/js/browser.min.js"></script>
	<script src="/resources/assets/js/breakpoints.min.js"></script>
	<script src="/resources/assets/js/util.js"></script>
	<script src="/resources/assets/js/main.js"></script>

</body>
</html>

get.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BOARD</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/resources/assets/css/main.css" />
<style>
	#banner input, #banner select, #banner option{
		color:#000000;
	}
	input:not(.button), textarea{
		border:1px solid black !important;
		color:#000000 !important;
		resize:none;
	}
	#banner .write_area ::placeholder{
		color:#888 !important;
	}
	.write_area{
		width:70%;
		margin:0 auto;	
	}
	.write_area input{
		margin-bottom: 32px;
	}
	.write_area > form > div{
		margin-bottom:32px;
	}
	.write_area > form > div:nth-of-type(odd) > h4 {
		float:left;
	}
	.write_area > form > div:nth-of-type(odd) > input {
		width:70%;
		float:right;
	}
	form h4{
		line-height:3.25rem;
	}
	hr{
		clear:both;	
	}
	.replyForm input, .replyForm textarea{
		width:100%;
	}
	.page{
		text-align: center;
		clear:both;
	}
	.mdf{
		height:75px; width:100%; resize:none;
	}
</style>
</head>
<body class="is-preload">
	<c:if test="${not empty mn}">
		<script>
			alert("${mn}번 게시글 수정완료!");
		</script>
	</c:if>
	<!-- Header -->
	<header id="header" class="alt">
		<a class="logo" href="/">Spring <span>Board</span></a>
		<nav id="nav">
			<ul>
				<c:choose>
					<c:when test="${loginUser == null}">
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/user/join">Join</a></li>
						<li><a href="/user/login">Login</a></li>
					</c:when>
					<c:otherwise>
						<li>${loginUser}님 환영합니다!</li>
						<li class="current"><a href="/">Home</a></li>
						<li><a href="/board/list">Board</a></li>
						<li><a href="/user/logout">Logout</a></li>
					</c:otherwise>
				</c:choose>
			</ul>
		</nav>
	</header>

<!-- Banner -->
	<div id="banner">
		<div class="wrapper style1 special">
			<div class="inner">
				<h1 class="heading alt">Board</h1>
				<p>&nbsp;</p>
				<a href="/board/list${cri.listLink}" class="button primary" style="float:right;
				border-radius:0px; border-bottom: 1px solid white;
				border-top: 1px solid white;">목록 보기</a>
				<br>
				<br>
				<br>
				<div class="write_area" style="clear:both;">
					<form name="boardForm" method="post" action="/board/remove">
						<input type="hidden" name="boardnum" value="${board.boardnum}">
						<input type="hidden" value="${cri.pagenum}" name="pagenum">
						<input type="hidden" value="${cri.amount }" name="amount">
						<input type="hidden" value="${cri.type }" name="type">
						<input type="hidden" value="${cri.keyword }" name="keyword">
						<div class="col-12">
							<h4>제목</h4>
							<input name="boardtitle" type="text" value="${board.boardtitle}" readonly>
						</div>
						<hr>
						<div class="col-12">
							<h4>내용</h4>
							<textarea name="boardcontents" rows="10" readonly>${board.boardcontents}</textarea>
						</div>
						<hr>
						<div>
							<h4>작성자</h4>
							<input name="userid" type="text" value="${board.userid}" readonly>
						</div>
						<c:if test="${loginUser == board.userid}">
							<hr>
							<div style="text-align: right;">
								<input type="button" value="수정" class="button primary"
								onclick="location.href = '/board/modify${cri.listLink}&boardnum=${board.boardnum}'">
								<input type="submit" value="삭제" class="button primary">
							</div>
						</c:if>
					</form>
					<hr>
					<h3>댓 글</h3>
					<p style="text-align: right;">
					<a href="#" class="button primary small regist">댓글 등록</a>
					</p>
					<div class="replyForm row" style="display:none;">
						<div style="width:15%;">
							<h4>작성자</h4>
							<input name="userid" value="${loginUser}" readonly style="text-align: center;">
						</div>
						<div style="width:65%;">
							<h4>내 용</h4>
							<textarea name="replycontents" placeholder="Contents" style="resize:none;"></textarea>
						</div>
						<div style="width:15%">
							<h4>&nbsp;</h4>
							<a href="#" class="button primary small finish" style="margin-bottom:1rem;">등록</a>
							<a href="#" class="button primary small cancel">취소</a>
						</div>
					</div>
					<br><br>
					<!-- 댓글 띄우는 ul -->
					<ul class="alt replies"></ul>
					<!-- 댓글 페이징 처리할 div -->
					<div class="page">
					</div>
				</div>
			</div>
		</div>
	</div>
<!-- Scripts -->
	<script src="/resources/assets/js/jquery.min.js"></script>
	<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
	<script src="/resources/assets/js/browser.min.js"></script>
	<script src="/resources/assets/js/breakpoints.min.js"></script>
	<script src="/resources/assets/js/util.js"></script>
	<script src="/resources/assets/js/main.js"></script>
	<script src="/resources/assets/js/reply.js"></script>

</body>
<script>
	let pagenum = 0;
	let boardnum = "${board.boardnum}";
	const replies = $(".replies")
	let maxCnt = 0;
	const loginUser = "${loginUser}";
	const page = $(".page");
	
	$(document).ready(function(){
		pagenum = 1;
		showList(1);
	})
	
	$(".regist").on("click",function(e){
		e.preventDefault();
		$(".replyForm").show();
		$(this).hide();
	})
	$(".cancel").on("click",function(e){
		e.preventDefault();
		$(".replyForm").hide();
		$(".regist").show();
		$("[name='replycontents']").val("");
	})
	$(".finish").on("click",function(e){

		e.preventDefault();
		let replycontents = $("[name='replycontents']").val();
		replyService.add(
			{boardnum:boardnum, userid:loginUser, replycontents:replycontents},
			function(result){
				//DOM 구현
				alert(result+"번 댓글 등록 완료!");
				
				if(maxCnt < 5 || (Math.ceil(maxCnt/5)==pagenum && maxCnt%5!=0) ){
					let str = "";
					
					str += '<li style="clear:both;" class="li'+result+'">';
					str += '<div style="display:inline; float:left; width:80%;">';
					//<strong class="userid132">apple</strong>
					str += '<strong class="userid'+result+'">'+loginUser+'</strong>';
					//<p class="reply132">댓글 내용용요용용</p>"
					str += '<p class="reply'+result+'">'+replycontents+'</p></div>'
					str += '<div style="text-align:right;">'
					str += '<strong>방금 전</strong><br>'
					//<a href="132" class="modify">수정</a>
					str += '<a href="'+result+'" class="modify">수정</a>'
					str += '<a href="'+result+'" class="mfinish" style="display:none;">수정완료</a>&nbsp;&nbsp;'
					str += '<a href="'+result+'" class="remove">삭제</a>'
					str += "</div></li>";
					replies.append(str);
					
					$(".modify").on("click",modifyReply);
					$(".mfinish").on("click",modifyReplyOk);
					$(".remove").on("click",deleteReply);
					maxCnt++;
				}
				else{
					showList(pagenum);
				}
			}
		)
		$("[name='replycontents']").val("");
		$(".replyForm").hide();
		$(".regist").show();
	})
	
	
	function showList(pagenum){
		replyService.getList(
				{boardnum:boardnum,pagenum:pagenum||1},
				function(replyCnt, list){
					if(list == null || list.length == 0){
						//현재 게시글에 달린 댓글이 없다는 뜻
						return;
					}
					//DOM 구현
					let str = "";
					maxCnt = replyCnt;
					for(let i=0;i<list.length;i++){
						//<li style="clear:both;" class="li132">
						str += '<li style="clear:both;" class="li'+list[i].replynum+'">';
						str += '<div style="display:inline; float:left; width:80%;">';
						//<strong class="userid132">apple</strong>
						str += '<strong class="userid'+list[i].replynum+'">'+list[i].userid+'</strong>';
						//<p class="reply132">댓글 내용용요용용</p>"
						str += '<p class="reply'+list[i].replynum+'">'+list[i].replycontents+'</p></div>'
						str += '<div style="text-align:right;">'
						str += '<strong>'+replyService.displayTime(list[i])+'</strong><br>'
						if(list[i].userid == loginUser){
							//<a href="132" class="modify">수정</a>
							str += '<a href="'+list[i].replynum+'" class="modify">수정</a>'
							str += '<a href="'+list[i].replynum+'" class="mfinish" style="display:none;">수정완료</a>&nbsp;&nbsp;'
							str += '<a href="'+list[i].replynum+'" class="remove">삭제</a>'
						}
						str += "</div></li>";
					}
					replies.html(str);
					
					$(".modify").on("click",modifyReply);
					$(".mfinish").on("click",modifyReplyOk);
					$(".remove").on("click",deleteReply);
					
					showReplyPage(replyCnt);
				}
		)
	}
	function showReplyPage(replyCnt){
		let endPage = Math.ceil(pagenum/5)*5;
		let startPage = endPage-4;
		
		let prev = startPage!=1;
		let next = false;
		
		if(endPage*5 >= replyCnt){
			endPage = Math.ceil(replyCnt/5);
		}
		if(endPage*5 < replyCnt){
			next = true;
		}
		
		let str = "";
		if(prev){
			//<a class="changePage" href="5"><code>&lt;</code></a>
			str += '<a class="changePage" href="'+(startPage-1)+'"><code>&lt;</code></a>';
		}
		for(let i=startPage;i<=endPage;i++){
			if(i == pagenum){
				str += "<code>"+i+"</code>"
			}
			else{
				str += '<a class="changePage" href="'+i+'"><code>'+i+'</code></a>';
			}
		}
		if(next){
			str += '<a class="changePage" href="'+(endPage+1)+'"><code>&gt;</code></a>';
		}
		page.html(str);
		
		$(".changePage").on("click",function(e){
			e.preventDefault();
			let target = $(this).attr("href");
			pagenum = parseInt(target);
			showList(pagenum);
		})
	}
	function deleteReply(e){
		e.preventDefault();
		let replynum = $(this).attr("href");
		replyService.remove(
				replynum,
				function(result){
					if(result == "success"){
						$(".li"+replynum).remove();
						alert(replynum+"번 댓글 삭제 완료!");
						//실제로 마지막 페이지의 유일한 댓글을 삭제하는 경우에는
						//현재 보고있는 페이지로 다시 불러오는 것이 아니라 이전페이지로 불러와야 한다. 
						showList(pagenum);
					}
				},
				function(err){
					alert("에러발생")
				}
		)
	}
	let flag = false;
	function modifyReply(e){
		e.preventDefault();
		if(!flag){
			flag = true;
			let replynum = $(this).attr("href");
			const replyTag = $(".reply"+replynum);
			//<textarea class="123 mdf">노잼댓글내용</textarea>
			replyTag.html('<textarea class="'+replynum+' mdf">'+replyTag.text()+'</textarea>');
			$(this).hide();
			$(this).next().show();
		}
		else{
			alert("수정중인 댓글이 있습니다!");
		}
	}
	function modifyReplyOk(e){
		flag = false;
		e.preventDefault();
		
		let replynum = $(this).attr("href");
		let replycontents = $("."+replynum).val();
		
		if(replycontents == ""){
			alert("댓글 내용을 입력하세요!");
			return;
		}
		
		replyService.modify(
			{replynum:replynum, replycontents:replycontents, boardnum:boardnum, userid:loginUser},
			function(result){
				if(result == "success"){
					alert(replynum+"번 댓글 수정 완료!");
					$(".reply"+replynum).html(replycontents);
					const dateObj = new Date();
					let hh = dateObj.getHours();
					let mi = dateObj.getMinutes();
					let ss = dateObj.getSeconds();
					let str = (hh>9?'':'0')+hh+":"+(mi>9?'':'0')+mi+":"+(ss>9?'':'0')+ss;
					$(".li"+replynum+">div:nth-child(2)>strong").html("(수정됨) "+str);
				}
			}
		)
		$(this).hide();
		$(this).prev().show();
	}
</script>
</html>

게시글의 페이지처리를 하기 위해서 DTO를 만들어야합니다.

Criteria

package com.koreait.domain;

import org.springframework.web.util.UriComponentsBuilder;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Criteria {
	private int pagenum;
	private int amount;
	private String type;
	private String keyword;
	private int startrow;
	
	public Criteria() {
		//this() : 현재 클래스의 생성자
		this(1,10);
	}

	public Criteria(int pagenum, int amount) {
		this.pagenum = pagenum;
		this.amount = amount;
		this.startrow = (this.pagenum-1)*this.amount;
	}
	
	public void setPagenum(int pagenum) {
		this.pagenum = pagenum;
		this.startrow = (this.pagenum-1)*this.amount;
	}
	
	public String[] getTypeArr() {
		//type이 null 이라면 return {}
		//type에 "TC"가 있다면 return {"T", "C"}
		return type == null ? new String[] {} : type.split("");
	}
	
	public String getListLink() {
		UriComponentsBuilder builder = UriComponentsBuilder.fromPath("")	// ? 앞에 오는 uri 문자열
				.queryParam("pagenum", pagenum)								// 파라미터 추가
				.queryParam("amount", amount)
				.queryParam("keyword", keyword)
				.queryParam("type", type);
		
		// ?pagenum=3&amount=10&keyword=app&type=TC
		return builder.toUriString();				//빌더가 가지고 있는 설정대로 문자열 만들기
	}
}

mapper에서 계산식이 안되기 때문에 startrow를 만들어주고 setter을 만들어 준다음 게산식을 여기서 작성해 준다. amount는 보여줄 수다. 예를 들어서 페이지가 1~10개까지만 보여줄거면 amount는 10인 것이다.

PageDTO

package com.koreait.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class PageDTO {
	private int startPage;
	private int endPage;
	private int realEnd;
	private int total;
	private boolean prev, next;
	private Criteria cri;
	
	public PageDTO(int total, Criteria cri) {
		int pagenum = cri.getPagenum();
		this.cri = cri;
		this.total = total;
		
		this.endPage = (int)Math.ceil(pagenum/10.0)*10;
		this.startPage = this.endPage - (cri.getAmount()-1);
		
		this.realEnd = (int)Math.ceil(total*1.0/10);
		
		endPage = endPage>realEnd?realEnd:endPage;
		
		this.prev = this.startPage > 1;
		this.next = this.endPage < this.realEnd;
	}
}

pageDTO의 역할은 page를 계산해주는 역할을 한다. endPage는 ceil로 cri에서 가져온 pageNum을 10.0으로 나누고 곱하기 10을 한 것을 정수로 바꾼 것이다. startPage는 endPage를 가져와서 cri에서 가져온 보여줄 수인 amount -1을 한 것이다. realEnd는 페이지의 총합인 total을 받아와서 1.0을 곱하고 10으로 나눈것을 정수화한것이다. prev는 < 이것을 말하는 것이고 1보다 커야 보이는 것이다. next는 realEnd가 endPage보다 작아야 보인다.

Criteria cri부분이 원래는 int pageNum이였지만 이렇게하면 못받아올 경우 에러가 뜬다. 그렇기 때문에 객체로 만들어서 보내준다. 객체로 만들어서 보내주면 없어도 null로 뜨기 때문이다.

BoardController

	@GetMapping("/list")
	public void list(Criteria cri, Model model) {
		model.addAttribute("list",service.getList(cri));
		model.addAttribute("pageMaker",new PageDTO(service.count(cri), cri));
	}

페이지를 클릭할 때마다 다른 페이지를 보여주기 위해서 위해서 getlist에도 매개변수로 cri를 넘겨주고 list 밑에 페이지를 보여주기 위해 pageMaker을 만들어서 pageDTO 객체를 생성한 다음 service.count에 cri를 매개변수로 넘겨주고 pageDTO에도 cri 매개변수를 넘겨준다.

BoardService

public List<BoardDTO> getList(Criteria cri);
public int count(Criteria cri);

controller에서 cri를 매개변수로 넘겨줬으니 service에서도 Criteria cri를 만들어서 매개변수로 받아야 한다. 그리고 int count(Criteria cri)를 생성해준다.

BoardServiceImpl

	@Override
	public List<BoardDTO> getList(Criteria cri) {
		log.info("==========getList==========");
		return mapper.getList(cri);
	}

@Override
	public int count(Criteria cri) {
		return mapper.getTotal(cri);
	}

service에서 넘겨준 cri를 serviceImpl에서 cri를 매개변수로 넘겨받고 mapper.getList(cri)로 넘겨줍니다. 그리고 count 메소드에서도 cri를 넘겨받아서 mapper.getTotal(cri)로 넘겨줍니다.

BoardMapper

List<BoardDTO> getList(Criteria cri);
int getTotal(Criteria cri);

BoardMapper

package com.koreait.mapper;

import java.util.List;

import com.koreait.domain.BoardDTO;
import com.koreait.domain.Criteria;

public interface BoardMapper {
	List<BoardDTO> getList(Criteria cri);
	void insert(BoardDTO board);
	int getMaxBoardnum(String userid);
	BoardDTO getDetail(Long boardnum);
	int getTotal(Criteria cri);
	int update(BoardDTO board);
	int delete(Long boardnum);
}

BoardMapper.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="com.koreait.mapper.BoardMapper">
	<sql id="cri">
		<if test="keyword != null and type != null">
			<trim prefixOverrides="or" prefix="(" suffix=") and">
				<foreach collection="typeArr" item="type">
					<trim prefix="or">
						<choose>
							<when test="type=='T'.toString()">
								(boardtitle like('%${keyword}%'))
							</when>
							<when test="type=='C'.toString()">
								(boardcontents like('%${keyword}%'))
							</when>
							<when test="type=='W'.toString()">
								(userid like('%${keyword}%'))
							</when>
						</choose>
					</trim>
				</foreach>
			</trim>
		</if>
	</sql>

	<insert id="insert">
		insert into spring_board (boardtitle,boardcontents,userid)
		values(#{boardtitle},#{boardcontents},#{userid})
	</insert>
	
	<update id="update">
		update spring_board set boardtitle=#{boardtitle}, boardcontents=#{boardcontents},
		updatedate=now() where boardnum=#{boardnum}
	</update>
	
	
	<select id="getList" resultType="com.koreait.domain.BoardDTO">
		select * from spring_board where
		<include refid="cri"></include>
		<![CDATA[
			0<boardnum order by boardnum desc limit #{startrow},#{amount}
		]]>
	</select>
	<select id="getMaxBoardnum" resultType="_int">
		select max(boardnum) from spring_board where userid=#{userid}
	</select>
	<select id="getDetail" resultType="com.koreait.domain.BoardDTO">
		select * from spring_board where boardnum=#{boardnum}
	</select>
	<select id="getTotal" resultType="_int">
		select count(*) from spring_board where <include refid="cri"></include> boardnum>0
	</select>
	
	<delete id="delete">
		delete from spring_board where boardnum=#{boardnum}
	</delete>
</mapper>

BoardService

package com.koreait.service;

import java.util.List;

import com.koreait.domain.BoardDTO;
import com.koreait.domain.Criteria;

public interface BoardService {
	public List<BoardDTO> getList(Criteria cri);
	
	public void regist(BoardDTO board);
	
	public BoardDTO get(Long boardnum);
	
	public boolean modify(BoardDTO board);
	
	public boolean remove(Long boardnum);
	
	public int getMaxBoardnum(String userid);

	public int count(Criteria cri);
}

BoardServiceImpl

package com.koreait.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.koreait.domain.BoardDTO;
import com.koreait.domain.Criteria;
import com.koreait.mapper.BoardMapper;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
public class BoardServiceImpl implements BoardService{
	@Setter(onMethod_ = @Autowired)
	private BoardMapper mapper;

	@Override
	public List<BoardDTO> getList(Criteria cri) {
		log.info("==========getList==========");
		return mapper.getList(cri);
	}

	@Override
	public void regist(BoardDTO board) {
		mapper.insert(board);
	}

	@Override
	public BoardDTO get(Long boardnum) {
		return mapper.getDetail(boardnum);
	}

	@Override
	public boolean modify(BoardDTO board) {
		return mapper.update(board) == 1;
	}

	@Override
	public boolean remove(Long boardnum) {
		return mapper.delete(boardnum) == 1;
	}
	
	@Override
	public int getMaxBoardnum(String userid) {
		return mapper.getMaxBoardnum(userid);
	}
	
	@Override
	public int count(Criteria cri) {
		return mapper.getTotal(cri);
	}
}
profile
발전하기 위한 공부

0개의 댓글