서블릿(Servlet)

Joy🌱·2023년 2월 16일
0

☕ Java

목록 보기
36/40
post-thumbnail

💁‍♀️ 서블릿(Servlet)이란,
Server + Applet의 합성어, JAVA 언어를 이용하여 사용자의 요청을 받아 처리하고 그 결과를 다시 사용자에게 전송하는 역할의 Class 파일.
즉, 웹에서 동적인 페이지를 java로 구현한 서버측 프로그램


💁‍ 서블릿의 역할

  • 요청 받기 : HTTP method 요청에 따른 parameter로 전달 받은 데이터 꺼내오기
  • 비즈니스 로직 처리 : DB 접속과 CRUD 에 대한 로직 처리
  • 응답 하기 : 문자열로 동적인 웹(HTML 태그) 페이지를 만들어서 스트림을 이용해서 내보내기

👀 서블릿 메소드

💁‍♀️ HttpServletRequest(interface)
getParameter(String) client가 보내준 값이 저장된 명칭이 매개변수와 같은 명칭에 저장된 값을 불러오는 메소드
getParameterNames() client가 보내준 값을 저장한 명칭을 불러오는
메소드
getParameterValues(String) client가 보내준 값이 여러개일 경우 그 값을 배열로 불러오는 메소드
getParameterMap() client가 보내준 값이 전체를 Map방식으로 불러
오는 메소드
setAttribute(String, object) request객체에 전달하고 싶은 값을 String 이름으로 Object저장하는 메소드
getAttribute(String) 매개변수와 동일한 객체 속성값 불어오는 메소드
removeAttribute(String) request객체에 저장되어 매개변수와 동일한 속성값 삭제하는 메소드
setCharacterEncoding(String) 전송받은 reques객체의 값들의 CharaterSet을 설정해주는 메소드
getRequestDispatcher(String)
컨테이너내에서 request, response객체를 전송하여 처리할 컨포넌트 (jsp파일 등)를 불러오는 매소드로 forward()메소드와 같이 사용


💁‍♀️ HttpServletResponse (interface)
setContentType(String) 응답으로 작성하는 페이지의 MIME type을 정하는 메소드
setCharacterEncoding(String) 응답하는 데이터의 CharacterSet을 지정해주는 메소드
getWriter() 문자를 페이지에 전송을 위한 Stream을 가져오는 메소드
getOutputStream() byte단위로 페이지에 전송을 위한 Stream을 가져오는 메소드
sendResdirect(String) client가 매개변수의 페이지를 다시 서버에 요청하게 하는 메소드

1) 사용자 데이터 전송방식

  • doGet()
    client에서 데이터를 전송 방식을 get방식으로 전송하게 되면 호출되는 메소드
  • doPost()
    client에서 데이터를 전송 방식을 Post방식으로 전송하게 되면 호출되는 메소드
  • 👉 반드시 ServletException 처리해야 함
<h1 align="center">service()의 역할</h1>

	<h3>GET 방식의 요청</h3>
	
	<h4>a 태그의 href 속성 값 변경</h4>
	<a href='request-service'>서비스 메소드 요청하기</a>
	
	<h4>form 태그의 method 속성을 get으로 설정(기본 값)</h4>
	<form action="request-service" method="get">
		<input type="submit" value="GET 방식 요청 전송">
	</form>
	
	<h3>POST 방식의 요청</h3>

	<h4>form 태그의 method 속성을 post로 설정</h4>
	<form action="request-service" method="post">
		<input type="submit" value="POST 방식 요청 전송">
	</form>
@WebServlet("/request-service")
public class ServiceMethodTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

//	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//		/* HTTP 메소드 판단 */
//		String method = request.getMethod();
//		System.out.println("http method : " + method);
//		
//		if("GET".equals(method)) {
//			doGet(request, response);
//		} else if("POST".equals(method)) {
//			doPost(request, response);			
//		}
//	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("GET 요청을 처리할 메소드 호출 됨...");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("POST 요청을 처리할 메소드 호출 됨...");	
	}

}

👀 서블릿 매개변수 객체

  • get방식
    URL창에 “ ? “ 뒤에 데이터를 입력하는 방법(쿼리스트링)으로 전송
    (데이터가 여러 개일 경우 &로 묶어서 전송)
    • 데이터 검색에 많이 사용
    • 데이터 크기의 한계 존재
    • 보안에 취약
  • post방식
    HTTP헤더의 내용으로 보내는 방식
    • 데이터 크기에 제한 X
    • 헤더에 포함되어 보안이 뛰어남

1) index.html

[1-1] form 태그를 이용한 GET 방식 요청

	<form action="querystring" method="get">
		<label>이름 : </label><input type="text" name="name"> <br>
		<label>나이 : </label><input type="number" name="age"> <br>
		<label>생일 : </label><input type="date" name="birthday"> <br>
		<label>성별 : </label> 
        <input type="radio" name="gender" id="male" value="M">
        <label for="male">남자</label> 
        <input type="radio" name="gender" id="female" value="F">
        <label for="female">여자</label>
		<br> <label>국적 : </label> <select name="national">
			<option value="ko">한국</option>
			<option value="ch">중국</option>
			<option value="jp">일본</option>
			<option value="etc">기타</option>
		</select> <br> <label>취미 : </label> 
        <input type="checkbox" name="hobbies" id="movie" value="movie">
        <label for="movie">영화</label> 
        <input type="checkbox" name="hobbies" id="music" value="music">
        <label for="music">음악</label> 
        <input type="checkbox" name="hobbies" id="sleep" value="sleep">
        <label for="sleep">취침</label> <br>

		<input type="submit">
	</form>

[1-2] a 태그의 href 속성에 직접 파라미터를 쿼리스트링 형태로 작성하여 GET 방식 요청

	<h4> get 방식 요청</h4>
	<a href="querystring?name=치즈&age=2&birthday=2020-12-25&gender=M&national=ko&hobbies=sleep">쿼리스트링을 이용한 값 전달</a>
	<!-- a 태그를 누르면 자동으로 위에 작성된 값이 서버로 넘어옴 -->

[2] form 태그를 이용한 post 방식 요청

	<form action="formdata" method="post">
	<!-- 위의 get 방식 요청과 내용 동일 -->
	</form>

