티스토리 뷰

1.    웹 서비스의 웹 3계층

 

-      1 ) 표현계층 ( Presentation Layer ) : 화면 정적(*.html), 동적(*.jsp) ( JSP )

-      2 ) 비즈니스 계층 ( Business Layer ) : Mission-critical Biz. Logic ( Servlet )

-      3 ) 영속성 계층 ( Persistence Layer ) : Oracle Cloud ATP ( Database )

 

-      + Servlet이나 JSP 1개가 위의 3계층을 모두 담당하는 것을 Model 1 Architecture

-      + 3계층에 따라서 MVC model에 맞게 웹을 개발하는 것을 Model 2 Architecture

-      + MVCModel / View / Controller을 의미한다.

 

2.    DAO 패턴 / DTO 패턴 / VO 패턴

 

-      3가지는 데이터베이스를 연동하는 프로그램을 개발할 때 사용되는 개발패턴이다.

 

-      1 ) DAO 패턴 ( Data Access Object )

-       : 데이터베이스를 연동할 때 사용되는 개발패턴 중 하나로 데이터 베이스(테이블)에 대한 조작(CRUD)을 메소드를 제공하는 객체이다.

 

-      2 ) DTO 패턴 ( Data Transfer Object )

-       : 화면UI에서 전송된 요청(전송)파라미터들을 전송 및 뒤로 전달하는 역할을 하는 객체

 

-      3 ) VO 패턴 ( Value Object )

-       : 영속성계층인 DB1개의 테이블에 1개의 레코드를 저장하는 객체로, 뒤에서 앞으로 전달하는 역할을 하는 객체이다.

 

3.    FrontController 패턴

 

-      웹 어플리케이션 개발시 사용자의 요청을 처리하기 위한 최초 진입점을 정의하고 사용하는 패턴이다.

-      이를 통해 사용자의 요청을 하나로 집중시키면, 요청을 분산시켜 발생되는 중복된 코드를 제거할 수 있으며, 사용자의 요청을 일관된 방법으로 관리할 수 있다는 장점이 있다.

-      예시로는 로그인 페이지가 있다.

-      FrontController 패턴을 적용할 서블릿에서는 사용자가 어떤 동작을 요청했는지 식별할 수 있게 고려해야 한다.

-      그렇기에, 서블릿에서는 사용자의 요청을 http://localhost:8080/context명/식별값 과 같은 매커니즘으로 식별할 수 있게 지원해줘야 한다.

 


[ 1. 커넥션 풀로 JDBC와 연동하기 - DML문 실행 ] (****)

 

package org.zerock.myapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

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


@Log4j2
@NoArgsConstructor

@WebServlet("/EmpInsert")
public class EmpInsertServlet extends HttpServlet { // 커넥션 풀 사용 ver.
	private static final long serialVersionUID = 1L;
	
	// =============================================================================
	
	// JNDI LOOKup을 통해 CP 주소 획득 및 CP 객체의 주소를 변수에 저장
	@Resource(name="jdbc/OracleCloudATPWithDriverSpy")
	private DataSource dataSource;
	
	// =============================================================================
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		log.trace("service(rea, res) invoked.");
		
		// =============================================================================
		// 		전송파라미터( Request Parameters ) 얻기
		// =============================================================================
		
		String empno = req.getParameter("empno");
		String ename = req.getParameter("ename");
		String sal = req.getParameter("sal");
		String deptno = req.getParameter("deptno");
		
		// =============================================================================
		// 		응답화면 생성 및 전송
		// =============================================================================
		
		// 응답문서 준비
		res.setContentType("text/html; charset=utf8");
		
		@Cleanup
		PrintWriter out = res.getWriter();
		
		out.println("<html><head></head><body>");
		
		// =============================================================================
		// 		JDBC 연동
		// =============================================================================
		
