Servlet (2일차) - 심화(Redirect & Forward)

지환·2023년 12월 1일
0

Jsp & Servlet

목록 보기
5/21
post-thumbnail

TestServlet


package com.example.demo.ch5;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.demo.dao.TestDao;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/test/testList")
public class TestServlet extends HttpServlet {
	Logger logger = LoggerFactory.getLogger(TestServlet.class);
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		logger.info("doGet");//405 -  Restful API 메소드 이름 오타발동
		TestDao tDao = new TestDao();
		Map<String,Object> pMap = new HashMap<>();
		tDao.procEmpcursor(pMap);
		logger.info(pMap.toString());
		req.setAttribute("pMap", pMap);
		//view계층 - 연결 - ReactJS, Vue.js
		RequestDispatcher view = req.getRequestDispatcher("./testList.jsp");
		view.forward(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		logger.info("doPost");
	}

}


testList

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.Map, com.google.gson.Gson" %>    
<%!
//<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
//variable rc_emp refcursor;
//exec proc_empcursor(:rc_emp);
//print rc_emp
//{  call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
//</select>
//{key=[vo,vo,vo,, ,,,,]}
	//page directive
	//디클러레이션 - 자바코드
	//전변, 메소드 선언할 수 있음
	//잊으세요
	//인스턴스화를 못함 - 플랫폼 독립적이어서 서버마다 자바클래스이름의 명명규칙이 달라서.
	//공통코드,  DB연동하는 코드, 재사용성 고려한 메소드이라면 XXX.java
%>    
<%
	//스크립틀릿 - 지변 - service()코드가 생성되니까 메소드 안에서 선언한 변수이니까
	Map<String,Object> rmap = (Map)request.getAttribute("pMap");
	Gson g = new Gson();
	String temp = g.toJson(rmap);
	//out.print(rmap);//out은 내장객체 - PrintWriter
	out.print(temp);
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사원목록 - 프로시저</title>
</head>
<body>

</body>
</html>

test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
<mapper namespace="com.mybatis.mapper.TestMapper"> 
<resultMap id="empVO" type="com.vo.EmpVO"/>
<select id="proc_login1" parameterType="java.util.Map" statementType="CALLABLE">
	{  call proc_login1(#{m_id, jdbcType=VARCHAR, mode=IN, javaType=java.lang.String}
	,#{m_pw, jdbcType=VARCHAR, mode=IN, javaType=java.lang.String}
	,#{r_msg, jdbcType=VARCHAR, mode=OUT, javaType=java.lang.String}
	)}