2) querystring

getParameter() getParameterValues()

HttpServlet 클래스의 servie 메소드가 요청 방식에 의해 GET 요청은 doGet(), POST 요청은 doPost() 메소드를 호출. 호출 시 request, response 객체도 전달.
HttpServletRequest 객체의 getParameter() 메소드를 통해 전달 받은 값 추출 가능.
이 때 input 태그에 name 속성으로 지정한 값이 넘어오므로 메소드의 인자로 해당 값을 문자열 형태로 전달.
form 태그 내의 모든 input 태그의 값을 HashMap 형태로 전달받아 관리하고 있는데 그 중 원하는 값을 찾기 위해서는 key 역할을 하는 문자열이 필요하기 때문.
(key 역할을 하는 문자열이 없을 경우 value 속성은 null로 리턴)

@WebServlet("/querystring")
public class QueryStringTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		String name = request.getParameter("name");
		System.out.println("이름 : " + name); // 웹에서 입력한 값이 넘어와서 콘솔에 출력됨
		
		/* getParameter() 는 리턴 타입이 문자열(String)이므로 숫자 타입이 필요하다면 parsing 처리해야한다. */
		int age = Integer.parseInt(request.getParameter("age"));
		System.out.println("나이 : " + age);
		
		/* java.sql.Date 타입으로 날짜 데이터 parsing */
		java.sql.Date birthday = java.sql.Date.valueOf(request.getParameter("birthday"));
		System.out.println("생일 : " + birthday);
		
		/* radio로 전달 된 값은 여러 값 중 한 가지만 전달되기 때문에 parameter로 전달 받은 값을 꺼내기만 하면 됨 */
		String gender = request.getParameter("gender");
		System.out.println("성별 : " + gender);
		
		/* select box를 이용한 방식도 크게 다르지 않음 */
		String national = request.getParameter("national");
		System.out.println("국적 : " + national);

		/* checkbox는 다중 입력될 수 있기 때문에 선택된 값이 문자열 배열로 전달
		 * getParameterValues() 메소드를 이용 */
		String[] hobbies = request.getParameterValues("hobbies");
		for(String hobby :hobbies) {
			System.out.println("취미 : " + hobby);
		}
	}
}

3) formdata

setCharacterEncoding() java.sql.Date.valueOf()

  • GET 방식의 요청은 request의 header를 통해 데이터를 전달하고 body는 아무런 데이터를 담지 않기 때문에 따로 읽지 않음.
    (처리 속도 빠름 - 주로 조회 동작 시 사용)
  • POST 방식의 요청은 request의 body를 통해 데이터를 전달하고 url에 데이터가 노출되지 않으며 데이터 길이에 제한 X
    (주로 데이터 저장, 수정 등의 동작 시 사용)
  • GET 방식의 데이터는 HTML charset에 기술 된 인코딩 방식으로 브라우저가 한글을 이해한 뒤 URLEncoder를 이용하여 반환하고 url 요청으로 전송. 따라서 헤더의 내용은 알맞게 해석되므로 별도의 charset 설정이 불필요.
  • POST 방식의 요청은 body에 담아서 전송하는데 헤더와는 별개로 URLEncoder를 이용하지 않고 데이터를 전송.
    charset을 별도로 설정하지 않았을 경우 null이 반환되는데 기본 값은 ISO-8859-1 방식으로 되어있어 한글 값이 깨지는 현상이 발생.
@WebServlet("/formdata")
public class FormDataTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/* 인코딩 방식을 설정하기 전, null 반환 */
		System.out.println(request.getCharacterEncoding());
		
		/* 파라미터 값을 꺼내기 전에 디코딩할 인코딩 방식을 지정해주면 해당 방식으로 body 값을 해석 */
		request.setCharacterEncoding("UTF-8"); // 한글 해석 가능해짐

		/* 인코딩 방식을 설정한 후, UTF-8 반환 */
		System.out.println(request.getCharacterEncoding());
		
		/* POST방식으로 전달될 때 한글인 값은 인코딩 방식을 지정 */
		String name = request.getParameter("name"); // parameter를 꺼내오기 전, 인코딩 방식 설정
		System.out.println("이름 : " + name); 
		
		int age = Integer.parseInt(request.getParameter("age"));
		System.out.println("나이 : " + age);
		
		java.sql.Date birthday = java.sql.Date.valueOf(request.getParameter("birthday"));
		System.out.println("생일 : " + birthday);
		
		String gender = request.getParameter("gender");
		System.out.println("성별 : " + gender);
		
		String national = request.getParameter("national");
		System.out.println("국적 : " + national);

		String[] hobbies = request.getParameterValues("hobbies");
		for(String hobby :hobbies) {
			System.out.println("취미 : " + hobby);
		}

4) getParameter, getParameterValues 외의 파라미터 값을 확인할 수 있는 메소드

getParameterMap() getParameterNames()

[1] 모든 데이터의 key를 이용하여 전송 된 값을 일괄 처리하는 방법

Map<String, String[]> requestMap = request.getParameterMap();
Set<String> keySet = requestMap.keySet();
Iterator<String> keyIter = keySet.iterator(); // key만 set으로 만든 것을 iterator로 줄 세우기
		
while(keyIter.hasNext()) { // iterator화 된 것들 중 다음이 있을 때까지 key를 돌려서 value 뽑아내기
	String key = keyIter.next();
	String[] value = requestMap.get(key);
			
	System.out.println("key : " + key);			// 키 출력
	for(String val : value) {
		System.out.println("value : " + val);	// 반복문으로 value 출력
	}
}

[2] 파라미터로 전달된 key 목록만 반환 받기

		Enumeration<String> names = request.getParameterNames();
		while(names.hasMoreElements()) {
			System.out.println(names.nextElement());
		}
	}
}

👀 Response

1) index.html

<ul>
	<li><a href='response?name=허치즈'>응답 확인하기</a></li>
	<li><a href='headers'>헤더 확인하기</a></li>
	<li><a href='status'>응답 상태 코드 확인하기</a><li>
</ul>

2) ResponseTestServlet

