오늘의 코딩순서
1.프로젝트 배포 방법
2. (폴더: och16)
- och10폴더의 자카르타 파일 4개, context.xml를 lib 폴더에 복붙함
↳ context.xml은 DBCP를 위해 걸어두는 것! - webapp 폴더에 images, sub2 폴더 만듬
- Board.sql을 메모장으로 열어서 내용 복사하고 Oracle에 넣어 커밋해서 BOARD TBL 만들기
메인 페이지 ↓↓↓
http://localhost:8181/och16/list.do
( 줌 강의 4:55~5:03, 5:08~5:16, 5:47~)
- (dao 패키지)Board.class + BoardDao.class (현장 HW)
- (service 패키지) CommandProcess.class + (control 패키지) Controller.servlet + web.xml
+ (lib 폴더) command.properties.file + (service 패키지) ListAction.class - list.jsp (타이핑 조금만 함)
오늘의 코딩 포인트
(폴더: och16)
- Board.class
package och16.dao;
import java.util.Date;
public class Board {
private int num;
private String writer;
private String subject;
private String content;
private String email;
private int readcount;
private String passwd;
private int ref;
private int re_step;
private int re_level;
private String ip;
private Date reg_date;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getReadcount() {
return readcount;
}
public void setReadcount(int readcount) {
this.readcount = readcount;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public int getRef() {
return ref;
}
public void setRef(int ref) {
this.ref = ref;
}
public int getRe_step() {
return re_step;
}
public void setRe_step(int re_step) {
this.re_step = re_step;
}
public int getRe_level() {
return re_level;
}
public void setRe_level(int re_level) {
this.re_level = re_level;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
}
※ 아래 코딩은 코딩한 순서가 아닌, 로직이 작동하는 순서에 가깝게 작성함!
1. 크롬 같은 웹에서 요청(Request)이 들어오면 가장 먼저 실행됨
- web.xml ==> 여기까지는 단순한 String에 지나지 않음
- <servlet-class>control.Controller</servlet-class>에서 '.' 앞은 서블렛의 패키지명, 뒤는 서블렛의 클래스명
즉 서블렛을 Controller로 만들어주겠다, Controller의 실체 - <servlet-name>Controller</servlet-name>으로 controller 지정하여, servlet 프로그램의 서버라고 선언
- <param-name>config</param-name>으로 초기값을 config라는 파라미터 명으로 지정
config의 값(value)은 /WEB-INF/command.properties로 지정 - <param-value>/WEB-INF/command.properties</param-value>는 congig의 변수,
config 파라미터를 command.properties로 init해서 넘김
Tip)- https에서 s 없애야 오류 없어지는 경우가 많음 => ∵최신 버전을 아직 못받아들여서
- <servlet-class>control.Controller</servlet-class>에서 '.' 앞은 서블렛의 패키지명, 뒤는 서블렛의 클래스명
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee" xsi:schemaLocation="http://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" id="WebApp_ID" version="6.0">
<display-name>och16</display-name>
<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>
<servlet>
<description></description>
<display-name>Controller</display-name>
<servlet-name>Controller</servlet-name>
<servlet-class>control.Controller</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/command.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
2. web.xml을 통해 요청(Request)를 받아들인다
- Controller.servlet⭐⭐⭐⭐⭐ ==> 정확히는 Controller 역할을 하는 Servlet임
1) web.xml에서 init된 파라미터 config를 해주었기 때문에 servlet에서 끌어당겨 Controller에서 쓸 수 있음
2) init: servlet에서 작성한 controller 작동 script
1. init String props=> /WEB-INF/command.properties
- /WEB-INF/command.properties라는 파라미터 값은 config, 다음으로 props에 들어감
public void init(ServletConfig config) throws ServletException {
// web.xml에서 propertyConfig에 해당하는 init-param 의 값을 읽어옴
String props = config.getInitParameter("config");
// /WEB-INF/command.properties
System.out.println("1. init String props=> "+ props); // /ch16/com
Properties pr = new Properties();
FileInputStream f = null;
2. init String configFilePath=> C:\jsp\jspSrc17\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\och16\WEB-INF\command.properties
- getRealPath를 통해 c:\jsp\jsp17\.metadata/.plugins/org.eclipse.wtpwebapps\och16\WEB-INF\command/properties를 받아옴
=> 받아오는 이유는 웹으로 배포를 할 때 사용자의 드라이브에는 이클립스도, 파일도 없으니 메타데이터를 따라가는 배포데이터를 만들어줌 => 이때 만들어진 데이터가 configFilePath - 윗부분 까지는 String에 지나지 않았다면, 이제 메모리에 업로드를 해야함
f = new FileInputStream(configFilePath);을 통해 configFilePath를 파일로 바꿔준 뒤,
properties의 값을 받은 pr.load(f);를 통해 파일명을 주며 메모리에 업로드함
이후 try-catch 문을 지나 close를 거쳐 업로드가 완료됨
try {
String configFilePath = config.getServletContext().getRealPath(props);
System.out.println("2. init String configFilePath=> "+ configFilePath); // /och16/com
f = new FileInputStream(configFilePath);
// Memory upload
pr.load(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (f != null)
try {
f.close();
} catch(IOException ex) {
System.out.println("IOException ex.getMessage()->"+ex.getMessage());
}
}
3. init command => /list.do
- 메모리에 올라간 pr(properties)에는 Map 방식⭐을 통해 command.properties.file에 있는 key값(여기서는 /list.do)을 받아옴 ++(String)부분 설명 추가하기
// list.do /content.do
// /list.do = service.ListAction
// /content.do = service.contentAction
Iterator keyIter = pr.keySet().iterator();
4. init className=> service.ListAction
- 가져온 key값을 keyIter에 넣고, hasNext (rs.next()와 비슷한 기능을 함)를 거치면 향상형 for문처럼 하나씩 통과함
↳ hasNext(): - key 값을 command에 넣고 => command가 getProperty를 가져와 value값의 이름, 즉 serviceListAction을 ClassName에 넣음
while (keyIter.hasNext()) {
String command = (String) keyIter.next();
String className = pr.getProperty(command);
System.out.println("3. init command => "+ command); // /och16/com
System.out.println("4. init className=> "+ className);
3. 요청(Request)한 url 주소를 따라 뒤의 value값이 Service에서 수행되도록, Controller에 짜인 로직에 따라 value값이 인스턴스화 되어서 CommandProcess로 넘어간다
3) try문 거치기
- ClassName에 넣은 값을 commandClass를 통해 Class명으로 만들어줌
- getDeclaredConstructor을 통해 CommandProcess를 인스턴스화 해줌
이때 CommandProcess를 인스턴스화 해주는 이유는 CommandProcess 가 서비스에 있기 때문이며, 상속받은 클래스들(ListAction, ContentAction 등)을 대신해서 While문을 다 받아줄 수 있기 때문
이는 ListAction la = new ListAction();과 같은 성능을 지니지만 효율은 더 좋음⭐ - commandMap에 저장되는데 괄호 안의 앞의 값인 command에는 String의 /list.do가 들어가고, 뒤의 값인 commandInstance에는 CommandProcess의 자식인 service.ListAction를 인스턴스화 한 값이 들어가고, 메모리에 값들이 올라감
try {
// ListAction la = new ListAction();
//소멸 Class
// Class commandClass = Class.forName(className);
// Object commandInstance = commandClass.newInstance();
//new Class ==> 제네릭의 요점은 클래스 유형을 모른다
Class<?> commandClass = Class.forName(className); //해당 문자열을 클래스로 만든다
CommandProcess commandInstance =
(CommandProcess)commandClass.getDeclaredConstructor().newInstance();
// list.do service.ListAction
// contend.do service.ContentAction
commandMap.put(command, commandInstance);
} catch (Exception e) {
e.printStackTrace();
}
3. init command => /list.do (한 번 더 돌려서 위의 3번과 같은 sysout이 나온것)
4. init className=> service.ListAction (한 번 더 돌려서 위의 4번과 같은 sysout이 나온것)
- 위의 작업을 전부 거치고 나서 while문을 한 번 더 돌림 ∵ 이후 추가한 /content.do 때문
=> 즉, 추가할 때마다 한 번씩 더 돌아감 - 이렇게 긴 처리가 된 Servlet은 Map방식을 통해서 private Map<String, Object> commandMap = new HashMap<String, Object>();에 String에는 key 값, Object에는 value값이 들어가 Object에 들어간 주소를 발동시킴
이렇게 하면 위의 Map 커맨드에 어떤 커맨드가 들어와도 큰 수정 없이 발동시킬 수 있음
즉, new보다 효율적임
public class Controller extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String, Object> commandMap = new HashMap<String, Object>();
4. Controller 에서 추가된 Object들은 CommandProcess.class를 통해 Service와 정보(request, response)를 주고받는다
- CommandProcess.class ==> Service에 있음
Tip)- requestpro: 여기에 비즈니스 로직이 들어감
design pattern관련하여 역할 써두기
- requestpro: 여기에 비즈니스 로직이 들어감
package och16.service;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public interface CommandProcess {
public String requestPro(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
}
5. Controller와 Service는 Get방식이나 Post방식을 통해 request와 response 값을 주고받는다
4) doGet, doPost
1-1. requestServletPro URI command=> /och16/list.do
1-2. requestServletPro URL command3=> http://localhost:8181/och16/list.do
- URI 자원에 대한 통합 자원 식별자, URL은 자원의 위치(주소)와 식별자를 동시에 보여줌
- init처리가 된 후 doGet, doPost를 만나면 requestServletPro가 호출되어 실행됨
- requestServletPro가 실행되고 나면 getRequestURI가 실행되는데, 이때 URI는 /och16/list.do를 뜻하고, URL은 http://localhost:8181/och16/list.do을 뜻함
이들을 각각 command, command3 객체에 넣어줌
2. requestServletPro command substring=> /list.do
- 저장된 /och16/list.do 에서 list.do만 사용하고 싶으므로 subString을 사용하여 contextPath만큼 잘라냄
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//위의 request와 response가 아래에 입력됨
requestServletPro(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
requestServletPro(request, response);
}
protected void requestServletPro(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String view = null;
CommandProcess com = null;
String command = request.getRequestURI();
StringBuffer command3 = request.getRequestURL();
System.out.println("1-1. requestServletPro URI command=> "+ command); // /och16/list.do
System.out.println("1-2. requestServletPro URL command3=> "+ command3); // /och16/list.do
command = command.substring(request.getContextPath().length());
System.out.println("2. requestServletPro command substring=> "+command); // /och16/
5) try문 거치기
- command.properties.file
Tip)- 프로젝트를 분할할 수 있음 => 0729 9시 리뷰 참고해서 더 추가하기
- Map 방식에 따라, '='의 왼쪽이 key가 되고 오른쪽이 value값이 됨
- properties
- Map 방식: key와 value
/list.do=service.ListAction
/content.do=service.ContentAction
3. requestServletPro command=> /list.do
4. requestServletPro com=> service.ListAction@4114cf1e
- list.do는 command.properties.file의 key값, commandMap에 key값을 넣으면 value값인 service.ListAction이 불려와서 인스턴스인 com에 저장됨
com이 인스턴스인 증거는 콘솔창을 보면 @와 숫자로 이루어진 로직이 보임, 이는 hash코드를 의미하며 이는 인스턴스 객체를 가리킴 - 'service.ListAction@숫자'가 들어간 com이 requestpro에 들어감
=> requestpro를 하는 이유
public String requestPro(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
이 때문에 ListAction.class, 즉 Service 모델이 실행됨
try {
//service.ListAction Instance
com = (CommandProcess) commandMap.get(command);
System.out.println("3. requestServletPro command=> "+ command); // /och16/
System.out.println("4. requestServletPro com=> "+ com); // /och16/
// com --> service.ListAction@32a22787
view = com.requestPro(request, response);
System.out.println("5. requestServletPro view=> "+ view); // /och16/
} catch (Exception e) {
throw new ServletException(e);
}
6. Service 모델은 DAO를 호출하여 로직을 작동시킨다
ListAction Service start...
- requestPro로 인해 Service 모델이 작동되고, 인스턴스화 된 value값이 넘어와 commandProcess를 상속받음
이로 인해 어떤 Service들이던 밑에 써넣으면, DAO와 값을 주고 받을 수 있게 됨
따라서 아래 DAO logic이 작동되면 BoardDao가 호출되고 연결되어 로직이 작업됨
로직의 결과값을 받아서 관련된 파라미터 값들을 전부 request.setAttribute에 저장함
7. 작동된 DAO 로직과 연결되어 DML작업을 통해 DB와 연결한 뒤 데이터를 주고 받는다.
- BoardDao.class >> 데이터베이스와의 연결을 관리하는 싱글턴 패턴의 DAO 클래스
Tip)- private static BoardDao instance;
- private: 접근 제어자를 사용하여 외부에서 직접 접근하거나 수정하지 못하도록 함
- BoardDao: 클래스의 유일한 인스턴스를 저장하는 static 변수
- private BoardDao()
- BoardDao 클래스의 생성자
- private 접근 제어자를 사용하여 외부에서 이 클래스를 직접 인스턴스화 하지 못하게 함
이는 Singleton 패턴을 유지하기 위한 중요한 부분
- public static BoardDao getInstance()
- BoardDao 클래스의 인스턴스를 반환하는 static 메서드
- public 접근 제어자를 사용하여 외부에서 이 메서드를 통해 유일한 인스턴스를 얻을 수 있게 함
- 인스턴스가 아직 생성되지 않았다면, new BoardDao()를 통해 생성함
- private Connection getConnection()
- 데이터베이스와 연결을 설정하고 Connection 객체를 반환하는 메서드
- private 접근 제어자를 사용하여 외부에서 직접 호출할 수 없도록 함
- 이 메서드는 주로 BoardDao 클래스 내부에서 데이터베이스와의 연결이 필요할 때 사용됨
- private static BoardDao instance;
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
//(0724)현장 HW1. Singleton + DBCP
public class BoardDao {
private static BoardDao instance;
private BoardDao() {
}
public static BoardDao getInstance() {
if (instance == null) {
instance = new BoardDao();
}
return instance;
}
private Connection getConnection() {
Connection conn = null;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup("java:comp/env/jdbc/OracleDB");
conn = ds.getConnection();
}catch(Exception e) {
System.out.println(e.getMessage());
}
return conn;
}
8. DAO와 DB가 주고받은 결과값(result 값)이 Controller에 return되어 Service(
##.jsp)모델이 실행되면 result 값인 ##.jsp를 Controller에 전달한다
↳ 이때 View가 정해지는데 사실 Controller에 전해지기 전, Service(예시_ ListAction.class )에서 정해진거나 다름없다
==> ListAction.class의 return 값이 "list.jsp"였기 때문
9. Service에서 Controller으로 이동할 때 작동하는 메서드는 RequestDispatcher.
페이지로 이동시킨뒤 forward 시켜주면 view로 이동이 완료된다.
5. requestServletPro view=> list.jsp
RequestDispatcher dispatcher = request.getRequestDispatcher(view);
dispatcher.forward(request, response);
Tip)
- 왜 이렇게 힘들게 코딩하느냐?
=> ListAction la = new ListAction(); 처럼 선언하고 짜고나면 controller의 값을 전부 수정해야 한다 - init
- getInitParameter
- properties⭐⭐⭐: Map방식에서 쓰이는 클래스, key와 value의 쌍으로 이루어진 데이터의 집합
- getRealPath
'JSP > Java Script' 카테고리의 다른 글
2024_07_26_금 (0) | 2024.07.26 |
---|---|
2024_07_25_목 (0) | 2024.07.26 |
2024_07_23_화~ 07_24_수 (0) | 2024.07.23 |
2024_07_22_월 (0) | 2024.07.22 |
2024_07_19_금 ⭐⭐⭐ (0) | 2024.07.19 |