Servlet/JSP를 이용해 웹 페이지를 만들기 위하여 환경설정을 해 보자
*Oracle & sql developer
1. 계정 생성 👉🏻 관리자 계정(sys as sysdba)에서 진행
-- SERVER / SERVER
CREATE USER SERVER IDENTIFIED BY SERVER; -- 계정 생성
GRANT CONNECT, RESOURCE TO SERVER; -- 최소한의 권한 부여
2. 해당 계정으로 접속 후 실행
3. Table Scripts.sql 스크립트 실행
(server계정으로 지정되어 있는지 반드시 확인 후 진행할 것)
*Java, Servlet, JSP & Eclipse
1. 환경 설정
1-1) Java EE(오른쪽 상단 커피콩 모양) 맞는지 확인
1-2) Console, Navigator, Problems 탭 추가
1-2) 인코딩 설정 (7가지)
Preferences - Gerenal - workspace - Text file encoding - Other : UTF-8
Preferences - Gerenal - Editors - Text Editors - Spelling - Other : UTF-8
Preferences - Web - Css Files - Encoding : ISO 10646/Unicode(UTF-8)
Preferences - Web - HTML Files - Encoding : ISO 10646/Unicode(UTF-8)
Preferences - Web - JSP Files - Encoding : ISO 10646/Unicode(UTF-8)
Preferences - JSON - JSON Files - Encoding : ISO 10646/Unicode(UTF-8)
Preferences - XML - XML Files - Encoding : ISO 10646/Unicode(UTF-8)
1-3) 서버 실행 환경 잡기
Preferences - Server - Runtime Environment
- Add - Apache Tomcat v8.5 (Create a new local server 체크)
- Next
- Name(수정해도 무방),Tomcat installation directory(톰캣 설치 경로: C:\dev\apache-tomcat-8.5.82)
- Finish
- 아래 사진과 같이 확인된다면 Apply and Close
1-4) 서버 추가
(1-3에서 create a new local server 체크했다면 자동으로 만들어졌을 것)
체크하지 않았다면, 아래 방법으로도 가능!
New - Server - Tomcat v8.5 Server (Server's host name 절대 수정 금지!) - Next - Finish
1-5) 서버 기타 환경
HTTP/1.1 port number: 8080 8888 변경
Serve modules without publishing 체크
2. 동적 웹 프로젝트 생성
프로젝트명: JSP_Project
Defalut output folder: WebContent\WEB-INF\classes
Content root: jsp
기본값이 프로젝트명으로 되어 있어 url에 노출됨 👉🏻 보안 문제로 변경하는 것을 권장
http://localhost:8888/jsp 👉🏻 이 설정으로 하여금 이 주소가 기본 url이 되는 셈
3. WebContent 내부에 메인 페이지(index.jsp) 만들기
메인 페이지로 설정 가능한 이름은 web.xml에서 확인 가능함
(이번 실습과 앞으로는 index.jsp를 사용할 예정)
WebContent 내부에 index.jsp 생성
4. 서버에 프로젝트 올리기
5. 구현할 기능 정의
[ CRUD ]
웹 사이트 입장에서 기본적으로 갖춰야 할 데이터 처리 기능들을 의미함 (데이터 추가, 조회, 수정, 삭제)
C : Create(생성)의 약자 => INSERT 구문 (데이터를 생성)
R : Read(읽기)의 약자 => SELECT 구문 (데이터를 조회)
U : Update(갱신)의 약자 => UPDATE 구문(데이터 수정)
D : Delete(삭제)의 약자 => DELETE 구문(데이터 삭제, 제거)
5-1) 회원 서비스
*회원 서비스 | C (INSERT) | R (SELECT) | U (UPDATE) | D (DELETE) |
로그인 | O | |||
회원가입 | O | |||
[아이디 중복확인] | O | |||
마이페이지 | O | |||
회원정보변경 | O | |||
회원탈퇴 | O (문맥상 DELETE는 맞지만 실제 구현은 UPDATE) |
5-2) 공지사항 서비스
*공지사항 서비스 | C (INSERT) | R (SELECT) | U (UPDATE) | D (DELETE) |
공지사항 리스트 조회 | O | |||
공지사항 상세 조회 | O | |||
공지사항 작성 | O | |||
공지사항 수정 | O | |||
공지사항 삭제 | O (문맥상 DELETE는 맞지만 실제 구현은 UPDATE) |
5-3) 일반 게시판 서비스
*일반 게시판 서비스 | C (INSERT) | R (SELECT) | U (UPDATE) | D (DELETE) |
게시판 리스트 조회 -페이징 처리 |
O | |||
게시판 상세 조회 | O | |||
게시판 작성 - 첨부파일 1개 업로드 |
O | |||
게시판 수정 | O | |||
게시판 삭제 | O (문맥상 DELETE는 맞지만 실제 구현은 UPDATE) | |||
댓글 리스트 조회 | O | |||
댓글 작성 | O |
5-4) 사진 게시판 서비스
*사진 게시판 서비스 | C (INSERT) | R (SELECT) | U (UPDATE) | D (DELETE) |
게시판 리스트 조회 - 썸네일 |
O | |||
게시판 상세 조회 | O | |||
게시판 작성 - 다중 첨부파일 업로드 |
O |
6. 화면 파일 폴더 생성 및 패키지 구성
6-1) WebContent 폴더에 views 폴더 생성
6-2) views 폴더에 각 기능을 구현할 일반 폴더 생성
views/member
views/notice
views/board
views/common
6-3) src 폴더에 패키지 생성
<member 관련 패키지>
com.kh.member.controller
com.kh.member.model.service
com.kh.member.model.dao
com.kh.member.model.vo
<notice 관련 패키지>
com.kh.notice.controller
com.kh.notice.model.service
com.kh.notice.model.dao
com.kh.notice.model.vo
<board 관련 패키지>
com.kh.board.controller
com.kh.board.model.service
com.kh.board.model.dao
com.kh.board.model.vo
<common 관련 패키지>
com.kh.common👉🏻 총 13개의 패키지 생성 / controller에는 Servlet만 들어갈 것!
6-4) src 폴더 내에 sql 관련 일반 폴더 생성
(JDBC와 관련된 파일들이 포함될 폴더)
1. scr/sql 일반 폴더로 생성
2. src/sql 폴더 하위에 driver, member, notice, board 일반 폴더 생성
나... 이때 member 경로 설정 잘못해서 이렇다
나중에 순서대로 진행할 때 전체 폴더 상태 보라고 하얀 박스만 쳐 놓았지만
그냥 member가 아니라 memer.model 안에 vo, dao, service 들어가야 하므로 헷갈리지 말기!
6-5) WebContent/resources 관련 일반 폴더 생성
1. WebContent/resources 일반 폴더로 생성
2. WebContent/resources 폴더 하위에 css, js, images 일반 폴더 생성
6-6) src/sql/driver 폴더 하위에 driver.properties 파일 생성
데이터베이스 접속 정보를 담는 파일
미러링된 WebContent/classes 파일 내에 생성하지 않도록 주의할 것
7. DB 연결
7-1) driver.properties에 접속 정보 작성
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
username=SERVER
password=SERVER
7-2) JDBC와 연결하기
외부 자료를 사용하고 싶다면 WEB-INF/lib 폴더에 .jar 넣어 주면 됨
ojdbc6.jar 파일을 복사해서 이클립스WEB-INF/lib 폴더에 그대로 붙여 넣기!
7-3) 쿼리문 .xml 가지고 오기
1. src/sql/member 경로에 member-mapper.xml
2. src/sql/board 경로에 board-mapper.xml
3. src/sql/notice 경로에 notice-mapper.xml
추후 mybatis를 쓸 때 xml을 가지고 오려면 xxx-mapper.xml로 통일해 줘야 해서 미리 이렇게 설정함
(기존에 JDBC 수업 때 썼던 query.xml 복붙 한 뒤에 수정해 줬음)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>SQL</comment>
<entry key="">
</entry>
</properties>
8. JDBC Template 클래스 생성
com.kh.common 패키지에 JDBCTemplate 클래스 생성
👉🏻 자주 쓰이는 JDBC 관련 반복 코드를 메소드 단위별로 묶어서 정의함
package com.kh.common;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
// 자주 쓰이는 JDBC 관련한 반복적인 코드를 메소드 단위별로 묶어서 정의 => static => 싱글톤 패턴
public class JDBCTemplate {
// 1. Connection 객체 생성 (DB 접속) 후 해당 Connection 객체를 반환하는 메소드
// 동적 코딩 방식
public static Connection getConnection() {
// driver.properties 파일로부터 key에 대한 value 값들을 읽어오기
// => Properties 객체가 필요함 (Map 계열 컬렉션)
Properties prop = new Properties();
// 읽어들이고자 하는 driver.properties 파일의 물리적인 경로
// => 지금 내가 작업 중인 폴더는 src 폴더가 아닌 배포되는 폴더 기준 (=실행되는 폴더 기준)으로 잡아야 함!
// 나중에 배포 시에는 src 폴더는 주지 않기 때문에 그때는 경로를 찾지 못함!
String fileName = JDBCTemplate.class.getResource("/sql/driver/driver.properties").getPath();
// .class까지의 의미: 이 클래스의 컴파일된 파일 / WebContent.WEB-INF.classes(슬래시가 의미하는 곳).com.kh.common.JDBCTemplate
// .getResource()까지의 의미: 괄호 안의 클래스 파일로부터 다른 파일을 얻어내겠다
// => ~~.class.getResource()이므로 class의 최상위 경로인 classes가 슬래시가 의미하는 바가 됨
// .getPath()까지의 의미: 해당 파일의 문자열 타입의 풀 경로
// C:\06_Web-workspace2\JSP_Project\WebContent\WEB-INF\classes\sql\driver\driver.properties
// 현재 fileName에는 위 경로가 담겨 있음
try {
prop.load(new FileInputStream(fileName));
} catch (IOException e1) {
e1.printStackTrace();
}
Connection conn = null;
try {
// 1) JDBC Driver 등록
Class.forName(prop.getProperty("driver"));
// 2) DB와 접속된 Connection 객체를 생성
conn = DriverManager.getConnection(prop.getProperty("url")
, prop.getProperty("username")
, prop.getProperty("password"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) { // 부모 클래스 타입이 더 밑에 있어야 Unreachable Code가 되지 않음!
e.printStackTrace();
}
return conn;
/*
// 정적 코딩 방식
Connection conn = null;
try {
// 1) JDBC Driver 등록
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2) DB와 접속된 Connection 객체를 생성
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "SERVER", "SERVER");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) { // 부모 클래스 타입이 더 밑에 있어야 Unreachable Code가 되지 않음!
e.printStackTrace();
}
return conn;
*/
}
// 2. 전달받은 Connection 객체를 가지고 commit 해 주는 메소드
public static void commit(Connection conn) {
try {
if(conn != null && conn.isClosed()) {
conn.commit();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3. 전달받은 Connection 객체를 가지고 rollback 해 주는 메소드
public static void rollback(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.rollback();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 4. 전달받은 Connection 객체를 반납시켜 주는 메소드
public static void close(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 5. 전달받은 Statement 객체를 반납시켜 주는 메소드
// => 다형성에 의해 자식 클래스인 PreparedStatement 객체도 반납 가능함
public static void close(Statement stmt) { // 오버로딩에 의해 같은 메소드명이 허용
try {
if(stmt != null && !stmt.isClosed()) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 6. 전달받은 ResultSet 객체를 반납시켜 주는 메소드
public static void close(ResultSet rset) {
try {
if(rset != null && !rset.isClosed()) {
rset.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
9. 공통 헤더(header) 만들기
WebContent/views/common 내에 menubar.jsp 이름으로 jsp file 생성
index.jsp 에서 하단의 코드 추가
<!-- 메인 페이지의 상단에 항상 menubar.jsp가 존재하도록 include -->
<%@ include file="views/common/menubar.jsp" %>
*Front-End & Visual Studio Code
1. menubar.jsp 꾸미기
C:\06_Web-workspace2\JSP_Project\WebContent\views 경로로 폴더 열고 작업 시작
<%@ 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>
<style>
#login-form, #user-info { float : right; }
/* 오른손잡이가 많은 사용자 편의상 오른쪽이 좋다고 함 */
#user-info a {
color : black;
text-decoration: none;
font-size : 12px;
}
.nav-area { background-color : black; }
.menu {
display : table-cell; /* 인라인 요소처럼 배치 가능 */
height : 50px;
width : 150px;
}
.menu a {
text-decoration : none;
color : white;
font-size : 20px;
font-weight : bold;
display : block;
/* 원래 글자에만 마우스 클릭이 먹혔는데 얘 하면 그 공간 자체를 클릭 가능해짐 */
width : 100%;
height : 100%;
line-height : 50px;
}
.menu a:hover {
background-color : darkgray;
}
button {
background-color: black;
color : white;
border-color : white;
}
</style>
</head>
<body>
<h1 align="center">Welcome D Class</h1>
<div class="lonin-area">
<!-- 로그인 전에 보여지는 로그인 form -->
<!-- action 값은 로그인 화면 처리 기능 만든 후 url 넣을 예정 -->
<form id="login-form" action="" method="post">
<table>
<tr>
<th>아이디: </th>
<td><input type="text" name="userId" required></td>
</tr>
<tr>
<th>비밀번호: </th>
<td><input type="password" name="userPwd" required></td>
</tr>
<tr>
<th colspan="2">
<button type="submit">로그인</button>
<button type="button">회원가입</button>
</th>
</tr>
</table>
</form>
<!-- 로그인 성공 후에 보여지는 프로필 화면 -->
<!--
<div id="user-info">
<b>xxx님!</b> 환영합니다요 ^0^ <br><br>
<div align="center">
<a href="">마이페이지</a>
<a href="">로그아웃</a>
</div>
</div>
-->
</div>
<br clear="both"> <!-- float 속성 해제 -->
<br>
<div class="nav-area" align="center">
<!-- (div.menu>a)*4 + Enter -->
<div class="menu"><a href="">HOME</a></div>
<div class="menu"><a href="">공지사항</a></div>
<div class="menu"><a href="">일반게시판</a></div>
<div class="menu"><a href="">사진게시판</a></div>
</div>
</body>
</html>
CRUD를 구성하기 전에
*Java, Servlet, JSP & Eclipse
1.vo에 sql의 한 행별로 정보를 받을 수 있게 member 클래스 구성하기
sql의 member 테이블 👉🏻 Java의 vo 클래스로 바꾸는 과정
package com.kh.member.model.vo;
import java.sql.Date;
public class Member {
// 필드부
private int userNo; // USER_NO NUMBER PRIMARY KEY,
private String userId; // USER_ID VARCHAR2(30) NOT NULL UNIQUE,
private String userPwd; // USER_PWD VARCHAR2(100) NOT NULL,
private String userName; // USER_NAME VARCHAR2(15) NOT NULL,
private String phone; // PHONE VARCHAR2(13),
private String email; // EMAIL VARCHAR2(100),
private String address; // ADDRESS VARCHAR2(100),
private String interest; // INTEREST VARCHAR2(100),
private Date enrollDate; // ENROLL_DATE DATE DEFAULT SYSDATE,
private Date modifyDate; // MODIFY_DATE DATE DEFAULT SYSDATE,
private String status; // STATUS VARCHAR2(1) DEFAULT 'Y' CHECK (STATUS IN('Y', 'N'))
// 생성자부
public Member() { } // 기본 생성자
public Member(int userNo, String userId, String userPwd, String userName, String phone, String email,
String address, String interest, Date enrollDate, Date modifyDate, String status) {
super();
this.userNo = userNo;
this.userId = userId;
this.userPwd = userPwd;
this.userName = userName;
this.phone = phone;
this.email = email;
this.address = address;
this.interest = interest;
this.enrollDate = enrollDate;
this.modifyDate = modifyDate;
this.status = status;
}
public int getUserNo() {
return userNo;
}
public void setUserNo(int userNo) {
this.userNo = userNo;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getInterest() {
return interest;
}
public void setInterest(String interest) {
this.interest = interest;
}
public Date getEnrollDate() {
return enrollDate;
}
public void setEnrollDate(Date enrollDate) {
this.enrollDate = enrollDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "Member [userNo=" + userNo + ", userId=" + userId + ", userPwd=" + userPwd + ", userName=" + userName
+ ", phone=" + phone + ", email=" + email + ", address=" + address + ", interest=" + interest
+ ", enrollDate=" + enrollDate + ", modifyDate=" + modifyDate + ", status=" + status + "]";
}
// 메소드부
}
2. MemberService와 MemberDao 클래스 만들기
이제 menubar.jsp의 action 속성을 채울 수 있음!
<h1 align="center">Welcome D Class</h1>
<div class="lonin-area">
<!-- 로그인 전에 보여지는 로그인 form -->
<!-- action 값은 로그인 화면 처리 기능 만든 후 url 넣을 예정 -->
<!--
현재 나의 위치: http://localhost:8888/jsp/
로그인 버튼을 클릭했을 때 이동하고자 하는 위치: http://localhost:8888/jsp/login.me
절대경로: /jsp/login.me
상대경로(마지막 슬래시 기준 문구가 붙는 것):login.me
-->
<form id="login-form" action="/jsp/login.me" method="post">
하지만 아직 login.me의 서블릿을 만들지 않아서 404 오류가 뜨는 상태!
URL은 잘 이동한 것을 볼 수 있음
3. login.me 서블릿 만들기
src.com.kh.member.controller 경로 확인
Class name: LoginController
URL Mapping 값: /login.me
post 형식이기 때문에 doPost 메소드를 써야 하지만,
기본적으로 doPost 메소드에서 doGet 메소드를 호출하기 때문에
doGet 메소드에서 진행할 것!