Model 1 DBCP 방식으로 댓글게시판 만들기

 

댓글 게시판

- 일반게시판에서 댓글을 달 수 있는 기능 추가

 

댓글게시판 : 주요 기능 소개

1. Connection Pool

2. 액션태그

3. DTO, DAO 클래스

4. 페이징 처리 ( inline View )

5. 댓글 처리

- 댓글, 대댓글이 있다

- 테이블 1개로 처리한다, 여러개로 처리하는 방법도 있다

- 테이블쪽에 댓글을 처리하기 위한 새로운 컬럼 3개가 더 들어간다

 

댓글 처리 컬럼 3가지

1. ref

- 관련글

- 부모글과 답글을 그룹으로 묶어주는 역할

- 부모글과 답글은 같은 ref 값을 가진다

- 원문 : num 컬럼과 ref 값이 같은 값을 가진다

- 답글 : 부모글의 ref 값과 같은 값을 가진다

* 가장 바깥쪽 부모글 = 원문

 

+ num 컬럼은 primary key 이고 sequence 로 값이 들어가는 컬럼이다

+ 원문의 num 과 ref 값은 sequence 로 값이 들어가며 같은 값이다

+ 답글에는 부모글과 같은 ref 값을 가지도록 insert

- 답글은 num 과 ref 값이 같은값이 아님, 원문글의 num 과 ref 값이 같은 것임

 

2. re_level

- 답글의 깊이를 저장

- 원문 : 0

- 1단계 답글 : 1

- 2단계 답글 : 2

 

3. re_step

- 답글의 출력 순서를 저장하여 제어

- 원문 : 0 으로 고정

- 답글 : 오름차순 정렬되어서 출력된다, 새로운 답글이 추가될때마다, re_step 값은 계속 변화한다

- 목록페이지에서 글을 정렬하여 처리할때 ref 컬럼 기준으로 내림차순 정렬한다

- 그럼 최근글이 위로가는데 부모글과 답글들은 ref 값이 동일하기때문에 1번 더 정렬조건을 줘야함

- 그때 사용하는 것이 re_step

 

댓글게시판 : 프로젝트 생성

- 이렇게 구조를 만들기 위해

+ 원문 작성 양식과 댓글 작성 양식이 따로 있다 writeForm 과 replyForm

- reboard 라는 프로젝트부터 생성

- 이후 reboard 프로젝트의 WebContent 폴더 하위에 초기에 보여줄 파일인 index.jsp 를 생성

 

댓글게시판 : 기초 파일 가져오기

- 클라우드에서 작성 양식, 환경설정 코드가 있는 reboard 폴더를 이클립스 WebContent 폴더로 가져오기

 

댓글게시판 : 몇가지 환경 구축

1) reboard 폴더 안의 context.xml 파일을 META-INF 폴더로 옮기기

- 커넥션 풀의 환경설정 파일이다

2) lib 폴더 안에 오라클용 JDBC Driver 인 ojdbc.jar 가져오기, 다른 프로젝트에서 가져오면 된다

 

댓글게시판 : Connection Pool 테스트

- dbcpAPITest.jsp 파일을 실행해서 커넥션 풀에서 커넥션 가져오기 테스트

- context.xml

- dbcpAPITest.jsp

- 커넥션 풀에서 커넥션 가져오기 테스트 성공

+ 추후 DAO 클래스 만들때 커넥션풀에서 커넥션 구하기 3줄 코드를 DAO 클래스에 넣을 것

  		Context init = new InitialContext();
  		DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/orcl");
  		conn = ds.getConnection();

 

댓글게시판 : 테이블 및 시퀀스 생성

- 이전에 회원관리 프로그램에서 사용했던 오라클계정인 totoro 계정에 board0 테이블 생성

- 아래 양식으로 테이블을 생성한다

create table board( 
	num number primary key,
	writer varchar2(20) not null,
	email varchar2(30),
	subject varchar2(50) not null,
	passwd varchar2(20) not null,
	reg_date timestamp not null,
	readcount number default 0,
	ref number not null,
	re_step number not null,
	re_level number not null,
	content varchar2(2000) not null,
	ip varchar2(20) not null );

create sequence board_seq
	start with 1
	increment by 1
	nocache;

 

- WebContent 하위에 sql 폴더를 만들고 안에 reboard.sql 파일 생성

 

- 커넥션 프로파일을 설정 : Oracle_11, New Oracle(totoro), xe 로 해준다

 

- 테이블과 시퀀스를 생성한다

- 시퀀스는 num 이라는 기본키 컬럼에 들어갈 시퀀스이다

- 테이블과 시퀀스 생성 확인

- 생성확인 완료

 

댓글게시판 : DAO 와 DTO 클래스 만들기

+ 확장자가 .java 인 파일은 src 폴더 안에 있어야 정상동작한다

+ 이후 Model 2 에선 기능에 따라 세분화하므로 DAO, DTO 가 다른 패키지에 들어감

- 지금은 같은 패키지인 reboard 패키지 안에 둘 다 넣자

 

댓글게시판 : DTO 클래스 작성

- 이걸 가져와서 DTO 클래스에 복붙

- varchar2 는 String 으로, number 는 int 로, timestamp 는 Timestamp로 바꿔서 자바 변수(프로퍼티) 만들기

+ 프로퍼티의 접근제어자는 private

+ java.sql 의 Timestamp import

- getter / setter 메소드 추가

 

- DTO 클래스 BoardDataBean.java 완성 코드

// DTO (Data Transfer Object)
package reboard;

import java.sql.Timestamp;

public class BoardDataBean {
	private int num;	// 프로퍼티(property)
	private String writer;
	private String email;
	private String subject;
	private String passwd;
	private Timestamp reg_date;
	private int readcount;
	private int ref;
	private int re_step;
	private int re_level;
	private String content ;
	private String ip;
	
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getPasswd() {
		return passwd;
	}
	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}
	public Timestamp getReg_date() {
		return reg_date;
	}
	public void setReg_date(Timestamp reg_date) {
		this.reg_date = reg_date;
	}
	public int getReadcount() {
		return readcount;
	}
	public void setReadcount(int readcount) {
		this.readcount = readcount;
	}
	public int getRef() {
		return ref;
	}
	public void setRef(int ref) {
		this.ref = ref;
	}
	public int getRe_step() {
		return re_step;
	}
	public void setRe_step(int re_step) {
		this.re_step = re_step;
	}
	public int getRe_level() {
		return re_level;
	}
	public void setRe_level(int re_level) {
		this.re_level = re_level;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getIp() {
		return ip;
	}
	public void setIp(String ip) {
		this.ip = ip;
	}
}

 

댓글게시판 : DAO 클래스 작성

DAO 에 들어갈 내용

1. 싱글톤