@WebServlet("/response")
public class ResponseTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
		String name = request.getParameter("name");
		
		/* 응답 컨텐츠 타입 설정
		 * 기본 값은 text/plain으로 되어 있지만 html 태그를 사용하면 text/html도 응답으로 수락 가능하도록
		 * 헤더 설정이 되어있기 때문에 text/html로 인식 가능
		 * 하지만 명시적으로 text/plain이라고 설정하면 태그가 아닌 문자열로 인식
         */
//		response.setContentType("text/plain");
		response.setContentType("text/html");
		
		/* 응답 시 별도 인코딩 지정이 없으면 ISO-8859-1로 처리되므로 한글 값이 깨짐 */
		System.out.println("default response encoding :" + response.getCharacterEncoding());
		
		response.setCharacterEncoding("UTF-8");
		
		/* 참고로 위의 두 가지 설정을 한 번에 처리 가능 !! */
		response.setContentType("text/html; charset=UTF-8");
		
		/* 사용자 브라우저에 응답하기 위해 HttpServletResponse가 가지는 getWriter() 메소드로 PrintWriter 인스턴스를 반환 받음 
		 * BufferedWriter와 형제격인 클래스이지만 더 많은 형태의 생성자를 제공하고 있어서 실제로는 범용성으로 인해 더 많이 사용 */
		PrintWriter out = response.getWriter();
		
		/* 문자열을 이용해서 사용자에게 내보내기 할 페이지를 작성 */
		StringBuilder responseBuilder = new StringBuilder();
		responseBuilder.append("<doctype html>\n")
					   .append("<html>\n")
					   .append("<head>\n")
					   .append("</head>\n")
					   .append("<body>\n")
					   .append("<h1>안뇽! " + name + "</h1>\n")
					   .append("</body>\n")
					   .append("</html>");
		
		/* 스트림을 이용해 내보내기 */
		out.print(responseBuilder.toString());
		
		/* 버퍼에 잔류한 데이터를 강제로 내보내기 */
		out.flush();
		
		/* 스트림 닫기 */
		out.close();
	}
}


👀 예외 핸들링

1) index.html

<ul>
	<li><a href='show404Error'>404 에러 확인</a>
	<li><a href='show500Error'>500 에러 확인</a>
</ul>

2) Show404ErrorServlet

WebServlet("/show404Error")
public class Show404ErrorServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.sendError(404, "PAGE NOT FOUND 🥺💦");
	}
}

3) Show500ErrorServlet

@WebServlet("/show500Error")
public class Show500ErrorServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.sendError(500, "이것은 개발자 조.효.연의 잘못이다.");
	}
}

4) ExceptionHandlerServlet

@WebServlet("/showErrorPage")
public class ExceptionHandlerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		Enumeration<String> attrNames = request.getAttributeNames();
		while(attrNames.hasMoreElements()) {
			String attrName = attrNames.nextElement();
			System.out.println(attrName + " : " + request.getAttribute(attrName));
		}
		
		/* 에러페이지 안에 tomcat의 기본 오류 페이지가 아닌 동적으로 구성하여 설정한 페이지를 응답 */
		/* getAttribute의 반환값은 object 이므로 형변환 */
		Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
		String errorMessage = (String)request.getAttribute("javax.servlet.error.message");
		
		StringBuilder errorPage = new StringBuilder();
		errorPage.append("<!doctype html>\n")
				 .append("<html>\n")
				 .append("<head>\n")
				 .append("</head>\n")
				 .append("<body>\n")
				 .append("<h1 align='center'>")
				 .append(statusCode)
				 .append(" - ")
				 .append(errorMessage)
				 .append("</h1>")
				 .append("</body>\n")
				 .append("</html>");
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		out.print(errorPage.toString());
		out.flush();
		out.close();
	}
}


👀 forward

RequestDispatcher()

💁‍♀️ forward란, 컨테이너 내에서 처음 요청 받은 페이지가 요청 데이터 (HttpServletRequest, HttpServletResponse)를 다른 페이지에 전송하여 처리를 요청을 하고 자신이 처리한 것처럼 하는 것
(url주소가 변경되지 않음)

1) index.html

<h1 align="center">forward</h1>

	<form action='forward' method='post'>
		<table align='center'>
			<tr>
				<td>아이디 : </td>
				<td><input type='text' name='userId'></td>
				<td rowspan='2'><button type='submit' style='height:50px'>로그인</button></td>
			</tr>
			<tr>
				<td>비밀번호 : </td>
				<td><input type='password' name='password'></td>
			</tr>
		</table>
	</form>

2) ReceiveInformationServlet

@WebServlet("/forward")
public class ReceiveInformationServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8");
		
		String userId = request.getParameter("userId");		// 웹에서 입력한 아이디값 전달 받음
		String password = request.getParameter("password"); // 웹에서 입력한 비밀번호값 전달 받음
		
		System.out.println("userId : " + userId);		
		System.out.println("password : " + password);	
		
		/* 요청 정보를 받고 비즈니스 로직까지 성공했다는 가정하에 조회 된 닉네임을 넣어
		 * "OOO님 환영합니다." 라는 메세지를 보여주는 화면을 응답
		 * 화면 응답에 대한 로직은 다른 서블릿으로 분리 */
		
		String nickname = "joyhyonie";
		
		/* PrintLoginSuccessServlet으로 처리를 위임하기 위해서는 RequestDispatcher 인스턴스 생성 
		 * forward 메소드를 이용해서 요청과 응답에 대한 객체를 전달
		 * 이때 다른 서블릿으로 넘길 데이터는 request의 setAttribute 메소드로 설정하고
		 * 받는 쪽에서는 getAttribute 메소드를 통해 꺼냄 */
		
		request.setAttribute("nickname", nickname);
		
		RequestDispatcher rd = request.getRequestDispatcher("print"); /* 'print'라는 주소값을 PrintLoginSuccessServlet에 명시 */
		rd.forward(request, response);
	}
}

3) PrintLoginSuccessServlet

