커뮤니티 - 게시글 목록 조회 기능 구현 (23.07.17~18)

·2023년 7월 17일
0

Server

목록 보기
22/35
post-thumbnail

📝 ERD

(Entity Relationship Diagram)
개체-관계 다이어그램
https://www.erdcloud.com/ (ERD 클라우드)

💡 Entity

데이터베이스에 있는 테이블 (개체)


이제부터 커뮤니티 사이트 구현 예시를 통해 관련 개념을 살펴보도록 하자.

💡 개념적 / 논리적 설계

💡 물리적 설계


📌 ORACLE

CREATE TABLE "MEMBER" (
   "MEMBER_NO"   NUMBER   PRIMARY KEY,
   "MEMBER_EMAIL"   VARCHAR2(50) NOT NULL,
   "MEMBER_PW"   VARCHAR2(30) NOT NULL,
   "MEMBER_NICK"   VARCHAR2(30) NOT NULL,
   "MEMBER_TEL"   CHAR(11) NOT NULL,
   "MEMBER_ADDR"   VARCHAR2(500),
   "PROFILE_IMG"   VARCHAR2(200),
   "ENROLL_DT"   DATE DEFAULT SYSDATE ,
   "SECESSION_FL"   CHAR(1)   DEFAULT 'N'
);


COMMENT ON COLUMN "MEMBER"."MEMBER_NO" IS '회원 번호';
COMMENT ON COLUMN "MEMBER"."MEMBER_EMAIL" IS '회원 이메일(아이디)';
COMMENT ON COLUMN "MEMBER"."MEMBER_PW" IS '회원 비밀번호';
COMMENT ON COLUMN "MEMBER"."MEMBER_NICK" IS '회원 닉네임(중복x)';
COMMENT ON COLUMN "MEMBER"."MEMBER_TEL" IS '전화번호(- 미포함)';
COMMENT ON COLUMN "MEMBER"."MEMBER_ADDR" IS '회원 주소';
COMMENT ON COLUMN "MEMBER"."PROFILE_IMG" IS '회원 프로필 이미지';
COMMENT ON COLUMN "MEMBER"."ENROLL_DT" IS '회원 가입일';
COMMENT ON COLUMN "MEMBER"."SECESSION_FL" IS '탈퇴여부(Y:탈퇴, N:미탈퇴)';


-- 회원 번호 시퀀스
CREATE SEQUENCE SEQ_MEMBER_NO;

INSERT INTO MEMBER
VALUES(SEQ_MEMBER_NO.NEXTVAL, 'user01@kh.or.kr', 'pass01!', 
    '유저일', '01012341234', '04540,,서울특별시 강남구 테헤란로 14길 6 5층',
     NULL, DEFAULT, DEFAULT);
     
COMMIT;

-- 로그인 회원
SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICK, MEMBER_TEL,
    MEMBER_ADDR, PROFILE_IMG,
    TO_CHAR(ENROLL_DT, 'YYYY-MM-DD HH24:MI:SS') AS ENROLL_DT FROM MEMBER
WHERE MEMBER_EMAIL = 'user01@kh.or.kr'
AND MEMBER_PW = 'pass01!'
AND SECESSION_FL = 'N';

-- 회원가입
INSERT INTO MEMBER
VALUES(SEQ_MEMBER_NO.NEXTVAL, MEMBER_EMAIL, MEMBER_PW, MEMBER_NICK, MEMBER_TEL, 
        MEMBER_ADDR, DEFAULT, DEFAULT, DEFAULT);
        
-- 암호화된 비밀번호로 업데이트
UPDATE MEMBER SET
MEMBER_PW = 'aBN5hiegXlvAovJLipPoPd5LB+xjPrAeu1tcAVg0p5MKGocvo6l825SD+ZMCOcHBFeGB7MnzH31SAnDzYYsSdg==';

-- MEMBER TABLE 비밀번호 컬럼 크기 변경
ALTER TABLE MEMBER
MODIFY MEMBER_PW VARCHAR2(100);

-- 암호화된 비밀번호로 업데이트
UPDATE MEMBER SET
MEMBER_PW = 'aBN5hiegXlvAovJLipPoPd5LB+xjPrAeu1tcAVg0p5MKGocvo6l825SD+ZMCOcHBFeGB7MnzH31SAnDzYYsSdg=='
WHERE MEMBER_NO = 1;

