본문 바로가기

Learning/JSP

JSP 개인프로젝트-영어 학습 사이트 만들기 (수강후기 게시판 전체보기, 게시글 입력)

이전글

https://amongthestar.tistory.com/150

 

JSP 개인프로젝트-영어 학습 사이트 만들기 (강의목록 전체보기, 상세보기)

이전글 (관리자 기능) https://amongthestar.tistory.com/149 JSP 개인프로젝트-영어 학습 사이트 만들기 (관리자 기능) 이전글 (로그인, 회원가입) https://amongthestar.tistory.com/148 JSP 개인프로젝트-영어..

amongthestar.tistory.com


이번에 포스팅할 내용은 수강후기 게시판 전체보기와 게시글 입력이다.

게시글 상세보기 및 수정 삭제, 댓글 입력 삭제는 다음 포스팅에 게시할 예정이다.

 

지금까지 로직 진행은 다음과 같다.

1. 메인 화면에서 세번째 탭에 해당하는 "과정"을 클릭하면 과정 전체목록이 카드 형식으로 보인다.

WClassCourseList.java -> courseList.jsp 로 이동

2. 카드 하나를 클릭하면 해당하는 과정의 상세내용을 볼 수 있다.

WClassCourseDetail.java -> courseDetail.jsp 로 이동

3. 과정의 상세내용 하단에 수강후기 게시판을 볼 수 있다.

 


수강후기 DB관련 DTO (WBoardDTO.java)

package com.wmember.model;

