Jsp & Servlet : Pojo3-2

지환·2023년 12월 16일
0

Jsp & Servlet

목록 보기
21/21
post-thumbnail

이번 시간에는 Test Case로 동작원리를 살펴보려고 한다.

전체적인 틀을 먼저 살펴보겠다.

Test Case 1)

http://localhost:8000/board2/boardDetail.gd3?b_no=5
이 url로 조건분기를 진행하겠다.

동작원리

1. ActionSupport

@WebServlet("*.gd3")

.gd3을 가로채서 ActionSupport 서블릿에서 진행한다.

  • upmu = command.split("/");

  • 이전에는 String reulst = null로 받았지만, 모든 Case를 받기 위해서 Object로 변경되었다.

  • 그렇다면 object로 뭘 받는것일까?

[Pojo2 - ActionServlet] 코드

		if("board".equals(upmu[0])) {
			logger.info("workname - board - execute호출");
			req.setAttribute("upmu",upmu);
			result = controller.execute(req, res);
		}

[Pojo3 - ActionSupport]

obj = HandlerMapping.getController(upmu, req, res);
  
  1. HandlerMapping클래스인 getController 메소드를 실행시킨다.
  2. 중요한 부분은 파라미터로 넘어가는 값이 upmu / req / res 라는 점이다.
  • req.setAttribute를 사용하지 않고 upmu 배열을 넘겼다. (메소드를 통해서)
  • obj는 HandlerMapping.getController 부분을 수행할 때 까지 Stop

HandlerMapping

  • 큰 골자부터 살펴보자. HandlerMapping클래스를 왜 설계했는가?

  • 직관적이지 않던, if문을 전담하는 클래스를 따로 구성한다.

  • 컨트롤클래스(board2Controller)에서 기존에 if문으로 분기처리 되었던 부분을 -> 메소드로 변경해보기 위해서.

    • 그렇다면 왜 req,res를 지원하는 메소드여야 하는가?

    • HashMapBinder , Logic 처리 ModelAndView에 전부 이 값이 필요하기 떄문이다.

  • 이 부분에서 중간 경유지 와 같은 역할을 한다.

if("board2".equals(upmu[0])) {
	    else if("boardDetail".equals(upmu[1])) {
			obj = controller.boardDetail(req, res);
			if(obj instanceof ModelAndView) {
				return (ModelAndView)obj;
	}

}
  1. "board2".equals(upmu[0]) 해당 조건에 걸리게 되어 조건문 스코프 안으로 들어간다.

    • upmu[0] 배열은 어떻게 사용할 수 있는가? 파라미터를 통해 원본값을 넘겨받았으니 사용가능하다.

    • obj = controller.boardDetail(req, res); 이 부분을 통해서 req,res를 넘겨준다.(원본값)

  2. 그 때, obj는 controller.boardDetail이 끝날 떄 까지 stop한다.

Board2Controller

	@Override
	public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
		logger.info("boardDetail");
		List<Map<String ,Object>> bList = null;//nList.size()=1
		// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
		//select * from notice where n_no=5;
		HashMapBinder hmb = new HashMapBinder(req);
		hmb.bind(pMap);
		bList = bLogic.boardDetail(pMap);
		logger.info(bList);
		ModelAndView mav = new ModelAndView(req);//WEB-INF/jsp/[[[board2/boardDetail]]].jsp
		mav.addObject("bList", bList);
		mav.setViewName("board2/boardDetail");
		return mav;
	}

전체적인 골자는 Select[전체조회] 일 떄와, 상세조회일때, 응답페이지가 달라지기 때문에 메소드를 분리하여 설계했다.Select일 경우에는 배포위치가 WebApp이기 때문에 경로를 처리하는 부분이 달라진다.
path = "forward:board/boardList";

Detail인 경우에는 WEB-INF일때
/WEB-INF/jsp/workname/xxxxx[메소드이름] or upmu[1].jsp 로 설정한다.

  • 이 또한 메소드 파라미터를 통해서 원본값 (req,res)를 받는다.

  • HashMapBinder hmb = new HashMapBinder(req)를 통해서 사용자가 입력 한 값에 대한 Map<>처리는 완료

  • bLogic으로 갔다가 Dao 처리 후 Oracle 값을 받고 돌아온 값이 bList에 담겨있다.

  • Detail인 경우 경로처리가 달라지기 때문에, ModelAndView클래스를 설계하여 사용할 것이다.

