회원제 게시판 프로그램 만들기 (2) - Board (23.05.31~06.01)

·2023년 6월 1일
0

Java

목록 보기
35/35
post-thumbnail

회원제 게시판 프로그램 중 게시판(Board) 만들기!


💡 Oracle SQL

✏️ JDBC_게시판프로그램.sql

-- 게시글 목록 조회 + 댓글 개수(상관 서브쿼리 + 스칼라 서브쿼리)
-- BOARD 테이블 : BOARD_NO, 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)
ORDER BY BOARD_NO DESC;
-- 게시글 번호가 크다 == 최신 글이다

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

-- 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 BOARD WHERE BOARD_NO = 1;
-- ORA-02292: 무결성 제약조건(ASH_MEMBER.SYS_C007898)이 위배되었습니다- 자식 레코드가 발견되었습니다

SELECT * FROM BOARD WHERE BOARD_NO = 1; -- BOARD 테이블 1번 게시글
SELECT * FROM REPLY WHERE BOARD_NO = 1; -- REPLY 테이블에서 BOARD 테이블 1번 게시글을 참조하는 댓글
--> 기본적으로 삭제 불가
--> 삭제 옵션을 추가하면 가능
--> ON DELETE SET NULL(자식 컬럼 NULL) / ON DELETE CASCADE(참조하던 자식 행도 삭제)

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

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

-- 삭제 옵션이 추가된 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 = ?;

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

-- 댓글 삭제
DELETE FROM REPLY
WHERE REPLY_NO = ?;

-- 댓글 수정
UPDATE REPLY SET
REPLY_CONTENT = ?
WHERE REPLY_NO = ?;

-- 게시글 작성
INSERT INTO BOARD
VALUES(SEQ_BOARD_NO.NEXTVAL, ?, ?, DEFAULT, DEFAULT, ?);

-- 게시글 검색
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 BOARD_TITLE LIKE '%' || ? || '%'; -- 제목 검색

-- WHERE BOARD_CONTENT LIKE '%' || ? || '%'; -- 내용 검색
-- WHERE BOARD_TITLE LIKE '%' || ? || '%'

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

WHERE MEMBER_NM LIKE '%' || ? || '%' -- 작성자 검색

ORDER BY BOARD_NO DESC; 


💡 Java

✏️ ProjectRun 클래스

package edu.kh.jdbc.run;

import edu.kh.jdbc.main.view.MainView;

public class ProjectRun {

	public static void main(String[] args) {
		
		MainView view = new MainView();
		view.displayMenu();
		
	}

}

✏️ MainView 클래스

package edu.kh.jdbc.main.view;

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

import edu.kh.jdbc.board.view.BoardView;
import edu.kh.jdbc.member.model.vo.Member;
import edu.kh.jdbc.member.view.MemberView;

public class MainView { // 메인 메뉴(메뉴 선택용/입력 화면)

	private Scanner sc = new Scanner(System.in);
	
	// 로그인한 회원의 정보가 저장된 객체를 참조할 변수
	private Member loginMember = null;
	
	// 회원 관련 기능 화면을 모아둔 객체를 참조할 변수
	private MemberView memberView = new MemberView();
	
	// 게시판 관련 기능 화면을 모아둔 객체를 참조할 변수
	private BoardView boardView = new BoardView();
	