-- 회원 정보 수정
UPDATE MEMBER SET
MEMBER_NICK = ?,
MEMBER_TEL = ?,
MEMBER_ADDR = ?

WHERE MEMBER_NO = 1;

-- 비밀번호 변경
UPDATE MEMBER SET
MEMBER_PW = ?
WHERE MEMBER_NO = ?
AND MEMBER_PW = ?;

-- 회원 탈퇴
UPDATE MEMBER SET
SECESSION_FL = 'Y'
WHERE MEMBER_NO = ?
AND MEMBER_PW = ?;

-- 이메일 중복 검사
-- 중복되면 1, 아니면 0
SELECT COUNT(*) FROM MEMBER
WHERE MEMBER_EMAIL = 'user01@kh.or.kr'
AND SECESSION_FL = 'N';

-- 닉네임 중복 검사
-- 중복되면 1, 아니면 0
SELECT COUNT(*) FROM MEMBER
WHERE MEMBER_NICK = ?
AND SECESSION_FL = 'N';

-- 이메일이 일치하는 회원 정보 조회
SELECT MEMBER_EMAIL, MEMBER_NICK, MEMBER_TEL, MEMBER_ADDR, 
    TO_CHAR(ENROLL_DT, 'YYYY"년" MM"월" DD"일"') AS ENROLL_DATE
FROM MEMBER
WHERE MEMBER_EMAIL = 'user01@kh.or.kr'
AND SECESSION_FL = 'N';

-- 회원 목록 조회(회원 번호, 이메일, 닉네임)
SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICK
FROM MEMBER
WHERE SECESSION_FL = 'N'
ORDER BY MEMBER_NO;

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

-- ""가 없으면 테이블명, 컬럼명, 시퀀스명 등을 무조건 대문자로 저장
-- ""가 있으면 대소문자 구분

-- 게시판 종류
DROP TABLE "BOARD_TYPE";

CREATE TABLE "BOARD_TYPE" (
	"BOARD_CD"	NUMBER		PRIMARY KEY,
	"BOARD_NM"	VARCHAR2(50)		NOT NULL
);

COMMENT ON COLUMN "BOARD_TYPE"."BOARD_CD" IS '게시판 코드';
COMMENT ON COLUMN "BOARD_TYPE"."BOARD_NM" IS '게시판 이름';


-- 게시판(게시글 저장 테이블)
DROP TABLE "BOARD";

CREATE TABLE "BOARD" (
	"BOARD_NO"	NUMBER		NOT NULL,
	"BOARD_TITLE"	VARCHAR2(150)		NOT NULL,
	"BOARD_CONTENT"	VARCHAR2(4000)		NOT NULL,
	"CREATE_DT"	DATE	DEFAULT SYSDATE	NOT NULL,
	"UPDATE_DT"	DATE		NULL,
	"READ_COUNT"	NUMBER	DEFAULT 0	NOT NULL,
	"BOARD_ST"	CHAR(1)	DEFAULT 'N'	NOT NULL,
	"MEMBER_NO"	NUMBER		NOT NULL,
	"BOARD_CD"	NUMBER		NOT NULL
);

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_DT" IS '작성일';
COMMENT ON COLUMN "BOARD"."UPDATE_DT" IS '마지막수정일';
COMMENT ON COLUMN "BOARD"."READ_COUNT" IS '조회수';
COMMENT ON COLUMN "BOARD"."BOARD_ST" IS '게시글상태(정상N,삭제Y)';
COMMENT ON COLUMN "BOARD"."MEMBER_NO" IS '작성자 회원번호';
COMMENT ON COLUMN "BOARD"."BOARD_CD" IS '게시판 코드';

-- BOARD 테이블 제약조건 추가
ALTER TABLE BOARD
ADD PRIMARY KEY(BOARD_NO); -- 제약 조건명 생략(SYS_XXXXX)

ALTER TABLE BOARD
ADD CONSTRAINT "FK_BOARD_CD" -- 제약 조건명 지정
FOREIGN KEY("BOARD_CD") -- BOARD의 BOARD_CODE 컬럼에 FK 지정
REFERENCES "BOARD_TYPE"; -- 참조할 테이블