@WebServlet("/print") /* getRequestDispatcher에 명시된 주소값을 이곳에 입력 */
public class PrintLoginSuccessServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/* forward된 서블릿에서도 요청 방식이 GET이면 doGET, POST면 doPost를 호출 */
		
		/* request에 이전 서블릿에서 전달하고자 하는 정보를 담았기 때문에 다시 꺼냄 */
		String nickname = (String) request.getAttribute("nickname");
		System.out.println("forword 확인 : " + nickname ); // 올바르게 전달 받음
		
		StringBuilder responseText = new StringBuilder();
		responseText.append("<!doctype html>\n")
				 .append("<html>\n")
				 .append("<head>\n")
				 .append("</head>\n")
				 .append("<body>\n")
				 .append("<h1 align='center'>")
				 .append(nickname)
				 .append("님, 안뇽하세요?")
				 .append("</h1>")
				 .append("</body>\n")
				 .append("</html>");
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		out.print(responseText.toString());
		out.flush();
		out.close();
	}
}

💁‍♀️변수의 기본 scope는 page 이기 때문에 다른 서블릿으로 데이터 공유 불가
하지만 forward 방식은 request, response를 전달하며 위임하므로 request에 정보를 저장하여 다른 서블릿에서도 공유할 수 있게 됨
forward 방식은 위임되는 서블릿의 존재를 client가 알 필요가 없기 때문에 url 자체는 변경 X
단, 서버로 전송한 데이터가 남아있는 상태로 새로고침(재요청)을 하게 되면 요청을 계속 반복하게 되어 DB에 insert 하는 행위가 반복되는 문제가 생길 수도 있음
이러한 경우에는 또 다른 페이지 전환 방식인 redirect 방식을 사용해야함


👀 redirect

sendRedirect()

💁‍♀️ redirect란,
client의 브라우저에게 매개변수로 등록한 페이지를 재요청하라는 응답을 해주는 것(301/302코드 전송)

  • encodeRedirectURL은 매개변수(URL)에 세션ID정보를 추가 재요청 처리
  • client가 별도로 다른 페이지 요청을 안해도 url주소(페이지)가 변경

1) index.html

<ul>
	<li><a href='othercite'>다른 웹 사이트로 redirect</a></li>
	<li><a href='otherservlet'>다른 웹 서블릿으로 redirect</a></li>
</ul>

2) OtherCiteRedirectServlet

@WebServlet("/othercite")
public class OtherCiteRedirectServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("GET 요청 후 naver 사이트로 redirect 확인 !");
		
		/* 브라우저의 개발자 도구 network 탭에서 확인하면 302 status code와 함께 location에 기재 
         * naver 사이트로 이동하는 것을 확인 가능
		 * 사용자 url 재작성이라고도 불리는 redirect 방식은 302번 응답코드인 경우
		 * 요청에 대한 처리를 잘 했으며 사용자의 url을 강제적으로 redirect 경로로 이동시키라는 의미 */
		response.sendRedirect("https://www.naver.com");
	}
}

2) OtherServletRedirectTest

[1] OtherServletRedirectTest

@WebServlet("/otherservlet")
public class OtherServletRedirectTest extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("GET 요청 정상 수락");
		response.sendRedirect("redirect"); /* 'redirect'라는 주소값을 RedirectServlet에 명시 */
	}
}

[2] RedirectServlet

@WebServlet("/redirect") /* OtherServletRedirectTest에 명시된 주소값을 이곳에 입력 */
public class RedirectServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		StringBuilder responseText = new StringBuilder();
		responseText.append("<!doctype html>\n")
				 .append("<html>\n")
				 .append("<head>\n")
				 .append("</head>\n")
				 .append("<body>\n")
				 .append("<h1 align='center'>")
				 .append("이 Servlet으로 redirect 성공!")
				 .append("</h1>")
				 .append("</body>\n")
				 .append("</html>");
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		out.print(responseText.toString());
		out.flush();
		out.close();
	}
}

💁‍♀️ redirect를 하는 경우 url이 재작성 되어서 새로고침해도 redirect된 페이지에대한 요청만을 반복. 즉, 이전 요청에 남아있던 정보는 더 이상 남아있지 않음.
HTTP 통신은 요청 시에 잠시 Connection을 맺고 응답 시에도 잠시 Connection을 맺으며 요청 단위 당 request 객체는 한 개만 생성.
따라서, 첫 요청의 request와 redirect된 페이지의 request는 서로 다른 객체이고 redirect를 사용하면 이전 서블릿과의 값을 공유해서 사용할 수 없게 됨.
redirect시에도 값을 유지하는 방법으로 크게 cookiesession을 이용할 수 있음

📌 redirect 흐름

  • forward : 본래의 주소값을 남겨두기 때문에 조회에 용이
  • redirect : 본래의 주소값과의 연결을 최초 실행 후 끊어버리기 때문에 삽입에 용이 (forward의 문제 해결)

💁‍♀️ cookie와 session의 차이
server가 client에게 응답하고나서 연결이 끊어진 이후에도 client에 대한 정보를 유지하기 위해 Server측에서 데이터를 보관하는 방법Session, client측에서 데이터를 보관하는 방법Cookie라고 함


💁‍♀️ cookie란,
클라이언트 즉 사용자 컴퓨터에 데이터를 저장하는 기술, 필요시에 해당하는 정보를 서버와 공유하여 정보를 유지하는 것

  • Map형식으로 저장
  • 데이터의 크기, 개수에 제한
  • 쿠키유지시간, 유효디렉터리, 유효도메인 등을 설정

Cookie 쿠키명 = new Cookie(“명칭”,”값”); Cookie클래스 생성(패키지 import)
setMaxAge(int expiry) 생성된 쿠키의 유효시간 설정
setPath(String uri) 생성된 쿠키의 경로 설정
response.addCookie(Cookie cookie) 생성된 쿠키 전송
request.getCookies() client에서 전송한 Cookie 읽어오기 (쿠키객체 배열로 리턴)
ex) Cookie[] list=HttpRequest.getCookies();
getName() / getValue() 이름과 값 호출

1) index.html

<form action='cookie' method='post'>
	<table>
		<tr>
			<td>firstName : </td>
			<td><input type='text' name='firstName'></td>
		</tr>
		<tr>
			<td>lasttName : </td>
			<td><input type='text' name='lastName'></td>
		</tr>
		<tr>
			<td colspan='2' align='center'> 
				<input type='submit'>
			</td>
		</tr>
	</table>
</form>

2) CookieHandlerServlet