public class WBoardDTO {
	private int num;
	private String userid;
	private String subject;
	private String content;
	private String reg_date;
	private int readcount;
	private int classnum;
	
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getUserid() {
		return userid == null ? "" : userid.trim();
	}
	public void setUserid(String userid) {
		this.userid = userid;
	}
	public String getSubject() {
		return subject == null ? "" : subject.trim();
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getContent() {
		return content == null ? "" : content.trim();
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getReg_date() {
		return reg_date == null ? "" : reg_date.trim();
	}
	public void setReg_date(String reg_date) {
		this.reg_date = reg_date;
	}
	public int getReadcount() {
		return readcount;
	}
	public void setReadcount(int readcount) {
		this.readcount = readcount;
	}
	public int getClassnum() {
		return classnum;
	}
	public void setClassnum(int classnum) {
		this.classnum = classnum;
	}
}

 


courseDetail.jsp

 

게시판 영역이다. 검색영역, 글쓰기 버튼, 수강후기 게시판영역으로 이루어져있다.

<!-- 게시판 -->
	<div class="board">
		<br/>
		
		<!-- 검색 시작-->
		<div align="right" id="searchDiv">
		<form name="search" id="search">
			<select name="field" id="field">
				<option value="userid">작성자</option>
				<option value="subject">제목</option>
			</select>
			<input type="text" name="word" id="word">
			<input type="button" value="찾기" id="btnSearch" class="btn btn-outline-secondary btn-sm">
			<a href="#coll"  id="writeBtn" class="btn btn-outline-dark btn-sm" data-toggle="collapse">글쓰기</a>
		</form>
		</div>
		<!-- 검색 끝-->
		
		<!-- 수강후기 게시판 -->
		<div id="result" align="center"></div>
		
		<!-- 글쓰기 폼 영역 -->
		  <div id="coll" class="collapse">
		   <form action="/Tutoring/board/boardinsert" method="post" id="wFrm">
			<br/><br/>
			  <!-- 수강후기 등록하고자 하는 강의 번호 -->
			  <input type="hidden" id="classnum" name="classnum" value="${dto.classnum}">
			  
			  <div class="input-group mb-3"><div class="input-group-prepend">
			      <span class="input-group-text">강의명</span></div>
			      <input type="text" class="form-control" id="classname" name="classname" readonly="readonly" value="${dto.classname}">
			  	  <div class="input-group-prepend"><span class="input-group-text">작성자</span></div>
			      <input type="text" class="form-control" id="userid" name="userid" readonly="readonly" value="${sessionScope.userid}">
			  </div>
			  <br/>
			  
			  <div class="input-group mb-3"><div class="input-group-prepend">
			      <span class="input-group-text">제목</span></div>
			      <input type="text" class="form-control" id="subject" name="subject">
			  </div>
			  <br/>
			  
			  <div class="input-group mb-3"><div class="input-group-prepend">
			      <span class="input-group-text">내용</span></div>
			      <textarea rows="5" cols="20" class="form-control" id="content" name="content"></textarea>
			  </div>
			  
			  <div class="button">
			 	  <input type="reset" class="btn btn-gray" value="취소">
			 	  <input type="button" class="btn btn-primary" id="send" value="후기등록">
			  </div>
			</form>
		  </div>
		  <!-- 글쓰기 폼 영역  끝-->
	</div>
<!-- 게시판 끝-->

 

수강후기 게시판 영역은 div태그만 있고 내용이 없다. 테이블 태그는 보이지 않는다. 어떻게 구현할까?

페이지가 로드되자 마자 수강후기 리스트를 불러오도록 자바스크립트 함수를 기입한다.

<script>
$(document).ready(function(){
	getData(1, "", "", ${dto.classnum}); //페이지 로드 시 수강후기 리스트 보기
//전체보기 함수
function getData(pageNum, field, word, classnum){
	$.get("/Tutoring/board/boardlist", 
			{"pageNum":pageNum, "field":field, "word":word, "classnum":classnum},
			function(d){
			$("#result").html(d);
	})
}//getData
//검색버튼 클릭
	$("#btnSearch").on("click", function(){
		getData(1, $("#field").val(), $("#word").val(), $("#classnum").val());
	})//btnSearch
  • $.get 함수로 "/Tutoring/board/boardlist"로 이동하고 들고온 값을 result div영역에 뿌린다.

 


WBoardListAction.java

(수강후기 게시판 전체 목록을 가져오는 서블릿. 경로는  "/Tutoring/board/boardlist"이다.)

package com.wboard.action;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.wmember.model.PageUtil;
import com.wmember.model.WBoardDAO;
import com.wmember.model.WBoardDTO;


/**
 * Servlet implementation class WBoardListAction
 */
@WebServlet("/board/boardlist")
public class WBoardListAction extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public WBoardListAction() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		WBoardDAO dao=WBoardDAO.getInstance();
		
		String pageNum=request.getParameter("pageNum")==null? "1":request.getParameter("pageNum");
		int currentPage=Integer.parseInt(pageNum);
		int pageSize=5;
		int startRow=(currentPage-1)*pageSize+1;
		int endRow=currentPage*pageSize;
				
		int count=0;
		String field=request.getParameter("field")==null?"":request.getParameter("field");
		String word=request.getParameter("word")==null?"":request.getParameter("word");
		
		//어떤 강의인지 강의 번호를 가져오기
		String classNumber=request.getParameter("classnum");
		int classnum=Integer.parseInt(classNumber);
		
		//수강후기 개수 출력
		count=dao.getCount(field, word,classnum);
		
		//총페이지 수
		int totPage=(count/pageSize)+(count%pageSize==0?0:1);
		int pageBlock=3;
		int startPage=((currentPage-1)/pageBlock)*pageBlock+1;
		int endPage=startPage+pageBlock-1;
		if(endPage>totPage) endPage=totPage;
		
		PageUtil pu=new PageUtil();
		pu.setCurrentPage(currentPage);
		pu.setEndPage(endPage);
		pu.setPageBlock(pageBlock);
		pu.setStartPage(startPage);
		pu.setTotPage(totPage);
		pu.setField(field);
		pu.setWord(word);
		pu.setClassnum(classnum);
		
		ArrayList<WBoardDTO> arr=null;
		if(word.equals("")) {
			arr=dao.boardList(startRow, endRow, classnum);
		}else {
			arr=dao.boardList(field, word, startRow, endRow, classnum);
		}
		
		int rowNo=count-((currentPage-1)*pageSize); //매 페이지의 시작번호
		
		request.setAttribute("rowNo", rowNo);
		request.setAttribute("pu", pu);
		request.setAttribute("board", arr);
		request.setAttribute("count", count);
		
		RequestDispatcher rd=request.getRequestDispatcher("listResult.jsp");
		rd.forward(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

 

관리자 페이지에서 강의목록을 내보냈던 것과 마찬가지로 페이징, 전체 게시글 수, 게시글 목록을 request 객체를 이용해 listResult.jsp로 내보낸다. 

 

 


 

PageUtil.java 내용은 앞서 관리자 기능에 대해 소개한 포스팅에 나와있다.

 


<WBoard.java의 boardList 메소드>(검색이 아닐때)

 

//검색 아닌 전체보기
	public ArrayList<WBoardDTO> boardList(int startRow, int endRow, int classnum) {
		Connection con=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		ArrayList<WBoardDTO>arr=new ArrayList<WBoardDTO>();
		
		try {
			con=getConnection();
			StringBuilder sb=new StringBuilder();
			sb.append(" select * from");
			sb.append(" (select aa.*, rownum rn from");
			sb.append(" (select * from WBoard where classnum="+classnum);
			sb.append(" order by num desc) aa");
			sb.append(" where rownum<=?) where rn>=?");
			ps=con.prepareStatement(sb.toString());
			ps.setInt(1, endRow);
			ps.setInt(2, startRow);
			rs=ps.executeQuery();
			while(rs.next()) {
				WBoardDTO dto=new WBoardDTO();
				dto.setNum(rs.getInt("num"));
				dto.setContent(rs.getString("content"));
				dto.setReadcount(rs.getInt("readcount"));
				dto.setReg_date(rs.getString("reg_date"));
				dto.setSubject(rs.getString("subject"));
				dto.setUserid(rs.getString("userid"));
				dto.setClassnum(rs.getInt("classnum"));
				arr.add(dto);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeConnection(con, ps, rs);
		}
		return arr;
	}

 

더보기

이때 SQL문이 헷갈려서 고생을 했다.. (좀 막혔던 부분들을 생각해보면 다 전체목록 보기에 관한 기능 구현에 관해서다..)

지금까지 전체 목록을 볼때는 검색한 결과인지 아닌지만 가르면 됐는데, 지금의 수강후기 게시판은 "특정 강의에 대한" 수강후기라는 단서가 붙는다.

 

그래서 처음엔 바깥에 select를 한번 더 감싸고 where classnum=강의번호; 라고 조건절을 줬고 그 select문 안에서 rn을 미리 부여해놓고 5에서 10사이, 10에서 15사이 이런식으로 골라냈었다. 당연히 1페이지에는 2개, 2페이지에는 5개 이렇게 오류가 날 수 밖에 없었다. (사실 오류가 아니고 당연한 결과다.) 

 

하지만 강의 페이지별로 수강후기 DB가 따로 있는게 아니고 전체 수강후기 DB에서 강의번호 별로 골라낸다.

 

따라서 바깥 select를 지우고, rownum where 절이 아닌 select * from wboard 뒤에 where classnum이라는 조건절을 추가해야한다. 즉 미리 classnum을 골라내고 rn을 계산해야하는 것이다.


<WBoard.java의 boardList 메소드>(검색일때)

 

//검색 전체보기
	public ArrayList<WBoardDTO> boardList(String field, String word, int startRow, int endRow, int classnum) {
		Connection con=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		ArrayList<WBoardDTO>arr=new ArrayList<WBoardDTO>();
		
		try {
			con=getConnection();
			StringBuilder sb=new StringBuilder();
			sb.append(" select * from");
			sb.append(" (select aa.*, rownum rn from");
			sb.append(" (select * from WBoard where classnum="+classnum);
			sb.append(" and upper ("+field+") like upper ('%"+word+"%')");
			sb.append(" order by num desc) aa");
			sb.append(" where rownum<=?) where rn>=?");
			ps=con.prepareStatement(sb.toString());
			ps.setInt(1, endRow);
			ps.setInt(2, startRow);
			rs=ps.executeQuery();
			while(rs.next()) {
				WBoardDTO dto=new WBoardDTO();
				dto.setNum(rs.getInt("num"));
				dto.setContent(rs.getString("content"));
				dto.setReadcount(rs.getInt("readcount"));
				dto.setReg_date(rs.getString("reg_date"));
				dto.setSubject(rs.getString("subject"));
				dto.setUserid(rs.getString("userid"));
				dto.setClassnum(rs.getInt("classnum"));
				arr.add(dto);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeConnection(con, ps, rs);
		}
		return arr;
	}

 


<WBoard.java의 getCount 메소드>

 

//게시물수 출력
	public int getCount(String field, String word, int classnum) {
		Connection con=null;
		Statement st=null;
		ResultSet rs=null;
		int count=0;
		String sql="";
		
		try {
			con=getConnection();
			st=con.createStatement();
			if(word.equals("")) {
				sql="select count(*) from WBoard where classnum="+classnum;
			}else {
				sql="select count(*) from WBoard where upper ("+field+") like upper ('%"+word+"%') and classnum="+classnum;
			}
			rs=st.executeQuery(sql);
			if(rs.next()) {
				count=rs.getInt(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeConnection(con, st, rs);
		}
		return count;
	}

listResult.jsp

WBoardListAction.java에서 내보내진 값을 board란 이름에 담아서 뿌린다. 페이징 영역도 포함하고 있다.

그런데 courseDetail.jsp->WBoardListAction.java->listResult.jsp로 이동된 것이므로 $.get의 콜백함수에 의해 이 값들이 courseDetail.jap의 result div에 뿌려지게 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<body>
<h5 style="text-align: left"><span id="cntSpan">수강후기(${count}</span>)</h5>
<table class="table table-hover table-bordered table-sm">
	<thead>
		<tr>
			<th style="width: 8%">글번호</th>
			<th style="width: 13%">작성자</th>
			<th>제목</th>
			<th style="width: 8%">작성일</th>
			<th style="width: 7%">조회수</th>
		</tr>
	</thead>
	<tbody>
		<c:forEach items="${board}" var="board" varStatus="st">
		<tr>
			<td>${rowNo-st.index}</td>
			<td>${board.userid}</td>
			<td><a href="/Tutoring/board/boardDetail?num=${board.num}">${board.subject}</a></td>
			<td>${board.reg_date}</td>
			<td>${board.readcount}</td>
		</tr>
		</c:forEach>
	</tbody>
</table>
<div align="center">
	<c:if test="${pu.startPage>pu.pageBlock}"> <!-- 이전 -->
		<a href="javascript:getData(${pu.startPage-pu.pageBlock},'${pu.field}','${pu.word}', ${pu.classnum})">[이전]</a>
	</c:if>
	<c:forEach begin="${pu.startPage}" end="${pu.endPage}" var="i"><!-- 페이지 출력 -->
		<c:if test="${i==pu.currentPage}"> <!-- 현재 페이지 -->
			<c:out value="${i}"/>
		</c:if>
		<c:if test="${i!=pu.currentPage}"> <!-- 현재 페이지 아닌 경우 링크 부여-->
			<a href="javascript:getData(${i},'${pu.field}','${pu.word}', ${pu.classnum})">${i}</a>
		</c:if>
	</c:forEach>
	<c:if test="${pu.endPage<pu.totPage}"> <!-- 다음-->
		<a href="javascript:getData(${pu.endPage+1},'${pu.field}','${pu.word}', ${pu.classnum})">[다음]</a>
	</c:if>
</div>
</body>
</html>

 


다음은 수강후기 글쓰기에 대한 내용이다.

지금까지는 로그인 없이 볼 수 있는 내용들이었지만 수강후기를 쓰려고 하면 로그인을 해야한다.

글쓰기 버튼을 누르면 숨어있던 글쓰기 폼이 나타난다. 부트스트랩의 collapse 클래스를 이용했다.

courseDetail.jsp 내에 글쓰기 폼이 있으므로 후기등록 버튼을 눌렀을때 실행되는 자바스크립트 함수도 이곳에 적어준다.

 

 

courseDetail.jsp 내의 글쓰기 폼 영역

 

//후기등록 버튼 클릭
	$("#send").click(function(){ //후기등록 버튼 클릭
         if(${sessionScope.userid==null}){
              alert("로그인 필요");
              location.href="/Tutoring/member/login";
         }else{
	       	 	var classnum=$("#classnum").val();
				var userid=$("#userid").val();
				var content=$("#content").val();
				var subject=$("#subject").val();
				var postString="classnum="+classnum+"&userid="+userid+"&content="+content+"&subject="+subject;
	            $.ajax({
	                  type:    "post",
	                  url :    "/Tutoring/board/boardinsert",
	                  data:    postString,
	                  success:function(d){
	                	   alert("후기가 등록되었습니다!");
	                	   $("#result").html(d);
	                       document.getElementById("wFrm").reset();
	                  },
	                  beforeSend: showRequest, //빈칸 확인
	                  error: function(e){
	                       alert("error:"+e);
	                  }
	            })//ajax
         }
     })//send
}); //document ready
//빈칸확인 함수
function showRequest(){
	if($("#subject").val()==""){
		alert("제목을 입력하세요");
		$("#subject").focus();
		return false;
	}
	if($("#content").val()==""){
		alert("내용을 입력하세요");
		$("#content").focus();
		return false;
	}
	return true;
}

 

로그인을 하지 않았을 경우 alert창을 띄우고 login화면으로 넘어간다.

로그인을 한 경우 $.ajax을 이용하여 비동기방식으로 수강후기가 입력되며, 새로고침을 하지 않아도 수강후기 화면이 업데이트 된다. 

 


WBoardInsertAction.java

($.ajax를 통해 넘어온 수강후기 입력 서블릿. 경로는 "/Tutoring/board/boardinsert"이다.)

 

package com.wboard.action;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.wmember.model.WBoardDAO;
import com.wmember.model.WBoardDTO;


/**
 * Servlet implementation class BoardInsertAction
 */
@WebServlet("/board/boardinsert")
public class WBoardInsertAction extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public WBoardInsertAction() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		RequestDispatcher rd=request.getRequestDispatcher("courseDetail.jsp");
		rd.forward(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		WBoardDTO board=new WBoardDTO();
		board.setClassnum(Integer.parseInt(request.getParameter("classnum")));
		board.setUserid(request.getParameter("userid"));
		board.setSubject(request.getParameter("subject"));
		board.setContent(request.getParameter("content"));
		WBoardDAO dao=WBoardDAO.getInstance();
		
		int classNumber=Integer.parseInt(request.getParameter("classnum"));
		int flag=0;
		flag=dao.boardInsert(board);

		if(flag==1){
			response.sendRedirect("/Tutoring/class/CourseDetail?num="+classNumber);
		}
	}
}

 

WBoardDAO.java의 boardInsert메소드를 수행하여 수강후기가 입력되었으면 return된 flag 값을 받아오고 1일 경우 다시 CourseDetail로 넘어간다. 수강후기가 해당되는 강의 상세보기로 넘어가므로 num값을 미리 받아와야한다.

 

 


<WBoardDAO.java의 boardInsert메소드>

 

//디비셋팅
	private static WBoardDAO instance=new WBoardDAO();
	public static WBoardDAO getInstance() {
		return instance;
	}
	private Connection getConnection() throws Exception{
		Context initCtx=new InitialContext();
		Context envCtx=(Context) initCtx.lookup("java:comp/env");
		DataSource ds=(DataSource) envCtx.lookup("jdbc/wiser");
		return ds.getConnection();
	}
	
	//게시물등록
	public int boardInsert(WBoardDTO vo) {
		Connection con=null;
		PreparedStatement ps=null;
		int flag=0;
		
		try {
			con=getConnection();
			String sql="INSERT INTO WBoard(num, userid, subject, content, reg_date, "
					+ "readcount, classnum)"
					+ "VALUES(WBoard_seq.nextval,?,?,?,sysdate,0,?)";
			ps=con.prepareStatement(sql);
			ps.setString(1, vo.getUserid());
			ps.setString(2, vo.getSubject());
			ps.setString(3, vo.getContent());
			ps.setInt(4, vo.getClassnum());
			flag=ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeConnection(con, ps);
		}
		return flag;
	}
    
    //닫기 closeConnection
	private void closeConnection(Connection con, PreparedStatement ps) {
		try {
			if(ps!=null) ps.close();
			if(con!=null) con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} 
	}
	private void closeConnection(Connection con, Statement st, ResultSet rs) {
		try {
			if(st!=null) st.close();
			if(con!=null) con.close();
			if(rs!=null) rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} 
	}

 

courseDetail의 $.ajax으로 다시 돌아가면 result영역에 수강후기 게시판이 업데이트되어 나타난다. 


게시글 상세보기 및 수정 삭제, 댓글과 관련된 포스팅은 다음에 하겠다.

 

https://github.com/kkj0712/JspProject_Tutoring

 

kkj0712/JspProject_Tutoring

Contribute to kkj0712/JspProject_Tutoring development by creating an account on GitHub.

github.com