다음은 ModelAndView 클래스를 굳이 왜 사용할까?

[Pojo2 코드]

else if("boardDetail".equals(upmu[1])) {
			logger.info("boardDetail");
			List<Map<String ,Object>> bList = null;//nList.size()=1
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			//select * from notice where n_no=5;
			hmb.bind(pMap);
			bList = bLogic.boardList(pMap);
			//원본에다가 담아 두자
			req.setAttribute("bList", bList);
			path="forward:/board/boardDetail.jsp";
		}
  • req.setAttribute를 사용하지 않고, 데이터만 저장한다. 그리고 이후에 path를 경로 설정하여 처리하지만,

  • ModelAndView는 데이터와 이동하고자 하는 ViewPage를 같이 저장한다.

    • xxx Controller에서 if문으로 분기하고 있는데, 이 부분을 해결하기 위해서 HandlerMapping을 구현했다.

    • xxxController는 서블릿이 아니다. Controller 인터페이스의 추상메소드로 구현 된 것 뿐이다. --> 실질적인 주인은 ActionServelt

    • Select로 n건을 조회하고 싶을 떈, (List<Map>)으로 받아야함.

        ModelAndView mav = new ModelAndView(req);//WEB-INF/jsp/[[[board2/boardDetail]]].jsp
		mav.addObject("bList", bList);
		mav.setViewName("board2/boardDetail");

여기서 ModelAndView를 인스턴스화한다.

  1. req를 넘겨주는 이유는 무엇일까?

    • req는 사용자에 대한 정보를 저장하고 있다.(form)

    • 그 값을 ModelAndView클래스로 넘겨주고 생성자로 받게 하면 req는 원본값을 유지한다.

    • mav.addObject를 하게 되면, ModelAndView 클래스안에 메소드로 정의되어 있는 addObject를 통해서 bList(오라클로부터 받은 사용자에 대한 디비정보)를 넘겨주게 된다.

ModelAndView

	HttpServletRequest req = null;//나는 어디서 주입을 받게 되나요?
	List<Map<String,Object>> list = new ArrayList<>();
	//컨트롤 클래스에서 결정된 화면의 이름을  담을 변수 선언
	String viewName = null;

	public ModelAndView(HttpServletRequest req) {
		this.req = req;
	}

생성자로부터 값을 유지하고

String viewName으로 경로에 대한 값을 저장할 수 있는 전역변수를 선언했다.

그리고
[Board2Controller.java]

		ModelAndView mav = new ModelAndView(req);//WEB-INF/jsp/[[[board2/boardDetail]]].jsp
		mav.addObject("bList", bList);
		mav.setViewName("board2/boardDetail");
		return mav;

[ModelAndView.java]

	HttpServletRequest req = null;//나는 어디서 주입을 받게 되나요?
	List<Map<String,Object>> list = new ArrayList<>();
	//컨트롤 클래스에서 결정된 화면의 이름을  담을 변수 선언
	String viewName = null;
    
public void addObject(String name, Object obj) {

		Map<String,Object> pMap = new HashMap<>();
		pMap.put(name, obj);
		req.setAttribute(name, obj);
		list.add(pMap);
	}
  • [Board2Controller]에서 mav.addObject("bList",bList);를 통해서 ModelAndView - addObejct(name,obj)메소드 인자로 전달한다.

  • addObejct("bList",bList)를 받게되고 이 값을 Map 객체를 생성하여 list.add(name,obj)로 list에 담는다.

    • 여기서 N건을 넘기고 싶다면 req.setAttribute(name,list)넘기면 된다.

    • 한 건을 넘기고 싶다면, req.setAttribute(name,obj)를 넘거야 된다.

Board2Controller

다시 돌아와서

		ModelAndView mav = new ModelAndView(req);//WEB-INF/jsp/[[[board2/boardDetail]]].jsp
		mav.addObject("bList", bList);
		mav.setViewName("board2/boardDetail");

