[JSP] 게시판 만들기 - 게시글 보기 기능 구현

2024. 8. 22. 21:40·프로젝트
목차
  1. 게시글 보기 기능 구현

[작성일: 2023. 04. 20]

 

게시글 보기 기능 구현

실제로 해당 글에 들어갔을 때 글의 내용을 보여주는 view 화면이 필요하다.

BbsDAO에 글 내용을 불러올 수 있는 함수를 추가하면 된다.

글 작성 페이지는 write.jsp 파일을 복사해서 수정한 다음 만들어준다.

 

게시글 기능을 테스트 시 특수문자나 공백을 작성하면 글 내용이 정상적으로 출력되지 않는다.

기본적으로 HTML 코드는 <>를 이용해서 태그를 사용하기 때문에 데이터베이스에 넣은 글자에 <>가 들어가 있다면 웹 브라우저는 HTML 기호인지, 출력하는 용도인지 알지 못한다.

 

그렇기 때문에 특수문자를 출력하는 부분을 제대로 처리해주어야 게시글 보기 기능이 완벽하게 구현될 수 있다.

 

 

 

[소스코드]

#bbsDAO

  • 맨 아래에 하나의 글을 불러올 수 있는 getBbs 함수 추가
package bbs;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
public class BbsDAO {
private Connection conn;
private ResultSet rs; // 정보를 담을 객체
public BbsDAO() {
try {
String dbURL = "jdbc:mariadb://localhost:3399/bbs";
// 내 컴퓨터에 설치된 mariadb 서버의 bbs 데이터베이스에 접속
String dbID = "root";
String dbPassword = "1234";
Class.forName("org.mariadb.jdbc.Driver"); // 매개체 라이브러리
conn = DriverManager.getConnection(dbURL, dbID, dbPassword);
} catch(Exception e) {e.printStackTrace();}
}
public String getDate() {
// 현재 시간을 가져올 함수
String SQL = "SELECT NOW()";
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
rs = pstmt.executeQuery();
if(rs.next()) {
return rs.getString(1); // 현재의 날짜를 반환함.
}
}catch(Exception e) {e.printStackTrace();}
return ""; // 빈 문자열 리턴으로 데이터 베이스 오류를 알려줌.
}
public int getNext() {
String SQL = "SELECT bbsID FROM bbs ORDER BY bbsID DESC";
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
rs = pstmt.executeQuery();
if(rs.next()) {
return rs.getInt(1) + 1; // 1을 더해서 다음 게시글이 보일 수 있도록 함.
}
return 1; // 현재가 첫 게시물인 경우
}catch(Exception e) {e.printStackTrace();}
return -1; // 데이터 베이스 오류를 알려줌.
}
public int write(String bbsTitle, String userID, String bbsContent) {
String SQL = "INSERT INTO bbs VALUES(?, ?, ?, ?, ?, ?);";
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1, getNext()); // 다음에 쓰여야 할 게시글 번호
pstmt.setString(2, bbsTitle);
pstmt.setString(3, userID);
pstmt.setString(4, getDate());
pstmt.setString(5, bbsContent);
pstmt.setInt(6, 1);
return pstmt.executeUpdate();
}catch(Exception e) {e.printStackTrace();}
return -1; // 데이터 베이스 오류를 알려줌.
}
public ArrayList<Bbs> getList(int pageNumber) { // ArrayList를 이용하여 Bbs를 담아낼 수 있도록 함.
String SQL = "SELECT * FROM bbs WHERE bbsID < ? AND bbsAvailable = 1 ORDER BY bbsID DESC LIMIT 10";
// bbsID가 특정한 숫자보다 작을 때, 삭제가 되지 않아 Available이 1인 글만 가져올 수 있도록, bbsID로 내림차순, 10개까지만 가져오기
ArrayList<Bbs> list = new ArrayList<Bbs>();
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1, getNext() - (pageNumber - 1) * 10);
// 작성될 번호의 글 번호에서부터 계산
rs = pstmt.executeQuery();
while(rs.next()) {
Bbs bbs = new Bbs();
bbs.setBbsID(rs.getInt(1));
bbs.setBbsTitle(rs.getString(2));
bbs.setUserID(rs.getString(3));
bbs.setBbsDate(rs.getString(4));
bbs.setBbsContent(rs.getString(5));
bbs.setBbsAvailable(rs.getInt(6));
list.add(bbs);
}
}catch(Exception e) {e.printStackTrace();}
return list;
}
public boolean nextPage(int pageNumber) {
// 게시글이 10개라면 다음페이지라는 버튼이 없어야 함.
// 페이징 처리
String SQL = "SELECT * FROM bbs WHERE bbsID < ? AND bbsAvailable = 1";
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1, getNext() - (pageNumber - 1) * 10);
rs = pstmt.executeQuery();
if(rs.next()) {
return true;
}
}catch(Exception e) {e.printStackTrace();}
return false;
}
// 하나의 글 내용을 불러올 수 있는 함수 추가
public Bbs getBbs(int bbsID) {
String SQL = "SELECT * FROM bbs WHERE bbsID = ?";
// bbsID가 특정한 숫자인 경우 어떠한 행위를 진행할 수 있도록 해줌.
try {
PreparedStatement pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1, bbsID);
rs = pstmt.executeQuery();
if(rs.next()) {
Bbs bbs = new Bbs();
bbs.setBbsID(rs.getInt(1));
bbs.setBbsTitle(rs.getString(2));
bbs.setUserID(rs.getString(3));
bbs.setBbsDate(rs.getString(4));
bbs.setBbsContent(rs.getString(5));
bbs.setBbsAvailable(rs.getInt(6));
return bbs;
}
}catch(Exception e) {e.printStackTrace();}
return null; // 해당 글이 존재하지 않는 경우 null 반환
}
}

 

 

