<< 학습 목표 >>

1. Context와 ServletContext를 설명할 수 있다.


서블릿으로 개발하다 보면 ServletContext와 ServletConfig 타입 객체를 사용해야하는 경우가 종종 생김

여기서는 ServletContext 타입 객체가 무엇이고 이 객체들을 통해서 어떤것들을 할 수 있는지 알아보자

 

우선 컨텍스트(Context) 가 있음

컨텍스트란 웹 프로젝트의 정보를 뜻함

웹 프로젝트 이름은 무엇이고 웹 프로젝트는 어느 경로에 있고, 웹 프로젝트가 동작하는 서버의 정보는 어떻게 되고 등 웹 프로젝트의 정보를 담고 있는 것이 컨텍스트임

이 컨텍스트는 웹 프로젝트의 정보이므로 웹 프로젝트 마다 하나씩 생성됨

 

이 컨텍스트는 C 드라이브에 있는 톰캣의 경우에는 [ C 드라이브 내 톰캣 ] -> [ conf 폴더 ] -> [ server.xml ] 파일을 보면 Context 태그로 등록 되어있음

 

이클립스에 있는 톰캣의 경우에는 이클립스 왼쪽에 [ Explorer 창 ] -> [ Servers ] -> [ Tomcat ~ ] -> [ server.xml ] 파일을 보면 마찬가지로 Context 태그로 등록 되어있음

 

이 컨텍스트 태그에다가 웹 프로젝트와 관련된 여러 정보를 등록해둬야함

아직 우리는 그렇게 세세하게 웹 프로젝트 정보를 설정할 일이 없으므로 "그렇구나" 정도로만 기억해두면 됨

 

서블릿 안에서도 이 컨텍스트를 가져와서 웹 프로젝트의 정보를 확인해야되는 경우가 종종 생기는데 서블릿이 컨텍스트를 가져올 때 사용하는 객체가 ServletContext 임

 

위 이미지에서 [ 웹 프로젝트1 ] 만 더 확대한 그림을 보자

 

웹 프로젝트 안에는 HTML, CSS, JS, 서블릿(Servlet), JSP 등등이 들어있을 것

서블릿은 서블릿 컨테이너(ServletContainer)에 의해서 동작함

 

서블릿 컨테이너는 서블릿에서 웹 프로젝트의 정보가 필요할 때, 즉 컨텍스트가 필요할 때를 위해서 ServletContext 타입 객체를 갖고 있음

이 객체 안에는 컨텍스트가 가지고 있는 웹 프로젝트의 정보가 들어있음

위 그림에서 연결해놓은것처럼 컨텍스트(웹 프로젝트의 정보)는 ServletContext와 같다고 보면 됨

 

어떤 서블릿에서 ServletContext가 필요하다면 ServletContext를 가져다 사용할 수 있음

또한 회원 가입 서블릿에서 ServletContext에 무언가를 저장해두면 다른 서블릿에서는 ServletContext를 통해서 회원 가입 서블릿에서 저장해둔 값을 꺼낼 수도 있음


ServletContext 를 활용한 간단한 예시 서비스를 보자

우리 서비스에 어떤 경로로 접근했든 오늘의 방문자 수를 알고 싶은 상황임

그리고 방문자 수는 매 서비스 마다 카운팅을 하고 싶음

우리 서비스의 방문자 수가 0명인 상태에서 철수가 우리 서비스의 A 페이지로 바로 접속을 했다면 방문자 수가 1명이 됨

그 후 철수가 이어서 B 페이지로 접속을 했다면 방문자 수가 2명이 되도록 하고 싶은 상황

상식적으로 생각해보면 "방문자 수는 1명 아닌가?" 싶지만 그런 처리를 하려면 굉장히 복잡하고 또 다른 방향으로 깊은 얘기를 해야하므로 그런건 고려하지 않을 예정

그리고 이건 ServletContext 를 경험하기 위한 예시 상황으로 실제 서비스에서 방문자 수를 체크할 때는 ServletContext 로 체크하지 않음

 

chapter04 -> service1, service2 서블릿을 만들고 아래 코드를 각 서블릿에 차례대로 입력하자

package chapter04;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
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("/chapter04/service_1")
public class Service1 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext sc = getServletContext();
		
		int count = 0;
		if(sc.getAttribute("count") != null) {
			count = (int) sc.getAttribute("count");
		}
		
		count++;
		sc.setAttribute("count", count);
		
		response.setCharacterEncoding("UTF-8");
		
		PrintWriter pw = response.getWriter();
		pw.print("<html>");
		pw.print("<head>");
		pw.print("	<meta charset=\"UTF-8\">");
		pw.print("	<title>Service1</title>");
		pw.print("</head>");
		pw.print("<body>");
		pw.print("<p>여기는 Service1 입니다</p>");
		pw.print("<h1>전체 방문자 수는 " + count + " 명입니다.</h1>");
		pw.print("</body>");
		pw.print("</html>");
	}

}
package chapter04;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
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("/chapter04/service_2")
public class Service2 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext sc = getServletContext();
		
		int count = 0;
		if(sc.getAttribute("count") != null) {
			count = (int) sc.getAttribute("count");
		}
		
		count++;
		sc.setAttribute("count", count);
		
		response.setCharacterEncoding("UTF-8");
		
		PrintWriter pw = response.getWriter();
		pw.print("<html>");
		pw.print("<head>");
		pw.print("	<meta charset=\"UTF-8\">");
		pw.print("	<title>Service1</title>");
		pw.print("</head>");
		pw.print("<body>");
		pw.print("<p>여기는 Service2 입니다</p>");
		pw.print("<h1>전체 방문자 수는 " + count + " 명입니다.</h1>");
		pw.print("</body>");
		pw.print("</html>");
	}

}

 

별도의 웹 페이지는 없고 서블릿 두 개만 존재함

웹 페이지가 없는 이유는 서블릿에서 웹 페이지를 출력하기 때문에

다시 얘기하지만 이 예시(방문자 수 세기) 자체에 대해서 깊게 생각할 필요는 없음

단순히 "ServletContext가 있다" 는 걸 보여주기 위한 예시임

 

서블릿을 보자

컨테이너가 준비해둔 서블릿 컨텍스트를 가져오기 위해 getServletContext 메서드를 호출했음(1)

 

서블릿 컨텍스트 안에 값을 저장할 때는 setAttribute 메서드를 사용하고 값을 꺼낼 때는 getAttribute 메서드를 사용함

setAttribute 로 값을 저장할 때 저장할 값은 Object 로 형변환 되서 저장되고 꺼낼 때는 Object 로 꺼내짐

그래서 서블릿 컨텍스트 안에 들어있는 값을 꺼내는데 그러한 이름의 값이 없다면 null이 반환되고 그러한 이름의 값이 있다면 그 값이 Object 타입으로 꺼내짐 

 

서블릿 컨텍스트 안에 이름이 count 인 속성(attribute)의 값이 있다면(2) 그 값을 꺼내서 원래의 형태로 형변환 후 count 변수에 저장함(3)

서블릿 컨텍스트 안에 이름이 count인 속성이 없다면 count 변수의 값은 그대로 0임

count변수는 웹 프로젝트 방문자의 수를 나타냄

 

count 변수의 값을 하나 증가 시킨 후 서블릿 컨텍스트의 setAttribute 메서드를 통해 서블릿 컨텍스트 안에 count 이름으로 현재 웹 프로젝트 방문자의 수를 저장함

 

그 후에는 HTML을 사용해 현재 웹 프로젝트 방문자의 수를 출력하고 있음

 

이제 서버를 실행시키고 Service1 또는 Service2 서블릿으로 접근하자

둘 중 어느 서블릿으로 먼저 접근해도 상관 없으며 방문자 수가 1부터 하나씩 증가하는걸 볼 수 있음

 

서블릿 컨텍스트는 서블릿 컨테이너가 생성하고 관리하므로 서버를 끄기 전까지 계속 유지됨

그래서 서버를 끄기 전까지는 웹 프로젝트 방문자 수가 누적됨

서버를 끄면 서블릿 컨테이너도 꺼지고 그에 따라 서블릿 컨텍스트도 사라지므로 웹 프로젝트 방문자 수가 사라짐


서블릿 컨텍스트는 컨텍스트(웹 프로젝트의 정보) 라고 했으므로 서블릿 컨텍스트를 사용해서 웹 프로젝트의 몇 가지 정보를 출력해보자

 

chapter04 -> serverInfo 서블릿을 추가하고 아래 코드를 추가하자

package chapter04;

import java.io.IOException;

import javax.servlet.ServletContext;
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("/chapter04/server_info")
public class ServerInfo extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext sc = getServletContext();
		
		System.out.println("프로젝트 명 => " + sc.getServletContextName());
		System.out.println("서블릿 버전 => " + sc.getMajorVersion());
		System.out.println("컨테이너 정보 => " + sc.getServerInfo());
	}

}

 

이를 통해 "서블릿 컨텍스트가 컨텍스트구나" 를 알 수 있을 것

 

실제 개발에서 서블릿 컨테이너를 활용하는 상황은 거의 없음

딱 한순간! 파일 업로드 기능을 구현할 때 서블릿 컨텍스트가 필요함

그래서 사실 지금까지 언급한 내용은 전혀 몰라도 상관 없음

파일 업로드 기능은 이 카테고리의 마지막에 설명할 것이니 그때가서도 "ServletContext 이 블로그에서 알려줬었는데 뭐였지?" 정도로 기억해도 무방함

 

서블릿 컨텍스트(ServletContext) 객체를 통해서 그나마 자주 사용하는 메서드는 아래와 같음

메서드 명 설명
setAttribute(String, Object) 서블릿 컨텍스트 안에 값을 저장함
값은 Object 타입으로 형변환되 저장되며 첫 번째 인자가 저장할 값의 이름임
getAttribute(String) 이름을 사용해 값을 꺼냄
꺼낸 값은 Object 타입이므로 원래의 형태로 형변환 해줘야함
removeAttribute(String) 이름에 저장된 값을 삭제함
getRealPath(String) 인자로 경로를 넣으며 해당 경로가 프로젝트 내에서 실제 경로가 어떻게 되는지 알려주는 메서드
특히! 파일 업로드 시에 반드시 필요한 메서드임
728x90
LIST

<< 학습 목표 >>