	// alt + shift + j : 메소드/클래스 설명용 주석 생성
	/**
	 * 메인 메뉴 출력 메소드
	 */
	public void displayMenu() {
		
		int menuNum = -1; // 메뉴 선택 값 저장 변수
		// 초기 값을 -1로 지정하여, 반복문 첫 바퀴에서 오류 발생 시 종료되는 것을 방지
		
		do {
			
			try {

				if(loginMember == null) { // 로그인이 되어 있지 않은 경우
					
					System.out.println("\n************* 회원제 게시판 프로그램 *************\n");
					
					System.out.println("1. 로그인");
					System.out.println("2. 회원 가입");
					System.out.println("0. 프로그램 종료");
					
					System.out.print("메뉴를 선택해 주세요 >> ");
					
					menuNum = sc.nextInt(); // InputMismatchException
					sc.nextLine(); // 입력 버퍼 개행문자 제거
					System.out.println(); // 개행
					
					switch(menuNum) {
					case 1 : loginMember = memberView.login(); break;
					case 2 : memberView.signUp(); break;
					case 0 : System.out.println("----- 프로그램 종료 -----"); break;
					default : System.out.println("메뉴에 작성된 번호를 입력해 주세요.");
					}
					
				} else { // 로그인이 되어 있는 경우
					
					
					System.out.println("\n************** 회원 메뉴 **************\n");
					// - 로그인한 회원 정보 조회
					// - 회원 목록 조회
					// - 회원 정보 수정(이름, 성별)
					// - 비밀번호 변경
					// - 회원 탈퇴
					System.out.println("1. 내 정보 조회");
					System.out.println("2. 가입된 회원 목록 조회");
					System.out.println("3. 내 정보 수정(이름, 성별)");
					System.out.println("4. 비밀번호 변경");
					System.out.println("5. 회원 탈퇴");
					
					System.out.println("6. * 게시판 메뉴 화면 *");
					
					System.out.println("9. 로그아웃");
					
					System.out.print("메뉴를 선택해 주세요 >> ");
					menuNum =sc.nextInt();
					sc.nextLine();
					
					switch(menuNum) {
					case 1 : memberView.myInfo(loginMember); break;
					case 2 : memberView.selectAll(); break;
					case 3 : memberView.updateMyInfo(loginMember); break;
					case 4 : memberView.updatePw(loginMember); break;
					case 5 : 
						int result = memberView.secession(loginMember);
						if(result > 0) loginMember = null; // 로그아웃
						break;
						
					case 6 : boardView.boardMenu(loginMember); break;
					
					case 9 : loginMember = null; break;
					default : System.out.println("메뉴에 작성된 번호를 입력해 주세요.");
					}
					
				}
				
			} catch(InputMismatchException e) {
				System.out.println("\n입력 형식이 올바르지 않습니다. 다시 시도해 주세요.");
				sc.nextLine(); // 입력 버퍼에 남은 잘못된 문자열 제거
			}
			
		} while(menuNum != 0);
		
	}
	
}

✏️ BoardView 클래스

package edu.kh.jdbc.board.view;

