BoardDAO
클래스에 전체 학생의 점수를 조회할 수 있는 메서드를 만든다.
BoardDAO.java
// 점수 목록을 조회하는 메서드
public List<ScoreVO> selectAll() {
List<ScoreVO> scoreList = new ArrayList<>();
String sql = "SELECT * FROM scores ORDER BY id ASC";
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
ScoreVO vo = new ScoreVO(
rs.getInt("id"),
rs.getString("name"),
rs.getInt("kor"),
rs.getInt("eng"),
rs.getInt("math"),
rs.getInt("total"),
rs.getDouble("average")
);
scoreList.add(vo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
rs.close();
pstmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return scoreList;
}
전체 학생의 데이터를 가져와 객체로 포장하여 리스트로 들고 있어야 하므로 List<ScoreVO>
형태로 scoreList
를 선언한다.
그리고 sql문과 커넥션부터 리절트셋까지 준비를 해 둔다.
리절트셋의 next()
메서드를 사용하여 다음 요소가 있는지 체크하고 있는 경우 rs
로부터 객체의 필드를 채우기 위한 데이터를 불러온다. 여기서 이름을 지정할 때에는 DB 테이블 에 지정한 이름을 따른다.
모든 데이터를 얻어왔으면 scoreList
에 vo
객체를 추가한다.
finally
구문에선 사용했던 conn, pstmt, rs
에 대해 close()
를 진행한다.
최종적으로 모든 구문이 실행 완료되었다면 scoreList
를 반환해준다.
이제 score_list.jsp
를 생성하고, 해당 페이지에서 학생 점수 리스트를 가져와 사용자에게 보여줄 수 있도록 해 보자.
score_list.jsp
<%@page import="java.util.List"%>
<%@ page import="kr.co.jsp.score.model.ScoreVO" %>
<%@ page import="kr.co.jsp.score.model.ScoreDAO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%--
DB에서 데이터를 가져와야 함
DB 테이블에 존재하는 모든 학생의 점수를 현재 jsp 파일로 갖고 와서
테이블 태그로 출력하기. -> DAO를 이용하자.
--%>
<%
ScoreDAO dao = ScoreDAO.getInstance();
List<ScoreVO> scoreList = dao.selectAll();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>학생들의 전체 성적 조회</h1>
<form action="search.jsp">
<input type="text" name="keyword" placeholder="검색할 이름을 입력">
<input type="submit" value="검색">
</form>
<table border="1">
<tr>
<th>번호</th>
<th>이름</th>
<th>국어</th>
<th>영어</th>
<th>수학</th>
<th>총점</th>
<th>평균</th>
<th>비고</th>
</tr>
<% for (ScoreVO vo : scoreList) { %>
<tr>
<td><%=vo.getId() %></td>
<td><%=vo.getName() %></td>
<td><%=vo.getKor() %></td>
<td><%=vo.getEng() %></td>
<td><%=vo.getMath() %></td>
<td><%=vo.getTotal() %></td>
<td><%=vo.getAverage() %></td>
<td><a href="delete.jsp?id=<%=vo.getId() %>">[삭제]</a></td>
</tr>
<% } %>
</table>
<br>
<a href="insert_form.jsp">새로운 점수 등록하기</a>
</body>
</html>
우선 이 부분을 보자.
ScoreDAO dao = ScoreDAO.getInstance();
List<ScoreVO> scoreList = dao.selectAll();
ScoreDAO
는 생성자가 외부로 공개되어있지 않고 싱글턴 패턴으로 구현하였으므로 getInstance()
를 통해 객체를 반환받아 가져오도록 하고,
학생 점수 리스트는 dao 객체에서 selectAll()
메서드를 통해 받아오도록 한다.
이제 받아온 학생 점수 리스트를 아래의 html 구문에서
<% for (ScoreVO vo : scoreList) { %>
로 순환하면서 vo 객체를 통해 각 get()
메서드를 통해 데이터를 가져와 표시한다.
delete.jsp
<%@page import="kr.co.jsp.score.model.ScoreDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
/*
파라미터 데이터(int)를 얻어오신 후 DAO에게 삭제 요청 해 주시면 됩니다.
(DAO의 주소값 받아와서 메서드 호출)
public boolean delete(???) <- DAO 클래스에 선언하세요.
삭제가 완료되면 score_list.jsp로 이동.
삭제가 실패해도 score_list.jsp로 이동.
삭제가 성공했는지, 실패했는지의 여부를 script 태그를 이용하여
경고창으로 알려 주세요.
location.href="URL" -> 이걸로 이동시키기.
*/
int id = Integer.parseInt(request.getParameter("id"));
if (ScoreDAO.getInstance().delete(id)) {
%>
<script>
alert("삭제에 성공했습니다!");
location.href="score_list.jsp";
</script>
<%
} else { %>
<script>
alert("삭제에 실패했습니다.")
location.href="score_list.jsp";
</script>
<% }
%>
앞서 점수 조회 페이지에서 이 구문을 주의할 필요가 있는데,
<td><a href="delete.jsp?id=<%=vo.getId() %>">[삭제]</a></td>
바로 이 부분이다.
우리가 다른 페이지에 어떠한 학생의 점수를 삭제해 달라고 했을 때, 다른 페이지에서는 어떤 학생을 삭제해 달라고 했는지 알 길이 없다.
따라서 우리는 id?=
로 id를 지정하여 보내는데, 앞서 가져온 학생 점수 리스트의 vo
객체를 이용하여 getId()
메서드로 id를 가져와 URL에 같이 붙여서 보내서 전송하도록 하고 있었다.
그러면 delete.jsp 페이지에서는 request
객체를 통해 "id"
에 해당하는 파라미터를 찾아 가져올 수 있다.
단, String 형태로 전달되므로 int형으로 변환을 거쳐주어야 한다. (id는 DB상에 NUMBER형으로 저장되어있다.)
이제 delete()
메서드를 ScoreDAO
에 구현할 필요가 있다.
ScoreDAO.java
public boolean delete(int id) {
String sql = "DELETE FROM scores WHERE id=?";
boolean flag = false;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
int rn = pstmt.executeUpdate();
if (rn == 1) flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
pstmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return flag;
}
커넥션과 pstmt(준비된 문장)을 불러오고, pstmt의 경우 sql문을 보면 ?
부분에 들어갈 것을 지정해 주어야 하므로 setInt
메서드에 매개 변수로 들어온 id를 지정해준다.
executeUpdate()
메서드의 경우 INSERT
, UPDATE
, DELETE
를 진행하는 경우 호출하는 메서드인데, 반환 값으로는 해당 실행문에 영향을 받는 메서드의 행 수가 반환된다.
우리는 해당하는 행 단 한 개만을 지울 것이므로, 만약 잘 지워졌다면 1이 반환 될 것이고, 그렇게 되었을 경우 잘 지워졌다는 의미로 flag
를 true
로 지정해둔다.
다시 delete.jsp 로 돌아와 아래의 부분에 집중해보자.
if (ScoreDAO.getInstance().delete(id)) {
%>
<script>
alert("삭제에 성공했습니다!");
location.href="score_list.jsp";
</script>
<%
} else { %>
<script>
alert("삭제에 실패했습니다.")
location.href="score_list.jsp";
</script>
<% }
결국 ScoreDAO.getInstance().delete(id)
가 잘 실행되었다면 1이 반환 될 것이고, if문에서 0을 제외한 값은 참으로 판단되므로 삭제가 잘 된 경우 삭제에 성공했다는 알림이 뜨고 score_list.jsp로 리다이렉트 될 것이다.
다음은 마지막으로, 검색 기능과 페이지를 구현하고 학생 점수와 관련된 실습은 마무리짓는다.
사실 score_list.jsp 페이지에 이미 검색 창 자체는 구현되어 있었다.
<form action="search.jsp">
<input type="text" name="keyword" placeholder="검색할 이름을 입력">
<input type="submit" value="검색">
</form>
바로 이 부분이다.
중요한건 DAO에 구현되어야 할 search()
메서드와 search.jsp 페이지이다.
search.jsp
<%@page import="kr.co.jsp.score.model.ScoreDAO"%>
<%@page import="java.util.ArrayList"%>
<%@page import="kr.co.jsp.score.model.ScoreVO"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
/*
SQL문에서 LIKE절을 사용할 때 %를 ?에 직접 붙이면 정상 동작하지 않는다.
?를 채울 때, pstmt객체의 setString()을 이용하여 채우게 되는데,
setString()은 ''을 자동으로 추가해 준다.
따라서 %가 '' 안으로 들어가지 못 하는 상황이 발생한다.
그러므로 SQL에는 ?만 쓰고, ?를 채울 때 검색에에 %를 앞 ,뒤로 연결하여 채운다.
*/
String keyword = "%" + request.getParameter("keyword") + "%";
/*
DAO클래스에 search라는 이름의 메서드를 선언해서 검색 결과를 리턴하세요.
리턴된 결과를 테이블 형식으로 body 내부에 출력해 주시면 되겠습니다.
테이블 형식은 score_list.jsp에 있는 테이블을 참조하세요.
검색 결과가 없다면 브라우저 화면에 테이블 대신 '검색 결과가 없습니다.'
라고 출력하시고 목록으로 돌아갈 수 있는 링크를 추가하세요.
*/
List<ScoreVO> scoreList = ScoreDAO.getInstance().search(keyword);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<% if (scoreList.isEmpty()) { %>
<h1>검색 결과가 없습니다.</h1>
<a href="score_list.jsp">목록으로 돌아가기</a>
<% } else { %>
<table border="1">
<tr>
<th>번호</th>
<th>이름</th>
<th>국어</th>
<th>영어</th>
<th>수학</th>
<th>총점</th>
<th>평균</th>
<th>비고</th>
</tr>
<% for (ScoreVO vo : scoreList) { %>
<tr>
<td><%=vo.getId() %></td>
<td><%=vo.getName() %></td>
<td><%=vo.getKor() %></td>
<td><%=vo.getEng() %></td>
<td><%=vo.getMath() %></td>
<td><%=vo.getTotal() %></td>
<td><%=vo.getAverage() %></td>
<td><a href="delete.jsp?id=<%=vo.getId() %>">[삭제]</a></td>
</tr>
<% } %>
</table>
<a href="score_list.jsp">목록으로 돌아가기</a>
<% } %>
</body>
</html>
주석으로도 표기하긴 했지만 이 부분에 집중해보자.
String keyword = "%" + request.getParameter("keyword") + "%";
예를 들어 홍길동, 김길동, 김홍길 이렇게 3명의 데이터가 존재한다고 쳐보자.
우리는 길동
이라는 이름을 가진 사람을 찾고 싶은데, 만약 %
를 질의문 키워드의 앞 뒤로 추가하지 않을 경우 정확히 길동
이라는 딱 두 글자의 이름을 가진 사람만을 검색하게 된다.
따라서 당연하게도 %길동%
형태의 질의를 날려서 길동
이라는 이름을 포함하고 있는 사람을 찾아야 한다.
그 밑의 html 파트에서는 search()
메서드를 통해 학생을 찾아오지 못한 경우 다른 페이지로 돌아갈 수 있는 링크를 제공하고, 찾은 경우에는 찾은 모든 학생을 표시하게 하고 있다.
이제 학생을 찾아오기 위해서 ScoreDAO
에 search()
메서드를 구현한다.
ScoreDAO.java
public List<ScoreVO> search(String keyword) {
String sql = "SELECT * FROM scores WHERE name LIKE ?";
List<ScoreVO> scoreList = new ArrayList<>();
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, keyword);
rs = pstmt.executeQuery();
while (rs.next()) {
ScoreVO vo = new ScoreVO(
rs.getInt("id"),
rs.getString("name"),
rs.getInt("kor"),
rs.getInt("eng"),
rs.getInt("math"),
rs.getInt("total"),
rs.getDouble("average")
);
scoreList.add(vo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
rs.close();
conn.close();
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return scoreList;
}
conn, pstmt, rs
를 준비하고 rs.next()
로 객체 순환하며 데이터를 받아와 저장하는 것은 사실상 selectAll()
과 거의 동일하다. 단, 질의문에서
SELECT * FROM scores WHERE name LIKE ?
로 되어 있어 ?에 아까 만든
%keyword%
가 들어오게 되고, 원하는 대상에 해당하는 행을 찾아 List에 넣고 반환해주게 된다.
이것으로 우리는 학생 점수를 조회, 저장, 검색, 삭제를 할 수 있는 기능을 구현했고 이 실습은 여기서 끝마치도록 한다.
간단하게나마 게시판 시스템을 구현하고자 한다.
우선, 게시판이 갖는 요소를 생각하고 그것에 대한 DB 테이블을 먼저 구현해야 한다.
CREATE TABLE board (
board_id NUMBER PRIMARY KEY,
writer VARCHAR2(45) NOT NULL,
title VARCHAR2(100) NOT NULL,
content VARCHAR2(2000),
reg_date DATE DEFAULT sysdate
);
위와 같은 sql문을 작성하였다. 각각 게시물 번호, 작성자, 제목, 내용, 등록 시간을 의미한다.
물론 게시물을 새로 작성할 때 마다 PK인 게시물 번호가 증가할 필요가 있으니 시퀀스도 작성하도록 한다.
CREATE SEQUENCE bid_seq
START WITH 1
INCREMENT BY 1
MAXVALUE 1000
NOCYCLE
NOCACHE;
이렇게 작성을 끝마쳤으면 각 sql문을 한번씩 실행하고 이클립스로 돌아와 BoardVO
클래스를 작성하자.
BoardVO.java
package kr.co.jsp.board.model;
import java.sql.Timestamp;
/*
CREATE TABLE board (
board_id NUMBER PRIMARY KEY,
writer VARCHAR2(45) NOT NULL,
title VARCHAR2(100) NOT NULL,
content VARCHAR2(2000),
reg_date DATE DEFAULT sysdate
);
*/
public class BoardVO {
private int boardId;
private String writer;
private String title;
private String content;
private java.sql.Timestamp regDate;
public BoardVO() {}
public BoardVO(int boardId, String writer, String title, String content, Timestamp regDate) {
super();
this.boardId = boardId;
this.writer = writer;
this.title = title;
this.content = content;
this.regDate = regDate;
}
public int getBoardId() {
return boardId;
}
public void setBoardId(int boardId) {
this.boardId = boardId;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public java.sql.Timestamp getRegDate() {
return regDate;
}
public void setRegDate(java.sql.Timestamp regDate) {
this.regDate = regDate;
}
}
다른 부분들은 너무 자세히 설명할 필요가 없는 부분이긴 하나, sql의 DATE
자료형은 Java 에서는 java.sql.Timestamp
를 사용하면 된다.
다음은 BoardDAO
의 차례인데, 우선 interface로 된 IBoardDAO
를 먼저 만든다.
IBoardDAO.java
package kr.co.jsp.board.model;
import java.util.List;
public interface IBoardDAO {
// 게시글 등록
boolean insert(BoardVO vo);
// 게시글 전체 조회
List<BoardVO> selectAll();
// 게시글 상세 조회
BoardVO selectOne(int bId);
// 게시글 수정
boolean update(BoardVO vo);
// 게시글 삭제
boolean delete(int bId);
}
인터페이스는 클래스를 만들기 위한 틀이나 사양서라고 생각하면 되고, 이 인터페이스는 이 인터페이스를 구현한 모든 클래스에 대해 동일한 동작을 보장하고 다형성의 장점을 갖게 한다.
이제 해당 인터페이스를 implements 한 BoardDAO
를 구현하는데, 클래스 생성 시 Interfaces 옵션에서 미리 인터페이스를 추가하여 구현하면 편하다.
BoardDAO.java
package kr.co.jsp.board.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import kr.co.jsp.util.JdbcUtil;
public class BoardDAO implements IBoardDAO {
private BoardDAO () {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static BoardDAO dao = new BoardDAO();
public static BoardDAO getInstance() {
if (dao == null) {
dao = new BoardDAO();
}
return dao;
}
// --------------------------------
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
private Connection getConnection () throws Exception {
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String uid = "jsp";
String upw = "jsp";
return DriverManager.getConnection(url, uid, upw);
}
@Override
public boolean insert(BoardVO vo) {
boolean flag = false;
String sql = "INSERT INTO board(board_id, writer, title, content) "
+ "VALUES(bid_seq.NEXTVAL, ?, ?, ?)";
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, vo.getWriter());
pstmt.setString(2, vo.getTitle());
pstmt.setString(3, vo.getContent());
if (pstmt.executeUpdate() == 1) flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, pstmt);
}
return flag;
}
@Override
public List<BoardVO> selectAll() {
List<BoardVO> articles = new ArrayList<>();
String sql = "SELECT * FROM board ORDER BY board_id DESC";
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
BoardVO vo = new BoardVO (
rs.getInt("board_id"),
rs.getString("Writer"),
rs.getString("title"),
rs.getString("content"),
rs.getTimestamp("reg_date")
);
articles.add(vo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, pstmt, rs);
}
return articles;
}
@Override
public BoardVO selectOne(int bId) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean update(BoardVO vo) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean delete(int bId) {
// TODO Auto-generated method stub
return false;
}
}
여기서부터는 살짝 설명을 생략하기로 하고,
못 보던 부분이나 설명이 필요한 부분만을 집중하도록 하겠다.
JdbcUtil()
이라는 메서드가 눈에 들어올텐데, 해당 메서드를 가진 클래스는 다음과 같이 구현되어있다.
JdbcUtil.java
package kr.co.jsp.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcUtil {
// INSERT, UPDATE, DELETE 시 호출
public static void close(Connection conn, PreparedStatement pstmt) {
try {
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
System.out.println("close 과정에서 에러 발생");
}
}
// SELECT 시 호출
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
System.out.println("close 과정에서 에러 발생");
}
}
INSERT, UPDATE, DELETE
와 SELECT
를 할 시에 rs가 있나 없나 여부에 따라 함수를 오버로딩하여 구현하고, 객체 사용 종료 시에 해당 함수를 사용할 수 있도록 했다.
게시판 구현 파트에서는 43일차 기준으로
content.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>게시글 등록</h2>
<form action="write_con.jsp" method="post">
<p>
# 작성자: <input type="text" name="writer"> <br>
# 제목: <input type="text" name="title"> <br>
# 글 내용: <br>
<textarea rows="3" name="content"></textarea> <br>
<input type="submit" value="등록">
</p>
</form>
</body>
</html>
게시글을 등록하고 write_con.jsp로 넘긴다.
write_con.jsp
<%@page import="kr.co.jsp.board.model.BoardDAO"%>
<%@page import="kr.co.jsp.board.model.BoardVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
/*
1. 사용자의 입력값을 가져오세요. 그리고 객체로 포장하세요.
2. DAO클래스의 주소값을 받아 DB에 내용을 삽입하는 메서드를 호출하세요.
(board_id -> 시퀀스 객체로 삽입, 날짜는 넣어줄 필요가 없다.)
3. 글 등록 성공 시 list.jsp로 리다이렉팅,
4. 글 등록 실패 시 write.jsp로 리다이렉팅 해 주세요.
*/
request.setCharacterEncoding("utf-8");
BoardVO vo = new BoardVO();
// writer, title, content
vo.setWriter(request.getParameter("writer"));
vo.setTitle(request.getParameter("title"));
vo.setContent(request.getParameter("content"));
if (BoardDAO.getInstance().insert(vo)) {
response.sendRedirect("list.jsp");
} else {
response.sendRedirect("write.jsp");
}
%>
컨트롤러에서는 게시물 vo
객체를 생성하고 write.jsp 에서 작성된 내용을 가져와 BoardDAO
인스턴스의 insert()
메서드를 통해 글을 작성해보고, 잘 되는 경우 list.jsp로 리다이렉트, 안 된 경우 write.jsp로 돌려보낸다.
list.jsp
<%@page import="kr.co.jsp.board.model.BoardVO"%>
<%@page import="java.util.List"%>
<%@page import="kr.co.jsp.board.model.BoardDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//BoardDAO 클래스의 selectAll() 을 호출하여
//DB에 들어있는 모든 글들을 list로 받아 오셔야 합니다.
//리턴값으로 받은 리스트를 body태그에 반복문을 사용하여 하나씩 작성 (테이블 형식으로).
//번호, 작성자, 제목, 비고(삭제)
//조건문을 사용하여 DB에서 얻어온 글이 하나도 없을 때는
//게시글이 존재하지 않는다 라는 글을 출력해 주세요.
List<BoardVO> postList = BoardDAO.getInstance().selectAll();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<% if (postList.isEmpty()) { %>
<h3>게시글이 존재하지 않습니다.</h3>
<% } else { %>
<h2>게시글 목록</h2>
<table border="1">
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>비고</th>
</tr>
<% for (BoardVO vo : postList) { %>
<tr>
<td><%=vo.getBoardId() %></td>
<td><%=vo.getWriter() %></td>
<td>
<a href="content.jsp?bId=<%=vo.getBoardId()%>"><%=vo.getTitle() %></a>
</td>
<td><a href="delete.jsp">[삭제]</a></td>
</tr>
<% }
}
%>
</table>
<a href="write.jsp">글 쓰기</a>
</body>
</html>
list.jsp 의 경우 여태 작성된 글의 목록을 싹 얻어와 표시한다.
content.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 요청과 함께 넘어오는 파라미터값(글 번호)을 얻어와서
// DAO의 selectOne()을 호출하여 객체 받아오기
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1><%= %>번 게시물 내용</h1>
<p>
# 작성자: ??? <br>
# 제목: ??? <br>
# 내용: <br>
<textarea rows="5" readonly>???</textarea> <br>
# 작성일: ???
</p>
<a href="list.jsp">글 목록 보기</a>
</body>
</html>
content.jsp 의 경우 작성된 글을 클릭했을 때의 내용을 표시하도록 한다. 이 부분부터의 구현과 글 하나의 내용만을 가져와 표시하기 위한 메서드는, 다음날부터 구현하기로 한다.