1. 포워딩하는 곳에서 포워딩을 받는 곳으로 데이터를 전달하기 위한 적절한 포워딩 방법을 선택할 수 있다.

2. 포워딩하는 곳에서 포워딩을 받는 곳으로 다양한 데이터를 전달할 수 있다.


포워딩을 할 때 포워딩을 하는 곳이 있고 포워딩을 받는 곳이 있음

포워딩을 하는 곳은 RequestDispatcher 를 사용하는 서블릿이고 포워딩을 받는 곳은 RequestDispatcher 에 명시된 HTML, JSP, 서블릿 등임

 

앞서 마지막에 만들었던 서블릿을 보자

 

포워딩을 하는 곳은 Login4 서블릿이고 포워딩을 받는 곳은 loginSuccess.html, login4.html 임

여기서는 포워딩을 받는 곳이 HTML 페이지 뿐이지만 JSP, 서블릿도 포워딩을 받을 수 있음


포워딩을 하는 곳에서 포워딩을 받는 곳으로 데이터를 전달해야할 때는 RequestDispatcher 를 사용하는게 바람직함

location.href, sendRedirect, addHeader 방식의 포워딩을 할 때도 포워딩을 받는 곳으로 데이터를 전달하기도 하지만 정말 어쩔 수 없는 경우이거나 잘못 사용하고 있을 가능성이 큼

 

location.href, sendRedirect, addHeader 방식의 포워딩을 할 때 포워딩을 받는 곳으로 데이터를 전달하지 말아야하는 이유는 이 세 방식은 데이터를 전달할 때 GET 방식으로 파라미터를 붙여서 데이터를 전달해야함

( 위 서블릿의 sendRedirect 를 잠깐 보고 오자 )

GET 방식의 특징 중 하나는 파라미터가 URL에 노출된다는 것

포워딩을 받는 곳에서 필요한 데이터가 중요한 개인 정보라거나 프로그램 동작에 민감한 영향을 미치는 데이터라면 사용자에게 그대로 노출되기 때문에 이 방식으로는 왠만하면 데이터를 전달하면 안됨

또 GET 방식의 특징 중 하나로 텍스트 밖에 전달할 수 없기 때문에 정보(객체)를 전달해야하는 경우에 전달할 수 있는 방법이 없음

 

RequestDispatcher 를 사용해 데이터를 전달하면 브라우저의 URL이 바뀌지 않으면서 포워딩 되기 때문에 포워딩 받는 곳에서 필요한 데이터가 노출될 일이 없음

또한 전달하고 싶은 모든 형태의 데이터를 다 전달할 수 있음

 

여기서는 RequestDispatcher 를 사용해 포워딩 받는 곳에서 데이터를 전달 하는 방법과 포워딩 받는 곳에서 전달 받은 데이터를 꺼내는 방법을 배워보자

굉장히 간단함


앞서 만들었던 forwarding_1과 forwarding_2 서블릿을 활용하자

package chapter04;

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("/chapter04/forwarding_1")
public class Forwarding1 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding1 서블릿의 doGet 메서드 호출됨");
		
		RequestDispatcher rd = request.getRequestDispatcher("/chapter04/forwarding_2");
		rd.forward(request, response);
	}

}
package chapter04;

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("/chapter04/forwarding_2")
public class Forwarding2 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding2 서블릿의 doGet 메서드 호출됨");
	}

}

 

여기서 주목해야할 부분은 포워딩을 하는 시점임

<< 포워딩을 하는 곳 ( Forwarding1 서블릿 ) >>

 

포워딩을 하는 시점(1) 에 보면 요청 정보(HttpServletRequest)와 응답 정보(HttpServletResponse) 를 담아서 포워딩을 하고 있는 것

그래서 포워딩을 하는 곳에서 포워딩을 받는 곳으로 데이터를 전달하고 싶을 때는 요청 정보 안에 데이터를 담아서 전달함

요청 정보에 데이터를 담을 때는 setAttribute 메서드를 사용함(1)

 

setAttribute 메서드의 형식과 설명을 읽어보면 첫 번째 인자는 저장할 데이터의 이름이고 두 번째 인자는 저장할 데이터임

 

우리가 request.setAttribute("message", "전달하는 데이터") 코드를 썼으므로 요청 정보 안에 [ message=전달하는 데이터 ] 형식으로 데이터가 저장되고 포워딩을 받는 곳에서 이름(message) 을 사용해서 데이터(전달하는 데이터) 를 꺼내서 쓸 수 있음

! 여기서 주의해야할 점 ! 데이터를 저장하기 위해서 setAttribute 메서드를 사용했다는 점

또 ! 주의해야할 점 ! 저장하는 데이터의 타입이 Object 인 점에 주목하자

우리가 뭘 저장하던 요청 정보 안에 저장될 때는 Object 타입으로 형변환 되서 저장됨

( Forwarding1 서블릿에 위와 같이 data1, data2, data3 도 저장하는 코드를 추가하자 )

 

이번에는 포워딩을 받는 곳에서 전달 받은 데이터를 꺼내보자

포워딩을 받는 곳(Forwarding2 서블릿)에서 전달 받은 데이터를 꺼낼 때는 getAttribute 메서드를 사용함(1)

 

특히나 꺼낼 때 getAttribute 메서드를 사용해서 꺼낸다. 이외에도 데이터가 저장될 때 원래 형태에서 Object 로 변환되 저장되었으므로 꺼낼 때도 꺼낸 후에 형변환을 통해서 원래 형태로 복원 시켜줘야함

 

이번에는 여러분이 먼저 data1, data2, data3 을 꺼내서 출력해보자

 

이제 Forwarding1 서블릿으로 요청을 보내는 웹 페이지를 만들고 요청을 보내 Forwarding2 에서 message부터 data3까지 출력되는걸 확인하자

 

webapp -> chapter04 -> request.html 페이지를 추가하고 아래 코드를 추가하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>RequestDispatcher 방식 포워딩</title>
</head>
<body>
	<a href="/studyProject/chapter04/forwarding_1">서버로 요청을 보냄</a>
</body>
</html>

 

이제 서버를 실행시키고 웹 페이지에 접근해 [ 서버로 요청을 보냄 ] 을 클릭하자


문자열을 전달할 수 있으니 데이터들(배열)이나 정보(객체)도 당연히 전달할 수 있음

 

객체를 생성하기 위해 chapter04 -> Person 클래스를 추가 하고 아래 코드를 넣자

package chapter04;

public class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

	// getter & setter 생략
}

 

Fowarding1 서블릿 코드를 아래와 같이 바꾸자

package chapter04;

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("/chapter04/forwarding_1")
public class Forwarding1 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding1 서블릿의 doGet 메서드 호출됨");
		
//		request.setAttribute("message", "전달하는 데이터");
//		request.setAttribute("data1", 1);
//		request.setAttribute("data2", 3.14);
//		request.setAttribute("data3", 'a');
		int[] array = {1, 2, 3};
		
		List<Integer> list = new ArrayList<>();
		list.add(1);
		list.add(2);
		list.add(3);
		
		Person p = new Person("홍길동", 21);
		
		request.setAttribute("array", array);
		request.setAttribute("list", list);
		request.setAttribute("person", p);
		
		RequestDispatcher rd = request.getRequestDispatcher("/chapter04/forwarding_2");
		rd.forward(request, response);
	}

}

 

Forwarding2 서블릿을 아래와 같이 바꾸자

package chapter04;

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

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("/chapter04/forwarding_2")
public class Forwarding2 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding2 서블릿의 doGet 메서드 호출됨");
		
//		String message = (String) request.getAttribute("message");
//		int data1 = (int) request.getAttribute("data1");
//		double data2 = (double) request.getAttribute("data2");
//		char data3 = (char) request.getAttribute("data3");
//		
//		System.out.println("message => " + message);
//		System.out.println("data1 => " + data1);
//		System.out.println("data2 => " + data2);
//		System.out.println("data3 => " + data3);
		
		int[] array = (int[]) request.getAttribute("array");
		List<Integer> list = (List<Integer>) request.getAttribute("list");
		Person p = (Person) request.getAttribute("person");
		
		System.out.println(Arrays.toString(array));
		System.out.println(list);
		System.out.println(p);
	}

}

 

서버를 재시작하고 [ 서버로 요청을 보냄 ] 을 클릭해보자


 

지금까지 포워딩을 할 때 데이터를 전달하는 방법을 배웠음

여기서 특징은 포워딩을 서블릿에서 서블릿으로 했음

포워딩은 서블릿에서 HTML 또는 서블릿에서 서블릿 또는 서블릿에서 JSP 로 할 수 있다고 했음

 

서블릿에서 HTML 로 포워딩할 때 HTML로 데이터를 전달할 수 있을까? 없음

HTML은 보여주기만 가능한 언어이기 때문에 서블릿에서 HTML로 데이터를 전달할 순 없음

 

서블릿에서 JSP 로 포워딩할 때 JSP로 데이터를 전달할 수 있을까? 있음

그러나 아직 우리가 JSP를 배우지 않았기 때문에 나중에 JSP를 할 때 배울 것

728x90
LIST

<< 학습 목표 >>

1. RequestDispatcher 를 사용해 포워딩 할 수 있다.


앞서 두 가지 포워딩 방법 중 첫 번째 포워딩 방법을 배웠음

1. 클라이언트에게 이동할 곳을 지정해주는 방법

2. 서버 안에서 이동하는 방법

 

이번에는 두 번째 포워딩 방법인 [ 서버 안에서 이동하는 방법 ] 을 배워보자

두 번째 포워딩 방법은 아래 그림과 같이 "복잡한 포워딩" 임

아래 그림과 같이 동작하는 포워딩이 아닌 지금 배울 포워딩은 "복잡한 포워딩" 이라고 생각하면 됨

그렇다고 해서 어렵거나 진짜 복잡하지는 않고 첫 번째 포워딩 방법 보다 서버에서 할 것이 늘어난 것

전에 했던 포워딩과 이 포워딩의 차이는 클라이언트가 포워딩 됐다는걸 아느냐 모르느냐임

클라이언트는 포워딩 됐다는걸 어떻게 알까? 브라우저의 URL이 바뀌느냐 안바뀌느냐로

