Servlet
, JSP
, JDBC
와 h2
를 이용하여 간단한 게시판 프로그램을 구현해보자
해당 프로젝트는 클론 코딩으로 진행되었다
웹 페이지의 기본적인 기능이 대부분 모여있는 것이 게시판이라고 할 수 있다. 기본적인 웹 브라우저와 서버의 동작을 해당 프로젝트를 통해 이해하고, 가장 많이 사용하는 웹 페이지의 기본 기능을 구현하면서 각 코드별 어떻게 상호작용하는 지 이해하는 것이 해당 프로젝트의 목표이다.
사용자가 로그인을 하지 않았다면 게시판을 열람만 할 수 있도록 하고
사용자가 회원가입 및 로그인을 했다면 게시판에 게시물을 올리고, 삭제하고, 수정할 수 있게 한다.
또한 사용자가 회원가입을 할 때 일반 회원과 관리자 회원을 구분하도록 하여, 관리자만 게시물을 삭제할 수 있도록 설정한다. 또한 사용자가 원할 때 언제든지 로그아웃을 할 수 있도록 로그아웃 버튼도 생성한다.
웹 페이지의 상단과 하단은 앞서 배운 header
와 footer
로 작성한다.
로그인하기 전이라면 언제든지 메인 화면으로 돌아올 수 있는 home 버튼, 회원 가입 버튼, 로그인 버튼으로 구성한다. 만약 로그인을 했다면 언제든지 메인 화면으로 돌아올 수 있는 home 버튼, 글 목록 및 글 등록 버튼, 로그아웃 버튼으로 구성한다.
웹사이트의 기본 정보를 나열한다. 대게 흔히 쓰는 사이트들을 들어가서 제일 밑으로 내리면 기업명, 기업 대표 번호, 업체명 주소 등 여러 정보들이 나오는 데 이런 정보를 뿌려주면 된다. 간단한 프로젝트 이므로 가상의 문의 전화번호 및 주소를 적어둔다.
게시판 목록 형태를 표로 구성한다. 행의 가장 상단인 각 속성들의 제목은 번호, 제목, 작성자, 등록일, 조회수로 나타낸다. 만약 게시판에 게시물을 아무도 등록하지 않았다면 해당 속성들만 나열되어있을 것이다.
게시물 등록 순서대로 번호가 Count
되어 게시물의 개수를 파악할 수 있다. 그리고 제목과 작성자는 한글을 입력해도 깨지지 않도록 encoding
처리를 해준다. 등록일은 시스템 서버의 년/월/일
이 자동으로 기입되도록 하며, 조회수는 기본적으로 null
이 아닌 0
을 세팅해준다.
게시물의 제목 및 내용 중에서 원하는 키워드가 있을 때 사용자가 검색하여 편리하게 찾을 수 있도록 한다. 검색은 제목이나 내용 중 하나를 선택한 후에 해당 조건에 맞는 내용을 검색할 수 있다. 예를 들어, 제목으로 선택한 후 ep
라고 검색했다면 제목이 yeppi
, yep0101
, eppizzz
등 ep
가 들어가는 모든 단어를 검색할 것이다.
게시판 화면에서 원하는 게시물을 클릭하면 해당 게시물의 제목, 작성자, 내용, 등록일, 조회수의 내용을 상세하게 보여준다. 언제든지 글 수정을 할 수 있도록 글 수정 버튼을 하단에 배치한다. 그러나 글 삭제 버튼은 관리자 권한으로 로그인하였을 때만 생성되도록 한다.
해당 토이프로젝트 소스코드가 push 되어 있는 yeppi의 github 입니다.
해당 프로젝트는 Servlet, JSP 를 중점으로 진행하였습니다.
JDBC 연결과 회원 가입 및 로그인의 JAVA 코드는 [ToyProject] JDBC-Java-h2 회원 관리 프로그램을 참고해주세요.
CREATE TABLE USERS(
ID VARCHAR2(8) PRIMARY KEY,
PASSWORD VARCHAR2(8),
NAME VARCHAR2(20),
ROLE VARCHAR2(5)
);
INSERT INTO USERS VALUES('test', 'test', 'yeppi', 'Admin');
CREATE TABLE BOARD(
SEQ NUMBER(5) PRIMARY KEY,
TITLE VARCHAR2(200),
WRITER VARCHAR2(20),
CONTENT VARCHAR2(2000),
REGDATE DATE DEFAULT CURRENT_DATE,
CNT NUMBER(5) DEFAULT 0
);
<%@ page contentType="text/html; charset=UTF-8"%>
<br>
<hr>
<br>
<center>
문의 전화 : 031-1234-1234<br> 주소 : 경기도 짱구대로 1234
</center>
</body>
</html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시판 프로그램</title>
</head>
<body>
<hr>
<a href="index.jsp">Home</a>
<%
UserVO user = (UserVO) session.getAttribute("user");
%>
<% if(user == null) { %>
<a href="insertUser.jsp">회원가입</a>
<a href="login.jsp">로그인</a>
<% } else { %>
<a href="getBoardList.do">글목록</a>
<a href="insertBoard.jsp">글등록</a>
<a href="logout.do">로그아웃</a>
<% } %>
<hr>
<br>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="../layout/header.jsp" %>
<center>
<form action="insertUser.do" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange">아이디</td>
<td><input type="text" name="id" size="10" /></td>
</tr>
<tr>
<td bgcolor="orange">비밀번호</td>
<td><input type="password" name="password" size="12" /></td>
</tr>
<tr>
<td bgcolor="orange">이름</td>
<td><input type="text" name="name" size="30" /></td>
</tr>
<tr>
<td bgcolor="orange">권한</td>
<td>
<input type="radio" name="role" value="USER" checked="checked" />일반회원
<input type="radio" name="role" value="ADMIN" />관리자
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="회원가입" />
</td>
</tr>
</table>
</form>
</center>
<%@ include file="../layout/footer.jsp" %>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="../layout/header.jsp" %>
<center>
<form action="login.do" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange">아이디</td>
<td><input type="text" name="id" size="20" /></td>
</tr>
<tr>
<td bgcolor="orange">비밀번호</td>
<td><input type="password" name="password" size="20" /></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="로그인" />
</td>
</tr>
</table>
</form>
</center>
<%@ include file="../layout/footer.jsp" %>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="../layout/header.jsp" %>
<center>
<form action="insertBoard.do" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td><td align="left">
<input type="text" name="title"/></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td><td align="left">
<input type="text" name="writer" size="10"/></td>
</tr>
<tr>
<td bgcolor="orange">내용</td><td align="left">
<textarea name="content" cols="40" rows="10"></textarea></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글등록"/></td>
</tr>
</table>
</form>
</center>
<%@ include file="../layout/footer.jsp" %>
<%@page contentType="text/html; charset=UTF-8" %>
<%@include file="../layout/header.jsp" %>
<center>
<!-- 검색 화면 시작 -->
<form action="getBoardList.do" method="post">
<table border="1" cellpadding="0" cellspacing="0" width="700">
<tr>
<td align="right">
<select name="searchCondition">
<option value="TITLE" <c:if test="${condition == 'TITLE' }">selected</c:if>>제목
<option value="CONTENT" <c:if test="${condition == 'CONTENT' }">selected</c:if>>내용
</select>
<input type="text" name="searchKeyword" value="${keyword }" />
<input type="submit" value="검색" />
</td>
</tr>
</table>
</form>
<!-- 검색 화면 종료 -->
<table border="1" cellpadding="0" cellspacing="0" width="700">
<tr>
<th bgcolor="orange" width="100">번호</th>
<th bgcolor="orange" width="200">제목</th>
<th bgcolor="orange" width="150">작성자</th>
<th bgcolor="orange" width="150">등록일</th>
<th bgcolor="orange" width="100">조회수</th>
</tr>
<c:forEach var="board" items="${boardList }">
<tr>
<td>${board.seq }</td>
<td align="left"><a href="getBoard.do?seq=${board.seq }">${board.title }</a></td>
<td>${board.writer }</td>
<td>${board.regDate }</td>
<td>${board.cnt }</td>
</tr>
</c:forEach>
</table>
</center>
<%@include file="../layout/footer.jsp" %>
<%@page contentType="text/html; charset=UTF-8" %>
<%@ include file="../layout/header.jsp" %>
<center>
<form action="updateBoard.do" method="post">
<input name="seq" type="hidden" value="${board.seq }"/>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input name="title" type="text" value="${board.title }"/></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">${board.writer }</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left">
<textarea name="content" cols="40" rows="10">${board.content }</textarea>
</td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left">${board.regDate }</td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left">${board.cnt }</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글 수정"/>
</td>
</tr>
</table>
</form>
<br>
<c:if test="${user.role == 'ADMIN' }">
<a href="deleteBoard.do?seq=${board.seq }">글삭제</a>
</c:if>
</center>
<%@ include file="../layout/footer.jsp" %>
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public DispatcherServlet() {}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 사용자 요청 path 정보를 추출
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/"));
System.out.println("요청 path : " + path);
// 추출한 path 정보에 따라서 요청을 분기처리
if(path.equals("/login.do")) {
System.out.println("로그인 기능 처리");
// 사용자 입력정보 추출
String id = request.getParameter("id");
String password = request.getParameter("password");
// DB 연동 처리
UserVO vo = new UserVO();
vo.setId(id);
UserDAO dao = new UserDAO();
UserVO user = dao.getUser(vo);
// 화면 이동
HttpSession session = request.getSession();
if(user != null) {
if(user.getPassword().equals(password)) {
// 상태 정보를 세션에 저장
session.setAttribute("user", user);
// 글 목록 화면으로 이동
response.sendRedirect("/getBoardList.do");
} else {
response.sendRedirect("/login.jsp");
}
} else {
response.sendRedirect("/insertUser.do");
}
} else if(path.equals("/logout.do")) {
System.out.println("로그아웃 기능 처리");
HttpSession session = request.getSession();
session.invalidate();
response.sendRedirect("/");
} else if(path.equals("/insertUser.do")) {
System.out.println("회원 등록 기능 처리");
// 사용자 입력정보 추출
String id = request.getParameter("id");
String password = request.getParameter("password");
String name = request.getParameter("name");
String role = request.getParameter("role");
// DB 연동 처리
UserVO vo = new UserVO();
vo.setId(id);
vo.setPassword(password);
vo.setName(name);
vo.setRole(role);
UserDAO dao = new UserDAO();
dao.insertUser(vo);
// 화면 이동
RequestDispatcher dispatcher = request.getRequestDispatcher("/getBoardList.do");
dispatcher.forward(request, response);
} else if(path.equals("/insertBoard.do")) {
System.out.println("글 등록 기능 처리");
// 사용자 입력정보 추출
String title = request.getParameter("title");
String writer = request.getParameter("writer");
String content = request.getParameter("content");
// DB 연동 처리
BoardVO vo = new BoardVO();
vo.setTitle(title);
vo.setWriter(writer);
vo.setContent(content);
BoardDAO boardDAO = new BoardDAO();
boardDAO.insertBoard(vo);
// 페이지 네비게이션
RequestDispatcher dispatcher = request.getRequestDispatcher("/getBoardList.do");
dispatcher.forward(request, response);
} else if(path.equals("/updateBoard.do")) {
System.out.println("글 수정 기능 처리");
// 사용자 입력정보 추출
String title = request.getParameter("title");
String seq = request.getParameter("seq");
String content = request.getParameter("content");
// DB 연동 처리
BoardVO vo = new BoardVO();
vo.setTitle(title);
vo.setSeq(Integer.parseInt(seq));
vo.setContent(content);
BoardDAO boardDAO = new BoardDAO();
boardDAO.updateBoard(vo);
// 페이지 네비게이션
RequestDispatcher dispatcher = request.getRequestDispatcher("/getBoardList.do");
dispatcher.forward(request, response);
} else if(path.equals("/deleteBoard.do")) {
System.out.println("글 삭제 기능 처리");
// 사용자 입력정보 추출
String seq = request.getParameter("seq");
// DB 연동 처리
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
BoardDAO boardDAO = new BoardDAO();
boardDAO.deleteBoard(vo);
// 페이지 네비게이션
RequestDispatcher dispatcher = request.getRequestDispatcher("/getBoardList.do");
dispatcher.forward(request, response);
} else if(path.equals("/getBoard.do")) {
System.out.println("글 상세 조회 기능 처리");
// 사용자 입력정보 추출
String seq = request.getParameter("seq");
// DB 연동 처리
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
BoardDAO boardDAO = new BoardDAO();
BoardVO board = boardDAO.getBoard(vo);
// 화면 이동
// 검색 결과를 request 객체에 등록하고 글 목록 화면(getBoardList.jsp)으로 이동(forwarding)
request.setAttribute("board", board);
RequestDispatcher dispatcher = request.getRequestDispatcher("getBoard.jsp");
dispatcher.forward(request, response);
} else if(path.equals("/getBoardList.do")) {
System.out.println("글 목록 검색 기능 처리");
// 사용자 입력정보 추출
String searchCondition = request.getParameter("searchCondition");
String searchKeyword = request.getParameter("searchKeyword");
// Null Check
if(searchCondition == null) searchCondition = "TITLE";
if(searchKeyword == null) searchKeyword = "";
request.setAttribute("condition", searchCondition);
request.setAttribute("keyword", searchKeyword);
// DB 연동 처리
BoardVO vo = new BoardVO();
vo.setSearchCondition(searchCondition);
vo.setSearchKeyword(searchKeyword);
BoardDAO dao = new BoardDAO();
List<BoardVO> boardList = dao.getBoardList(vo);
// 화면 이동
// 검색 결과를 request 객체에 등록하고 글 목록 화면(getBoardList.jsp)으로 이동(forwarding)
request.setAttribute("boardList", boardList);
RequestDispatcher dispatcher = request.getRequestDispatcher("getBoardList.jsp");
dispatcher.forward(request, response);
} else {
System.out.println("요청 URL이 잘못되었습니다.");
}
}
}
처음에는 일반 회원 권한으로 로그인하여 글만 수정하고, 두번째는 관리자 권한으로 로그인하여 글 삭제합니다.
그리고 제목과 내용 키워드 중 제목에서 sp 에 해당하는 게시물을 찾아내는 영상입니다.
그 외 다른 기능들은 github 코드를 참고해주세요
클론 코딩이라서 지난번 프로젝트보다는 비교적 수월했다. 웹과 서버의 동작 방식이 여러개가 있고 각 강단점을 잘 파악할 수 있는 계기가 되었다. Servlet
과 JSP
뿐만아니라 EL
와 JSTL
을 사용하여 더 가벼운 코드를 작성하는 것이 무엇인지 잘 알 수 있었다. 특히 Dispatch
클래스를 활용할 때 여러 동작을 한번에 관리할 수 있어서 매우 편하다고 생각했다. xml
, html
파일에 의존적이지 않고 각 기능마다 필요한 페이지를 동적인 형태로 만든다는 것이 무슨 의미인지 잘 와닿지 않았었는 데 JSP
를 많이 이용하면서 많은 개념을 이해할 수 있었다.