나머지 이 부분을 수행하는데, mav.addObject("bList",bList);를 하면

  • bList : 사용자에 대한 디비정보를 쥐고 있다. 그것을 ModelAndView 클래스 addObject 메소드에 파라미터로 넘겨서 값을 "저장"한다.

    • 같은말은 ? = addObject메소드에서 (String name, Object obj)는 KeyValue 값이다.

    • 그 값을 ModelAndView에서 전역변수 list로 관리한다.

    • req.setAttribute(name,obj)를 하게 된다면, 파라미터 기준 req.setAttribute("bList",bList)를 넘긴다.

    • req.getAttribute로 로 받아, 이 값을 jsp에서 사용가능하다.

위에 적혀있는 코드를 수행하고 나면 return으로 mvn을 하게된다. 이 떄, mvn에는 "경로"와 "list" 객체를 쥐고 있다.

그리고 다시 mav를 쥐고 있는 값 return을 통해서

handlerMapping으로 가고

			else if("boardDetail".equals(upmu[1])) {
				obj = controller.boardDetail(req, res);
				if(obj instanceof ModelAndView) {
					return (ModelAndView)obj;
				}
				else if(obj instanceof String) {
					return (String)obj;
				}
			}//////////end of boardDetail메소드 호출
  • obj = mav를 쥐고있다. (경로 + list)를 갖고있다.

    • 한 번 더 조건을 분기하여 만약에 타입이 ModelAndView라면 return obj를 한다.

ActionSupport

object & PaveMove 슬라이싱

obj = HandlerMapping.getController(upmu, req, res);

다시 여기로 돌아와서 obj = mav를 갖고있다. + 타입(ModelAndView)이다.

지금부터 다른 클래스를 호출하는 것은 끝났다. 이젠, 경로 설정 및 object를 슬라이싱해서 어떤 jsp를 호출할지, 정하는 부분이다.

이 부분을 정할 때, "타입"으로 Object를 나눠서 처리한다.

앞쪽에 위치한 부분은 "단순히 paveMove 배열에 값을 넣기위한 조건 분기이다." 핵심을 잃지말자.

타입을 살펴보면, 현재까지

  1. String : Json, image(avatar.png) , path

  2. ModelAndView : Detail

  3. byte[] : Quill editor (마임타입이 enctype)

이렇게 큰 골자로 조건을 분기한다.

여기서 괄목해야 될 부분은 String이다.

  1. 만약에 "타입"이 String일 경우에는 총 3가지로 분기된다.
    • Contains(":") : forward, sendRedirect

      • 콜론을 가지고 분기하여 paveMove[]에 값을 담는다.

        • paveMove[0] : (forward/sendRedirect)✅

        • paveMove[1] : board2/boardList

    • Contains("/") : WEB-INF

      • WEB-INF인 경우에는 경로설정 자체를 board/boardList 이런식으로 앞에 (:)이 붙지 않게 되어 있다.(설계를 그렇게 함)

      • "/"기준으로 paveMove에 값을 담게된다.

        • paveMove[0] : board
        • paveMove[1] : boardList
    • else (Json/파일이름)

      • 파일이름 같은 경우이기 때문에,

        • paveMove : new String[1];

          • paveMove[0] = obj.toString();

정리하면,

  • ((forward,sendRedirect)✅ , board/boardList) : forward,sendRedirect인경우

  • (board,boardList) : WEB-INF

  • (avatar.png) : else


  1. 타입이 ModelAndView인 경우(Detail)인 경우

    • WEB-INF로 처리해야되기 때문에

    • mav = (ModelAndView)obj를 받는다.(list,viewName)를 쥐고 있다.

    • pageMove[0] = ""

    • pageMove[1] = "board2/boardDetail"


이렇게 나오게 된다.

정리하면, ModelAndView인 경우에는 무조건 ("","board2/boardDetail")로 담기게 된다.


  1. 타입 자체가 "byte[]" 인 경우에는 Quill Editor로 처리 할 경우를 생각해 붙였음 (리액트와 연동 시 필요함)

paveMove 경로처리

  1. 항상 NullPointException을 예방하기 위해서 방어적인 프로그래밍을 해야한다. 그렇기 떄문에 if문으로 Null체크를 반드시 해주고,

  2. 앞에선 pageMove에 대한 조건 분기를 "