전에 했던 포워딩을 하게 되면 요청했던 URL과 결과를 보고 있는 페이지의 URL이 다르므로 클라이언트가 "포워딩(이동) 됐구나" 를 알 수 있지만 이번 방법을 사용하게 되면 요청했던 URL은 그대로이지만 결과를 보고 있는 페이지는 상황에 맞는 적절한 페이지가 보여짐


<< 서버 안에서 이동하는 방법 >>

이 포워딩의 좀 더 적절한 예를 들자면 분업을 예로 들 수 있음

앞서 서블릿으로 클라이언트에게 "결괏값을 담고 있는 페이지" 를 전달하기가 굉장히 불편했음

Spring 또는 실무의 웹 프로젝트에서는 서블릿이 클라이언트의 요청을 받아 처리하고 클라이언트에게 상태 코드와 결괏값을 전달해야한다면 그대로 서블릿에서 응답하지만 "결괏값을 담고 있는 페이지" 를 전달할 때는 그 처리를 JSP에게 넘김

 

1. 클라이언트가 서버에게 요청함

2. 결괏값을 전달해야한다면 서블릿이 클라이언트에게 결괏값을 전달함

3. 결과 페이지를 전달해야한다면 서블릿이 여기서 배울 포워딩 방법(RequestDispatcher) 를 사용해 JSP로 포워딩함

4. JSP 로 만들어진 결과 페이지를 클라이언트에게 전달함

 

이렇게 서블릿으로 해결하기 어렵거나 불편한 부분을 다른 곳으로 넘겨주는 방법이 여기서 배울 포워딩 방법임

 

이 방법은 요청 정보(HttpServletRequest) 안에 들어있는 RequestDispatcher 를 꺼내서 포워딩을 하는 방법임


바로 RequestDispatcher 를 사용한 포워딩 코드를 보자

우선 웹 페이지부터 추가하자

webapp -> chapter04 -> request.html 을 만들고 아래 코드를 입력하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>RequestDispatcher 방식 포워딩</title>
</head>
<body>
	<a href="/studyProject/chapter04/forwarding_1">서버로 요청을 보냄</a>
</body>
</html>

이 페이지는 굉장히 간단한 페이지로 a 태그를 사용해 서버로 요청을 보냄

이때 우리가 어느 서버로 요청을 보내는지 a 태그의 href 속성을 잘 보자

 

이번에는 요청을 받을 서블릿을 추가하자

chapter04 -> Forwarding1 서블릿을 추가하고 아래 코드를 추가하자

package chapter04;

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("/chapter04/forwarding_1")
public class Forwarding1 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding1 서블릿의 doGet 메서드 호출됨");
		
		RequestDispatcher rd = request.getRequestDispatcher("/chapter04/forwarding_2");
		rd.forward(request, response);
	}

}

 

또 chapter04 -> Forwarding2 서블릿을 추가하고 아래 코드를 추가하자

package chapter04;

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("/chapter04/forwarding_2")
public class Forwarding2 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Forwarding2 서블릿의 doGet 메서드 호출됨");
	}

}

 

웹 페이지에서 [ 서버로 요청을 보냄 ] 을 클릭하면  Forwarding1 서블릿의 doGet 메서드가 동작해 "Forwarding1 서블릿의 doGet 메서드 호출됨" 이 출력됨(1)

그 후 클라이언트 몰래 포워딩을 하기 위해 RequestDispatcher 를 사용해 포워딩을 하고 있음

먼저 요청 정보(HttpServletRequest) 안에 들어있는 RequestDispatcher 객체를 꺼내는데(2) 이때 RequestDispatcher 를 통해서 이동할 다음 서블릿의 경로를 지정해줘야함(3)

꺼낸 RequestDispatcher 객체의 forward 메서드를 사용해서 포워딩을 하고 있음(4)

 

RequestDispatcher 방식으로 포워딩을 하면 클라이언트에게 응답이 가는게 아닌 그 다음 서블릿(/chapter04/forwarding_2) 의 doGet 메서드가 호출됨

서블릿(Forwarding2)의 doGet 메서드가 동작해 "Forwarding2 서블릿의 doGet 메서드 호출됨" 이 출력되고 난 후에 Forwarding2 서블릿이 클라이언트에게 응답을 함

 

처음 웹 페이지에서 [ 서버로 요청을 보냄 ] 을 클릭해 클라이언트는 forwarding_1 서블릿으로 요청을 보냈음

요청을 받은 서블릿 내부에서는 RequestDispatcher 를 사용해서 forwarding_2 서블릿으로 처리를 넘겼음

그러나 웹 페이지의 URL은 여전히 forwarding_1 임 

이렇게 RequestDispatcher 를 사용하면 클라이언트의 URL이 바뀌지 않기 때문에 내부에서 어떤 동작이 이뤄지는지 알지 못한다는 특징이 있음

 

브라우저의 URL을 확인해보자

 

브라우저에서는 왜 빈 화면만 보일까??

우리는 두 번째 서블릿에서 Sysout만 하고 있지 "상태 코드" 또는 "결괏값"을 전달하거나 "결괏값을 담은 페이지"를 전달하지 않고 있기 때문에 클라이언트 페이지에서 보이는건 아무것도 없음

 

여기까지 RequestDispatcher의 내용 끝~!


이번에는 지금까지 배운 포워딩 방법을 활용해보자

이 전 글에서 배운 포워딩 방법과 이번에 배운 포워딩 방법을 다 사용함

 

이 글의 서론에서 실무에서는 서블릿의 RequestDispatcher와 JSP를 사용한다고 했음

우리는 아직 JSP를 배우지 않았기 때문에 서블릿과 HTML을 사용해보자

 

로그인 페이지와 로그인 서블릿, 로그인 결과를 보여줄 페이지 이 세 개를 사용한 로그인 서비스를 만들자

사용자는 로그인 웹 페이지에서 아이디, 비밀번호를 입력하고 [ 로그인 ] 버튼을 누름(1)

로그인 서블릿은 로그인 요청을 받고 로그인 처리를 함(2)

이때, 로그인에 성공했다면 로그인 성공 페이지로 포워딩함(3)

로그인에 실패했다면 로그인 페이지에 GET 방식으로 status 파라미터를 붙여 로그인 페이지로 포워딩함(4)

 

 

먼저 로그인 웹 페이지를 만들자

webapp -> chapter04 -> login4.html 을 추가하고 아래 코드를 입력하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>로그인</title>
	<style type="text/css">
		p {color: red; display: none;}
	</style>
</head>
<body>
	<form action="/studyProject/chapter04/login_4" method="POST">
		아이디: <input type="text" name="id"><br>
		비밀번호: <input type="password" name="pw"><br>
		<p>아이디 또는 비밀번호를 확인하세요.</p>
		<input type="submit" value="로그인">
	</form>
	
	<script type="text/javascript">
		let parameter = location.search;
		if(parameter != '') {
			parameter = parameter.split("=");
			
			let name = parameter[0];
			let value = parameter[1];
			if(name == "status" && value == "fail") {
				let pTag = document.querySelector("p");
				pTag.style.display = "";
			}
		}
	</script>
</body>
</html>

사용자가 로그인 페이지에 접근하면 아이디, 비밀번호, [ 로그인 ] 버튼이 보임

p 태그는 display: none 으로 해뒀기 때문에 보이지 않음

이 웹 페이지는 위 동작 과정 그림의 (1), (4) 를 담당함

사용자에게 아이디, 비밀번호를 입력 받고 [ 로그인 요청 ] 을 서버로 보낼 수 있게 해주는 페이지(1) 이고  사용자가 아이디 또는 비밀번호를 잘못 입력해 로그인에 실패했을 경우에는 서버가 보내준 GET 파라미터를 꺼내서 status 파라미터 값이 fail일 경우에는 로그인 실패 메세지(아이디 또는 비밀번호를 확인하세요.) 를 보여주는 페이지(4) 임

 

만약 사용자가 웹 개발자라면 이 웹 페이지를 열어보면 페이지 내 JS 코드를 해석할 수 있을 것

그리고 로그인 페이지의 URL 뒤에 GET 방식으로 status=fail 파라미터를 붙인다면 p 태그가 보여 "아이디 또는 비밀번호를 확인하세요." 가 보일 것

 

로그인 웹 페이지를 이용하는 사용자는 웹 개발을 모르는 사람이라고 하겠음

 

 

로그인 요청을 받을 서블릿을 만들자

chapter04 -> login4 서블릿을 추가하고 아래 코드를 입력하자

package chapter04;

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("/chapter04/login_4")
public class Login4 extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
		
		String tempId = "myId";
		String tempPw = "myPw";
		
		if(id.equals(tempId) && pw.equals(tempPw)) {
			RequestDispatcher rd = request.getRequestDispatcher("/chapter04/loginSuccess.html");
			rd.forward(request, response);
		} else {
			response.sendRedirect("/studyProject/chapter04/login4.html?status=fail");
		}
	}

}

 

이 서블릿에서 살펴볼 것이 두 가지 있음

1. 로그인 성공의 경우와 로그인 실패의 경우 포워딩 하는 방식이 다름

2. 로그인 성공의 경우 포워딩 경로가 /chapter04 로 시작하고 로그인 실패의 경우 포워딩 경로가 /studyProject 로 시작함

 

<< 로그인 성공의 경우와 로그인 실패의 경우 포워딩 하는 방식이 다름 >>

로그인에 성공했을 경우 RequestDispatcher 를 사용해 포워딩하고 있는데 왜그럴까?

특별한 이유는 없음

그저 우리가 여기서 RequestDispatcher 를 배웠기 때문에 사용한 것

로그인에 실패했을 경우 sendRedirect 를 사용해 포워딩하고 있는데 왜그럴까?

더보기

특별한 이유는 없음

그저 사용하고 싶어서 사용했을 뿐


 

로그인에 성공했을 경우 서블릿 자체에서 로그인 성공 페이지를 전달하도록 PrintWriter 객체를 꺼내서 로그인 성공 페이지를 print 해도되고 포워딩을 할 때는 앞서 배운 세 가지와 여기서 배운 한 가지 중 어느 것을 사용해도 상관 없음

로그인에 실패했을 때도 마찬가지로 서블릿 자체에서 로그인 실패 페이지를 전달하도록 PrintWriter 객체를 꺼내서 로그인 실패 페이지를 print 해도 되고 포워딩을 할 때는 앞서 배운 세 가지와 여기서 배운 한 가지 중 어느 것을 사용해도 상관 없음

 

