국비교육(22-23)

30일차(1)/jsp(9) : Request Scope

서리/Seori 2022. 11. 18. 13:03

30일차(1)/jsp(9) : Request Scope

 

새 다이나믹 웹프로젝트 생성 : Step03_Scope

 

index.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/index.jsp</title>
</head>
<body>
	<div class="container">
		<h1>인덱스 페이지입니다.</h1>
		<ul>
			<li><a href="${pageContext.request.contextPath }/fortune">오늘의 운세</a></li>
			<li><a href="${pageContext.request.contextPath }/member">회원 한명의 정보 보기</a></li>
			<li><a href="${pageContext.request.contextPath }/friend/list">친구 목록 보기</a></li>
		</ul>
	</div>
</body>
</html>

 

 

test.servlet 패키지에 <FortuneServlet>

package test.servlet;

import java.io.IOException;

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

@WebServlet("/fortune")
public class FortuneServlet extends HttpServlet{
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//오늘의 운세를 얻어오는 비즈니스 로직을 수행
		String fortune="동쪽으로 가면 귀인을 만나요!";
		
		//응답에 필요한 데이터를 request scope(HttpServletRequest 객체)에 담는다.
		req.setAttribute("fortuneToday", fortune);
		
		//응답을 여기서 하지 않고 jsp페이지로 응답을 위임할 수 있다.
		RequestDispatcher rd=req.getRequestDispatcher("/test/fortune.jsp");
		rd.forward(req, resp);
	}
}

 

 

/test/ <fortune.jsp> 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//request scope에 "fortuneToday"라는 키값으로 저장된 값을 읽어와서
	//원래 타입으로 casting해서 변수에 대입하기
	String fortuneToday=(String)request.getAttribute("fortuneToday");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/test/fortune.jsp</title>
</head>
<body>
	<p> 오늘의 운세: <strong><%=fortuneToday %></strong></p>
</body>
</html>

 

- fortune의 비즈니스 로직을 수행하고 응답을 하면 되는데,
 응답을 servlet 페이지에서 하지 않고 jsp페이지에 위임시킬 수가 있다.

 

- 운세부분(로직)은 servlet에서 하고, 응답은 jsp페이지에서 한다면

  응답에 필요한 데이터는 어떻게 전달할 것인가?
1) 응답을 위임하는 방법은?
2) 얻어온 오늘의운세를 어떻게 전달할 수 있을까?

 

- req 변수 안의 getRequestDispatcher 메소드를 사용!

- 경로를 변수로 받고 메소드 요청 전달자(request dispatcher) 객체를 리턴해준다.

 응답을 위임하는데 필요한 객체이다.

 

- java는 모든작업에서 객체를 활용한다. 각각의 객체를 어떻게 잘 사용하느냐가 java를 배우는 관건!

 

- RequestDispacher 타입 변수의 forward 메소드에 매개로 두개의 값을 전달해주어야 한다.(req, resp)

 

 

- HttpServletRequest 설명 : 링크

 

- 서비스 메소드의 req는 HttpServletRequest이지만, ServletRequest가 HttpServletRequest의 부모타입이다.

- 자식타입을 부모타입에 담는 것이기 때문에 가능!

 

RequestDispatcher rd=req.getRequestDispatcher("응답할 jsp 경로");

rd.forward(req, resp);

- 최초 servlet이 전달받았던 리퀘스트와, req, resp 객체를 jsp에도 전달해주겠다는 의미.

- 위임받을 jsp페이지를 넣어두고, req, resp 값을 전달하면 응답이 위임된다.

- 요청전달자 객체의 참조값을 얻어와서 forward객체를 호출해야 한다.

 

- 클라이언트의 요청을 메소드의 리턴타입으로 받아와야 한다.

 

- fortune (servlet) 페이지에서 요청하면 

 주소창에 보이는 페이지 주소는 그대로지만, 해당 페이지 소스를 확인해보면 fortune.jsp가 응답한 것을 볼 수 있다.

 

 

- 최초 요청을 받은 servlet을 대신해서 jsp가 응답했다!

  위와 같은 단계로 요청이 실행된다고 보면 된다.

 

- jsp <%%>에서 사용하는 request와 같다. jsp에 참조값을 전달해 주는 것!

 

- 처음 전달된 타입은 HttpServletRequest이고,

 forward에서 인자로 필요한 것은 ServletRequest 이지만

 ServletRequest가 부모타입이기 때문에 담아서 사용할 수 있다.

 


 

- Servlet 페이지에서 응답에 필요한 데이터를 request scope에 담아서 전달한다.

 

.setAttribute : 어떤 속성을 담는 메소드.

- request 영역(scope)에 key-value를 쌍으로 저장한다.

- 영역을 scope 라고도 한다.

 

- object 타입이므로 어떤 타입이든지 다 들어갈 수 있다. 아무거나 다 전달할 수 있다!

 

- key값과 object type 담기

 

- jsp페이지에서는 getAtrribute 로 가져와준다.

 

- 변수에 담고 jsp페이지로 전달하는 것

 


- servlet - jsp 분업화된 것. 비즈니스 로직과 응답을 분리한다.

- Spring Framework에서 이렇게 많이 한다.모든 요청을 처음에는 servlet이 받고, 응답은 forward로 받아서 jsp로 이동

 