2. 정적메소드 getInstance() 생성

3. Connection Pool 에서 커넥션 구해오는 메소드

4. 그 이후 댓글게시판 CRUD 관련 메소드

 

1. 싱글톤

- 외부 접근 불가능하게 private, 공유하기 위해 static 해서 자기자신의 클래스로 객체 생성을 한번만 하기

 

2. 정적메소드 getInstance() 생성

 

3. Connection Pool 에서 커넥션 구해오는 메소드 getConnection()

- 메소드 호출시 Connection Pool 에서 커넥션을 구해주도록 함

- DBCP 방식으로 DB 와 연결하므로 이 DB 연결시 커넥션을 구해오는 이 메소드를 사용해야한다

- 커넥션 구할때 예외처리를 해야한다, 여기선 throws 로 Exception 던지기

+ Connection 클래스 import

- 테스트할때 사용했던 dbcpAPITest.jsp 3줄을 복사해서 넣는다

- 단축키 Ctrl + Shift + O 로 import 시키기

- javax.naming.Context 를 선택후 Next, 다음은 javax.sql.DataSource 선택후 Finish

+ Context 와 DataSource 둘 다 Interface 이다

- getConnection() 메소드 완성

- 가져온 커넥션을 리턴하는 코드로 바꿈

 

4. 그 이후 댓글게시판 CRUD 관련 메소드

- 가장 먼저 원문 작성 메소드를 만들어야한다, 그 후 다양한 메소드들을 DAO에 작성

 

댓글게시판 : 파일별 기능 및 구조 설명

- 만들어야할 파일들 보기

+ check.jsp 는 유효성 검사

- 원문 글 과 댓글 작성 기능을 따로 처리

- writeForm.jsp 은 원문 글작성 파일

- replyForm.jsp 는 답글 작성 파일


댓글게시판 : 원문 글 작성 기능

- writeForm.jsp 에서 글 작성시 write.jsp 로 넘어감

- writeForm.jsp

<%@ page contentType="text/html; charset=utf-8" %>
<%@ include file="color.jsp"%>
<html>
<head>
<title>게시판</title>
<link href="style.css" rel="stylesheet" type="text/css">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="check.js"></script>
</head>

<% 
  int num=0,ref=1,re_step=0,re_level=0;	// 원문
 /*  try{  
    if(request.getParameter("num")!=null){  // 답글
	num=Integer.parseInt(request.getParameter("num"));
	ref=Integer.parseInt(request.getParameter("ref"));
	re_step=Integer.parseInt(request.getParameter("re_step"));
	re_level=Integer.parseInt(request.getParameter("re_level"));
	} */
%>
   
<body bgcolor="<%=bodyback_c%>">  
<center><b>글쓰기</b>
<br>
<form method="post"  action="writePro.jsp">
<input type="hidden" name="num" value="<%=num%>">
<input type="hidden" name="ref" value="<%=ref%>">
<input type="hidden" name="re_step" value="<%=re_step%>">
<input type="hidden" name="re_level" value="<%=re_level%>">

<table width="400" border="1" cellspacing="0" cellpadding="0"  bgcolor="<%=bodyback_c%>" align="center">
   <tr>
    <td align="right" colspan="2" bgcolor="<%=value_c%>">
	    <a href="list.jsp"> 글목록</a> 
   </td>
   </tr>
   <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center">이 름</td>
    <td  width="330">
       <input type="text" size="10" maxlength="10" id="writer" name="writer"></td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >제 목</td>
    <td  width="330">
    <%if(request.getParameter("num")==null){%> <!--  원문 -->
       <input type="text" size="40" maxlength="50" id="subject" name="subject"></td>
	<%}else{%>									<!-- 답글 -->
	   <input type="text" size="40" maxlength="50" id="subject" name="subject" value="[답변]"></td>
	<%}%>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center">Email</td>
    <td  width="330">
       <input type="text" size="40" maxlength="30" id="email" name="email" ></td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >내 용</td>
    <td  width="330" >
     <textarea id="content" name="content" rows="13" cols="40"></textarea> </td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >비밀번호</td>
    <td  width="330" >
     <input type="password" size="8" maxlength="12" id="passwd" name="passwd"> 
	 </td>
  </tr>
<tr>      
 <td colspan=2 bgcolor="<%=value_c%>" align="center"> 
  <input type="submit" value="글쓰기" >  
  <input type="reset" value="다시작성">
  <input type="button" value="목록보기" OnClick="window.location='list.jsp'">
</td></tr></table>    
    
</form>      
</body>
</html>

- 원문 글 작성은 앞서했던 일반게시판의 글 작성 기능과 비슷하다

- 다른 점은 form 태그안에 hidden으로 들어가는 값이 있다

- 변수 선언하는 부분이 추가되었다

- 원문은 ref 는 num 과 같은값, re_step 과 re_level 은 0 으로 고정

- hidden 으로 넘어가는 값들

- DTO 의 프로퍼티들이 int 형이므로 useBean 으로 DTO 객체가 생성될때 자동 초기화 되므로 굳이 hidden 으로 전달하지 않아도 된다, 필수는 아니다

- hidden 으로 값이 넘어오지 않더라도 num, readcount, ref, re_step, re_level 은 자동으로 0 으로 초기화됨 

 

- 여기서 입력한 원문 글과 hidden 으로 전달하는 값을 writePro.jsp 로 전송한다

- wirtePro.jsp 생성 후 작성하자

- writePro.jsp

<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	request.setCharacterEncoding("utf-8");
%>

<jsp:useBean id="board" class="reboard.BoardDataBean"></jsp:useBean>
<jsp:setProperty property="*" name="board"/>

<%
	// 클라이언트의 IP주소 구해오기
	String ip = request.getRemoteAddr();
	board.setIp(ip);
	
	BoardDBBean dao = BoardDBBean.getInstance();
	int result = dao.insert(board);		// 원문 글 작성
	
	if(result == 1) {
%>
		<script>
			alert("글 작성 성공");
			location.href="list.jsp";
		</script>
<%	} else {%>
		<script>
			alert("글 작성 실패");
			history.go(-1);
		</script>
<%	}%>

- useBean action tag 로 DTO 객체 board 생성, board 의 프로퍼티 중 기본자료형은 모두 초기화됨

- 원문 작성 폼에서 넘어온 hidden 을 포함한 여러 값들이 DTO 객체 board 에 저장됨

- hidden 으로 값이 넘어오지 않았더라도 num, readcount, ref, re_step, re_level 은 자동으로 0 으로 초기화됐을 것

- reg_date (날짜시간), ip (아이피) 는 원문 작성 폼에서 넘어오지 않는다

- reg_date 는 sql 문에서 sysdate 를 작성하면 되고, ip 는 여기서 처리해야하므로 클라이언트의 ip 주소를 request.getRemoteAddr()로 구해서 객체 board 의 ip 프로퍼티에 setIp() 로 저장