</select>
<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
	{  call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
</select>
 <select id="currentTime" parameterType="string" resultType="string"> 
	SELECT to_char(sysdate, 'YYYY-MM-DD') FROM dual
 </select> 
</mapper>

procEmpCursor

public void procEmpcursor(Map<String,Object> pMap) {
		//Map<String,Object> pMap = new HashMap<>();
		logger.info("procEmpcursor" + pMap);
		
		try {
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_empcursor",pMap);
			logger.info(pMap.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

소스분석

[테스트 케이스]
HTML로 폼태그나 태그를 사용하지 않고 진행하겠다.
수동으로 url 주소를 입력하고 엔터를 눌렀을 떄 발생하는 상황에 대해서 얘기하겠다.

클라이언트 사이드에서 http://localhost:8000/test/testList를 검색했을 때 나오는 결과에 대해 서버사이드 / 클라이언트는 어떤 동작을 할까?

우선 클라이언트는 서버에 요청한다.(url 주소는 http://localhost:8000/test/testList) 이 주소이며, 이 값을 응답으로 내보내는 형식은 GET방식이다. 지금 작성하고 있는 글은 RestFul Api에 대해서 작성하는게 아니기 떄문에 생략하겠다.

자, 그러면 Get방식 안에서 어떻게 동작할까? 이전 게시글에서 doGet(req-요청,res-응답)에 대해 언급했다. 요청,응답객체는 톰캣 WAS에서 객체를 주입해주고 서버사이드 쪽에서 forward방식으로 View계층에 전달한다. 그렇다면 (요청, 응답) 객체는 살아 있을까? -> Yes

필자는 마이바티스를 연동 했기 떄문에 오라클 DB에 저장되어 있는 프로시저를 호출하려고 한다.

<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
	{  call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
</select>

이 문장을 나는 이렇게 해석할 것이다.
<select>
- id가 "proc_empcursor를 호출 할떄, parameterType은 java.util.Map 타입으로 해주고
- statementType = "CALLABLE"로 지정하면, 프로시저에서 OUT으로 지정한 값들이 사용한 파라미터 map에 담긴다.
</select>

그리고 selet태그 안쪽 해석

- proc_empcursor를 불러줘 key는 내가 지정한 것이고, jdbcType은 CURSOR로 지정하고, mode는 OUT이야 
- JavaType은 sql.ResultSet : n건을 담아오는데. 그 한 건을 VO에 담는다.
- 그 resultMap은 복잡한 결과 매핑을 간편하게 만들어주는 태그이다. VO는 empVO이다.

해당 proc_empcursor는 ref커서이기 때문에 행을 리턴하는 것이 아니라, 전체 행을 반환한다.
  • 프로시저를 부를 떈 call를 한다.

  • resultMapempVO이다. (복잡한 결과 매핑을 간편하게 만들어주기 위해 만들어진 태그다.)

    • resultMap이 empVO인 것을 보아 {key = [vo,vo,vo,vo,,,]} 이런 형식으로 되어있다.
      (맵형태이고 리스트 아니다!, key는 필자가 지정했다.)
    • 왜 맵형태로 저장되는가? parameterType으로 java.util.Map으로 지정했기 때문이다.
  • ResultSet - 커서 조작에 대한 인터페이스이름-n건 그 한 건을 VO에 담는다. (단 한 건만 VO에 담는다.)
    하지만 parameterType="java.util.Map"으로 했기 때문에, Map객체로 쌓인다.

  • statementType="CALLABLE" 로 지정하면 프로시저에서 out로 지정한 값들이 proc_empcursor를 요청할때 사용한 파라미터 map에 담긴다.

  • 프로시저는 리턴이 없음 mode = OUT(내보낸다) jbdbType은 refcursor니깐


위에서 얘기한 값을 TestDao로 인스턴스화 마이바티스 객체를 받아온다.

procEmpCursor(DB)

public void procEmpcursor(Map<String,Object> pMap) {
		//Map<String,Object> pMap = new HashMap<>();
		logger.info("procEmpcursor" + pMap);
		
		try {
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_empcursor",pMap);
			logger.info(pMap.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  • TestDao로 인스턴스화해서 Session 객체를 받아온 다음
  • sqlSessionFactory는 마이바티스 전역 정보를 가지고 실행을 제어한다.
  • sqlSession을 생성한다.
  • Application당 하나만 생성하는 것을 권장한다. 이 메소드를 사용한다.

@WebServlet("/test/testList")
public class TestServlet extends HttpServlet {
	Logger logger = LoggerFactory.getLogger(TestServlet.class);
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		logger.info("doGet");//405 -  Restful API 메소드 이름 오타발동
		TestDao tDao = new TestDao();
		Map<String,Object> pMap = new HashMap<>();
		tDao.procEmpcursor(pMap);
		logger.info(pMap.toString());
		req.setAttribute("pMap", pMap);
		//view계층 - 연결 - ReactJS, Vue.js
		RequestDispatcher view = req.getRequestDispatcher("./testList.jsp");
		view.forward(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		logger.info("doPost");
	}

}
  • TestDao를 인스턴스화 해서 procEmpcursor 메소드에 담는다. (pMap)객체를 그리고 나서 요청객체에 setAttribute하면 pMap의 값을 담는다. 받을때는? 뷰 계층에서(JSP)<% %> 있는 res.getAttribute를 이용하면 된다. 그리고 view계층에 forward한다.

view(jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.Map, com.google.gson.Gson" %>    
<%!
//<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
//variable rc_emp refcursor;
//exec proc_empcursor(:rc_emp);
//print rc_emp
//{  call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
//</select>
//{key=[vo,vo,vo,, ,,,,]}
	//page directive
	//디클러레이션 - 자바코드
	//전변, 메소드 선언할 수 있음
	//잊으세요
	//인스턴스화를 못함 - 플랫폼 독립적이어서 서버마다 자바클래스이름의 명명규칙이 달라서.
	//공통코드,  DB연동하는 코드, 재사용성 고려한 메소드이라면 XXX.java
%>    
<%
	//스크립틀릿 - 지변 - service()코드가 생성되니까 메소드 안에서 선언한 변수이니까
	Map<String,Object> rmap = (Map)request.getAttribute("pMap");
	Gson g = new Gson();
	String temp = g.toJson(rmap);
	//out.print(rmap);//out은 내장객체 - PrintWriter
	out.print(temp);
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사원목록 - 프로시저</title>
</head>
<body>

</body>
</html>
  • request.getAttribute("pMap");로 맵을 받아와서 Gson형태로 변환한다.
    • java객체를 json형식으로 변환하는 것은 더욱 쉽다. gson객체의 toJson("Java 객체")메소드를 이용하면 된다.
profile
아는만큼보인다.

0개의 댓글