서블릿 공부를 하면서 WAS를 직접 만들어보는 시간을 가지고 있다. 

아직 완성되지는 않았지만 중간점검(?) 느낌으로 기록하려고 한다. 

 

먼저 코드를 작성하기 전에 아이패드로 그림을 그려서 설계를 해보았다. 

(설계라고 말하기도 민망ㅎㅅㅎ)

전체적인 시나리오? 작성을 하면서 계속 생각한 것이 "먼저 숲을 보고 나무를 보자" 였다. 

 

그래서 일단 나의 목표는!!!!

각 클래스의 역할, 클래스 간의 관계, 전체적인 흐름을 이해하는 것이었다.

그리고 그 안에 내부적인 기능에 대한 코드 작성은 나중에 더 신경쓰는 방향으로 결정했다.

(그냥 ....... 그래서 메소드 안에 코드가 깔끔하지 않을 수 있다는 것..ㅎ)

 

 

깃허브 : github.com/YuJinSong412/Mini_WAS


설계


 

[그림2]

 

일단 GET 요청을 처리하는 것으로 생각하고 작성했다. 그리고 클래스로더도 사용하지 않았다.

이렇게 적어보면서 코드를 작성한다고 했을 때는 어떤식으로 접근해야되는지 생각해봤다.

(필요한 클래스, 메소드 등 생각해봄.. 코드적으로 생각함..)

 

 

 

 

[그림3]
[그림4]

 

이렇게 정리해보니까 빨리 코드 작성하고 싶은 마음이 생겼었다. 

이래서 설계하라는건가~~~~~~~?ㅋㅋ큐ㅠㅜㅜㅎㅎ

 

결과 화면


 

 

제출 누르면

 

 

 

 

자바 소스 코드


설계한 것처럼 일단 트리구조는 아래 사진과 같다. 

 

 

 

설계에서 적었던 순서대로 코드 진행을 했다.

 

0) 서버 실행할 때 컨테이너 실행도 같이 하면서 서블릿 생성하고 초기화(init())를 한다.

더보기
import container.Container;
import server.WebServer;

public class ServerLaunch {

  public static void main(String[] args) {

    Container container = new Container();  
    container.start();   //컨테이너 실행 
    
    WebServer webServer = new WebServer();
    webServer.start();   //웹서버 실행
  }

}

container.start() 메소드를 보면 서블릿을 인스턴스화 하고 초기화를 합니다. 

더보기
package container;

public class Container {

  ServletInit servletInit;

  public Container() {

    servletInit = new ServletInit();
  }

  public void start() {

    servletInit.startServletInit();
  }
}
package container;

import java.io.File;
import java.util.ArrayList;

public class ServletInit {

  static ArrayList<String> servletNames;

  static ArrayList<Servlet> servletClasses;

  public ServletInit() {

    servletNames = new ArrayList<String>();
    servletClasses = new ArrayList<Servlet>();
  }

 public void startServletInit() {

    getServletName();
    
    for (String servletName : servletNames) {
      try {
        Class c = Class.forName("servlet." + servletName);
        Servlet my = (Servlet) c.newInstance();
        
        servletClasses.add(my);
        my.init();
      } catch (ClassNotFoundException e1) {
        //클래스를 찾지 못했을 경우에 대한 예외사항
        e1.printStackTrace();
      } catch(InstantiationException e2) {
        //인스턴스(new) 실패 시에 대한 예외사항
        e2.printStackTrace();
      } catch(IllegalAccessException e3) {
        //파일접근에 대한 예외사항
         e3.printStackTrace();
      }
    }
  }

  private void getServletName() {

    String path = "C:/Users/kev/eclipse-workspace/WebProject/src/servlet";
    
    File dir = new File(path);
    
    File[] files = dir.listFiles();
    
    for (File file : files) {
      int lastSeparator = file.toString().lastIndexOf("\\");
      int startExtension = file.toString().indexOf(".");
      String servletName = file.toString().substring(lastSeparator + 1, startExtension);
      servletNames.add(servletName);
    }
  }
}

 

여기서 try 부분을 보면 Servlet 인터페이스가 왜 필요한지 알게된 것 같다.

만약 Servlet 인터페이스 없이 (정해진 규격없이) 서블릿 클래스를 만들면 하나씩 직접 다 인스턴스화 해줘야 했다. (for문을 사용할 수 없었음)

그래서 Servlet 인터페이스를 만들고 "Servlet my = (Servlet) c.newInstance();" 이렇게 형변환할 때 Servlet으로 주면 그 Servlet 인터페이스의 구현 클래스들을 순서대로 인스턴스화할 수 있었다. 

 

 

 

이제 웹서버 실행을 보면 

더보기
// 클래스 역할 : 1) 요청을 받음 2)정적,동적파일 구분 3)응답
public class WebServer {

  private static final int PORT = 5027;

  private final String SERVER_PATH = "C:\\Users\\kev\\eclipse-workspace\\WebProject\\WebContent\\basic";

  public static ExecutorService executorService; // 스레드풀인 ExecutorService 선언

  private ServerSocket serverSocket;