- 데이터를 안전하게 저장하기 위해 테이블에 저장하기위해 DAO 의 insert() 메소드를 호출할 것

- 매개변수로서 사용자가 폼에서 입력한 정보를 저장하고있는 객체 board 를 전달

- insert 에 성공하면 alert 를 띄우고 목록 페이지인 list.jsp 로 페이지 이동

 

- DAO 클래스에 가서 insert() 메소드를 작성하자

 

DAO 클래스 원문 글 작성 메소드 작성

- 원문 글 작성 기능을 하는 insert() 작성

+ 원문 글 작성과 답글 작성 기능을 따로 구현해야 sequence 를 사용 가능

- BoardDBBean.java

// DAO (Data Access Object)
package reboard;

import java.sql.Connection;
import java.sql.PreparedStatement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class BoardDBBean {
	// 싱글톤 : 객체 생성을 1번만 수행하는 것
	private static BoardDBBean instance = new BoardDBBean();
	
	public static BoardDBBean getInstance() {	// 정적 메소드
		return instance;
	}
	// 커넥션 풀에서 커넥션을 구해오는 메소드
	public Connection getConnection() throws Exception {
  		Context init = new InitialContext();
  		DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/orcl");
  		return ds.getConnection();
	}
	
	// 원문 글작성
	public int insert(BoardDataBean board) {
		int result = 0;
		
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
			String sql = "insert into board values(board_seq.nextval,?,?,?,?,";
			sql += "sysdate,?,board_seq.nextval,?,?,?,?)";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, board.getWriter());
			pstmt.setString(2, board.getEmail());
			pstmt.setString(3, board.getSubject());
			pstmt.setString(4, board.getPasswd());
			pstmt.setInt(5, 0);		// readcount
			pstmt.setInt(6, 0);		// re_step
			pstmt.setInt(7, 0);		// re_level
			pstmt.setString(8, board.getContent());
			pstmt.setString(9, board.getIp());
			
			result = pstmt.executeUpdate();	// SQL문 실행
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}
		}
		
		return result;
	}
}

- 추가한 부분 코드만

<insert() 메소드 형식>

- 작성할 글을 저장하고 있는 DTO 객체를 매개변수로 받음

- insert 성공시 1 을 반환하기위해 리턴자료형은 int

<SQL문 부분>

- num 값은 원문이든 답글이든 무조건 sequence 로 1씩 증가한 값이 들어가야함

- 원문의 경우에는 ref 값은 num 값과 같은 값이 들어가야하므로 sequence 값을 넣음

* 1개의 SQL 문에서는 sequence 의 nextval 을 2번 쓰더라도 같은 값이 들어간다! 

ex) num 에 1 이 들어가면 ref 도 1 이 들어감, ref 에 2가 들어가는 것이 아님

+ 원문 작성 양식과 댓글 작성 양식을 1개의 양식으로 작성시 sequence 사용불가, 그땐 그룹함수 사용해야함

+ 원문은 re_step 이 0 이고, 그 아래 답글들의 re_step 은 1,2,3,4.. 으로 저장됨

<? 값 세팅 부분>

- board 객체에서 값을 꺼내서 ? 에 세팅

- 원문은 re_level, re_step 은 0 , 이 값은 이미 넘어올 객체에서 0 으로 설정되었으므로 굳이 0 으로 쓸 필요없지만 0 으로 설정했다

- 글 작성시 readcount 도 0 이므로 0 으로 설정

 

- 잘 구현되었는지 writeForm.jsp 를 실행해서 글 작성 시도해보자

- 아직 목록페이지 list.jsp 가 구현되지 않았으므로 오류 발생

- sql 문에서 테이블 생성을 확인 가능

 

+ SQL Developer 에서 totoro 계정에 대한 connection 을 생성해서 연결하자

- 나중에 SQL문을 실행했을때 어떤 값이 들어갔는지와 각종 컬럼의 값을 확인 가능

 

- 다음으로 목록페이지 list.jsp 를 생성후 작성하자


댓글게시판 : 글 목록 / 게시판 목록

- 기본변수 3가지, 파생변수 6가지 필요

- 원문은 그냥 출력, 댓글은 왼쪽에 여백을 만들어서 출력

ex)

 

 

 - list.jsp 생성 후 작성하자

<%@page import="reboard.BoardDataBean"%>
<%@page import="java.util.List"%>
<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 1. 한 화면에 출력할 데이터 갯수
	int page_size = 10;

	String pageNum = request.getParameter("page");
	if(pageNum == null) {
		pageNum = "1";	// 1page : 최근글이 보이는 페이지
	}
	
	// 2. 현재 페이지 번호
	int currentPage = Integer.parseInt(pageNum);
	
	// startRow : 각 page에 추출할 데이터의 시작번호
	// endRow : 각 page에 추출할 데이터의 끝번호
	// 1 page : startRow=1,  endRow=10
	// 2 page : startRow=11, endRow=20
	// 3 page : startRow=21, endRow=30
	int startRow = (currentPage - 1) * page_size + 1;  
	int endRow = currentPage * page_size;
	
	// 3. 총 데이터 갯수
	int count = 0;
	
	BoardDBBean dao = BoardDBBean.getInstance();
	count = dao.getCount();
	System.out.println("count : " + count);
	
	List<BoardDataBean> list = null;
	if(count > 0) {
		list = dao.getList(startRow, endRow);	// 데이터 10개 추출
	}
	System.out.println("list : " + list);
%>

- 일반게시판의 목록 페이지를 구현할때와 비슷하다

https://laker99.tistory.com/117

- 총 데이터 갯수를 구하기 위해 DB 와 연동 필요, DAO 클래스에서 getCount() 메소드 생성 후 작성 (아래에 있음)

- 이후 리스트를 생성 후 DAO 의 getList(startRow, endRow) 를 호출해서 해당 페이지의 데이터 10개만 가져오기

- DAO 클래스에서 getList() 메소드 생성 후 작성 (아래에 있음)

- list.jsp 실행시 count 값이 출력되고, list 안에 있는 DTO 객체 값(데이터가 있는 주소)가 출력됨

- 1개의 글만 작성했으므로 count 는 1 로 출력, list 의 안의 값(주소)은 1개만 출력됨

- 미완성, 이후 getCount() 와 getList() 메소드를 작성 후 다시 list.jsp 로 돌아와서 작성하기


DAO 클래스의 getCount()