#bbs.jsp

  • 글 제목 부분에 악성 스크립트 방지를 위해 replaceAll 추가(특수문자 치환)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="bbs.BbsDAO" %>
<%@ page import="bbs.Bbs" %>
<%@ page import="java.util.ArrayList" %> <!-- 게시판 목록을 출력하기 위함 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width", initial-scale="1">
<link rel="stylesheet" href="css/bootstrap.css">
<title>JSP 게시판 웹 사이트</title>
<style type="text/css">
a, a:hover {
color:#000000;
text-decoration: none;
}
</style>
</head>
<body>
<%
String userID = null;
if(session.getAttribute("userID")!=null) {
userID = (String)session.getAttribute("userID");
}
int pageNumber = 1; // 기본페이지
if(request.getParameter("pageNumber") != null) {
pageNumber = Integer.parseInt(request.getParameter("pageNumber"));
}
%>
<!-- 네비게이션 구현(바) -->
<nav class="navbar navbar-default"> <!-- 네비게이션 -->
<div class="navbar-header"> <!-- 네이게이션 상단 부분 -->
<!-- 네비게이션 상단 박스 영역 -->
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false"> <!-- 모바일 상태에서 오른쪽에 메뉴가 나옴. -->
<!-- 오른쪽 메뉴 바에서 선 3개 -->
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="main.jsp">JSP 게시판 웹 사이트</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav"> <!-- 네비게이션 바의 메뉴 -->
<li><a href="main.jsp">메인</a></li>
<li class="active"><a href="bbs.jsp">게시판</a></li>
<!-- 메인과 게시판으로 이동 -->
</ul>
<%
if(userID == null) {
%>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class=dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">접속하기<span class="caret"></span></a>
<!-- 임시주소링크 #을 기재함. -->
<ul class="dropdown-menu">
<li><a href="login.jsp">로그인</a></li>
<li><a href="join.jsp">회원가입</a><li>
</ul>
</li>
</ul>
<%
} else {
%>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class=dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">회원관리<span class="caret"></span></a>
<!-- 임시주소링크 #을 기재함. -->
<ul class="dropdown-menu">
<li><a href="logoutAction.jsp">로그아웃</a></li>
</ul>
</li>
</ul>
<%
}
%>
</div>
</nav>
<div class="container">
<div class="row">
<table class="table table-striped" style="text-align: center; border: 1px solid #dddddd">
<!-- 게시판에 글 목록들이 홀수와 짝수가 번갈아가며 색상이 변경됨. -->
<thead>
<tr>
<th style="background-color: #eeeeee; text-align: center;">번호</th>
<th style="background-color: #eeeeee; text-align: center;">제목</th>
<th style="background-color: #eeeeee; text-align: center;">작성자</th>
<th style="background-color: #eeeeee; text-align: center;">작성일</th>
</tr>
</thead>
<tbody>
<%
BbsDAO bbsDAO = new BbsDAO();
ArrayList<Bbs> list = bbsDAO.getList(pageNumber);
for(int i=0; i<list.size(); i++){
%>
<tr> <!-- 게시글이 실제로 출력될 부분 -->
<td><%=list.get(i).getBbsID() %></td>
<td><a href="view.jsp?bbsID=<%=list.get(i).getBbsID() %>">
<%=list.get(i).getBbsTitle().replaceAll(" ", "&nbsp;").replaceAll("<","&lt;")
.replaceAll(">","&gt;").replaceAll("\n","<br>") %></a></td>
<!-- 제목을 눌렀을 때는 해당 게시글의 내용을 보여준다. -->
<td><%=list.get(i).getUserID() %></td>
<td><%=list.get(i).getBbsDate().substring(0, 11)
+ list.get(i).getBbsDate().substring(11,13) + "시"
+ list.get(i).getBbsDate().substring(14,16) + "분" %></td>
</tr>
<%
}
%>
</tbody>
</table>
<!-- 페이지 버튼 -->
<%
if(pageNumber != 1){
%>
<a href="bbs.jsp?pageNumber=<%=pageNumber -1 %>" class="btn btn-success btn-arraw-left">이전</a>
<%
} if(bbsDAO.nextPage(pageNumber + 1)){
%>
<a href="bbs.jsp?pageNumber=<%=pageNumber +1 %>" class="btn btn-success btn-arraw-left">다음</a>
<%
}
%>
<a href="write.jsp" class="btn btn-primary pull-right">글쓰기</a>
</div>
</div>
<script src="//code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="js/bootstrap.js"></script>
</body>
</html>

 

 