ALTER TABLE BOARD
ADD CONSTRAINT "CHK_BOARD_ST"
CHECK("BOARD_ST" IN('N', 'Y'));

ALTER TABLE BOARD
ADD CONSTRAINT "FK_BOARD_MEMBER"
FOREIGN KEY("MEMBER_NO")
REFERENCES MEMBER;

-- BOARD NO용 시퀀스
CREATE SEQUENCE SEQ_BOARD_NO;

-- BOARD_TYPE 데이터 삽입
INSERT INTO BOARD_TYPE VALUES(1, '공지사항');
INSERT INTO BOARD_TYPE VALUES(2, '자유 게시판');
INSERT INTO BOARD_TYPE VALUES(3, '질문 게시판');

SELECT * FROM BOARD_TYPE;

-- BOARD 테이블 샘플 데이터 삽입(PL/SQL)

BEGIN
    FOR I IN 1..500 LOOP
    
        INSERT INTO BOARD
        VALUES(SEQ_BOARD_NO.NEXTVAL,
                SEQ_BOARD_NO.CURRVAL || '번째 게시글',
                SEQ_BOARD_NO.CURRVAL || '번째 게시글 내용입니다.',
                DEFAULT, DEFAULT, DEFAULT, DEFAULT, 1, 3
        );
    END LOOP;
END;
/

SELECT * FROM MEMBER;

-- 공지사항 게시판 조회
SELECT COUNT(*) FROM BOARD WHERE BOARD_CD = 1;

-- 자유 게시판 조회
SELECT COUNT(*) FROM BOARD WHERE BOARD_CD = 2;

-- 질문 게시판 조회
SELECT COUNT(*) FROM BOARD WHERE BOARD_CD = 3;

-- 게시판 이름 조회
SELECT BOARD_NM FROM BOARD_TYPE
WHERE BOARD_CD = 2;

commit;

-- 특정 게시판의 전체 게시글 수 조회
-- (단, 삭제글은 제외)
SELECT COUNT(*) FROM BOARD
WHERE BOARD_CD = 1
AND BOARD_ST = 'N';

-- 특정 게시판에서 일정한 범위의 목록 조회
SELECT * FROM(
    SELECT ROWNUM RNUM, A.* FROM(
        SELECT BOARD_NO, BOARD_TITLE, MEMBER_NICK,
                TO_CHAR(CREATE_DT, 'YYYY-MM-DD') AS CREATE_DT,
                READ_COUNT
        FROM BOARD
        JOIN MEMBER USING(MEMBER_NO)
        WHERE BOARD_CD = 2
        AND BOARD_ST = 'N'
        ORDER BY BOARD_NO DESC
    ) A
)
WHERE RNUM BETWEEN 1 AND 10;

📌 VS Code

🔎 boardList-style.css

/* section, div, form{
    border: 1px solid black;
} */

/* a태그 공통 설정 */
.board-list a{
    text-decoration: none;
    color: black;
}

.board-list a:hover{
    text-decoration: underline;
}

/* 게시판 목록 화면 전체 영역 설정 */
.board-list{
    width: 1000px;
    margin: 50px auto;
}

/* 게시판 이름 */
.board-name{
    font-size: 2.5em;
    margin-left: 30px;
}

/* 게시글 목록 영역 */
.list-wrapper{
    width: 100%;
    min-height: 670px;
}

/* 목록 출력 테이블 */
.list-table{
    width: 100%;
    padding: 20px;

    /* 테두리 상쇄 여부 지정 
        -> 고유 테두리를 지닌 요소와 겹치는 경우
           테두리를 분리할지 상쇄(덮어쓰기) 할지 지정 
    */
    border-collapse: collapse;
}

.list-table > thead{
    background-color: #455ba8;
    color: white;
}

.list-table tr{
    height: 60px;
}

.list-table > tbody > tr{
    border-bottom: 1px solid gray;
}

.list-table tr > *{
    text-align: center;
}

.list-table tr > *:nth-of-type(1){width: 10%;}
.list-table tr > td:nth-of-type(2){
    width: 50%;
    text-align: left;
    padding-left: 50px;
}
.list-table tr > *:nth-of-type(3){width: 15%;}
.list-table tr > *:nth-of-type(4){width: 15%;}
.list-table tr > *:nth-of-type(5){width: 10%;}

