본문 바로가기

programming/Gukbi

국비 교육 58일차 - 답글형 게시판 만들기

728x90
반응형

request, response를 배웠으니 이걸 활용할 수 있는 예제를 만들어봤다. 

그냥 게시판은 이미 한두번 만들어 봤으니 답글을 달 수 있는 게시판을 연습해봤다. 생각보다 테이블 안에 들어갈 데이터를 생각하는게 복잡해서 처음에 바로바로 머릿속에 들어오지 않았다. 

 

이거는 테이블 만든것부터 다시 보고 넘어가야 할 것 같다. 

 

-- 묻고 답하기 => request, response
/*
                        새글입력시마다
                    ORDER BY group_id DESC, group_step ASC
    no      subject         group_id        group_step      group_tab      root     depth
    1       AAAAAAA             1                0              0           0          1 
    2       ->BBBBBB            1                1              1           1          1 
    3        ->CCCCCC           1                2              2           2          0 
    4       DDDDDDD             2                0              0           0          2 
    5        ->KKKKK            2                1              1           4          0     
    6        ->OOOOO            2                2              1           4          0 


*/
CREATE TABLE jspReplyBoard(
    no NUMBER, 
    name VARCHAR2(34) CONSTRAINT jrb_name_nn NOT NULL,
    subject VARCHAR2(1000) CONSTRAINT jrb_subject_nn NOT NULL,
    content CLOB CONSTRAINT jrb_content_nn NOT NULL,
    pwd VARCHAR2(10)CONSTRAINT jrb_pwd_nn NOT NULL,
    regdate DATE DEFAULT SYSDATE,
    hit NUMBER DEFAULT 0,
    group_id NUMBER, --답변그룹
    group_step NUMBER DEFAULT 0, -- 답변순서
    group_tap NUMBER DEFAULT 0, -- 유형
    root NUMBER DEFAULT 0,-- 어느 게시물에 대한 답변 
    depth NUMBER DEFAULT 0,-- 답변이 몇개인지
    CONSTRAINT jrb_no_pk PRIMARY KEY(no)
);
CREATE SEQUENCE jrb_no_seq
    START WITH 1
    INCREMENT BY 1
    NOCACHE
    NOCYCLE;

group_id, group_step은 게시판 출력에 있어 필요한 colum이다. 그래야 같은 그룹끼리 묶어서 출력을 해줄 수 있다. 

group_tab역시 마찬가지로, 들여쓰기를 해서 어떤 게시글의 답변인지 구분할 수 있게 해준다. 

 

root, depth는 답글을 삭제할때 필요한 column이다. 삭제 되었을때 어떻게 처리해줄지를 생각하면서 데이터를 변경하거나 가져와줄 수 있어야 하기 때문이다. 

 

그리고 게시물 번호는 자동으로 하나씩 증가해야하기 때문에 시퀀스도 만들어줬다. 

 

// 게시판 기능 
	  // 목록
	  public List<BoardVO> boardListData(int page)
	  {
		  // => ArrayList VS Vector : 예상문제 
		  List<BoardVO> list=new ArrayList<BoardVO>();
		  /*
		   *        List : 데이터의 중복허용 , 순서가 있다 (인덱스:0)
		   *         |
		   *       ===================
		   *       ArrayList , Vector , LinkedList
		   *       
		   *       1. 글올리기
		   *       3.  => ㄴㄴㄴ
		   *       5.  => ㅔㅔㅔ   
		   *            =>   
		   *       2. ㄴㄴㄴ
		   *       4.    => ㄴㄴㄴ
		   */
		  try
		  {
			  getConnection();
			  String sql="SELECT no,subject,name,regdate,hit,group_tab,num "
					    +"FROM (SELECT no,subject,name,regdate,hit,group_tab,rownum as num "
					    +"FROM (SELECT no,subject,name,regdate,hit,group_tab "
					    +"FROM jspReplyBoard ORDER BY group_id DESC,group_step ASC)) "
					    +"WHERE num BETWEEN ? AND ?";
			            // 페이징기법 => 인라인뷰 
			  ps=conn.prepareStatement(sql);
			  // ?에 값을 채운다 
			  int rowSize=10;
			  int start=(rowSize*page)-(rowSize-1); 
			  int end=rowSize*page;
			  /*
			   *    1page  1
			   *    2page  11
			   */
			  ps.setInt(1, start);
			  ps.setInt(2, end);
			  
			  // 실행
			  ResultSet rs=ps.executeQuery();
			  while(rs.next())
			  {
				  BoardVO vo=new BoardVO();
				  vo.setNo(rs.getInt(1));
				  vo.setSubject(rs.getString(2));
				  vo.setName(rs.getString(3));
				  vo.setRegdate(rs.getDate(4));
				  vo.setHit(rs.getInt(5));
				  vo.setGroup_tab(rs.getInt(6));
				  list.add(vo);
			  }
			  rs.close();
		  }catch(Exception ex)
		  {
			  ex.printStackTrace();
		  }
		  finally
		  {
			  disConnection();
		  }
		  return list;
	  }

 