  public void start() {

    ExecutorService threadPool = new ThreadPoolExecutor(10, // 코어 스레드 개수
        100, // 최대 스레드 개수
        120L, // 놀고 있는 시간
        TimeUnit.SECONDS, // 놀고 있는 시간 단위
        new SynchronousQueue<Runnable>() // 작업 큐
    ); // 초기 스레드 개수 0개,
    executorService = threadPool;
    
    try {
      serverSocket = new ServerSocket();
      serverSocket.bind(new InetSocketAddress(PORT));
      
      // 연결을 수락하는 코드
      while (true) {
        System.out.println("서버가 연결을 기다림");
        Socket socket = serverSocket.accept();
        // socket.setSoTimeout(1000);
        
        // 스레드 풀의 작업 생성 - 따로 빼고 싶음.. 고민..
        Runnable runnable = new Runnable() {

          @Override
          public void run() {

            InputStream inputStream = null;
            BufferedReader bufferedReader = null;
            OutputStream outputStream = null;
            
            try {
              // 요청을 받는 역할 - 요청을 읽어서 request 객체에 정보를 넣음
              Request request = new Request();
              inputStream = socket.getInputStream();
              bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
              
              receiveRequest(request, bufferedReader);
              System.out.println("오청받기 성공");
              
              outputStream = socket.getOutputStream();
              Response response = new Response(outputStream);
              
              // 정적 파일인지 아닌지 구분
              File file = getFile(request);
              if (file.isFile()) {
                sendResponse(file, response); // 정적파일이면 Response 응답을 보냄
              } else {
                new ContainerController(request, response).start(); // 동적파일이면 컨테이너 역할을 하는
              }
              
            } catch (IOException e) {
              e.printStackTrace();
            } finally {
              try {
              
                inputStream.close();
                bufferedReader.close();
                outputStream.close();
                socket.close();
                
              } catch (IOException e) {
                e.printStackTrace();
              }
            }
          }
        };
        executorService.submit(runnable);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (!serverSocket.isClosed()) {
      stopServer();
    }
  }

.....이어서

서버가 연결을 기다리다가 요청을 받으면 Runnable 작업 생성을 한다. WebServer 클래스의 역할인 요청 받고, 파일 구분하고, 응답하는 것 모두 runnable 안에서 이루어진다.(이 3가지 수행이 하나의 작업 단위임)

 

runnable 안에 있는 이 두 부분을 아래 설명과 같이 볼 것! 

더보기
// 요청을 받는 역할 - 요청을 읽어서 request 객체에 정보를 넣음
              Request request = new Request();
              inputStream = socket.getInputStream();
              bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
              
              receiveRequest(request, bufferedReader);
              System.out.println("오청받기 성공");
              outputStream = socket.getOutputStream();
              Response response = new Response(outputStream);
              
              // 정적 파일인지 아닌지 구분
              File file = getFile(request);
              if (file.isFile()) {
                sendResponse(file, response); // 정적파일이면 Response 응답을 보냄
              } else {
                new ContainerController(request, response).start(); // 동적파일이면 컨테이너 역할을 하는
              
              }
 

 

 

 

1) 받은 HTTP Request 부분을 클래스 Request에 정보를 담는다.

앞에 Runnable 안에 있는 receiveRequest()메소드가 Request 객체 안에 요청받은 정보를 넣는 행동을 한다.

더보기
  private void receiveRequest(Request request, BufferedReader bufferedReader) {

    try {
      String line = "tmp";
      
      Map<String, String> requestHeaderMap = new HashMap<String, String>();
      
      for (int i = 1; (line = bufferedReader.readLine()) != null && !line.equals(""); i++) {
        // System.out.println(line);
        
        if (i == 1) {
          int firstBlank = line.indexOf(" ");
          int lastBlank = line.lastIndexOf(" ");
         
          request.setMethod(line.substring(0, firstBlank));
          request.setRequestUrl(line.substring(firstBlank + 1, lastBlank).trim());
          request.setHttpVersion(line.substring(lastBlank + 1).trim());
          
        } else {
          
          int indexOfColon = line.indexOf(": ");
          
          if (indexOfColon == -1) {
            continue;
          } else {
            
            String headerName = line.substring(0, indexOfColon).trim();
            String headerValue = line.substring(indexOfColon + 1).trim();
            
            requestHeaderMap.put(headerName, headerValue);
          }
        }
      }
      
      request.setHeaderMap(requestHeaderMap);
      
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

 

Request 클래스를 보면 아래 코드와 같다.

더보기
package communication;

import java.util.HashMap;
import java.util.Map;

public class Request {

  private String method;

  private String requestUrl;

  private String httpVersion;

  private String connection;

  private String accept;

  private String acceptEncoding;

  private Map<String, String> headerMap;

  public Request() {

    headerMap = new HashMap<String, String>();
  }

  public String getMethod() {

    return method;
  }

  public void setMethod(String method) {

    this.method = method;
  }

  public String getRequestUrl() {

    return requestUrl;
  }

  public void setRequestUrl(String requestUrl) {

    this.requestUrl = requestUrl;
  }

  public String getHttpVersion() {

    return httpVersion;
  }

  public void setHttpVersion(String httpVersion) {

    this.httpVersion = httpVersion;
  }

  public String getConnection() {

    return connection;
  }

  public void setConnection(String connection) {

    this.connection = connection;
  }

  public String getAccept() {

    return accept;
  }

  public void setAccept(String accept) {

    this.accept = accept;
  }

  public String getAcceptEncoding() {

    return acceptEncoding;
  }

  public void setAcceptEncoding(String acceptEncoding) {

    this.acceptEncoding = acceptEncoding;
  }

  public Map<String, String> getHeaderMap() {

    return headerMap;
  }

  public void setHeaderMap(Map<String, String> headerMap) {

    this.headerMap = headerMap;
  }
}

 

 

2) 정적 페이지, 동적 페이지 구분하기 (isFile) -> 경로 안에 파일이 있으면 정적!

3) 정적 페이지는 index.html(해당 페이지)를 읽는다.

더보기
  private File getFile(Request request) {

    String changeRequestUrl = request.getRequestUrl().replace("/", "\\");
    String requestUrl = SERVER_PATH + changeRequestUrl + ".html";
    
    File file = new File(requestUrl);
    
    return file;
  }

이 메소드를 이용해서 파일을 return하고 isFile()이 true이면 정적파일이므로 Response 응답을 보낸다. => sendResponse(file, response);

 

 

4) 클래스 Response로 응답하여 화면을 보여준다.

더보기
  public void sendResponse(File file, Response response) throws IOException {

    response.setFirstLine("HTTP/1.1 200 OK");
    response.setContentType("Content-Type: text/html; charset=UTF-8");
    response.setContentLength("Content-Length: " + file.length());
    response.showScreen(file);
    
    System.out.println("정적 파일 응답 보내줌");
  }

 

여기서도 Response 클래스를 보면 아래와 같다.

더보기
package communication;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;

public class Response {

