본문 바로가기
국비학원

[국비지원] KH 정보교육원 79일차

by 도전하는 개발자 2022. 7. 20.

Kh day 079 

 오늘은 속도감 있게 수업이 나가다보니 정신도 없고 따라가기 벅찼던거 같다. 그러나 이제 교육과정의 절반이 넘었고 3달뒤면 수료다. 파이널 프로젝트는 이미 시작 되었고 가장 중요한 백앤드를 배우고 있다. 힘들더라도 긴장을 늦춰서는 안 된다!

 

---

 

6장 서블릿과 JDBC연동

1. JDBC개요
여기는 해본거다 혼자 보세요

---

2. JDBC 고급 기법
여기도 해본거다 혼자해보세요

---

2.2 DAO 패턴 및 DTO 패턴



* Design Pattern -> GoF 
 : 특수한 기능을 수행하는 클래스를 최소개수로 설계하는 방법
   ex) 싱글턴 패턴 (Singleton Pattern)

1) DAO 패턴 (Data Access Object)
일반적으로 어플리케이션 개발은 GUI화면을 가지며 화면에 보여줄 수 있는 데이터 관리를 위해서 데이터베이스를 사용해서 개발하게 된다. 웹 브라우저에서 보여지는 것처럼 GUI 화면을 구성하는 코드를 Presentation Logic 이라고 하며 GUI화면에 데이터를 보여주기 위해서 데이터베이스를 검색하는 코드 및 GUI화면에서 새로 발생된 데이터 (예를 들면 회원가입)를 데이터베이스에 저장하는 코드와 같은 실제적인 작업을 처리하는 코드를 business logic 이라고 한다. presentation logic 과 business logic을 하나의 클래스로 모두 구현 할 수도 있고 여러 클래스로 모듈화 시켜서 구현할 수도 있다. 하나의 클래스로 구현하면 유지보수가 어려워지기 때문에 바람직하지 않다. 예를들어 2개의 로직이 하나의 클래스 파일에 저장되면 화면을 변경하거나 또는 business logic이 변경될 때 복잡한 코드로 인해서 유지보수가 어려워진다. 따라서 모듈화 시켜 개발하게 되며 모듈화한 클래스들중에서 데이터베이스 처리하는 코드만을 관리하는 클래스를 작성할 수 있는데 이 클래스 파일을 DAO 클래스라고 한다. 이렇게 DAO 클래스를 사용해서 개발하는 것이 보편화 되었기 때문에 DAO 패턴이라고 부른다. 일반적으로 DAO 클래스는 테이블 당 한 개씩 생성해서 사용한다. DAO 클래스안에는 특정 테이블에서 수행할 작업을 메소드로 정의해서 구현하며 presentation logic에서는 DAO 클래스의 메소드를 호출하면서 원하는 작업을 구현하게 된다.

2) DTO 패턴 (Data Transfer Object)
presentation logic과 business logic을 여러 클래스로 분리해서 작업은 하지만 서로 간에 긴밀한 관계가 유지되면서 작업이 이루어진다. presentation logic 에서 보여줄 데이터를 얻기 위해서 business logic에게 요청을 하면 business logic은 필요한 데이터를 데이터베이스에서 검색해서 presentation logic에게 반환하는 작업등을 하게 된다. 이렇게 데이터를 다른 logic에게 전송 및 반환할 때 효율적으로 데이터를 사용할 수 있게 클래스를 작성할 수 있는데 이 클래스를 DTO 클래스라고 한다. 이름 그대로 데이터를 전송할 때 사용되는 클래스이다. DTO 클래스를 사용하면 데이터를 전송할 때와 전송된 데이터를 얻어서 사용할 때 효율적으로 사용할 수 있는 장점이 있다. 일반적으로 도메인객체(Domain Object), VO(Value Object)라고도 한다

--

먼저 VO를 만들어주자
패키지명을 domain으로 한다

 

package org.zerock.myapp.domain;

import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.Value;

//@EqualsAndHashCode
//@ToString
//@Getter(lombok.AccessLevel.PUBLIC)
//@Setter(lombok.AccessLevel.PUBLIC)
//@NoArgsConstructor
//@AllArgsConstructor