/* 버튼 영역 */
.btn-area{
    height: 50px;
    display: flex;
    justify-content: flex-end;
    align-items: center;
}

/* 글쓰기 버튼 */
#insertBtn{
    margin-right: 50px;

    width: 80px;
    height: 40px;

    background-color: white;
    border: 2px solid #455ba8;
    color: #455ba8;

    border-radius: 5px;

    font-weight: bold;
    cursor: pointer;
}

/* 페이지네이션(Pagination) */
.pagination{
    padding: 0;
    list-style: none;

    display: flex;
    justify-content: center;
}

.pagination > li{
    width: 20px;
    margin: 0 5px;
    text-align: center;
}

.pagination a{
    width: 100%;
    height: 100%;
    display: block;
    /* letter-spacing: -2px; */
}

/* 현재 페이지 번호 */
.pagination .current{
    background-color: #455ba8;
    color: white;
    font-weight: bold;
    border-radius: 50%;
}

/* 검색 영역 */
#boardSearch{
    width: 500px;
    height: 30px;

    display: flex;
    justify-content: space-between;
    margin: 30px auto;
}

/* 검색 영역 공통 스타일 */
#boardSearch *{
    box-sizing: border-box;
    padding: 0;
    margin: 0;
}

#boardSearch select{
    width: 100px;
}

#boardSearch input{
    margin: 0 10px;
    flex-grow: 1;
    padding-left: 10px;
}

#boardSearch button{
    width: 100px;

    background-color: #455ba8;
    color: white;
    font-weight: bold;
    cursor: pointer;
    border: 0;
}

📌 Eclipse

🔎 boardList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!-- map에 저장된 값을 각각 변수에 저장 -->
<c:set var="boardName" value="${map.boardName}" />
<c:set var="pagination" value="${map.pagination}" />
<c:set var="boardList" value="${map.boardList}" />

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${boardName}</title>

    <link rel="stylesheet" href="${contextPath}/resources/css/boardList-style.css">

    <link rel="stylesheet" href="${contextPath}/resources/css/main-style.css">

    <script src="https://kit.fontawesome.com/4dca1921b4.js" crossorigin="anonymous"></script>
</head>
<body>
    <main>
		<jsp:include page="/WEB-INF/views/common/header.jsp"/>		

        <section class="board-list">

            <h1 class="board-name">${boardName}</h1>

            <div class="list-wrapper">
                <table class="list-table">
                    <thead>
                        <tr>
                            <th>글번호</th>
                            <th>제목</th>
                            <th>작성자</th>
                            <th>작성일</th>
                            <th>조회수</th>
                        </tr>
                    </thead>

                    <tbody>

                        <c:choose>
                            <c:when test="${empty boardList}">
                                <!-- 게시글 목록 조회 결과가 비어 있다면 -->
                                <tr>
                                    <th colspan="5">게시글이 존재하지 않습니다.</th>
                                </tr>
                            </c:when>

                            <c:otherwise>
                                <!-- 게시글 목록 조회 결과가 비어 있지 않다면 -->

                                <!-- 향상된 for문처럼 사용 -->
                                <c:forEach var="board" items="${boardList}">
                                    <tr>
                                        <td>${board.boardNo}</td>
                                        <td>
                                            <a href="#">${board.boardTitle}</a>
                                        </td>
                                        <td>${board.memberNickname}</td>
                                        <td>${board.createDate}</td>
                                        <td>${board.readCount}</td>
                                    </tr>
                                </c:forEach>

                            </c:otherwise>

                        </c:choose>

                    </tbody>

                </table>
            </div>

            <div class="btn-area">
                <button id="insertBtn">글쓰기</button>
            </div>
            
            <div class="pagination-area">

                <!-- 페이지네이션 a태그에 사용될 공통 주소를 저장한 변수 선언 -->
                <c:set var="url" value="list?type=${param.type}&cp="/>

                <ul class="pagination">
                    <!-- 첫 페이지로 이동 -->
                    <li><a href="${url}1">&lt;&lt;</a></li>

                    <!-- 이전 목록 마지막 번호로 이동 -->
                    <li><a href="${url}${pagination.prevPage}">&lt;</a></li>

                    <!-- 범위가 정해진 일반 for문을 사용 -->
                    <c:forEach var="i" begin="${pagination.startPage}" end="${pagination.endPage}" step="1">

                        <c:choose>
                            <c:when test="${i == pagination.currentPage}">
                                <li><a class="current">${i}</a></li>
                            </c:when>

                            <c:otherwise>
                                <li><a href="${url}${i}">${i}</a></li>
                            </c:otherwise>
                        </c:choose>

                    </c:forEach>

                    <!-- 다음 목록 시작 번호로 이동 -->
                    <li><a href="${url}${pagination.nextPage}">&gt;</a></li>
                    
                    <!-- 끝 페이지로 이동 -->
                    <li><a href="${url}${pagination.maxPage}">&gt;&gt;</a></li>
                </ul>
            </div>

            <form action="#" method="get" id="boardSearch">

                <select name="key">
                    <option value="t">제목</option>
                    <option value="c">내용</option>
                    <option value="tc">제목+내용</option>
                    <option value="w">작성자</option>
                </select>

                <input type="text" name="query" placeholder="검색어를 입력해 주세요.">

                <button>검색</button>
            </form>

        </section>
    </main>

    <jsp:include page="/WEB-INF/views/common/footer.jsp"/>
