국비 32

냐아암·2023년 5월 31일
0

국비

목록 보기
45/114

게시판 메뉴

SQL (JDBC 게시판 프로그램)

ALTER SESSION SET "_ORACLE_SCRIPT"=true;

-- [관리자 계정] MEMBER 계정 생성
CREATE USER khj_member IDENTIFIED BY member1234;

-- [관리자 계정] CONNECT, RESOURCE + CREATE VIEW 권한 부여 + 객체 생성 공간 할당
GRANT CONNECT, RESOURCE, CREATE VIEW TO khj_member;
ALTER USER khj_member DEFAULT TABLESPACE SYSTEM QUOTA UNLIMITED ON SYSTEM;

-- khj_member 계정 접속 방법 추가

-- [멤버 계정]
CREATE TABLE MEMBER(
    MEMBER_NO NUMBER PRIMARY KEY,
    MEMBER_ID VARCHAR2(20) NOT NULL,
    MEMBER_PW VARCHAR2(20) NOT NULL,
    MEMBER_NM VARCHAR2(30) NOT NULL,
    MEMBER_GENDER CHAR(1) CHECK (MEMBER_GENDER IN ('M','F')),
    ENROLL_DATE DATE DEFAULT SYSDATE,
    SECESSION_FL CHAR(1) DEFAULT 'N' CHECK (SECESSION_FL IN ('Y','N'))
);
COMMENT ON COLUMN MEMBER.MEMBER_NO IS '회원번호(PK)';
COMMENT ON COLUMN MEMBER.MEMBER_ID IS '회원 아이디';
COMMENT ON COLUMN MEMBER.MEMBER_PW IS '회원 비밀번호';
COMMENT ON COLUMN MEMBER.MEMBER_NM IS '회원 이름';
COMMENT ON COLUMN MEMBER.MEMBER_GENDER IS '회원 성별(M/F)';
COMMENT ON COLUMN MEMBER.ENROLL_DATE IS '회원 가입일';
COMMENT ON COLUMN MEMBER.SECESSION_FL IS '탈퇴여부(Y/N)';


-- 게시판 테이블
DROP TABLE BOARD;

CREATE TABLE BOARD(
    BOARD_NO NUMBER PRIMARY KEY,
    BOARD_TITLE VARCHAR2(200) NOT NULL,
    BOARD_CONTENT VARCHAR2(4000) NOT NULL,
    CREATE_DATE DATE DEFAULT SYSDATE,
    READ_COUNT NUMBER DEFAULT 0,
    MEMBER_NO NUMBER REFERENCES MEMBER -- MEMBER 테이블 PK 값 참조

);

COMMENT ON COLUMN BOARD.BOARD_NO IS '게시글 번호';
COMMENT ON COLUMN BOARD.BOARD_TITLE IS '게시글 제목';
COMMENT ON COLUMN BOARD.BOARD_CONTENT IS '게시글 내용';
COMMENT ON COLUMN BOARD.CREATE_DATE IS '게시글 작성일';
COMMENT ON COLUMN BOARD.READ_COUNT IS '조회수';
COMMENT ON COLUMN BOARD.MEMBER_NO IS '회원번호 (작성자)';

-- 댓글 테이블
DROP TABLE REPLY;

CREATE TABLE REPLY (
    REPLY_NO NUMBER PRIMARY KEY,
    REPLY_CONTENT VARCHAR2(500) NOT NULL,
    CREATE_DATE DATE DEFAULT SYSDATE,
    MEMBER_NO NUMBER REFERENCES MEMBER, -- MEMBER 테이블 PK 참조
    BOARD_NO NUMBER REFERENCES BOARD -- BOARD 테이블 PK 참조

);

COMMENT ON COLUMN REPLY.REPLY_NO IS '댓글 번호(PK)';
COMMENT ON COLUMN REPLY.REPLY_CONTENT IS '댓글 내용';
COMMENT ON COLUMN REPLY.CREATE_DATE IS '댓글 작성일';
COMMENT ON COLUMN REPLY.MEMBER_NO IS '회원번호(작성자)';
COMMENT ON COLUMN REPLY.BOARD_NO IS '게시글 번호(어떤 게시글의 댓글인지 확인)';

-- 각 테이블 PK 생성용 시퀀스 생성
CREATE SEQUENCE SEQ_MEMBER_NO; -- 1부터 1씩 증가, 반복X
CREATE SEQUENCE SEQ_BOARD_NO;
CREATE SEQUENCE SEQ_REPLY_NO;

-- 게시글 목록 조회 + 댓글 개수(상관 서브쿼리 + 스칼라 서브쿼리)
-- BOARD 테이블: BOARD_NO, BOARD_TITLE, BOARD_CONTENT, CREATE_DATE, READ_COUNT, MEMBER_NO
SELECT B.BOARD_NO, BOARD_TITLE, CREATE_DATE, READ_COUNT, MEMBER_NM,
    (SELECT COUNT(*) FROM REPLY R
     WHERE R.BOARD_NO = B.BOARD_NO) REPLY_COUNT
FROM BOARD B
JOIN MEMBER USING(MEMBER_NO)
ORDER BY BOARD_NO DESC;
-- 게시글 번호가 크다 == 최신 글이다.

-- 댓글 개수 조회(특정 게시글만)
SELECT COUNT(*) FROM REPLY
WHERE BOARD_NO = 3;


-- BOARD 테이블 샘플 데이터
INSERT INTO BOARD 
VALUES(SEQ_BOARD_NO.NEXTVAL, '샘플 게시글 1', '샘플1 내용입니다.', DEFAULT, DEFAULT, 1);

INSERT INTO BOARD 
VALUES(SEQ_BOARD_NO.NEXTVAL, '샘플 게시글 2', '샘플2 내용입니다.', DEFAULT, DEFAULT, 1);

INSERT INTO BOARD 
VALUES(SEQ_BOARD_NO.NEXTVAL, '샘플 게시글 3', '샘플3 내용입니다.', DEFAULT, DEFAULT, 1);

COMMIT;

-- 댓글 샘플 데이터 삽입
-- REPLY_NO, REPLY_CONTENT, CREATE_DATE, MEMBER_NO, BOARD_NO
INSERT INTO REPLY
VALUES(SEQ_REPLY_NO.NEXTVAL, '샘플1의 댓글1', DEFAULT, 1, 1);

INSERT INTO REPLY
VALUES(SEQ_REPLY_NO.NEXTVAL, '샘플1의 댓글2', DEFAULT, 1, 1);

INSERT INTO REPLY
VALUES(SEQ_REPLY_NO.NEXTVAL, '샘플1의 댓글3', DEFAULT, 1, 1);

COMMIT;


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- 특정 게시글 상세 조회
SELECT B.*, MEMBER_NM 
FROM BOARD B
JOIN MEMBER M ON(B.MEMBER_NO = M.MEMBER_NO)
WHERE BOARD_NO = 1;

-- 특정 게시글에 댓글 목록 조회
SELECT R.* , MEMBER_NM
FROM REPLY R
JOIN MEMBER M ON(R.MEMBER_NO = M.MEMBER_NO)
WHERE BOARD_NO = 1
--ORDER BY REPLY_NO DESC; -- 최근 댓글이 상단
ORDER BY REPLY_NO; -- 최근 댓글이 하단
-- 댓글 목록에서 최근 작성한 글은 제일 위?   제일 아래?
--                             SNS   카페, 커뮤니티