  private String firstLine;

  private String contentType;

  private String contentLength;

  OutputStream outputStream;

  public Response() {}

  public Response(OutputStream outputStream) {

    this.outputStream = outputStream;
  }

  public OutputStream getOutputStream() {

    return outputStream;
  }

  public void setOutputStream(OutputStream outputStream) {

    this.outputStream = outputStream;
  }

  public String getFirstLine() {

    return firstLine;
  }

  public void setFirstLine(String firstLine) {

    this.firstLine = firstLine;
  }

  public String getContentType() {

    return contentType;
  }

  public void setContentType(String contentType) {

    this.contentType = contentType;
  }

  public String getContentLength() {

    return contentLength;
  }

  public void setContentLength(String contentLength) {

    this.contentLength = contentLength;
  }

  public void showScreen(File file) throws IOException {

    PrintWriter out = new PrintWriter(outputStream);
    out.println(firstLine);
    out.println(contentType);
    out.println(contentLength);
    out.println();
    out.flush();

    ArrayList<Byte> fileBytes = getFileBytes(file);
    byte[] byteArr = new byte[fileBytes.size()];
    int writeCount = 0;
    for (byte fileByte : fileBytes) {
      byteArr[writeCount] = fileByte;
      writeCount++;
    }
    outputStream.write(byteArr);
    outputStream.flush();
  }

  private ArrayList<Byte> getFileBytes(File file) {

    ArrayList<Byte> fileBytes = new ArrayList<Byte>();
    try {
      FileInputStream fis = new FileInputStream(file);
      int data = 0;
      while ((data = fis.read()) != -1) {
        fileBytes.add((byte) data);
      }
      fis.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return fileBytes;
  }
}

 

 

5) 동적페이지는 컨테이너의 역할을 하는 클래스인 ContainerController로 Request와 Response객체를 넘긴다. 그리고 HTTPServletRequest와 HTTPServletResponse 객체를 생성한다.

 

앞 코드 다시 보면 "new ContainerController(request, response).start();" 이것이 있다. 이것이 실행된다.

더보기
package container;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import communication.Request;
import communication.Response;

// 클래스 역할 : httpServletRes,req 객체 생성, xml 분석, 스레드 실행 후 서블릿 실행, httpServletReq 응답 처리(?)
public class ContainerController {

  private static final String XML_PATH = "C:\\Users\\kev\\eclipse-workspace\\WebProject\\WebContent\\WEB-INF\\web.xml";

  private static final String FOLDER_NAME = "/basic";

  private static final String SERVLET_FOLDER_NAME = "servlet.";

  HTTPServletRequest httpServletRequest;

  HTTPServletResponse httpServletResponse;

  Map<String, String> servletMap;

  String foundServletName;

  public ContainerController(Request request, Response response) {

    System.out.println("컨테이너 로직까지 왔음");
    httpServletRequest = new HTTPServletRequest(request);
    httpServletResponse = new HTTPServletResponse(response);
    servletMap = new HashMap<String, String>();
  }

  public void start() {

    // xml분석
    servletMap = readDeploymentDescriptor();
    
    if (servletMap.size() == 0) {
      
      System.out.println("서블릿이 없습니다.");
      
    } else {
      
      foundServlet();
      executeServlet();
      
    }
  }
  ....이어서

여기는 start() 메소드 안을 볼 것!

 

<기억할 메소드>

readDeploymentDescriptor();

foundServlet();

executeServlet();  

 

6번인 XML 파일 분석하기 전에 HTTPServletRequest와 HTTPServletResponse 클래스를 먼저 보겠다.

더보기
package container;

import java.util.HashMap;
import java.util.Map;
import communication.Request;

public class HTTPServletRequest {

  Request request;

  private String method;

  private String queryString;

  private String requestUrl;

  public HTTPServletRequest(Request request) {

    this.request = request;
    
    reApply();
  }

  private void reApply() {

    method = request.getMethod();
    
    String[] separationUrl = request.getRequestUrl().split("\\?");
    
    if (separationUrl.length != 0) {
      
      requestUrl = separationUrl[0];
      queryString = separationUrl[1];
      
    }
  }

  public Request getRequest() {

    return request;
  }

  public void setRequest(Request request) {

    this.request = request;
  }

  public String getMethod() {

    return method;
  }

  public void setMethod(String method) {

    this.method = method;
  }

  public String getQueryString() {

    return queryString;
  }

  public void setQueryString(String queryString) {

    this.queryString = queryString;
  }

  public String getRequestUrl() {

    return requestUrl;
  }

  public void setRequestUrl(String requestUrl) {

    this.requestUrl = requestUrl;
  }

  public String getParameter(String name) {

    Map<String, String> queryStringValue = new HashMap<String, String>();
    
    queryStringValue = divideQueryString();
    
    String result = "";
    
    for (String key : queryStringValue.keySet()) {
      if (key.equals(name)) {
        result = queryStringValue.get(key);
      } else {
        result = null;
      }
    }
    
    return result;
    
  }

  private Map<String, String> divideQueryString() {

    Map<String, String> queryStringValue = new HashMap<String, String>();
    
    String[] separationQuery = queryString.split("=");
    
    queryStringValue.put(separationQuery[0], separationQuery[1]);
    
    return queryStringValue;
  }
}
package container;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import communication.Response;

public class HTTPServletResponse {

  Response response;

  private String contentType;

  OutputStream outputStream;

  private int contentLength;

  public HTTPServletResponse(Response response) {

    this.response = response;
    
    reApply();
  }

  private void reApply() {

    this.outputStream = response.getOutputStream();
  }

  public PrintWriter headerInfo() {

    PrintWriter out = new PrintWriter(outputStream);
    
    out.println("HTTP/1.1 200 OK");
    out.println("Content-Type: text/html; charset=UTF-8");
    out.println();
    out.flush();
    
    return out;
  }