<< 로그인 성공의 경우 포워딩 경로가 /chapter04 로 시작하고 로그인 실패의 경우 포워딩 경로가 /studyProject 로 시작함 >>

RequestDispatcher 를 사용할 때는 URL에서 프로젝트 경로(http://localhost:8080/studyProject)까지는 반드시 생략해야함

서버 자체에서 포워딩을 하기 때문에 프로젝트 경로까지는 알고 있어서 생략해야함

만약 프로젝트 경로를 붙이면 문제가 생김

 

앞서 배운 세 가지 방식은 클라이언트에게 "여기로 가!" 라고 지시한것이기 때문에 정확하게 입력한다면 프로토콜(http)부터 써야함

브라우저는 URL에 프로토콜 ~ 포트번호까지(http://localhost:8080) 을 생략하면 현재 페이지의 프로토콜 ~ 포트번호까지 자동으로 붙여서 사용한다고 했음

그래서 sendRedirect 로 포워딩을 했을 때는 브라우저가 서버의 지시를 받고 전달 받은 URL의 앞에 "http://localhost:8080" 을 붙여서 이동함

 

이제 서버를 실행시키고 로그인 페이지에 접근해서 아이디, 비밀번호를 정확하게 입력했을 때 결과와 URL을 확인하자

 

이번에는 아이디와 비밀번호를 다르게 입력하고 결과와 URL을 확인하자

 

여기까지 포워딩 방식을 사용해서 조금 더 실무에 가까운 로그인 페이지를 만들어봤음

이 카테고리(Servlet)에서는 서블릿만 배우기 때문에 실무와 똑같은 프로젝트를 할 수는 없지만 이 카테고리의 마지막에 서블릿에서 배운 모든걸 활용해서 간단한 서비스를 만들어 볼 예정임

728x90
LIST

<< 학습 목표 >>

1. 포워딩의 두 가지 방식에 대해서 설명할 수 있다.

2. 세 가지 방식의 포워딩 코드를 작성할 수 있다.

3. 세 가지 방식 포워딩의 특징을 설명할 수 있다.


지금까지는 클라이언트에게 "결괏값" 을 보내거나 "결괏값을 담고 있는 페이지"를 보냈음

경우에 따라서는 클라이언트에게 "결과로 B 페이지로 이동해!" 라고 지시해야하는 경우가 생김

이렇게 "다른 곳으로 이동해!" 라고 지시하는 걸 포워딩(Forwarding) 이라고 함

 

포워딩을 하는 방법은 크게 두 가지가 있음

1. 클라이언트에게 이동할 곳을 지정해주는 방법

2. 서버 안에서 이동하는 방법

 

대출을 하기 위해서 은행에 방문한 상황을 예로 들어보자

대출해야하니 [ 대출 파트 ] 로 가야하지만 [ 예금, 출금, 적금 파트 ] 로 가서 "대출하러 왔는데요~" 라고 한 상황임

은행 = 서버 / 각 파트 직원 = 서블릿 / 고객 = 클라이언트

 

그때 직원이 [ 대출은 대출 파트에 가셔야합니다. ] 라고 했다면 포워딩의 1번 상황임

고객이 직접 [ 대출 파트 ] 로 이동해야하는 것처럼 포워딩 1번 방식을 사용하면 서버는 클라이언트에게 이동(포워드 / Forward) 할 경로를 알려줘야하고 클라이언트는 해당 경로로 이동해야함

( 이 상황은 그저 예인것으로 포워딩도 이와 같은 순서로 동작하지는 않음 / "이런 느낌의 간단한 포워딩이 있구나" 정도로만 생각하면 됨 )

 

마찬가지로 대출해야하니 [ 대출 파트 ] 로 가야하지만 [ 예금, 출금, 적금 파트 ] 로 가서 "대출하러 왔는데요~"(1) 라고 한 상황일 때 직원1이 [ 직원2님~ 고객님 대출하려고 오셨는데 이런이런 저런저런거 확인해주세요 ] 라고 대신 요청(2)을 하고 직원2는 직원1이 요청한 처리(3)를 한 후 결과를 직원1에게 되돌려줌(4) 그리고 나서 직원1이 그 결과를 고객에게 알려주는 것(5)

( 이 상황은 그저 예인것으로 포워딩도 이와 같은 순서로 동작하지는 않음 / "이런 느낌의 복잡한 포워딩이 있구나" 정도로만 생각하면 됨 )

 

개발에서 포워딩을 해야되는 상황이 언제가 있을까?

생각 보다 굉장히 많은 상황에서 포워딩이 필요함

 

네이버에 로그인을 해보자

아이디, 비밀번호를 입력하고 [ 로그인 ] 버튼을 눌러보자

로그인에 성공했을 때 어떻게 되는지?

첫 페이지로 이동(포워딩 / forwarding)이 됨

 

네이버에서 검색을 해보자

검색 결과를 보여주기 위해서 검색 결과 페이지로 이동(포워딩 / forwarding)이 됨

 

이렇게 대부분의 상황에서 포워딩이 필요함


여기서는 포워딩의 첫 번째 방법인

1. 클라이언트에게 이동할 곳을 지정해주는 방법

을 배워보자

 

클라이언트에게 이동할 곳을 지정해주는 방법은 다양하지만 주로 다음과 같은 방법들을 사용함

1. JS의 location.href 를 사용

2. 요청 정보(HttpServletRequest)의 sendRedirect 메서드를 사용

3. 응답 정보(HttpServletResponse)의 addHeader 메서드를 사용

 

로그인 페이지와 서블릿을 사용해서 위 1, 2, 3 포워딩 방법을 배우자


포워딩의 첫 번째 방법(클라이언트에게 이동할 곳을 지정해주는 방법) 중 첫 번째 방법을 알아보자

<< JS의 location.href 를 사용 >>

이 방법의 경우 서버에서 포워딩을 위해 할 것이 없음

클라이언트의 JS에서 location.href 를 사용하면 됨

 

먼저 로그인 서블릿을 만들자

chapter04 -> login1 서블릿을 만들고 아래 코드를 입력하자

서버는 포워딩(이동) 경로를 명시해주지 않았고 아이디와 비밀번호가 tempId, tempPw와 일치하면 200 상태 코드를 응답하고 아이디 또는 비밀번호가 tempId, tempPw와 일치하지 않으면 400 상태 코드를 응답함

package chapter04;

import java.io.IOException;
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("/chapter04/login_1")
public class Login1 extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
		
		String tempId = "myId";
		String tempPw = "myPw";
		
		if(id.equals(tempId) && pw.equals(tempPw)) {
			response.setStatus(200);
		} else {
			response.setStatus(400);
		}
	}

}

 

요청하고 요청 결과에 맞게 포워딩 하기 위한 페이지를 만들자

<< 로그인에 성공했을 때 보여줄 페이지 >>

webapp -> chapter04 -> loginSuccess.html 을 추가하고 아래 코드를 입력 하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>로그인 성공!</title>
</head>
<body>
	<h1>로그인에 성공하셨습니다!</h1>
</body>
</html>

 

<< 로그인 요청을 보낼 페이지 >>

webapp -> chapter04 -> login1.html 을 추가하고 아래 코드를 입력 하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>로그인</title>
</head>
<body>
	<form action="/studyProject/chapter04/login_1" method="POST">
		아이디: <input type="text" name="id"><br>
		비밀번호: <input type="password" name="pw"><br>
		<button type="button">로그인</button>
	</form>
	
	<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
	<script>
		$("button").on("click", function() {
			let id = $("input[name=id]").val();
			let pw = $("input[name=pw]").val();
			
			$.ajax({
				url: $("form").attr("action"),
				type: $("form").attr("method"),
				data: "id="+id+"&pw="+pw,
				success: function() {
					location.href = "/studyProject/chapter04/loginSuccess.html";
				},
				error: function() {
					alert("아이디 또는 비밀번호를 확인해주세요.");
				}
			})
		});
	</script>
</body>
</html>

 

우리가 지금 어떤 포워딩을 배우고 있다? JS의 location.href 를 사용한 포워딩

웹 페이지의 JS 를 사용해서 로그인에 성공했다면 로그인 성공 페이지로 이동하도록 포워딩 하고 있음(1)

 

이제 서버를 시작한 후 login.html에 접속해 아이디를 myId 로, 비밀번호를 myPw 로 입력하고 [ 로그인 ] 버튼을 눌러보자

서버는 200 상태 코드를 응답하고 200 상태 코드를 응답 받은 클라이언트는 success: function 내 코드가 동작해 로그인 성공 페이지로 이동(포워딩)함


포워딩의 첫 번째 방법(클라이언트에게 이동할 곳을 지정해주는 방법) 중 두 번째 방법을 알아보자

<< 요청 정보(HttpServletRequest)의 sendRedirect 메서드를 사용 >>

이 방법은 서버가 클라이언트에게 이동할 경로를 지정해주는 방법임

그래서 클라이언트가 포워딩을 위해 할 일은 없음

 

로그인 서블릿을 추가하자

chapter04 -> login2 서블릿을 만들고 아래 코드를 입력하자

package chapter04;

import java.io.IOException;
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("/chapter04/login_2")
public class Login2 extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
		
		String tempId = "myId";
		String tempPw = "myPw";
		
		if(id.equals(tempId) && pw.equals(tempPw)) {
			response.sendRedirect("/studyProject/chapter04/loginSuccess.html");
		} else {
			response.setStatus(400);
		}
	}

}

 

서버는 로그인에 성공했을 때 포워딩(이동) 경로를 명시해줬음(1)

로그인에 실패했을 때는 400 상태 코드를 응답함(2)

 

이제 요청을 하고 로그인에 실패했을 때 결과를 보여줄 웹 페이지를 만들자

webapp -> chapter04 -> login2.html 을 추가하고 아래 코드를 입력하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>로그인</title>
</head>
<body>
	<form action="/studyProject/chapter04/login_2" method="POST">
		아이디: <input type="text" name="id"><br>
		비밀번호: <input type="password" name="pw"><br>
		<input type="submit" value="로그인">
	</form>
</body>
</html>

이 HTML의 경우 별도로 JS 가 없으니 이 HTML 자체에 대한 설명은 생략함

 