[pojo2 - ActionServlet]

			  if("redirect".equals(pageMove[0])) {
					res.sendRedirect(path);//board/boardList.jsp
					//forward 처리시와 동일한 컨벤션을 적용하기 위해서 접두어와 접미어를 붙이는 과정에서 오류가 발동함.
					//현재 구조상 입력|수정|삭제 모두 처리 성공시 목록페이지로 응답이 나가도록 설계가 되어 있다는 것을 간과함
					//res.sendRedirect("/"+path+".jsp");//board/boardList.gd2.jsp
				}//end of sendRedirect
				else if("forward".equals(pageMove[0])) {
					RequestDispatcher view = req.getRequestDispatcher("/"+path+".jsp");
					view.forward(req, res);
				}//end of forward
				else {//콜론이 없는 경우에 실행되는 코드임 - WEB-INF
					path = pageMove[0] +"/"+pageMove[1];//board/boardList
					// /WEB-INF/jsp/board/boardList.jsp -> spring ViewResolver
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");
					view.forward(req, res);	

이렇게 진행했지만,

지금은

[ActionSupport]

           if(pageMove !=null && pageMove.length == 2) {
				logger.info("pageMove 원소의 갯수가 2개 일 때");
				String path = pageMove[1];
				if("redirect".equals(pageMove[0])) {
					res.sendRedirect(path);
				}else if("forward".equals(pageMove[0])) {
					RequestDispatcher view = req.getRequestDispatcher(path);
					view.forward(req, res);
				}else {
					// /WEB-INF/jsp/board/boardList.jsp -> spring ViewResolver
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");
					view.forward(req, res);										
				}
			}///////////////////end of if
			else if(pageMove !=null && pageMove.length == 1) {//quill editor 이미지 선택시 파일이릌반환
				res.setContentType("text/plain;charset=utf-8");
				PrintWriter out = res.getWriter();
				out.print(obj);
				return;
			}
			//JSON포맷으로 반환되는 값을 출력하기 - @ResponseBody, @RestController역할 재현
			else {
				res.setContentType("text/plain;charset=utf-8");
				PrintWriter out = res.getWriter();
				out.print(obj);
				return;				
			}
  • pageMove의 길이가 2일 땐,(pageMove.length == 2) 3가지 경우밖에 없다.

    • redirect

    • forward

    • WEB-INF(else)

  • pageMove의 길이가 1일 땐, pageMove.length == 1

    • 이미지 선택 시 파일인 경우이다.
  • else(paveMove의 길이가 1,2 둘다 아닐 떈)

    • JSON포맷으로 반환되는 값을 출력한다.

하지만 http://localhost:8000/board2/boardDetail.gd3?b_no=6 인 경우에는

pageMove("", board2/boardDetail)

그렇게 되면 forward방식으로 url은 고정이며, 페이지에 대한 처리는 완료되어 나타난다.

			if(pageMove !=null && pageMove.length == 2) {
				logger.info("pageMove 원소의 갯수가 2개 일 때");
				String path = pageMove[1];
				if("redirect".equals(pageMove[0])) {
					res.sendRedirect(path);
				}else if("forward".equals(pageMove[0])) {
					RequestDispatcher view = req.getRequestDispatcher(path);
					view.forward(req, res);
				}else {
					logger.info("여길타야되는거아닌가?");
					// /WEB-INF/jsp/board/boardList.jsp -> spring ViewResolver
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");
					view.forward(req, res);										
				}
			}///////////////////end of if

이 부분에서 else부분을 실행했다.


정리

Pojo1 -AcionForward에선

  • private String path

  • private String isRedirct
    로 경로와 forward와 sendRedirect를 관리했고,

Pojo2 - ActionForward를 없애고, String으로 받았다.

  • 제한적인 ActionForward가 아닌, 대부분의 모든 마임타입을 받을 수 있는 String으로 타입을 바꿨다.

Pojo3 - ModelAndView 생성 및 HandelrMapping 생성

  • obj로 리턴타입에 대한 제한적인 부분을 해결했고, ModelAndView클래스를 생성해서 Detail인 경우에 객체 공유 및 WEB-INF에 대한 경우의수를 추가하여 접근에 대한 문제를 해결했다.

  • if문에 대한 분기처리도(컨트롤러) 메소드로 해결함.


req, res Depth


if문 분기 전체로직

profile
아는만큼보인다.

0개의 댓글