step1. 클라가 XXX.do 요청
- 특별한 확장자를 통해 웹프로그램 요청
- 어떤 do를 요청하던 간에 단 하난의 컨트롤러를 요청할 수 있도록 하는 것 ex) hrd-net,ok캐쉬백 : 확장자 xxx.do 요청함
step2. [컨트롤러]는 요청에 대한 처리함 - [Model] : Serive + DAO
- 기존에는 DAO를 이용했지만, DAO를 직접 쓰지 않고, service를 이용할 것임
- 물론 데이타 베이스는 DAO가 하지만 그 중간에 Service 사용할 것
- 왜? DAO의 메소드 하나는 sql 명령을 딱 하나만 전달 가능하지만, DAO의 메소드를 여러번 호출하여 원하는 기능을 구현해야 원하는 결과를 얻고 싶기 때문
- 그럴 때, Service클래스로 DAO를 여러번 호출해 모듈화시키기
- Service = sql 여러개를 하나로 제공할 수 있도록 만듦
- 대형프로그램 만들 시, sql 명령을 여러번 사용해야하는 데 DAO 여러번 호출은 권장하지 않으므로 중간에 service 클래스를 만들어 모듈화시킬 것임
step3. 포워드이동 or 리다이렉트이동
- 일반적으로 요청에 대한 처리가 끝나면 포워드 이동할 것임
- 때에 따라 리다이렉트 이동도 사용함
step4. 만드는 순서
- DAO -> Service -> Controller -> view
step5. 클래스마다 예외구문 쓰지 않고 다 떠넘길 것 : try ~ catch
- DAO클래스에서 try ~ catch 안쓰고 떠넘김,
- Service클래스에서 try ~ catch 안쓰고 떠넘김,
- 모델(Model) 클래스에서 한꺼번에 try ~ catch 적용 (예외처리)
- JSP에 try~catch는 속도가 느려져 안쓰지만, 클래스는 써주긴함
- 하지만, 각 클래스마다 예외처리를 하는 것은 속도도 느려지고 비효율적이므로 떠넘기고 마지막에 한꺼번에 잡아줄 것임!
01. context.xml & ojdc11.jar & JdbcDAO.java 만들기
META-INF > context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/oracle" auth="Container" type="javax.sql.DataSource"
factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe" username="scott" password="tiger"
initialSize="10" maxIdle="10" maxTotal="15"/>
</Context>
ojdc11.jar
JdbcDAO.java
package xyz.itwill.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
//JDBC 기능을 구현하기 위한 DAO 클래스가 상속받기 위한 부모클래스
public abstract class JdbcDAO {
private static DataSource dataSource;
static {
try {
dataSource=(DataSource)new InitialContext().lookup("java:comp/env/jdbc/oracle");
} catch (NamingException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public void close(Connection con) {
try {
if(con!=null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close(Connection con, PreparedStatement pstmt) {
try {
if(pstmt!=null) pstmt.close();
if(con!=null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close(Connection con, PreparedStatement pstmt, ResultSet rs) {
try {
if(rs!=null) rs.close();
if(pstmt!=null) pstmt.close();
if(con!=null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
02. 테이블 & DTO
1) USERINFO 테이블
create table userinfo(userid varchar2(100) primary key
,password varchar2(100),name varchar2(200),email varchar2(300),status number(1));
desc userinfo;
2) UserinfoDTO.java
package xyz.itwill.dto;
/*
이름 널? 유형
-------- -------- -------------
USERID NOT NULL VARCHAR2(100)
PASSWORD VARCHAR2(100)
NAME VARCHAR2(200)
EMAIL VARCHAR2(300)
STATUS NUMBER(1)
*/
public class UserinfoDTO {
private String userid;
private String password;
private String name;
private String email;
private int status;
public UserinfoDTO() {
// TODO Auto-generated constructor stub
}
public String getUserid() {return userid;}
public void setUserid(String userid) {this.userid = userid;}
public String getPassword() {return password;}
public void setPassword(String password) {this.password = password;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public int getStatus() {return status;}
public void setStatus(int status) {this.status = status;}
}
03. DAO
1) UserinfoModelOneDAO.java
package xyz.itwill.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import xyz.itwill.dto.UserinfoDTO;
public class UserinfoModelOneDAO extends JdbcDAO {
private static UserinfoModelOneDAO _dao;
private UserinfoModelOneDAO() {
// TODO Auto-generated constructor stub
}
static {
_dao=new UserinfoModelOneDAO();
}
public static UserinfoModelOneDAO getDAO() {
return _dao;
}
insertUserinfo(userinfo) : 회원삽입
public int insertUserinfo(UserinfoDTO userinfo) {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="insert into userinfo values(?,?,?,?,?)";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userinfo.getUserid());
pstmt.setString(2, userinfo.getPassword());
pstmt.setString(3, userinfo.getName());
pstmt.setString(4, userinfo.getEmail());
pstmt.setInt(5, userinfo.getStatus());
rows=pstmt.executeUpdate();
} catch (SQLException e) {
System.out.println("[에러]insertUserinfo() 메소드의 SQL 오류 = "+e.getMessage());
} finally {
close(con, pstmt);
}
return rows;
}
updateUserinfo(userinfo) : 회원수정
public int updateUserinfo(UserinfoDTO userinfo) {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="update userinfo set password=?,name=?,email=?,status=? where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userinfo.getPassword());
pstmt.setString(2, userinfo.getName());
pstmt.setString(3, userinfo.getEmail());
pstmt.setInt(4, userinfo.getStatus());
pstmt.setString(5, userinfo.getUserid());
rows=pstmt.executeUpdate();
} catch (SQLException e) {
System.out.println("[에러]updateUserinfo() 메소드의 SQL 오류 = "+e.getMessage());
} finally {
close(con, pstmt);
}
return rows;
}
deleteUserinfo(userid) : 회원삭제
public int deleteUserinfo(String userid) {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="delete from userinfo where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userid);
rows=pstmt.executeUpdate();
} catch (SQLException e) {
System.out.println("[에러]deleteUserinfo() 메소드의 SQL 오류 = "+e.getMessage());
} finally {
close(con, pstmt);
}
return rows;
}
selectUserinfo(userid) : 회원출력
public UserinfoDTO selectUserinfo(String userid) {
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
UserinfoDTO userinfo=null;
try {
con=getConnection();
String sql="select * from userinfo where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userid);
rs=pstmt.executeQuery();
if(rs.next()) {
userinfo=new UserinfoDTO();
userinfo.setUserid(rs.getString("userid"));
userinfo.setPassword(rs.getString("password"));
userinfo.setName(rs.getString("name"));
userinfo.setEmail(rs.getString("email"));
userinfo.setStatus(rs.getInt("status"));
}
} catch (SQLException e) {
System.out.println("[에러]selectUserinfo() 메소드의 SQL 오류 = "+e.getMessage());
} finally {
close(con, pstmt, rs);
}
return userinfo;
}
selectUserinfoList() : 회원리스트출력
public List<UserinfoDTO> selectUserinfoList() {
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
List<UserinfoDTO> userinfoList=new ArrayList<>();
try {
con=getConnection();
String sql="select * from userinfo order by userid";
pstmt=con.prepareStatement(sql);
rs=pstmt.executeQuery();
while(rs.next()) {
UserinfoDTO userinfo=new UserinfoDTO();
userinfo.setUserid(rs.getString("userid"));
userinfo.setPassword(rs.getString("password"));
userinfo.setName(rs.getString("name"));
userinfo.setEmail(rs.getString("email"));
userinfo.setStatus(rs.getInt("status"));
userinfoList.add(userinfo);
}
} catch (SQLException e) {
System.out.println("[에러]selectUserinfoList() 메소드의 SQL 오류 = "+e.getMessage());
} finally {
close(con, pstmt, rs);
}
return userinfoList;
}
2) UserinfoModelTwoDAO.java
throw ~ catch()를 DAO클래스에서 하지말고, 떠넘기자 (예외전달)
package xyz.itwill.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import xyz.itwill.dto.UserinfoDTO;
public class UserinfoModelTwoDAO extends JdbcDAO {
private static UserinfoModelTwoDAO _dao;
private UserinfoModelTwoDAO() {
// TODO Auto-generated constructor stub
}
static {
_dao=new UserinfoModelTwoDAO();
}
public static UserinfoModelTwoDAO getDAO() {
return _dao;
}
insertUserinfo(userinfo) : 회원삽입
public int insertUserinfo(UserinfoDTO userinfo) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="insert into userinfo values(?,?,?,?,?)";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userinfo.getUserid());
pstmt.setString(2, userinfo.getPassword());
pstmt.setString(3, userinfo.getName());
pstmt.setString(4, userinfo.getEmail());
pstmt.setInt(5, userinfo.getStatus());
rows=pstmt.executeUpdate();
} finally {
close(con, pstmt);
}
return rows;
}
updateUserinfo(userinfo) : 회원수정
public int updateUserinfo(UserinfoDTO userinfo) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="update userinfo set password=?,name=?,email=?,status=? where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userinfo.getPassword());
pstmt.setString(2, userinfo.getName());
pstmt.setString(3, userinfo.getEmail());
pstmt.setInt(4, userinfo.getStatus());
pstmt.setString(5, userinfo.getUserid());
rows=pstmt.executeUpdate();
} finally {
close(con, pstmt);
}
return rows;
}
deleteUserinfo(userid) : 회원삭제
public int deleteUserinfo(String userid) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
int rows=0;
try {
con=getConnection();
String sql="delete from userinfo where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userid);
rows=pstmt.executeUpdate();
} finally {
close(con, pstmt);
}
return rows;
}
selectUserinfo(userid) : 회원출력
public UserinfoDTO selectUserinfo(String userid) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
UserinfoDTO userinfo=null;
try {
con=getConnection();
String sql="select * from userinfo where userid=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, userid);
rs=pstmt.executeQuery();
if(rs.next()) {
userinfo=new UserinfoDTO();
userinfo.setUserid(rs.getString("userid"));
userinfo.setPassword(rs.getString("password"));
userinfo.setName(rs.getString("name"));
userinfo.setEmail(rs.getString("email"));
userinfo.setStatus(rs.getInt("status"));
}
} finally {
close(con, pstmt, rs);
}
return userinfo;
}
selectUserinfoList() : 회원리스트출력
public List<UserinfoDTO> selectUserinfoList() throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
List<UserinfoDTO> userinfoList=new ArrayList<>();
try {
con=getConnection();
String sql="select * from userinfo order by userid";
pstmt=con.prepareStatement(sql);
rs=pstmt.executeQuery();
while(rs.next()) {
UserinfoDTO userinfo=new UserinfoDTO();
userinfo.setUserid(rs.getString("userid"));
userinfo.setPassword(rs.getString("password"));
userinfo.setName(rs.getString("name"));
userinfo.setEmail(rs.getString("email"));
userinfo.setStatus(rs.getInt("status"));
userinfoList.add(userinfo);
}
} finally {
close(con, pstmt, rs);
}
return userinfoList;
}
04. Service
1) UserinfoService.java
throw ~ catch()를 Service클래스에서 하지말고, 떠넘기자 (예외전달)
package xyz.itwill.service;
import java.sql.SQLException;
import java.util.List;
import xyz.itwill.dao.UserinfoModelTwoDAO;
import xyz.itwill.dto.UserinfoDTO;
import xyz.itwill.exception.AuthFailException;
import xyz.itwill.exception.ExistUserinfoException;
import xyz.itwill.exception.UserinfoNotfoundException;
//Service 클래스 : 모델(Model) 클래스의 요청 처리 메소드에게 데이타베이스 관련 처리 기능을 제공하기 위한 클래스 - 단위 프로그램(모듈 프로그램) : 컴포넌트(Component)
// => 다수의 DAO 클래스의 메소드를 호출하여 필요한 기능을 제공하기 위한 메소드 작성 - 모듈화
// => 데이타베이스 관련 기능 구현시 발생되는 모든 문제에 대한 인위적 예외 발생
// => 문제를 해결하는 것은 아니고, 인위적 예외만 발생시키는 것
public class UserinfoService {
private static UserinfoService _service;
private UserinfoService() {
// TODO Auto-generated constructor stub
}
static {
_service = new UserinfoService();
}
public static UserinfoService getServie() {
return _service;
}
addUserinfo(userinfo) : 회원을 추가하다
//회원정보를 전달받아 USERINFO 테이블에 삽입하는 메소드
//=> 전달받은 회원정보의 아이디가 USERINFO 테이블에 저장된 기존 회원정보의 아이디와
//중복될 경우 인위적 예외 발생
public void addUserinfo(UserinfoDTO userinfo) throws SQLException, ExistUserinfoException {
//검색된 아이디가 있는 경우(아이디 중복) - insertUserinfo()실행안됨
if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userinfo.getUserid())!=null) {
//예외객체를 일부러 만들어 인위적 예외 발생
//아이디가 중복된 예외를 기존에 있는 예외클래스로 만들지 말고, (너무 광할한 범위임)
//아이디중복예외클래스를 따로 만들어 예외 발생 권장
//throw new Exception();
//예외가 발생될 경우 하단에 존재한 명령 미실행
throw new ExistUserinfoException("이미 사용중인 아이디를 입력하였습니다.");
}
UserinfoModelTwoDAO.getDAO().insertUserinfo(userinfo);
}
modifyUserinfo(userinfo) : 회원을 수정하다
//회원정보를 전달받아 USERINFO 테이블에 저장된 회원정보를 변경하는 메소드
//=> 전달받은 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
public void modifyUserinfo(UserinfoDTO userinfo) throws SQLException, UserinfoNotfoundException {
//검색된 아이디에 해당하는 회원정보가 없는 경우
if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userinfo.getUserid())==null) {
throw new UserinfoNotfoundException("회원정보가 존재하지 않습니다.");
}
UserinfoModelTwoDAO.getDAO().updateUserinfo(userinfo);
}
removeUserinfo(userid) : 회원을 삭제하다
//아이디를 전달받아 USERINFO 테이블에 저장된 해당 아이디의 회원정보를 삭제하는 메소드
//=> 전달받은 아이디의 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
public void removeUserinfo(String userid) throws SQLException, UserinfoNotfoundException {
//검색된 아이디에 해당하는 회원정보가 없는 경우
if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userid)==null) {
throw new UserinfoNotfoundException("회원정보가 존재하지 않습니다.");
}
UserinfoModelTwoDAO.getDAO().deleteUserinfo(userid);
}
getUserinfo(userid) : 회원을 출력하다
//아이디를 전달받아 USERINFO 테이블에 저장된 해당 아이디의 회원정보를 검색하여 반환하는 메소드
//=> 전달받은 아이디의 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
public UserinfoDTO getUserinfo(String userid) throws SQLException, UserinfoNotfoundException {
if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userid)==null) {
throw new UserinfoNotfoundException("회원정보가 존재하지 않습니다.");
}
return UserinfoModelTwoDAO.getDAO().selectUserinfo(userid);
}
getUserinfoList() : 회원리스트를 출력하다
//USERINFO 테이블에 저장된 모든 회원정보를 검색하여 반환하는 메소드
//만약 서칭기능을 추가한다면? EmptyException 만들면 좋을 것
public List<UserinfoDTO> getUserinfoList() throws SQLException{
return UserinfoModelTwoDAO.getDAO().selectUserinfoList();
}
auth(userid,password) : 인증을 처리하다
//아이디와 비밀번호를 전달받아 인증 처리하기 위한 메소드
// => 인증실패시 인위적 예외 발생 - 예외가 발생되지 않은 경우 인증 성공
public void auth(String userid, String password) throws SQLException, AuthFailException {
UserinfoDTO userinfo = UserinfoModelTwoDAO.getDAO().selectUserinfo(userid);
if(userinfo==null) {//아이디 인증 실패
throw new AuthFailException("입력된 아이디가 존재하지 않습니다.");
}
if(!userinfo.getPassword().equals(password)) {//비밀번호 인증 실패
throw new AuthFailException("입력된 아이디가 잘못 되었거나 비밀번호가 맞지 않습니다.");
}
}
05. web.xml파일(매핑처리) | properties파일
WEB-INF > web.xml - 매핑처리
- web.xml로 매핑처리하면 좋은 점: 서블릿클래스나 필터클래스를 직접 변경하지 않고, web.xml문서만 변경하면 되므로 서버 restart하지 않아도 됨!
- 유지보수의 효율성 증가
- 확장성 증가
<?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>mvc</display-name>
<!-- filter : 필터 클래스를 필터로 등록하기 위한 엘리먼트 -->
<!-- => servlet 엘리먼트 선언 전에 작성하는 것을 권장 - 왜? filter클래스가 먼저 만들어저야하므로 -->
<filter>
<filter-name>encoding filter</filter-name>
<filter-class>xyz.itwill.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<!-- filter-mapping : 필터가 실행되기 위한 요청 웹프로그램의 URL 패턴을 등록하기 위한 엘리먼트 -->
<filter-mapping>
<filter-name>encoding filter</filter-name>
<!-- 클라이언트의 "모든" 웹프로그램(서블릿이든, JSP든) 요청에 대한 필터가 실행되도록 URL 패턴 등록 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- servlet : 서블릿 클래스를 웹프로그램(서블릿)으로 등록하기 위한 엘리먼트 -->
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>xyz.itwill.mvc.ControllerServlet</servlet-class>
<!-- init-param : 서블릿 클래스에 필요한 값을 제공하기 위한 엘리먼트 -->
<init-param>
<param-name>configFile</param-name>
<param-value>/WEB-INF/model.properties</param-value>
</init-param>
<!-- load-on-startup : WAS 실행시 서블릿 클래스를 서블릿 객체로 생성하기 위한 엘리먼트 -->
<!-- => 클라이언트의 요청 없이 WAS 실행시 서블릿 객체를 미리 생성 - init() 메소드가 자동 호출되어 초기화 작업 -->
<!-- => 엘리먼트값은 0 이상의 정수값으로 설정하며 정수값이 작을수록 먼저 서블릿 객체로 생성 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- servlet-mapping : 웹프로그램(서블릿)에 URL 패턴을 등록하기 위한 엘리먼트 -->
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
</welcome-file-list>
</web-app>
WEB-INF > model.properties
- 서블릿은 접근할 수 있되, 클라이언트는 접근 불가능하게 하도록 WEB-INF폴더에 만듦
#Command(Key - String) = ModelClass(Value - String)
/loginForm.do = xyz.itwill.mvc.LoginFormModel
/login.do = xyz.itwill.mvc.LoginModel
/logout.do = xyz.itwill.mvc.LogoutModel
/writeForm.do = xyz.itwill.mvc.WriteFormModel
/write.do = xyz.itwill.mvc.WriteModel
/list.do = xyz.itwill.mvc.ListModel
/view.do = xyz.itwill.mvc.ViewModel
/modifyForm.do = xyz.itwill.mvc.ModifyFormModel
/modify.do = xyz.itwill.mvc.ModifyModel
/remove.do = xyz.itwill.mvc.RemoveModel
/error.do = xyz.itwill.mvc.ErrorModel
06. ControllerServlet | Action | ActionForward
- 회원관리 프로그램에서 클라이언트 요청에 대한 모델 객체가 매핑되도록 설계
- 로그인정보 입력페이지(환영메세지 출력페이지) : /loginForm.do - LoginFormModel Class
- 로그인 처리페이지 :/login.do - LoginModel Class
- 로그아웃 처리페이지 : /logout.do - LogoutModel Class
- 회원정보 입력페이지 : /writeForm.do - WriteFormModel Class
- 회원정보 삽입페이지 : /write.do - WriteModel Class
- 회원목록 출력페이지 : /list.do - ListModel Class
- 회원정보 출력페이지 : /view.do - ViewModel Class
- 변경회원정보 입력페이지 : /modifyForm.do - ModifyFormModel Class
- 회원정보 변경페이지 : /modify.do - ModifyModel Class
- 회원정보 삭제페이지 : /remove.do - RemoveModel Class
- 에러메세지 출력페이지 : /error.do - ErrorModel Class
ControllerServlet.java
package xyz.itwill.mvc;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//컨트롤러(Controller - Servlet) :
//=> 클라이언트의 모든 요청을 모델(Model - Class)의 요청 처리 메소드를
//호출하여 요청을 처리하고 처리결과를 뷰(View - JSP)로 전달하여 응답되도록 제어하는 웹프로그램
//1.
//클라이언트의 모든 요청을 받을 수 있도록 서블릿을 설정하여 단일 진입점의 기능 구현
// => Front Controller Pattern
//@WebServlet("URL") : 서블릿 클래스를 웹프로그램(서블릿)으로 등록하고 요청 URL 주소를 매핑하는 어노테이션
// => 매핑 설정될 URL 주소에 패턴문자(* : 전체 또는 ? : 문자 하나)를 사용하여 URL 패턴 등록 가능
// => @WebServlet("*.do") : 클라이언트가 [XXX.do] 형식의 URL 주소로 요청한 경우 서블릿 실행
// => @WebServlet 어노테이션 대신 [web.xml] 파일에서 서블릿 클래스를 웹프로그램(서블릿)으로 등록하고 URL 주소 매핑 처리
public class ControllerServlet extends HttpServlet {
필드
private static final long serialVersionUID = 1L;
//요청정보(Key - String)와 모델 객체(Value - Action)를 하나의 요소(Entry)로 묶어 여러개 저장할 Map 객체의 필드
// => Map 객체를 이용하여 요청정보(Key)로 모델 객체(Value)를 빠르게 제공받기 위해 사용
private Map<String, Action> actionMap;
init(config)
//클라이언트 최초 요청에 의해 서블릿 객체가 생성된 후 가장 먼저 자동으로 "1번만 호출"되는 메소드
//=> 특징) 서버가 종료되기 전까지 소멸되지 않음
//=> 특징) 어떤 클라이언트가 요청해도 딱 1번만 메소드 실행함
//=> 서블릿 객체의 초기화 작업을 위해 오버라이드 선언
@Override
public void init(ServletConfig config) throws ServletException {
//System.out.println("ControllerServlet 클래스의 init() 메소드 호출");
actionMap=new HashMap<String, Action>();
//3.
/*
//방법2. 비권장
//=> 단점) Model객체를 추가할 경우 Controller서블릿 계속 변경해야함
//Map 객체에 엔트리(Entry - Key : 요청정보, Value : 모델 객체) 추가
actionMap.put("/loginForm.do", new LoginFormModel());
actionMap.put("/login.do", new LoginModel());
actionMap.put("/logout.do", new LogoutModel());
actionMap.put("/writeForm.do", new WriteFormModel());
actionMap.put("/write.do", new WriteModel());
actionMap.put("/list.do", new ListModel());
actionMap.put("/view.do", new ViewModel());
actionMap.put("/modifyForm.do", new ModifyFormModel());
actionMap.put("/modify.do", new ModifyModel());
actionMap.put("/remove.do", new RemoveModel());
actionMap.put("/error.do", new ErrorModel());
*/
//3.
//방법3. 권장 - Properties 파일이용
//=> 장점) Model객체를 추가해도 Controller서블릿 변경 필요 없음
//=> 장점) 어떤 프로그램을 만들든 동일한 Controller서블릿 사용가능
//Properties 파일에 "요청정보"와 "모델 클래스"를 저장하고 파일을 읽어 Map 객체의 엔트리 추가
//=> 유지보수의 효율성 증가 :
//=> 컨트롤러를 변경하지 않고 Properties 파일만 변경하여
//요청정보와 모델 객체 변경 가능
//Properties 파일(XXX.properties) :
//=> 프로그램 실행에 필요한 값을 제공하기 위한 텍스트 파일
//Properties 파일의 정보를 저장하기 위한 Properties 객체 생성
Properties properties=new Properties();
//ServletConfig.getInitParameter(String name) :
//=> [web.xml] 파일에서 init-param 엘리먼트로 제공되는 값을 읽어와 반환하는 메소드
String configFile=config.getInitParameter("configFile");
//System.out.println("configFile = "+configFile);
//Properties 파일의 시스템 경로를 반환받아 저장
//파일명도 변경될 수 있으므로
//String configFilePath=config.getServletContext().getRealPath("/WEB-INF/model.propeties");
//[web.xml]파일의 <init-param> 이용해 아래처럼 변경
String configFilePath=config.getServletContext().getRealPath(configFile);
//System.out.println("configFilePath = "+configFilePath);
try {
//Properties 파일에 대한 입력스트림을 생성하여 저장
FileInputStream in=new FileInputStream(configFilePath);
//입력스트림을 사용하여 Properties 파일의 내용을 읽어 Properties 객체에 엔트리로 저장
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
//Properties 객체의 모든 키(Key)를 반환받아 반복 처리
//Properties.keySet() :
//=> Properties 객체에 저장된 모든 엔트리의 키(Key)를 Set 객체로 반환하는 메소드
for(Object key:properties.keySet()) {//Set 객체로부터 요소를 하나씩 제공받아 반복 처리
//Properties 객체에 저장된 엔트리의 키(Key) - 요청정보
String actionCommand=(String)key;
//Properties 객체에 저장된 엔트리의 값(Value) - 모델 클래스
String actionClass=(String)properties.get(key);
//문자열로된 클래스 경로일 뿐이므로 try-catch구문에 아래처럼 작성필요..
//Action actionObject=(Action)Class.forName(actionClass).getDeclaredConstructor().newInstance();
try {
//모델 클래스를 이용하여 모델 객체 생성 - 리플렉션 기능 사용
//ex) 스프링은 리플렉션 기능을 이용해 객체를 알아서 만들어줌
//일반객체: 프로그램(클래스)을 읽어들여 메모리에 저장할 때 만들어짐(프로그램 실행 시가 아님)
//리플랙션: CPU가 명령을 실행할 때 만들어짐
//미리 객체는 안만들어지나, 접근 지정자에 상관없이 객체의 필드와 메소드에 접근 가능
//new연산자를 통해 새로운 객체를 만드는 것이 아닌, 메모리에 직접 객체를 만드는 것
//리플렉션(Reflection) : 프로그램 명령 실행시 클래스(Clazz)를 읽어 객체를 생성하고
//=> 객체의 필드 또는 메소드에 접근하도록 제공하는 기능
//Class.forName(String className) : 문자열로 표현된 클래스를 전달받아 클래스를 읽어 메모리에 저장하고 Class 객체(Clazz)를 반환하는 메소드 - ClassNotFoundException 발생
//Class.getDeclaredConstructor() : 메모리에 저장된 클래스(Class 객체)의 생성자가 저장된 Constructor 객체를 반환하는 메소드
//Constructor.newInstance() : Constructor 객체에 저장된 생성자를 이용하여 Object 타입의 객체를 생성하여 반환하는 메소드
Action actionObject=(Action)Class.forName(actionClass).getDeclaredConstructor().newInstance();
//Map 객체에 엔트리(Entry - Key : 요청정보, Value : 모델 객체) 추가
actionMap.put(actionCommand, actionObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
service(request, response)
//클라이언트의 요청을 처리하기 위한 자동 호출되는 메소드
// => 클라이언트가 서블릿(웹프로그램)을 요청할 때마다 서블릿 객체를 이용하여 반복적으로 호출
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ControllerServlet 클래스의 service() 메소드 호출");
//2.
//클라이언트 요청 분석 : 요청 URL 주소 이용 - <http://localhost:8000/mvc/XXX.do>
//HttpServletRequest.getRequestURI() : 요청 URL 주소에서 URI 주소를 반환하는 메소드
String requestURI=request.getRequestURI();
//System.out.println("requestURI = "+requestURI); [//requestURI = /mvc/XXX.do]
//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
String contextPath=request.getContextPath();
//System.out.println("contextPath = "+contextPath); [//contextPath = /mvc]
//클라이언트 요청에 대란 요청값을 반환받아 저장
String command=requestURI.substring(contextPath.length());
//System.out.println("command = "+command); [//command = /XXX.do]
//3.
//클라이언트 요청을 모델(Model)을 사용하여 처리하고 뷰(View) 관련 정보를 반환받아 저장
// => 모델 역할의 Java 클래스로 객체를 생성하여 요청 처리 메소드 호출
// => 하나의 요청에 대해 하나의 모델이 처리되도록 설정 - Command Controller Pattern
//회원관리 프로그램에서 클라이언트 요청에 대한 모델 객체가 매핑되도록 설계
// => 로그인정보 입력페이지(환영메세지 출력페이지) - /loginForm.do >> LoginFormModel Class
// => 로그인 처리페이지 - /login.do >> LoginModel Class
// => 로그아웃 처리페이지 - /logout.do >> LogoutModel Class
// => 회원정보 입력페이지 - /writeForm.do >> WriteFormModel Class
// => 회원정보 삽입페이지 - /write.do >> WriteModel Class
// => 회원목록 출력페이지 - /list.do >> ListModel Class
// => 회원정보 출력페이지 - /view.do >> ViewModel Class
// => 변경회원정보 입력페이지 - /modifyForm.do >> ModifyFormModel Class
// => 회원정보 변경페이지 - /modify.do >> ModifyModel Class
// => 회원정보 삭제페이지 - /remove.do >> RemoveModel Class
// => 에러메세지 출력페이지 - /error.do >> ErrorModel Class
//3 - 방법1. 비권장 - init() 이용해 좀 더 고급스럽게 만들자
//=> 요청이 들어오면 그 때부터 Model객체를 만들어 메소드를 호출 찾는데 속도 매우 느림
/*
//모델 클래스가 상속받은 인터페이스를 이용하여 참조변수 선언
// => 참조변수에는 인터페이스를 상속받은 모든 자식클래스(모델)로 생성된 객체 저장 가능
Action action=null;
if(command.equals("/loginForm.do")) {
action=new LoginFormModel();
} else if(command.equals("/login.do")) {
action=new LoginModel();
} else if(command.equals("/logout.do")) {
action=new LogoutModel();
} else if(command.equals("/writeForm.do")) {
action=new WriteFormModel();
} else if(command.equals("/write.do")) {
action=new WriteModel();
} else if(command.equals("/list.do")) {
action=new ListModel();
} else if(command.equals("/view.do")) {
action=new ViewModel();
} else if(command.equals("/modifyForm.do")) {
action=new ModifyFormModel();
} else if(command.equals("/modify.do")) {
action=new ModifyModel();
} else if(command.equals("/remove.do")) {
action=new RemoveModel();
} else if(command.equals("/error.do")) {
action=new ErrorModel();
} else {//요청에 대한 모델 클래스가 없는 경우
action=new ErrorModel();
}
*/
//3 - 방법2. Map 객체에 저장된 엔트리에서 요청정보(Key)를 이용하여 모델 객체(Value)를 반환받아 저장
// => 찾는데 속도 매우 빠름 - 메모리 효율 및 가독성 증가
Action action=actionMap.get(command);
if(action==null) {//참조변수에 요청에 대한 모델 객체가 저장되어 있지 않은 경우
action=actionMap.get("/error.do");
}
//인터페이스 참조변수를 이용하여 추상메소드를 호출하면 참조변수에 저장된 모델 객체에 오버라이드 선언된 요청 처리 메소드 호출 - 오버라이드에 의한 다형성
// => 요청 처리 메소드에 의해 요청 처리 후 응답 관련 정보가 저장된 ActionForward 객체를 반환받아 저장
ActionForward actionForward=action.execute(request, response);
//4.
//응답 관련 정보를 저장된 ActionForward 객체를 이용하여 응답 처리
if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우 - 포워드 이동
//컨트롤러에서 뷰(XXX.jsp)로 스레드를 이동하여 JSP 문서의 실행결과(HTML 문서)를 클라이언트에게 전달하여 응답
request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
} else {//ActionForward 객체의 forward 필드값이 [false]인 경우 - 라다이렉트 이동
//컨트롤러에서 클라이언트에게 요청 URL 주소(XXX.do)를 전달하여 재요청하도록 응답
response.sendRedirect(actionForward.getPath());
}
}
[인터페이스] Action.java
package xyz.itwill.mvc;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//모든 모델 역활의 클래스가 반드시 상속받아야 되는 인터페이스
// => 모델 클래스의 요청 처리 메소드에 대한 작성 규칙 제공
// => 컨트롤러 역활의 서블릿에서 모델 객체로 요청 처리 메소드를 쉽게 호출할 수 있으며 유지보수의 효율성 증가
//인터페이스에는 요청 처리 메소드를 추상 메소드로 선언
// => 인터페이스를 상속받은 모든 자식클래스에서 반드시 추상 메소드를 오버라이드 선언
// => 요청 처리 메소드는 HttpServletRequest 객체와 HttpServletResponse 객체를 매개변수로
//전달받아 요청 처리하고 뷰 관련 정보를 ActionForward 객체로 반환하도록 작성
// => 요청 처리 메소드에서 발생되는 ServletException과 IOException는 예외 전달
public interface Action {
ActionForward execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException;
}
[클래스]ActionForward.java
package xyz.itwill.mvc;
//응답 관련 정보(뷰 - View)를 저장하기 위한 클래스
public class ActionForward {
//이동 형식에 대한 정보를 저장하기 위한 필드 - false : 리다이렉트 이동, true : 포워드 이동
//리다이렉트 이동 : 클라이언트에게 요청 URL 주소(/XXX.do)를 전달하여 다시 요청하도록 응답 처리 - 클라이언트 브라우저의 요청 URL 주소 변경
//포워드 이동 : 서블릿(컨트롤러)에서 JSP(뷰)로 스레드를 이동하여 응답 처리 - 클라이언트 브라우저의 요청 URL 주소 미변경
private boolean forward;
//이동될 웹프로그램의 경로를 저장하기 위한 필드
// => 리다이렉트 이동 : /XXX.do, 포워드 이동 : /XXX.jsp
private String path;
public ActionForward() {
// TODO Auto-generated constructor stub
}
public boolean isForward() { return forward;}
public void setForward(boolean forward) { this.forward = forward; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
}
07. EncodingFilter클래스
EncodingFilter.java : 필터클래스는 서블릿클래스 만드는 방법과 동일
package xyz.itwill.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
//필터 클래스 : 특정 웹프로그램 요청에 대한 웹프로그램 실행 전 또는 후에 동작될 명령을 작성하기 위한 클래스
// => 웹프로그램 실행 전 동작될 명령으로 리퀘스트 메세지(요청 관련 정보 - HttpServletRequest 객체)의 정보를 변경하는 명령 작성
// => 웹프로그램 실행 후 동작될 명령으로 리스폰즈 메세지(응답 관련 정보 - HttpServletResponse 객체)의 정보를 변경하는 명령 작성
//Header는 건들이지만 Body는 HTML(응답결과)가 있기 때문에 왠만해선 잘 건들이지 않음
//필터 클래스는 반드시 Filter 인터페이스를 상속받아 작성하며 @WebFilter 어노테이션 또는 [web.xml]
//파일에서 필터 클래스를 필터로 등록하고 필터가 동작되기 위한 URL 패턴정보를 매핑 처리
//클라이언트가 요청하는 "모든" 웹프로그램에 대해 실행 "전" 리퀘스트 메세지의 몸체부에 저장되어
//전달되는 문자값에 대한 문자형태(캐릭터셋)을 변경하는 필터 클래스 - 인코딩 필터
public class EncodingFilter implements Filter {
//변경할 캐릭터셋을 저장하기 위한 필드
private String encoding;
//필터 클래스로 객체가 생성된 후 가장 먼저 1번만 호출되는 메소드 - 필터 객체의 초기화 작업
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//encoding="utf-8";
//FilterConfig.getInitParameter(String name) :
//=>[web.xml] 파일의 init-param 엘리먼트로 제공되는 값을 얻어와 반환하는 메소드
encoding = filterConfig.getInitParameter("encoding");
}
//요청 웹프로그램 실행 전 또는 후에 동작될 명령을 작성하는 메소드
//=> 등록된 URL 패턴의 웹프로그램 실핸 전에 자동 호출되는 메소드
//실행 전에 동작될 ServletRequest객체와 ServletResponse객체를 WAS에게 제공받음
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//작성순서1.
//웸프로그램 실행 전에 동작될 명령 작성 - 리퀘스트 메시지의 정보 변경
//=> "utf-8"라고 직접 작성하면 확장성이 떨어지므로 [web-xml]의 init-param 엘리먼트 이용하기
if(request.getCharacterEncoding()==null || !request.getCharacterEncoding().equalsIgnoreCase(encoding)){
request.setCharacterEncoding(encoding);
}
//작성순서2.
//FilterChain.doFilter(ServletRequest request, ServletResponse response):
//=> 요청 웹프로그램과 연결하여 실행되도록 설정하는 메소드
chain.doFilter(request, response); //요청 웹프로그램 실행
////작성순서3.
//웸프로그램 실행 후에 동작될 명령 작성 - 리스폰즈 메시지의 정보 변경
//response.setCharacterEncoding("text/html; charset=utf-8");
//응답결과가 xml, json 다양하게 제공할 수 있으므로 위의 명령은 작성 안함
}
}
08. ExistUserinfoException | UserinfoNotfoundException | AuthFailException
ExistUserinfoException.java
package xyz.itwill.exception;
//회원정보(아이디)가 중복될 경우 발생될 예외를 표현하기 위한 클래스
// => 예외 클래스는 반드시 Exception 클래스를 상속받아 작성
public class ExistsUserinfoException extends Exception {
private static final long serialVersionUID = 1L;
public ExistsUserinfoException() {
// TODO Auto-generated constructor stub
}
public ExistsUserinfoException(String message) {
super(message);
}
}
UserinfoNotfoundException.java
package xyz.itwill.exception;
//회원정보를 찾을 수 없을 경우 발생될 예외를 표현하기 위한 클래스
public class UserinfoNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
public UserinfoNotFoundException() {
// TODO Auto-generated constructor stub
}
public UserinfoNotFoundException(String message) {
super(message);
}
}
AuthFailException.java
package xyz.itwill.exception;
//인증이 실패한 경우 발생될 예외를 표현하기 위한 클래스
public class AuthFailException extends Exception {
private static final long serialVersionUID = 1L;
public AuthFailException() {
// TODO Auto-generated constructor stub
}
public AuthFailException(String message) {
super(message);
}
}
'jsp' 카테고리의 다른 글
[jsp] 30. JSP model2 (MVC패턴) (0) | 2024.07.18 |
---|---|
[jsp] 29. 회원정보 관리 프로그램(MVC) (0) | 2024.07.18 |
[jsp] 28. JSTL(Java Standard Tag Library) (0) | 2024.07.18 |
[jsp] 27. EL 커스텀태그 @taglib (0) | 2024.07.17 |
[jsp] 26. filter클래스 만드는법 (context-param VS init-param) (0) | 2024.07.16 |