@Value // 이거 하나만 쓰면 위에것 다 적용된다 
// Setter는 안 되는데 VO객체는 어짜피 Setter 안 씀 (DTO는 Setter필요)
public class EmpVO {
	private Integer empno;
	private String ename;
	private String job;
	private Integer mgr;
	private Date hireDate; // util 패키지로 임포트
	private Double sal;
	private Double comm;
	private Integer deptno;
	
	// 기본생성자 직접 만들기 (@NoArgsConstructor)
//	public EmpVO() {
//		;;
//	} // default constructor
	
	// 매개변수 있는 생성자 직접 만들기 (@AllArgsConstructor)
//	public EmpVO(Integer empno, String ename, 
//			     String job, Integer mgr, 
//			     Date hireDate,  Double sal, 
//			     Double comm, Integer deptno) {
//		this.empno = empno;
//		this.ename = ename;
//		this.job = job;
//		this.mgr = mgr;
//		this.hireDate = hireDate;
//		this.sal = sal;
//		this.comm = comm;
//		this.deptno = deptno;
//	} // constructor (필드이름과 매개변수 이름이 충돌하니 this를 이용해서 접근해  )
	
	// Getter 메소드 직접 만들기 (@Getter(lombok.AccessLevel.PUBLIC))
//	public Integer getEmpno() { return this.empno; }
//	public String getEname() { return this.ename; }
//	public String getJob() {return this.job;}
//	public Integer getMgr() { return this.mgr; }
//	public Date getHireDate() { return this.hireDate; }
//	public Double getSal() { return this.sal; }
//	public Double getComm() { return this.comm; }
//	public Integer getDeptno() { return this.deptno; }

	// Setter 메소드 직접 만들기 (@Setter(lombok.AccessLevel.PUBLIC))
//	public void setEmpno (Integer empno) { this.empno = empno; }
//	public void setEname (String ename) { this.ename = ename; }
//	public void setJob (String job) { this.job = job; }
//	public void setMgr (Integer mgr) { this.mgr = mgr; }
//	public void setHireDate (Date hireDate) { this.hireDate = hireDate; }
//	public void setSal (Double sal) { this.sal = sal; }
//	public void setComm (Double comm) { this.comm = comm; }
//	public void setDeptno (Integer deptno) { this.deptno = deptno; }
		
} // end class


EmpVO.java

이제 DAO를 만들자
패키지명은 pesistence로 한다

package org.zerock.myapp.persistence;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.zerock.myapp.domain.EmpVO;

import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor
public class EmpDAO {
	
//	public static DataSource dataSource;
	private static DataSource dataSource;
	
	static { // JNDI lookup을 통해, DataSource 자원객체를 필드에 초기화 시키자
		// WAS가 생성한 DataSource 객체의 획득방법
		// WAS의 표준 API인, JNDI API를 이용해서 설정에 의해 자동생성된 데이터 소스 획득 
		
		try {
    		// 1. JNDI tree의 뿌리에 접근하게 해주는 객체를 획득
    		Context ctx = new InitialContext(); // 100% 성공 (Web Application 안에서 수행된다면...)
    		
    		// 2. Context 객체를 가지고, 지정된 이름을 가지는 리소스 열매를 찾아서 획득
    		EmpDAO.dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/OracleCloudATP");
    		
    		log.info("\t+ this.dataSource : {}", EmpDAO.dataSource);
    	} catch(Exception e) {
    		e.printStackTrace();
    	} // try-catch  	
		
	} // static initializer
	
	public List<EmpVO> selectAll() throws SQLException {
		log.trace("select() invoked.");
		
		// Scott 스키마의 'emp' 테이블을 모두 조회해서, 리스트 컬렉션으로 반환
		// 리스트 컬렉션의 요소는 EmpVO이어야 함.
		
		Connection conn = EmpDAO.dataSource.getConnection();
		
		String sql = "SELECT * FROM emp ORDER BY empno";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		
		ResultSet rs = pstmt.executeQuery();
		
		List<EmpVO> list = new Vector<>();
		
		try (conn; pstmt; rs;) { // 순서를 신경써서 닫으려면 try-with로 닫자
 			while(rs.next()) {
 				Integer empno = rs.getInt("empno");
 				String ename = rs.getString("ename");
 				String job = rs.getString("job");
 				Integer mgr = rs.getInt("mgr");
 				Date hireDate = rs.getDate("hireDate");
 				Double sal = rs.getDouble("sal");
 				Double comm = rs.getDouble("comm");
 				Integer deptno = rs.getInt("deptno");
 				
 				EmpVO vo = new EmpVO(empno, ename, job, mgr, hireDate, sal, comm, deptno);
 				list.add(vo);
 			} // while
		} // try-with-resources
		
		return list;
	} // select

} // end class


