*JAVA/JSP 35번 글의 시점부터는 노트북이 고장나는 바람에.. 다른 분의 글과 이전에 올렸던 글들을 참고하여, 다른 데스크탑의 윈도우 환경에 환경설정을 다시 진행한 후 작성하였습니다. 환경설정 관련 글들은 아래를 참고해 주세요.
- JDK11 설치 및 환경 변수(JAVA_HOME) 설정 - CLASSPATH는 추가하지 않음
자바(JAVA) 11 설치 및 환경설정 | 자바 11 환경 변수
자바로 코딩 공부를 하려면 개발 할 수 있는 환경을 만들어 주어야 한다. 그 순서를 먼저 작성하자면JAVA SE JDK 설치 -> JAVA 환경 변수 설정 -> IDE 설치 이렇게 볼 수 있겠다. 이 포스팅은 자바로 개
velog.io
- 톰캣 설치, 이클립스 설치 - 이 글을 쓰는 시점에서는 Tomcat 8버전이 없어, Tomcat 9버전 설치
JSP) JDK, 이클립스(eclipse), 톰캣 설치 및 환경 설정
1. JDK 설치 → Path 설정 2.eclipse JAVA EE버전 다운로드 3.웹컨테이너 톰캣 설치 →톰캣 환경설정 1. JDK 설치 - JAVA) 1. JDK 참고하여 설치하고 설정한다. -https://storyblogger.tistory.com/15?category=984696 2. 구글에
storyblogger.tistory.com
- 이클립스 JDK 버전 설정 - JDK11로 설정
[이클립스] JDK 버전 바꾸는 법
스프링을 사용하는 에러가 난다? 그러면 JRE 버전을 확인해보자! 프로젝트의 JRE System Library의 오른쪽을 살펴보면 [JavaSE-17]이라고 되어있다. 스프링이나 하이버네이트를 사용할 때, 자바 8이나 자
myvelop.tistory.com
- JDK 설치 및 환경 변수(JAVA_HOME) 설정, 톰캣 설치, 이클립스 설치 및 JDK 버전 설정 이후~ ⇒ 톰캣 버전과 jsp 파일 경로는 글 내용과 다르게 아래 이미지와 같음
JAVA/JSP 1. 개발 환경 구축 - 이클립스 기본 설정
*JDK 설치 및 환경 변수(JAVA_HOME) 설정, 톰캣 설치, 이클립스 설치를 완료한 상태OpenJDK 11: 자바 프로그램을 컴파일하고 실행해주는 기본 도구*이클립스 JDK-17이 설치되어있어서, https://coding-house.tisto
keep-programming-study.tistory.com
JAVA/JSP 1. 개발 환경 구축 - JSP 예제 테스트
1. 프로젝트 생성1) [File] → [New] → [Dynamic Web Project] 선택 2) 프로젝트 설정 화면 → Project name: HelloJSP, Target runtime: Apache Tomcat v9.0, Dynamic web module version: 4.0으로 설정 후 Next 클릭 3) 자바 소스 파일
keep-programming-study.tistory.com
JAVA/JSP 1. 개발 환경 구축 - 마지막 추가 설정
1. 외부 웹 브라우저로 실행하기-크롬이클립스는 웹 애플리케이션 실행 시, 자체 브라우저를 통해 실행 결과 출력웹 애플리케이션 배포 시 사용자는 크롬/파이어폭스 같은 전용 웹 브라우저로
keep-programming-study.tistory.com
- 오라클(Oracle Database 11gR2 Express Edition) 설치 및 설정
JAVA/JSP 14. 데이터베이스 - 특징, 오라클 설치(Oracle Database 11gR2 Express Edition), 사용자 계정 생성 및
1. 데이터베이스의 특징우리가 매일 PC나 스마트폰을 통해 접하는 거의 모든 웹 애플리케이션에서 사용함매일 업데이트되는 뉴스나 날씨 등의 정보는 데이터베이스가 없다면 클라이언트에 전달
keep-programming-study.tistory.com
JAVA/JSP 15. 데이터베이스 - 테이블 및 시퀀스 생성, JDBC 설정 및 데이터베이스 연결
*회원제 게시판 만들기-회원 인증 필요4. 테이블 및 시퀀스 생성1) 테이블 생성(1) member 테이블 - 아이디, 패스워드, 이름, 가입 날짜테이블 정의컬럼명데이터 타입 null 허용 키 기본값 설명idvarchar2
keep-programming-study.tistory.com
JAVA/JSP 16. 데이터베이스 - 커넥션 풀로 성능 개선, 간단한 쿼리 작성 및 실행
6. 커넥션 풀로 성능 개선웹은 클라이언트의 요청에 서버가 응답하는 구조→ Connection 객체 생성 때마다 네트워크 통신이 이뤄지며, 시간이 걸리는 작업들이 수반됨 == 시스템 성능에 큰 영향을
keep-programming-study.tistory.com
- 시퀀스 설정
JAVA/JSP 15. 데이터베이스 - 테이블 및 시퀀스 생성, JDBC 설정 및 데이터베이스 연결
*회원제 게시판 만들기-회원 인증 필요4. 테이블 및 시퀀스 생성1) 테이블 생성(1) member 테이블 - 아이디, 패스워드, 이름, 가입 날짜테이블 정의컬럼명데이터 타입 null 허용 키 기본값 설명idvarchar2
keep-programming-study.tistory.com
1. 프로젝트 구상
- EL, JSTL, 파일 업로드, 서블릿 활용
1) 제공할 기능
- 비회원제
- 회원인증 없이 누구나 글 작성
- 글쓰기 시 비밀번호 입력이 필수이며, 비밀번호를 통해 수정/삭제 가능
- 자료실
- 글쓰기 시 파일을 첨부할 수 있는데, 파일 첨부 시 정해진 용량 이상은 업로드 불가
- 첨부된 파일은 다운로드 가능
2) 프로젝트 프로세스
(1) 모델2의 구조
(2) 자료실형 게시판 다이어그램
- *.java: 컨트롤러, *.jsp: 뷰
(3) 게시판 기능별 요청명 패턴
기능 | 매핑 방법 | 요청명 | 컨트롤러(서블릿) | 뷰(JSP) 경로 |
목록 보기 | web.xml | /board/list.board | ListController | /mvcboard/List.jsp |
글쓰기 | web.xml | /board/write.board | WriteController | /mvcboard/Write.jsp |
상세 보기 | 어노테이션 | /board/view.board | ViewController | /mvcboard/View.jsp |
비밀번호 검증 | 어노테이션 | /board/pass.board | PassController | /mvcboard/Pass.jsp |
수정 | 어노테이션 | /board/edit.board | EditController | /mvcboard/Edit.jsp |
삭제 | 어노테이션 | 필요 없음 | PassController | 필요 없음 |
다운로드 | 어노테이션 | /board/download.board | DownloadController | 필요 없음 |
2. 목록 보기 구현
1) 테이블 생성
(1) 테이블 정의서
컬럼명 | 데이터 타입 | null 허용 | 키 | 기본값 | 설명 |
idx | number | N | 기본키 | 게시물 일련번호 | |
name | varchar2(50) | N | 작성자 이름 | ||
title | varchar2(200) | n | 제목 | ||
content | varchar2(2000) | N | 내용 | ||
postdate | date | N | sysdate | 작성일 | |
ofile | varchar2(200) | 원본 파일명 | |||
sfile | varchar2(30) | 저장된 파일명 | |||
downcount | number | N | 0 | 다운로드 횟수 | |
pass | varchar2(50) | N | 비밀번호 | ||
visitcount | number | N | 0 | 게시물 조회수 |
(2) 테이블 생성 쿼리문
create table mvcboard (
idx number primary key,
name varchar2(50) not null,
title varchar2(200) not null,
content varchar2(2000) not null,
postdate date default sysdate not null,
ofile varchar2(200),
sfile varchar2(30),
downcount number(5) default 0 not null,
pass varchar2(50) not null,
visitcount number default 0 not null
);
(3) 오라클 데이터베이스에 테이블을 생성하고 테스트용 데이터 추가
- 명령 프롬프트(cmd)에서 sqlplus 명령으로 오라클 데이터베이스에 연결한 후, 사용자이름 hello 비밀번호 1234로 로그인
- 테이블 생성 쿼리문 입력 -> desc mvcboard로 테이블 생성 확인
- 테스트용 데이터 추가
insert into mvcboard (idx, name, title, content, pass)
values (seq_board_num.nextval, '감자', '자료실 제목 1', '내용1', '1234');
insert into mvcboard (idx, name, title, content, pass)
values (seq_board_num.nextval, '고구마', '자료실 제목 2', '내용2', '1234');
insert into mvcboard (idx, name, title, content, pass)
values (seq_board_num.nextval, '돼지감자', '자료실 제목 3', '내용3', '1234');
insert into mvcboard (idx, name, title, content, pass)
values (seq_board_num.nextval, '당근', '자료실 제목 4', '내용4', '1234');
insert into mvcboard (idx, name, title, content, pass)
values (seq_board_num.nextval, '양파', '자료실 제목 5', '내용5', '1234');
2) DTO 및 DAO 클래스 생성
(1) DTO 클래스
// Java Resources/src/mvcboard/MVCBoardDTO.java
package mvcboard;
public class MVCBoardDTO {
// 1. 멤버 변수 선언
private String idx;
private String name;
private String title;
private String content;
private java.sql.Date postdate;
private String ofile;
private String sfile;
private int downcount;
private String pass;
private int visitcount;
// 2. 게터/세터: 이클립스의 source -> Generate Getters and Setters 클릭하여 자동생성
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public java.sql.Date getPostdate() {
return postdate;
}
public void setPostdate(java.sql.Date postdate) {
this.postdate = postdate;
}
public String getOfile() {
return ofile;
}
public void setOfile(String ofile) {
this.ofile = ofile;
}
public String getSfile() {
return sfile;
}
public void setSfile(String sfile) {
this.sfile = sfile;
}
public int getDowncount() {
return downcount;
}
public void setDowncount(int downcount) {
this.downcount = downcount;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public int getVisitcount() {
return visitcount;
}
public void setVisitcount(int visitcount) {
this.visitcount = visitcount;
}
}
(2) DAO 클래스
// Java Resources/src/mvcboard/MVCBoardDAO.java
package mvcboard;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import common.DBConnPool;
public class MVCBoardDAO extends DBConnPool { // 커넥션 풀 상속
public MVCBoardDAO() {
super();
}
// 검색 조건에 맞는 게시물 개수 반환
public int selectCount(Map<String, Object> map) {
int totalCount = 0;
// 쿼리문 준비
String query = "SELECT COUNT(*) FROM mvcboard";
// 검색 조건이 있을 경우, WHERE 절로 추가
if (map.get("searchWord") != null) {
query += " WHERE " + map.get("searchField") + " "
+ "LIKE '%" + map.get("searchWord") + "%'";
}
try {
stmt = con.createStatement(); // 쿼리문 생성
rs = stmt.executeQuery(query); // 쿼리문 실행
rs.next();
totalCount = rs.getInt(1); // 검색된 게시물 개수 저장
} catch (Exception e) {
System.out.println("게시물 카운트 중 예외 발생");
e.printStackTrace();
}
return totalCount; // 게시물 개수를 서블릿으로 반환
}
// 검색 조건에 맞는 게시물 목록 반환(페이징 기능 지원)
public List<MVCBoardDTO> selectListPage(Map<String, Object> map) {
List<MVCBoardDTO> board = new Vector<MVCBoardDTO>();
// 쿼리문 준비
String query = "SELECT * FROM ( "
+ " SELECT Tb.*, ROWNUM rNum FROM ( "
+ " SELECT * FROM mvcboard ";
// 검색 조건이 있을 경우, WHERE 절로 추가
if (map.get("searchWord") != null) {
query += " WHERE " + map.get("searchField") + " "
+ "LIKE '%" + map.get("searchWord") + "%'";
}
query += " ORDER BY idx DESC "
+ " ) Tb "
+ " ) "
+ " WHERE rNum BETWEEN ? AND ?"; // 게시물 구간은 인파라미터로
try {
psmt = con.prepareStatement(query); // 동적 쿼리문 생성
psmt.setString(1, map.get("start").toString()); // 인파라미터 설정
psmt.setString(2, map.get("end").toString());
rs = psmt.executeQuery(); // 쿼리문 실행
// 반환된 게시물 목록을 List 컬렉션에 추가
while (rs.next()) {
MVCBoardDTO dto = new MVCBoardDTO();
dto.setIdx(rs.getString(1));
dto.setName(rs.getString(2));
dto.setTitle(rs.getString(3));
dto.setContent(rs.getString(4));
dto.setPostdate(rs.getDate(5));
dto.setOfile(rs.getString(6));
dto.setSfile(rs.getString(7));
dto.setDowncount(rs.getInt(8));
dto.setPass(rs.getString(9));
dto.setVisitcount(rs.getInt(10));
board.add(dto);
}
} catch (Exception e) {
System.out.println("게시물 조회 중 예외 발생");
}
return board; // 게시물 목록 반환
}
}
3) 진입 화면 작성, 요청명과 서블릿 매핑
(1) 진입 화면(뷰) jsp 파일 추가
<!-- src/main/webapp/board/Default.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>파일 첨부형 게시판</h2>
<a href="../board/list.board">게시판 목록 바로가기</a>
</body>
</html>
(2) 요청명과 서블릿 매핑(web.xml 수정)
<!-- src/main/webapp/WEB-INF/web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
<display-name>HelloJSP</display-name>
... 생략 ...
<servlet> <!-- 서블릿 등록 -->
<servlet-name>MVCBoardList</servlet-name> <!-- 1. 서블릿 매핑을 위한 서블릿명 -->
<servlet-class>mvcboard.ListController</servlet-class> <!-- 2. 요청을 처리할 서블릿을 패키지를 포함하여 명시 -->
</servlet>
<servlet-mapping> <!-- 서블릿과 요청명 매핑 -->
<servlet-name>MVCBoardList</servlet-name> <!-- 3. 1과 동일한 서블릿명 -->
<url-pattern>/board/list.board</url-pattern> <!-- 4. 컨텍스트 루트를 제외한 요청명 작성 -->
</servlet-mapping>
<context-param>
<param-name>POSTS_PER_PAGE</param-name>
<param-value>10</param-value>
</context-param>
<context-param>
<param-name>POSTS_PER_BLOCK</param-name>
<param-value>5</param-value>
</context-param>
</web-app>
4) List 컨트롤러(서블릿) 작성, BoardPage.java 작성
// Java Resources/src/mvcboard/ListController.java
package mvcboard;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 1.
public class ListController extends HttpServlet {
// 2.
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 3. DAO 생성
MVCBoardDAO dao = new MVCBoardDAO();
// 4. 뷰에 전달할 매개변수 저장용 맵 생성
Map<String, Object> map = new HashMap<String, Object>();
String searchField = req.getParameter("searchField");
String searchWord = req.getParameter("searchWord");
// 5. 쿼리스트링으로 전달받은 매개변수 중 검색어가 있다면 map에 저장
if(searchWord != null) {
map.put("searchField", searchField);
map.put("searchWord", searchWord);
}
// 6. 게시물 개수
int totalCount = dao.selectCount(map);
// 7. 페이지 처리 시작
ServletContext application = getServletContext();
int pageSize = Integer.parseInt(application.getInitParameter("POSTS_PER_PAGE"));
int blockPage = Integer.parseInt(application.getInitParameter("POSTS_PER_BLOCK"));
// 8. 현재 페이지 확인
int pageNum = 1; // 기본값
String pageTemp = req.getParameter("pageNum");
if (pageTemp != null && !pageTemp.equals("")) {
pageNum = Integer.parseInt(pageTemp); // 요청받은 페이지로 수정
}
// 9. 목록에 출력할 게시물 범위 계산
int start = (pageNum - 1) * pageSize + 1; // 첫 게시물 번호
int end = pageNum * pageSize; // 마지막 게시물 번호
map.put("start", start);
map.put("end", end); // 페이지 처리 끝
List<MVCBoardDTO> boardLists = dao.selectListPage(map);
// 10. 게시물 목록 받기
dao.close(); // DB 연결 닫기
// 11.
// 뷰에 전달할 매개변수 추가
String pagingImg = BoardPage.pagingStr(totalCount, pageSize, blockPage, pageNum, "../board/list.board");
// 바로가기 영역 HTMl 문자열
map.put("pagingImg", pagingImg);
map.put("totalCount", totalCount);
map.put("pageSize", pageSize);
map.put("pageNum", pageNum);
// 12. 전달할 데이터를 request 영역에 저장 후 List.jsp로 포워드
// 13.
req.setAttribute("boardLists", boardLists);
req.setAttribute("map", map);
req.getRequestDispatcher("/board/List.jsp").forward(req, resp);
}
}
// Java Resources/src/mvcboard/BoardPage.java
package mvcboard;
public class BoardPage {
public static String pagingStr(int totalCount, int pageSize, int blockPage,
int pageNum, String reqUrl) {
String pagingStr = "";
// 전체 페이지 수 계산
int totalPages = (int) (Math.ceil(((double) totalCount / pageSize))); //1.
// '이전 페이지 블록 바로가기' 출력
int pageTemp = (((pageNum -1) / blockPage) * blockPage) + 1;
if (pageTemp != 1) { //2.
pagingStr += "<a href='" + reqUrl + "?pageNum=1'>[첫 페이지]</a>"; //3.
pagingStr += " ";
pagingStr += "<a herf='" + reqUrl + "?pageNum=" + (pageTemp -1)
+"'>[이전 블록]</a>"; //4.
}
// 각 페이지 번호 출력
int blockCount = 1;
while (blockCount <= blockPage && pageTemp <= totalPages) {
if (pageTemp == pageNum) { //5.
//현재 페이지는 링크를 걸지 않음
pagingStr += " " + pageTemp + " ";
} else {
pagingStr += " <a href='" + reqUrl + "?pageNum=" + pageTemp
+ "'>" + pageTemp + "</a> "; //6.
}
pageTemp++; //7.
blockCount++;
}
// '다음 페이지 블록 바로가기' 출력
if (pageTemp <= totalPages) { //8.
pagingStr += "<a href'" + reqUrl + "?pageNum=" + pageTemp
+ "'>[다음 블록]</a>"; //9.
pagingStr += " ";
pagingStr += "<a href='" + reqUrl + "?pageNum=" + totalPages
+ "'>[마지막 페이지]</a>"; //10.
}
return pagingStr;
}
}
5) 뷰(JSP) 만들기
<!-- src/main/webapp/board/List.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 첨부형 게시판 목록 보기</title>
</head>
<body>
<h2>파일 첨부형 게시판 목록 보기</h2>
<!-- 검색 폼: 검색어는 ListController로 전송된 후 DAO 클래스 메서드들의 인수로 전달됨 -->
<form method="get">
<table border="1" width="90%">
<tr>
<td align="center">
<select name="searchField">
<option value="title">제목</option>
<option value="content">내용</option>
</select>
<input type="text" name="searchWord" />
<input type="submit" value="검색하기" />
</td>
</tr>
</table>
</form>
<!-- 목록 테이블 -->
<table border="1" width="90%">
<tr>
<th width="10%">번호</th>
<th width="*">제목</th>
<th width="15%">작성자</th>
<th width="10%">조회수</th>
<th width="15%">작성일</th>
<th width="8%">첨부</th>
</tr>
<c:choose>
<c:when test="${ empty boardLists }"> <!-- 게시물이 없을 때: EL의 empty 연산자로 게시물이 있는지 확인 -->
<tr>
<td colspan="6" align="center">
등록된 게시물이 없습니다^^*
</td>
</tr>
</c:when>
<c:otherwise> <!-- 게시물이 있을 때: c:forEach 태그로 반복 출력 -->
<c:forEach items="${ boardLists }" var="row" varStatus="loop"> <!-- varStatus로 게시물의 가상번호 계산 -->
<tr align="center">
<td> <!-- 번호 -->
${ map.totalCount - (((map.pageNum-1) * map.pageSize) + loop.index)}
</td>
<td align="left"> <!-- 제목(상세보기 페이지 링크) -->
<a href="../board/view.board?idx=${ row.idx }">${ row.title }</a>
</td>
<td>${ row.name }</td> <!-- 작성자 -->
<td>${ row.visitcount }</td> <!-- 조회수 -->
<td>${ row.postdate }</td> <!-- 작성일 -->
<td> <!-- 첨부 파일(다운로드 링크) -->
<c:if test="${ not empty row.ofile }">
<a href="../board/download.board?ofile=${ row.ofile }&sfile=${ row.sfile }&idx=${ row.idx }">[Down]</a>
</c:if>
</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</table>
<!-- 하단 메뉴(페이지 번호 바로가기, 글쓰기) -->
<table border="1" width="90%">
<tr align="center">
<td>
${ map.pagingImg }
</td>
<td width="100"><button type="button"
onclick="location.href='../board/write.board';">글쓰기</button></td>
</tr>
</table>
</body>
</html>