@WebServlet("/cookie")
public class CookieHandlerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8");
		
		String firstName = request.getParameter("firstName");
		String lastName = request.getParameter("lastName");
		
		System.out.println("firstName : " + firstName);
		System.out.println("lastName : " + lastName);
		
		/* redirect 하기 전, Cookie 생성 */
		/* 1. 쿠키 생성 */
		Cookie firstNameCookie = new Cookie("firstName", firstName); 	// Cookie를 생성할 때, 키와 밸류를 지정해야함
		Cookie lastNameCookie = new Cookie("lastName", lastName); 		// Cookie를 생성할 때, 키와 밸류를 지정해야함
		
		/* 2. 쿠키의 만료 시간 설정 */
		/* 초 단위로 설정하며 하루를 만료시간으로 두고 싶다면 60 * 60 * 24 */
		firstNameCookie.setMaxAge(60 * 60 * 24); /* 60초, 60분, 24시 */
		firstNameCookie.setMaxAge(60 * 60 * 24);
		
		/* 3. 응답 헤더에 쿠키를 담기 */
		response.addCookie(firstNameCookie);
		response.addCookie(lastNameCookie);
		
		/* redirect는 url을 재작성하여 url을 이용해 요청하는 방식이기 때문에 GET 방식의 요청 */
		response.sendRedirect("redirect");
	}
}

3) RedirectServlet

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		/* 두 request 객체가 서로 공유되는지 테스트 하기 위해 출력문 동일하게 작성 */
		String firstName = request.getParameter("firstName");
		String lastName = request.getParameter("lastName");
		
		System.out.println("firstName : " + firstName);
		System.out.println("lastName : " + lastName);
		
		/* null 값으로 출력되는 것을 통해 이전 Servlet과 request 객체가 공유되지 않음을 확인 가능 */
		
		/* 1. request에서 쿠키 목록을 쿠키 배열 형태로 꺼내오기 */
		Cookie[] cookies = request.getCookies();
		for(Cookie cookie : cookies) {
			/* 2. cookie 객체의 getName과 getValue를 이용해 담긴 값을 꺼내오기 */
			System.out.println("[cookie] " + cookie.getName() + " : " + cookie.getValue());
			// [cookie] firstName : hyoyeon
			// [cookie] lastName : cho
			// cookie는 공유되는 값이기 때문에 정상적으로 값 받아옴
			
			/* 쿠키를 이용해서 null이었던 값들을 채운 뒤, 클라이언트 화면에 응답하기 */
			if("firstName".equals(cookie.getName())) {
				firstName = cookie.getValue();
			} else if("lastName".equals(cookie.getName())) {
				lastName = cookie.getValue();
			}
		}
		
		StringBuilder responseText = new StringBuilder();
		responseText.append("<!doctype html>\n")
				 .append("<html>\n")
				 .append("<head>\n")
				 .append("</head>\n")
				 .append("<body>\n")
				 .append("<h1 align='center'>")
				 .append("Your first name is ")
				 .append(firstName)
				 .append(" and last name is ")
				 .append(lastName)
				 .append("</h1>")
				 .append("</body>\n")
				 .append("</html>");
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		out.print(responseText.toString());
		out.flush();
		out.close();
		
		/* 응답화면에 정상적으로 입력한 이름이 출력되어있는 것을 확인 */
	}
}

💁‍♀️ 키는 텍스트 파일 형태로 클라이언트 컴퓨터에 저장. 다른 사용자와 함께 사용하는 컴퓨터인 경우 쿠키에 민감한 개인 정보를 담기에는 보안이 취약해지는 문제 존재. 따라서, 민감한 개인 정보를 취급하는 경우에는 쿠키대신 세션을 이용
세션은 쿠키와 유사한 형태로 key=value 쌍으로 저장 되지만 서버에서 관리 되기 때문에 보안에 더 우수


👀 session

💁‍♀️ session이란,
서버에 데이터를 저장하는 기술로 client에는 Session ID를 부여하고 client가 request에 SessionID를 보내면 ID를 기준으로 일치 하는 Session정보를 컨테이너가 생성하여 그 객체의 데이터를 가져와 사용. 만일 client가 보낸 SessionID가 없으면 새로 객체를 생성


HttpRequest.getSession(); Session 생성 (Session객체를 컨테이너가 자동으로 생성하여 request객체에 넣어주기 때문에 그 객체를 불러오는것을 생성이라고 함)
세션명.setAttribute(“이름”,”값(Obj)”); 생성된 Session 데이터 설정
세션명.setMaxInactiveInterval(숫자); 생성된 Session 유지시간 설정
세션명.getAttribute(“이름”); 데이터 불러오기
getAttributeNames() 객체에 등록되어 있는 모든 정보의 이름만 반환
removeAttribute(String) request 객체에 저장되어 매개변수와 동일한 속성값 삭제
getId() SessionID값을 가져옴
getCreationTime() Session객체가 생성된 시간을 반환(밀리초)
getMaxInactiveInterval() 클라이언트 요청이 없을때 서버가 현재의 세션을 언제까지 유지
할지를 초 단위로 반환(default 30분)
getLastAccessedTime() 클라이언트 요청이 마지막으로 시도된 시간을 반환(밀리세컨초)
isNew() 새로생성된 Session이면 true 아니면 false
invalidate() 현재의 Session을 삭제
setMaxInactiveInterval(int) 객체의 유지시간을 설정, 지정된 시간이 지나면 객체 자동삭제

1) index.html

<form action='session' method='post'>
	<table>
		<tr>
			<td>firstName : </td>
			<td><input type='text' name='firstName'></td>
		</tr>
		<tr>
			<td>lasttName : </td>
			<td><input type='text' name='lastName'></td>
		</tr>
		<tr>
			<td colspan='2' align='center'> 
				<input type='submit'>
			</td>
		</tr>
	</table>
</form>

2) SessionHandlerServlet