아무래도 답글형 게시판이다보니까 쿼리문장을 써줄때 더 고려할 부분이 많았다. 

 

일단 출력을 할때는 group_tab을 이용해서 공백을 보여줘야 하기 때문에 select문으로 같이 가져와줬다. 그리고 글 출력을 해줄때 group_id가 같은 글들끼리 묶어주고, group_step 순으로 출력을 해줘야하기 때문에 

ORDER BY group_id DESC, group_step ASC로 설정을 해준다. 

 

그리고 페이지를 나눠줘야 하기 때문에 start와 end를 잡아준다. 리스트이기 때문에 while문을 사용해서 vo에 입력을 해주면 출력이 완성된다. 

 

 

<%@page import="java.text.SimpleDateFormat"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*,com.sist.dao.*"%>
<%
    /*
         list.jsp?page=  => null(X) ""
         list.jsp        => null
         list.jsp?page=1 => 1
    */
    // page : 내장객체 ==> 자바(this) , 웹 (page)
    // page=this
    String strPage=request.getParameter("page");
    // 시작 => 페이지를 지정하지 않는다 
    if(strPage==null)
    	strPage="1";
    
    int curpage=Integer.parseInt(strPage);
    BoardDAO dao=new BoardDAO();
    List<BoardVO> list=dao.boardListData(curpage);
    int count=dao.boardRowCount();
    int totalpage=(int)(Math.ceil(count/10.0)); // 총페이지
    
    count=count-((10*curpage)-10);
    
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>묻고답하기</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.row{
   width:800px;
   margin: 0px auto;
}
</style>
</head>
<body>
<%--
		화면 변경
		<html>
			<a href="이동할 파일명"> : GET
			<form action="이동할 파일명"> : GET/POST
		<JavaScript>
			location.href="이동할 파일" : GET
		<Java>
			sendRedirect("이동할 파일") : GET sendRedirect("a.jsp?a=10")
			forward("이동할 파일") : GETC
 --%>
  <div style="height:30px"></div>
  <div class="container">
   <div class="row">
    <img src="board.png" style="width:800px;height:150px">
    <table class="table">
      <tr>
       <td>
        <a href="insert.jsp" class="btn btn-sm btn-primary">새글</a>
       </td>
      </tr>
    </table>
    <div style="height:400px">
	    <table class="table table-hover">
	      <tr class="info">
	        <th class="text-center" width=10%>번호</th>
	        <th class="text-center" width=45%>제목</th>
	        <th class="text-center" width=15%>이름</th>
	        <th class="text-center" width=20%>작성일</th>
	        <th class="text-center" width=10%>조회수</th>
	      </tr>
	      <%
	         for(BoardVO vo:list)
	         {
	      %>
	              <tr>
			        <td class="text-center" width=10%><%=count-- %></td>
			        <td width=45%>
			        <%--답변 표시 --%>
			        <%
			           if(vo.getGroup_tab()>0)
			           {
			        	   for(int i=0;i<vo.getGroup_tab();i++)
			        	   {
			        		   out.println("&nbsp;&nbsp;");
			        	   }
			        %>
			              <img src="re.gif">
			        <%
			           }
			        %>
			        <a href="detail.jsp?no=<%= vo.getNo()%>&page=<%=curpage%>"><%=vo.getSubject() %></a>
			        &nbsp;
			        <%
			           String today=new SimpleDateFormat("yyyy-MM-dd").format(new Date());
			           String dbday=vo.getRegdate().toString();
			           if(today.equals(dbday))
			           {
			        %>
			              <sup style="color:red">new</sup>
			        <%
			           }
			        %>
			        </td>
			        <td class="text-center" width=15%><%=vo.getName() %></td>
			        <td class="text-center" width=20%><%=vo.getRegdate().toString() %></td>
			        <td class="text-center" width=10%><%=vo.getHit() %></td>
			      </tr>
	      <%
	         }
	      %>
	    </table>
    </div>
    <table class="table">
      <tr>
       <td class="text-right">
        <a href="list.jsp?page=<%=curpage>1?curpage-1:curpage %>" class="btn btn-sm btn-danger">이전</a>
        <%=curpage %> page / <%=totalpage %> pages
        <a href="list.jsp?page=<%=curpage<totalpage?curpage+1:curpage %>" class="btn btn-sm btn-warning">다음</a>
       </td>
      </tr>
    </table>
   </div>
  </div>