서버를 재시작 후 login2.html 페이지에 접속해 아이디, 비밀번호를 입력하고 로그인을 해보자

form 태그로 요청했으므로 브라우저의 URL 이 form 태그의 action 속성에 명시해둔 URL 로 바뀌면서 요청이 들어감

이때 로그인에 성공했다면 서블릿에서는 reseponse.sendRedirect 메서드가 호출되고 이는 서버가 클라이언트에게 "/studyProject/chapter04/loginSuccess.html" 로 이동해 라고 이동할 경로를 지명해주는 것(1)

그러면 브라우저의 URL이 지명해준 URL로 바뀌며 로그인 성공 메세지가 보임

 

그리고 다시 login2.html 페이지에 접속해 아이디, 비밀번호를 myId, myPw가 아닌 것으로 입력하고 로그인을 해보자

form 태그로 요청했으므로 브라우저의 URL 이 form 태그의 action 속성에 명시해둔 URL 로 바뀌면서 요청이 들어감

이때 로그인에 실패했으므로 서블릿에서는 response.status 메서드가 호출되고 이는 서버가 클라이언트에게 400 상태 코드만 응답하는 것

브라우저는 서버로 부터 400 상태 코드만 받게 되므로 이동이 이뤄지지도 않고 별도의 메세지가 보이지 않음


첫 번째 방법 ( JS의 location.href 를 사용 ) 과 두 번째 방법 ( 요청 정보(HttpServletRequest)의 sendRedirect 메서드를 사용 ) 에서 서버 코드의 차이와 클라이언트 코드의 차이를 각각 비교해보자

 

<< 서버 코드의 차이 >>

첫 번째 방법은 클라이언트 사이드 언어인 JS 를 사용해서 포워딩을 하는 방식이므로 서버에서 별도로 지정할 게 없는 것임

두 번째 방법은 서버 사이드 언어인 서블릿을 사용해서 클라이언트에게 "여기로 이동해!" 라고 명시했기 때문에 서버에서 별도로 지정해줘야함

 

<< 클라이언트 코드의 차이 >>

 

첫 번째 방법은 클라이언트 사이드 언어인 JS 를 사용해서 포워딩을 하는 방식이므로 ajax로 요청하고 결과를 받아서 상황에 맞게 포워딩을 하거나 결과를 보여주고 있음

두 번째 방법은 서버 사이드 언어인 서블릿을 사용해서 포워딩 할 곳을 클라이언트가 지명받았기 때문에 클라이언트는 별도의 처리 없이 바로 form 태그로 요청하면 됨

두 번째 방법을 사용하는데 ajax 로 요청하게 되면 로그인에 성공했을 때 로그인 성공 페이지로 이동할 수 없음

ajax 로 요청했을 때 서버가 sendRedirect 로 응답을 하면 ajax 는 응답을 이해하지 못하고 아무 반응을 하지 않음


포워딩의 첫 번째 방법(클라이언트에게 이동할 곳을 지정해주는 방법) 중 세 번째 방법을 알아보자

<< 응답 정보(HttpServletResponse)의 addHeader 메서드를 사용 >>

 

또 다른 로그인 서블릿을 추가하자

chapter04 -> login3 서블릿을 만들고 아래 코드를 입력하자

package chapter04;

import java.io.IOException;
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("/chapter04/login_3")
public class Login3 extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
		
		String tempId = "myId";
		String tempPw = "myPw";
		
		if(id.equals(tempId) && pw.equals(tempPw)) {
			response.setStatus(303);
			response.addHeader("location", "/studyProject/chapter04/loginSuccess.html");
		} else {
			response.setStatus(400);
		}
	}

}

 

로그인에 성공했을 때 응답 정보 상태 코드에 303 을 설정하고(1) 응답 정보 헤더에 location="/studyProject/chapter04/loginSuccess.html" 을 담아(2) 응답한 것

 

클라이언트가 전달 받은 응답 정보는 이와 같음

 

"300번대 상태 코드는 클라이언트의 요청을 처리하긴 했지만 처리를 완료하지는 못했다. 처리를 완료하기 위해서는 내가 지명한 곳으로 이동해라." 라는 뜻을 갖고 있는 상태코드임

따라서 브라우저는 300번대 상태 코드를 응답 받게 되면 헤더에 location이 있는지 확인함

헤더에 location이 있다면 location에 담긴 URI 로 이동을 하고 헤더에 location이 없다면 서바가 이동할 곳을 지명해주지 않았기 때문에 이동하지 않고 그 자리 그대로 있음

 

우리는 303번 상태 코드를 설정하고 헤더에 location을 담았기 때문에 브라우저가 해당 경로로 이동함

따라서 로그인에 성공했을 경우 로그인 성공 페이지가 보여짐

 

그러나 로그인에 실패했을 때는 서버가 400 상태 코드를 응답하므로 브라우저는 아무것도 하지 않음


여기까지 포워딩을 하는 큰 두 가지 방법

1. 클라이언트에게 이동할 곳을 지정해주는 방법

2. 서버 안에서 이동하는 방법

중 클라이언트에게 이동할 곳을 지정해주는 방법의 세 가지 방법을 알아봤음

1. JS의 location.href 를 사용

2. 요청 정보(HttpServletRequest)의 sendRedirect 메서드를 사용

3. 응답 정보(HttpServletResponse)의 addHeader 메서드를 사용

 

여기서 자주 사용하는 방법은 1, 2번 방법이고 3번은 자주 사용하지는 않음

 

1번은 언제 사용할까? 클라이언트의 잘못으로 클라이언트의 요청이 실패할 수 있을 경우

2번은 언제 사용할까? 클라이언트의 요청에 대한 결과 페이지가 무조건 있을 경우

 

1번의 경우는 회원가입, 로그인 등을 예로 들 수 있음

회원가입을 할 때 일반적으로 아이디, 연락처, 이메일 등은 누군가 이미 사용 중이라면 사용할 수 없음

보통은 아이디를 입력하고 중복 확인을 별도로 하지만 중복 확인을 하는 시점에는 아무도 사용하지 않는 아이디였는데 [ 회원 가입 ] 버튼을 누르는 시점에서는 누군가 사용할 수도 있음

극단적인 예를 들어보면 어느 사이트에 회원 가입을 하는데 아이디 중복 확인까지 마쳤음

이때는 사용중이지 않은 아이디였다고 나왔음

그 후 [ 회원 가입 ] 버튼을 누르기 직전에 갑자기 부모님께서 심부름을 시켜서 심부름을 갔다 왔다고 해보자

그러면 그 사이 누군가 내가 중복 확인할 때 입력했던 아이디로 가입을 했을 수 있음

나는 그러한 사실을 모르고 그대로 [ 회원 가입 ] 버튼을 누를 것

그러면 회원 가입하는 시점에 이미 사용중인 아이디가 됐으므로 회원 가입에 실패하게 됨

이런 상황이 1번의 상황(클라이언트의 잘못으로 클라이언트의 요청이 실패할 수 있을 경우)임

이때 내가 다시 아이디를 다른 아이디로 입력하고 중복 확인 후 곧바로 회원가입을 한다면 회원 가입이 성공할 것

 

또는 로그인을 예로 들어보자, 특히나 요즘 공감할 수 있는 예임

점점 보안 관련 사항 때문에 비밀번호를 더 복잡하게 만들어야하고 이전에 사용했던 비밀번호는 사용하지 못함

그래서 내가 A 사이트와 B 사이트 계정의 비밀번호를 서로 다르게 해놓은 상태임

A 사이트의 비밀번호는 1111 이고 B 사이트의 비밀번호는 2222임

그런 상황에서 A 사이트에 접속할때 실수로 비밀번호를 2222 로 입력하고 로그인을 하려 했다면 나(클라이언트)의 실수로 로그인에 실패하게 됨

그럴 경우 화면에 "아이디 또는 비밀번호를 확인해주세요" 라는 문구가 보임

그 후 비밀번호를 1111 로 바꾸고 로그인을 하면 로그인에 성공할 것

이렇게 클라이언트의 어떤 잘못이나 실수로 클라이언트의 요청이 실패할 수 있고 또는 성공할 수 있을 때 1번의 방법(JS의 location.href 를 사용) 으로 요청을 처리함

 

2번의 경우는 검색을 예로 들 수 있음

네이버에 "칼국수" 를 검색하고 싶은데 실수로 "갈국수" 로 입력하고 검색 버튼을 눌렀다고 하자

그렇다고 "칼국수" 라고 다시 입력하세요 라는 문구가 보일까? 아님

그대로 갈국수의 결과가 보임

검색처럼 사용자가 원하는 결과를 무조건 보여줘야할 때 2번의 방법(요청 정보(HttpServletRequest)의 sendRedirect 메서드를 사용) 으로 요청을 처리함

 

그러나 반드시 그런것만은 아니니 이런 기준으로 이해하고 사용하다 보면 여러분만의 기준이 생길 것

 

2번 대신에 3번의 방법으로 요청을 처리할 수도 있지만 그건 여러분이 경험이 더 쌓이면 판단할 수 있을 것

 

만약에 실제로 개발할 때 이런 예가 와닿지 않는다면 직접 1번 또는 2번의 방법만을 사용해서 개발을 해보자 그러면 어느 순간 또 다른 방법이 필요하다는걸 분명히 느끼게 될 것

728x90
LIST

<< 학습 목표 >>

1. 서버가 클라이언트에게 JSON 데이터를 전달할 수 있다.

2. 클라이언트는 서버가 보낸 JSON 데이터를 받을 수 있다.

3. 자바의 객체를 JSON 객체로 변환할 수 있다.


지금까지 서버가 "결괏값" 을 전달하는 방법, "결괏값을 담은 페이지"를 전달하는 방법, "상태 코드"를 전달 하는 방법을 배웠음

이 방법들은 각자 사용하는게 아닌 다같이 종합해서 사용함

1. 상태 코드 + 결괏값

2. 상태 코드 + 결괏값을 담은 페이지

 

이번에는 결괏값에 단순한 형태의 값이 아닌 정보를 담는 방법을 배워보자


클라이언트에 정보를 전달할 때는 JSON 형식으로 전달해주는게 좋음

그래야 클라이언트가 받아서 사용하기 편함

 

클라이언트에 정보를 전달하는 방법은 매우 간단함