</body>
</html>

🔎 Board.java

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

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@NoArgsConstructor
public class Board {
	
	private int boardNo;
	private String boardTitle;
	private String memberNickname;
	private String createDate;
	private int readCount;
	private String thumbnail;

}

🔎 Pagination.java

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

public class Pagination {
	// 페이지네이션(페이징 처리)에 필요한 모든 값들을 저장하고 있는 객체
	
	private int currentPage;	// 현재 페이지 번호
	private int listCount;		// 전체 게시글 수
	
	private int limit = 10;		// 한 페이지에 보여질 게시글의 수
	private int pageSize = 10;	// 목록 하단 페이지 번호의 노출 개수
	
	private int maxPage;		// 제일 큰 페이지 번호 == 마지막 페이지 번호
	private int startPage;		// 목록 하단에 노출된 페이지의 시작 번호
	private int endPage;		// 목록 하단에 노출된 페이지의 끝 번호
	
	private int prevPage;		// 목록 하단에 노출된 번호의 이전 목록 끝 번호
	private int nextPage;		// 목록 하단에 노출된 번호의 다음 목록 시작 번호
	
	
	// 생성자
	public Pagination(int currentPage, int listCount) {
		this.currentPage = currentPage;
		this.listCount = listCount;
		
		calculatePagination(); // 계산 메소드 호출
	}


	public int getCurrentPage() {
		return currentPage;
	}


	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
		
		calculatePagination();
	}


	public int getListCount() {
		return listCount;
	}


	public void setListCount(int listCount) {
		this.listCount = listCount;
		
		calculatePagination();
	}


	public int getLimit() {
		return limit;
	}


	public void setLimit(int limit) {
		this.limit = limit;
		
		calculatePagination();
	}


	public int getPageSize() {
		return pageSize;
	}


	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	
		calculatePagination();
	}


	public int getMaxPage() {
		return maxPage;
	}


	public void setMaxPage(int maxPage) {
		this.maxPage = maxPage;
	}


	public int getStartPage() {
		return startPage;
	}


	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}


	public int getEndPage() {
		return endPage;
	}


	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}


	public int getPrevPage() {
		return prevPage;
	}


	public void setPrevPage(int prevPage) {
		this.prevPage = prevPage;
	}


	public int getNextPage() {
		return nextPage;
	}


	public void setNextPage(int nextPage) {
		this.nextPage = nextPage;
	}


	@Override
	public String toString() {
		return "Pagination [currentPage=" + currentPage + ", listCount=" + listCount + ", limit=" + limit
				+ ", pageSize=" + pageSize + ", maxPage=" + maxPage + ", startPage=" + startPage + ", endPage="
				+ endPage + ", prevPage=" + prevPage + ", nextPage=" + nextPage + "]";
	}
	
	
	// 페이징 처리에 필요한 값을 계산하는 메소드
	private void calculatePagination() {
		
		// 전체 게시글 수 : 500 // 보여지는 게시글의 수 : 10개
		// -> 마지막 페이지 번호는? 500 / 10 = 50

		// 전체 게시글 수 : 501 // 보여지는 게시글의 수 : 10개
		// -> 마지막 페이지 번호는? 501 / 10 = 51 (50.1 올림 처리)
		
		maxPage = (int)Math.ceil( (double)listCount / limit );
		
		// * startPage : 목록 하단에 노출된 페이지의 시작 번호
		
		// 현재 페이지가 1~10인 경우 : 1
		// 현재 페이지가 11~20인 경우 : 11
		// 현재 페이지가 21~30인 경우 : 21

		startPage = (currentPage - 1)/ pageSize * pageSize + 1;
		
		// * endPage : 목록 하단에 노출된 페이지의 끝 번호
		
		// 현재 페이지가 1~10인 경우 : 10
		// 현재 페이지가 11~20인 경우 : 20
		// 현재 페이지가 21~30인 경우 : 30
		
		endPage = startPage + pageSize - 1;
		
		// 만약 endPage가 maxPage를 초과하는 경우
		if(endPage > maxPage) {
			endPage = maxPage;
		}

		// * prevPage(<) : 목록 하단에 노출된 번호의 이전 목록 끝 번호
		// * nextPage(>) : 목록 하단에 노출된 번호의 다음 목록 시작 번호
		
		// 현재 페이지가 1~10일 경우
		// < :  1 페이지
		// > : 11 페이지
		
		// 현재 페이지가 11~20일 경우
		// < : 10 페이지
		// > : 21 페이지
		
		// 현재 페이지가 41~50일 경우 (maxPage가 50)
		// < : 40
		// > : 50
		
		if(currentPage <= pageSize) {
			prevPage = 1;
		} else {
			prevPage = startPage - 1;
		}
		
		if(endPage == maxPage) {
			nextPage = maxPage;
		} else {
			nextPage = endPage + 1;
		}
	}
	
}

