Jsp & Servlet : Pojo 1-3

지환·2023년 12월 9일
0

Jsp & Servlet

목록 보기
17/21
post-thumbnail

proc_list 정리

이 메모장을 킨 이유는 proc_list에 대한 이해를 돕기 위함이다.

http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울

테스트 케이스를 이와같이 가져갔다.

url 및 postman으로 해당 값을 요청한다면 서버쪽에서 .gd (url)로 인식해 해당 url를 가로챈다.

그렇게 되면, notice[0] procNoticeList[1]번방으로 가지고 있다.

그렇게되면 FrontMVC 컨트롤러에 들어가서 url를 쪼갠다음 그 배열을 담고

		if("notice".equals(upmu[0])) {
			req.setAttribute("upmu",upmu);
			//개선점
			// 메소드마다 url패턴을 적용할 수 없어서 if문으로 처리하였다.
			// 스프링: 메소드마다 매핑을 지원하는 @이 지원되고 있다. 이것과 비교 했을 떄 별로이다.
			af = nc.execute(req, res);
		}

req.setAttribute를 통해 HttpServlet으로부터 상속받은 (req,res)-WAS가 주입해줌

이 부분을 유심히 봐야된다.

setAttribute로 배열을 noticeController에 넘겨주고

noticeController에서 모델계층(순수자바)랑 대화하는 부분이다. 이 떄 중요한 부분은

HashMapBinder라는 공통코드를 만들어서 사용자가 입력한 값을 req로써 받아온다.

즉 gubun=n_title&keyword=겨울 이렇게 입력한 gubun(key) = n_title(value) || keyword(key) = 겨울(value)

담아오는 과정을 살펴보자.


Map<String,Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);