</body>
</html>

 jsp는 html과 java가 같은 페이지에 코딩이 되지만, doGet과 doPost가 따로 나뉘어 있지 않다. 그래서 이런점들을 고려를 해서 코드를 작성해줘야 한다. 

 

일단 html을 쓰기 전에 먼저 java안에 들어가야할 내용을 채워준다. 리스트는 해당하는 page의 내용을 보여주기 때문에, boardListData()메소드에 필요한 변수 page를 먼저 설정해줘야 한다. 

 

일단 사용자가 입력한 페이지를 getPararmeter로 받는다. 처음 페이지를 들어갔을때는 당연히 입력받은 값이 없기 때문에 null이면 1페이지로 설정을 해준다. 

 

page값이 설정되면 메소드에 변수로 입력해줘서 해당하는 페이지의 값을 출력할 수 있도록 해준다. 

 

다 되었으면 for문을 사용해서 html안에 내용을 출력해준다. 

 

<%
	         for(BoardVO vo:list)
	         {
	      %>
	              <tr>
			        <td class="text-center" width=10%><%=count-- %></td>
			        <td width=45%>
			        <%--답변 표시 --%>
			        <%
			           if(vo.getGroup_tab()>0)
			           {
			        	   for(int i=0;i<vo.getGroup_tab();i++)
			        	   {
			        		   out.println("&nbsp;&nbsp;");
			        	   }
			        %>
			              <img src="re.gif">
			        <%
			           }
			        %>
			        <a href="detail.jsp?no=<%= vo.getNo()%>&page=<%=curpage%>"><%=vo.getSubject() %></a>
			        &nbsp;
			        <%
			           String today=new SimpleDateFormat("yyyy-MM-dd").format(new Date());
			           String dbday=vo.getRegdate().toString();
			           if(today.equals(dbday))
			           {
			        %>
			              <sup style="color:red">new</sup>
			        <%
			           }
			        %>
			        </td>
			        <td class="text-center" width=15%><%=vo.getName() %></td>
			        <td class="text-center" width=20%><%=vo.getRegdate().toString() %></td>
			        <td class="text-center" width=10%><%=vo.getHit() %></td>
			      </tr>
	      <%
	         }
	      %>

 일단 먼저 group_tab이 있는지 확인하고, 있다면 안쪽으로 들어가서 답변처럼 보이게끔 공백을 출력해준다. 

공백은 group_tab의 갯수만큼 출력해주면 되기 때문에 for문 안의 조건이 위와 같이 들어간다. 

 

제목을 클릭하면 상세보기를 할 수 있어야하기 때문에 a 태그에 no&curpage 정보를 같이 넘겨준다. url로 넘겨주기 때문에 doGet()방식으로 넘겨주는거라고 볼 수 있다. 

 

또 글 옆에 new 표시를 띄워주기 위해서, 오늘 날짜를 new Date()f를 통해서 today라는 변수에 담아준 다음에, db에 저장되어 있는 값과 같을 경우 제목 옆에 new를 추가해준다. 

 

 

그리고 이제 차례대로 이름, 작성일, 조회수를 추가해준면 된다. 

 

 

 