#view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="bbs.Bbs" %>
<%@ page import="bbs.BbsDAO" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width", initial-scale="1">
<link rel="stylesheet" href="css/bootstrap.css">
<title>JSP 게시판 웹 사이트</title>
</head>
<body>
<%
String userID = null;
if(session.getAttribute("userID")!=null) {
userID = (String)session.getAttribute("userID");
}
// 매개변수 및 기본셋팅
int bbsID = 0;
if(request.getParameter("bbsID") != null) {
bbsID = Integer.parseInt(request.getParameter("bbsID"));
}
if(bbsID == 0) {
PrintWriter script = response.getWriter();
script.println("<script>");
script.println("alert('유효하지 않은 글입니다.')");
script.println("location.href = 'bbs.jsp'");
script.println("</script>");
}
Bbs bbs = new BbsDAO().getBbs(bbsID);
%>
<!-- 네비게이션 구현(바) -->
<nav class="navbar navbar-default"> <!-- 네비게이션 -->
<div class="navbar-header"> <!-- 네이게이션 상단 부분 -->
<!-- 네비게이션 상단 박스 영역 -->
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false"> <!-- 모바일 상태에서 오른쪽에 메뉴가 나옴. -->
<!-- 오른쪽 메뉴 바에서 선 3개 -->
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="main.jsp">JSP 게시판 웹 사이트</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav"> <!-- 네비게이션 바의 메뉴 -->
<li><a href="main.jsp">메인</a></li>
<li class="active"><a href="bbs.jsp">게시판</a></li>
<!-- 메인과 게시판으로 이동 -->
</ul>
<%
if(userID == null) {
%>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class=dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">접속하기<span class="caret"></span></a>
<!-- 임시주소링크 #을 기재함. -->
<ul class="dropdown-menu">
<li><a href="login.jsp">로그인</a></li>
<li><a href="join.jsp">회원가입</a><li>
</ul>
</li>
</ul>
<%
} else {
%>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class=dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">회원관리<span class="caret"></span></a>
<!-- 임시주소링크 #을 기재함. -->
<ul class="dropdown-menu">
<li><a href="logoutAction.jsp">로그아웃</a></li>
</ul>
</li>
</ul>
<%
}
%>
</div>
</nav>
<div class="container">
<div class="row">
<table class="table table-striped" style="text-align: center; border: 1px solid #dddddd">
<!-- 게시판에 글 목록들이 홀수와 짝수가 번갈아가며 색상이 변경됨. -->
<thead>
<tr>
<th colspan="3"style="background-color: #eeeeee; text-align: center;">게시판 글 보기 양식</th>
<!-- 2개의 열을 사용할 수 있도록 colspan 사용 -->
</tr>
</thead>
<tbody>
<tr>
<td style="width: 20%;">글 제목</td>
<td colspan="2"><%= bbs.getBbsTitle().replaceAll(" ", "&nbsp;").replaceAll("<","&lt;")
.replaceAll(">","&gt;").replaceAll("\n","<br>") %></td>
</tr>
<tr>
<td>작성자</td>
<td colspan="2"><%= bbs.getUserID() %></td>
</tr>
<tr>
<td>작성일자</td> <!-- 글이 작성된 시간이므로 bbs 페이지에서 그대로 복사해옴. -->
<td colspan="2"><%=bbs.getBbsDate().substring(0, 11)
+ bbs.getBbsDate().substring(11,13) + "시"
+ bbs.getBbsDate().substring(14,16) + "분" %></td>
</tr>
<tr>
<td>내용</td>
<td colspan="2" style="min-height: 200px; text-align: left">
<%= bbs.getBbsContent().replaceAll(" ", "&nbsp;").replaceAll("<","&lt;")
.replaceAll(">","&gt;").replaceAll("\n","<br>") %></td>
</tbody>
</table>
<a href="bbs.jsp" class="btn btn-primary">목록</a>
<!-- 해당 글의 작성자가 본인이라면 해당 글을 수정, 삭제할 수 있도록 함. -->
<%
if(userID != null && userID.equals(bbs.getUserID())){
%>
<a href="update.jsp?bbsID=<%= bbsID %>" class="btn btn-primary">수정</a>
<a href="deleteAction.jsp?bbsID=<%= bbsID %>" class="btn btn-primary">삭제</a>
<%
}
%>
</div>
</div>
<script src="//code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="js/bootstrap.js"></script>
</body>
</html>

 

 

 