- BoardDBBean.java 중 getCount() 부분만

	// 총 데이터 갯수 구하기
	public int getCount() {
		int result = 0;
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			con = getConnection();
			
			String sql = "select count(*) from board";
			
			pstmt = con.prepareStatement(sql);
			rs = pstmt.executeQuery();	// SQL문 실행
			if(rs.next()) {
//				result = rs.getInt(1); // 아래와 같다
				result = rs.getInt("count(*)");
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(rs != null) try { rs.close(); } catch(Exception e) {}
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}
		}
		return result;
	}

 

- list.jsp 실행시 count 값이 출력

- 1개의 글만 작성했으므로 coutn 는 1 로 출력


DAO 클래스의 getList()

- BoardDBBean.java 중 getList() 부분만

	// 데이터 목록 구하기 : 데이터 10개 추출
	public List<BoardDataBean> getList(int start, int end) {
		List<BoardDataBean> list = new ArrayList<BoardDataBean>();
		
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			con = getConnection();
			
			String sql = "select * from (select rownum rnum, board.* from ";
			sql += "(select * from board order by ref desc, re_step asc) board) ";
			sql += "where rnum >= ? and rnum <= ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, start);
			pstmt.setInt(2, end);
			rs = pstmt.executeQuery();	// SQL문 실행
			
			while(rs.next()) {
				BoardDataBean board = new BoardDataBean();
				
				board.setNum(rs.getInt("num"));
				board.setWriter(rs.getString("writer"));
				board.setEmail(rs.getString("email"));
				board.setSubject(rs.getString("subject"));
				board.setPasswd(rs.getString("passwd"));
				board.setReg_date(rs.getTimestamp("reg_date"));
				board.setReadcount(rs.getInt("readcount"));
				board.setRef(rs.getInt("ref"));
				board.setRe_level(rs.getInt("re_level"));
				board.setRe_step(rs.getInt("re_step"));
				board.setContent(rs.getString("content"));
				board.setIp(rs.getString("ip"));
				
				list.add(board);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(rs != null) try { rs.close(); } catch(Exception e) {}
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}			
		}
		return list;
	}

<getList() 메소드 형식>

- 돌려줄 데이터가 여러개 이므로 List 가 리턴자료형이다

<SQL문>

- 첫번째 서브쿼리는 rownum 컬럼의 별칭을 지정하기위한 쿼리

- 두번째 서브쿼리의 별칭이 board

- 일반 게시판과 달리 댓글 게시판은 num 컬럼이 아닌 ref 컬럼을 기준으로 내림차순 정렬한다

- 원문의 경우에는 num 과 ref 가 같으므로 최근글이 위로 간다

- 부모글과 댓글은 ref 값이 같으므로, 한번 더 정렬해야하므로 re_step 기준 오름차순으로 추가 정렬한다

+ 원문은 re_step 이 0, 댓글은 1,2,3.. 으로 댓글작성 순서대로 re_step 이 됨

<SQL문 실행 후>

- rs 에 저장된 값을 저장하기 위해 DTO 객체 board 를 생성하고 List 에 추가하고 다시 다음 데이터를 board에 저장

- rs 에 들어있는 값을 컬럼명을 통해서 가져온 뒤 board 객체에 setter 메소드로 저장

 

- DTO 객체가 저장한 주솟값이 출력됨

+ 현재 글이 1개이므로 1개의 DTO 객체(글)만 가져왔다, 1개의 주솟값만 출력됨


- 다시 list.jsp 로 돌아와서 돌려받은 데이터를 출력하기

- list.jsp (이어서)

<%@page import="java.text.SimpleDateFormat"%>
<%@page import="reboard.BoardDataBean"%>
<%@page import="java.util.List"%>
<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 1. 한 화면에 출력할 데이터 갯수
	int page_size = 10;

	String pageNum = request.getParameter("page");
	if(pageNum == null) {
		pageNum = "1";	// 1page : 최근글이 보이는 페이지
	}
	
	// 2. 현재 페이지 번호
	int currentPage = Integer.parseInt(pageNum);
	
	// startRow : 각 page에 추출할 데이터의 시작번호
	// endRow : 각 page에 추출할 데이터의 끝번호
	// 1 page : startRow=1,  endRow=10
	// 2 page : startRow=11, endRow=20
	// 3 page : startRow=21, endRow=30
	int startRow = (currentPage - 1) * page_size + 1;  
	int endRow = currentPage * page_size;
	
	// 3. 총 데이터 갯수
	int count = 0;
	
	BoardDBBean dao = BoardDBBean.getInstance();
	count = dao.getCount();
	System.out.println("count : " + count);
	
	List<BoardDataBean> list = null;
	if(count > 0) {
		list = dao.getList(startRow, endRow);	// 데이터 10개 추출
	}
	System.out.println("list : " + list);
	
	if(count == 0) {
%>	
		작성된 글이 없습니다.
<% 	} else { %>
		<a href="writeForm.jsp">글작성</a> 글갯수 : <%=count %>
		<table border=1 width=700 align=center>
			<caption>게시판 목록</caption>
			<tr>
				<th>번호</th>
				<th>제목</th>
				<th>작성자</th>
				<th>작성일</th>
				<th>조회수</th>
				<th>IP주소</th>
			</tr>
<%
			// number : 각 페이지에 출력될 시작 번호
			int number = count - (currentPage - 1) * page_size;
			SimpleDateFormat sd = 
					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			
			for(int i = 0 ; i < list.size() ; i++) {
				BoardDataBean board = list.get(i);
%>
				<tr>
					<td><%=number-- %></td>
					<td><%=board.getSubject() %></td>
					<td><%=board.getWriter() %></td>
					<td><%=sd.format(board.getReg_date()) %></td>
					<td><%=board.getReadcount() %></td>
					<td><%=board.getIp() %></td>
				</tr>
<%			} // for end %>
		</table>
<%	} %>

- 일반게시판 글 목록 출력과 비슷

- count 가 0 이 아닌경우 돌려받은 데이터를 출력

- list.get(i) 로 for문을 이용해서 list 안의 DTO 객체를 하나씩 가져와서 for문에서 새로만든 DTO 객체에 저장 후 출력

- 제목에 링크를 걸어서 상세페이지로 넘어가도록 링크설정하고 글번호와 페이지번호 값을 가져가기 (list.jsp 부분)

- 원문이 아닌 댓글인 경우에는 왼쪽에 여백을 설정하기 (list.jsp 부분)

- 댓글의 깊이인 re_level 만큼 왼쪽에 여백이 생성됨

ex) 1단계 댓글은 조금, 2단계 댓글은 더 많이 벌어짐

- 페이지 링크 설정 ( 1 누르면 1 페이지로 이동하는 버튼) 은 아래에서 이어서 작성

- 현재는 원문 글 뿐이므로 댓글 제목 앞의 여백 확인 불가능

- 글을 몇개 더 작성하기

- SQL Developer 에서 확인

- 모두 원문이므로 num 과 ref 값이 같다