import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

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.println("선택 >> ");
				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);
					
					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입력 형식이 올바르지 않습니다. 다시 시도해 주세요.");
				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("\n게시글이 등록되었습니다.\n");
			} else {
				System.out.println("\n게시글 작성 실패\n");
			}
			
		} catch (Exception e) {
			System.out.println("\n<게시글 작성 중 예외 발생>\n");
			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();
		} finally {
			
		}
		
	}
	
	/** 게시글 상세 조회
	 * @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();
	            
	            switch(menuNum) {
	            case 0 : System.out.println("\n게시판 메뉴로 돌아갑니다...\n"); break;
	            case 1 : insertReply(loginMember, boardNo); break;
	            case 2 : case 3 : 
	            	
	            	String tmp = menuNum == 2 ? "\n[댓글 수정]\n" : "\n[댓글 삭제]\n"; // 삼항 연산자
	            	System.out.println(tmp);
	            	
	            	System.out.print("댓글 번호 입력 : ");
	            	int replyNo = sc.nextInt();
	            	sc.nextLine();
	            	
	            	// 입력 받은 댓글 번호가 댓글 목록에 있는지 확인
	            	
	            	Reply reply = null; // 확인된 댓글을 참조할 변수
	            	
	            	for(Reply r : board.getReplyList()) { // 반복 접근
	            		
	            		if(r.getReplyNo() == replyNo) { // 입력 받은 번호와 일치하는 댓글이 있다면

	            			reply = r;
	            			break;
	            		}
	            	}
	            	
	            	if(reply == null) { // 같은 댓글 번호가 목록에 없는 경우
	            		System.out.println("\n해당 댓글이 존재하지 않습니다.\n");
	            	
	            	} else { // 같은 댓글 번호가 목록에 있을 경우
	            		
	            		// 해당 댓글의 회원 번호(작성자)와
	            		// 로그인한 회원의 번호가 같은지 확인
	            		// -> 같을 경우 로그인한 사람의 댓글이다!
	            		if(reply.getMemberNo() == loginMember.getMemberNo()) {
	            			
	            			if(menuNum == 2)	updateReply(replyNo); // 댓글 수정
	            			else				deleteReply(replyNo); // 댓글 삭제
	            			
	            		} else {
	            			System.out.println("\n현재 로그인한 회원의 댓글이 아닙니다.\n");
	            		}
	            		
	            	}
	            	
	            	break;
	            
	            case 4 : case 5 :
	            	// 게시글 작성자 번호 == 로그인 회원 번호
	            	if(board.getMemberNo() == loginMember.getMemberNo()) {
	            		
	            		// 4번 게시글 수정
	            		if(menuNum == 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 replyNo
	 */
	private void updateReply(int replyNo) {
		System.out.println("수정할 내용 입력(종료 시 @exit 입력)");
		String input = inputContent();
		
		// Reply 객체 생성해서 댓글 번호, 내용 저장
		Reply reply = new Reply();
		
		reply.setReplyNo(replyNo);
		reply.setReplyContent(input);
		
		try {
			int result = service.updateReply(reply);
			
			if(result > 0) {
				System.out.println(replyNo + "번 댓글이 수정되었습니다.\n");
			} else {
				System.out.println("\n댓글 수정 실패\n");
			}
			
		} catch (Exception e) {
			System.out.println("\n<댓글 수정 중 예외 발생>\n");
			e.printStackTrace();
		}
		
	}
	
	/** 댓글 삭제
	 * @param replyNo
	 */
	private void deleteReply(int replyNo) {
	
		char ch = ' ';

		while(true) {
			System.out.print("정말로 삭제하시겠습니까?(Y/N) : ");
			ch = sc.next().toUpperCase().charAt(0);
			
			if(ch == 'Y' || ch == 'N') {
				break;
			} else {
				System.out.println("Y 또는 N을 입력해 주세요.\n");
			}
		}
			
		if(ch =='Y') {
			
			// 보안 문자 생성
			String cap = capcha();
			System.out.print("다음 보안 문자를 입력해 주세요 >> " + cap);
			
			System.out.print("\n보안 문자 입력 : ");
			String input = sc.next();
			
			if(input.equals(cap)) {
				
				try {
					int result = service.deleteReply(replyNo);
					
					if(result > 0) {
						System.out.println(replyNo + "번 댓글이 삭제되었습니다.");
					} 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");
		}
	
	}

	/** 게시글 수정
	 * @param boardNo
	 */
	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는 사용 방법은 똑같음)
	 *    -> 차이점은 동기/비동기 차이밖에 없음
	 * 
	 * 
	 * */
	
	/** 댓글 작성
	 * @param loginMember
	 * @param boardNo
	 */
	private void insertReply(Member loginMember, int boardNo) {
		
		System.out.println("[댓글 작성]");
		
		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();
		}
	}
	
	/** 게시글/댓글 내용 입력 메소드
	 * @return content
	 */
	private String inputContent() {
		
		// String content = "";
		StringBuffer content = new StringBuffer();
		
		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 객체 생성이 없다)
			}
			
		}	
		
		return content.toString(); // StringBuffer에 오버라이딩된 toString()
								   // -> 저장된 문자열을 String 형태로 반환
	}

	/** 게시글 삭제
	 * @param boardNo
	 */
	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.print("다음 보안 문자를 입력해 주세요 >> " + cap);
			
			System.out.print("\n보안 문자 입력 : ");
			String input = sc.next();

			if(input.equals(cap)) { // 입력받은 문자열과 보안 문자가 같을 때
				// 삭제 Service 호출
				try {

					int result = service.deleteBoard(boardNo);
					
					if(result > 0) {
						System.out.println(boardNo + "번 게시글이 삭제되었습니다.\n");
						
					} 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 필드/메소드 호출 시 클래스명 생략
import static edu.kh.jdbc.common.JDBCTemplate.*;
// * 기호 : 모두, 전부(ALL)

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;
	}

	/** 게시글 상세 조회
	 * @param boardNo
	 * @return
	 * @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 필드에 조회한 댓글 목록을 대입(세팅)
			board.setReplyList(replyList);
			
			// 3-2) 게시글 조회수 증가 DAO 메소드(UPDATE) 호출 후 결과(성공한 행의 개수) 반환 받기
			int result = dao.increaseReadCount(conn, boardNo);
			// increase : 증가하다
			
			// 트랜잭션 처리 + 조회수 동기화
			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; // 게시글 상세 조회 + 댓글 목록
	}

	/** 게시글 삭제 Service
	 * @param boardNo
	 * @return result
	 * @throws Exception
	 */
	public int deleteBoard(int boardNo) throws Exception {

		// 1) Connection 생성
		Connection conn = getConnection();
		
		// 2) DAO 수행 후 결과 반환 받기
		int result = dao.deleteBoard(conn, boardNo);
		
		// 3) DAO 수행 결과에 따라 트랜잭션 처리
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		// 4) 사용한 Connection 객체 반환
		close(conn);
			
		// 5) DAO 수행 결과 View로 반환
		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();
		
		// DAO 수행
		int result = dao.insertReply(conn, reply);
		
		// 트랜잭션 처리
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		// 커넥션 반환
		close(conn);
		
		return result;
	}

	/** 댓글 수정 Service
	 * @param reply
	 * @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 reply
	 * @return result
	 * @throws Exception
	 */
	public int deleteReply(int replyNo) throws Exception {
		
		Connection conn = getConnection();
		
		int result = dao.deleteReply(conn, replyNo);
		
		if(result > 0) commit(conn);
		else		   rollback(conn);
		
		close(conn);
		
		return result;
	}

	/** 게시글 작성 Service
	 * @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;
	}

	/** 게시글 검색 Service
	 * @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.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;

/**
 * @author user1
 *
 */
public class BoardDAO {

	// JDBC 객체 참조용 변수 선언(java.sql)
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;
	
	// 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) 후 결과 반환 받기 (ResultSet)
			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 {
			// 8) 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;
	}

	/** 게시글 삭제 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 conn
	 * @param reply
	 * @return result
	 * @throws Exception
	 */
	public int insertReply(Connection conn, Reply reply) throws Exception {
		
		// 결과 저장용 변수 선언
		int result = 0;
		
		try {
			// SQL 작성
			String sql = prop.getProperty("insertReply");

			// PreparedStatement 생성
			pstmt = conn.prepareStatement(sql);
			
			// 위치 홀더에 값 세팅
			pstmt.setString(1, reply.getReplyContent());
			pstmt.setInt(2, reply.getMemberNo());
			pstmt.setInt(3, reply.getBoardNo());
			
			// 실행
			result = pstmt.executeUpdate();
			
		} finally {
			// JDBC 객체 자원 반환
			close(pstmt);
		}
		
		// 결과 반환
		return result;
	}

	/** 댓글 수정 DAO
	 * @param conn
	 * @param reply
	 * @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 conn
	 * @param replyNo
	 * @return result
	 * @throws Exception
	 */
	public int deleteReply(Connection conn, int replyNo) throws Exception {
		
		int result = 0;
		
		try {
			String sql = prop.getProperty("deleteReply");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, replyNo);
			
			result = pstmt.executeUpdate();
			
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/** 게시글 작성 DAO
	 * @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<Board>();
		
		try {
			// SQL 작성(menuNum에 따라서 SQL 조합)
			String sqp = prop.getProperty("searchBoard1")
						+ prop.getProperty("condition" + menuNum)
						+ prop.getProperty("searchBoard2");
			
			pstmt = conn.prepareStatement(sqp);
			
			// 위치 홀더에 알맞은 값 세팅
			// * 주의 *
			// -> 제목 + 내용을 검색하는 조건(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;

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

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

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 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 + "]";
	}
	
}

✏️ 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 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="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_TITLE LIKE '%' || ? || '%'
		OR BOARD_CONTENT LIKE '%' || ? || '%'
	</entry>
	
	<!-- 검색 조건 4(작성자) -->
	<entry key="condition4">
		WHERE MEMBER_NM LIKE '%' || ? || '%'
	</entry>
	
</properties>
profile
풀스택 개발자 기록집 📁

0개의 댓글