그럼 다음은 상세보기

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="com.sist.dao.*,java.util.*"%>
<%
	String no=request.getParameter("no");
	String strpage=request.getParameter("page");
	
	BoardDAO dao=new BoardDAO();
	BoardVO vo=dao.boardOneRowData(Integer.parseInt(no), 1);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title> 
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.row{
   width:800px;
   margin: 0px auto;
}
</style>
</head>
<body>
<div style="height:30px"></div>
  <div class="container">
   <div class="row">
    <img src="board.png" style="width:800px;height:150px">
     <table class="table">
      <tr>
       <th class="text-center danger" width=20%>번호</th>
       <td class="text-center" width=30%><%=vo.getNo() %></td>
       <th class="text-center danger" width=20%>작성일</th>
       <td class="text-center" width=30%><%=vo.getRegdate() %></td>
      </tr>
      <tr>
       <th class="text-center danger" width=20%>이름</th>
       <td class="text-center" width=30%><%=vo.getName()%></td>
       <th class="text-center danger" width=20%>조회수</th>
       <td class="text-center" width=30%><%=vo.getHit() %></td>
      </tr>
      <tr>
       <th class="text-center danger" width=20%>제목</th>
       <td colspan="3"><%=vo.getSubject() %></td>
      </tr>
      <tr>
      	<td class="text-left" valign="top" height=200 colspan="4">
      	 <pre style="white-space:pre-wrap;border:none;background-color:white;"><%=vo.getContent() %></pre>
      	</td>
      </tr>
      <tr>
       <td colspan="4" class="text-right">
        <a href="#" class="btn btn-sm btn-danger">답변</a>
        <a href="update.jsp?no=<%=no %>&page=<%=strpage %>" class="btn btn-sm btn-success">수정</a>
        <a href="#" class="btn btn-sm btn-warning">삭제</a>
        <a href="list.jsp?page=<%=strpage %>" class="btn btn-sm btn-primary">back</a>   
       </td>
      </tr>
     </table>
    </div>
    </div>
</body>
</html>

  상세보기는 별로 어렵진 않다. url에서 no와 page값을 받아왔기 때문에 해당하는 글의 내용을 보여주는 코드를 짜기만 하면 되기 때문이다. 일단 화면ui를 대강 잡아 준 다음 dao에서 정보를 출력해주는 메소드를 만들어 준다. 

 

public BoardVO boardOneRowData(int no, int type)
	 {
		 BoardVO vo=new BoardVO();
		 try	
		 {
			getConnection();
			String sql="";
			// 상세보기와 수정이 같은 메소드를 쓰기 때문에 type을 줘서 조회수 증가하는 경우 하지 않는 경우 나눠줌
			if(type==1)
			{
				sql="UPDATE jspReplyBoard SET "
					+"hit=hit+1 "
					+"WHERE no=?";
				ps=conn.prepareStatement(sql);
				ps.setInt(1, no);
				ps.executeUpdate();
			}
			
			sql="SELECT no, name, subject, content, regdate, hit "
				+"FROM jspReplyBoard "
				+"WHERE no=?";
			ps=conn.prepareStatement(sql);
			ps.setInt(1, no);
			ResultSet rs=ps.executeQuery();
			rs.next();
			vo.setNo(rs.getInt(1));
			vo.setName(rs.getString(2));
			vo.setSubject(rs.getString(3));
			vo.setContent(rs.getString(4));
			vo.setRegdate(rs.getDate(5));
			vo.setHit(rs.getInt(6));
			rs.close();
	
		 } catch (Exception ex) {
			ex.printStackTrace();
		 }
		 finally
		 {
			 disConnection();
		 }
		 return vo;
	 }

 상세보기와 수정은 같은 메소드를 공유하기 때문에 type에 따라 쓰임을 나눠줄 수 있게 type변수도 매개변수로 미리 설정을 해준다. 상세보기의 경우 조회수가 1씩 증가해야 하지만 수정의 경우 증가할 필요가 없기 때문이다. 

 

상세보기일 경우 hit를 1씩 증가해주는 sql문장을 작성해준다. 

그리고 글의 내용을 가져와야 하기 때문에 해당하는 select문장 역시 작성해주면 끝난다. 

 

<tr>
       <td colspan="4" class="text-right">
        <a href="#" class="btn btn-sm btn-danger">답변</a>
        <a href="update.jsp?no=<%=no %>&page=<%=strpage %>" class="btn btn-sm btn-success">수정</a>
        <a href="#" class="btn btn-sm btn-warning">삭제</a>
        <a href="list.jsp?page=<%=strpage %>" class="btn btn-sm btn-primary">back</a>   
       </td>
      </tr>

 글 내용을 모두 출력해주고 답변, 수정, 삭제, 목록에 해당하는 페이지들로 이동할 수 있게 코드를 짜주면 된다. 

 

글쓰기 메소드

 