- 모두 원문이므로 re_level, re_step 은 0 이다

 

 

- 페이지 링크 설정 ( 1 누르면 1 페이지로 이동하는 버튼) 이어서

- list.jsp 에서 페이지 링크 설정 부분만 (추가한 부분만)

<!-- 페이지 링크 설정 -->
<center>
<%
if(count > 0) {
	
	// pageCount : 총 페이지 수
	int pageCount = count / page_size + ((count % page_size == 0 ? 0 : 1));
	System.out.println("pageCount : " + pageCount);
	
	// startPage : 각 블럭의 시작 페이지 번호 : 1, 11, 21...
	// endPage : 각 블럭의 끝 페이지 번호 : 	10, 20, 30...
	int startPage = ((currentPage - 1) / 10) * 10 + 1;
	int block = 10;	// 1개 블럭의 크기 : 10개의 페이지로 구성
	int endPage = startPage + block - 1;
	
	// 가장 마지막 블럭에는 endPage값을 pageCount값으로 수정
	if(endPage > pageCount) {	// 마지막 블럭인 경우
		endPage = pageCount;
	}
%>
	<!-- 1페이지로 이동 -->
	<a href="list.jsp?page=1" style="text-decoration:none"> << </a>
<%
	// 이전 블럭으로 이동
	if(startPage > 10) {// 첫 블럭이 아닌 경우 %>
		<a href="list.jsp?page=<%=startPage-10%>">[이전]</a>
<%	} 

	// 각 블럭당 10개의 페이지 출력
	for(int i = startPage; i <= endPage; i++) {
		if(i == currentPage) {	// 현재 페이지 %>
			[<%=i %>]
<% 		} else {	%>
			<a href="list.jsp?page=<%=i%>">[<%=i %>]</a>
<% 		}
	} // for end
	
	// 다음 블럭으로 이동
	if(endPage < pageCount) {	// 마지막 블럭이 아닌 경우
%> 		
		<a href="list.jsp?page=<%=startPage+10%>">[다음]</a>
<%	} %>

	<!-- 마지막 페이지로 이동 -->
	<a href="list.jsp?page=<%=pageCount %>" 
		style="text-decoration:none"> >> </a>
<%} %>

</center>

- 데이터가 적어서 1 페이지만 나온다

- list.jsp 전체 코드 아래에


list.jsp 전체 코드

 

<%@page import="java.text.SimpleDateFormat"%>
<%@page import="reboard.BoardDataBean"%>
<%@page import="java.util.List"%>
<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 1. 한 화면에 출력할 데이터 갯수
	int page_size = 10;

	String pageNum = request.getParameter("page");
	if(pageNum == null) {
		pageNum = "1";	// 1page : 최근글이 보이는 페이지
	}
	
	// 2. 현재 페이지 번호
	int currentPage = Integer.parseInt(pageNum);
	
	// startRow : 각 page에 추출할 데이터의 시작번호
	// endRow : 각 page에 추출할 데이터의 끝번호
	// 1 page : startRow=1,  endRow=10
	// 2 page : startRow=11, endRow=20
	// 3 page : startRow=21, endRow=30
	int startRow = (currentPage - 1) * page_size + 1;  
	int endRow = currentPage * page_size;
	
	// 3. 총 데이터 갯수
	int count = 0;
	
	BoardDBBean dao = BoardDBBean.getInstance();
	count = dao.getCount();
	System.out.println("count : " + count);
	
	List<BoardDataBean> list = null;
	if(count > 0) {
		list = dao.getList(startRow, endRow);	// 데이터 10개 추출
	}
	System.out.println("list : " + list);
	
	if(count == 0) {
%>	
		작성된 글이 없습니다.
<% 	} else { %>
		<a href="writeForm.jsp">글작성</a> 글갯수 : <%=count %>
		<table border=1 width=700 align=center>
			<caption>게시판 목록</caption>
			<tr>
				<th>번호</th>
				<th>제목</th>
				<th>작성자</th>
				<th>작성일</th>
				<th>조회수</th>
				<th>IP주소</th>
			</tr>
<%
			// number : 각 페이지에 출력될 시작 번호
			int number = count - (currentPage - 1) * page_size;
			SimpleDateFormat sd = 
					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			
			for(int i = 0 ; i < list.size() ; i++) {
				BoardDataBean board = list.get(i);
%>
				<tr>
					<td><%=number-- %></td>
					<td>
<% 
					// 댓글 제목 앞에 여백 주기
					if(board.getRe_level() > 0) {	// 댓글인 경우
						for(int j = 1; j <= board.getRe_level(); j++) {	%>
							&nbsp;&nbsp;
<%						}
					}
%>
<a href="content.jsp?num=<%=board.getNum()%>&page=<%=currentPage%>">			
					<%=board.getSubject() %>
</a>					
					</td>
					<td><%=board.getWriter() %></td>
					<td><%=sd.format(board.getReg_date()) %></td>
					<td><%=board.getReadcount() %></td>
					<td><%=board.getIp() %></td>
				</tr>
<%			} // for end %>
		</table>
<%	} %>

<!-- 페이지 링크 설정 -->
<center>
<%
if(count > 0) {
	
	// pageCount : 총 페이지 수
	int pageCount = count / page_size + ((count % page_size == 0 ? 0 : 1));
	System.out.println("pageCount : " + pageCount);
	
	// startPage : 각 블럭의 시작 페이지 번호 : 1, 11, 21...
	// endPage : 각 블럭의 끝 페이지 번호 : 	10, 20, 30...
	int startPage = ((currentPage - 1) / 10) * 10 + 1;
	int block = 10;	// 1개 블럭의 크기 : 10개의 페이지로 구성
	int endPage = startPage + block - 1;
	
	// 가장 마지막 블럭에는 endPage값을 pageCount값으로 수정
	if(endPage > pageCount) {	// 마지막 블럭인 경우
		endPage = pageCount;
	}
%>
	<!-- 1페이지로 이동 -->
	<a href="list.jsp?page=1" style="text-decoration:none"> << </a>
<%
	// 이전 블럭으로 이동
	if(startPage > 10) {// 첫 블럭이 아닌 경우 %>
		<a href="list.jsp?page=<%=startPage-10%>">[이전]</a>
<%	} 

	// 각 블럭당 10개의 페이지 출력
	for(int i = startPage; i <= endPage; i++) {
		if(i == currentPage) {	// 현재 페이지 %>
			[<%=i %>]
<% 		} else {	%>
			<a href="list.jsp?page=<%=i%>">[<%=i %>]</a>
<% 		}
	} // for end
	
	// 다음 블럭으로 이동
	if(endPage < pageCount) {	// 마지막 블럭이 아닌 경우
%> 		
		<a href="list.jsp?page=<%=startPage+10%>">[다음]</a>
<%	} %>

	<!-- 마지막 페이지로 이동 -->
	<a href="list.jsp?page=<%=pageCount %>" 
		style="text-decoration:none"> >> </a>
<%} %>