@WebServlet("/session")
public class SessionHandlerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8");
		
		String firstName = request.getParameter("firstName");
		String lastName = request.getParameter("lastName");
		
		System.out.println("firstName : " + firstName);
		System.out.println("lastName : " + lastName);
		
		/* redirect 하기 전, Session 생성 */
		/* 쿠키는 클라이언트 컴퓨터에 저장되므로 민감한 정보를 담기에는 적합하지 않음
		 * 서버 쪽에서 관리되는 세션이라는 인스턴스를 활용 */
		HttpSession session = request.getSession();
		
		/* 세션은 강제로 만료 시키는 기능도 있지만 만료 시간을 설정해 주는 것이 좋음
		 * 기본 설정 시간은 30분 이며, 필요에 따라 늘이거나 줄이면 됨 */
		System.out.println("session default 유지 시간 : " + session.getMaxInactiveInterval()); // 1800 출력 (1800초 = 30분)		
		session.setMaxInactiveInterval(60 * 10); // 10분으로 설정
		System.out.println("session 새로 set한 후 유지 시간 : " + session.getMaxInactiveInterval());
		
		/* 세션은 브라우저 당 한 개씩 고유한 아이디를 가지고 하나의 인스턴스를 이용
		 * 매번 반복적인 요청을 할 경우 동일한 session id를 리턴 */
		System.out.println("session id : " + session.getId());
		// but, 모든 브라우저를 종료하고 새롭게 열어 다시 실행하면 다른 session id 반환됨
		
		/* 세션은 redirect를 해도 값을 유지할 수 있는 request보다 더 넓은 범위의 공유 영역이라고 표현할 수 있음 */
		session.setAttribute("firstName", firstName);
		session.setAttribute("lastName", lastName);
		
		/* redirect는 url을 재작성하여 url을 이용해 요청하는 방식이기 때문에 GET 방식의 요청 */
		response.sendRedirect("redirect");
	}
}

3) RedirectServlet

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		HttpSession session = request.getSession();
		System.out.println("redirect 페이지의 session id : " + session.getId());
		
		/* 세션 안에 담긴 모든 attribute의 키 목록 반환 */
		Enumeration<String> sessionNames = session.getAttributeNames();
		while(sessionNames.hasMoreElements()) {
			System.out.println(sessionNames.nextElement());
		}
		
		/* 동일한 아이디를 가진 세션에서는 setAttribute 한 값을 getAttribute로 꺼내올 수 있음 */
		String firstName = (String) session.getAttribute("firstName");
		String lastName = (String) session.getAttribute("lastName");
		
		StringBuilder responseText = new StringBuilder();
		responseText.append("<!doctype html>\n")
				 .append("<html>\n")
				 .append("<head>\n")
				 .append("</head>\n")
				 .append("<body>\n")
				 .append("<h1 align='center'>")
				 .append("Your first name is ")
				 .append(firstName)
				 .append(" and last name is ")
				 .append(lastName)
				 .append("</h1>")
				 .append("</body>\n")
				 .append("</html>");
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		out.print(responseText.toString());
		out.flush();
		out.close();
	}
}

📌 session 흐름


👀 filter

💁‍♀️ filter란,
Servlet으로 전달되는 client의 request 혹은 Servlet에서 client로 전달되는 response중간에 가로채서 필터링하기 위해 객체와 메서드를 정의해 둔 인터페이스

👉 index.html

<h1 align="center">필터의 라이프 사이클</h1>
	<ul>
		<li><a href="first/filter">Filter 사용하기</a></li>
	</ul>
	
<h1 align="center">필터의 활용</h1>
<form action="member/regist" method="post">
	<label>아이디 : </label>
	<input type='text' name='userId'> <br>
	<label>비밀번호 : </label>
	<input type='password' name='password'> <br>
	<label>이름 : </label>
	<input type='text' name='name'> <br>
	<button type='submit'>가입하기</button>
</form>

👉 filter의 흐름

[n]은 호출(출력)되는 순서를 의미

1) FirstFilter

/* 서블릿으로 가는 요청을 가로챌 url 패턴 작성 */
@WebFilter("/first/filter")
public class FirstFilter extends HttpFilter implements Filter {
       
	/* 기본 생성자 */
    public FirstFilter() {
        System.out.println("[1] FirstFilter 인스턴스 생성!'o'");
    }

    /* 필터 인스턴스가 소멸될 때 호출되는 메소드 - 서버 종료 시 */
	public void destroy() {
		System.out.println("[6] filter destory 호출!👋");
	}

	/* servlet으로 request가 전달 되기 전에 요청을 가로채는 역할 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		/* request, response를 가공처리하기 위해 여기에 코드를 작성한다. */
		System.out.println("[3] filter doFilter 호출!'ㅁ'");

		/* 가공 처리를 한 뒤 다음 필터 혹은 서블릿의 service를 호출한다. */
		// pass the request along the filter chain
		chain.doFilter(request, response);
		
		/* 서블릿에서 처리 후에 다시 수행할 내용이 있으면 작성한다. */
		System.out.println("[5] 서블릿 요청 처리 완료!^ㅁ^");
	}

	/* 필터 인스턴스가 최초 생성 될 때 호출 되는 메소드 - 초기화 */
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("[2] filter init 호출!'^'");
	}
}

2) FirstFilterServlet

@WebServlet("/first/filter")
public class FirstFilterServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("[4] 서블릿 요청 확인 !!!!!!");
	}
}

👉 filter의 활용

1) web.xml

  <!-- 필터 등록 -->
  <filter>
  	<filter-name>encoding</filter-name>
  	<filter-class>com.greedy.section02.uses.EncodingFilter</filter-class>
  	<init-param>
  		<param-name>encoding-type</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  
  <!-- 필터 매핑 -->
  <filter-mapping>
  	<filter-name>encoding</filter-name> <!-- 위의 filter-name과 동일하게 -->
  	<url-pattern>/*</url-pattern> <!-- /* : 모든 url 패턴을 의미 -->
  </filter-mapping>

2) EncodingFilter

* 필터 등록은 web.xml에서 처리 */
// @WebFilter("/member/regist")
public class EncodingFilter extends HttpFilter implements Filter {

	private String encodingType;
	
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
		/* doFilter의 매개변수 request는 타입이 ServletRequest. 
		 * 이것은 HttpServletRequest의 상위 타입이기 때문에 getMethod()를 사용할 수 없음.
		 * 따라서 HttpServletRequest로 다운 캐스팅하여 getMethod() 사용 */
		HttpServletRequest hrequest = (HttpServletRequest) request;
		if("POST".equals(hrequest.getMethod())) { // POST 방식으로 요청되었을 경우, 인코딩 처리 필요
			request.setCharacterEncoding(encodingType);
		}
		