앞서 배운것들만 제대로 복습했다면 "다를거 없네?" 라고 생각이 들 것

 

우선 JSON 데이터를 전달할 서버부터 만들자

chapter04 -> SendJson 서블릿을 만들고 아래 코드를 넣자

package chapter04;

import java.io.IOException;
import java.io.PrintWriter;

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 org.json.JSONObject;

@WebServlet("/chapter04/send_json")
public class SendJson extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		JSONObject jo = new JSONObject();
		jo.put("name", "홍길동");
		jo.put("age", 21);
		jo.put("height", 178.1);
		
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json");
		PrintWriter pw = response.getWriter();
		pw.print(jo);
	}

}

 

자바에서 JSON을 사용해야하므로 JSON 라이브러리를 추가해야함

JSON 라이브러 추가하는 방법을 잊었다면 이 글을 보고 오자 ( https://codingaja.tistory.com/33 )

 

서블릿이 클라이언트에게 JSON을 전달해야하므로 타입이 JSONObject, JSONArray 등인 객체를 생성해 그 안에 전달할 정보를 저장함(1)

그 후 응답 정보(HttpServlerResponse) 헤더의 content-type 을 application/json 으로 해야함(2)

그 다음 평소와 같이 PrintWriter 객체를 꺼내서 JSON 을 print 하면 끝!(3)

 

이번에는 서버로 요청을 보내고 서버가 보낸 JSON 데이터를 받을 웹 페이지를 만들자

chapter04 -> receiveJson.html 을 추가하고 아래 코드를 입력하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>서버에게 JSON을 받는 페이지</title>
</head>
<body>
	<button type="button" onclick="request()">요청</button>
	
	<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
	<script type="text/javascript">
		function request() {
			$.ajax({
				url: "",
				type: "GET",
				dataType: "json",
				success: function(data) {
					console.log(data);
				},
				error: function() {
					alert("어떤 문제가 생김");
				}
			});
		}
	</script>
</body>
</html>

 

클라이언트의 ajax에서 오랜만에 또는 처음보는 속성이 있음

dataType

dataType 속성은 서버가 어떤 형태의 데이터를 보냈는지 미리 적어두는 속성으로 dataType: "json" 이므로 "서버가 json 데이터를 보낼꺼야" 라고 말하는 것

dataType: "text" 라면 "서버가 text 데이터를 보낼꺼야" 라고 말하는 것

dataType과 MIME-TYPE은 다른것이므로 MIME-TYPE 형식으로 적으면 안되고 쓸 수 있는 값 또한 정해져있음

dataType 에 쓸 수 있는 값
1. "xml" : 서버가 xml 데이터를 보낼꺼야
2. "html" : 서버가 html 데이터를 보낼꺼야
3. "script" : 서버가 script 데이터를 보낼꺼야
4. "json" : 서버가 json 데이터를 보낼꺼야
5. "jsonp": 서버가 보낸 데이터를 지정한 함수가 반환한 값으로 JSON 데이터로 처리하겠다
6. "text" : 서버가 text 데이터를 보낼꺼야

( jsonp 의 경우는 이해할 필요는 없음 / 이런게 있다 정도로만 알면 됨 )

 

위 ajax 를 역할별로 분류해보면 해당 속성, 값을 사용해서 요청 정보를 생성하고 요청을 보냄(1)

응답이 오면 해당 속성, 값을 사용해서 응답 정보를 분석해 success 또는 error 의 function 이 동작함(2)

 

이제 서버를 시작하고 웹 페이지에서 [ 요청 ] 버튼을 눌러보자

서버가 보낸 데이터를 success: function의 data 매개변수가 받았음

그 후 success: function 안의 코드가 동작해 전달 받은 json 데이터를 console 에 출력하고 있음

결과를 확인하려면 브라우저 -> 개발자 모드 -> [ console ] 패널 을 확인해보자


여기까지 서바가 JSON 데이터를 전달하고 클라이언트가 JSON 데이터를 받는 방법을 알아봤음

 

주된 내용은 여기까지가 끝! 인데 한 가지만 더 알아보고 마무리하자

자바에서는 JSON 보다 클래스, 객체를 사용해서 정보를 저장함

보통 이렇게 정보를 저장하는 객체를 DTO 또는 VO 라고 부름

 

서버에서는 다음과 같이 홍길동의 정보를 객체에 저장해뒀음(1)

이 객체가 갖고 있는 정보를 클라이언트에게 전달해줘야할 때는 어떻게할까?

 

다음과 같이 JSONObject 를 만들어 담은 다음(2)에 우리가 앞서 했듯이 JSONObject를 print 하면 됨(3)


클라이언트가 서버로 JSON 데이터를 보내는 일은 취업하기 전까지 없을 수 있지만 서버가 클라이언트에게 JSON 데이터를 보내야하는 일은 있을 수 있으니 잘 기억해둬야함

728x90
LIST

<< 학습 목표 >>

1. 서블릿이 상태 코드를 설정해 응답할 수 있다.

2. 서블릿에서 상황에 맞는 상태 코드를 설정할 수 있다.


지금까지 우리는 클라이언트에게 "결괏값" 을 전달하는 방법과 "결괏값을 담고 있는 페이지" 를 전달하는 방법을 배웠음

서버가 클라이언트에게 전달하는 "결괏값" 또는 "결괏값을 담고 있는 페이지" 는 응답 정보의 몸통(Body)에 담음

 

클라이언트에게 보내는 HTTP 버전은 1.1 버전이고 헤더는 비어있는 상태임

( 사실 헤더는 비어있지 않고 무언가가 들어있는데 지금 우리가 헤더까지 생각하면 너무 복잡해지니 무시해도됨 / 자소서, 면접을 준비하면서 요청 정보, 응답 정보의 헤더를 찾아보거나 취업하고 난 이후에 알아도 무방함 )

 

우리가 서버(서블릿)에서 상태 코드를 별도로 설정하지 않고 "결괏값" 또는 "결괏값을 담고 있는 페이지" 만 클라이언트에게 전달 했으므로 클라이언트가 받게 되는 응답 정보의 상태 코드는 200, 상태 코드 설명은 상태 코드 200에 맞는 OK 가 들어있는 상태가 됨

따라서 지금까지 클라이언트가 받은 응답 코드는 최종적으로 아래와 같음


응답 정보의 상태 코드는 클라이언트와 서버가 통신할 때 중요한 역할을 담당함

클라이언트는 서버가 응답했을 때 가장 먼저 상태 코드를 봐야함

 

여기서는 서버가 상태 코드를 설정해 응답하고 서버가 보낸 상태 코드에 맞게 처리하는 방법을 배워보자

 

이번에는 서버(서블릿) 먼저 만들자

chapter04 패키지 내 Status200 서블릿을 추가하고 아래 코드를 입력하자

package chapter04;

import java.io.IOException;
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("/chapter04/status/200")
public class Status200 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setStatus(200);
	}

}

 

응답 정보의 상태 코드를 설정해야하므로 당연히 HttpServletResponse 객체를 다뤄야함

위 서블릿은 별도의 코드는 없고 상태 코드를 200으로 설정하고 있음

서블릿이 상태 코드를 설정하는 방법은 여기까지가 끝!

응답 정보 객체(HttpServletResponse) 의 setStatus 메서드를 사용해서 상태 코드를 설정함

 

이번에는 웹 페이지를 만들자

webapp -> chapter04 -> status.html 을 만들고 아래 코드를 입력하자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>상태코드별 처리 방법</title>
</head>
<body>
	<button type="button" onclick="request(200)">서버로 요청1</button>
	<button type="button" onclick="request(300)">서버로 요청2</button>
	<button type="button" onclick="request(400)">서버로 요청3</button>
	<button type="button" onclick="request(500)">서버로 요청4</button>
	
	<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
	<script type="text/javascript">
		function request(path) {
			$.ajax({
				url: "/studyProject/chapter04/status/"+path,
				type: "GET",
				success: function() {
					alert("서버가 200번대 상태 코드로 응답했습니다.");
				},
				error: function() {
					alert("서버가 300, 400, 500번대 상태 코드로 응답했습니다.");
				}
			});
		}
	</script>
</body>
</html>

 

이 웹 페이지는 버튼 마다 각각 다른 서블릿으로 요청을 보내고 있음

요청을 보낼 때는 request 함수 내 ajax를 사용하고 있음

 

ajax는 간단하게 요청을 보내고(1) 응답을 받았을 때 간단한 처리 /alert으로 출력/ 를 하고 있음(2)

 

이제 서버를 시작하고 웹 페이지에 접속해 [ 서버로 요청1 ] 버튼을 눌러보자

[ 서버가 200번대 상태 코드로 응답했습니다. ] 가 출력됨

 

여기서 주의 깊게 봐야할 점은 서버가 200번대 상태 코드로 응답했을 때 success의 function 이 동작한다는 점


서블릿이 300, 400, 500번대 상태 코드를 설정해 응답하는 서블릿은 여러분이 먼저 만들어보자

더보기

<< 300번대 상태 코드 설정 >>

package chapter04;

import java.io.IOException;
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("/chapter04/status/300")
public class Status300 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setStatus(300);
	}

}

 

더보기

<< 400번대 상태 코드 설정 >>

package chapter04;

import java.io.IOException;
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("/chapter04/status/400")
public class Status400 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setStatus(400);
	}

}

 

더보기

<< 500번대 상태 코드 설정 >>

package chapter04;

import java.io.IOException;
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("/chapter04/status/500")
public class Status500 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setStatus(500);
	}

}

 

서버를 재시작하고 다시 웹 페이지에 접근해 [ 서버로 요청2 ], [ 서버로 요청3], [ 서버로 요청4 ] 버튼을 눌러보자

서버가 300, 400, 500번대 상태 코드로 응딥을 하게 되면 error의 function이 동작함


여기까지 서버가 상태 코드를 담아 클라이언트에게 전달하는 방법을 배웠음

또 클라이언트가 상태 코드가 담긴 응답 정보를 받았을 때 ajax의 succes, error 중 한 함수가 동작한다는 것까지 해서 클라이언트가 상태 코드에 따른 처리하는 방법을 배웠음

 

굉장히 간단함

 

그렇다면 서버가 언제 200, 300, 400, 500번대 상태 코드를 언제 응답해야하는지 알아보자

서버가 300, 500번대 상태 코드를 응답하는 경우는 거의 없음