🔎 BoardListServlet.java

package edu.kh.community.board.controller;

import java.io.IOException;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.kh.community.board.model.service.BoardService;

@WebServlet("/board/list")
public class BoardListServlet extends HttpServlet{
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {

			// 쿼리스트링 얻어오기 == 파라미터 얻어오기
			
			int type = Integer.parseInt(req.getParameter("type"));
			
			
			// nav 메뉴(공지사항, 자유게시판, 질문게시판) 선택 시
			// 쿼리스트링에 cp가 없음 --> cp = 1 고정
			int cp = 1;
			
			// 페이지네이션 번호 선택시
			// 쿼리스트링에 cp가 있음 --> cp = 쿼리스트링의 cp값
			if(req.getParameter("cp") != null) { // 쿼리스트링에 "cp"가 존재한다면
				cp = Integer.parseInt(req.getParameter("cp"));
			}
			
			BoardService service = new BoardService();
			
			// 게시판 이름, 페이지네이션 객체, 게시글 리스트를 한번에 반환하는 Service 호출
			Map<String, Object> map = service.selectBoardList(type, cp);
			
			// request 범위로 map을 세팅
			req.setAttribute("map", map);
			
			String path = "/WEB-INF/views/board/boardList.jsp";
			
			RequestDispatcher dispatcher = req.getRequestDispatcher(path);
			dispatcher.forward(req, resp);

		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

🔎 BoardService.java

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

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

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

import edu.kh.community.board.model.dao.BoardDAO;
import edu.kh.community.board.model.vo.Board;
import edu.kh.community.board.model.vo.Pagination;

public class BoardService {

	private BoardDAO dao = new BoardDAO();

	/** 게시글 목록 조회 Service
	 * @param type
	 * @param cp
	 * @return map
	 * @throws Exception
	 */
	public Map<String, Object> selectBoardList(int type, int cp) throws Exception {
		
		Connection conn = getConnection();
		
		// 1. 게시판 이름 조회 DAO 호출
		String boardName = dao.selectBoardName(conn, type);
		
		// 2-1. 특정 게시판 전체 게시글 수 조회 DAO 호출
		int listCount = dao.getListCount(conn, type);
		
		// 2-2. 전체 게시글 수 + 현재 페이지(cp)를 이용해 페이지네이션 객체 생성
		Pagination pagination = new Pagination(cp, listCount);
		
		// 3. 게시글 목록 조회
		List<Board> boardList = dao.selectBoardList(conn, pagination, type);
		
		// 4. Map 객체를 생성하여 1, 2, 3 결과 객체를 모두 저장
		Map<String, Object> map = new HashMap<String, Object>();
		
		map.put("boardName", boardName);
		map.put("pagination", pagination);
		map.put("boardList", boardList);
		
		close(conn);

		return map; // Map 객체 반환
	}
	
}


🔎 BoardDAO.java

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

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

import java.io.FileInputStream;
import java.sql.Connection;
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.community.board.model.vo.Board;
import edu.kh.community.board.model.vo.Pagination;

public class BoardDAO {
	
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;
	
	private Properties prop;
	
	public BoardDAO() {
		
		try {
			prop = new Properties();
			
			String filePath = BoardDAO.class.getResource("/edu/kh/community/sql/board-sql.xml").getPath();
			
			prop.loadFromXML(new FileInputStream(filePath));
					
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}

	/** 게시판 이름 조회 DAO
	 * @param conn
	 * @param type
	 * @return boardName
	 * @throws Exception
	 */
	public String selectBoardName(Connection conn, int type) throws Exception{
		String boardName = null;
		
		try {
			String sql = prop.getProperty("selectBoardName");
			
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, type);
			
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				boardName = rs.getString(1);
			}
			
		} finally {
			close(rs);
			close(pstmt);
		}

		return boardName;
	}

	/** 특정 게시판 전체 게시글 수 조회 DAO
	 * @param conn
	 * @param type
	 * @return listCount
	 * @throws Exception
	 */
	public int getListCount(Connection conn, int type) throws Exception{
		
		int listCount = 0;
		
		try {
			
			String sql = prop.getProperty("getListCount");
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, type);
			
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				listCount = rs.getInt(1);
			}
			
		} finally {
			close(rs);
			close(pstmt);
		}

