자바 기반의 웹 컴포넌트
웹에서 호출 가능한 자바 프로그램
Web Brower
의 요청HTML에서 POST
요청을 보내기 위해서는 <form>
태그를 사용할 수 밖에 없다. (이외 다른 방법이 없다.)
<a href>
태그를 통해 GET
요청을 보낼 수 있다.
JS Script
를 통해 API
방식으로 POST
, PUT
, PATCH
등의 요청을 보낼 수 있다.
Postman
같은 경우, POST 요청을 지정해서 보내줄 수 있다.
.html
파일의 확장자를 .jsp
로 수정해주었다.WAS
가 JSP를 인식하고 Servlet으로 변환해야함을 알려주기 위함이다.
form
, method
부분을 퍼블리싱된 디폴트 값이 아닌, 우리 서블릿과 연동시키기 위해 수정했다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
...
<form method= "post" action="<%=request.getContextPath()%>/user_servlet">
<div class="form-floating mb-3">
<input class="form-control" id="inputEmail" type="email" placeholder="name@example.com" />
<label for="inputEmail">Email address</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="inputPassword" type="password" placeholder="Password" />
<label for="inputPassword">Password</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />
<label class="form-check-label" for="inputRememberPassword">Remember Password</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="small" href="password.html">Forgot Password?</a>
<a class="btn btn-primary" href="index.html">Login</a>
</div>
</form>
<%@ page ...%>
부분의 코드가 존재하면 jsp
파일로 인식된다.
<%=request.getContetPath()%>
부분은 WAS 의 ServletContext
를 참조한다.
- 외장 WAS를 사용하는 현재 상황에서는, 하나의 Tomcat WAS 서버에서 여러 개의 어플리케이션을 띄울 수 있다.
ServletContext
를 사용한다.<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
...
<form method="post" action="<%=request.getContextPath()%>/user_servlet">
<div class="row mb-3">
<div class="col-md-6">
<div class="form-floating mb-3 mb-md-0">
<input name="user_id" class="form-control" id="inputFirstName" type="text" placeholder="Enter your first name" />
<label for="inputFirstName">ID</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input name="lname" class="form-control" id="inputLastName" type="text" placeholder="Enter your last name" />
<label for="inputLastName">Name</label>
</div>
</div>
</div>
<div class="form-floating mb-3">
<input name="user_name" class="form-control" id="inputEmail" type="email" placeholder="name@example.com" />
<label for="inputEmail">Email address</label>
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="form-floating mb-3 mb-md-0">
<input name="passwd" class="form-control" id="inputPassword" type="password" placeholder="Create a password" />
<label for="inputPassword">Password</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-3 mb-md-0">
<input name="passwd2" class="form-control" id="inputPasswordConfirm" type="password" placeholder="Confirm password" />
<label for="inputPasswordConfirm">Confirm Password</label>
</div>
</div>
</div>
<div class="mt-4 mb-0">
<div class="d-grid"><a class="btn btn-primary btn-block" href="login.jsp">Create Account</a></div>
</div>
</form>
<% out.println("aaa"); %>
는
<%= aaa %>
와 같다.
**JSP**
에서 **out**
을 워낙 많이 쓰다보니, 이에 대한 축약 버전 ( **<%= %>**
)을 만들어주었다.
input type="hidden"
의 pagecode
값을 넘겨주어 서블릿단에서 해당 코드값을 통핸 조건 분기를 통해 요청이 온 페이지를 구분할 수 있습니다.<form method="post" action="<%=request.getContextPath()%>/user_servlet">
<input type="hidden" name="pagecode" value="P001">
<div class="row mb-3">
<div class="col-md-6">
<div class="form-floating mb-3 mb-md-0">
<input name="user_id" class="form-control" id="inputFirstName" type="text" placeholder="Enter your ID" />
<label for="inputFirstName">ID</label>
</div>
...
pagecode
값을 P001
로 주었습니다. <form method= "post" action="<%=request.getContextPath()%>/user_servlet">
<input type="hidden" name="pagecode" value="P002">
<div class="form-floating mb-3">
<input name="userId" class="form-control" id="inputFirstName" type="text" placeholder="Enter your ID" />
<label for="inputFirstName">ID</label>
</div>
...
pagecode
값을 P002
로 주었습니다.register.jsp
, login.jsp
는 위와 같이 작성하고,UserServlet
, UserDAO
, UserVO
를 만든다.import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/user_servlet")
public class UserServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO : login, register
String pagecode = request.getParameter("pagecode");
UserDAO dao = new UserDAO();
// --------------------register----------------//
if(pagecode.equals("P001")) {
String userId = request.getParameter("user_id");
String passwd = request.getParameter("passwd");
UserVO uvo = new UserVO();
uvo.setUserId(userId);
uvo.setPasswd(passwd);
int insertRows = dao.userInsert(uvo);
if(insertRows == 1) {
response.sendRedirect("index.jsp");
} else {
response.sendRedirect("500.html");
}
// --------------------login-------------------//
} else if(pagecode.equals("P002")) {
String userId = request.getParameter("userId");
String passwd = request.getParameter("passwd");
boolean loginCheck = dao.userLogin(userId, passwd);
if(loginCheck) {
response.sendRedirect("index.jsp");
} else {
response.sendRedirect("500.html");
}
// --------------------other-------------------//
} else {
response.sendRedirect("500.html");
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
super.destroy();
}
}
doPost
를 오버라이딩함으로써, POST
요청으로 오는 두 요청( 회원가입 : register / 로그인 : login )을 처리했습니다.pagecode
에 대한 조건 분기를 했습니다.prototype
형식으로 간단하게 로직의 흐름을 작성했습니다.서블릿에서 받은 데이터들을 DB에 담기위해 캡슐화한 UserVO
를 정의합니다.
Service Layer
를 두지 않았고, 서블릿 기술 자체에 의존하여 개발하기에 굳이 DTO를 만들어주지 않았습니다.
- DTO
는 Spring MVC Framework
에서 컨트롤러로 데이터를 넘겨줄 때,
바인딩 받고싶은 데이터만 묶어 이를 특정해주는 용도로 사용하거나, Controller Layer
에서 Service Layer
로 계층 간의 데이터를 이동시킬 때 캡슐화하는 용도로 사용해야한다 생각했습니다.
// Entity
public class UserVO {
private String userId;
private String uname;
private String email;
private String passwd;
private String regDate;
public UserVO() {
}
public UserVO(String userId, String uname, String email, String passwd, String regDate) {
this.userId = userId;
this.uname = uname;
this.email = email;
this.passwd = passwd;
this.regDate = regDate;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getRegDate() {
return regDate;
}
public void setRegDate(String regDate) {
this.regDate = regDate;
}
@Override
public String toString() {
return "UserVO [userId=" + userId + ", uname=" + uname + ", email=" + email + ", passwd=" + passwd
+ ", regDate=" + regDate + "]";
}
}
UserDAO를 통해 데이터를 INSERT 하기 위해 Oracle에 USER Table을 하나 생성해주었습니다.
이를 위한 DDL문이 필요합니다.
-- 제가 예전에 실습했던 USERS Table이 이미 존재했었습니다.
-- 이 데이터를 복제본을 뜨기 위해 다음과 같은 DDL로 새로운 테이블에 이동시켰습니ㅏㄷ.
CREAT TABLE USERS_COPY AS (
SELECT * FROM USERS
);
-- SQLDeveloper 툴에서 USERS 테이블의 테이블 명을 바꾸었습니다.
-- USERS 테이블을 생성했습니다.
CREATE TABLE USERS(
SEQ NUMBER PRIMARY KEY,
USERID VARCHAR2(10),
UNAME VARCHAR2(10),
EMAIL VARCHAR2(20),
PASSWD VARCHAR2(10),
REGDATE DATE DEFAULT SYSDATE
);
USERS
테이블에서 사용할 Sequence
를 생성했습니다.CREATE SEQUENCE USERS_SEQ START WITH 1 INCREMENT BY 1 NOCACHE;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.kosta.sample.common.MyOracleConnection;
// Data Access Object ( DML )
public class UserDAO {
private static final MyOracleConnection myOracleConn = new MyOracleConnection();
public int userInsert(UserVO uvo) {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "INSERT INTO USERSS(ID, PASSWORD) VALUES(?,?);";
int row = 0;
try {
System.out.println(uvo.toString());
conn = myOracleConn.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, uvo.getUserId());
pstmt.setString(2, uvo.getPasswd());
row = pstmt.executeUpdate();
} catch(SQLException e){
e.printStackTrace();
} finally {
try {
if(pstmt != null) {
pstmt.close();
}
if(conn != null) {
conn.close();
}
} catch(SQLException e) {
e.printStackTrace();
}
}
return row;
}
public boolean userLogin(String userId, String passwd) {
boolean userCheck = true; // rs.next();
return userCheck;
}
}
DataSource
의 DBCP
를 활용하여 커넥션을 가져오는 MyOracleConnection
클래스를 재사용했습니다. ( 이전 주차에 구현했었습니다. )public interface MyConnection {
Connection getConnection();
void close(Connection conn);
}
///////////////////////////////////////////////////////////////////////////////////
//
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import oracle.jdbc.pool.OracleConnectionPoolDataSource;
public class MyOracleConnection implements MyConnection {
private static DataSource ds;
private static final String DB_URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
private static final String DB_USER = "it";
private static final String DB_PASSWORD = "0000";
static {
try {
OracleConnectionPoolDataSource ocds = new OracleConnectionPoolDataSource();
ocds.setURL(DB_URL);
ocds.setUser(DB_USER);
ocds.setPassword(DB_PASSWORD);
ds = ocds;
} catch(SQLException e) {
System.out.println("DataSource 연결 실패");
}
}
@Override
public Connection getConnection() {
Connection conn = null;
try {
conn = ds.getConnection();
} catch(SQLException e) {
System.out.println("getConnection Exception");
e.printStackTrace();
}
return conn;
}
@Override
public void close(Connection conn) {
if(conn != null) {
try {
conn.close();
} catch(SQLException e) {
System.out.println("conn.close() Exception");
e.printStackTrace();
}
}
}
}
private static final MyOracleConnection myOracleConn = new MyOracleConnection();
public int userInsert(UserVO uvo) {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "INSERT INTO USERS VALUES(USERS_SEQ.NEXTVAL,?,?,?,?,SYSDATE)";
int row = 0;
try {
System.out.println(uvo.toString());
conn = myOracleConn.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, uvo.getUserId());
pstmt.setString(2, uvo.getUname());
pstmt.setString(3, uvo.getEmail());
pstmt.setString(4, uvo.getPasswd());
row = pstmt.executeUpdate();
} catch(SQLException e){
e.printStackTrace();
} finally {
try {
if(pstmt != null) {
pstmt.close();
}
if(conn != null) {
conn.close();
}
} catch(SQLException e) {
e.printStackTrace();
}
}
return row;
}
**NEXTVAL**
을 호출해주었습니다.**USERS**
테이블 DDL문에서**REGDATE**
컬럼의 기본값은 **SYSDATE**
로 설정되어 있지만,**REGDATE**
값에 **SYSDATE**
를 직접 넣어주었습니다.public boolean userLogin(String userId, String passwd) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "SELECT USERID, UNAME, EMAIL FROM USERS WHERE USERID = ? AND PASSWD = ?";
boolean loginSucceed = false;
try {
conn = myOracleConn.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userId);
pstmt.setString(2, passwd);
rs = pstmt.executeQuery();
if(rs.next()) {
loginSucceed = true;
}
} catch(SQLException e){
e.printStackTrace();
} finally {
try {
if(rs != null) {
rs.close();
}
if(pstmt != null) {
pstmt.close();
}
if(conn != null) {
conn.close();
}
} catch(SQLException e) {
e.printStackTrace();
}
}
return loginSucceed;
}
**return**
했지만,**Session**
화 하여 관리할 수도 있습니다.**Redis**
를 사용할 수 있습니다.JSP
에서 Servlet
들어오는 방법<form>
태그를 통해 들어올 수 있다. + <input type="submit">
<a href>
태그를 통해 들어올 수 있다.<script>
태그 안의 javascript
를 통해 API 호출 방식
으로 Servlet을 호출할 수 있다.form
tag + 기억해둘 속성들method
= "get/post"
- 폼 데이터(form data)가 서버로 제출될 때 사용되는 HTTP Method 명시
action
= "/servlet_url"
- 폼 데이터를 서버로 보낼 때, 해당 데이터가 도착할 URL을 명시함.
enctype
속성은 폼 데이터(form data)가 서버로 제출(submit
)될 때, 해당 데이터가 인코딩되는 방법을 명시함.
- 기본 속성 값 : application/x-www-form-urlencoded
모든 문자들이 서버로 보내기 전에 인코딩됨을 명시한다.
파일, 이미지 포함시 : multiplart/form-data
text/plain
: 공백 문자(space)는 "+" 기호로 변환, 나머지 문자는 모두 인코딩되지 않음.
a
taghref
= "/servlet_url"
이 태그는 무조건 GET
메서드로 서버에 접근한 것이다.
DB의 컬럼 데이터를 처음부터 너무 크게 잡으면 안되겠습니다.
작은 사이즈에서 큰 사이즈로 제약을 바꾸는 것은 가능하지만, 큰 사이즈의 제약에서 작은 사이즈의 제약으로 변경하는 것은 어렵기 때문입니다.
기존의 데이터가 문제가 될 수 있기 때문입니다.