300번대와 500번대의 의미는? 잘모르겠다면 https://codingaja.tistory.com/16 이 글을 다시 보고 오자

 

서버는 주로 200, 400번대 상태 코드로 응답함

 

회원가입 처리를 하는 서블릿을 만들었는데

1. 유효성 검증에 통과하지 못했다면 400번대 상태 코드로 설정함

2. 유효성 검증에 통과했으나 이미 사용중인 아이디여서 "회원 가입에 실패" 했을 경우 400번대 상태 코드로 설정함

3. 유효성 검증에 통과했고 아이디도 사용중인 아이디가 아니여서 "회원 가입에 성공" 했을 경우 200번대 상태 코드로 설정함

 

로그인 처리를 하는 서블릿을 만들었는데

1. 유효성 검증에 통과하지 못했다면 400번대 상태 코드로 설정함

2. 유효성 검증에 통과했고 아이디, 비밀번호가 일치하는 계정있을 경우 "로그인 성공" 이므로 200번대 상태 코드로 설정함

3. 유효성 검증에 통과했지만 아이디, 비밀번호가 일치하는 계정이 없을 경우 "로그인 실패" 이므로 400번대 상태 코드로 설정함

 

등등...

 

이런 상황에서 상태 코드를 설정함

 

꼭 상태 코드만 설정하는게 아닌 상태 코드 + ("결괏값" 또는 "결괏값을 담고 있는 페이지") 를 같이 보낼 수도 있음


여기까지 서버에서 상태 코드를 설정하는 방법과 언제 어떤 상태 코드를 설정해서 응답 해야하는지 알아봤음

만약 "그럼... 클라이언트는 상태 코드를 받아서 그 이후에 어떻게 처리를 해줘야하지?" 라고 생각이 들었다면 잘 이해하고 있다는 것!

이는 클라이언트의 처리이므로 HTML, CSS, JS 로 그 이후에 처리를 하면 됨

 

로그인에 성공했을 때 HTML, CSS, JS 를 활용해서 메인 화면으로 넘어가게 하면 됨

로그인에 실패했을 때 HTML, CSS, JS 를 활용해서 "아이디 또는 비밀번호를 확인하세요" 메세지를 보여주면 됨

728x90
LIST

<< 학습 목표 >>

1. 클라이언트에게 "결괏값을 담고 있는 페이지"를 전달할 수 있다.

2. 클라이언트에게 한글을 포함한 데이터를 전달할 수 있다.


앞 글에서는 클라이언트가 Jquery의 ajax를 사용해 요청을 해 서버가 "결괏값" 만 응답했음

이번에는 클라이언트가 그외의 방법 중 form 태그를 사용해 요청을 하고 서버는 "결괏값을 담은 페이지"를 응답하는 방법을 배워보자


항상 그래왔듯 먼저 클라이언트 페이지부터 만들자

앞서 만들었던 ajaxRequest.html 을 복사 붙여넣기 한 후 이름을 formRequest.html로 바꾸자

그리고 그 안에 JS는 모두 지우고 로그인 버튼을 button 태그 대신 input 태그로 바꾸자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Form으로 요청하는 페이지</title>
</head>
<body>
	<form action="/studyProject/chapter04/login2" method="POST">
		<h1>로그인 페이지</h1>
		<fieldset>
			아이디: <input type="text" name="id">
		</fieldset>
		<fieldset>
			비밀번호: <input type="password" name="pw">
		</fieldset>
		<fieldset>
			<input type="submit" value="로그인">
		</fieldset>
	</form>
</body>
</html>

 

이번에는 서블릿을 만들자 ValureResponse 서블릿을 복사 붙여넣기 하고 PageResponse 로 이름을 바꾸자

경로는 /chapter04/login2 로 바꾸고 아래 코드를 입력하자

package chapter04;

import java.io.IOException;
import java.io.PrintWriter;

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 utility.ParameterUtil;

@WebServlet("/chapter04/login2")
public class PageResponse extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter output = response.getWriter();
		
		String id = ParameterUtil.getString(request, "id");
		String pw = ParameterUtil.getString(request, "pw");
		
		output.print("<html>");
		output.print("<head>");
		output.print("    <meta charset=\"UTF-8\">");
		output.print("    <title>Response Page</title>");
		output.print("    <style>");
		output.print("        p.fail {color: red;}");
		output.print("        p.success {color: blue;}");
		output.print("    </style>");
		output.print("</head>");
		output.print("<body>");
		
		if(id == null || pw == null) {
			output.print("    <p class=\"fail\">ID or PW is empty</p>");
			output.print("    <a href=\"/studyProject/chapter04/formRequest.html\">Go Back!</a>");
		} else {
			String tempId = "myId";
			String tempPw = "myPw";
			
			if(id.equals(tempId) && pw.equals(tempPw)) {
				output.print("    <p class=\"success\">Login Success~!</p>");
			} else {
				output.print("    <p class=\"fail\">ID or PW do not match</p>");
				output.print("    <a href=\"/studyProject/chapter04/formRequest.html\">Go Back!</a>");
			}
		}
		
		output.print("</body>");
		output.print("</html>");
	}

}

 

서블릿을 분석해보자

1. 클라이언트에게 "결괏값을 담은 페이지"를 보내기 위해 응답 정보(HttpServletResponse) 객체에서 PrintWriter 를 꺼냈음

2. 클라이언트가 보낸 id, pw 파라미터 값을 꺼냈음

3. 클라이언트가 id, pw 파라미터를 비정상적인 값으로 보냈을 때 전달할 페이지와 id, pw 파라미터를 정상적인 값으로 보냈을 때 전달할 페이지의 공통 HTML 태그 작성

4. 클라이언트가 id, pw 파라미터를 비정상적인 값으로 보냈다면 "Id or PW is empty" 가 보이는 페이지를 클라이언트에게 전달

5. 클라이언트가 id, pw 파라미터를 정상적인 값으로 보냈다면

6. id와 tempId가 일치하고 pw와 tempPw가 일치한다면 "Login Success~!" 가 보이는 페이지를 클라이언트에게 전달

7. id와 tempId 가 일치하지 않거나 pw와 tempPw 가 일치하지 않는다면 "ID or PW do not match" 가 보이는 페이지를 클라이언트에게 전달

이 서블릿 자체만 놓고 봤을 때 주의 깊게 봐야하는 부분은 클라이언트에게 전달하는 모든 것이 다 영문이라는 점

 

이제 서버를 재시작하고 formRequest.html 페이지에 접속해 로그인을 해보자


이제 앞서 클라이언트에게 "결괏값"을 전달했던 코드와 이번에 "결괏값을 담고 있는 페이지"를 전달하는 코드를 비교해보자

 

결괏값을 전달해야하는 상황이든 결괏값을 담고 있는 페이지를 전달하는 상황이든 PrintWriter 객체를 꺼낸 후 print 를 함

이때 값만 print를 하면 결괏값이 전달되는 것이고 HTML 코드를 print 하면 결괏값을 담고 있는 페이지가 전달되는 것


앞서 결괏값을 보낼때와 이번에 결괏값을 담고 있는 페이지에서 모두 영문만 보냈음

한글을 보내면 어떻게 될까?

위에서 언급한대로 결괏값을 보낼 때와 결괏값을 담고 있는 페이지를 보내는건 한 끗 차이이므로 결괏값을 담고 있는 페이지를 보내는 상황에서만 한글을 보내는 걸 알아보자

여기서 배운 원리는 결괏값을 보낼 때도 마찬가지로 적용됨

 

전체 코드가 길어 바뀐 부분만 가져왔음

바뀐 부분은 서블릿의 doPost 메서드 내 if ~ else 코드임

if(id == null || pw == null) {
	output.print("    <p class=\"fail\">아이디 또는 비밀번호가 비어있습니다.</p>");
	output.print("    <a href=\"/studyProject/chapter04/formRequest.html\">되돌아가기!</a>");
} else {
	String tempId = "myId";
	String tempPw = "myPw";
	
	if(id.equals(tempId) && pw.equals(tempPw)) {
		output.print("    <p class=\"success\">로그인 성공~!</p>");
	} else {
		output.print("    <p class=\"fail\">아이디 또는 비밀번호가 일치하지 않습니다</p>");
		output.print("    <a href=\"/studyProject/chapter04/formRequest.html\">되돌아가기!</a>");
	}
}

 

서버를 재시작하고 formRequest.html 부터 다시 시작해보자

아이디와 비밀번호를 아래와 같이 입력하고 [ 로그인 ] 버튼을 누르면 "아이디 또는 비밀번호가 일치하지 않습니다" 문구가 보여야함

 

그러나 ? 또는 알 수 없는 문자로 채워진 페이지가 보여지게 됨

 

왜이럴까?

Chapter03에서 클라이언트가 서버로 한글 데이터를 보냈을 때 서버에서 어떤 코드를 반드시 써줘야했음

그래야 그 한글 데이터가 정상적으로 꺼내졌음

어떤 코드였을까?

더보기

request.setCharacterEncoding("UTF-8");


이렇게 클라이언트가 서버로 한글 데이터를 보냈을 때 꺼내기 전 반드시 선행 되야할 코드가 있는것처럼 서버가 클라이언트로 한글 데이터를 보낼 때 PrintWriter 객체를 꺼내기 전 반드시 선행 되야하는 코드가 있음

 

response.setCharacterEncoding("UTF-8");

 

이것도 마찬가지로 왜 이래야하는지 이해하려면 서블릿과는 크게 상관이 없는 인코딩부터 얘기해야하므로 외우자

 

서버가 클라이언트로 한글 데이터를 보낼 때는 PrintWriter 객체를 꺼내기 전 반드시 response.setCharacterEncoding("UTF-8"); 코드를 써야한다.(1)

 

여기서 중요한 점은 PrintWriter 객체를 꺼내기 전! 이라는 것임

꺼낸 후에는 저 코드를 입력해도 무용지물임

728x90
LIST

<< 학습 목표 >>

1. 클라이언트에게 "결괏값"을 전달해야하는 상황과 "결괏값을 담고 있는 페이지"를 전달해야하는 상황을 구분할 수 있다.

2. 클라이언트에게 "결괏값"을 전달할 수 있다.

3. 클라이언트는 서버에게 "결괏값"을 전달 받은 후 사용자에게 결과를 보여줄 수 있다.