		try {
			
			Connection conn = this.dataSource.getConnection();
			log.debug("\t + conn : {}", conn);
			// 커낵션 풀에서 얻은 커넥션의 정보가 나오기에, URL이 우리가 지정한 jdbcUrl이 나온다.
			
			// 데이터베이스에 값을 넣는 sql문이다. ( DML )
			String sql = "INSERT INTO emp ( empno, ename, sal, deptno ) VALUES( ?, ?, ?, ? )";
			PreparedStatement pstmt = conn.prepareStatement(sql);
			
			// ?에 값 바인딩하기
			pstmt.setInt(1, Integer.parseInt(empno)); // ?의 경우 배열이 아니기에, 1번부터 시작한다.
			pstmt.setString(2, ename);
			pstmt.setDouble(3, Double.parseDouble(sal));
			pstmt.setInt(4, Integer.parseInt(deptno));
			
			// 쿼리를 실행해서 총 몇개가 영향을 받았는지 알려준다.
			int affectedLines = pstmt.executeUpdate();
			
			log.info("\t + affectedLines : {}", affectedLines);
			
			try ( conn; pstmt; ){
				
				// 응답문서로 출력
				out.println( String.format("<p>affectedLines : %s</p>", affectedLines));
				
			} // try - with - resources
			
		} catch(Exception e) {
			throw new IOException(e);
		} finally {
			out.println("</body></html>");
			out.flush();
		} // try - catch
		
	} // service
	
	// =============================================================================

} // end class

[ 2. VO 패턴 / DAO 패턴 / DTO 패턴 적용 ] (****)

 

[ 2 - 1. EmpVo 생성 ]

 

package org.zerock.myapp.domain;

import lombok.Value;


// + getter / setter 롬복
//@Getter @Setter

// + 생성자 롬복
//@NoArgsConstructor
//@AllArgsConstructor

// + Data 롬복으로 getter, setter, 매개변수가 없는 생성자를 한번에 만들 수 있다. + @toString
// + @Data는 DTO 패턴에서 사용된다. (***)
//@Data

// + VO패턴에서는 읽기 전용이기에, @Value를 통해서 읽기는 가능하지만, 수정은 못하도록 해야 한다. 
// + @Value는 VO패턴에서 사용된다. (***)
@Value
public class EmpVO {
	
	private Integer empno;			// PK
	private String ename;			// NULL 허용
	private Double sal;				// NULL 허용
	private Integer deptno;			// NULL 허용
	
	// NULL을 포함해야 될 때에는 기본타입이 아닌 Wrapper(참조) 타입을 사용해야 한다. (***)

} // end class

 

[ 2 - 2. EmpDAO 생성 ]

 

package org.zerock.myapp.persistence;

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 javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.zerock.myapp.domain.EmpVO;

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


@Log4j2
@NoArgsConstructor
public class EmpDAO { // POJO : Plain Old Java Object
	
	// 서블릿이 아니면, 자동으로 JNDI Lookup할 수 없다. -> 수동으로 해야 한다.
	// @Resource(name="jdbc/OracleCloudATPWithDriverSpy")
	
	// + Servlet 클래스의 내부에서는 위의 어노테이션으로 자원객체를 JNDI tree로부터
	// + 얻어낼 수 있지만, 그 외의 클래스에서는 불가능하다.
	
	// ====================================
	
	private static DataSource dataSource;
	
	// ====================================
	
	static { // JNDI Lookup을 해서, DataSource 객체의 주소를 획득
		
		try {
			
			Context ctx = new InitialContext();
			
			Object obj = ctx.lookup("java:comp/env/jdbc/OracleCloudATPWithDriverSpy");
			dataSource = (DataSource) obj;
			
		} catch (NamingException e) { ;; } // try - catch
		
	} // static initializer : 원래는 생성자 안에서 해야 한다.
	
	// ====================================
	