		return listCount;
	}

	/** 특정 게시판에서 일정한 범위의 목록 조회 DAO
	 * @param conn
	 * @param pagination
	 * @param type
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> selectBoardList(Connection conn, Pagination pagination, int type) throws Exception{
		
		List<Board> boardList = new ArrayList<Board>();
		
		try {
			
			String sql = prop.getProperty("selectBoardList");
			
			// BETWEEN 구문에 들어갈 범위 계산
			int start = ( pagination.getCurrentPage() - 1 ) * pagination.getLimit() + 1;
			int end = start + pagination.getLimit() - 1;
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, type);
			pstmt.setInt(2, start);
			pstmt.setInt(3, end);
			
			rs = pstmt.executeQuery();
			
			while(rs.next()) {
				Board board = new Board();

				board.setBoardNo( rs.getInt("BOARD_NO"));
				board.setBoardTitle( rs.getString("BOARD_TITLE"));
				board.setMemberNickname( rs.getString("MEMBER_NICK"));
				board.setCreateDate( rs.getString("CREATE_DT"));
				board.setReadCount( rs.getInt("READ_COUNT"));
				
				boardList.add(board);
			}
			
		} finally {
			close(rs);
			close(pstmt);
		}
		
		return boardList;
	}
	
}


🔎 board-sql.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>board-sql.xml</comment>

	<!-- 게시판 이름 조회 -->
	<entry key="selectBoardName">
		SELECT BOARD_NM FROM BOARD_TYPE
		WHERE BOARD_CD = ?
	</entry>
	
	<!-- 특정 게시판 전체 게시글 수 조회 -->
	<entry key="getListCount">
		SELECT COUNT(*) FROM BOARD
		WHERE BOARD_CD = ?
		AND BOARD_ST = 'N'
	</entry>
	
	<!-- 특정 게시판에서 일정한 범위의 목록 조회 -->
	<entry key="selectBoardList">
		SELECT * FROM(
		    SELECT ROWNUM RNUM, A.* FROM(
		        SELECT BOARD_NO, BOARD_TITLE, MEMBER_NICK,
		                TO_CHAR(CREATE_DT, 'YYYY-MM-DD') AS CREATE_DT,
		                READ_COUNT
		        FROM BOARD
		        JOIN MEMBER USING(MEMBER_NO)
		        WHERE BOARD_CD = ?
		        AND BOARD_ST = 'N'
		        ORDER BY BOARD_NO DESC
		    ) A
		)
		WHERE RNUM BETWEEN ? AND ?
	</entry>
</properties>

profile
풀스택 개발자 기록집 📁

0개의 댓글