[실행화면]

love라는 ID로 로그인 해서 user1이 작성한 글을 보게 되면 글은 정상적으로 보이지만 수정과 삭제 버튼이 없는 것을 확인할 수 있다. 

 

내가 쓴 글이 아닌 경우

 

내가 쓴 글일 경우

 

 

love라는 아이디로 작성한 글에 들어가면 수정과 삭제 버튼이 생긴 것을 확인할 수 있다.

물론 수정버튼과 삭제버튼이 존재만 할 뿐 누르면 기능을 구현하지 않았기 때문에 아무것도 동작하진 않는다.

목록을 누르면 게시판 화면으로 다시 돌아가는 기능은 구현되어 있다.

 

 

특수문자를 읽을 수 있도록 처리를 해주었기 때문에 특수문자 또한 잘 출력되는 것을 볼 수 있다.

 

추가적으로 특수문자와 관련해서 중요한 게 하나 있다면 웹 패킹 공격에 해당하는 것으로 크로스 사이트 스크립팅 공격이 있다.

게시판에 글을 작성할 때 스크립트 문장을 삽입하며 글을 보고 있는 사람한테 영향을 끼칠 수 있는 해킹 기법이다.

만약 특수문자 처리가 잘 되지 않았다면 다음과 같은 문제가 발생할 수 있다.

 

우선 글 제목에 <script> alert('Hello World'); </script> 를 삽입 후 글쓰기 버튼을 눌러보았다.

 

경고창 발생

 

 

게시판의 메인 화면에 들어가게 됐을 때 Hello World라는 경고창이 발생한다.

이게 바로 크로스 사이트 스크립트 공격의 가장 기본적인 형태이다.

글 제목이 보이지 않게 되고 스크립트 문장이 그대로 들어가기 때문에 문제가 발생한다.

새로고침을 하더라도 계속해서 스크립트 문장이 실행된다. 

 

해당 문제는 코드에 작성되어 있듯이 글 내용 뿐만 아니라 글 제목에서도 특수문자를 치환해주는 방법을 이용해서 해결해주어야 한다.

 

 

 

replaceAll로 특수문자 치환 코드를 작성해주면 더이상 경고창이 뜨지 않고 글 제목에 스크립트 문장이 그대로 적용되는 것을 볼 수 있다.

 

보안은 아주 중요하기 때문에 꼭 필요한 작업이라는 것을 기억해야 한다.

 

 

 

 

 

 

 

 

 

 


🐣 입문 개발자가 작성한 글이므로 틀린 내용이나 오타가 있을 수 있습니다.

🐣 출처: https://youtu.be/SC7EP8ID9D8

 

저작자표시 비영리 변경금지 (새창열림)
  1. 게시글 보기 기능 구현
'프로젝트' 카테고리의 다른 글
  • [JSP] 게시판 만들기 - 메인 페이지 디자인
  • [JSP] 게시판 만들기 - 게시글 수정 및 삭제 기능 구현
  • [JSP] 게시판 만들기 - 게시판 글 목록 기능 구현
  • [JSP] 게시판 만들기 - 글쓰기 기능 구현
뚜비
뚜비
1년차 백엔드&iOS 개발자의 감자 탈출 블로그 🥔🥔
  • 뚜비
    뚜비의 개발로그
    뚜비
  • 전체
    오늘
    어제
  • 글쓰기     관리
    • Devlog
      • Back-End
        • Java
        • Spring
        • JPA
        • HTTP
        • Security
        • Back-End
        • Front-End
      • 알고리즘
      • iOS
        • Swift
      • Database
      • Tips
        • Git & GitHub
        • A to Z
      • 프로젝트
      • 생각정리
  • 태그

    spring
    jsp
    HTTP
    객체
    백준
    자바스크립트
    데이터베이스
    sql
    스프링
    게시판만들기
    성능최적화
    변수
    생성자
    다형성
    알고리즘
    MVC
    javascript
    프로그래머스
    Swift
    김영한
    Security
    자바
    Java
    최주호
    DB
    html
    JPA
    Spring Security
    Database
    의존성주입
  • hELLO· Designed By정상우.v4.10.0
뚜비
[JSP] 게시판 만들기 - 게시글 보기 기능 구현
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.