</center>

댓글게시판 : 글 상세페이지 (상세 정보 구하기)

- 다음으로 상세페이지 content.jsp 를 생성하자

<%@page import="java.text.SimpleDateFormat"%>
<%@page import="reboard.BoardDataBean"%>
<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%
	int num = Integer.parseInt(request.getParameter("num"));
	String nowpage = request.getParameter("page");
	
	BoardDBBean dao = BoardDBBean.getInstance();
	
	// 조회수 1 증가 + 상세 정보 구하기
	BoardDataBean board = dao.updateContent(num);
	
	// 부모글 정보 구하기
	int ref = board.getRef();
	int re_level = board.getRe_level();
	int re_step = board.getRe_step();
	
	SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	String content = board.getContent().replace("\n","<br>");
%>


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세 페이지</title>
</head>
<body>
<table border=1 width=400 align=center>
	<caption>상세 페이지</caption>
	<tr>
		<td>번호</td>
		<td><%=board.getNum() %></td>
		<td>조회수</td>
		<td><%=board.getReadcount() %></td>
	</tr>
	<tr>
		<td>작성자</td>
		<td><%=board.getWriter() %></td>
		<td>작성일</td>
		<td><%=sd.format(board.getReg_date()) %></td>
	</tr>
	<tr>
		<td>제목</td>
		<td colspan=3><%=board.getSubject() %></td>
	</tr>
	<tr>
		<td>내용</td>
		<td colspan=3>
 			<pre><%=board.getContent() %></pre>
			<%=content %>
		</td>
	</tr>
	<tr>
		<td colspan=4 align=center>
			<input type="button" value="댓글">
			<input type="button" value="수정">
			<input type="button" value="삭제">
			<input type="button" value="목록">
		</td>
	</tr>
</table>
</body>
</html>

- list.jsp 에서 넘어온 글번호 num 과 페이지번호 page 값을 getParameter()로 받기

- setInt() 의 매개변수로 쓰기 위해 num 을 int 형으로 형변환

- 조회수 1 증가 + 상세 정보 구하기 를 하는 DAO 의 updateContent() 메소드 호출

- 다음은 updatateContent() 에서 돌려받은 상세 정보 중 댓글과 관련된 3가지 컬럼값을 따로 변수를 만들어서 저장

- 내용의 줄을 바꾸기 위해 board.getContent() 를 replace() 처리해서  새로 만든 content 변수에 저장

- 그 뒤 테이블에 상세 정보 출력

- 댓글 버튼을 포함한 4개의 버튼 생성

- 댓글 버튼을 누르면 댓글 작성 폼으로 넘어감

- 버튼 처리 부분 설명 아래에서 할 것

 

- 먼저 DAO 클래스에 updateContent() 메소드를 생성하자

- 한개의 정보를 검색하므로 리턴 자료형은 DTO, 글번호가 필요하므로 매개변수로 num 전달


DAO 클래스 조회수 1 증가 & 상세 정보 구하기 메소드작성

- BoardDBBean.java  중 updateContent() 메소드 부분만

	// 상세 페이지 : 조회수 1 증가 시키고 상세정보 구하기
	public BoardDataBean updateContent(int num) {
		
		BoardDataBean board = new BoardDataBean();
		
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			con = getConnection();
			
			String sql = "update board set readcount=readcount+1 ";
			sql += "where num=?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, num);
			pstmt.executeUpdate();	// SQL문 실행
			
			sql = "select * from board where num=?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, num);
			rs = pstmt.executeQuery();	// SQL문 실행
			
			if(rs.next()) {
				board.setNum(rs.getInt("num"));
				board.setWriter(rs.getString("writer"));
				board.setEmail(rs.getString("email"));
				board.setSubject(rs.getString("subject"));
				board.setPasswd(rs.getString("passwd"));
				board.setReg_date(rs.getTimestamp("reg_date"));
				board.setReadcount(rs.getInt("readcount"));
				board.setRef(rs.getInt("ref"));
				board.setRe_level(rs.getInt("re_level"));
				board.setRe_step(rs.getInt("re_step"));
				board.setContent(rs.getString("content"));
				board.setIp(rs.getString("ip"));
			}
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if(rs != null) try { rs.close(); } catch(Exception e) {}
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}					
		}
		return board;
	}

- 한개의 정보를 리턴하므로 리턴 자료형은 DTO, 글 번호로 상세 정보를 구하므로 매개변수는 int 형

- 2번의 SQL문을 실행해야한다

1. 조회수 1 증가 update문

2. 상세 정보 구하기 select문

+ DAO 전체 코드는 마지막에

 

- 이후 list.jsp 에서 글을 클릭 하면 상세 페이지로 들어갈 수 있다


상세페이지 버튼 처리

1. 목록 버튼

			<input type="button" value="목록" 
			onClick="location.href='list.jsp?page=<%=nowpage%>'">

- 목록페이지 list.jsp 로 이동하면서 원래 페이지로 돌아가기 위해 nowpage 값을 사용

+ 쌍따옴표 중첩이 되지 않으므로 " " 안에는 외따옴표 ' ' 를 써준다

 

2. 댓글 버튼

			<input type="button" value="댓글"
			onClick="location.href='replyForm.jsp?num=<%=num%>&page=<%=nowpage%>&ref=<%=ref%>&re_level=<%=re_level%>&re_step=<%=re_step%>'">

- 댓글을 작성할 때, 부모글에 대한 정보(변수) 를 가져가야한다

- 일반게시판에서는 2가지인 num 과 page 만 가져갔지만 댓글게시판에선 5가지 변수를 가져가야함

1. num : 부모글의 번호를 가져야함

2. page : 원래 페이지로 돌아가기 위해 가져야함

3. ref : 부모글과 같은 ref 값을 가져야하므로 부모의 ref 값을 가져가야함

4. re_level : 부모글이 0 이면 댓글은 1 이 증가한 1 이어야하므로 부모의 re_level 값을 가져가야함

5. re_step : 부모글 다음 1 증가 된 값이 들어가야하므로 부모의 re_step 값을 가져가야함

= '댓글' 버튼 클릭시 값이 잘 넘어가는지 확인


댓글게시판 : 댓글 작성 기능

- 이제 댓글 작성 폼인 replyForm.jsp 파일을 생성 후 작성

+ 원문 글 작성 폼과 댓글 작성 폼을 따로 만들고 있다, 그래야 sequence 사용 가능

- replyForm.jsp

<%@ page contentType="text/html; charset=utf-8" %>
<%@ include file="color.jsp"%>
<html>
<head>
<title>게시판</title>
<link href="style.css" rel="stylesheet" type="text/css">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="check.js"></script>
</head>

