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++) { %>
<% }
}
%>
<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 기준으로 오름차순 정렬이다