- '어떤 키 값'으로 '어떤 타입'이 담겼는지를 확인해서 쓰면 된다. 

- object 타입으로 담았던 데이터의 casting이 필요하기 때문에!

 

Q) 속도가 빨라져서 분업하는것인가?

A) 아니다. jsp를 단순화시키기 위해서이다.(응답에만 집중할 수 있도록 하기 위해)

 나중에는 java코드도 없이 바로 출력하게 된다.

 


 

/member 링크 추가

<MemberServlet> 생성

package test.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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 test.member.dto.MemberDto;

@WebServlet("/member")
public class MemberServlet extends HttpServlet{
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//회원 한명의 정보를 얻어오는 비즈니스 로직 수행
		MemberDto dto=new MemberDto(1, "바나나", "서울");
				
		//request scope에 "dto"라는 키값으로 MemberDto type 담기
		req.setAttribute("dto", dto);
		
		//jsp페이지로 응답을 위임시키기 (forward 이동)
		RequestDispatcher rd=req.getRequestDispatcher("/test/member.jsp");
		rd.forward(req, resp);		
	}
}

 

- forward: 응답을 다른 페이지에 위임시키는 메소드

- sendRedirect: 요청을 다시 하라고 강제로 페이지를 이동시키는 메소드

 

 

<member.jsp>

<%@page import="test.member.dto.MemberDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//"dto"라는 키값으로 담긴 MemberDto type의 참조값 얻어오기
	MemberDto dto=(MemberDto)request.getAttribute("dto");
	
%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test/member.jsp</title>
</head>
<body>
	<h3>회원 한명의 정보입니다.</h3>
	<p> 번호 : <strong><%=dto.getNum() %></strong></p>
	<p> 이름 : <strong><%=dto.getName() %></strong></p>
	<p> 주소 : <strong><%=dto.getAddr() %></strong></p>
</body>
</html>

 

- 어떤 키워드(key)로, 어떤 타입의 정보가 저장되었는지 기억하기

- 여기서는 "dto" / MemberDto 타입

 

- 주소는 servlet 처럼 생겼지만 페이지 소스에서 보이는 것은 jsp페이지

 

- 분업화하여 응답에 필요한 데이터는 servlet에서, forward 이동해서 응답은 jsp에서!

- jsp에 java코드가 많으면 유지보수가 힘들기 때문에, jsp페이지를 단순화시키기 위해서이다.

 


 

- index.jsp 에 친구목록을 볼 /friend/list 링크 추가

 

<FriendList> Servlet생성

package test.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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

@WebServlet("/friend/list")
public class FriendList extends HttpServlet{
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		//친구목록을 얻어오는 비즈니스 로직을 수행한다.
		List<String> list=new ArrayList<>();
		list.add("바나나");
		list.add("딸기");
		list.add("복숭아");
						
		//로직 수행결과로 얻어낸 데이터(Model)을 request scope에 담기
		req.setAttribute("list", list);
		
		//jsp 페이지로(view page) forward 이동해서 응답하기 (forward이동은 WEB-INF 폴더 안쪽도 가능하다.)
		RequestDispatcher rd=req.getRequestDispatcher("/WEB-INF/views/friend/list.jsp");
		rd.forward(req, resp);
	}
}

 

- 프로그래밍의 관점에서 데이터를 모델이라고도 부른다.

 

 

- jsp 페이지로(view page) forward 이동해서 응답하기.(forward이동은 WEB-INF 폴더 안쪽도 가능하다.)

 

- WEB-INF는 원래는 보안되는 폴더이기 때문에, 클라이언트가 직접 요청해서 들어갈 수는 없다.

- 하지만 forward 이동하는 경우에는 WEB-INF폴더 사용이 가능하다.(이건 클라이언트가 요청하는 것이 아니므로)

 

 

/WEB-INF/views/friend/ <list.jsp> 생성하기

<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//"list"라는 키값으로 request scope 에 담겨 있는 List<String> type의 참조값 얻어오기
	List<String> list=(List<String>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/WEB-INF/views/friend/list.jsp</title>
</head>
<body>
	<h3>친구 목록입니다.</h3>
	<ul>
		<% for(String tmp:list){ %>
			<li><%=tmp %></li>
		<%} %>
	</ul>
</body>
</html>

 

- 어떤 키워드로, 어떤 타입의 정보가 저장되었는지 기억하기

- "list" / String 타입

 

 

- 클라이언트가 /friend/list 요청을 하면

→ friendListServlet이 요청을 받아서 request scope에 담아주고

→ 응답은 forward 한 /WEB-INF/views/friend/list.jsp 페이지에서 하고

→ 응답을 브라우저 화면으로 response 해준다.

 

- request 영역에 담은 것은 응답할 때까지만 남아 있다.(응답하기 전까지만 유효하다)

 응답을 다 하고 나면 사라진다. 일회성 데이터라고 할 수 있다.

 

- 페이지 소스에서 확인해보면 jsp 페이지를 보여주고 있다는 것을 알 수 있다.

 

- 해당 jsp 경로를 클라이언트가 주소창에 직접 요청하면 접속할 수 없다.

 forward 이동 용도로만 사용된다.

- 주소창에 WEB-INF 경로가 들어가면 잘못 요청한 것!