-- 게시글 수 증가
-- 이전 조회수 +1을 조회수 컬럼에 대입
UPDATE BOARD
SET READ_COUNT = READ_COUNT + 1
WHERE BOARD_NO = ?;

DELETE FROM REPLY
WHERE BOARD_NO =1;


DELETE FROM BOARD
WHERE BOARD_NO =1;

ROLLBACK;

DELETE FROM BOARD WHERE BOARD_NO=1;
-- ORA-02292: 무결성 제약조건(KHJ_MEMBER.SYS_C007694)이 위배되었습니다- 자식 레코드가 발견되었습니다

ROLLBACK;

SELECT * FROM BOARD;


SELECT * FROM BOARD WHERE BOARD_NO=1;
SELECT * FROM BOARD WHERE BOARD_NO=1;
--> 기본적으로 삭제 불가
--> 삭제 옵션을 추가하면 가능
--> ON DELETE SET NULL(자식 컬럼 NULL) / ON DELETE CASCADE(참조하던 자식 행도 삭제)

-- 제약조건은 ALLTER(변경) 없음 -> 삭제 후 다시 추가

-- 기존 REPLY 테이블에 FK 제약조건 삭제
ALTER TABLE REPLY DROP CONSTRAINT SYS_C007694;

-- 삭제 옵션이 추가된 FK 다시 추가
ALTER TABLE REPLY
ADD FOREIGN KEY(BOARD_NO)
REFERENCES BOARD 
ON DELETE CASCADE;
-- Table REPLY이(가) 변경되었습니다.

-- 다시 1번 게시글 삭제 시도
DELETE FROM BOARD WHERE BOARD_NO =1;

SELECT * FROM BOARD WHERE BOARD_NO=1;
SELECT * FROM REPLY WHERE BOARD_NO=1;

ROLLBACK;

-- 게시글 수정
UPDATE BOARD
SET BOARD_TITLE = ?, BOARD_CONTENT = ?
WHERE BOARD_NO = ?;

SELECT * FROM BOARD;

-- 댓글 작성
INSERT INTO REPLY 
VALUES( SEQ_REPLY_NO.NEXTVAL, ?, DEFUALT, ?, ?);

UPDATE REPLY
SET REPLY_CONTENT = 'oo'
WHERE REPLY_NO = 4;

SELECT * FROM REPLY;

DELETE FROM REPLY
WHERE REPLY_NO = ?;

COMMIT;

-- 게시글 작성
INSERT INTO BOARD
VALUES(SEQ_BOARD_NO.NEXTVAL, BOARD_TITLE, BOARD_CONTENT, CREATE_DATE, READ_COUNT, MEMBER_NO);

-- 게시글 검색
SELECT BOARD_NO, BOARD_TITLE, CREATE_DATE, READ_COUNT, MEMBER_NM, 
    (SELECT COUNT(*) FROM REPLY R
    WHERE R.BOARD_NO = B.BOARD_NO) REPLY_COUNT
FROM BOARD B
JOIN MEMBER USING(MEMBER_NO)
WHERE 1=1
--AND BOARD_CONTENT LIKE '%' || ? || '%' ; -- 내용 검색

--AND BOARD_TITLE LIKE '%' || ? || '%' ; -- 제목 검색

--AND BOARD_CONTENT LIKE '%' || ? || '%' 
--OR BOARD_TITLE LIKE '%' || ? || '%'; -- 제목 + 내용

AND MEMBER_NM LIKE '%' || ? || '%' -- 작성자 검색
ORDER BY BOARD_NO DESC;


SELECT BOARD_NO, BOARD_TITLE, CREATE_DATE, READ_COUNT, MEMBER_NM, 
    (SELECT COUNT(*) FROM REPLY R
    WHERE R.BOARD_NO = B.BOARD_NO) REPLY_COUNT
FROM BOARD B
JOIN MEMBER USING(MEMBER_NO)
WHERE 1=1
AND BOARD_TITLE LIKE '%밤%' ;

CreateXML