		// pass the request along the filter chain
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
		/* web.xml에서 필터 등록 시 기재한 init-param의 key를 이용하여 fConfig에서 꺼낼 수 있음 */
		encodingType = fConfig.getInitParameter("encoding-type"); // web.xml에서 가져온 키 값 입력
	}
}

3) PasswordEncryptFilter

/* member와 관련된 기능일 경우에만 필터를 거치도록 설정 */
@WebFilter("/member/*") 
public class PasswordEncryptFilter extends HttpFilter implements Filter {
    
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
		/* 기존 request 객체를 Wrapper 객체로 변경 */
		/* HttpServletRequest로 다운 캐스팅 */
		HttpServletRequest hrefquest = (HttpServletRequest) request;
		RequestWrapper wrapper = new RequestWrapper(hrefquest);

		// pass the request along the filter chain
		chain.doFilter(wrapper, response);
	}
}

4) ResgistMemberServlet

@WebServlet("/member/regist")
public class ResgistMemberServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/* 1. 인코딩 필터 (EncodingFilter)
		 * 2. 암호화 필터 (PasswordEncryptFilter)
		 * */
		
		String userId = request.getParameter("userId");
		String password = request.getParameter("password");
		String name = request.getParameter("name");
		
		System.out.println("userId : " + userId);
		System.out.println("password : " + password);
		System.out.println("name : " + name);
		
		/* 암호화 처리 된 패스워드는 동일한 값이 입력 되더라도 랜덤 솔팅 기법에 의해 매번 다른 값을 가지게 됨
		 * 나중에 DB에 이 상태로 기록하게 되면 가입된 회원 정보로 로그인할 때 비밀번호가 같은지는 어떻게 비교할 수 있나? 
		 * 암호화 된 문자열은 일반 문자열 비교가 불가능하고 BCryptPasswordEncoder의 matches 라는 메소드를 사용
		 * */
		
		/* matches() 메소드로 password 일치 여부 확인 */
		BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
		System.out.println("비밀번호가 pass01인지 확인 : " + passwordEncoder.matches("pass01", password));
		System.out.println("비밀번호가 pass02인지 확인 : " + passwordEncoder.matches("pass02", password));
	}
}

5) RequestWrapper

public class RequestWrapper extends HttpServletRequestWrapper {

	/* 부모 클래스에 기본 생성자가 존재하지 않으므로 자식 클래스에서도 request를 부모 클래스로 전달해주는 생성자가 필요 */
	public RequestWrapper(HttpServletRequest request) {
		super(request);
	}

	/* request 객체의 나머지 기능은 원래 기능을 그대로 사용하고 getParameter 메소드만
	 * overriding해서 재정의한 메소드를 호출 */
	@Override
	public String getParameter(String name) {
		
		String value = "";
		
		if("password".equals(name)) {
			/* 암호화 로직 작성 */
			BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
			value = passwordEncoder.encode(super.getParameter(name));
			/* 적용 후, password가 알 수 없는 문자들로 출력 (암호화 성공) */
		}
		return value;
	}
}

👀 JSP (Java Server Pages)

💁‍♀️ JSP란,
Java 언어를 기반으로 하는 Server Side 스크립트 언어이며,
HTML 코드에 Java 코드를 넣어 동적인 웹 페이지를 생성하는 웹 애플리케이션 도구

👉 index.html

<h1 align='center'>JSP</h1>
<ul>
	<li><a href='jsp/1_simpleJspState.jsp'>JSP 기본 문법</a></li>
	<li><a href='jsp/2_pageDirective.jsp'>page 지시자 태그</a></li>
	<li><a href='jsp/3_includeDirective.jsp'>include 지시자 태그</a></li>
	<li><a href='jsp/4_request.jsp'>JSP를 이용한 응답 처리</a></li>
</ul>

1) JSP 태그

<%@ %> : directive (지시자 태그)
<%! %> : declare (선언 태그)
<% %> : scriptlet (스크립틀릿 태그)
<%= %> : expression (브라우저 출력 태그)
<%-- --%> : comment (주석 태그)

<!-- 1. 페이지 지시자 태그
		페이지에 대한 설정을 하는 태그로 현재 페이지에 스크립틀릿 태그를 이용하여 작성하는 문법은 자바라는 의미이며,
		response header에 응답을 위한 설정을 하는 것도 가능 -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>1_simpleJspState</title>
</head>
<body>
	
	<h1>simpleJspState</h1>
	<p>
		JSP는 표면상으로는 HTML 문서와 유사하지만 JSP 컨테이너가 최초 JSP를 요청할 시 JSP를 서블릿으로 변환 시킨 뒤,
		서블릿 컨테이너가 변환 된 서블릿을 이용해 인스턴스를 생성하고 호출.
		JSP는 매 요청마다 기존 JSP 파일이 변환 되었는지 확인하여 변경이 없는 경우 기존 인스턴스를 사용하고,
		변경이 있는 경우 translate 과정을 거쳐 인스턴스를 다시 생성. <br>
	</p>
	<p>
		JSP는 HTML 기반의 문서에 Java 문법을 사용할 수 있도록 지원.
		JSP의 태그 엘리먼트를 이용하여 사용 목적 별로 자바 코드를 사용할 수 있도록 함.
		directive(지시자 태그), declare(선언 태그), scriptlet(스크립틀릿 태그), expression, comment(주석 태그)가 있음.
	</p>

	<!-- 2. JSP 주석 태그 -->
	<%-- HTML 주석은 클라이언트에게 노출되지만 JSP 주석은 클라이언트에게 노출되지 않음 --%>
	
	<!-- 3. 선언 태그
			서블릿으로 변환 시 선언 태그 내에 작성한 내용을 필드로 선언해준다. -->
	<%!
		private String name;
		private int age;
	%>
	
	<!-- 4. 스크립틀릿 태그 -->
	<%
		/* 간단한 자바 코드를 작성할 수 있는 부분이다.
		   자바의 주석과 동일하게 사용할 수 있다. 
		   선언한 태그 내에서 작성한 내용을 초기화하고 출력할 수도 있고, 간단한 로직 처리도 가능하다. 
		*/
		name = "치즈";
		age = 2;
		
		System.out.println("name : " + name);
		System.out.println("age : " + age);
		
		for(int i = 0; i < name.length(); i++) {
			System.out.println(name.charAt(i));
		}
		// [출력문]
		// name : 치즈
		// age : 2
		// 치
		// 즈
	%>
	
	<!-- 5. expression 태그
			PrintWriter를 이용하여 브라우저에 값을 내보내기 하여 브라우저에 보여지게 한다. 
			(변수명도 함께 출력)-->
	name : <%= name %> <br>
	age : <%= age %>
	<!-- 
	[브라우저 출력문]
	name : 치즈
	age : 2
	 -->
	