// 글쓰기
	  public void boardInsert(BoardVO vo)
	  {
		  try
		  {
			  getConnection();
			  String sql="INSERT INTO jspReplyBoard(no,name,subject,content,pwd,group_id) "
					    +"VALUES(jrb_no_seq.nextval,?,?,?,?,"
					    +"(SELECT NVL(MAX(group_id)+1,1) FROM jspReplyBoard))";
			  ps=conn.prepareStatement(sql);
			  ps.setString(1, vo.getName());
			  ps.setString(2, vo.getSubject());
			  ps.setString(3, vo.getContent());
			  ps.setString(4, vo.getPwd());
			  
			  // 실행 요청
			  ps.executeUpdate();
		  }catch(Exception ex)
		  {
			  ex.printStackTrace();
		  }
		  finally
		  {
			  disConnection();
		  }
	  }

 insert문을 써주면 된다. 여기서 잘 생각할 점은 no값은 미리 만들어둔 시퀀스로 자동 증가를 시켜주고, group_id는 기존에 있는 group_id에서 가장 큰 값에 1을 더해주거나, 아예 없는 경우 1로 셋팅을 해줘야 한다는 점이다. 

 

<%@ 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>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.row{
   width:800px;
   margin: 0px auto;
}
td{
  font-size: 9pt;
  font-family: 맑은 고딕;
}
</style>
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(function(){
	// 글쓰기 버튼을 클릭시에 처리 
	$('.sendBtn').click(function(){
		let name=$('input[name=name]').val(); // value
		//alert(name);
		if(name.trim()=="") 
		{
			$('input[name=name]').focus();
			return;// 밑에 있는 문장은 수행 할 수 없다 = 함수 종료
		}
		
		let subject=$('input[name=subject]').val();
		if(subject.trim()=="")
		{
			$('input[name=subject]').focus();
			return;
		}
		
		let content=$('textarea').val();
		if(content.trim()=="")
		{
			$('textarea').focus();
			return;
		}
		
		let pwd=$('input[name=pwd]').val();
		if(pwd.trim()=="")
		{
			$('input[name=pwd]').focus();
			return;
		}
		
		// 데이터를 전송 
		$('#frm').submit();
		
	})
})
</script>
</head>
<body>
 <div style="height:30px"></div>
  <div class="container">
   <div class="row">
    <img src="board.png" style="width:800px;height:150px">
    <form method=post action="insert_ok.jsp" id="frm" autocomplete="off">
    <table class="table">
      <tr>
        <th class="text-right danger" width=15%>이름</th>
        <td width=85%>
          <input type=text name=name class="input-sm" size=15>
        </td>
      </tr>
      <tr>
        <th class="text-right danger" width=15%>제목</th>
        <td width=85%>
          <input type=text name=subject class="input-sm" size=55>
        </td>
      </tr>
      <tr>
        <th class="text-right danger" width=15% valign="top">내용</th>
        <td width=85%>
          <textarea rows="10" cols="62" name=content></textarea>
        </td>
      </tr>
      <tr>
        <th class="text-right danger" width=15%>비밀번호</th>
        <td width=85%>
          <input type=password name=pwd class="input-sm" size=15>
        </td>
      </tr>
      <tr>
        <td colspan="2" class="text-center">
          <input type="button" value="글쓰기" class="btn btn-sm btn-primary sendBtn">
          <input type="button" value="취소" onclick="javascript:history.back()"
        	  class="btn btn-sm btn-success"
          >
        </td>
      </tr>
    </table>
    </form>
   </div>
  </div>
</body>
</html>

 사용자가 값을 입력하지 않아 null값이 들어갈 경우를 생각해서 공백이 들어가지 않도록 한다. 만약에 들어간다면 js를 이용해서 공백이 생기지 않도록 조치를 취해준다. 

 

들어가야할 데이터가 다 채워졌으면 insert_ok로 값을 넘겨준다. 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="com.sist.dao.*"%>
<%-- 데이터를 받아서 오라클에 전송--%>
<%
    try
    {
    	request.setCharacterEncoding("UTF-8");//POST
    }catch(Exception ex){}
    String name=request.getParameter("name");
    String subject=request.getParameter("subject");
    String content=request.getParameter("content");
    String pwd=request.getParameter("pwd");
    
    BoardVO vo=new BoardVO();
    vo.setName(name);
    vo.setSubject(subject);
    vo.setContent(content);
    vo.setPwd(pwd);
    
    // DAO를 통해서 => 오라클로 전송 
    BoardDAO dao=new BoardDAO();
    dao.boardInsert(vo);
    // 이동 => list.jsp
    response.sendRedirect("list.jsp");
    
%>

728x90
반응형