#1 JSP Project 07(Blog) - 글쓰기/메인화면목록
210630 유투버 '데어프로그래밍'님 강의 참조
saveForm/list.jsp 디자인하기 (글쓰기에 썸머노트 활용!), 필요 코드 넣기--> 글쓰기/메인화면(목록)
- 썸머노트 적용전에 테스트하기
- 헤더에 섬머노트용 코드 추가
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script>
- 실제 적용 코드
*saveForm
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="../layout/header.jsp" %>
<div class="container">
<form action="/blog/board?cmd=save" method="POST">
<input type = "hidden" name="userId" value="${sessionScope.principal.id}"/>
<div class="form-group">
<label for="title">Title:</label>
<input type="text" class="form-control" placeholder="title" id="title" name="title">
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea id = "summernote" class="form-control" rows="5" id="content" name="content"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<script>
$('#summernote').summernote({
placeholder: 'You can write here.',
tabsize: 2,
height: 400
});
</script>
</body>
</html>
*sessionScope는 로그인시 내가 세션을 principal로 줬기때문에 거기에 대한 값을 찾는 기능
*list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="../layout/header.jsp" %>
<div class="container">
<div class="m-2">
<form class="form-inline d-flex justify-content-end" action="/blog/board">
<input type="hidden" name="cmd" value="search" />
<input type="hidden" name="page" value="0" />
<input type="text" name="keyword" class="form-control mr-sm-2" placeholder="Search">
<button class="btn btn-primary m-1">검색</button>
</form>
</div>
<div class="progress col-md-12 m-2">
<div class="progress-bar" style="width: 70%"></div>
</div>
<div class="card col-md-12 m-2">
<div class="card-body">
<h4 class="card-title">제목</h4>
<a href="#" class="btn btn-primary">상세보기</a>
</div>
</div>
<br />
<ul class="pagination justify-content-center">
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</div>
</body>
</html>
글쓰기/메인화면 완료하기
-BoardController- BoardSerive - SaveReqDto - BoardDao 코딩
@Data
public class SaveReqDto {
private int userId;
private String title;
private String content;
}
@WebServlet("/board")
public class BoardController extends HttpServlet {
...
protected void doProcess(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String cmd = request.getParameter("cmd");
BoardService boardService = new BoardService();
// http://localhost8080/blog/board?cmd=saveForm
HttpSession session = request.getSession(); // 글쓰기는 유저만 가능하도록 세션 부여!
// 서비스 호출
if (cmd.equals("saveForm")) {
User principal = (User) session.getAttribute("principal");
if (principal != null) {
response.sendRedirect("board/saveForm.jsp");
} else {
response.sendRedirect("user/loginForm.jsp");
}
} else if (cmd.equals("save")) {
int userId = Integer.parseInt(request.getParameter("userId"));
String title = request.getParameter("title");
String content = request.getParameter("content");
SaveReqDto dto = new SaveReqDto();
dto.setUserId(userId);
dto.setTitle(title);
dto.setContent(content);
boardService.글쓰기(dto);
}
}
}
public class BoardService {
private BoardDao boardDao;
public BoardService() {
boardDao = new BoardDao();
}
public int 글쓰기(SaveReqDto dto) {
return boardDao.save(dto);
}
}
public class BoardDao {
public int save(SaveReqDto dto) { // 회원가입
// Admin은 DB에서 따로 변경하면 쉽다.
// 1) 쿼리문 준비
String sql = "INSERT INTO board (userid, title, content, createDate) VALUES(?,?,?,now())";
PreparedStatement pstmt = null;
// 2) DB커넥션 연결
Connection conn = DBConn.getConnection(); //
try {
// 3)
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, dto.getUserId());
pstmt.setString(2, dto.getTitle());
pstmt.setString(3, dto.getContent());
int result = pstmt.executeUpdate();
return result;
} catch (Exception e) {
e.printStackTrace();
} finally { // try-catch 상관없이 무조건 실행.
// 코드가 길어지니 DBconn에서 함수로 따로 관리
DBConn.close(conn, pstmt);
}
return -1;
}
}
글쓰기에 제한 걸기
- 글쓰기는 회원만 가능하도록하고 중요한 두군데를 꼭 막아야한다 1) Controller(세션체크) 2)각각의 페이지(예:saveForm.jsp)를 직접 url요청하면 막기
- 필터를 만들어서 각페이지에 매번 코딩하는것보다 필터가 처리하도록 한다
- web.xml에 필터 추가 (*톰켓이 뻗는 경우가 생기면, 데어프로그래밍님의 깃허브에서 코드 복붙하자!)
- ForbiddenUrlConfig 세팅
public class ForbiddenUrlConfig implements Filter{
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if(request.getRequestURI().equals("/blog/") || request.getRequestURI().equals("/blog/index.jsp")) {
chain.doFilter(request, response);
}else {
PrintWriter out = response.getWriter();
out.print("Wrong connection");
out.flush();
}
}
}
*현재 이렇게만 세팅하면 모든 jsp페이지가 락이 걸린다. (sendRedirect는 무조건 필터를 타기 때문이다)
*해결방법은 RequestDispatcher! -> RequestDispatcher는 톰켓이 res,resp을 재사용하여 내부적으로 redirect 시킴
- index.jsp 수정
<%
RequestDispatcher dis =
request.getRequestDispatcher(""board?cmd=list"");
dis.forward(request, response); // 톰켓이 생성하는 request와 response를 재사용한다. 다시 접근하는게 아니라 내부적으로 움직인다는 뜻.
%>
- Controller에 index.jsp 로 리다이렉트 제외하고 모든것을 index.jsp 처럼 수정
글목록 보기
- BoardController 세팅
@WebServlet("/board")
public class BoardController extends HttpServlet {
...
} else if (cmd.equals("list")) { //글목록 들고오기
List<Board> boards = boardService.글목록보기(); //DB에서 받아온 데이터
request.setAttribute("boards", boards); //데이터를 담아서 REQ를 유지한체 REQD로 처리
RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
dis.forward(request, response);
}
- BoardService 세팅
public class BoardService {
..
public List<Board> 글목록보기(){
return boardDao.findAll();
}
}
-BoardDao 세팅
public class BoardDao {
public List<Board> findAll() { // User의 select문 가져오자
// 1) 쿼리문 준비
String sql = "SELECT * FROM board ORDER BY id DESC";
PreparedStatement pstmt = null;
// 2) DB커넥션 연결
Connection conn = DBConn.getConnection();
ResultSet rs = null;
List<Board> boards = new ArrayList<>();
try {
// 3)
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) { //rs,next()는 DB에서 커서를 이동
Board board = Board.builder()
.id(rs.getInt("id"))
.title(rs.getString("title"))
.content(rs.getString("content"))
.readCount(rs.getInt("readCount"))
.userId(rs.getInt("userId"))
.createDate(rs.getTimestamp("createDate"))
.build();
boards.add(board);
}
return boards;
....
- 현재까지세팅으로 들고온 데이터를 list.jsp에 뿌리기
*글목록 페이징 전 중요포인트 세션
- 세션은 클라이언트가 어떤 요청시에 그 요청을 응답하면서 서버에서 만들어서 배부(예:로그인)
- 세션은 클라이언트가 특정 요청시에 배부받았던 세션을 계속해서 들고 있음, 즉 그 세션을 DB에서 매칭을 하면서 이 클라이언트가 어느상태인지를 파악함
- 세션을 통해 STATELESS -> STATEFUL 처럼 운용이 가능함
글목록 페이징하기
- 이전/다음을 누르면 작동하도록 list.jsp에 코드 수정
- 페이징을 받을 수 있도록 BoardController에 "list" 코드 추가
@WebServlet("/board")
public class BoardController extends HttpServlet {
...
} else if (cmd.equals("list")) {
- 메인화면인 index.jsp에 초기 페이징 코드 수정
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
RequestDispatcher dis =
request.getRequestDispatcher("board?cmd=list&page=0");
dis.forward(request, response); // 톰켓이 생성하는 request와 response를 재사용한다. 다시 접근하는게 아니라 내부적으로 움직인다는 뜻.
%>
- list함수를 통해 페이지를 받을 수 있도록 BoardService의 글목록보기() 함수 수정
public class BoardService {
...
public List<Board> 글목록보기(int page){
return boardDao.findAll(page);
}
}
- BoardDao에서 페이징 수를 위한 코드 삽입
public class BoardDao {
public int count() {
String sql = "SELECT count(*) FROM board";
Connection conn = DBConn.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if(rs.next()) {
return rs.getInt(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 무조건 실행
DBConn.close(conn, pstmt, rs);
}
return -1;
}
- list.jsp에 jstl의 param이라는 함수를 통해 페이지 수를 받아와서 그대로 적용
<ul class="pagination justify-content-center">
<c:choose>
<c:when test="${param.page == 0}">
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page-1}">Previous</a></li>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${lastPage == param.page}">
<li class="page-item disabled"><a class="page-link" href="#">Next</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page+1}">Next</a></li>
</c:otherwise>
</c:choose>
</ul>
- BoardController에 페이지가 없으면 버튼이 비활성화 되도록 세팅
} else if (cmd.equals("list")) { //글목록 들고오기
int page = Integer.parseInt(request.getParameter("page")); // 최초 : 0, Next : 1, Next: 2
List<Board> boards = boardService.글목록보기(page);
request.setAttribute("boards", boards);
// 계산 (전체 데이터수랑 한페이지몇개 - 총 몇페이지 나와야되는 계산) 3page라면 page의 맥스값은 2
// page == 2가 되는 순간 isEnd = true
// request.setAttribute("isEnd", true);
int boardCount = boardService.글개수();
int lastPage = (boardCount-1)/4; // 2/4 = 0, 3/4 = 0, 4/4 = 1, 9/4 = 2 ( 0page, 1page, 2page)
request.setAttribute("lastPage", lastPage);
RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
dis.forward(request, response);
}
}
}
- 페이지 계산을하는 서비스
public class BoardService {
...
public int 글개수() {
return boardDao.count();
}
- 퍼센트 계산해서 페이징 바 표시 (컨트롤러/list.jsp)
*BoardController
double currentPosition = (double) page/(lastPage)*100;
request.setAttribute("currentPosition", currentPosition);
**중요포인트! 페이징수 및 퍼센트 수는 반드시 setAttribute로 뿌려줘야한다!!
- <script> 제목에 쓰면 홈페이지 뻗는 현상 처리 -->Board 에 간단 코드 추가
public class Board {
...
public String getTitle() {
return title.replaceAll("<","<").replaceAll(">", ">");
}
}