지금까지 지나온 과정을 보면

Chapter01. 웹 프로그래밍 기초

Chapter02. 서블릿 기초

Chapter03. 클라이언트가 서블릿을 호출하는 방법 & 요청 방식에 따라 구현할 서블릿 메서드

 

앞서서는 클라이언트 코드 설명도 많이 했음

여기(Chapter04)부터 마지막(Chapter07)까지는 이제 모두 서블릿, 서버와 관련된 설명들임

클라이언트 코드 관련된 설명은 필요할 때만 하고 그외에는 코드만 첨부하겠음

 

Chapter03에서도 중간 중간 언급했지만 클라이언트인 HTML, CSS, JS 는 이 블로그에서 다루지 않으니 클라이언트 관련된 지식은 해당 책을 보거나 해당 블로그를 찾아보고 오시면 됩니다.


Chapter04의 처음으로 서버가 클라이언트에게 응답하는 기본적인 방법을 알아보자

https://codingaja.tistory.com/21 글에서도 간략하게 언급했지만

서버가 클라이언트에게 처리 결과 전달할때는

1. "결괏값"만 전달

2. "결괏값을 담고 있는 페이지"를 전달

하는 두 가지 방법이 있음

 

"결괏값"만 전달 하는 상황은 클라이언트가 XMLHttpRequest, Ajax로 요청했을 때임

그외에 나머지 요청들은 "결괏값을 담고 있는 페이지"를 전달해야함

왜일까?

더보기

XMLHttpRequest, Ajax는 현재 페이지에 그대로 머문 상태로 요청만 들어가므로 결과를 보여 줄 페이지가 현재 페이지임

 

그러나 나머지 요청(a 태그, form 태그, location.href 등등)은 해당 URL 로 페이지가 전환되므로 결괏값만 전달하게 되면 웹 브라우저는 딸랑 결괏값만 보여줌

사용자에게는 결과 화면(결과 페이지) 가 보여야하므로 서버는 "결괏값을 담고 있는 페이지"를 전달해야함

반드시 그런건 아니지만 이렇게 기억하고 있으면 대체로 맞고 이렇게 기억하고 개발하다 보면 정확한 기준을 스스로 습득할 수 있을 것

 

혹시 위 말들에 ? 가 생긴다면 HTML, JS, Servlet Chapter02 를 처음부터 다시 복습해야함


지금까지 서버가 전달(Response, 응답) 이라고 표현했는데 앞으로는 별도의 첨언 없이 전달, 응답 같은 의미로 사용하겠음


먼저! 1번의 상황을 알아보자

클라이언트는 Jquery의 Ajax를 사용해서 요청하고 서버는 요청을 처리한 결괏값만 보낼 것임

 

webapps 안에 chapter04 폴더를 만들고 그 안에 ajaxRequest.html 을 만들고 아래 코드를 넣자

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Ajax로 요청하는 페이지</title>
</head>
<body>
	<form action="/studyProject/chapter04/login1" method="POST">
		<h1>로그인 페이지</h1>
		<fieldset>
			아이디: <input type="text" name="id">
		</fieldset>
		<fieldset>
			비밀번호: <input type="password" name="pw">
		</fieldset>
		<fieldset>
			<button type="button" onclick="login()">로그인</button>
		</fieldset>
	</form>
	
	<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
	<script type="text/javascript">
		function login() {
			let $form = $("form");
			let $id = $("input[type=text]");
			let $pw = $("input[type=password]");
			let id = $id.val();
			let pw = $pw.val();
			
			$.ajax({
				url: $form.attr("action"),
				type: $form.attr("method"),
				data: "id="+id+"&pw="+pw,
				success: function(responseData) {
					console.log(responseData);
					
					if(responseData == "yes") {
						alert("로그인에 성공했습니다!");
					} else if(responseData == "no") {
						alert("로그인에 실패했습니다ㅠㅠ");
					} else {
						alert("아이디 또는 비밀번호가 비어있습니다.");
					}
				}
			});
		}
	</script>
</body>
</html>

HTML 코드 안에 ajax 요청 부분 안에 success: function(responseData) 를 보자

서버가 응답을 하면 success: function(responseData) 안에 있는 console.log ~ 부터 동작을 함

function의 매개변수로 있는 responseData는 서버가 전달한 결괏값을 받을 매개변수임

 

서버가 전달한 결괏값을 responseData 에 저장하고 그 안에 있는 소스 코드가 동작함

서버가 전달한 결괏값을 출력해보고(1) 서버가 전달한 결괏값에 맞는 처리 결과를 alert 함수로 보여주고 있음(2)

 

이제 클라이언트의 요청을 받고 처리한 후 결과를 전달할 서블릿을 추가하자

서블릿을 추가하기 전에! 서블릿에서 유효성 검증(Validation)에 필요한 메서드 먼저 만들자

utility 패키지를 만들고 그 안에 ParameterUtil 클래스를 추가한 뒤 아래 코드를 입력하자

package utility;

import javax.servlet.http.HttpServletRequest;

public class ParameterUtil {
	/**
	 * name 파라미터 값을 꺼내는 메서드
	 * 파라미터가 없거나 파라미터 값을 trim() 한 후 비어있다면 null을 반환
	 * 
	 * @param request 파라미터를 담고 있는 객체
	 * @param name 값을 꺼낼 파라미터의 이름
	 * @return String 또는 null
	 */
	public static String getString(HttpServletRequest request, String name) {
		String str = null;
		
		str = request.getParameter(name);
		
		if(str != null) {
			str = str.trim();
			
			if(str.length() == 0) {
				str = null;
			}
		}
		
		return str;
	}
}

이 메서드가 직접 유효성 검증을 하지는 않음

이 메서드는 요청 정보(HttpServletRequest) 에 들어있는 파라미터 값을 꺼내줌

 

 

이제 서블릿을 추가하자

chapter04 패키지를 만들고 그 안에 ValueResponse 서블릿에 아래 코드를 입력하자

package chapter04;

import java.io.IOException;
import java.io.PrintWriter;

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 utility.ParameterUtil;

@WebServlet("/chapter04/login1")
public class ValueResponse extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter output = response.getWriter();
		
		String id = ParameterUtil.getString(request, "id");
		String pw = ParameterUtil.getString(request, "pw");
		
		if(id == null || pw == null) {
			output.print("empty");
		} else {
			String tempId = "myId";
			String tempPW = "myPw";
			
			if(id.equals(tempId) && pw.equals(tempPw)) {
				output.print("yes");
			} else {
				output.print("no");
			}
		}
	}

}

 

클라이언트에게 결괏값을 전달하기 위해서는 응답 정보(HttpServletResponse)에 들어있는 PrintWriter 객체를 꺼내야함(1)

아이디 또는 비밀번호를 전달하지 않았다면 클라이언트에게 "empty" 문자열을 전달함

아이디와 비밀번호 모두 전달했다면(2) 전달한 아이디가 tempId와 같고 전달한 비밀번호가 tempPw와 같다면 "yes" 문자열을 전달(3), 아이디 또는 비밀번호가 일치하지 않는다면 "no" 문자열을 전달함

 

여기서 tempId, tempPw는 로그인 성공/실패를 판단하기 위한 아이디/비밀번호임

이 챕터의 마지막에서는 서블릿과 DB를 연동하고 실제로 회원가입, 로그인을 할 수 있는 프로젝트를 할 것

그때 되면 tempId, tempPw 대신 DB를 활용하게 됨

 

이제 뷰와 서블릿(컨트롤러의 역할과 서비스의 역할을 동시에함)을 완성했으니 서버를 실행시키자

로그인 페이지에 아이디와 비밀번호를 tempId, tempPw와 동일하게 입력한 후 [ 로그인 ] 버튼을 눌러보자

그럼 화면에 "로그인에 성공했습니다!" 가 출력됨


클라이언트의 요청부터 서버의 응답까지 전체 과정을 세세하게 살펴보자


1.로그인 페이지에서 아이디, 비밀번호 입력 후 [ 로그인 ] 버튼을 클릭(1)하면 JS의 login 함수가 호출됨(2)

2. 그 후 사용자가 입력한 아이디, 비밀번호를 JS로 불러옴(3)

3. 그 후 ajax가 동작해 서버로 요청이 들어감(4)

 

4. 서버에서는 응답하기 위해 PrintWriter 객체를 꺼냄(1)

5. 클라이언트가 보낸 id, pw 파라미터 값을 꺼냄(2)

더보기

이때 getString 메서드 내부 코드를 분석해서 해석을 덧붙이자면 클라이언트가 id, pw 파라미터 자체를 보내지 않았거나 id, pw 파라미터를 보내긴 했지만 빈 문자열 또는 공백(스페이스 바)만 들어가 문자열을 보냈다면 null 이 반환됨

우리처럼 일반적으로 웹 페이지에서 [ 로그인 ] 을 했다면 id, pw 파라미터를 무조건 보내지만 이 챕터의 마지막에 배울 방법을 사용하면 id, pw 파라미터를 보내지 않고 [ 로그인 ] 을 하는 방법도 있음


6. 클라이언트가 id, pw 파라미터를 비정상적인 값으로 보냈다면 "empty" 문자열을 클라이언트에게 응답함(3)

7. 클라이언트가 id, pw 파라미터를 정상적인 값으로 보냈다면(4)

8. id와 tempId가 일치하고 pw와 tempPw가 일치한다면 "yes" 문자열을 클라이언트에게 응답함(5)

9. id와 tempId가 일치하지 않거나 pw와 tempPw가 일치하지 않는다면 "no" 문자열을 클라이언트에게 응답함(6)

 

서버가 응답했으니 다시 클라이언트의 ajax 로 돌아와야함

왜? ajax로 요청했으니까 / 서버의 응답에 따라 사용자에게 어떤 결과를 보여줘야할지도 ajax가 결정하는 것(1)

 

10. 서버가 보낸 데이터("empty", "yes", "no" 중에 하나)를 responseData 매개변수에 저장(2)

11. 서버가 보낸 데이터를 출력(3)

12. 서버가 보낸 데이터에 따른 결과를 사용자에게 보여줌(4)


여기까지 서버가 클라이언트에게 결괏값을 전달해야하는 상황과 결괏값을 전달하는 방법을 알아봤음

728x90
LIST