package edu.kh.jdbc.common;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class CreateXML { // 혼자서 실행하면 파일 사라집니다. 주의!!!!
	
	// XML(eXtensible Markup Language): 단순화된 데이터 기술 형식
	
	// XML 사용하려는 이유
	// - DB 연결, SQL같이 수정이 빈번한 내용을
	//   코드에 직접 작성하면 좋지 않다.
	
	//--> Java == 컴파일 언어 -> 코드가 조금만 수정되어도 전체 컴파일 다시 함
	//						--> 시간이 오래 걸림
	
	// 그런데, XML 외부 파일을 이용해서 XML 내용을 바꿔도
	// Java에서 XML 파일을 읽어오는 코드는 변하지 않음 --> 컴파일 X --> 시간 효율 상승
	
	public static void main(String[] args) {
		
		// XML은 K : V 형식의 map, XML은 문자열만 저장
		
		// Map<String, String> == Properties
		
		// * Properties 컬렉션 객체 *
		// 1. <String, String> 으로 Key, Value가 타입 제한된 Map
		// 2. XML 파일을 생성하고 읽어오는 데 특화
		
		Properties prop = new Properties();
		
		try {
			
			FileOutputStream fos = new FileOutputStream("board-sql.xml");
														// 파일 이름
									// 주석
			prop.storeToXML(fos, "Board Service SQL"); // XML 파일 생성
			// surround try catch
			
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	

}

board-sql.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>Board Service SQL</comment>
	
	<!-- 게시글 목록 조회 + 댓글 개수 -->
	<entry key="selectAll">
		SELECT B.BOARD_NO, BOARD_TITLE, CREATE_DATE, READ_COUNT, MEMBER_NM,
		    (SELECT COUNT(*) FROM REPLY R
		    WHERE R.BOARD_NO = B.BOARD_NO) REPLY_COUNT
		FROM BOARD B
		JOIN MEMBER USING(MEMBER_NO)
		ORDER BY BOARD_NO DESC
	</entry>

	<!-- 특정 게시물 상세 조회 -->
	<entry key ="selectOne">
		SELECT B.*, MEMBER_NM 
		FROM BOARD B
		JOIN MEMBER M ON(B.MEMBER_NO = M.MEMBER_NO)
		WHERE BOARD_NO = ?
	</entry>
	
	<!-- 특정 게시물 댓글 목록 조회 -->
	<entry key = "selectReplyList">
		SELECT R.* , MEMBER_NM
		FROM REPLY R
		JOIN MEMBER M ON(R.MEMBER_NO = M.MEMBER_NO)
		WHERE BOARD_NO = ?
		ORDER BY REPLY_NO
	</entry>
	
	<!-- 게시글 조회수 증가 -->
	<entry key="increaseReadCount">
		UPDATE BOARD
		SET READ_COUNT = READ_COUNT + 1
		WHERE BOARD_NO = ?
	</entry>
	
	<!-- 게시글 댓글 삭제(숙제) -->
	<entry key="deleteReply2">
		DELETE FROM REPLY
		WHERE BOARD_NO =?
	</entry>
	
	<!-- 게시글 삭제(숙제) -->
	<entry key ="deleteBoard2">
		DELETE FROM BOARD
		WHERE BOARD_NO =?
	</entry>
	
	<!-- 게시글 삭제 -->
	<entry key ="deleteBoard">
		DELETE FROM BOARD 
		WHERE BOARD_NO =?
	</entry>

	<!-- 게시글 수정 -->
	<entry key = "updateBoard">
		UPDATE BOARD
		SET BOARD_TITLE = ?, BOARD_CONTENT = ?
		WHERE BOARD_NO = ?
	</entry>
	
	<!-- 댓글 작성 -->
	<entry key = "insertReply">
		INSERT INTO REPLY 
		VALUES( SEQ_REPLY_NO.NEXTVAL, ?, DEFAULT, ?, ?)
	</entry>
	
	<!-- 댓글 수정 -->
	
	<entry key = "updateReply">
		UPDATE REPLY
		SET REPLY_CONTENT = ?
		WHERE REPLY_NO = ?
	</entry>
	
	<!-- 댓글 삭제 -->
	<entry key = "deleteReply">
		DELETE FROM REPLY
		WHERE REPLY_NO = ?
	</entry>
	
	<!-- 게시글 작성 -->
	<entry key = "insertBoard">
		INSERT INTO BOARD
		VALUES(SEQ_BOARD_NO.NEXTVAL, ?, ?, DEFAULT, DEFAULT, ?)
	</entry>
	
	<!-- 게시글 검색 1 -->
	<entry key = "searchBoard1">
		SELECT BOARD_NO, BOARD_TITLE, CREATE_DATE, READ_COUNT, MEMBER_NM, 
	    (SELECT COUNT(*) FROM REPLY R
	    WHERE R.BOARD_NO = B.BOARD_NO) REPLY_COUNT
		FROM BOARD B
		JOIN MEMBER USING(MEMBER_NO)
	</entry>
	
	<!-- 게시글 검색 2 -->
	<entry key = "searchBoard2">
		ORDER BY BOARD_NO DESC
	</entry>
	
	<!-- 검색조건 1(제목) -->
	<entry key = "condition1">
		WHERE BOARD_TITLE LIKE '%' || ? || '%' 
	</entry>
	
	<!-- 검색조건 2(내용) -->
	<entry key = "condition2">
		WHERE BOARD_CONTENT LIKE '%' || ? || '%'
	</entry>
	
	<!-- 검색조건 3(제목 + 내용) -->
	<entry key = "condition3">
		WHERE BOARD_CONTENT LIKE '%' || ? || '%' 
    	OR BOARD_TITLE LIKE '%' || ? || '%'
	</entry>
	
	<!-- 검색조건 4(작성자) -->
	<entry key = "condition4">
		WHERE MEMBER_NM LIKE '%' || ? || '%'
	</entry>
	
	

</properties>

MainView는 Member 게시글 참고

BoardView

package edu.kh.jdbc.board.view;

import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;

import edu.kh.jdbc.board.model.service.BoardService;
import edu.kh.jdbc.board.model.vo.Board;
import edu.kh.jdbc.board.model.vo.Reply;
import edu.kh.jdbc.member.model.vo.Member;

// 게시판 메뉴 전용 화면
public class BoardView {
	
	private Scanner sc = new Scanner(System.in);
	
	private BoardService service = new BoardService();

	/** 게시판 전용 메뉴 화면
	 * @param loginMember (로그인한 회원 정보)
	 */
	public void boardMenu(Member loginMember) {
		
		int menuNum = -1;
		
		do {
			
			try {
				
				System.out.println("\n********** 게시판 메뉴 **********\n");
	            
	            System.out.println("1. 게시글 목록 조회");
	            System.out.println("2. 게시글 상세 조회(게시글 번호 입력)");
	            				// 게시글 작성자와 로그인한 회원이 같을 때
	            				// 게시글 수정(UPDATE), 게시글 삭제(DELETE)
	            System.out.println("3. 게시글 작성(INSERT)");
	            System.out.println("4. 게시글 검색(제목/내용/제목+내용/작성자)");
	            
	            System.out.println("0. 회원 메뉴로 돌아가기");
				
				System.out.print("메뉴를 선택해주세요: ");
				menuNum = sc.nextInt();
				sc.nextLine(); // 개행 문자 제거
				
				System.out.println(); // 줄바꿈
				
				
				switch(menuNum) {
				case 1: selectAll(); break;
				case 2: selectOne(loginMember); break;
				// 상세 조회 시 게시글 수정 / 삭제(게시글 작성 == 로그인한 회원 비교)
				// 댓글(누가 작성? / 작성자가 수정, 삭제 확인)
				// -> loginMember를 매개변수로 전달
				case 3: insertBoard(loginMember.getMemberNo()); break;
				case 4: searchBoard(); break;
				case 0: System.out.println("회원 메뉴로 돌아갑니다."); break;
				default : System.out.println("메뉴에 작성된 번호를 입력해주세요."); break;
				
				
				
				}
				
				
				
				
			} catch (InputMismatchException e) {
				System.out.println("\n입력 형식이 올바르지 않습니다. 다시 입력해주세요.|");
				sc.nextLine(); // 입력버퍼에 남은 잘못된 문자열 제거
			}
			
			
		} while(menuNum != 0);
		
		
	}

	/**
	 * 게시글 검색
	 */
	private void searchBoard() {
		
		System.out.println("\n[게시글 검색]\n");
		
		int menuNum = -1;
		
		do {
			
			try {
				
				System.out.println("-- 검색 조건을 선택해주세요 --");
				System.out.println("1. 제목");
				System.out.println("2. 내용");
				System.out.println("3. 제목 + 내용");
				System.out.println("4. 작성자");
				System.out.println("0. 돌아가기");
				
				System.out.print("선택 >> ");
				menuNum = sc.nextInt();
				sc.nextLine();
				
				switch(menuNum) {
				case 0: System.out.println("\n게시판 메뉴로 돌아갑니다...\n"); break;
				case 1: case 2: case 3: case 4: 
					// 검색어 입력 -> Service 호출
					
					System.out.print("검색어: ");
					String keyword = sc.nextLine();
					
					List<Board> boardList = service.searchBoard(menuNum, keyword);
													// case 1,2,3,4 구분하기 위해
					if(boardList.isEmpty()) {
						// 검색 결과가 비어있다 == 검색 결과가 없다.
						System.out.println("\n검색 결과가 없습니다.\n");
						
					}else {
						System.out.println("------------------------------------------------------------------------");
						System.out.printf("%3s  %13s%12s   %7s%3s %7s%2s %s\n", "글번호", "제목", "", "작성자", "", "작성일", "",
								"조회수");
						System.out.println("------------------------------------------------------------------------");

						// 향상된 for문
						for (Board b : boardList) {

							System.out.printf("%3d  %20s [%d]  %10s  %s %3d\n", b.getBoardNo(), b.getBoardTitle(),
									b.getReplyCount(), b.getMemberName(), b.getCreateDate().toString(),
									b.getReadCount());
						}
					}
					
					break;
				default : System.out.println("\n메뉴에 작성된 번호를 입력해주세요.\n");
				
				
				}
				
			} catch (InputMismatchException e) {
				System.out.println("\n입력 형식이 올바르지 않습니다. 다시 시도해주세요.\n");
				sc.nextLine(); // 입력버퍼에 남은 잘못된 문자 제거
				
			} catch (Exception e) {
				System.out.println("\n<게시글 검색 중 예외 발생>\n");
				e.printStackTrace();
				break; // 검색 반복문 종료
			}
			
		} while(menuNum != 0);
		
	}

	/** 게시글 작성
	 * @param memberNo
	 */
	private void insertBoard(int memberNo) {
		
		System.out.println("\n[게시글 작성]\n");
		
		System.out.print("게시글 제목: ");
		String boardTitle = sc.nextLine();
		
		System.out.println("\n게시글 내용(종료 시 @exit 입력)\n");
		
		String boardContent = inputContent();
		
		Board board = new Board();
		
		board.setBoardTitle(boardTitle);
		board.setBoardContent(boardContent);
		board.setMemberNo(memberNo);
		
		try {
			int result = service.insertBoard(board);
			
			if(result >0 ) {
				System.out.println("게시글 작성 성공");
			} else {
				System.out.println("게시글 작성 실패");
			}
			
		} catch (Exception e) {
			System.out.println("게시글 작성 중 예외 발생");
			e.printStackTrace();
		}
		
		
	}

	/**
	 * 게시글 목록 조회
	 */
	private void selectAll() {
		
		System.out.println("[게시글 목록 조회]");
		
		try {
			
			// 게시글 목록 조회 Service 호출 후 결과 반환 받기
			List<Board> boardList = service.selectAll();
			
			if(boardList.isEmpty()) { // 조회 결과가 없는 경우
				System.out.println("\n[조회된 게시글이 없습니다.]\n");
				
			}else {
				// 숙제: 출력구문 꾸미기
				
				
				System.out.println("------------------------------------------------------------------------");
	            System.out.printf("%3s  %13s%12s   %7s%3s %7s%2s %s\n",
	                     "글번호", "제목", "", "작성자", "", "작성일", "" , "조회수");
	            System.out.println("------------------------------------------------------------------------");
	            
	            // 향상된 for문
	            for(Board b : boardList) {
	               
	               System.out.printf("%3d  %20s [%d]  %10s  %s %3d\n",
	                     b.getBoardNo(), b.getBoardTitle(), b.getReplyCount(),
	                     b.getMemberName(), b.getCreateDate().toString(), b.getReadCount());
	            }
			}
			
			
			
		} catch (Exception e) {
			System.out.println("\n<게시글 목록 조회 중 예외 발생>\n");
			e.printStackTrace();
		}
		
	}
	
	/** 게시글 상세 조회
	 * @param loginMember
	 */
	private void selectOne(Member loginMember) {
		
		System.out.println("[게시글 상세 조회]");
		
		System.out.print("조회할 게시글 번호 입력: ");
		
		int boardNo = sc.nextInt();
		sc.nextLine();
		
		// 게시글 상세 조회 Service 호출 후 결과 반환(게시글 1개의 정보 == Board)
		try {
			
			Board board = service.selectOne(boardNo);
			
			if (board != null) { // 조회된 게시글이 있을 경우
				
				// 상세 조회 출력
	            System.out.println("\n------------------------------------------------------------");
	            System.out.printf("번호 : %d     |  제목 : %s\n", board.getBoardNo(), board.getBoardTitle());
	            System.out.println("------------------------------------------------------------");
	            System.out.printf("작성자 : %s\n"
	                        + "작성일 : %s\n"
	                        + "조회수 : %d\n", 
	                        board.getMemberName(), board.getCreateDate(), board.getReadCount());
	            System.out.println("------------------------------------------------------------");
	            System.out.printf("\n%s\n\n", board.getBoardContent());
	            System.out.println("------------------------------------------------------------");
	            
	            // 댓글 목록 조회
	            System.out.println("\n[댓글]");
	            for( Reply r : board.getReplyList() ) {
	               System.out.printf("<%d> | %s | %s\n", 
	                     r.getReplyNo(), r.getMemberName(), r.getCreateDate());
	               
	               System.out.println(r.getReplyContent());
	               System.out.println(".............................................................\n");
	            }

				// -------------------------------------------
				// 상세 조회용 메뉴 출력

				System.out.println("===== 상세 조회 메뉴 =====");

				System.out.println("1. 댓글 삽입"); // 어떤 회원이든 가능
				
				// 댓글 번호 입력 받아
				// 댓글을 작성한 회원 번호 == 로그인한 회원 번호
				// -> 수정/ 삭제
				System.out.println("2. 댓글 수정"); 
				System.out.println("3. 댓글 삭제");
				// 댓글 번호 입력 -> 댓글이 있는지 확인 -> 해당 댓글이 로그인한 회원 것이 맞는지 확인

				// 상세 조회된 게시글의 회원번호(작성자) == 로그인한 회원번호(로그인한 사람)
				// -> 게시글 수정 / 삭제
				if(board.getMemberNo() == loginMember.getMemberNo()) {
					System.out.println("4. 게시글 수정");
					System.out.println("5. 게시글 삭제");
					
				}

				System.out.println("0. 게시판 메뉴로 돌아가기");

				System.out.print("메뉴 선택 >> ");
				int menuNum = sc.nextInt();
				sc.nextLine();
				
				// 코드 복잡해지니까 그냥 while문 빼고 진행
				switch(menuNum) {
				case 0: System.out.println("\n게시판 메뉴로 돌아갑니다...\n"); break;
				case 1: insertReply(loginMember, boardNo); break;
				case 2: case 3: 
					
					String temp = menuNum == 2 ? "\n[댓글 수정]\n" : "\n[댓글 삭제]\n";
					System.out.println(temp);
					
					System.out.print("댓글 번호 입력: ");
					int inputNo = sc.nextInt();
					sc.nextLine();
					
					// 입력받은 댓글 번호가 댓글 목록에 있는지 확인
					
					Reply reply = null; // 확인된 댓글을 참조할 변수
					
					for(Reply r : board.getReplyList()) { // 반복 접근
						
						if(r.getReplyNo() == inputNo) { // 입력받은 번호와 일치하는 댓글이 있다면
							
							reply = r; // reply 객체에 일치하는 댓글 객체 주소를 담겠다.
							break;
							
						}
					}
					
					if(reply == null) { // 같은 댓글 번호가 목록에 없는 경우
						System.out.println("\n해당 댓글이 존재하지 않습니다.\n");
					} else { // 같은 댓글 번호가 목록에 있는 경우
						
						// 해당 댓글의 회원번호 (작성자)와 로그인한 회원 번호가 같은지 확인
						// -> 같을 경우 로그인한 사람의 댓글이다.
						if(loginMember.getMemberNo() == reply.getMemberNo() ) {
							
							if(menuNum ==2) updateReply(inputNo); // 댓글 수정
							else			deleteReply(inputNo); // 댓글 삭제
							
						} else {
							System.out.println("\n현재 로그인한 회원의 댓글이 아닙니다.\n");
						}
						
						
						
						
					}
					
					break;
				
				case 4: case 5: 
					// 게시글 작성자 번호 == 로그인 회원번호
					if(board.getMemberNo() == loginMember.getMemberNo()) {
						
						// 4번
						if(menuNum == 4) { // 4번 게시글 수정
							
							updateBoard(boardNo); // 수정용 메소드
							
							
							
						} else { // 5번 게시글 삭제
							deleteBoard(boardNo); // 삭제용 메소드
							
						}
						
						
						
					} else {
						System.out.println("메뉴에 표시된 번호만 입력해주세요.");
					}
					
					break;
				default : System.out.println("메뉴에 표시된 번호만 입력해주세요.");
				}
	            
	            
			} else { // 조회된 게시글이 없을 경우 board == null
				System.out.println("\n존재하지 않는 게시글 번호입니다.\n");
				
			}
			
		} catch (Exception e) {
			System.out.println("\n<게시글 상세 조회 중 예외 발생>\n");
			e.printStackTrace();
		}
		
	}

	/** 댓글 삭제
	 * @param inputNo
	 */
	private void deleteReply(int inputNo) {
		
		char ch = ' '; // Y/N 저장
		
		while(true) {
			
			System.out.print("정말 삭제하시겠습니까?(Y/N) ");
			ch = sc.next().toUpperCase().charAt(0);
			
			if(ch == 'Y' || ch == 'N') { // Y 또는 N인 경우 추가 입력 X(반복 종료)
				break;
			} else {
				System.out.println("Y 또는 N을 입력해주세요.\n");
			}
			
		}
		
		if(ch == 'Y') { // 삭제하려고 하는 경우
			
			// 보안문자 생성
			String cap = capcha();
			
			System.out.println("다음 보안문자를 입력해주세요 >> " + cap);
			
			System.out.print("보안문자 입력: ");
			String input = sc.next();
		
			if(input.equals(cap)) {
				
				try {
					
					int result = service.deleteReply(inputNo);
					
					if(result > 0) {
						System.out.println(inputNo + "번 댓글이 삭제되었습니다.\n");
					}else {
						System.out.println("\n댓글 삭제 실패\n");
					}
					
				} catch (Exception e) {
					System.out.println("\n<댓글 삭제 중 예외 발생>\n");
					e.printStackTrace();
				}
				
			} else {
				System.out.println("\n보안문자 불일치\n");
			}
		
		}
		
	}

	/** 댓글 수정
	 * @param inputNo
	 */
	private void updateReply(int inputNo) {
		
		System.out.println("수정할 내용을 입력 (종료 시 @exit 입력)");
		
		String replyContent = inputContent();
		
		Reply reply = new Reply();
		
		reply.setReplyNo(inputNo);
		reply.setReplyContent(replyContent);
		
		try {
			
			int result = service.updateReply(reply);
			
			if(result > 0) {
				System.out.println(inputNo + "번 댓글이 수정되었습니다.\n");
			} else {
				System.out.println("\n댓글 수정 실패\n");
			}
			
		} catch (Exception e) {
			System.out.println("\n댓글 수정 중 오류 발생\n");
		}
		
		
		
		
	}

	/** 댓글 작성
	 * @param loginMember
	 * @param boardNo
	 */
	private void insertReply(Member loginMember, int boardNo) {
		
		System.out.println("\n[댓글 작성]\n");
		
		System.out.println("댓글 내용 입력(종료 시 @exit 입력)\n");
		
		String replyContent = inputContent();
		
		// 회원 번호, 게시글 번호, 댓글 내용 -> 하나의 Reply 객체에 저장
		Reply reply = new Reply();
		
		reply.setMemberNo(loginMember.getMemberNo());
		reply.setBoardNo(boardNo);
		reply.setReplyContent(replyContent);
		
		try {
			
			// 댓글 삽입(INSERT) Service 호출 후 결과 반환 받기
			int result = service.insertReply(reply);
			
			if(result > 0) {
				System.out.println("\n댓글이 작성되었습니다.\n");
			} else {
				System.out.println("댓글 작성 실패");
			}
			
			
			
			
		} catch (Exception e) {
			System.out.println("\n<댓글 작성 중 예외 발생>\n");
			e.printStackTrace();
		}
		
		
		
	}

	/** 게시글 수정
	 * @param boardNo
	 * @return
	 */
	private void updateBoard(int boardNo) {
		
		System.out.println("\n[게시글 수정]\n");
		
		System.out.print("수정할 제목: ");
		String boardTitle = sc.nextLine();
		
		System.out.println("\n수정할 내용(종료 시 @exit 입력)\n");
		
		String boardContent = inputContent(); // 내용 입력 메소드 호출 후 결과 반환 받기
		
		// 게시글 번호 / 수정한 제목 + 내용을 한 번에 저장할 Board 객체 생성
		Board board = new Board();
		
		board.setBoardNo(boardNo);
		board.setBoardTitle(boardTitle);
		board.setBoardContent(boardContent);
		
		try {
			int result = service.updateBoard(board);
			
			if(result > 0) {
				System.out.println(boardNo + "번 게시글이 수정되었습니다.\n");
				
			} else {
				System.out.println("수정 실패");
			}
			
		} catch (Exception e) {
			System.out.println("\n<게시글 수정 중 예외 발생>\n");
			e.printStackTrace();
		}
		
	}
	
	/* String(객체)
	 * - 불변성(immutable) <-> 가변성(mutable)
	 * 
	 * -> 한 번 생성된 String 객체에 저장된 문자열은 변하지 않는다.
	 * 
	 * ex) String str = "abc"; // Heap 영역에 String 객체가 생성되고
	 * 						   // 생성된 객체에 "abc" 문자열 저장
	 * 
	 * 		str = "123";  // Heap 영역에 새로운 String 객체가 생성되고
	 * 					  // 생성된 객체에 "123" 문자열 저장 후
	 * 					  // 객체 주소를 str에 대입
	 * 
	 * ex) String str = "abc";
	 * 
	 * 		str += "123"; // "123"이 저장된 String 객체 생성 후
	 * 					  // "abc"와 "123"이 합쳐진 String 객체가 추가로 별도 생성
	 * 					  //  그 후 "abc123" 객체의 주소를 str에 저장
	 * 
	 * 
	 *  ** String 문제점 **
	 *  
	 *  - String에 저장된 값을 바꾸거나 누적하려 할 때마다
	 *    String 객체가 무분별하게 생성됨 --> 메모리 낭비(메모리 누수)
	 *    
	 *  ** 해결 방법 **
	 *  - StringBuffer / StringBuilder(가변성)
	 *    클래스를 자바에서 제공함
	 * 
	 * 
	 *   (StringBuffer / StringBuilder는 사용 방법은 똑같음)
	 *   -> 차이점은 동기 / 비동기 차이밖에 없음
	 * 
	 * 
	 * 
	 * 
	 * */
	
	/** 게시글/댓글 내용 입력 메소드
	 * @return content
	 */
	private String inputContent() {
		
//		String content = "";
		StringBuffer content = new StringBuffer(/*n*/); // 크기는 가변성
		
		String input = null;
		
		while (true) { // @exit가 입력될 때까지 무한히 문자열을 입력받아
					   // 하나의 변수에 누적 == 게시글 내용

			input = sc.nextLine();

			if (input.equals("@exit")) { // @exit 입력된 경우
				break;
			} else { // 줄 바꾸며 누적
//				content += input + "\n";
				content.append(input);
				content.append("\n");
				// StringBuffer에 저장된 문자열의 제일 뒤에 input을 추가하겠다(누적)
				// append: (제일 뒤에)덧붙이다. 추가하다. 첨부하다.
				
				// -> 하나의 StringBuffer 객체에 문자열이 계속 누적됨 == 가변
				//	  (추가적인 String 객체 생성이 없다.)
			}

		}
		
		// 반환 타입 String
		return content.toString(); // StringBuffer에 오버라이딩된 toString()
								   // -> 저장된 문자열을 String 형태로 반환
		
	}

	/** 게시글 삭제
	 * @param boardNo
	 * @throws Exception 
	 */
	private void deleteBoard(int boardNo) {
		
		// "정말 삭제하시겠습니까?(Y?N)" -- 제대로 입력될 때까지 무한 반복
		// -> "Y" 입력 시 
		// -> 보안문자 생성
		// -> 보안문자 일치하는 경우에만 삭제 진행
		
		System.out.println("\n[게시글 삭제]\n");
		
		char ch = ' '; // Y/N 저장
		
		while(true) {
			
			System.out.print("정말 삭제하시겠습니까?(Y/N) ");
			ch = sc.next().toUpperCase().charAt(0);
			
			if(ch == 'Y' || ch == 'N') { // Y 또는 N인 경우 추가 입력 X(반복 종료)
				break;
			} else {
				System.out.println("Y 또는 N을 입력해주세요.\n");
			}
			
		}
		
		if(ch == 'Y') { // 삭제하려고 하는 경우
			
			// 보안문자 생성
			String cap = capcha();
			
			System.out.println("다음 보안문자를 입력해주세요 >> " + cap);
			
			System.out.print("보안문자 입력: ");
			String input = sc.next();
			
			if(input.equals(cap)) { // 입력받은 문자열과 보안문자가 같을 때
				// 숙제 삭제 구문 완성해오기
			
				try {
//					int result = service.deleteBoard2(boardNo);
					int result = service.deleteBoard(boardNo);
					
					if(result > 0 ) {
						System.out.println(boardNo + "번 게시글이 삭제되었습니다.");
					} else {
						System.out.println("삭제 실패");
					}
				} catch (Exception e) {
					System.out.println("\n<게시판 삭제 중 예외 발생>\n");
					e.printStackTrace();
				}
				
			} else {
				// 취소
				System.out.println("\n보안문자가 일치하지 않습니다.(삭제 취소)\n");
			}
			
			
		} else { // 삭제 취소
			System.out.println("\n삭제를 취소했습니다.\n");
		}
		
	}

	/** 보안문자 생성 메소드(랜덤 영어 소문자 5개)
	 * @return cap
	 */
	private String capcha() {
		
		String cap = "";
		
		for(int i=0; i<5; i++) {
			cap += (char)(Math.random()*26 + 'a'); 
							// int 형변환: 97 ~ 122
							// char 형변환: 'a' ~ 'z'
		}
		return cap;
	}

}

BoardService

package edu.kh.jdbc.board.model.service;

// import static: static 필드/ 메소드 호출 시 클래스명 생략
// * 기호: 모두, 전부 (All)
import static edu.kh.jdbc.common.JDBCTemplate.*;

import java.sql.Connection;
import java.util.List;

import edu.kh.jdbc.board.model.dao.BoardDAO;
import edu.kh.jdbc.board.model.vo.Board;
import edu.kh.jdbc.board.model.vo.Reply;

public class BoardService {
	
	private BoardDAO dao = new BoardDAO();

	/** 게시글 목록 조회하는 Service
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> selectAll() throws Exception {
		
		// 1) Connection 생성
		Connection conn = getConnection();
		
		// 2) DAO 메소드 (SELECT) 호출 후 결과 반환 받기
		List<Board> boardList = dao.selectAll(conn);
		
		// 3) Connection 반환
		close(conn);
		
		// 4) DAO 수행 결과를 View에 반환
		return boardList;
		
	}

	/** 게시글 상세 조회 Service
	 * @param boardNo
	 * @return board
	 * @throws Exception
	 */
	public Board selectOne(int boardNo) throws Exception {
		
		// 1) Connection 생성
		Connection conn = getConnection();
		
		// 2) 특정 게시글 상세 조회 DAO 메소드(SELECT) 호출 후 결과 반환 받기
		Board board = dao.selectOne(conn, boardNo);
		
		if(board != null) { // 2)번 게시글 상세 조회 내용이 있을 경우에만
			
			// 3-1) 특정 게시글 댓글 목록 조회 DAO 메소드(SELECT) 호출 후 결과 반환 받기
			List<Reply> replyList = dao.selectReplyList(conn, boardNo);
			
			// Board 객체의 replyList 필드에 조회한 댓글 목록을 대입(세팅)
			// return은 하나만 반환 가능
			board.setReplyList(replyList); //!!!!!!!!!!!!!!!!!!!!!!!
			
			// 3-2) 게시글 조회수 증가 DAO 메소드(UPDATE) 호출 후 결과(성공한 행의 개수 반환)
			int result = dao.increaseReadCount(conn, boardNo);
			// increase: 증가V
			
			// 트랜잭션 제어 처리 + 조회수 동기화
			if(result > 0) {
				commit(conn); 
				
				// DB에는 READ_COUNT 업데이트
				// -> 업데이트 전에 게시글 정보를 조회했음
				// -> 조회된 게시글 조회수가 DB 조회수보다 1 낮음
				// -> 조회된 게시글의 조회수를 +1 시켜서 DB와 동기화
				board.setReadCount(board.getReadCount()+1);
				
			}else {
			rollback(conn);
			}
			
		}
		
		// 4) Connection 반환
		close(conn);
		
		// 5) DAO 수행 결과 View로 반환
			
		return board; // 게시글 상세 조회 + 댓글 목록
	}

	/** 게시글 삭제
	 * @param boardNo
	 * @throws Exception
	 */
	public int deleteBoard2(int boardNo) throws Exception{
		
		Connection conn = getConnection();
		
		int result = dao.deleteBoard2(conn, boardNo);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
		
	}

	/** 게시글 삭제 Service
	 * @param boardNo
	 * @return
	 */
	public int deleteBoard(int boardNo) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.deleteBoard(conn, boardNo);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 게시글 수정 Service
	 * @param board
	 * @return result
	 * @throws Exception
	 */
	public int updateBoard(Board board) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.updateBoard(conn, board);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 댓글 작성 Service
	 * @param reply
	 * @return result
	 * @throws Exception
	 */
	public int insertReply(Reply reply) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.insetReply(conn, reply);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 댓글 수정 Service
	 * @param inputNo
	 * @return result
	 * @throws Exception
	 */
	public int updateReply(Reply reply) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.updateReply(conn, reply);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
		
	}

	/** 댓글 삭제 Service
	 * @param inputNo
	 * @return result
	 * @throws Exception
	 */
	public int deleteReply(int inputNo) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.deleteReply(conn, inputNo);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 게시글 작성
	 * @param board
	 * @return result
	 * @throws Exception
	 */
	public int insertBoard(Board board) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.insertBoard(conn, board);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 게시글 검색
	 * @param menuNum
	 * @param keyword
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> searchBoard(int menuNum, String keyword) throws Exception {
		
		Connection conn= getConnection();
		
		List<Board> boardList = dao.searchBoard(conn, menuNum, keyword);
		
		close(conn);
		
		return boardList;
	}

}

BoardDAO

package edu.kh.jdbc.board.model.dao;

import static edu.kh.jdbc.common.JDBCTemplate.*;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import edu.kh.jdbc.board.model.vo.Board;
import edu.kh.jdbc.board.model.vo.Reply;

public class BoardDAO {
	
	//JDBC 객체 참조용 변수 선언(java.sql)
	private Statement stmt; 
	private PreparedStatement pstmt;
	private ResultSet rs;
	// 객체의 heap 메모리는 비어있을 수 없으므로 자동으로 null 값이 됨
	
	// SQl 내용을 저장할 Properties 객체 참조 변수 선언
	private Properties prop;
	
	// 기본 생성자(board-sql.xml 파일 읽어오기(Properties)
	public BoardDAO() {
		
		try {
			
			prop = new Properties();
			
			// XML 파일 읽기
			prop.loadFromXML(new FileInputStream("board-sql.xml"));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/** 게시글 목록 조회 DAO
	 * @param conn
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> selectAll(Connection conn) throws Exception {
		
		// 결과 저장용 변수
		List<Board> boardList = new ArrayList<Board>();
		
		try {
			
			// 1) SQL 작성
			String sql = prop.getProperty("selectAll");
			
			// 2) Statement 객체 생성
			stmt = conn.createStatement();
			
			// 3) SQL 수행(SELECT) 후 결과 반환받기(ResultSet)
			rs = stmt.executeQuery(sql);
			
			// 4) ResultSet을 한 행씩 (rs.next()) 모두 접근
			while(rs.next()) {
				
				// 5) 현재 행에서 컬럼명을 이용해서 컬럼값 얻어오기
				int boardNo = rs.getInt("BOARD_NO");
				String boardTitle = rs.getString("BOARD_TITLE");
				Date createDate = rs.getDate("CREATE_DATE");
				int readCount = rs.getInt("READ_COUNT");
				String memberName = rs.getString("MEMBER_NM");
				int replyCount = rs.getInt("REPLY_COUNT");
				
				// 6) Board 객체를 생성하여 컬럼값 담기
				Board board = new Board(boardNo, boardTitle, createDate, readCount, memberName, replyCount);
				
				// 7) Board 객체를 boardList에 추가
				boardList.add(board);
				
			}
			
		} finally {
			
			// 8) JDBC 자원 반환(Connection 제외)
			close(rs);
			close(stmt);
			
		}
		
		// 결과 반환
		return boardList;
	}

	/** 특정 게시글 상세 조회 DAO
	 * @param conn
	 * @param boardNo
	 * @return board
	 * @throws Exception
	 */
	public Board selectOne(Connection conn, int boardNo) throws Exception {
		
		Board board = null; // 결과 저장용 변수
		
		try {
			
			// 1) SQL 작성
			String sql = prop.getProperty("selectOne");
			
			// 2) PreparedStatement 생성
			pstmt = conn.prepareStatement(sql);
			
			// 3) 위치홀더 '?' 알맞은 값 세팅
			pstmt.setInt(1, boardNo);
			
			// 4) SQL 수행(SELECT) 후 결과 반환 받기 (Result Set)
			rs = pstmt.executeQuery();
			
			// 5) 조회된 한 행(if)이 있을 경우 조회된 컬럼 값 얻어오기
			if(rs.next()) {
				
//				int boardNo = rs.getInt("BOARD_NO");
				// --> 입력받은 boardNo와 조회된 BOARD_NO은 같으므로
				//	   굳이 DB 조회 결과에서 얻어오지 않아도 된다.
				String boardTitle = rs.getString("BOARD_TITLE");
				Date createDate = rs.getDate("CREATE_DATE");
				int readCount = rs.getInt("READ_COUNT");
				String memberName = rs.getString("MEMBER_NM");
				String boardContent = rs.getString("BOARD_CONTENT");
				int memberNo = rs.getInt("MEMBER_NO");
				
				
				// 6) Board 객체를 생성하여 컬럼 값 세팅
				board = new Board();
				
				board.setBoardNo(boardNo); // 매개변수를 세팅
				board.setBoardTitle(boardTitle);
				board.setBoardContent(boardContent);
				board.setCreateDate(createDate);
				board.setReadCount(readCount);
				board.setMemberName(memberName);
				board.setMemberNo(memberNo);
				
				
			}
			
		} finally {
			
			// 7) 사용한 JDBC 자원 반환
			close(rs);
			close(pstmt);
			
		}
		// 결과 반환
		return board;
		
	}

	/** 특정 게시글 댓글 목록 조회 DAO
	 * @param conn
	 * @param boardNo
	 * @return replyList
	 * @throws Exception
	 */
	public List<Reply> selectReplyList(Connection conn, int boardNo) throws Exception{
		
		List<Reply> replyList = new ArrayList<Reply>(); // 결과 저장용 변수
		
		try {
			
			// 1) SQL문 작성
			String sql = prop.getProperty("selectReplyList");
			
			// 2) PreparedStatement 생성
			pstmt = conn.prepareStatement(sql);
			
			// 3) 위치홀더 '?'에 알맞은 값 세팅
			pstmt.setInt(1, boardNo);
			
			// 4) SQL(SELECT) 수행 후 결과 (ResultSet) 반환 받기
			rs = pstmt.executeQuery();
			
			// 5) 조회된 결과를 한 행씩 접근 (while(rs.next))
			// -> 각 행별로 컬럼 값 얻어오기
			while(rs.next()) {
				
				int replyNo = rs.getInt("REPLY_NO");
				String replyContent = rs.getString("REPLY_CONTENT");
				Date createDate = rs.getDate("CREATE_DATE");
				int memberNo = rs.getInt("MEMBER_NO");
				String memberName = rs.getString("MEMBER_NM");
				// boardNo는 매개변수 사용
				
				// 6) Reply 객체 생성하여 컬럼 값 담기
				Reply reply = new Reply();
				
				reply.setReplyNo(replyNo);
				reply.setReplyContent(replyContent);
				reply.setCreateDate(createDate);
				reply.setMemberNo(memberNo);
				reply.setMemberName(memberName);
				reply.setBoardNo(boardNo);
				
				
				// 7) replyList에 Reply 객체 추가
				replyList.add(reply);
				
			}
			
		} finally {
			
			// 9) JDBC 자원 반납
			close(rs);
			close(pstmt);
			
		}
		
		return replyList;
	}

	/** 게시글 조회수 증가 DAO
	 * @param conn
	 * @param boardNo
	 * @return result
	 * @throws Exception
	 */
	public int increaseReadCount(Connection conn, int boardNo) throws Exception {
		
		int result = 0; // 결과 저장용 변수
		
		try {
			String sql = prop.getProperty("increaseReadCount");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, boardNo);
			
			result = pstmt.executeUpdate();
			
		} finally {
			
			close(pstmt);
			
		}
		
		return result;
	}
	

	public int deleteBoard2(Connection conn, int boardNo) throws Exception {
		
		int result = 0;
		
		
		try {
			String sql1 = prop.getProperty("deleteReply2");
			
			pstmt = conn.prepareStatement(sql1);
			
			pstmt.setInt(1, boardNo);
			
			result = pstmt.executeUpdate();
			
			String sql2 = prop.getProperty("deleteBoard2");
			
			pstmt = conn.prepareStatement(sql2);
			System.out.println("test");
			pstmt.setInt(1, boardNo);
			
			result = pstmt.executeUpdate();
			
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/** 게시글 삭제 DAO
	 * @param conn
	 * @param boardNo
	 * @return result
	 * @throws Exception
	 */
	public int deleteBoard(Connection conn, int boardNo) throws Exception{
		
		 int result = 0;
	      
	      try {
	         
	         String sql = prop.getProperty("deleteBoard");
	      
	         pstmt = conn.prepareStatement(sql);
	      
	         pstmt.setInt(1, boardNo);
	      
	         result = pstmt.executeUpdate();
	      
	      } finally {
	         close(pstmt);
	      }
	      
	      return result;

	}

	/** 게시글 수정 DAO
	 * @param conn
	 * @param board
	 * @return result
	 * @throws Exception
	 */
	public int updateBoard(Connection conn, Board board) throws Exception {
		
		int result = 0;
		
		try {
			
			String sql = prop.getProperty("updateBoard");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, board.getBoardTitle());
			pstmt.setString(2, board.getBoardContent());
			pstmt.setInt(3, board.getBoardNo());
			
			result = pstmt.executeUpdate();
			
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/** 댓글 작성 DAO
	 * @param reply
	 * @return result
	 * @throws Exception
	 */
	public int insetReply(Connection conn, Reply reply) throws Exception {
		
		int result = 0;
		
		try {
			
			String sql = prop.getProperty("insertReply");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, reply.getReplyContent());
			pstmt.setInt(2, reply.getMemberNo());
			pstmt.setInt(3, reply.getBoardNo());
			
			result = pstmt.executeUpdate();
			
			
		} finally {
			
			close(pstmt);
			
		}
		
		return result;
	}

	/** 댓글 수정 DAO
	 * @param conn
	 * @param inputNo
	 * @return result
	 * @throws Exception
	 */
	public int updateReply(Connection conn, Reply reply) throws Exception {
		
		int result = 0;
		
		try {
			
			String sql = prop.getProperty("updateReply");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, reply.getReplyContent());
			pstmt.setInt(2, reply.getReplyNo());
			
			result = pstmt.executeUpdate();
			
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/** 댓글 삭제 DAO
	 * @param inputNo
	 * @return result
	 * @throws Exception
	 */
	public int deleteReply(Connection conn, int inputNo) throws Exception {
		
		int result = 0;
		
		try {
			
			String sql = prop.getProperty("deleteReply");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, inputNo);
			
			result = pstmt.executeUpdate();
			
		} finally {
			
			close(pstmt);
			
		}
		
		return result;
	}

	/** 게시글 작성
	 * @param conn
	 * @param board
	 * @return result
	 * @throws Exception
	 */
	public int insertBoard(Connection conn, Board board) throws Exception {
		
		int result = 0;
		
		try {
			
			String sql = prop.getProperty("insertBoard");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, board.getBoardTitle());
			pstmt.setString(2, board.getBoardContent());
			pstmt.setInt(3, board.getMemberNo());
			
			result = pstmt.executeUpdate();
			
		} finally {
			
			close(pstmt);
		}
		
		return result;
	}

	/** 게시글 검색 DAO
	 * @param conn
	 * @param menuNum
	 * @param keyword
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> searchBoard(Connection conn, int menuNum, String keyword) throws Exception {
		
		// 결과 저장용 변수
		List<Board> boardList = new ArrayList<>();
		
		try {
			// SQL 작성(menuNum에 따라서 SQL 조합)
			String sql = prop.getProperty("searchBoard1") 
					+ prop.getProperty("condition" + menuNum) 
					+ prop.getProperty("searchBoard2");
			
			pstmt = conn.prepareStatement(sql);
			
			// 위치홀더에 알맞은 값 세팅
			// 주의사항 !!!!!!!!!!
			// -> 제목 + 내용을 검색하는 조건(3번)은 혼자만 위치홀더 2개이다.
			pstmt.setString(1, keyword);
			if(menuNum == 3)  pstmt.setString(2, keyword); 
 			
			rs = pstmt.executeQuery(); // SELECT문 수행 결과 ResultSet 반환
			
			while(rs.next()) {
			
			int boardNo = rs.getInt("BOARD_NO");
			String boardTitle = rs.getString("BOARD_TITLE");
			Date createDate = rs.getDate("CREATE_DATE");
			int readCount = rs.getInt("READ_COUNT");
			String memberName = rs.getString("MEMBER_NM");
			int replyCount = rs.getInt("REPLY_COUNT");
			
			Board board = new Board(boardNo, boardTitle, createDate, readCount, memberName, replyCount);
			
			boardList.add(board);
				
			}
			
		} finally {
			
			close(rs);
			close(pstmt);
			
		}
		
		return boardList;
	}

}

Board

package edu.kh.jdbc.board.model.vo;

// VO(Value Object): 값 저장용 객체
// * 꼭 테이블과 같은 모양일 필요가 없다.
// -> 어떤 데이터를 저장하여 옮기고 싶은지에 따라 필드 구성이 달라짐


import java.sql.Date;
import java.util.List;

public class Board {
	
	// 게시글 목록 조회
	private int boardNo;
	private String boardTitle;
	private Date createDate;
	private int readCount;
	private String memberName;
	private int replyCount;
	
	
	// 게시글 상세 조회
	private String boardContent;
	private List<Reply> replyList; // 댓글 목록
	
	// 게시글 수정, 삭제
	private int memberNo;
	
	// 기본 생성자
	public Board() {}

	// 목록 / 검색용 생성자
	public Board(int boardNo, String boardTitle, Date createDate, int readCount, String memberName, int replyCount) {
		super();
		this.boardNo = boardNo;
		this.boardTitle = boardTitle;
		this.createDate = createDate;
		this.readCount = readCount;
		this.memberName = memberName;
		this.replyCount = replyCount;
	}

	public int getBoardNo() {
		return boardNo;
	}

	public void setBoardNo(int boardNo) {
		this.boardNo = boardNo;
	}

	public String getBoardTitle() {
		return boardTitle;
	}

	public void setBoardTitle(String boardTitle) {
		this.boardTitle = boardTitle;
	}

	public Date getCreateDate() {
		return createDate;
	}

	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	public int getReadCount() {
		return readCount;
	}

	public void setReadCount(int readCount) {
		this.readCount = readCount;
	}

	public String getMemberName() {
		return memberName;
	}

	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}

	public int getReplyCount() {
		return replyCount;
	}

	public void setReplyCount(int replyCount) {
		this.replyCount = replyCount;
	}

	public String getBoardContent() {
		return boardContent;
	}

	public void setBoardContent(String boardContent) {
		this.boardContent = boardContent;
	}

	public List<Reply> getReplyList() {
		return replyList;
	}

	public void setReplyList(List<Reply> replyList) {
		this.replyList = replyList;
	}

	public int getMemberNo() {
		return memberNo;
	}

	public void setMemberNo(int memberNo) {
		this.memberNo = memberNo;
	}

	@Override
	public String toString() {
		return "Board [boardNo=" + boardNo + ", boardTitle=" + boardTitle + ", createDate=" + createDate
				+ ", readCount=" + readCount + ", memberName=" + memberName + ", replyCount=" + replyCount
				+ ", boardContent=" + boardContent + ", replyList=" + replyList + ", memberNo=" + memberNo + "]";
	}


}

Reply

package edu.kh.jdbc.board.model.vo;

import java.sql.Date;

public class Reply {
	
	
	private int replyNo;
	private String replyContent;
	private Date createDate;
	private int memberNo;
	private String memberName;
	private int boardNo;
	
	public Reply() {}

	public Reply(int replyNo, String replyContent, Date createDate, int memberNo, String memberName, int boardNo) {
		super();
		this.replyNo = replyNo;
		this.replyContent = replyContent;
		this.createDate = createDate;
		this.memberNo = memberNo;
		this.memberName = memberName;
		this.boardNo = boardNo;
	}

	public int getReplyNo() {
		return replyNo;
	}

	public void setReplyNo(int replyNo) {
		this.replyNo = replyNo;
	}

	public String getReplyContent() {
		return replyContent;
	}

	public void setReplyContent(String replyContent) {
		this.replyContent = replyContent;
	}

	public Date getCreateDate() {
		return createDate;
	}

	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	public int getMemberNo() {
		return memberNo;
	}

	public void setMemberNo(int memberNo) {
		this.memberNo = memberNo;
	}

	public String getMemberName() {
		return memberName;
	}

	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}

	public int getBoardNo() {
		return boardNo;
	}

	public void setBoardNo(int boardNo) {
		this.boardNo = boardNo;
	}

	@Override
	public String toString() {
		return "Reply [replyNo=" + replyNo + ", replyContent=" + replyContent + ", createDate=" + createDate
				+ ", memberNo=" + memberNo + ", memberName=" + memberName + ", boardNo=" + boardNo + "]";
	}
	

}
profile
개발 일지

0개의 댓글