	public List<EmpVO> selectAll() throws SQLException {
		
		// selectAll() : 해당 테이터를 모두 선택해서 반환
		log.trace("select() invoked.");
		
		log.debug("\t + this.dataSource : {}", dataSource);
		
		List<EmpVO> list = new ArrayList<>();
		// EmpVO 객테타입을 보관하는 리스트(ArrayList)를 생성
		
		try {
			
			Connection conn = dataSource.getConnection();
			
			String sql = "SELECT empno, ename, sal, deptno FROM emp";
			
			PreparedStatement pstmt = conn.prepareStatement(sql);
			
			ResultSet rs = pstmt.executeQuery();
			
			try( conn; pstmt; rs; ) {
				
				while ( rs.next() ) {
					
					Integer empno = rs.getInt("empno");
					String ename = rs.getString("ename");
					Double sal = rs.getDouble("sal");
					Integer deptno = rs.getInt("deptno");
					
					// VO(값 객체)는 1개의 테이블의 1개의 레코드를 읽기 전용으로 보관하는 객체이다.
					
					EmpVO vo = new EmpVO(empno, ename, sal, deptno);
					// EmpVO 객체로 하나의 레코드의 값을 가지고 있는 객체를 생성한다. 
					
					list.add(vo);
					// EmpVO 객체를 리스트에 저장한다.
					
				} // while : 레코드 하나씩 빼오기
				
			} // try - with - resources
			
		} catch(Exception e) {
			throw new SQLException(e);
		} // try - catch
		
		return list;
		// EmpVO 객체를 보관하고 있는 리스트를 반환한다.
		
	} // selectAll - 모든 레코드를 반환
	
	// ====================================

} // end class

 

[ 2 - 3. Servlet 생성 ]

 

package org.zerock.myapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.zerock.myapp.domain.EmpVO;
import org.zerock.myapp.persistence.EmpDAO;

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


@Log4j2
@NoArgsConstructor

@WebServlet("/EmpAllSelect")
public class EmpAllSelectServlet 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.");
		
		// ========================================
		
		// 이 안에서 DAO 객체를 생성하여, 영속성 계층에서 데이터 획득
		
		EmpDAO dao = new EmpDAO();
		// dao가 호출하는 것은 서블릿이다.
		
		@Cleanup("clear")
		List<EmpVO> emps = null;
		
		try {
			
			emps = dao.selectAll();
			// List는 자원객체이기 때문에, 닫아줘야 한다.
			// @Cleanup은 자원객체에 맞게 ( )에 지정하여 닫아줘야 한다.
			
			emps.forEach(log::info);
			// log.debug("emps : {}", emps);
			
		} catch (SQLException e) {
			throw new IOException(e);
		} // try - catch
		
		// ========================================
		//		응답문서 준비
		// ========================================
		
		res.setContentType("text/html; charset=utf8");
		
		@Cleanup
		PrintWriter out = res.getWriter();
		
		out.println("<html><head></head><body>");
		
		out.println("<ol>");
		
		emps.forEach( vo -> {
			
			String tag = String.format("<li> %s, %s, %s, %s </li>", vo.getEmpno(), vo.getEname(), vo.getSal(), vo.getDeptno() );
			
			out.println(tag);
			
		}); // for each
		
		out.println("</ol>");
		
		out.println("</body></html>");
		
		out.flush();
		
	} // service

} // end class

 


[ 3. FrontController - 모든 요청이 이 서블릿으로 들어온다. ] (****)

 

[ 3 - 1. html 파일 준비 ]

 

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FrontController 패턴 연습</title>
</head>

<body>

    <!-- 모두 .do로 끝나고 있다. -->
    <!-- 모두 실제로는 없는 파일들이다. -->
    <a href="insert.do">저장하기</a><br>
    <a href="/08Chapter/delete.do">삭제하기</a><br>

    <!-- 아래의 a태그의 경우 8090이라는 다른 사이트로 요청을 보냈으나, 없을 경우 연결되지 못한다. -->
    <a href="http://localhost:8090/08Chapter/update.do">수정하기</a><br>

    <a href="select.do">조회하기</a><br>

    <!-- 링크는 단순히 이동하는 것이 아니라, GET 방식으로 요청을 보낸것이다. (***) -->
    <!-- <a href="https://www.naver.com/">NAVER</a><br> -->
    <!-- <a href="http://localhost:7000">NETCAT</a><br> -->

</body>

</html>

 

[ 3 - 2. FrontController Servlet 생성 ]

 

package org.zerock.myapp;

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;

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


@Log4j2
@NoArgsConstructor

@WebServlet("*.do") // 다른 서블릿이 *.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.");
		
	} // service

} // end class
728x90
댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
최근에 올라온 글
Total
Today
Yesterday
공지사항