hmb에 req를 넣는것을 볼 수 있다. req의 주인은 누구인가? - FrontMVC이다.
req에 저장된 {키:벨류} 값들을 HashMapBinder를 통하면 hmb에는 주입된 객체가 생성된다.


	public void bind(Map<String,Object> pMap)
	{
		pMap.clear(); // map을 비워주는 역할
		logger.info("HashMapBinder bind 메소드 입구입니다.");
		Enumeration<String> em = req.getParameterNames();
		// 담을 걸 꺼내기 
		while(em.hasMoreElements())
		{ 
			String key = em.nextElement();
			pMap.put(key,req.getParameter(key));
		}
		

em에 req.getParameterNames()를 허게되면 <input name=""> name값을 모두 받아온다.

여기서 name은 무엇인가? key값을 의미한다. 그 Key값을 em에 저장하고

while문을 돌면서 key값을 받고 pMap.put(key,req.getParameter("ket"));

이렇게 하게 되면 pMap에 url로 받아온 값을 pMap형태로 저장한것이다.

doGet 방식은 -> 쿼리스트링으로 전달이 되고

doPost 방식은 -> 바디에 포함되어 보내진다.

지금 Proc_list는 doGet방식이다.

그래서 http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울 이렇게 쿼리스트링으로 전달했다.

이렇게 사용자로부터 값을 입력받은 값을


HashMapBinder hmb = new HashMapBinder(req);

이렇게 req객체를 주입함으로써 사용자로부터 입력받은 내용을 저장하는 hmb변수를 생성하는것을 볼 수 있다.

이 때 까지는 bind메소드를 사용하기 위해서 인스턴스화만 진행했다. 하지만 생성자로 req를 받고 

이걸 생성자로 처리한 부분이 중요하다(아이디어)

생성자로 받아오기 이전에 먼저 HttpServletRequest req = null이라는 전역변수를 먼저 선언해줘야한다.


HttpServletRequest req = null; 

이 부분에서 req를 받아오기 위해서 HttpServletRequest를 생각해야된다는점이 중요하다. Enumaration은 어떻게 동작하는가? 
Enumeration<String>으로 선언햇는데 req.getParameterNames으로 키값을 받아온다.

다음은 noticeController에서 어디로 전달되는것인가?

-> notice[0] procNoticeList[1]번방이다.

-> procNoticeList.equals(upmu[1]){} 조건 분기에 걸린다.

-> hmb.bind(pMap)을 왜 여기자리에서 했을까?

  • hmb.bind(pMap)을 하게되면 사용자가 입력한 값을 pMap이 쥔(put) 상태이다.
  • 이 상태이면 pMap은 bind함수로 인해 사용자의 값을 쥐고 있고,

증명해봐?

[HashMapBinder.java : 33]{{keyword=겨울, gubun=n_title}}
[NoticeController.java : 94]{{keyword=겨울, gubun=n_title}}

로그를 찍어봤다.

-> 이렇게 값을 쥐고 있는다는 점을 알 수 있다.

그런 다음 nList = nLogic.procNoticeList("pMap(사용자의 입력이 저장된 값");를 넣는다.

-> pMap을 파라미터로 넣어주게 되고

이제 nLogic부분 처리로 들어간다.


nLogic proc_List메소드로 들어온다.

  • pMap (맵으로 받고), 그 맵을 sqlSession.selectList("마이바티스 id",pMap)으로 넣었다. 이렇게 넣어주면, pMap에 커서값이 매핑되어 리턴된다.

잘 매핑되었는지 확인해보자.


sqlSession.selectList("proc_noticeList",pMap); 

logger.info(pMap.toString());

==============================
{{keyword=겨울, key=[NoticeVO(n_no=1, n_title=휴관일, n_content=이번주 일요일은 휴관일입니다., n_writer=관리자), NoticeVO(n_no=0, n_title=겨울방학이벤트
, n_content=133만원 겨울방학이벤트
, n_writer=관리자), NoticeVO(n_no=2, n_title=겨울방학이벤트
, n_content=233만원 겨울방학이벤트
, n_writer=관리자)], gubun=n_title}}

이렇게 map형태로 잘 받아온다. map 형태는 {key=value}를 일컫는 말이다.

이 결과값이 nList.add(pMap)을 하지 않아도 return값으로 잘 받아오네?

그렇다면 한 번 해보자.

```java
public List<Map<String,Object>> procNoticeList(Map<String,Object> pMap){
		logger.info("NoticeLogic클래스 안 procNoticeList에 들어왔습니다.");
		List<Map<String,Object>> nList = new ArrayList<>();
		sqlSessionFactory = MyBatisCommonFactory.getSqlSessionFactory();
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			sqlSession.selectList("proc_noticeList",pMap); // pMap에 담기는데 앞에 xml key값에 key값이 담긴다.
			nList.add(pMap);
			logger.info(nList.toString());
			logger.info(pMap.toString());
		} catch (Exception e) {
			logger.info(e.toString());
		}
		return nList;
	}
  • nList.add(pMap);를 진행했는데, MyBatis에서 자동으로 nList에 매핑해주는걸 볼 수 있다.

  • 결과값으로

{[{keyword=겨울, key=[NoticeVO(n_no=1, n_title=휴관일, n_content=이번주 일요일은 휴관일입니다., n_writer=관리자), NoticeVO(n_no=0, n_title=겨울방학이벤트
, n_content=1년 33만원 겨울방학이벤트
, n_writer=관리자), NoticeVO(n_no=2, n_title=겨울방학이벤트
, n_content=2년 33만원 겨울방학이벤트
, n_writer=관리자)], gubun=n_title}]}

==================List=============================

==================pMap================================
{{keyword=겨울, key=[NoticeVO(n_no=1, n_title=휴관일, n_content=이번주 일요일은 휴관일입니다., n_writer=관리자), NoticeVO(n_no=0, n_title=겨울방학이벤트
, n_content=1년 33만원 겨울방학이벤트
, n_writer=관리자), NoticeVO(n_no=2, n_title=겨울방학이벤트
, n_content=2년 33만원 겨울방학이벤트
, n_writer=관리자)], gubun=n_title}}

1,2번 차이점을보자. 밑은 맵 형태로 저장되어있지만, 1번을 보면 [] 배열로 한 번 더 감싼 것을 볼 수 있다. 이 말은 nList.add(pMap); 이 부분이다.

밑 xml을 하나씩 분리해보자.

	<select id ="proc_noticeList" parameterType="Map" statementType="CALLABLE">
	{ call proc_noticeList(#{key, jdbcType =CURSOR, mode = OUT, javaType = java.sql.ResultSet, resultMap=ntVO})}
	</select>
  • proc_noticeList: 오라클에서 프로시저를 불러오기위해 사용한다.

  • mode = out : (내보내기 위해서)

  • resultMap = ntVO 라는 말은 결과 타입으로 ntVO에 담는다는 얘기다. 그렇다면 오라클과 통신시 ntVO의 타입, 이름 전부 같아야한다.

  • CURSOR기 때문에, N건으로 리턴하기 때문에 그 값을 받아오려면 selectList로 받아오는게 맞다.

  • 그 출력된 값을 타입이 ntVO로 받는데, 저장방식이 map이라는 얘기다.

  • 그 n건을 받아서 nList에 저장한 뒤, 리턴한다.

근데 우리는 url로 준 조건을 확인해보자.

http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울 인데

이 조건에 맞는 결과가 출력 됐는가 ?

대답은 No

커서로 n개의 행을 받았다. 전체조회로 전부 받아온 것이다.

즉, 해당 쿼리스트링은 동작하지 않았고, 실질적인 오라클의 코드 동작으로 이 부분이 실행된것이다.

구체적으로 살펴보자.

CREATE OR REPLACE PROCEDURE SCOTT.proc_noticeList(rc_notice OUT SYS_REFCURSOR) IS
BEGIN
    OPEN rc_notice FOR
        SELECT n_no, n_title, n_content, n_writer FROM notice;
END;
/

이 부분이 실행되서 저렇게 리턴 된 것이다.

그러면 이 값을 retrun 한 nList를 가지고 req.setAttribute("nList", nList); 를 하게 되면 nList는 jsp 로 보내지고 화면에 뿌려진다.

af 관점으로 보자.
af

  • path : /notice/noticeprocNoticeList.jsp 가 저장된다.
  • isRedirect : false -> Forward로 진행한다.

다시 FrontMVC -> af = nc.execute로 돌아와서 isRedirect false인 foward를 진행한다.

forward를 진행하면 url은 그대로이고 페이지만 변경되니

url은 http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울 인데,

실질적인 페이지 처리는 /notice/noticeprocNoticeList.jsp가 됐다.


정리

http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울 로 요청

쿼리스트링 이후 부분은 어차피 pMap에 저장되는 부분 (req)

pMap엔 {gubun : n_title , keyword : 겨울}로 매핑된다.

이렇게 저장된 pMap를 마이바티스로 넘겨준다. -> 그렇게 되면 이 부분에 대한 리턴을 받게 되는데

REFCURSOR이고 n건에 대한 출력값을 받는다. 하지만 마이바티스를 보면, 조건문에 대한 처리는 되어 있지 않다.

실질적인 실행은 오라클 커서로 진행됐다는 점을 알 수 있다.

그리고 nList를 리턴받은 것을 토대로 다시 af에 값을 넣어준다음에 포워드를 진행하는데

url은 http://localhost:8000/notice/procNoticeList.gd?gubun=n_title&keyword=겨울 그대로 이지만,

실행은 /notice/noticeprocNoticeList.jsp가 진행된다.


Detail 정리

Test Case : http://localhost:8000/notice/noticeDetail.gd?n_no=1

우선 이렇게 실행한다면,

FrontMVC -> NoticeController -> HashMapBinder(pMap{n_no : 1}) -> nLogic.noticeList(pMap) -> pMap에대한 xml 마이바티스 실행

-> nList 리턴 한 값으로 다시 NoticeController로 돌아와서 path : /notice/noticeDetail.jsp + isRedirect = false 담는다.

-> frontMVC로 와서 Forward 실행한다. 이 부분에서 중요한데, url은 그대로이고 페이지 내용은 바뀐다.

그래서 url은 http://localhost:8000/notice/noticeDetail.gd?n_no=1

페이지 처리는 path : /notice/noticeDetail.jsp 했다.

디비적인 부분으로 관찰해보자.

Detail에 대해 따로 구현되어있는가?

따로 구현되어있지 않다. 왜그럴까?

<select id="noticeList" parameterType="map"  resultType = "map">
		select n_no, n_title, n_content, n_writer from notice
		<where>
			<if test="n_no!=null">AND n_no=#{n_no}</if>
			<if test="gubun!=null">
				<choose>
					<when test='gubun.equals("n_title")'>
						AND n_title LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_content")'>
						AND n_content LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_writer")'>
						AND n_writer LIKE '%'||#{keyword}||'%'
					</when>
				</choose>
			</if>
		</where>
	</select> 

이 부분 때문이다. if문 안쪽에 n_no가 null이 아닐 떄, 실행된다. 즉, n_no가 1이기 떄문에 해당 조건은 충족되고, n_no = 부분이 실행된다.

gubun 부분은 null값이라 조건을 실행하지 않는다.

이렇듯 마이바티스를 통해서 조건을 동적으로 처리할 수 있다. 즉, 어떤 값을 넣어주느냐에 따라 처리결과가 바뀌게된다.

일반적인 오라클로 처리했다면, 디테일에대한 메소드를 만들어서 처리했겠지만, 마이바티스를 통해서 정말 간단하게 구현했다.

정리해서 실질적으로 실행되는 부분은

select n_no, n_title, n_content, n_writer from notice
AND n_no=#{n_no}

이 부분이 실행된다.

req.setAttribute로 jsp에 뿌려서 확인해보자.

잘 실행 되었다.


화면

공지사항 처리를 화면으로 표현하면서 생각해보자.

noticeList Case

이와 같이 Default로 뿌려지는 값은 어떻게 확인이 가능한가?

로직을 따라가보자.

url : localhost:8000/notice/noticeList.gd 엔터 치는 순간

FrontMVC -> NoticeController -> HashMapBinder(req)

이 때 req엔 값이 담겨 있을까? 지금은 Get방식이기 때문에 쿼리스트링으로 넘겨주는 값이 없어서 아무것도 담겨 있지 않다.

그렇다면 HashMapBinder에도 결과적으로 처리되는 값은 아무것도 없다는 것을 뜻한다.

그러면 비어있는 pMap를 noticeList메소드에 넘겨주게 되고

noticeList(pMap)

nList = sqlSession.selectList("noticeList",pMap);

이렇게 빈 값을 MyBatis에 넘겨준다.

	<select id="noticeList" parameterType="map"  resultType = "map">
		select n_no, n_title, n_content, n_writer from notice
		<where>
			<if test="n_no!=null">AND n_no=#{n_no}</if>
			<if test="gubun!=null">
				<choose>
					<when test='gubun.equals("n_title")'>
						AND n_title LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_content")'>
						AND n_content LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_writer")'>
						AND n_writer LIKE '%'||#{keyword}||'%'
					</when>
				</choose>
			</if>
		</where>
	</select> 

이렇게 가게되고 지금은 if 조건절에 있는 n_no / gubun 값이 없기 때문에 이 부분은 실행하지 않는다.

결과적으로

<select id="noticeList" parameterType="map"  resultType = "map">
		select n_no, n_title, n_content, n_writer from notice

이 부분이 실행되어 디폴트 값으로 화면에 그려진다.

앞에서 보여줬던 그림이다.

자, 이제 조건검색을 했다고 가정해보자.

조건검색

분류선택에는 제목, 작성자, 내용이 있다.

이 태그들이 의미하는 바가

		    	<select id="gubun" class="form-select" aria-label="분류선택">
		      		<option value="none">분류선택</option>
		      		<option value="n_title">제목</option>
		      		<option value="n_writer">작성자</option>
		      		<option value="n_content">내용</option>
		    	</select>	

			<div class="col-6">
		 		<input type="text" id="keyword" class="form-control" placeholder="검색어를 입력하세요" 
		           aria-label="검색어를 입력하세요" aria-describedby="btn_search" onkeyup="searchEnter()"/>
			</div>
			<div class="col-3">
		 		<button id="btn_search" class="btn btn-danger" onClick="noticeSearch()">검색</button>
		 	</div>
		</div>	

	

이렇게된다. 1:1 매핑되는 것이다.

이 얘기를 왜 꺼내냐면, 이 값들이 url로 들어가기 떄문이다.

예를 들어 제목, 휴관일을 입력한다면,

{gubun : n_title , keyword : 휴관일}이 매핑되어 url에 들어간다. 이 값들은 마이바티스에서도 사용되니 알고 있어야된다.

어떻게 마이바티스와 연동이 되는가? 검색 버튼을 눌렀을 때 url이 이동하도록 처리하면 된다.

자바스크립트로 처리되는 부분을 보자.

우리가 검색을 눌렀을 떄,

    <script type = "text/javascript">
    
    function searchEnter(){
    	console.log("searchEnter");
    }

		function noticeSearch(){
			console.log("noticeSearch");
			const gubun = document.querySelector("#gubun").value;
			const keyword = document.querySelector("#keyword").value;
			console.log(`${gubun}, ${keyword}`)
			location.href="/notice/noticeList.gd?gubun="+gubun+"&keyword="+keyword;

		}    
    </script>

gubun.value + keyword값들을 value로 가져와서 --> location.href = "/notice/noticeList.gd?gubun=n_title&keyword =휴관이 들어간 상태로 url이 바뀐다.

이것이 의미하는바가 큰데, url이 바뀐다는 것은 기존의 요청은 끊어지고 새로운 요청이 받아진다는 사실이다.

즉,

http://localhost:8000/notice/noticeList.gd?gubun=n_title&keyword=휴관로 들어가면,

pMap에 {gubun:n_title , keyword : 휴관}으로 매핑되어 마이바티스 키&벨류 값으로 매핑된다.

	<select id="noticeList" parameterType="map"  resultType = "map">
		select n_no, n_title, n_content, n_writer from notice
		<where>
			<if test="n_no!=null">AND n_no=#{n_no}</if>
			<if test="gubun!=null">
				<choose>
					<when test='gubun.equals("n_title")'>
						AND n_title LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_content")'>
						AND n_content LIKE '%'||#{keyword}||'%'
					</when>
					<when test='gubun.equals("n_writer")'>
						AND n_writer LIKE '%'||#{keyword}||'%'
					</when>
				</choose>
			</if>
		</where>
	</select> 

이번은 어떠한가? 이번엔 gubun이 실행된다.

gubun이 n_title인 경우에 -> (choose -> when 'gobun.equals('n_title')) 이 조건에 걸리고 키워드에 휴관이 들어가게 되어

휴관에 관련된 모든 정보가 조회된다.

그리고 나서 이 정보를 nList에 담고 -> 다시 FrontMVC로 가서 Forward를 진행한다.

이 떄, path는 /notice/noticeList.jsp 이지만, forward 방식이기 때문에

http://localhost:8000/notice/noticeList.gd?gubun=n_title&keyword=휴관 이 url은 고정하고

실제 출력은 /notice/noticeList.jsp게 된다.

profile
아는만큼보인다.

1개의 댓글

comment-user-thumbnail
2023년 12월 14일

2023-12-14(3회독(

답글 달기