</body>
</html>

2) Exception

◼ pageDirective.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.Date, java.util.ArrayList" errorPage="errorPage.jsp"%>
<%-- 
	page 지시자 태그에서 사용 가능한 속성은 많지만, 현재 알고 있어야 하는 속성은 많지 않다.
	- import : java.lang 패키지 외의 클래스를 현 jsp 파일에서 사용할 때 import 속성에 정의하면
			   import 선언부를 작성하는 것과 같음
	- errorPage : 현재 페이지에서 Exception이 발생하게 되면 속성 값에 설정한 JSP 경로로 exception을 던짐
	- isErrorPage : 현재 페이지가 Exception을 처리할 페이지인 경우 true로 지정. 기본 값은 false.
--%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>2_pageDirective</title>
</head>
<body>
	<%
		Date date = new Date();
		System.out.println(date);
		
		ArrayList<String> list = new ArrayList<>();
	%>
	
	<%
		/* exception을 발생시키는 코드 작성 */
		String str = "Hello";
		char ch = str.charAt(5);
	%>
    
</body>
</html>

◼ errorPage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isErrorPage="true" %>
<%--
	isErrorPage="true" 설정을 통해 JSP 안에서 exception 객체 사용 가능
 --%>
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>errorPage</title>
</head>
<body>
	<%
		String exceptionType = exception.getClass().getName();
		String exceptionMessage = exception.getMessage();
	%>
	<!-- 브라우저 화면에 출력 -->
	<h1 align='center'><%= exceptionType %></h1>
	<h2 align='center'><%= exceptionMessage %></h2>
	
	<!--
		[브라우저 출력문]
		java.lang.StringIndexOutOfBoundsException
		String index out of range: 5
	-->
</body>
</html>

3) SimpleDateFormat

◼ includeDirective.jsp

<h1>include directive</h1>
	<div>
		<%-- include 지시자 태그를 이용하여 file 속성에 jsp 경로를 지정해주면 해당 jsp에 작성한 내용을
			 그대로 포함시켜 현재 jsp 파일을 동작시킴 --%>
		<%@ include file="today.jsp" %>
		<!-- 
			[브라우저 출력문]
			20230220일 월요일 오후 031007-->
		
		<%
			// today.jsp 파일에서 사용한 변수와 동일한 변수를 사용하면 컴파일 에러가 발생 
			// (include file은 jsp 파일 자체를 잘라내어온다라고 생각하면 됨)
			// String output = "";
		%>
	</div>

◼ today.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.Date, java.text.SimpleDateFormat"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>today</title>
</head>
<body>
	<%
		Date today = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy년 MM월 dd일 E요일 a hh시 mm분 ss초");
		String output = sdf.format(today);
	%>
	
	<h3><%= output %></h3>

</body>
</html>

4) request & response

◼ request.jsp

<h1 align='center'>메뉴 주문</h1>
	
	<form action='<%= request.getContextPath() %> /menu/order' method="post"> <!-- 절대 경로 작성 -->
		<select id="menu" name="menuName">
			<option>함박스테이크</option>
			<option>짜파게티</option>
			<option>짬짜면</option>
			<option>순두부찌개</option>		
		</select>
		<input type="number" min="0" max="100" step="1" name="amount">
		<button type="submit">선택 완료</button>
	</form>

◼ MenuOrderServlet class

@WebServlet("/menu/order")
public class MenuOrderServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/* 1. 요청 받은 값 확인 및 검증 */
		request.setCharacterEncoding("UTF-8");
		
		String menuName = request.getParameter("menuName");
		int amount = Integer.parseInt(request.getParameter("amount"));
		
		/* 2. 비즈니스 로직 처리 
		 * 비즈니스 로직은 대부분 DB에 CRUD 연산을 이용해 이루어지게 되는데 여기서는 간단한 처리만 시도 */
		int orderPrice = 0;
		
		switch(menuName) {
		case "함박스테이크" : orderPrice = 6000 * amount; break;
		case "짜파게티" : orderPrice = 7000 * amount; break;
		case "짬짜면" : orderPrice = 5000 * amount; break;
		case "순두부찌개" : orderPrice = 8000 * amount; break;
		}
		
		/* 3. 응답 페이지를 생성 후 응답 
		 * 직접 페이지를 생성한 뒤 스트림으로 내보내기를 할 수도 있지만, 
		 * 페이지 작성이 간결하고 응답을 보여주는 역할에 대해서 구분하여 응답 페이지만 전용으로 처리하는 JSP로
		 * request에 값을 담은 다음 forward 해서 화면에 출력 
		 * */
		
		/* 전달하고자 하는 값 set */
		request.setAttribute("menuName", menuName);
		request.setAttribute("amount", amount);
		request.setAttribute("orderPrice", orderPrice);
		
		/* JSP로 forward */
		RequestDispatcher rd = request.getRequestDispatcher("/jsp/5_response.jsp"); // JSP 경로 작성
		rd.forward(request, response);
	}
}

◼ response.jsp

<%
// request에 setAttribute로 담은 값을 getAttribute로 꺼내기
// getAttribute() 반환값은 object 이므로 String으로 다운 캐스팅
String menuName = (String)request.getAttribute("menuName");
int amount = (Integer)request.getAttribute("amount");
int orderPrice = (Integer)request.getAttribute("orderPrice");
%>

<h1>주문하신 음식 : <%= menuName %></h1>
<h3>주문하신 수량 : <%= amount %></h3>
<h3>결제하실 최종 금액 : <%= orderPrice %></h3>

👀 EL & JSTL

💁‍♀️ EL이란,


💁‍♀️ JSTL이란,

👉 index.html

profile
Tiny little habits make me

0개의 댓글

관련 채용 정보