HomeController
RegisterFormController
RegisterController
FrontController
실행과정
- 요청에 대한 응답은 재요청 URL과 회원가입 성공화면 이다.
- 최종적으로 받은 응답은 회원가입 폼 화면과 회원가입 성공화면이다.
실행 결과
RegisterSuccessController
RegisterSuccess.jsp
실행결과
로그인
LoginFormController
LoginController
loginform.jsp
navbar.jsp
home.jsp
> 실행결과
![](https://velog.velcdn.com/images/hcw0709/post/21a56a33-1c6c-430a-b3fa-ebce02fe25a5/image.png)
![](https://velog.velcdn.com/images/hcw0709/post/9d5217e6-d044-4d54-9e6d-35d3bfac4517/image.png)
![](https://velog.velcdn.com/images/hcw0709/post/8925905c-655f-4bc9-90af-85138cd41537/image.png)
> 로그인 전
![](https://velog.velcdn.com/images/hcw0709/post/c6a0dcc0-5af0-4ea5-921c-88b746cafb36/image.png)
> 로그인 후
![](https://velog.velcdn.com/images/hcw0709/post/5d185014-b73c-400b-b3c5-f2211dc98f53/image.png)
> **로그아웃**
LogoutController
> 실행결과
![](https://velog.velcdn.com/images/hcw0709/post/579ab4ce-665a-4c30-a604-ad9e569a55fa/image.png)
### 새글 등록
> **새글 등록 버튼 활성화 /비활성화**
list.jsp
<c:if test="${not empty loginUserId }">
<a href="form.hta" class="btn btn-primary btn-sm float-end">새 글쓰기</a>
</c:if>
> 로그인 전
![](https://velog.velcdn.com/images/hcw0709/post/e6a1a61c-87ef-4977-bcbc-753797ec38eb/image.png)
> 로그인 후
![](https://velog.velcdn.com/images/hcw0709/post/a2c3d2f7-5d37-4146-96f7-309e6e6dd657/image.png)
> **글 등록폼 활성화 / 비활성화**
FormController
package com.sample.app.controller.post;
import com.sample.model2.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
/*
* 요청 URI
/post/form.hta
요청 파라미터
없음
반환값
post/form.jsp
redirect:/app/user/loginform.hta?error=deny
요청처리 내용
세션에서 로그인된 사용자 정보를 조회한다.
사용자정보가 존재하지 않으면 로그인폼을 요청하는 재요청 URL("redirect:/app/user/loginform.hta?error=deny")을 반환한다.
세션에 로그인된 사용자 정보가 존재하면 psot/form.jsp를 반환한다.
*/
public class FormController implements Controller {
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 세션에서 로그인된 사용자정보를 조회한다.
// 로그인된 사용자정보가 존재하지 않으면 로그인폼을 요청하는 재요청 URL을 반환한다.
HttpSession session = request.getSession();
if (session.getAttribute("loginUserId") == null) {
return "redirect:/app/user/loginform.hta?error=deny";
}
// 세션에 로그인된 사용자정보가 존재하면 post/form.jsp를 반환한다.
return "post/form.jsp";
}
}
> 실행결과 (로그인 전)
![](https://velog.velcdn.com/images/hcw0709/post/d25ae7f6-5c13-4bf5-8877-23224f251b36/image.png)
> 실행결과 (로그인 후)
![](https://velog.velcdn.com/images/hcw0709/post/3f1269be-3ca5-42b6-9894-0c220c0b4114/image.png)
> **글 등록**
InsertController
package com.sample.app.controller.post;
import com.sample.app.dao.PostDao;
import com.sample.app.vo.Post;
import com.sample.model2.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
/*
요청 URI
/post/insert.hta
요청 파라미터
title
content
반환값
redirect:/app/user/loginform.hta?error=deny
redirect:list.hta
요청처리 내용
세션에서 로그인된 사용자 정보를 조회한다.
사용자정보가 존재하지 않으면 로그인폼을 요청하는 재요청 URL("redirect:/app/user/loginform.hta?error=deny")을 반환한다.
요청파라미터값을 조회한다.
Post객체를 생성해서 제목, 로그인한 사용자 아이디, 내용을 저장한다.
PostDao의 insertPoset(Post post)를 호출해서 게시글 정보를 저장시킨다.
게시글 목록을 요청하는 재요청 URL을 반환한다.
*/
public class InsertController implements Controller {
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 세션에서 로그인된 사용자정보를 조회한다.
// 로그인된 사용자정보가 존재하지 않으면 로그인폼을 요청하는 재요청 URL을 반환한다.
HttpSession session = request.getSession();
if (session.getAttribute("loginUserId") == null) {
return "redirect:/app/user/loginform.hta?error=deny";
}
String loginUserId = (String) session.getAttribute("loginUserId");
// 요청 파라미터값을 조회한다.
String title = request.getParameter("title");
String content = request.getParameter("content");
// Post객체를 생성해서 게시글 제목, 내용, 로그인한 사용자 아이디를 대입한다.
Post post = new Post();
post.setTitle(title);
post.setUserId(loginUserId);
post.setContent(content);
// PostDao객체의 insertPost(Post post) 메소드를 호출해서 게시글 정보를 저장한다.
PostDao postDao = PostDao.getInstance();
postDao.insertPost(post);
// 게시글 목록을 요청하는 재요청 URL을 반환한다.
return "redirect:list.hta";
}
}
> 실행결과
> **리스트 출력**
ListController
package com.sample.app.controller.post;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sample.app.dao.PostDao;
import com.sample.app.dto.PostListDto;
import com.sample.model2.Controller;
import com.sample.util.Pagination;
import com.sample.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/*
* 요청 URI
* /post//list.hta
* /post/list.hta?page=2
* 요청파라미터
* page
* 반환값
* post/list.jsp
* 요청처리 내용
* 요청파라미터에서 페이지번호를 조회한다.
총 데이터갯수를 조회한다.
페이징처리 처리를 위한 Pagination객체 생성
게시글 목록 조회범위를 계산해서 Map 객체에 저장한다.
PostDao의 getPosts(Map<String, Object> param) 메소드를 실행해서 게시글을 조회한다.
*
조회된 게시글목록을 요청객체의 속성으로 저장한다.
생성된 Pagination 객첼르 요청객체의 속성으로 저장한다.
*
게시글 목록정보와 페이징정보를 표시하는 jsp 페이지를 반환한다.
/
public class ListController implements Controller {
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 요청 파라미터값을 조회한다.
int currentPage = StringUtils.stringToInt(request.getParameter("page"), 1);
PostDao postDao = PostDao.getInstance();
// 총 게시글 갯수를 조회하고, Pagination객체를 생성한다.
int totalRows = postDao.getTotalRows();
Pagination pagination = new Pagination(currentPage, totalRows);
// 게시글 목록 조회에 필요한 정보를 저장하는 Map 객체를 생성한다.
Map<String, Object> param = new HashMap<>();
param.put("begin", pagination.getBegin());
param.put("end", pagination.getEnd());
// PostDao의 getPosts(Map<String, Object> param)메소드를 실행시켜서 게시글 목록을 조회한다.
List<PostListDto> postListDtos = postDao.getPosts(param);
// 요청객체의 속성으로 게시글 목록정보를 저장한다.
request.setAttribute("posts", postListDtos);
// 요청객체의 속성으로 페이징처리 정보를 저장한다.
request.setAttribute("pagination", postListDtos);
// 내부이동할 jsp 페이지를 반환한다.
return "post/list.jsp";
}
}
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
<title>application</title>
</head>
<body>
<%@ include file="../common/navbar.jsp" %>
<div class="container my-3">
<div class="row mb-3">
<div class="col">
<h1 class="fs-4 border p-2 bg-light">게시글 목록</h1>
</div>
</div>
<div class="row mb-3">
<div class="col">
<p>게시글 목록을 확인하세요.
<c:if test="${not empty loginUserId }">
<a href="form.hta" class="btn btn-primary btn-sm float-end">새 글쓰기</a>
</c:if>
</p>
<table class="table table-sm">
<colgroup>
<col width="10%">
<col width="*">
<col width="10%">
<col width="10%">
<col width="7%">
<col width="15%">
</colgroup>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th class="text-center">작성자</th>
<th class="text-center">조회수</th>
<th class="text-center">댓글수</th>
<th class="text-center">등록일</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test="${empty posts }">
<tr>
<td colspan="6" class="text-center">게시글이 없습니다.</td>
</tr>
</c:when>
<c:otherwise>
<%--
${posts}는 List<PostListDto> 객체가 조회된다.
<c:forEach />는 List<PostListDto> 객체에 저장된 PostListDto의 갯수만큼 컨텐츠를 반복해서 출력한다.
var="dto"는 List<PostListDto>객체에서 순서대로 하나씩 조회한 PostListDto 객체가 대입된다.
${dto.no }는 PostListDto객체의 멤버변수 no에 저장된 값을 출력한다.
--%>
<c:forEach var="dto" items="${posts }">
<tr>
<td>${dto.no }</td>
<td><a href="detail.hta?postNo=${dto.no }" class="text-decoration-none">${dto.title }</a></td>
<td class="text-center">${dto.userName }</td>
<td class="text-center">${dto.readCount }</td>
<td class="text-center">${dto.commentCount }</td>
<td class="text-center"><fmt:formatDate value="${dto.createdDate }" /></td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</tbody>
</table>
<nav>
<ul class="pagination pagination-sm justify-content-center">
<li class="page-item"><a class="page-link" href="list.hta?page=1">이전</a></li>
<li class="page-item"><a class="page-link" href="list.hta?page=1">1</a></li>
<li class="page-item"><a class="page-link active" href="list.hta?page=2">2</a></li>
<li class="page-item"><a class="page-link" href="list.hta?page=3">3</a></li>
<li class="page-item"><a class="page-link" href="list.hta?page=4">4</a></li>
<li class="page-item"><a class="page-link" href="list.hta?page=2">다음</a></li>
</ul>
</nav>
</div>
</div>
```
실행결과
list.hta의 구조
insert.hta의 잘못된 구조
list와 insert의 차이
- list는 조회이므로 데이터가 많이 필요하지 않기 때문에 redirect가 필요 없다.
- insert는 추가 /변경 /삭제 작업 중 하나이기 때문에 따로 또 조회를 하는 작업이 더 필요하다. 그렇기 때문에 redirect가 필요하다.
DetailController
package com.sample.app.controller.post;
import java.util.List;
import com.sample.app.dao.CommentDao;
import com.sample.app.dao.PostDao;
import com.sample.app.dto.CommentListDto;
import com.sample.app.dto.PostDetailDto;
import com.sample.model2.Controller;
import com.sample.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/*
* 요청 URI
* /post/detail.hta
* 요청 파라미터
* postNo
* 반환값
* post/detail.jsp
* 요청처리 내용
* 요청파라미터로 전달된 게시글번호를 조회한다.
* PostDao객체의 getPostByNo(int postNo)를 실행해서 게시글 번호에 해당하는 게시글 상세정보를 조회한다.
* CommentDao객체의 getCommentsByPostNo(int postNo)를 실행해서 게시글 번호에 해당하는 댓글목록을 조회한다.
* 요청객체의 속성으로 조회된 게시글 정보를 저장한다.
* 요청객체의 속성으로 조회된 댓글목록 정보를 저장한다.
*
* 게시글 상세정보와 댓글 목록을 표시하는 jsp 페이지를 반환한다.
*/
public class DetailController implements Controller {
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 요청파라미터 값을 조회한다.
int postNo = StringUtils.stringToInt(request.getParameter("postNo"));
PostDao postDao = PostDao.getInstance();
CommentDao commentDao = CommentDao.getInstance();
// 게시글 번호에 해당하는 게시글 상세정보를 조회한다.
PostDetailDto dto = postDao.getPostByNo(postNo);
// 게시글 번호에 해당하는 댓글 목록 정보를 조회한다.
List<CommentListDto> commentListDtos = commentDao.getCommentsByPostNo(postNo);
// 요청객체의 속성으로 게시글정보의 댓글목록정보를 저장한다.
request.setAttribute("post", dto);
request.setAttribute("comments", commentListDtos);
return "post/detail.jsp";
}
}
detail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
<title>application</title>
</head>
<body>
<%@ include file="../common/navbar.jsp" %>
<div class="container my-3">
<div class="row mb-3">
<div class="col">
<h1 class="fs-4 border p-2 bg-light">게시글 상세정보</h1>
</div>
</div>
<div class="row mb-3">
<div class="col">
<p>게시글 상세정보를 확인하세요. <button class="btn btn-primary btn-sm float-end" data-bs-toggle="modal" data-bs-target="#modal-form-comments">댓글 쓰기</button></p>
<table class="table table-sm">
<colgroup>
<col width="15%">
<col width="35%">
<col width="15%">
<col width="35%">
</colgroup>
<tbody>
<tr>
<th>제목</th>
<td colspan="3">${post.title }</td>
</tr>
<tr>
<th>번호</th>
<td>${post.no }</td>
<th>작성자</th>
<td>${post.userName }</td>
</tr>
<tr>
<th>등록일</th>
<td><fmt:formatDate value="${post.createdDate }" /></td>
<th>최종수정일</th>
<td><fmt:formatDate value="${post.updatedDate }" /></td>
</tr>
<tr>
<th>조회수</th>
<td><fmt:formatNumber value="${post.readCount }" /></td>
<th>댓글수</th>
<td><fmt:formatNumber value="${post.commentCount }" /></td>
</tr>
<%--
그냥 ${post.content}로 했을 때는 혹시나 누군가가 글 등록 시 나쁜 짓을 할 수 있으므로
밑의 c:out 방식이나 textarea 방식으로 해야 한다.
그런데 그냥 c:out만 하면 들여쓰기나 줄 바꿈이 안되므로
<tr>
<th>내용</th>
<td colspan="3"><textarea rows="5" class="form-control" readonly="readonly">${post.content }</textarea></td>
</tr>
--%>
<tr>
<th>내용</th>
<td colspan="3"><c:out value="${post.content }" /></td>
</tr>
<%--
<tr>
<th>내용</th>
<td colspan="3"><script><br>alert('안녕')<br></script></td>
</tr>
--%>
</tbody>
</table>
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<a href="modifyform.hta?postNo=100" class="btn btn-warning btn-sm">수정</a>
<a href="delete.hta?postNo=100" class="btn btn-danger btn-sm">삭제</a>
</div>
</div>
<div class="row mb-3">
<div class="col">
<div class="border p-3">
<p class="mb-0 small">
<span>홍길동</span> <span class="text-muted float-end">2022-12-11</span>
</p>
<p>
댓글내용입니다. 댓글 내용입니다.
<a href="deleteComment.hta?postNo=100&commentNo=1000" class="float-end"><i class="bi bi-trash-fill text-danger"></i></a>
</p>
</div>
</div>
</div>
</div>
<!-- 댓글 등록폼 -->
<div class="modal" tabindex="-1" id="modal-form-comments">
<div class="modal-dialog">
<form id="form-add-depts" class="p-3" method="post" action="insertComment.hta">
<input type="hidden" name="postNo" value="100">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">댓글 등록폼</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row mb-2">
<div class="col-sm-12">
<textarea class="form-control" rows="3" name="content"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-xs" data-bs-dismiss="modal">닫기</button>
<button type="submit" class="btn btn-primary btn-xs">등록</button>
</div>
</div>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>
실행결과 (${post.content} 로 실행 시)
실행결과 (<c:out value="${post.content }" /> 로 실행 시)
- 그냥 ${post.content}로 실행시 혹시나 누군가가 나쁜짓을 하기 위해 위의 결과처럼 실행된다.
- 그래서 아래처럼 c:out방식을 이용하여 실행 시 나쁜 짓을 했어도 그대로 내용으로 화면에 출력된다.