  public PrintWriter getWriter() throws IOException {

    PrintWriter out = headerInfo();
    
    return out;
  }

  public void showScreen(ByteArrayOutputStream bout) throws IOException {

    headerInfo();
    
    bout.writeTo(outputStream);
  }

  public int getContentLength() {

    return contentLength;
  }

  public void setContentLength(int contentLength) {

    this.contentLength = contentLength;
  }

  public String getContentType() {

    return contentType;
  }

  public void setContentType(String contentType) {

    this.contentType = contentType;
  }

  public OutputStream getOutputStream() {

    return outputStream;
  }

  public void setOutputStream(OutputStream outputStream) {

    this.outputStream = outputStream;
  }
}

 

 

 

6) XML 파일을 분석하여

 

readDeploymentDescriptor() 메소드를 이용해서 분석한다. 

더보기
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	id="WebApp_ID" version="4.0">
	<display-name>Mini_WAS</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>

	<servlet>
		<servlet-name>index2</servlet-name>
		<servlet-class>servlet.MyServlet</servlet-class>
		<init-param>
			<param-name>adminEmail</param-name>
			<param-value>ujsong4@naver.com</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>index2</servlet-name>
		<url-pattern>/basic/SelectBeer.do</url-pattern>
	</servlet-mapping>
</web-app>
private Map<String, String> readDeploymentDescriptor() {

    Map<String, String> servletMap = new HashMap<String, String>();
    
    File file = new File(XML_PATH);
    
    try {
      FileInputStream fis = new FileInputStream(file);
      BufferedReader br = new BufferedReader(new InputStreamReader(fis));
      
      String line = "";
      String oldLine = "";
      String servletClass = "";
      String servletName = "";
      
      for (int i = 1; (line = br.readLine()) != null; i++) {
        if (oldLine.contains("<servlet-name>") && line.contains("<servlet-class>")) {
          int name_firstText = oldLine.indexOf(">") + 1;
          int name_lastText = oldLine.indexOf("</") - 1;

          int firstText = line.indexOf(">") + 1;
          int lastText = line.indexOf("</") - 1;

          servletName = oldLine.substring(name_firstText, name_lastText + 1);
          servletClass = line.substring(firstText, lastText + 1);

          servletMap.put(servletName, servletClass);

        } else if (oldLine.contains("<servlet-name>") && line.contains("<url-pattern>")) {
          int name_firstText = oldLine.indexOf(">") + 1;
          int name_lastText = oldLine.indexOf("</") - 1;

          String servletName2 = oldLine.substring(name_firstText, name_lastText + 1);

          if (servletName2.equals(servletName)) {
            int firstText = line.indexOf(">") + 1;
            int lastText = line.indexOf("</") - 1;
            String servletUrl = line.substring(firstText, lastText + 1);

            servletMap.put(servletUrl, servletClass);
          }
        }
        oldLine = line;
      }
      fis.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return servletMap;
  }

 

 

 

7) 찾을 서블릿을 찾으면 그 서블릿의 service(HTTPServletRequest, HTTPServletResponse) 메소드를 실행한다. 

 

foundServlet()메소드에서 찾는 서블릿을 찾고 executeServlet()메소드에서 service()메소드를 실행한다.

더보기
  private void foundServlet() {

    String requestUrl_new = FOLDER_NAME + httpServletRequest.getRequestUrl();
    
    String servletName = "";
    
    for (String key : servletMap.keySet()) {
      if (key.equals(requestUrl_new)) {
        servletName = servletMap.get(key);
        System.out.println(servletName + " 서블릿 찾았다.");
      }
    }
    
    foundServletName = servletName;
    
  }
  private void executeServlet() {

    try {
      int i = 0;
      for (String servletName : ServletInit.servletNames) {
        if (foundServletName.equals(SERVLET_FOLDER_NAME + servletName)) {
          ServletInit.servletClasses.get(i).service(httpServletRequest, httpServletResponse);
        }
        i++;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

 

 

 

8) service()는 내가 작성한 서블릿의 오버라이딩된 doGet()를 자동 호출하여 실행한다.

 

Servlet 인터페이스 , HttpServlet 추상 클래스, MyServlet 클래스 순서대로..

더보기
package container;

public interface Servlet {

  public void init(ServletConfig servletConfig);

  public void init();

  public void service(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse);

  public void destroy();

  public String getServletInfo();

  public ServletConfig getServletConfig();
}
package container;

public abstract class HttpServlet implements Servlet {

  private static final String METHOD_GET = "GET";

  ServletConfig servletConfig;

  @Override
  public void init(ServletConfig config) {

    servletConfig = config;
    this.init();
  }

  public void init() {

    System.out.println("서블릿 init()초기화!");
  }

  public void destroy() {}

  // public String getInitParameter(String name) {
  //
  // return getServletConfig().getInitParameter(name);
  // }
  @Override
  public void service(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse) {

    System.out.println("service() 메소드는 HttpServlet 클래스 여기로");
    
    String method = httpServletRequest.getMethod();
    
    if (method.equals(METHOD_GET)) {
      System.out.println("doGet() 메소드 실행");
      doGet(httpServletRequest, httpServletResponse);
    }
  }

  public void doGet(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse) {}

  @Override
  public String getServletInfo() {

    // TODO Auto-generated method stub
    return null;
  }

  @Override
  public ServletConfig getServletConfig() {

    // TODO Auto-generated method stub
    return null;
  }
}
package servlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import container.HTTPServletRequest;
import container.HTTPServletResponse;
import container.HttpServlet;

public class MyServlet extends HttpServlet {

  private static final String ENCODING = "utf-8";

  @Override
  public void doGet(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse) {

    ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
    PrintWriter out;
    
    try {
      out = new PrintWriter(new OutputStreamWriter(bout, ENCODING));
      // out = httpServletResponse.getWriter();
      
      out.println("<html><head><style>body{background-color: #F8F4BD;}</style></head><body>" + "<h1 align=center>유진스의 선택은!!!!???<br></h1>");
      
      String result = httpServletRequest.getParameter("kind");
      
      out.println("<br><h3>맛도 좋고 몸에도 좋은 ~ <span style = 'color: red'>" + result + "</span></h3></body></html>");
      out.flush();
      
      httpServletResponse.setContentLength(bout.size());
      System.out.println(bout.size());
      
      httpServletResponse.showScreen(bout);
      
    } catch (IOException e) {
      
      e.printStackTrace();
      
    }
  }
}

 

 

 

9) HTTPServletResponse 객체로 응답을 보내서 화면을 보여준다.

 

여기서 2가지 방법을 적용시켜봤다. 

1. 처음에는 동적페이지는 응답 헤더에 content-Length를 주지 않아도 된다고 해서 content-Length를 주지 않는 방법

2. 임시 바이트 버퍼를 사용하여 길이를 먼저 측정하고 출력하는 방법

 

MyServlet의 doGet()메소드와 HTTPServletResponse 2개만 다시 보면 된다.  HTTPServletResponse 부분은 다른 메소드를 사용함

[첫 번째 방법!!!]

더보기
public class MyServlet extends HttpServlet {

  private static final String ENCODING = "utf-8";

  @Override
  public void doGet(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse) {

    //ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
    PrintWriter out;
    
    try {
      //out = new PrintWriter(new OutputStreamWriter(bout, ENCODING));
       out = httpServletResponse.getWriter();
      
      out.println("<html><head><style>body{background-color: #F8F4BD;}</style></head><body>" + "<h1 align=center>유진스의 선택은!!!!???<br></h1>");
      
      String result = httpServletRequest.getParameter("kind");
      
      out.println("<br><h3>맛도 좋고 몸에도 좋은 ~ <span style = 'color: red'>" + result + "</span></h3></body></html>");
      out.flush();
      
//      httpServletResponse.setContentLength(bout.size());
//      System.out.println(bout.size());
//      
//      httpServletResponse.showScreen(bout);
      
    } catch (IOException e) {
      
      e.printStackTrace();
      
    }
  }
}

 

[두 번째 방법!!!]

더보기
public class MyServlet extends HttpServlet {

  private static final String ENCODING = "utf-8";

  @Override
  public void doGet(HTTPServletRequest httpServletRequest, HTTPServletResponse httpServletResponse) {

    ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
    PrintWriter out;
    
    try {
      out = new PrintWriter(new OutputStreamWriter(bout, ENCODING));
      // out = httpServletResponse.getWriter();
      
      out.println("<html><head><style>body{background-color: #F8F4BD;}</style></head><body>" + "<h1 align=center>유진스의 선택은!!!!???<br></h1>");
      
      String result = httpServletRequest.getParameter("kind");
      
      out.println("<br><h3>맛도 좋고 몸에도 좋은 ~ <span style = 'color: red'>" + result + "</span></h3></body></html>");
      out.flush();
      
      httpServletResponse.setContentLength(bout.size());
      System.out.println(bout.size());
      
      httpServletResponse.showScreen(bout);
      
    } catch (IOException e) {
      
      e.printStackTrace();
      
    }
  }
}

 

이렇게 다 하면 앞에 캡쳐한 결과 화면이 실행된다~!

 


아직 ServletConfig 적용도 못했고 GET 메소드 처리하는 것밖에 안했고,, 코드도 정리해야하고,, 사실 이 모든 것이 이상할 수도 있는데...

상무님 검사를 받아야한당,,, 

검사를 받고 또 작성할 것임😤😭🥺

'IT > Servlet & JSP' 카테고리의 다른 글

[Servlet & JSP] 3. 속성과 리스너  (0) 2020.11.09
[Servlet & JSP] 2. Servlet 따라가보기  (0) 2020.11.05

발표자료 4 ~ 7 단원 내용 중 ...

2. Servlet 따라가보기 이어서!

발표자료보다 내용을 더 추가해서 PPT가...없을 수 있다....ㅎ..

 

[이전글]

2020/11/05 - [IT/Servlet & JSP] - [Servlet & JSP] 2. Servlet 따라가보기 

 

[Servlet & JSP] 2. Servlet 따라가보기

발표자료 4 ~ 7 단원 내용 중 ... Servlet 최초 클라이언트의 요청이 있기 전에 서블릿은 항상 로딩되고 초기화됩니다. 로딩되는 시기는 컨테이너 시작 (즉, 톰캣이 실행될 때) 이루어집니다. 그리고

song-yujin.tistory.com

 

속성과 리스너


이제 속성 내용은 앞에서 이야기한 ServletConfig ServletContext내용입니다.

 

 

 

앞에서 이야기한 ServletConfig 가장 역할은 서블릿 초기화 파라미터에 접근할 있는 능력이 있다는 것입니다.

ServletConfig 값을 설정하기 위한 곳이 코드 보이는 것처럼 DD(배포 서술자)이고, 하나의 Servlet에서 계속적으로 읽어와서 사용하고자하는 용도보다는 처음에 Servlet 로딩시 읽어와서 계속 사용하고자 하는 의미가 큽니다.

 

서블릿의 설정이기 때문에 servlet안에다가 작성을 해줘야합니다. 그리고 서블릿 코드에서는 GetServletConfig() 메소드의 리턴값이 servletConfig여서 servletConfig getInitParameter()메소드를 접근하여 사용할 있습니다.

 

 

 

그런데 화면을 나타내는 JSP페이지에서도 서블릿의 초기화 파라미터 값을 읽으려면, 이것은 그대로 서블릿의 설정이기 때문에 가지 작업을 해야합니다.

그리고 만약에 이메일 정보를 모든 애플리케이션에서 공유해야하고, 요청할 때마다 달라지지 않을 , 서블릿에만 적용되는 초기화 파라미터 말고 애플리케이션 적용되는 것이 없나? 대한 한계가 나왔습니다.

 

 

 

한계의 답이 컨텍스트 초기화 파라미터입니다.

이것은 서블릿 초기화 파라미터와 동일하지만, 컨텍스트 초기화 파라미터는 특정 하나의 서블릿만 사용하는 것이 아니라 모든 애플리케이션에서 이용할 있다는 차이가 있습니다. 애플리케이션에 있는 모든 JSP, 서블릿에서 별다른 코딩 없이도 컨텍스트 초기화 파라미터 정보에 접근할 있으며, 모든 서블릿의 DD 수정하는 따위는 하지 않아도 됩니다.

 

코드를 보면 servletConfig 거의 비슷한데 다른점은 servlet 안에다가 작성하면 안되는 부분, 그리고 getServletContext()라는 것입니다.

 

 

 

표는 ServletConfig와 ServletContext 비교하여 정리한 표입니다. 

 

ServletContext에 대해서 더 정리를 하겠습니다.

ServletContext는 JSP나 서블릿을 컨테이너 또는 다른 웹 애플리케이션과 연결하는 끈이라고 할 수 있습니다.

 

 

컨텍스트 파라미터의 한계


컨텍스트 파라미터에는 String 밖에 저장할 수 없습니다. 

초기화 파라미터로 Dog라는 객체를 저장하고 싶을 때나 애플리케이션에 있는 모든 서블릿이 데이터베이스 Connection객체를 공유하고 싶을 때는 어떻게 하지? 라는 한계가 나왔습니다. 

 

가장 일반적인 방법은 "컨텍스트 파라미터에 DataSource 검색명(lookup name)를 저장하는 것입니다.

 

그런데 이것을 또 누가 하느냐? 이야기가 나올 수 있습니다.

서블릿에서 검색명으로 실제 DataSource 객체를 찾아서, 이 객체를 속성에 묶어두면 되지 않을까? 싶지만 안됩니다. 

서블릿 코드 안에다가 넣을 수 없는 이유는 어떤 서블릿이 가장 먼저 호출될지 모르기 때문입니다.

 

 

 

 

리스너


이것에 대한 답은 리스너입니다. 

 

웹 애플리케이션에도 main()처럼 제일 먼저 실행되는 메소드, 어떤 서블릿이나 JSP보다도 먼저 실행되어야 하는 코드를 작성할 메소드!

이것이 바로 리스너입니다.

 

즉, 컨텍스트 초기화 이벤트에 리스닝하는 것입니다. 이를 이용하면 컨텍스트 초기화 파라미터를 읽은 다음, 애플리케이션이 클라이언트에게 서비스하기 전에 특정 코드를 실행할 수 있습니다. 

 

이것은 서블릿이나 JSP가 아닌 다른 자바 객체가 해주는데 "ServletContextListener"입니다.

ServletContextListener의 목적은 애플리케이션 초기화입니다.  

 

이 클래스는 ServletContext 이벤트(초기화(생성)와 소멸)에 귀 기울이고 있어야 합니다. 

javax.servlet.ServletContextListener 인터페이스를 구현하면 이 클래스를 만들 수 있습니다. 

 

빨간색 표시로 되어있는 부분은 구현한 클래스가 이러한 기능들을 가져야 합니다.

 

 

ServletContextListener 사용하기 


지금부터 ServletContextListener를 만들고 실행시키는 테스트를 해서 어떻게 동작하는지 보겠습니다. 

 

이 예제에서는 String 초기화 파라미터를 실제 객체(Dog)로 전환할 것입니다.

 

<시나리오>

  1. 리스너는 컨텍스트 초기화 파라미터에서 개의 품종을 확인한 다음, 이 값을 Dog 객체 생성자 인자로 사용하여 Dog를 생성
  2. 리스너는 Dog 객체를 ServletContext 속성에 묶어 두는 작업

그러면 서블릿이 이를 끄집어 낼 수 있습니다. 

이렇게 하면 서블릿이 애플리케이션 객체(여기서는 Dog)를 공유할 수 있습니다. 이제 서블릿이 컨텍스트 파라미터를 읽을 필요가 없습니다. 

 

초기화 파라미터로 객체를 생성할 수 있다는 것과 이 객체를 모든 애플리케이션에서 공유할 수 있다는 것이 중요합니다. 

 

 

<Dog 예제>

  1. 리스너는 ServletContextEvent에서 ServletContext의 참조를 얻습니다.
  2. 리스너는 ServletContext에서 컨텍스트 초기화 파라미터 품종(breed)이 무엇인지 값을 읽습니다. 리턴되는 값은 품종이 무엇인가 하는 String입니다.
  3. 리스너는 품종 String으로 Dog 생성자를 호출합니다. 
  4. 리스너는 ServletContext의 속성(Attribute)에 생성한 Dog를 물려 놓습니다.
  5. 테스트 서블릿에서 ServletContext에 물려 있는 Dog 객체를 끄집어 내어, 품종이 무엇인지 getBreed() 메소드를 호출하여 확인합니다. 

따라서 정리를 하면,

1. ServletContext 이벤트를 리스닝하기 위해서는 ServletContextListener 인터페이스를 구현

2. 컴파일된 파일을 WEB-INF/classes 디렉토리에 배포

3. 배포 서술자 web.xml에 <listener> 항목을 추가하여 컨테이너에게 알림

 

 

 

ServletContextListener 튜토리얼


3개의 클래스와 하나의 DD가 필요합니다.

 

  1. MyServletContextListener.java (The ServletContextListener) : 컨텍스트 초기화 파라미터를 읽어, Dog 객체를 생성하고 이를 컨텍스트 속성에 묶음
  2. Dog.java (속성 클래스) : 평범한 자바 객체, ServletContextListener에서 생성되어 속성값이 됨. ServletContext의 속성에 묶어 두면, 나중에 서블릿이 꺼내 씀
  3. ListenerTester.java (서블릿) : HttpServlet 클래스를 상속함. 컨테스트에서 Dog 객체를 끄집어 내어 getBreed() 메소드를 호출하여 이 내용을 Response에 출력할 것.
  4. web.xml (배포 서술자) : 컨테이너한테 리스너를 만들었음을 알려줄 때. 

 

 

1. MyServletContextListener 작성

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyServletContextListener implements ServletContextListener {

  public void contextInitialized(ServletContextEvent event) {

    ServletContext sc = event.getServletContext();  // event에게 servletContext 요구
    String dogBreed = sc.getInitParameter("breed");   //컨텍스트에서 초기화 파라미터 읽음
    Dog d = new Dog(dogBreed);   //dog 객체 생성
    sc.setAttribute("dog", d);    //컨텍스트 속성에 이름/객체의 쌍으로 묶어 놓음
  }

  public void contextDestroyed(ServletContextEvent event) {

  }

}

컨텍스트 초기화 파라미터에서 개의 품종이 무엇인지 확인하여, 해당 품종의 Dog 객체를 생성한 다음, 이 객체를 컨텍스트 속성으로 묶어 놓은 것입니다. 

 

 

2. Dog 클래스 작성

public class Dog {

  private String breed;

  public Dog(String breed) {

    this.breed = breed;
  }

  public String getBreed() {

    return breed;
  }
}

컨텍스트 초기화 파라미터로 생성한 다음, ServletContext에 이 클래스를 속성으로 저장할 것입니다. 

 

 

3. ListenerTester 작성

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;

/**
 * Servlet implementation class ListenerTester
 */
@WebServlet("/ListenerTester")
public class ListenerTester extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ListenerTester() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	  response.setContentType("text/html");
	  PrintWriter out = response.getWriter();
	  
	  out.println("test context attributes set by listener<br>");
	  
	  out.println("<br>");
	  
	  Dog dog = (Dog) getServletContext().getAttribute("dog");   //리스너가 제대로 작동하지 않았다면, NullPointException 예외를 만날 것임.
	  
	  out.println("Dog's breed is: " + dog.getBreed());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

이 클래스는 ServletContextListener를 테스트하기 위한 것입니다. 제대로 작동한다면, 서블릿의 doGet()을 실행할 경우 Dog 객체가 서블릿 컨텍스트 안에 기다리고 있습니다. 

 

 

3. ListenerTester 작성

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>Project2</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <context-param>
  	<param-name>breed</param-name>
  	<param-value>Great Dane</param-value>
  </context-param>
  
  <listener>
  	<listener-class>
  		MyServletContextListener
  	</listener-class>
  </listener>
</web-app>

 

'IT > Servlet & JSP' 카테고리의 다른 글

[Mini_WAS] WAS만들기 - 중간점검  (0) 2020.12.02
[Servlet & JSP] 2. Servlet 따라가보기  (0) 2020.11.05

발표자료 4 ~ 7 단원 내용 중 ...

 

Servlet


최초 클라이언트의 요청이 있기 전에 서블릿은 항상 로딩되고 초기화됩니다. 로딩되는 시기는 컨테이너 시작 (즉, 톰캣이 실행될 때) 이루어집니다.

 

그리고 이제 클라이언트 요청을 받았을 때 흐름순서를 보면,

 

  1. 클라이언트 사용자쪽에서 URL을 입력하면 HTTP Request가 servlet Container로 전송합니다. 
  2. 컨테이너는 HttpServletRequest와 HttpServletResponse 두 개의 객체를 생성합니다.
  3. 그리고 URL을 분석하여 어떤 서블릿을 요청했는지 파악하고 해당 서블릿을 처리할 스레드를 생성해서 request와 response 객체 참조를 넘깁니다. 
  4. 컨테이너는 해당 서블릿 service() 메소드를 호출하여, service() 메소드는 doGet()고 doPost()를 자동 호출하는데, 브라우저에서 지정한 방식에 따라 둘 중에 하나를 선택해서 호출합니다.
  5. doGet() 메소드는 동적 페이지를 생성한 후, HttpServletResponse 객체에 응답을 보냅니다. 
  6. 응답이 끝나면 HttpServletRequest와 HttpServletResponse 두 객체를 소멸시킵니다. 

 

이런 서블릿에 관련된 것들은 모두 javax.servlet 이나 javax.servlet.http 둘 중 하나입니다.

http와 관련이 있는 것들은 javax.servlet.http이고 나머지는 javax.servlet입니다.  

 

 

자바 서블릿 클래스는 Servlet 인터페이스를 구현해야 합니다. Servlet 서블릿 컨테이너가 서블릿에 대해 호출하는 메소드 5가지를 정의한 인터페이스입니다. 굵은 표시로 되어있는 것이 생명주기 메소드입니다.

 

GenericServlet이랑 HttpServlet 추상 클래스로

- genericServlet 대부분 서블릿의 서블릿 행위 라고 하는 것들이 여기서부터 나왔다는 것

- http http적인 측면을 반영하기 위해서 service()메소드를 재정의한 것입니다. 메소드는 오로지 http request response 받아드립니다.

 

마지막으로 Myservlet 이런 상위 클래스의 메소드를 상속받음으로써, 여기선 doGet()이나 doPost() 메소드를 재정의하기만 하면 됩니다.

 

 

 

그래서 총 앞의 흐름대로 이야기를 하면

 

  1. 컨테이너가 처음 init() 메소드를 호출합니다. 여기서 init()을 재정의하지 않으면 아까 말한 '서블릿 행위'인 genericServlet의 init()을 호출합니다.
  2. 다음 클라이언트의 요청이 들어오면 새로운 스레드를 만들어서 service()메소드를 호출합니다. 이것도 재정의하지 않으면 httpServlet service()메소드가 실행됩니다.
  3. 그리고 Service()메소드에서 재정의한 doGet()이나 doPost() 하나를 실행하는 것입니다.

이렇게 앞에 나오는 그림이나 글로 봤을 때 이해는 되지만 너무 추상적인 느낌이 들고, 메소드들을 직접 확인해보고 싶었습니다. 

 

 

 

그래서 처음 초기화하는 init() 메소드부터 확인을 해봤습니다.

생성자의 실행은 단지 서블릿이 존재하지 않음상태에서 초기화됨상태로 옮겨감을 의미합니다. 즉, 클라이언트의 요청에 서비스할 준비가 되었다는 뜻입니다.

그리고 생성자는 단지 객체를 만드는 것이지 서블릿을 만드는 것이 아닙니다. 서블릿이 되기 위해서는 서블릿적인 것을 내려받아야하는데, 쉽게 말해서 자기 명함을 갖고 있는 정식 서블릿이 되려면 2가지가 필요합니다.

 

 

 

2가지는, ServletConfig ServletContext 파라미터 입니다.

두가지에 대해서는 뒷부분에 자세히 나오지만 일단 필요한 부분만 먼저 이야기하자면, ServletConfig에서 'config' 설정의 뜻으로, 서블릿마다 설정했던 값을 뜻합니다. ServletConfig 통해 서블릿 이름, 서블릿 서블릿 초기 매개변수 , 서블릿 환경 정보 등을 얻을 있습니다.

 

 

 

그래서 다시 돌아와서 서블릿이 되기 위해서는, 서블릿을 설정하는 servletConfig 필요합니다.

컨테이너가 서블릿을 초기화할 , 사실 init()메소드를 먼저 실행하여 존재하지 않음에서 초기화된 서블릿 객체를 만들고, 단지 객체인 것을 다음 init(ServletConfig) 메소드를 호출하여 리얼 정식 서블릿을 생성합니다.

 

 

 

서블릿마다 하나씩 ServletConfig 생성하는데, 컨테이너는 DD에서 서블릿 초기화 파라미터를 읽어서 정보를 이름, 쌍으로 해서 ServletConfig 넘겨줍니다. 다음에 ServletConfig 서블릿의 init()메소드에 제공합니다.

 

 

 

 

여기서 ServletConfig 사용할 있는 이유는 앞에서 Servlet 인터페이스 부분을 보면 getServletConfig() 메소드가 있는데, 메소드가 리턴한 ServletConfig 객체에 대한 참조로 ServletConfig 메소드를 호출할 있습니다.

 

 

 

 

이렇게 흐름의 첫 번째인 init() 대한 것을 봤습니다.

이제는 마지막 2~3번째 service() 호출하고 실행이 되어서 재정의한 doGet() doPost() 실행에 대한 것을 보겠습니다.

 

 

 

httpServlet service()메소드 내용입니다. 클라이언트가 날린 http 메소드가 무엇인지 보고 메소드 종류에 따라서 메소드를 호출합니다. 그래서 service() 호출해서 doGet()이든 doPost() 실행하는 것입니다.

 

 

 

여기까지 코드를 직접 보면서 흐름 이해를 해봤고 다음에는 doGet() doPost() 인자로 request response객체의 참조를 넘긴다고 했는데 객체를 가지고 있다는 것이 무엇을 의미하는지 확인해봤습니다.

 

 

 

httpServletRequest 인터페이스는 http 프로토콜에 관련된 메소드들이 추가되어 있습니다. 서블릿이 클라이언트/브라우저와 대화하기 위한 것들이고 httpServletResponse 인터페이스는 http 관련된 오류, 쿠키, 헤더 정보에 대한 메소드들이 추가되어있습니다.

 

이 2개의 인터페이스는 책에 따르면,,

 

 

Head First Servlets & JSP 책

이 2개의 인터페이스는 누가 구현하나? API에 구현된 클래스가 있나? 

그 답은 '컨테이너'이고 '없습니다.' 입니다. 컨테이너 벤더가 구현하는데 어떻게 되는지 신경 쓰지 마세요.! 

요점은 컨테이너가 넘겨준 객체에서 어떤 메소드를 호출할 수 있는가를 아는 것입니다.

 

 

 

 

Request 먼저보면, index.html에서 개의 옵션 사용자가 선택한 하나를 color라는 이름의 파라미터로 전송을 합니다.

전송 method 방식은 post 하고 MyServlet.post()메소드를 보면 request.getParameter()폼에 있는 이름과 맞게 color라고 작성해주고 선택한 값이 들어가게 됩니다.

예시 말고도, 파라미터 값이 여러개일 경우 있지만 결국에는 request 클라이언트의 요청을 처리해주는 것입니다.

 

 

 

이제는 response 클라이언트로 돌려보내는 것입니다. 정보를 분석해서 브라우저는 화면을 출력합니다. Response 객체는 제가 언급하고 싶은 하나가 setContentType()메소드 입니다. 

contentType 무엇을 의미하냐면, 브라우저에게 지금 내려보내는 것이 무엇이다라는 것을 알려주는 것입니다. 그래야 브라우저가 이예 대비하여 여기서는 html 화면에 그릴 준비를 합니다. contentType HTTP 응답에 반드시 포함되어야 하는 HTTP 헤더 정보입니다.

 

 

 

그리고 요청에 대한 응답을 누가할지 선택할 수도 있습니다.

요청을 완전히 다른 URL 방향을 바꿀 수도 있고, 애플리케이션에 있는 다른 컴포넌트(JSP)에게 처리를 위임할 수도 있습니다.

리다이렉트에서는 방향을 바꾸겟다고 판단했다면, sendRedirect()메소드만 호출하면 됩니다.

디스패치는 다른 컴포넌트, JSP 작업을 넘기는 것의 코드로 RequestDispatcher 사용합니다.

'IT > Servlet & JSP' 카테고리의 다른 글

[Mini_WAS] WAS만들기 - 중간점검  (0) 2020.12.02
[Servlet & JSP] 3. 속성과 리스너  (0) 2020.11.09

+ Recent posts