EmpDAO.java

webapp 폴더 아래 META-INF 폴더를 만들어 context.xml을 만들자

 



--------

7장 서블릿 고급

FrontController 패턴
웹 어플리케이션 개발시 사용자의 요청을 처리하기 위한 최초 진입점(initial point)을 정의하고 사용하는 패턴을 의미한다. 모든 사용자의 요청을 집중화시키면 요청을 분산시켜 발생되는 중복된 코드를 제거할 수 있고, 사용자의 요청을 일관된 방법으로 관리할 수 있는 장점이 있다. 

다음은 FrontController 패턴을 적용하지 않은 경우의 아키텍쳐이다. 클라이언트의 개별적인 요청을 서로 다른 서블릿이 처리하기 때문에 중복코드가 발생될 수 있고, 다수의 서블릿으로 인한 유지보수가 어려워 질수 있다.


FrontController 패턴을 적용한 전체적인 아키텍쳐는 다음과 같다. 사용자의 모든 요청을 단 하나의 서블릿이 처리하는 집중화 형태이기 때문에 중복코드가 제거되고 유지보수가 쉬워진다.


FrontController 패턴을 적용한 서블릿에서 고려해야 되는 사항은 사용자가 어떤 동작을 요청 했는지를 식별할 수 있어야 된다. 따라서 사용자가 서블릿에 요청 할 때, 다음과 같은 메커니즘으로 서블릿이 사용자의 요청을 식별할 수 있도록 지원한다

http://서버IP번호:포트번호/context명/식별값

사용자는 명시적으로 URL값에 ‘식별값’을 추가하여 요청하고, 서블릿에서는 ‘식별값’을 비교하여 어떤 요청인지를 구별할 수 있다. ‘식별값’은 임의의 문자열값으로서 일반적으로 웹 프레임워크(Spring , Struts2)에서 사용하는 방식으로 지정한다
따라서 다음과 같이 데이터를 저장하는 요청인 경우에는 insert.do로 지정하고 조회를 하는 요청인 경우에는 select.do 형식으로 지정할 수 있다.



요청 받은 서블릿에서는 다음과 같이 2가지 작업을 통하여 원하는 ‘식별값’을 얻는다. 

1) 서블릿의 맵핑명을 다음과 같이 *.do 값의 확장자 패턴 형식으로 지정한다. 
따라서 반드시 사용자는 xxx.do 형식으로 요청해야 된다. 

@WebServlet("*.do")
public class 서블릿명 extends HttpServlet { .... }

2) 서블릿에서 다음 코드를 사용하여 ‘식별값’을 비교 처리 할 수 있다.





---

1) webapp 폴더밑에 resources폴더 만들고 거기에 request.html 파일을 먼저 만들자

 

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="utf8">
    <title>FrontController 패턴 실습</title>
</head>

<body>
    <h1>FrontController 패턴실습</h1>

    <a href="insert.do">저장하기</a><br>
    <a href="/08Chapter/delete.do">삭제하기</a><br>
    <a href="http://localhost:8080/08Chapter/update.do">수정하기</a><br>
    <a href="select.do">조회하기</a><br>
</body>

</html>


request.html

2) 그 후에 서블릿 파일을 만든다. URL 매핑은 " *.do "다 

 

@Log4j2
@NoArgsConstructor

@WebServlet("*.do")
public class FrontControllerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked.");
		
		String requestURI = req.getRequestURI();
		String contextPath = req.getContextPath();
		String command = requestURI.substring(contextPath.length());
		 
		if(command.equals("/insert.do")){
			System.out.println("insert 요청");
			
		}else if(command.equals("/delete.do")){
			System.out.println("delete 요청");
			
		}else if(command.equals("/update.do")){
			System.out.println("update 요청");
			
		}else{
			System.out.println("select 요청");
		} // if-else
		
	} // service

} // end class


FrontControllerServlet.java