<% 
  int num=0,ref=1,re_step=0,re_level=0;	// 원문
  String nowpage = "";
 
   if(request.getParameter("num")!=null){  // 답글
		num=Integer.parseInt(request.getParameter("num"));
		ref=Integer.parseInt(request.getParameter("ref"));
		re_step=Integer.parseInt(request.getParameter("re_step"));
		re_level=Integer.parseInt(request.getParameter("re_level"));
		nowpage = request.getParameter("page");
   }
%>
   
<body bgcolor="<%=bodyback_c%>">  
<center><b>글쓰기</b>
<br>
<form method="post"  action="replyPro.jsp">
<input type="hidden" name="num" value="<%=num%>">
<input type="hidden" name="ref" value="<%=ref%>">
<input type="hidden" name="re_step" value="<%=re_step%>">
<input type="hidden" name="re_level" value="<%=re_level%>">
<input type="hidden" name="page" value="<%=nowpage%>">

<table width="400" border="1" cellspacing="0" cellpadding="0"  bgcolor="<%=bodyback_c%>" align="center">
   <tr>
    <td align="right" colspan="2" bgcolor="<%=value_c%>">
	    <a href="list.jsp"> 글목록</a> 
   </td>
   </tr>
   <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center">이 름</td>
    <td  width="330">
       <input type="text" size="10" maxlength="10" id="writer" name="writer"></td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >제 목</td>
    <td  width="330">
    <%if(request.getParameter("num")==null){%> <!--  원문 -->
       <input type="text" size="40" maxlength="50" id="subject" name="subject"></td>
	<%}else{%>									<!-- 답글 -->
	   <input type="text" size="40" maxlength="50" id="subject" name="subject" value="Re."></td>
	<%}%>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center">Email</td>
    <td  width="330">
       <input type="text" size="40" maxlength="30" id="email" name="email" ></td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >내 용</td>
    <td  width="330" >
     <textarea id="content" name="content" rows="13" cols="40"></textarea> </td>
  </tr>
  <tr>
    <td  width="70"  bgcolor="<%=value_c%>" align="center" >비밀번호</td>
    <td  width="330" >
     <input type="password" size="8" maxlength="12" id="passwd" name="passwd"> 
	 </td>
  </tr>
<tr>      
 <td colspan=2 bgcolor="<%=value_c%>" align="center"> 
  <input type="submit" value="글쓰기" >  
  <input type="reset" value="다시작성">
  <input type="button" value="목록보기" OnClick="window.location='list.jsp'">
</td></tr></table>    
    
</form>      
</body>
</html>

- writeForm.jsp 의 내용을 복붙 후 수정

- form의 action 을 replyPro.jsp 로 수정 후 hidden 으로 페이지번호 가져가는 코드 추가

- try 주석 부분을 풀고 이전 페이지에서 넘어온 값을 저장하기

- 전에서 넘어온 값인 num 값이 null 이 아닐때 == '부모글이 있을때', 즉, '댓글이면' 이라는 의미

- hidden 을 포함하여 총 10가지 값이 replyPro.jsp 로 넘어가고 있다

- 또, 댓글의 제목에는 "Re." 를 value 로 설정

- 댓글 작성 폼

 

- 다음으로는 replyPro.jsp 파일을 생성 후 작성

<%@page import="reboard.BoardDBBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%
	request.setCharacterEncoding("utf-8");
%>

<jsp:useBean id="board" class="reboard.BoardDataBean"/>
<jsp:setProperty property="*" name="board"/>

<%
	board.setIp(request.getRemoteAddr());
	String nowpage = request.getParameter("page");
	
	BoardDBBean dao = BoardDBBean.getInstance();
	int result = dao.reply(board);	// 댓글 작성
	
	if(result == 1) {
%>		
		<script>
			alert("댓글 작성 성공");
			location.href="list.jsp?page=<%=nowpage%>";
		</script>
<%	} else { %>
		<script>
			alert("댓글 작성 실패");
			history.go(-1);
		</script>
<%	}%>

1. replyForm.jsp 에서 post 방식으로 한글값이 넘어오므로 한글값 인코딩

2. useBean, setProperty action tag 로 폼에서 넘어온 값을 DTO 객체 board에 저장

3. ip 주소는 폼에서 넘어오지 않으므로 직접 setIp() 로 저장 

4. 그리고 넘어온 페이지번호는 DTO 프로퍼티에 없으므로 DTO 객체에 저장불가, 따로 다른 변수 nowpage 를 생성해서 저장

5. DAO 의 reply() 메소드로 댓글 작성

- 댓글작성할떄 필요한 값들을 저장한 DTO 객체 board 를 매개변수로 전달

6. 댓글 작성 성공시 목록페이지 list.jsp 로 이동, 원래 페이지로 이동하기 위해 nowpage 값 사용

 

+ DAO 클래스의 댓글 작성 기능을 하는 reply() 메소드

- DAO reply() 메소드가 어렵다

- 2가지 SQL 문을 사용할 것

- 댓글 관련 3가지 컬럼 중 re_step 컬럼 작업이 어려움

- re_step : 원문은 0으로 고정 , 댓글, 대댓글은 고정된 값이 아닌 계속해서 바뀜

- update 문으로 re_step 값을 증가시켜줘야함

 

DAO 클래스 댓글 작성 메소드 작성

- BoardDBBean.java 중 reply() 부분 코드만

	// 댓글 작성
	public int reply(BoardDataBean board) {
		int result = 0;
		
		// 부모글에 대한 정보
		int ref = board.getRef();
		int re_step = board.getRe_step();
		int re_level = board.getRe_level();
		
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
// 1. 원문이 부모인 경우
//		: 원문이 re_step=0 이기 떄문에, 모든 댓글들의 re_step 값이 1 증가 된다.
// 2. 댓글이 부모인 경우
//		: 부모의 re_step 보다 큰 댓글만 re_step 값이 1 증가 된다.
			
			String sql = "update board set re_step=re_step+1 ";
			sql += "where ref=? and re_step > ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, ref);		// 부모의 ref
			pstmt.setInt(2, re_step); 	// 부모의 re_step
			
			pstmt.executeUpdate();		// SQL문 실행
			
			sql = "insert into board values(board_seq.nextval,?,?,?,?,";
			sql += "sysdate,?,?,?,?,?,?)";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, board.getWriter());
			pstmt.setString(2, board.getEmail());
			pstmt.setString(3, board.getSubject());
			pstmt.setString(4, board.getPasswd());
			pstmt.setInt(5, 0); 			// readcount
			pstmt.setInt(6, ref); 			// ref
			pstmt.setInt(7, re_step+1); 	// re_step
			pstmt.setInt(8, re_level+1); 	// re_level
			pstmt.setString(9, board.getContent());
			pstmt.setString(10, board.getIp());
			
			result = pstmt.executeUpdate();	// SQL문 실행
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}							
		}
			
		return result;
	}

