로그인 페이지 만들기에 이어 이번에는 MVC 패턴을 적용한 웹 어플리케이션에서 게시판을 만들어봅니다. Spring 없이 순수 JSP와 서블릿으로 구성되며, 기존 로그인과 회원가입, 정보조회를 만들었던 웹페이지에 기능을 붙여서 구현합니다.
[· JSP & Servlet/- 부트스트랩] - 부트스트랩으로 게시판 만들기_리스트 화면 [1/3]
[· JSP & Servlet/- 부트스트랩] - 부트스트랩으로 게시판 만들기_글쓰기 화면 [2/3]
[· JSP & Servlet/- 부트스트랩] - 부트스트랩으로 게시판 만들기_게시물 보기 화면 [3/3]
[· JSP & Servlet/- 기본 문법] - MVC 패턴의 게시판 만들기_계층형 게시판 로직 설계 [1/5]
[· JSP & Servlet/- 기본 문법] - MVC 패턴의 게시판 만들기_페이징 처리(오라클DB) [2/5]
[· JSP & Servlet/- 기본 문법] - MVC 패턴의 게시판 만들기_리스트 출력 로직 [3/5]
[· JSP & Servlet/- 기본 문법] - MVC 패턴의 게시판 만들기_글작성 로직 [4/5]
[· JSP & Servlet/- 기본 문법] - MVC 패턴의 게시판 만들기_게시물 보기 로직 [5/5]
전체 코드는 깃허브에 있습니다. 커밋 시점은 "add board" 입니다.
https://github.com/codevang/hssweb
요청된 페이지의 게시물 데이터를 DB에서 가져와 DTO 배열에 넣은뒤 전달해주는 DAO 클래스의 로직입니다. 이전 글에서 다룬 페이징 처리와 쿼리만 알면 간단합니다. 페이징 처리와 리스트를 처리할 뷰(JSP)는 위 링크글들을 참조하시면 됩니다.
먼저 요청 페이지에 따른 페이지 목록의 범위를 산출해서 request 객체에 담아줍니다. 없을 경우 게시물이 하나도 없는 경우이므로 적절히 처리해줍니다. 객체의 전달은 어떤 방법을 써도 무방할 것 같습니다. 다만 저도 코드를 짜면서 느낀건데 일정한 기준을 가지고 객체 전달 방식을 통일해주는게 좋을 것 같습니다.
/* 리스트 보기 */
public int readList(int page) {
try {
// 페이징 범위 산출 (null값은 게시물이 하나도 없을 경우)
int[] paging = paging(page); // DB 커넥션은 여기서 해줌
if (paging == null) {
return Ctrl.FALSE;
} else {
request.setAttribute("bdPaging", paging); // 페이징 객체 전달
}
이제 DB에 쿼리를 날려서 요청된 페이지에 속한 게시물 데이터를 받아옵니다. 어떤 종류의 statement 객체를 사용하건 쿼리만 잘 들어가면 됩니다. preparedStatement 객체는 물음표(?)로 지정한 곳을 정수형 또는 문자열로 쉽게 대체할 수 있으므로 가시성도 좋고 사용이 편합니다. 정수형을 넣는 setInt()는 숫자만 들어가고 문자열을 넣는 setString()은 홑따옴표(')가 양쪽에 추가되므로 서로 다르게 사용해줘야 합니다.
- 시작 게시물 rownum : (요청온 페이지 - 1) * 한페이지에 출력할 수 + 1
- 끝 게시물 rownum : 시작 게시물 + 한페이지에 출력할 수 - 1
// 리스트 정보 가져오기
String query = "select * from "
+ "(select ROWNUM as rnum, A.* from "
+ "(select * from board_chat order by bdgroup desc, bdorder)A ) "
+ "where rnum >= ? and rnum <= ?";
preStmt = conn.prepareStatement(query);
// 요청된 페이지에 따른 게시물 범위 지정
int startPage = (page - 1) * BoardList.pagePerList + 1; // 시작 게시물
int endPage = startPage + BoardList.pagePerList - 1; // 끝 게시물
preStmt.setInt(1, startPage);
preStmt.setInt(2, endPage);
result = preStmt.executeQuery();
쿼리가 정상적으로 실행됐으면 반복문으로 result안의 결과를 DTO 객체에 담아준 뒤, ArrayList에 하나씩 추가해줍니다.
// 결과를 ArrayList에 추가
ArrayList<BoardDTO> list = new ArrayList<>(); // 리스트 정보 담아줄 객체
while (result.next()) {
BoardDTO dto = new BoardDTO();
dto.setBdNum(result.getInt("bdnum"));
dto.setBdIndent(result.getInt("bdindent"));
dto.setBdTitle(result.getString("bdtitle"));
dto.setBdUserID(result.getString("bduserid"));
dto.setBdViewCount(result.getInt("bdviewcount"));
dto.setBdDate(result.getString("bddate"));
list.add(dto);
}
이렇게 담은 DTO객체의 배열을 뷰에게 전달해주면 됩니다. 이 부분 또한 각자 정한 구조에 맞는 방식으로 전달해주면 될 것 같습니다.
한가지 주의사항은 ArryaList와 같이 제네릭 타입 < >이 지정되는 객체는 타입 변환을 할 때 unsafe warning이 발생한다는 것입니다. 실행 자체에 영향을 주진 않지만 매우 거슬리므로, 어노테이션을 붙여서 warning을 제거하거나 저 같이 일반 배열 형태로 변환 후 전달해주면 깔끔합니다. return으로 전달하면 별 문제가 없겠지만 Attribute를 통해 전달할 때는 Object 타입으로 전달되므로 형변환을 해줘야 하기 때문에 warning이 뜹니다.
// 일반 배열로 지정해서 전달 (제네릭 타입은 타입변환에서 warning 발생)
BoardDTO[] bdList = list.toArray(new BoardDTO[list.size()]);
request.setAttribute("bdList", bdList); // 리스트 전달
return Ctrl.TRUE;
마지막으로 예외 처리를 한 뒤 모든 자원을 해제해줍니다. 저는 DB연결과 자원해제를 메소드로 따로 만들어서 사용했습니다. 업데이트 쿼리가 아닌 셀렉트 쿼리이므로 롤백은 따로 해주지 않아도 무방합니다.
} catch (Exception e) {
System.out.println("readList db working Fail");
e.printStackTrace();
} finally {
dbClose();
}
참고로 MVC 패턴에서 저처럼 작업 결과를 request에 담아서 뷰(jsp)에 전달하기 위해서는 리다이렉트 방식이 아닌 포워딩 방식으로 전달해줘야 합니다. 리다이렉트의 경우 응답을 한 뒤 재요청을 유도하는 것이기 때문에 응답과 동시에 request 객체가 소멸되기 때문입니다.
아래는 컨트롤러에서 포워딩해주는 구문입니다. 가장 아랫단에 page에 지정한 곳으로 리다이렉트하는 코드가 있기 때문에 포워딩 후에는 바로 return을 시켜줘야 합니다. 그렇지 않으면 이미 응답을 커밋했을 경우 다시 커밋할 수 없다는 메세지가 발생하게 됩니다.
/* 메인에서 게시판을 누르거나 페이지 번호를 클릭했을 경우(get방식으로 받음) */
} else if (uri.equals("/boardChat")) {
command = new BoardList(request);
int result = command.execute();
//
if (result == Ctrl.TRUE || result == Ctrl.FALSE) {
String path = "/board/boardList.jsp";
RequestDispatcher disp = request.getRequestDispatcher(path);
disp.forward(request, response);
return;
} else {
page = "/exception/exception.jsp";
}
[ db.BoardDAO.java - 리스트 출력 로직 전체 코드 ]
/* 리스트 보기 */
public int readList(int page) {
try {
// 페이징 범위 산출 (null값은 게시물이 하나도 없을 경우)
int[] paging = paging(page); // DB 커넥션은 여기서 해줌
if (paging == null) {
return Ctrl.FALSE;
} else {
request.setAttribute("bdPaging", paging); // 페이징 객체 전달
}
// 리스트 정보 가져오기
String query = "select * from "
+ "(select ROWNUM as rnum, A.* from "
+ "(select * from board_chat order by bdgroup desc, bdorder)A ) "
+ "where rnum >= ? and rnum <= ?";
preStmt = conn.prepareStatement(query);
// 요청된 페이지에 따른 게시물 범위 지정
int startPage = (page - 1) * BoardList.pagePerList + 1; // 시작 게시물
int endPage = startPage + BoardList.pagePerList - 1; // 끝 게시물
preStmt.setInt(1, startPage);
preStmt.setInt(2, endPage);
result = preStmt.executeQuery();
// 결과를 ArrayList에 추가
ArrayList<BoardDTO> list = new ArrayList<>(); // 리스트 정보 담아줄 객체
while (result.next()) {
BoardDTO dto = new BoardDTO();
dto.setBdNum(result.getInt("bdnum"));
dto.setBdIndent(result.getInt("bdindent"));
dto.setBdTitle(result.getString("bdtitle"));
dto.setBdUserID(result.getString("bduserid"));
dto.setBdViewCount(result.getInt("bdviewcount"));
dto.setBdDate(result.getString("bddate"));
list.add(dto);
}
// 일반 배열로 지정해서 전달 (제네릭 타입은 타입변환에서 warning 발생)
BoardDTO[] bdList = list.toArray(new BoardDTO[list.size()]);
request.setAttribute("bdList", bdList); // 리스트 전달
return Ctrl.TRUE;
} catch (Exception e) {
System.out.println("readList db working Fail");
e.printStackTrace();
} finally {
dbClose();
}
return Ctrl.EXCEPT;
}
/* 페이징 범위 구하기 */
private int[] paging(int page) throws Exception {
dbConnect();
stmt = conn.createStatement();
String query = "select count(*) from board_chat";
result = stmt.executeQuery(query);
// 전체 게시물 갯수로 총 페이지 수 산출 (하나도 없으면 null 리턴)
int totalContent = 0;
int totalPage = 0;
while (result.next()) {
totalContent += result.getInt(1);
}
if (totalContent == 0) {
return null;
}
totalPage = totalContent / BoardList.pagePerList; // 최종 전체 페이지 갯수
if (totalContent % BoardList.pagePerList > 0) {
totalPage++; // 나머지가 있다면 1을 더해줌
}
// 페이징 범위 계산
int startPage, endPage; // 시작과 끝 페이지
startPage = ((page - 1) / BoardList.pagingCount) * BoardList.pagingCount
+ 1;
endPage = startPage + BoardList.pagingCount - 1;
if (endPage > totalPage) {
endPage = totalPage;
}
int[] startEnd = new int[2]; // 결과를 전달해줄 배열
startEnd[0] = startPage;
startEnd[1] = endPage;
// result 객체는 호출한 곳에서 재활용할 것이므로 자원해제
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}
return startEnd;
}
'▸JSP & Servlet > 기본 문법' 카테고리의 다른 글
MVC 패턴의 게시판 만들기_게시물 보기 로직 [5/5] (0) | 2020.03.03 |
---|---|
MVC 패턴의 게시판 만들기_글작성 로직 [4/5] (0) | 2020.03.03 |
MVC 패턴의 게시판 만들기_페이징 처리(오라클DB) [2/5] (0) | 2020.03.02 |
MVC 패턴의 게시판 만들기_계층형 게시판 로직 설계 [1/5] (4) | 2020.02.28 |
MCV 패턴의 자동 로그인, 회원가입 만들기 [3/3] (0) | 2020.02.24 |
댓글