1. 객체 board 에 저장되어 있는 부모글에 대한 정보를 구해온다

- hidden 으로 넘어왔던 부모글의 정보들이다

- ref, re_step, re_level 이다

2. SQL 문을 2번 실행, 첫번째로 update 문

- update문으로 re_step 을 1 증가시킨다

- update문을 ref 가 부모의 ref 와 같고 re_step 이 부모의 re_step 보다 클때만 실행하라는 where 문의 조건

3. 두번째로 댓글 삽입 위한 insert
- 원문에서는 ref 자리 ?에 sequence 가 들어갔지만, 댓글에선 ref 자리 ? 에 부모의 ref 가 들어가야함

- ref 자리에는 부모의 ref 값이 들어간다

- re_step 자리에는 부모의 re_step 보다 1 증가한 값이 들어간다

- re_level 자리에는 부모의 re_level 보다 1 증가한 값이 들어간다

 

4. 원문이 부모인 경우와, 댓글이 부모인 경우를 나눠서 생각해야한다

- 그럼 두 경우 update문 where 부분의 의미가 달라진다

- update문에서 re_step 이 1 증가되는건 

 

1) 원문이 부모인 경우 (가장 바깥쪽 글 = 원문)
- 원문이 re_step=0 이기 떄문에, update 문에 의해 새 댓글을 달면 그 원문의 기존에 있던 모든 댓글들의 re_step 값이 1 증가 된다.

- where 문에서 ref 가 부모의 ref 값과 같을때 라는 의미는 '그 원문과 원문의 댓글들 집합 중에서' 라는 의미

- 부모의 re_step 값은 0 이므로 항상 re_step > 0 을 만족하므로 re_step 이 1 증가

- 그리고 원문의 기존 댓글들을 모두 re_step 을 1 증가 시킨 후, 본인의 re_step 값을 부모의 re_step 값보다 1 큰, 즉 1을 삽입
2) 댓글이 부모인 경우
- 부모의 re_step 보다 큰 댓글만 re_step 값이 1 증가 된다.

- 부모보다 큰 re_step 값을 가진 댓글만 re_step 이 증가됨

- 대댓글을 쓸때는 ref=부모의 ref 라는 where문의 첫번째 조건에서, 원문글, 그 글 댓글, 그 글 대댓글을 모두 포함 범위

- 그 다음의 where 조건인 부모의 re_step 보다 큰 값 이라는 의미는,  부모 댓글보다 먼저 달린 모든 댓글과 그 댓글의 대댓글들을 의미

 

* 아래에 정리되어있다

 

- '다섯번째 원문' 에 댓글을 달아보자

- 부모글과 댓글은 ref 값이 7 로 동일하다

- 1단계 댓글은 re_level 이 1

- 첫번째 댓글은 re_step 은 현재 1 이지만, 새로운 댓글을 달면 2로 바뀐다, 이 역할을 update 문이 한다

 

- 댓글을 하나 더 달아보자

- 나중에 단 댓글이 댓글 중 가장 위에 나타나고, re_step 값이 1이 된다

- 첫번째 댓글은 re_step 값이 2가 된다

- 즉 부모가 원문인 경우에는 insert 문에서 re_step 이 부모의 re_step 보다 1 증가한 값이 삽입됨

- 그렇기때문에 ref 기준 내림차순 정렬 다음으로 추가로 re_step 기준 오름차순 정렬할때 정렬되어 부모글 바로 아래에 최근 댓글이 출력됨

 

- 세번째 댓글을 달자

 

- 이제 '두번째 댓글' 에 대댓글을 달아보자

- '1대댓글'을 작성시 update 문으로 '첫번째 댓글' 의 re_step 값이 부모인 '두번째 댓글' 의 re_step 값보다 크므로  '첫번째 댓글' 의 re_step 값을 1 증가시킴

- 그 후 insert 문으로 부모인 '두번째 댓글' 의 re_step 값보다 1 증가한 값인 3을 자신의 re_step 값으로 설정


reply() 메소드 SQL문 정리

- 원문이 부모일때, 댓글이 부모일떄를 나눠서 생각해야한다

* 주의 : 작성 순서를 자세히 보기

* 먼저 달린 댓글 = re_step 값이 크다, 즉 부모보다 큰 re_step 값을 가진 댓글은 부모보다 먼저 달린 댓글임

 

 

1. 원문이 부모일때

1-1) update 문

- 조건 a. 'ref= 부모의 ref' 의미 = 기존에 그 부모 원문글에 달려있던 댓글들 지칭

- 조건 b. 're_step > 부모의 re_step' 의미 = 부모의 re_step 은 0 이므로 그 원문의 모든 댓글 지칭

- 조건 a & b. 그 부모의 모든 댓글을 의미

- 조건 a & b 에 해당하는 데이터의 re_step 값을 1 증가시킴

 

1-2) insert 문

- ref 값은 부모와 똑같이 설정

- re_level 은 부모보다 +1 을 해서 1 로 설정

- re_step 은 부모보다 +1 을 해서 1 로 설정, 기존의 댓글들이 모두 update 문에 의해 re_step값이 +1 되어서 2부터 시작하므로 새 댓글의 re_step 값을 1로 설정할 수 있다

 

2. 원문이 댓글일때

2-1) update 문

- 조건 a. 'ref= 부모의 ref' 의미 = 기존에 그 부모 댓글의 원문 + 그 원문의 모든 댓글들 + 그 원문의 대댓글들 (즉 그림에서 빨간색으로 묶은 모든 글)

- 조건 b. 're_step > 부모의 re_step' 의미 = 그 부모 댓글과 형제인 댓글 중 부모 댓글보다 먼저 달린 모든 댓글과, 그 댓글의 대댓글들

- 조건 a & b. 그 부모댓글보다 먼저 달린 모든 댓글과, 그 댓글의 대댓글들 의미

- 조건 a & b 에 해당하는 데이터의 re_step 값을 1 증가시킴

 

2-2) insert 문

- ref 값은 부모와 똑같이 설정

- re_level 은 부모보다 +1 을 해서 2 로 설정

- re_step 은 부모보다 +1 을 해서 설정, 그러므로 부모 바로 밑에 출력된다, 부모댓글보다 먼저 작성되었던 댓글과 그 댓글의 대댓글들이 모두 update 문에 의해 re_step 값이 +1 이 되어서 비어있는 숫자를 가지는 것

+ 그림처럼 정렬하기 위해서 이 작업을 해야한다, 단순 작성 최신순이 아님, re_step 기준으로 오름차순 정렬이다


+ Recent posts