728x90
반응형

[Servlet] 자바 서블릿과 네트워크 | 요청과 응답이 오가는 과정을 파헤쳐보자

 

[Servlet] 자바 서블릿과 네트워크 | 요청과 응답이 오가는 과정을 파헤쳐보자

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2 [Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1 [Serv

crushed-taro.tistory.com

1. Thread

1. Thread 개요

1. Process와 Thread

  • Process
    • 실행 중인 프로그램을 가리키며, 프로그램이 실행될 때마다 개별적으로 생성된다.
    • 하나의 프로세스는 프로그램을 수행함에 있어 필요한 데이터, 메모리 등의 할당 받은 자원, 하나 이상의 스레드로 구성된다.
  • Thread
    • 프로세스 내에서 할당된 자원을 이용해 실제 작업을 수행하는 작업 단위로, 모든 프로세스는 하나 이상의 스레드를 가지며 각각 독립적인 작업 단위를 지닌다.

 

2. main thread

  • 모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하며 시작되어, main() 메소드의 첫 코드부터 아래로 순차적으로 실행되다가 return을 만나면 종료된다.
  • 필요에 의해 작업 스레드들을 만들어서 병렬 코드를 실행(= 멀티 스레드를 이용한 멀티 태스킹)할 수 있다.
  • 싱글 스레드는 메인 스레드가 종료되면 프로세스도 종료되지만, 멀티 스레드는 실행 중인 스레드가 하나라도 있다면 프로세스가 종료되지 않는다.

 

2. Multi Thread

1. Multi Process와 Multi Thread

Thread 사진 1

  • 멀티 프로세스 : 각각의 프로세스를 독립적으로 실행한다.
  • 멀티 스레드 : 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행한다.

 

2. Single Thread와 Multi Thread

Thread 사진 2

  • 싱글 스레드 : 메인 스레드 하나만 가지고 작업을 처리하며, 한 작업씩 차례대로 처리한다.
  • 멀티 스레드 : 메인 스레드 외의 추가적인 스레드를 이용하여 병렬적으로 작업을 처리한다.

Thread 사진 3

  • 멀티 스레드의 장점
    1. 자원을 보다 효율적으로 사용할 수 있다.
    2. 사용자에 대한 응답성이 향상된다.
    3. 애플리케이션의 응답성이 향상된다.
    4. 작업이 분리되어 코드를 간결하게 작성할 수 있다.
    5. CPU 효율성이 향상된다.
  • 멀티 스레드의 단점
    1. 동기화(= Synchronization)에 주의해야 한다.
    2. 교착상태(= dead-lock)가 발생하지 않도록 주의해야 한다.
    3. 이처럼 프로그래밍 시 고려해야 할 사항이 많다.

 

3. Thread 사용

1. Thread 구현 방법

  • Thread 클래스를 상속 받아 구현하는 방법이 있다.
  • Runnable 인터페이스로 구현하는 방법이 있다.

 

2. Thread 동작 구조

  • run() 호출

Thread 사진 4

 

  • start() 호출

Thread 사진 5

 

  • 스레드 스케줄링

Thread 사진 6

 

3. Java Thread Scheduling 방식

  • 우선 순위 방식 (Priority)
    • 우선 순위가 높은 스레드가 작업 시간을 더 많이 가지도록 하는 스케쥴링 방식이다.
    • 스레드에 1부터 10까지 우선 순위 번호를 부여할 수 있으며, 번호가 높을수록 우선 순위가 높다.
    • 스레드 생성 시 우선 순위 기본값은 5이다.
    • 사용 예시
public class Run {
	public static void main(String[] args) {
		클래스명 레퍼런스 = new 생성자(); //Thread를 상속한 객체 생성
		레퍼런스.setPriority(1 ~ 10);
	}
}

 

  • 순환 할당 방식 (Round-Robin)
    • 시간 할당량(Time Slice)을 정하여 하나의 스레드를 정해진 시간만큼 실행시키는 방식이다.
    • JVM에 의해 정해지기 때문에 코드로 제어가 불가하다.

 

4. Thread control

  • 효율적이고 정교한 스케쥴링을 위하여 실행 중인 thread의 상태를 제어하는 기능을 한다.

Thread 사진 7

  • Thread control의 주요 method는 다음과 같다.
method명 설명
void interrupt() - (sleep() 이나 join() 에 의해) 일시정지 상태인 스레드를 실행대기 상태로 변경
- 해당 스레드에서 InterruptException이 발생해 일시정지 벗어남
void join()
void join(long millis)
void join(ling millis, int nanos)
- 지정된 시간 동안 스레드 실행시킴
- 지정 시간이 지나거나 작업이 종료되면 join() 호출한 스레드로 돌아와 실행 계속함
static void sleep(long millis)
static void sleep(long millis, int nanos)
- 지정된 시간 동안 스레드 일시정지 시킴
- 지정 시간이 지나면 자동으로 실행대기 상태로 변경
static void yield() 실행 중 다른 스레드에게 양보하고 실행대기
void wait()
void wait(long timeout)
void wait(long timeout, int nanos)
동기화 된 블록 안에서 다른 스레드가 이 객체의 notify(), notifyAll()을 호출하거나 지정된 시간이 지날 때까지 현재 thread 대기시킴
void notify() - 동기화 된 블록 안에서 호출한 객체 내부에서 대기 중인 스레드 깨움
- 여러 스레드가 있을 경우 임의의 스레드 하나에만 통보
void notifyAll() - 동기화 된 블록 안에서 호출한 객체 내부에서 대기 중인 모든 스레드 깨움
- 하지만 lock은 하나의 스레드만 얻을 수 있음

 

4. 동기화 (Synchronized)

1. 동기화란

  • 한번에 한 개의 스레드만 프로세스 공유 자원(= instance)에 접근할 수 있도록 락(Lock)을 걸어 다른 스레드가 진행 중인 작업에 간섭하지 못하도록 하는 것이다.

Thread 사진 8

 

2. 동기화 method와 동기화 block

  • 동기화 method
public synchronized void method() {
	// 한 개의 스레드만 실행할 수 있다.
}

 

  • 동기화 블록
public void method1() {
	// 여러 스레드를 실행할 수 있다.

	synchronized (공유 인스턴스) {
		// 한 개의 스레드만 실행할 수 있다.
	}

	// 여러 스레드를 실행할 수 있다.
}

 

Thread 사진 9
Thread 사진 10

 

5. Daemon Thread

1. Daemon Thread란

  • 다른 스레드의 작업을 돕는 보조 역할을 수행하는 스레드이다.
  • 데몬 스레드 이외의 스레드가 모두 종료되면 데몬 스레드는 강제로 종료된다.
  • ex) 가비지 컬렉션, 워드 프로세서의 자동 저장, 화면 자동 갱신 등

 

2. Daemon Thread 생성

  • 데몬 스레드가 될 스레드의 레퍼런스 변수에 setDaemon(true)를 호출하여 생성한다.
  • 단, start() method 호출 전에 setDaemon(true)를 호출해야 한다.
  • → 그러지 않으면 IllegalThreadStateException이 발생한다.

 

6. 실습 예제

1. 간단한 웹 서버 구현 예제

  • 요청 시 Header 값을 출력하는 간단한 웹서버를 구현하는 코드이다.
  • 클라이언트가 요청을 한번 보내면 서버에는 총 3번의 요청이 들어온다. 왜냐하면 브라우저가 favicon 요청을 위해 request를 다시 한 번 보내기 때문이다.
  • 해당 예제에서는 favicon 설정 없이 진행하므로 3번째 요청에 대해서만 처리하도록 구현한다.
public static void main(String[] args) throws IOException {

      /* 클라이언트의 요청을 기다릴 ServerSocket 생성 */
      ServerSocket listener = new ServerSocket(8002);

      /* 요청 횟수를 판단하기 위한 변수 선언 */
      int count = 1;

      try {

          System.out.println("Http Server started at 8002 port");

          while (true) {

              /* 클라이언트 요청이 들어오면 통신할 socket 생성 */
              Socket socket = listener.accept();

              try {
                  /* 요청한 클라이언트의 정보 받아옴 */
                  System.out.printf("New Client Connect! Connected IP : %s, Port : %d\n",
                          socket.getInetAddress(), socket.getPort());

                  count++;

                  InputStream in = socket.getInputStream();
                  OutputStream out = socket.getOutputStream();

                  /* 파비콘 요청을 위한 요청은 건너뛰고 header 내용은 3번째 요청인 본 요청으로 읽음 */
                  int value = 0;
                  while((value = in.read()) != -1 && count == 3) {
                      System.out.print((char)value);
                  }

                  String responseText = "<h1>Hello World</h1>";

                  String responseGeneralHeader = "HTTP/1.1 200 OK \r\n";
                  String contentType = "Content-Type: text/html; charset=utf-8\r\n";
                  String contentLength = "Content-Length: " + responseText.length() + "\r\n";
                  String whiteLine = "\r\n";

                  /* 패킷 통신을 위해 byte 단위로 출력하도록 함 */
                  out.write(responseGeneralHeader.getBytes());
                  out.write(contentType.getBytes());
                  out.write(contentLength.getBytes());
                  out.write(whiteLine.getBytes());

                  out.write(responseText.getBytes());
                  out.write(whiteLine.getBytes());

                  out.flush();

              } finally {
                  /* 자원 반납 */
                  socket.close();
              }
          }
      } finally {
          /* 자원 반납 */
          listener.close();
      }

  }

 

2. Multi Thread를 생성하는 간단한 웹 서버 예제

  • 요청 시마다 멀티 스레드를 생성하여 Header 값을 출력하는 간단한 웹 서버를 구현하는 코드이다.
public static void main(String[] args) throws IOException {

		// 8000번 포트로 서버소켓 생성
    ServerSocket serverSocket = new ServerSocket(8000);

    Socket client;

    while ((client = serverSocket.accept()) != null) {

        Socket finalClient = client;

        /* client socket의 요청을 승인하여 연결될 때마다 스레드 생성 */
        new Thread(() -> {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(finalClient.getInputStream()));

                // General header parsing
                String generalHeader = reader.readLine();
                System.out.println("generalHeader = " + generalHeader);

                String requestMethod = generalHeader.split(" ")[0];
                String requestPath = generalHeader.split(" ")[1];

                System.out.println("requestMethod = " + requestMethod);
                System.out.println("requestPath = " + requestPath);

                // Request header parsing
                Map<String, String> requestHeader = new HashMap<>();
                String line;
                while ((line = reader.readLine()) != null) {

                    /* 읽어온 라인이 비어있지 않은 경우 */
                    if (line.isBlank()) {
                        break;
                    }

                    /* 읽어온 라인을 :(콜론)을 기준으로 나누어 key-value 구분해서 받음 */
                    String key = line.split(": ")[0];
                    String value = line.split(": ")[1];

                    requestHeader.put(key, value);
                }
                System.out.println("requestHeader = " + requestHeader);

                // POST방식의 요청인 경우 body parsing
                String body = null;
                if ("POST".equals(requestMethod)) {

                    int contentLength = Integer.parseInt(requestHeader.get("Content-Length"));
                    char[] temp = new char[contentLength];
                    reader.read(temp);
                    body = new String(temp).trim();
                }
                System.out.println("body = " + body);

            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }).start();

    }

}
728x90
반응형
728x90
반응형

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2

 

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1 [Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1[Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기 [Servl

crushed-taro.tistory.com

1. Network

1. Network 개요

1. Network란

Network 사진 1

  • 여러 대의 컴퓨터를 통신 회선으로 연결한 것이다.
  • (홈 네트워크, 지역 네트워크, 인터넷 등이 해당된다.)

 

2. Server & Client

  • 서버와 클라이언트는 네트워크로 연결된 컴퓨터 간의 관계를 역할로 구분한 개념이다.
    • 서버는 서비스를 제공하는 컴퓨터(혹은 프로그램)로, 클라이언트의 연결을 수락하고 요청 내용을 처리하여 응답을 보내는 역할이다.
    • 클라이언트는 서비스를 받는 컴퓨터(혹은 프로그램)로, 네트워크 데이터를 필요로 하는 모든 애플리케이션에 해당한다.
  • 클라이언트는 서버에 연결 요청을 하려면 IP주소와 Port 번호를 알아야 한다.

Network 사진 2

  • IP 주소는 네트워크 상에서 컴퓨터를 식별하는 번호이며, 네트워크 어댑터(랜카드)마다 할당된다.
  • Port는 같은 컴퓨터 내에서 프로그램을 식별하는 번호이다.

 

2. Java Network

1. Inet Class

  • IP주소를 다루기 위해 Java에서 제공하는 Class이다.
  • 주요 method는 다음과 같다.
Method 명 설명
byte[] getAddress() IP주소를 byte배열로 반환
static InetAddress[] getAllByName(String host) 도메인명에 지정된 모든 호스트의 IP주소를 배열로 반환
static InetAddress getByAddress(byte[] addr) byte 배열로 IP주소 반환
static InetAddress getByName(String host) 도메인명으로 IP주소 반환
String getCanonicaHostName() FQDN(Full Qualified Domain Name) 반환
String getHostAddress() 호스트의 IP주소 반환
String getHostName() 호스트의 이름 반환
static InetAddress getLocalHost() 지역호스트의 IP주소 반환
boolean isMulticastAddress() IP주소의 멀티캐스트 주소 여부 반환
boolean isLoopbackAddress() IP주소의 loopback 주소(127.0.0.1) 여부 반환

 

2. Socket Programming

1. Socket Programming이란

  • 소켓을 이용한 통신 프로그래밍이다.
    • 소켓(Socekt)이란 프로세스 간 통신에 사용되는 양쪽 끝 단을 의미한다.

 

2. Socket Programming 프로토콜 종류

  • TCP
    • 연결 지향적 프로토콜로, 서버가 수락해야 데이터 송수신이 가능하다. (Like 전화)
    • 데이터 전송 속도가 느리지만 정확하고 안정적으로 전달할 수 있다.
  • UDP
    • 비연결 지향적 프로토콜로, 연결 없이 일방적으로 데이터를 전달한다. (Like 택배)
    • 데이터 전송 속도가 빠르지만 신뢰성 없는 데이터를 전송한다.

 

3. TCP Socket Programming

Network 사진 3

  • 클라이언트와 서버 간의 1:1 소켓 통신을 한다.
  • 서버가 먼저 실행되어 클라이언트의 요청을 기다려야 하고, 서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 한다.
  • 자바에서는 TCP 소켓 프로그래밍을 위해 java.net패키지에서 ServerSocket과 Socket Class를 제공한다.

 

  • 서버용 TCP 소켓 프로그래밍 순서
    1. 서버의 port 번호를 정한다.
    2. 서버용 Socket 객체를 생성한다.
    3. 클라이언트 쪽에서 접속 요청이 올 때까지 대기한다.
    4. 접속 요청이 오면 요청 수락 후, 해당 클라이언트에 대한 Socket 객체를 생성한다.
    5. 연결된 클라이언트와 입출력 stream을 생성한다.
    6. 보조 stream을 통해 성능을 개선할 수 있다.
    7. stream을 통해 읽고 쓰기를 진행한다.
    8. 통신을 종료한다.

 

  • 클라이언트용 TCP 소켓 프로그래밍 순서
    1. 서버의 IP 주소와 서버가 정한 port번호를 매개변수로 하여 클라이언트용 Socket 객체를 생성한다.
    2. 서버와의 입출력 Stream을 오픈한다.
    3. 보조 Stream을 통해 성능을 개선할 수 있다.
    4. Stream을 통해 읽고 쓰기를 진행한다.
    5. 통신을 종료한다.

 

  • TCP upgrade → HTTP

Network 사진 4

  • TCP에서 업그레이드된 것이 HTTP이다.
    • 몇 번째 요청인지 순번까지 포함하여 보내고, 받는 쪽에서 재조합해 사용한다.
    ⇒ Socket class를 이용한다.

 

4. UDP Socket Programming

Network 사진 5

  • UDP는 비연결 지향이므로 연결 요청을 받아 줄 서버 소켓이 필요 없다.
  • Java.net 패키지에서 제공하는 두 개의 DatagramSocket간에 DatagramPacket으로 변환된 데이터를 주고 받는다.
728x90
반응형
728x90
반응형

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1

 

[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1

[Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기 [Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기[Servlet] Servlet Filter 구현 예제와 실전 적용 방법 [Servlet] Servlet Filter 구

crushed-taro.tistory.com

 

1. MVC Architecture

1. MVC Architecture 구성

1. Model

  • 웹 애플리케이션의 비즈니스 로직, 즉 수행할 서비스를 담당한다.
    1. Service
      • 여러 DAO를 호출하여 데이터 접근 및 갱신을 하는 클래스이다.
      • DAO를 통해 읽은 데이터에 대한 비즈니스 로직을 수행하여 Controller에 그 결과를 전송한다.
    2. DAO (Data Access Object)
      • 데이터베이스에 직접 접근하여 요청 받은 결과를 반환하는 클래스이다.
    3. VO (Value Object)
      • 계층 간 데이터 교환을 위한 객체 클래스를 말한다.
      • 동의어: DTO(Data Transfer Object), Domain Object, Bean, Entity
      • ※ VO의 동의어는 유래에 따라 의미가 다른 부분이 있지만, 정확한 구분이 어려워 유사한 의미로 쓰인다.

 

2. View

  • 사용자가 요청을 하거나, 요청한 정보를 응답으로 받아서 볼 수 있는 화면을 담당한다.
  • JSP, HTML 등을 통해 표현한다.

 

3. Controller

  • 사용자의 요청을 전달받아 응답 처리를 위한 Service를 호출하고 결과를 View에 전송하는 클래스로, Servlet으로 작성한다.
  • 전달 받은 정보를 바탕으로 사용자 요청을 분석하고, 이를 서비스에 전달할 VO객체를 생성하여 Service에 전달한다. 그리고 Service로부터 받은 결과를 관련된 View 화면에 담아 사용자에게 응답한다.

 

2. 요청의 전달

1. RequestDispatcher

  • 사용자의 요청을 다른 Servlet이나 JSP 페이지에 전달할 때 사용하는 클래스로, request 객체를 사용해 생성할 수 있다.
  • 예시
RequestDispatcher view = request.getRequestDispatcher("/result.jsp" );
view.forward( request, response );

 

2. 동적 페이지 View(JSP)로 응답할 경우

  • request 내장 객체를 통해 RequestDispatcher 객체를 생성한다.
RequestDispatcher view = request.getRequestDispatcher("/result.jsp");

 

  • 대상 View에서 필요한 정보를 request 영역에 저장한다.
request.setAttribute("member", member);

 

  • request와 response를 인자 값으로 전달하여 정보를 전송한다.
view.forward( request, response );

Servlet 사진 1

 

3. 정적 페이지(JSP) View로 응답할 경우

  • 정적인 html은 동적으로 화면을 생성할 필요가 없으므로 응답 view에 정보를 전달할 필요가 없다.
  • 이때 사용하는 것이 HttpServletResponse 객체의 sendRedirect() 메소드이다.
response.sendRedirect("/error.html");
  • 이렇게 sendRedirect 정보를 받은 클라이언트는 서버로부터 받은 페이지 주소로 다시 요청을 시도한다.
  • ※ request 객체는 한 번 요청한 후 제거되기 때문에 sendRedirect()를 통해 request의 정보를 전달해야 하면, 쿼리스트링을 통해 데이터를 전달해야 한다.

Servlet 사진 2

728x90
반응형
728x90
반응형

[Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기

 

[Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기

[Servlet] Servlet Filter 구현 예제와 실전 적용 방법 [Servlet] Servlet Filter 구현 예제와 실전 적용 방법[Servlet] Servlet에서 Request와 Response를 가공하는 가장 좋은 방법 | Wrapper 클래스1. Servlet Filter1. Servlet Fil

crushed-taro.tistory.com

1. MVC Architecture

1. MVC Architecture 개요

1. Model - View - Controller

  • 웹 어플리케이션 개발 시 MVC 패턴을 적용하여 각각의 역할별 작업이 가능하도록 분담하는 설계 패턴을 의미한다.
  • 대부분의 웹 어플리케이션 구조는 MVC 패턴을 적용하고 있다.

Servlet 사진 1

 

2. MVC Architecture 단계별 이해하기

1. 원시적 애플리케이션 프로그램 구조

  • 시스템은 위에서 아래로 실행되므로 아래 그림처럼 최초 화면부터 로직을 거쳐 결과 화면 현출까지 구성되어 있다.
  • 비즈니스 로직에서는 데이터베이스에 접근하여 데이터를 처리한다.

Servlet 사진 2

 

  • 이처럼 모든 흐름을 하나의 소스 코드에서 관리하는 경우 아래처럼 진행되기 때문에 두 가지 문제점이 발생한다.

Servlet 사진 3

  1. 중복되는 코드 작성의 우려가 있다.
  2. 일부 수정 사항이 발생하면 모든 로직의 쿼리에 영향을 준다.

 

  • 이러한 문제들로 비즈니스 로직 재사용성 저하되므로 이를 해결하기 위하여 각각의 로직을 분리하여 코드를 작성하고 호출하도록 개선한다.

 

2. DAO

💡 DAO(Data Access Object) : 데이터베이스에 접근하기 위한 용도의 객체
⇒ CRUD 연산을 담당하는 메소드의 집합으로 이루어진 클래스
C(Create) : 삽입(insert)
R(Read) : 조회(select)
U(Update) : 수정(update)
D(Delete) : 삭제(delete)

 

  • DAO의 사용 목적은 쿼리문 단위로 기능을 분리해 재사용하기 위함이다.
    • 기능을 하나의 로직으로 통째로 묶어 사용하는 것보다 잘게 분리해 놓을수록 재사용 가능성이 높아지기 때문이다.
     

Servlet 사진 4

  • 위의 블럭 조각을 예시로 볼 때도, A 조각보다는 잘게 쪼개진 B조각이 더 많은 블럭에 끼워 맞출 수 있으므로 활용도가 높다.

 

  • 생산성과 유지보수성을 고려했을 때 작은 단위로 구분하는 편이 훨씬 - 유리하므로, 작은 단위로 구성하고 이를 모아둔 것이 DAO이다.

Servlet 사진 5

 

  • 논리적 작업은 하나의 트랜잭션으로 작동해야 하고, 하나의 트랜잭션은 반드시 하나의 Connection으로 수행해야 한다.

Servlet 사진 6

  • 따라서 Connection을 통제하는 쪽은 논리 기능을 하는 기능 수행 쪽이고, DAO는 해당 Connection을 매개변수로 빌려줘 사용하도록 하는 것이다.
  • 조회 결과를 담은 resultSet은 java.sql 패키지에 의존하고 있고, 그 의존에 의한 영향을 최소화하기 위하여 resultSet은 데이터베이스에 직접 접근하는 DAO 클래스 내에서만 사용하고 기능 수행단에서 쓰지 않는 것이 일반적이다.

 

3. Service

  • DAO를 활용해 아래처럼 구성하면 화면과 로직이 하나의 코드로 이루어진다.

Servlet 사진 7

  • 이 경우에는 문제점은 크게 세 가지로 볼 수 있다.
    1. 화면마다 로직을 작성해야 한다.
    2. 화면이나 로직 둘 중 하나만 다른 경우(같은 화면-다른 로직 or 다른 화면-같은 로직)에 매번 파일을 새로 작성해야 한다.
    3. 화면단 코드를 작성하는 프론트엔드 개발자와 기능 로직의 코드를 작성하는 백엔드 개발자가 다른 경우가 많은데, 같은 파일에 화면과 로직을 동시에 포함하면 작업 시 충돌 가능성이 있다.

⇒ 따라서 Service 클래스를 추가하여 화면과 로직을 분리한다.

Servlet 사진 8

 

  • Connection 객체의 autoCommit 속성을 통해 DML 처리 후 자동 commit 여부를 지정할 수 있는데, 보통 비즈니스 로직을 하나의 트랜잭션으로 처리할 수 있도록 false로 설정한다.
    • commit/rollback 여부는 하나의 트랜잭션을 처리하는 로직에서 처리한다.
     

Servlet 사진 9

  • Service 역할에는 비즈니스 로직에서 처리를 위하여 다음 두 가지도 포함된다.
    • connection에 대한 전반적 라이프사이클 관리
    • transaction 제어

 

  • 즉, 트랜잭션 제어를 담당하는 클래스에서 connection의 open부터 commit, rollback과 객체 반납을 처리하는데 해당 Class들의 패키지가 Service이다.

Servlet 사진 11

  • MVC 패턴에서 가장 핵심은 model이고, 그중에서도 service이다.
  • ⇒ SOA (Service Oriented Architecture)

 

4. Controller

  • Service가 추가되더라도 view에서 service를 직접 호출하는 결합(의존)이 여전히 존재한다.
    • service 이름, 내용 등이 바뀌면 view에도 영향을 준다
    • 비즈니스 로직을 직접 호출한다.
    • 사용 기기 종류 등에 따른 view가 추가될 때마다 service의 수정에 영향을 받는 파일 갯수가 늘어난다.
     

Servlet 사진 12

  • 각 view에 특성에 따라 service 내용을 수정해야 하는 경우도 발생한다. 예를 들어 web과 Android는 값 전달 방식이 다른데, 비즈니스 로직에 view마다 적용되는 로직을 작성하고 수정해야 한다.

⇒ view-service 간 의존 관계 낮추고자 controller를 추가하여 중간 다리 역할을 하도록 한다.

 

  • Controller 역할을 다음과 같다.

Servlet 사진 13

  • view에서 파라미터 값을 받아 필요에 따라 가공하여 service로 전달한다.
    • 시간 등의 정보는 view에서 받으면 사용자 조작으로 이상한 정보를 입력할 가능성이 있으므로 Controller에서 생성하기도 한다.
  • service 로직 처리 결과를 받아 view 호출 및 결과 반환한다.

 

5. MVC Architecture 완성

  • 결과적으로 프로그램 흐름 구성은 아래처럼 구성된다.

Servlet 사진 14

 

  • 흐름에 따라 처리하는 내용을 간략하게 보면 아래 그림과 같다.

Servlet 사진 15

 

  • 이 구조를 계층 구조로 나누어보면 아래와 같다.

Servlet 사진 16

💡 DTO (Data Transfer Object)
여러 계층간 데이터 전송을 위해 다양한 타입의 데이터를 하나로 묶어 전송할 용도의 클래스이다.
(VO, Bean, Entity 등은 모두 DTO를 의미한다.)

DTO 클래스의 필수 작성 조건
- 모든 필드의 접근 제한자는 private (외부에서 직접 접근 불가해야 함)
- 기본생성자와 모든 필드를 초기화하는 생성자
- 모든 필드에 대한 setter/getter
- toString Overriding을 이용한 필드 값 반환용 메소드
- 직렬화 처리 (DB와 byte 단위로 데이터 송수신하기 위함)

DTO 클래스 작성 시 참고 사항
- 컬럼 하나에 DTO 필드(변수) 하나가 대응되도록 만드는 것이 일반적이다.
- 필드 네이밍은 컬럼명을 낙타봉 표기법으로만 바꿔주면 된다.
728x90
반응형
728x90
반응형

[Servlet] Servlet Filter 구현 예제와 실전 적용 방법

 

[Servlet] Servlet Filter 구현 예제와 실전 적용 방법

[Servlet] Servlet에서 Request와 Response를 가공하는 가장 좋은 방법 | Wrapper 클래스1. Servlet Filter1. Servlet Filter 개요1. Servlet Filter란jakarta.servlet.Filter Interface를 상속 받아 구현하는 Class이다.HTTP 요청과 응

crushed-taro.tistory.com

1. Servlet Listener

1. Servlet Listener 개요

1. Servlet Listener란

  • 웹 컨테이너가 관리하는 LifeCycle 사이에 발생하는 이벤트를 감지하여, 이벤트 발생 시 그에 대한 일련의 로직을 처리하는 인터페이스이다.

 

2. Servlet Listener 동작 구조

Servlet 사진 1
Servlet 사진 2

https://docs.oracle.com/cd/B14099_19/web.1012/b14017/filters.htm

 

Servlet Filters and Event Listeners

Servlet filters are used for preprocessing Web application requests and postprocessing responses, as described in the following sections: Overview of Servlet Filters When the servlet container calls a method in a servlet on behalf of the client, the HTTP r

docs.oracle.com

  • 대상 서블릿 동작에 따른 리스너 메서드가 동작한다.

 

3. Servlet Listener 사용하는 경우

- Page scope : 하나의 Servlet, 즉 하나의 Class에서만 공유 가능하다.
- Request scope : forward에 한정해 공유할 수 있는 범위이다.
- Session scope : redirect 방식에서도 활용할 수 있는 범위로, 대표적으로 로그인 정보가 이 영역에 속한다. 사용자가 사용하는 모든 페이지에서 사용자의 정보를 가지고 있어야 하기 때문이다.
- Application scope : 브라우저별 정보보다 넓은 범위이다.

Servlet 사진 3

  • Context 변경이 발생하는 경우 (→ 톰캣 컨테이너 자체에 리스너 연결)
    • 웹 애플리케이션의 시작, 종료 시점
    • context에 attribute 추가, 제거, 수정 시점
  • Session 객체에 변경이 발생하는 경우 (→ 세션에서 발생 가능한 이벤트)
    • HttpSession의 시작, 종료 시점
    • HttpSession에 attribute 추가, 제거 , 수정 시점
  • Request 객체에 변경이 발생하는 경우 (→ request 관련 이벤트)
    • ServletRequest 생성, 소멸 시점
    • ServletRequest에 attribute 추가, 제거, 수정 시점

 

2. Servlet Listener 종류

1. Context Listener

  • ServletContextListener
    • 웹 애플리케이션의 시작과 종료 시 자동 발생하는 이벤트로, Context 생성/소멸 및 Application 생성/소멸 시점에 로직을 처리한다.
    • Method
      • contextInitialized (ServletContextEvent e) : void
      • → 웹 컨테이너가 처음 구동되어 ServletContext가 생성될 때 작동
      • contextDestoryed (ServletContextEvent e) : void
      • → 웹 컨테이너가 종료될 때, ServletContext가 소멸될 때 작동
  • ServletContextAttributeListener
    • 웹 컨테이너에 저장된 속성의 값이 변경될 경우 발생하는 이벤트로, ServletContext 객체에 속성이 추가, 삭제, 수정되는 시점에 로직을 처리한다.
    • Method
      • attributeAdded (ServletContextAttributeEvent e) : void
      • → 새로운 속성 값이 추가될 때 실행
      • attributeRemoved (ServletContextAttributeEvent e) : void
      • → 속성 값이 제거될 때 실행
      • attributeReplaced (ServletContextAttributeEvent e) : void
      • → 속성 값이 변경될 때 실행

 

2. Session Listener

  • HttpSessionListener
    • HTTP session의 생성 및 소멸 시에 작동하는 이벤트로, HttpSession 객체가 생성되거나 소멸되는 시점에 로직을 처리한다.
    • Method
      • sessionCreated (HttpSession e) : void
      • → Session 생성 시 실행
      • sessionDestoryed (HttpSession e) : void
      • → Session 무효화 될 때 실행
  • HttpSessionAttributeListener
    • Session에 대한 속성의 값이 변경될 경우 발생하는 이벤트로, HttpSession에 대한 속성 값이 변경될 경우 로직을 처리한다.
    • Method
      • attributeAdded (HttpSessionBindingEvent e) : void
      • → Session에 새로운 속성 값이 추가될 때 실행
      • attributeRemoved (HttpSessionBindingEvent e) : void
      • → Session에 속성 값이 제거될 때 실행
      • attributeReplaced (HttpSessionBindingEvent e) : void
      • → Session에 속성 값이 변경될 때 실행
  • HttpSessionBindingListener
    • 현재 Session에 객체가 추가되거나 해제될 때 발생하는 이벤트로, 사용자의 현재 Session에 바인딩 되거나 해제될 객체가 발생할 경우 로직을 수행한다.
    • Method
      • valueBound (HttpSessionBindingEvent e) : void
      • → 객체가 Session에 연결될 때 실행
      • valueUnBound (HttpSessionBindingEvnet e) : void
      • → 객체가 Session으로부터 연결이 해제될 때 실행

    • HttpSessionAttributeListener : 세션에 어떤 속성이 추가/수정/삭제되는 이벤트가 발생한 경우
    • HttpSessionBindingListener : 자신이 세션에 속성으로 추가/삭제된 경우

 

  • HttpSessionActivationListener
    • HTTP session이 활성화 또는 비활성화를 감지했을 때 작동하며, HttpSession이 새로 생성되어 활성화될 때 로직을 처리한다.
    • Method
      • sessionDidActivate (HttpSessionEvent e) : void
      • → Session 활성화 될 때
      • sessionWillPassivate (HttpSessionEvent e) : void
      • → Session 비활성화 되려고 할 때

    • session migration : session 활성화 상태에 따라 동작한다.
    • changes to id : session id 새로 발급되거나 변경되었을 때, session migration과 엮어서 사용한다.
    • session migration이 필요한 경우는 다음과 같다.
     

Servlet 사진 4

  • 사용자가 많아지면 똑같은 프로그램 탑재한 서버를 추가로 사용한다.
  • 이런 경우 클라이언트로부터 요청이 들어왔을 때 중간에서 요청을 각 서버로 분산시키는 역할을 하는 것이 L4 스위치로 로드 밸런서라고도 부른다. 이 스위치는 라운드 로빈 방식 사용한다.

Servlet 사진 5

  • 서버가 분산된 경우 세션을 공유해 사용(= 세션 클러스트링)할 수 있도록 하는데, 이때 세션이 유효한지, 활성화 상태인지 등을 확인하기 위해 이벤트 리스너를 사용한다.

 

3. Request Listener

  • ServletRequestListener
    • 클라이언트로부터 서버로 요청 시 자동 발생하는 이벤트로, request 객체가 생성되거나 소멸되는 시점에 로직을 처리한다.
    • Method
      • requestInitialized (ServletRequestEvent e) : void
      • → request 생성 시 작동
      • requestDestroyed (ServletRequestEvent e) : void
      • → request 소멸 시 작동
  • ServletRequestAttributeListener
    • request에 대한 속성의 값이 변경될 경우 발생하는 이벤트로, request객체에 대한 속성 값이 변경될 경우 로직을 처리한다.
    • Method
      • attributeAdded (ServletRequestAttributeEvent e) : void
      • → request에 새로운 속성 값이 추가될 때 실행
      • attributeRemoved (ServletRequestAttributeEvent e) : void
      • → request에 속성 값이 제거될 때 실행
      • attributeReplaced (ServletRequestAttributeEvent e) : void
      • → request에 속성 값이 변경될 때 실행

 

3. Servlet Listener 활용 예시

1. Context Listener

  • ServletContextListener와 ServletContextAttributeListener
import jakarta.servlet.ServletContextAttributeEvent;
import jakarta.servlet.ServletContextAttributeEvent;
import jakarta.servlet.ServletContextAttributeListener;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class ContextListenerTest implements ServletContextListener, ServletContextAttributeListener {

    public ContextListenerTest() {
        /* context가 생성될 때 변화를 감지하는 Listener 인스턴스 함께 생성 */
        System.out.println("context listener 인스턴스 생성");
    }

    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        /* context에 attribute가 추가될 때 동작 */
        System.out.println("context attribute added!!");
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        /* context에 attribute가 제거될 때 동작 */
        System.out.println("context attribute removed!!");
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        /* context에 attribute가 변경될 때 동작 */
        System.out.println("context attribute replaced!!");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        /* context가 최초 생성될 때 생성자 호출 이후 동작하는 메소드 */
        System.out.println("context init!!");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        /* context가 소멸할 때 동작 */
        System.out.println("context destroy!!");
    }

}

 

2. Session Listener

  • HttpSessionListener와 HttpSessionAttributeListener
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionActivationListener;
import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;

@WebListener
public class SessionListenerTest implements HttpSessionListener, HttpSessionAttributeListener {

    public SessionListenerTest() {
        /* context가 로드될 때 인스턴스 생성 */
        System.out.println("session listener 인스턴스 생성");
    }

    public void sessionCreated(HttpSessionEvent se)  {
        /* 세션이 생성될 때 동작*/
        System.out.println("session created!!");
        System.out.println("생성된 session id : " + se.getSession().getId());
    }

    public void sessionDestroyed(HttpSessionEvent se)  {
        /* 세션이 만료될 시 동작 */
        System.out.println("session destroyed!!");
    }

    public void attributeAdded(HttpSessionBindingEvent se)  {
        /* session에 attribute가 추가될 때 동작 */
        System.out.println("session attribute added!!");
        System.out.println("session에서 추가된 attr : " + se.getName() + ", " + se.getValue());
    }

    public void attributeRemoved(HttpSessionBindingEvent se)  {
        /* session에 attribute가 추가될 때 동작 */
        System.out.println("session attribute removed!!");
        System.out.println("session에서 제거된 attr : " + se.getName() + ", " + se.getValue());
    }

    public void attributeReplaced(HttpSessionBindingEvent se)  {
        /* session의 attribute가 갱신될 때 동작 */
        System.out.println("session attribute replaced!!");
        System.out.println("session에서 수정된 attr : " + se.getName() + ", " + se.getValue());
    }

}

 

  • HttpSessionBindingListener
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

public class UserDTO implements HttpSessionBindingListener {

    private String name;
    private int age;

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

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

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

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        /* 해당 클래스로 만든 인스턴스가 세션에 바인딩(값이 추가) 되는 경우 동작 */
        System.out.println("value bound!!");

    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        /* 해당 클래스로 만든 인스턴스가 세션에 바인딩 해제(값 제거 혹은 세션 만료) 되는 경우 동작 */
        System.out.println("value unbounded!!");
    }

}

 

3. Request Listener

  • ServletRequestListener와 ServletRequestAttributeListener
import jakarta.servlet.ServletRequestAttributeEvent;
import jakarta.servlet.ServletRequestAttributeListener;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class RequestListenerTest implements ServletRequestListener, ServletRequestAttributeListener {

    public RequestListenerTest() {
        /* context가 로드될 떄 생성자 호출하여 인스턴스 생성 */
        System.out.println("request listener 인스턴스 생성");
    }

    public void requestDestroyed(ServletRequestEvent sre)  {
        /* request가 소멸될 때 호출 */
        System.out.println("request destoryed!!");
    }

    public void attributeRemoved(ServletRequestAttributeEvent srae)  {
        /* request에 attribute가 제거될 때 호출 */
        System.out.println("request attribute removed!!");
    }

    public void requestInitialized(ServletRequestEvent sre)  {
        /* request가 생성될 때 호출 */
        System.out.println("request init!!");
    }

    public void attributeAdded(ServletRequestAttributeEvent srae)  {
        /* request에 attribute가 추가될 때 호출 */
        System.out.println("request attribute added!!");
    }

    public void attributeReplaced(ServletRequestAttributeEvent srae)  {
        /* request에 attribute가 갱신될 때 호출 */
        /* (참고) org.apache.catalina.ASYNC_SUPPORTED라는 attribute가 자동 수정되어 한번 호출됨
         *       서블릿 3.0에서부터 비동기 방식의 요청 처리를 지원한다는 내용 */
        System.out.println("request attribute replaced!!");
        System.out.println(srae.getName() + ", " + srae.getValue());
    }

}
728x90
반응형
728x90
반응형

[Servlet] Servlet에서 Request와 Response를 가공하는 가장 좋은 방법 | Wrapper 클래스

1. Servlet Filter

1. Servlet Filter 개요

1. Servlet Filter란

  • jakarta.servlet.Filter Interface를 상속 받아 구현하는 Class이다.
  • HTTP 요청과 응답 사이에서 전달되는 데이터를 가로채어, 서비스에 맞게 변경하고 걸러내는 필터링 작업을 수행한다.

Servlet 사진 1

  • 필터 설정에 따라 해당하는 요청 및 응답 시에 반드시 거쳐야 하며, 비밀번호 암호화 처리, 인코딩 설정 등 공통 관리에 해당하는 기능을 수행할 수 있다.
  • 필터는 인증 필터, 압축 필터, 리소스 접근 트리거 이벤트 필터, 로깅 필터, 이미지 변환 필터, 토크나이져 필터 등 다양하게 활용 가능하다.

 

2. Servlet Filter 처리 내용

  • Request에 대한 처리
    • 보안 관련 사항
    • 요청 header와 body 형식 지정
    • 요청에 대한 log 기록 유지
  • Response에 대한 처리
    • 응답 stream 압축
    • 응답 stream 내용 추가 및 수정
    • 새로운 응답 작성
    +@ : 여러 가지 필터를 연결(= chain, 서로 호출)하여 사용할 수 있다.

 

3. Servlet Filter 동작 구조

Servlet 사진 2

  • 요청 및 응답에 따른 서블릿 수행 전후에 지정한 필터를 거치며 데이터를 가공하는 역할을 한다.

 

4. Servlet Filter 내부 동작

Servlet 사진 3

  • 서블릿의 실행 전후에 동작하므로 서블릿의 service() 메소드 실행 전후에 작동한다.
  • 필터가 여러 개일 경우 stack 방식으로 순차적으로 수행된다.

 

5. Filter Chain (Interface)

  • Filter를 여러 개 사용하는 경우, Filter Chain으로 동작시킬 수 있다.

Servlet 사진 4

  • Chain처럼 서로 연결되어 있는 Filter를 doFilter() 메소드를 이용하여 순차적으로 실행시키는 인터페이스이다.
  • doFilter() 메소드는 chain으로 연결되어 있는 다음 필터 또는 서블릿을 실행하는 메소드이다.
doFilter(ServletRequest req, ServletResponse res);
  • 마지막 필터가 실행된 후에는 service() 메소드를 실행시켜 서블릿의 메소드(= doGet(), doPost())를 실행한다.

 

2. Servlet Filter 사용

1. DD 설정 (= web.xml 설정)

  • Filter를 등록한다.
<filter>
	<filter-name>Filter 설정명</filter-name>
	<filter-class>Filter 구현 Class명</filter-class>
	<init-param> // filter에서 사용한 값 설정
		<param-name>초기값 설정명</param-name>
		<param-value>초기 설정값</param-value>
	</init-param>
</filter>

 

  • url 패턴과 Filter를 mapping한다.
<filter-mapping>
	<filter-name>등록된 Filter명</filter-name>
	<url-pattern>요청할 페이지 형식</url-pattern>
</filter-mapping>

 

  • Filter를 적용할 서블릿을 지정하여, 서블릿과 Filter를 mapping한다.
<filter-mapping>
	<filter-name>등록된 Filter명</filter-name>
	<servlet-name>적용할 Servlet명</servlet-name>
</filter-mapping>

 

2. @WebFilter Annotation 설정

  • Filter 인터페이스를 구현한 클래스 상단에 @WebFilter(url-pattern) 어노테이션을 추가한다.
@WebFilter("/first/*")
public class FirstFilter implements Filter {
	필터 동작 내용
}

 

3. Filter Interface method

  • init (FilterConfig config);
    • 웹 컨테이너가 Filter를 호출하면, init method가 호출되어 Filter 객체를 생성하며 초기화한다.
    • 매개변수 FilterConfig는 web.xml에 있는 <filter>정보를 가지고 있다.
  • doFilter (ServletRequest req, ServletResponse res, FilterChain chain);
    • Filter가 수행될 때 구동하는 method로, 요청 객체와 응답 객체를 사용해 일련의 작업을 수행한 뒤, chain을 통해 가공된 값을 목적지로 전송한다.
  • doFilter (ServletRequest req, ServletResponse res, FilterChain chain);
    • Filter가 수행될 때 구동하는 method로, 요청 객체와 응답 객체를 사용해 일련의 작업을 수행한 뒤, chain을 통해 가공된 값을 목적지로 전송한다.

 

4. Filter Class 작성

public class Class명 implements Filter {
	@Override
	public void init(FilterConfig config) throws ServletException { 
		(Filter 호출 시 작업 설정)
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { 
		(Filtering 작업할 내용)
	}

	@Override
	public void destroy() { 
		(삭제 시 작업 설정)
	}
}

 

5. Filter Class 활용 예시

  • 공통적인 인코딩 설정을 할 CharsetEncodingFilter Class를 작성한다.
import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

public class EncodingFilter implements Filter {

    private String encodingType;

    public void destroy() {}

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest hrequest = (HttpServletRequest) request;
        if("POST".equals(hrequest.getMethod())) {
            request.setCharacterEncoding(encodingType);
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {
        /* xml 팡리에 init-param을 지정하여 fConfig로 값을 꺼내 사용 */
        encodingType = fConfig.getInitParameter("encoding-type");
    }

}

 

  • web.xml에 필터 및 초기 파라미터 값을 등록한다.
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>com.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>
    <url-pattern>/*</url-pattern>
</filter-mapping>
728x90
반응형
728x90
반응형

[Servlet] Servlet에서 Session과 Cookie 완벽 이해하기 | 차이점, 사용법, 예제까지 정리

 

[Servlet] Servlet에서 Session과 Cookie 완벽 이해하기 | 차이점, 사용법, 예제까지 정리

[Servlet] Servlet Forward와 Redirect의 차이점과 사용법 완벽 정리 [Servlet] Servlet Forward와 Redirect의 차이점과 사용법 완벽 정리[Servlet] Servlet 메서드와 파라미터 제대로 알기 - 초보자를 위한 웹 개발 가이

crushed-taro.tistory.com

1. Servlet Wrapper

1. Servlet Wrapper

1. Servelt Wrapper란

  • 관련 클래스(ServletRequest, ServletResponse, HttpServletRequest, HttpServletResponse)를 내부에 보관하며 해당 인터페이스를 구현한 객체를 참조하여 구현 메소드를 위임한다.
  • Java Event처리의 Adapter Class와 비슷한 기능을 한다고 볼 수 있다.
  • 사용자가 별도의 request나 response 객체를 생성하여 활용할 때 Wrapper Class를 상속하여 활용하면, 편하게 원하는 Class만 재정의하여 사용할 수 있다.

 

2. Wrapper Class

  • HttpServletRequestWrapper
    • 요청한 정보를 변경하는 Wrapper Class로, HttpServletRequest 객체를 매개로 하는 생성자를 가진다.
public SampleWrapper(HttpServletRequest wrapper) {
	super(wrapper);
}

 

  • HttpServletResponseWrapper
    • 응답할 정보를 변경하는 Wrapper Class로, HttpServletResponse 객체를 매개로 하는 생성자를 가진다.
public SampleWrapper(HttpServletResponse wrapper) {
	super(wrapper);
}

 

3. Wrapper Class 활용 예시

  • HttpServletRequestWrapper 클래스를 상속받아 비밀번호를 암호화하는 Wrapper 클래스를 작성한다.
    • 요청 받은 정보로부터 값을 꺼내올 때 암호화된 값을 꺼내오도록 request 객체 등을 자체적으로 조작하기 위하여 wrapper를 활용한다.
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class RequestWrapper extends HttpServletRequestWrapper {

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

    @Override
    public String getParameter(String key) {

        String value = "";
        if("password".equals(key)) {
            /* spring-security-crypto 라이브러리 사용 */
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            value = passwordEncoder.encode(super.getParameter(key));
        } else {
            value = super.getParameter(key);
        }

        return value;
    }

}

 

  • 위 에서 작성한 Wrapper를 적용하는 필터 클래스를 작성한다.
import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;

@WebFilter("/member/*")
public class PasswordEncryptFilter implements Filter {

    public void destroy() {}

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest hrequest = (HttpServletRequest) request;
        RequestWrapper wrapper = new RequestWrapper(hrequest);

        chain.doFilter(wrapper, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {}

}

 

  • 요청을 처리하는 서블릿에서 확인하면 암호화된 값이 현출되는 것을 확인할 수 있고, BCryptPasswordEncoder 클래스의 matches() 메소드를 통해 암호화된 값이 특정 값과 일치하는지 확인할 수 있다.
@WebServlet("/member/regist")
public class RegistMemberServlet extends HttpServlet {

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

        String password = request.getParameter("password");
        System.out.println("userPwd : " + password);

        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        System.out.println(passwordEncoder.("pass01", password));

    }

}

 

4. (참고) 암호화 및 Bcrpt

Servlet 사진

  • 위 그림처럼 중간에 패킷 정보를 빼내어 가져가는 해킹 기법인 ‘패킷스니핑’에 대비하여 개인 정보 등 보안에 민감한 데이터에는 암호화 처리가 필요하다.
    • 암호화하면 패킷스니핑으로 해킹해도 복호화하지 못하는 이상 크게 의미가 없기 때문이다.
    • 데이터베이스 해킹에 대비해 DB 저장 시에도 암호화된 데이터를 넣어야 한다.
  • 서버는 양방향 암호화 처리를 한다.
    • 암호화란 평문을 다이제스트(= 기존 문자열을 변환한 일정 길이의 문자열)로 변경하는 것이고, 복호화는 다이제스트를 다시 평문으로 변경하는 것이다.
    • 이때 암호화는 가능하지만 복호화는 불가한 것이 단방향 암호화이고, 암호화와 복호화 모두 가능한 것이 양방향 암호화이다.

저장한 서버도 관리자도 알아서는 안되는 고객의 개인 정보 등이 있을 수 있으므로 데이터베이스는 단방향 암호화 처리를 한다.

💡 BCrypt 암호화
비밀번호를 데이터베이스에 저장할 목적으로 설계된 암호화 알고리즘이다.
랜덤 솔팅 기법을 적용한 다이제스트 생성을 지연시킨 단방향 해시 암호화 알고리즘이다.
암호화는 가능하나 복호화가 불가능한 높은 수준의 암호화 알고리즘이다.
  • 해시 알고리즘이란 어떤 메시지를 넣더라도 해시 함수를 통과하면 동일한 길이의 랜덤한 문자열이 반환되는 것으로, 빠른 속도로 다이제스트 생성이 가능하다.
    1. 무차별 대입 → 모든 경우의 수에 대해 다이제스트를 생성해 맞춰보면서 패턴 탐색
    2. 사전식 대입 → 사전 속 단어를 다이제스트로 변경해 다이제스트와 비교해 패턴 탐색
  • 이러한 해시 알고리즘은 다이제스트 생성 방식이 동일하므로 아래 두 가지 방법으로 패턴만 알아내면 복호화가 가능해진다.
  • 따라서 위와 같은 패턴 탐색을 방지하고자 Bcrypt 암호화에서 적용한 salting 기법은 원문만 암호화하지 않고 지정한 다른 글자(= salt값)를 붙여 암호화하는 것이고, BCrypt는 암호화할 때마다 랜덤한 salt값을 이용한다.
728x90
반응형
728x90
반응형

[Servlet] Servlet Forward와 Redirect의 차이점과 사용법 완벽 정리

 

[Servlet] Servlet Forward와 Redirect의 차이점과 사용법 완벽 정리

[Servlet] Servlet 메서드와 파라미터 제대로 알기 - 초보자를 위한 웹 개발 가이드 [Servlet] Servlet 메서드와 파라미터 제대로 알기 - 초보자를 위한 웹 개발 가이드[Servlet] 초보자를 위한 Servlet Mapping과

crushed-taro.tistory.com

1. Session & Cookie

1. Session & Cookie 개요

1. HTTP 통신

  • HTTP 통신 방법
    • HTTP는 서버와 클라이언트 간의 요청과 응답으로 데이터를 주고 받는 형식이다.
    • 서버는 클라이언트의 요청에 응답을 완료하면 연결을 끊는다. (= stateless)
    • 따라서 클라이언트는 서버에 또 다른 요청을 하려면 새로 연결하여 응답을 받아야 한다.
  • HTTP 통신의 특징
    1. 무 연결(connectionless)
      1. 클라이언트가 서버로 요청할 때, 요청과 응답이 존재하는데 계속 연결된 상태가 아니다.
      2. HTTP는 요청할 때 잠깐, 응답할 때 잠깐만 연결하는 무 연결의 특징을 가진다.
      3. 기본적으로 HTTP는 TCP 프로토콜(= 연결지향 프로토콜로 요청 시 서버가 수락해야 송수신 가능)에 특화 기능을 추가하여 사용하는 것이다.
    2. 무 상태(stateless)
      1. 서블릿 컨테이너 내에는 여러 개의 서블릿이 있는데, 각각의 서블릿에서 상태값(속성,변수값)을 다른 서블릿에서 공유해 쓸 수 없음을 의미한다.
  • HTTP 통신의 문제점
    • 연결이 끊어지면서 유지되어야 하는 정보가 사라지는 문제가 발생한다.
    • (예시: 로그인 후의 로그인 계정 정보, 장바구니에 넣은 데이터 등)

 

2. Session과 Cookie

  • HTTP 통신의 문제점을 보완하고자 연결이 끊어진 후에도 클라이언트에 대한 정보를 유지하기 위한 두 가지 방법이 있다.
  • ⇒ 서버 측에 데이터를 보관하는 Session과 클라이언트 측에 데이터를 보관하는 Cookie이다.

Servlet 사진 1

 

2. Cookie

1. Cookie란

  • 클라이언트 측, 즉 사용자 컴퓨터에 데이터를 텍스트 파일 형태로 저장하는 기술로, 필요 시에 저장한 정보를 서버와 공유하여 정보를 유지하는 것이다.
    • 패키지 : jakarta.servlet.http.Cookie
     

Servlet 사진 2

  • 데이터는 Map형식으로 저장되고 데이터의 크기, 개수에 제한이 있다.
    • 서블릿에서 쿠키 정보를 설정해 주면, 쿠키를 클라이언트가 가지고 있으면서 다른 서블릿에 요청할 때 담아 보낸다.
  • Cookie 유지 시간, 유효 디렉토리, 유효 도메인 등을 설정할 수 있다.
  • ⇒ 즉, 호출(조회)한 데이터를 클라이언트 PC에 가지고 있도록 하는 것으로 Cookie에 대한 보관 정보는 클라이언트가 주축이다.
  • Cookie는 간단하게 이용할 수 있다는 장점이 있으나, 공용PC를 사용하거나 url에 일부 데이터를 포함하는 경우 보안에 취약하다.

 

2. Cookie 속성 설정

  • name = value
    • ASCII 문자만 사용 가능하며, 한번 설정된 쿠키의 name은 수정할 수 없다.
    • 쿠키의 이름에는 공백문자와 일부 특수문자( ( ) = , " \ ? @ : ; )를 사용할 수 없다.
  • expire = ‘날짜’
    • Cookie의 유지 시간으로, 설정이 따로 없으면 브라우저 동작 동안 유지한다.
  • path = ‘경로’
    • Cookie가 전달되는 서버의 유효 디렉토리를 지정하는 속성이다.
  • domain = ‘서버정보’
    • Cookie가 전달되는 유효 서버를 설정한다.
  • secure
    • https나 ssl보안 프로토콜로 요청할 때만 서버에 전송한다.

 

3. Cookie 설정 및 전송

  • Cookie 객체를 생성한다.
    • 이때 패키지를 import 해야 한다.
Cookie 쿠키명 = new Cookie('name','value');

 

  • 생성된 Cookie의 설정값을 지정한다.
메소드 내용
setMaxAge(int expiry) 유효 시간 설정
setPath(String uri) 경로 설정 (서버의 모든 요청이 아닌, 특정 경로를 통한 요청으로 Cookie를 사용하는 경우)
setDomain(String domain) Cookie 도메인 설정 및 Cookie생성 (도메인 외의 도메인 설정 시 사용)

 

  • 응답 헤더에 쿠키를 담는다.
response.addCookie(Cookie cookie)

 

  • 응답을 보낸다.

 

4. 전송한 Cookie 활용

  • 전달받은 Cookie 목록을 HttpServletRequest객체를 이용해 배열 형태로 읽어온다.
    • request.getCookies() : Cookie 객체 배열 형태로 반환
Cookie[] list = HttpRequest.getCookies();

 

  • Cookie의 getName()와 getValue()를 이용해 담긴 값을 호출한다.
for(Cookie c : list) {
	System.out.println(c.getName());
	System.out.println(c.getValue());
}
  • Cookie에 저장된 정보를 가지고 로그인 확인 및 팝업 설정 등을 할 수 있다.

 

5. Cookie 확인 방법

  • Chrome
    • 브라우저 확인
      • 개발자도구(F12) → Application → Cookies에서 확인
    • 저장 경로 : C:\\Users\\*user*\\AppData\\Local\\Google\\Chrome\\UserData\\Default\\Cache
  • IE (Internet Explorer)
    • 브라우저 확인
      • 개발자도구(F12) → network → 페이지 선택 → 우측 화면 Cookie
    • 저장 경로 : C:\\Users\\*user*\\AppData\\Local\\Microsoft\\Windows\\INetCache

 

3. Session

1. Session이란

  • 쿠키의 보안상 단점과 지원하지 않는 브라우저 문제 등으로 상태를 유지해야 하는 메커니즘에 적합하지 않은 경우(예: 회원 정보를 이용해서 해당 회원의 로그인 상태를 지속적으로 유지해야 하는 경우 등)가 다수 있다. 따라서 세션 인스턴스를 이용해서 상태를 유지하는 메커니즘을 제공한다.
    • 서버에 데이터를 저장하는 기술로, 클라이언트에는 Session ID를 부여한다. 클라이언트가 요청 시 Session ID를 보내면 일치하는 Session정보를 컨테이너가 생성하여 그 객체의 데이터를 사용할 수 있다.
    즉, 브라우저마다 제공하는 HashMap으로 서버에서 클라이언트에게 제공한다. Session에 값을 넣으면 모든 서블릿에서 해당 Session ID로 고유 공간을 찾아 값을 공유해 사용할 수 있다.

Servlet 사진 3

  • 패키지 : jakarta.servlet.http.HttpSession
  • 데이터를 서버에서 관리하므로 보안상 안전하고, 브라우저가 종료되면 세션도 함께 소멸한다.
  • 만일 클라이언트가 보낸 Session ID가 없으면 새로 세션 객체를 생성한다.

 

2. 데이터 상태 저장 영역

- Page scope : 하나의 Servlet, 즉 하나의 Class에서만 공유 가능하다.
- Request scope : forward에 한정해 공유할 수 있는 범위이다.
- Session scope : redirect 방식에서도 활용할 수 있는 범위로, 대표적으로 로그인 정보가 이 영역에 속한다. 사용자가 사용하는 모든 페이지에서 사용자의 정보를 가지고 있어야 하기 때문이다.
- Application scope : 브라우저별 정보보다 넓은 범위이다.

 

Servlet 사진 4

 

3. Session 생성

  • HttpSession은 직접 생성할 수 없고, request 객체의 getSession() 메소드를 이용해 반환 받을 수 있다.
HttpSession SessionID = HttpServletRequest.getSession();

 

  • SessionID가 일치하는 Session 정보가 있으면 관련 객체를 호출하고, 없으면 boolean값에 따라 객체를 생성하거나 null 값을 반환한다. (true : 객체 생성 / false : null값 반환)
HttpRequest.getSession(boolean);

 

4. Session 설정 및 호출

  • Session의 속성값을 설정한다.
SessionID.setAttribute('이름','값(Obj)'); //Session 데이터 설정
SessionID.setMaxInactiveInterval(숫자);   //Session 유지시간 설정

 

  • Session의 속성값을 꺼내온다.
HttpSession SessionID = HttpServletRequest.getSession();
SessionID.getAttribute('이름'); // 데이터 호출

 

5. Session 데이터 삭제 방법

  • 설정한 만료 시간이 지나면 세션이 자동으로 만료된다.
  • setMaxInactiveInterval(int interval) 메소드를 통해 세션에 개별적으로 적용한 만료 시간
session.setMaxInactiveInterval(60 * 10);  // 만료시간 10분 설정

 

 

  • 각 웹 애플리케이션의 WEB-INF 디렉토리 하위의 web.xml (배포서술자) 파일을 통해 지정한 만료 시간
<session-config>
	<session-timeout>60</session-timeout>
</session-config>

 

  • tomcat 설치 디렉터리 하위 conf 경로에 위치한 web.xml 파일을 통해 지정한 만료 시간
  • ⇒ 톰캣의 conf 디렉터리에는 애플리케이션과 마찬가지로 web.xml 파일을 가지고 있고, 우선순위인 setMaxInactiveInterval() 메소드나 웹 애플리케이션의 web.xml에 설정하지 않으면 이곳에 설정된 값으로 적용된다.
<session-config>
	<session-timeout>60</session-timeout>
</session-config>

 

  • removeAttribute() 메소드로 session의 attribute을 지운다.
session.removeAttribute("속성명");

 

  • invalidate() 메소드를 호출하면 세션의 모든 데이터를 제거한다.
    • 세션 자체를 무효화, 즉 강제 만료시키는 메소드이므로 실행 이후 세션을 이용하려고 하면 에러가 발생한다
session.invalidate();

 

6. Session Method

method명 내용
setAttribute(String, object) request객체에 전달하고 싶은 값을 String 이름-Object 값으로 설정
getAttribute(String) 매개변수와 동일한 객체의 속성 값 가져옴
getAttributeNames() 객체에 등록되어 있는 모든 속성의 이름만 반환
removeAttribute(String) request객체에 저장된 매개변수와 동일한 속성 값 삭제
getId() SessionID값 가져옴
getCreationTime() Session객체가 생성된 시간 반환 (msec)
getMaxInactiveInterval() client 요청이 없을 때, 서버가 현재 Session을 언제까지 유지할지 초 단위로 반환(default = 30분)
getLastAccessedTime() client 요청이 마지막으로 시도된 시간 반환 (msec)
isNew() 새로 생성된 Session이면 true, 아니면 false 반환
invalidate() 현재 Session 삭제
setMaxInactiveInterval(int) 객체 유지 시간을 설정하고, 지정 시간 지나면 객체 자동 삭제

 

728x90
반응형
728x90
반응형

[Servlet] Servlet 메서드와 파라미터 제대로 알기 - 초보자를 위한 웹 개발 가이드

 

[Servlet] Servlet 메서드와 파라미터 제대로 알기 - 초보자를 위한 웹 개발 가이드

[Servlet] 초보자를 위한 Servlet Mapping과 생명주기, 초기 설정법 총정리 [Servlet] 초보자를 위한 Servlet Mapping과 생명주기, 초기 설정법 총정리[Servlet] Servlet 기초 완벽 가이드 | 동작 방식과 DD(web.xml) 설

crushed-taro.tistory.com

1. Forward & Redirect

1. forward()

1. forward() 역할

  • 컨테이너 내에서 처음 요청 받은 페이지가 요청 데이터 (HttpServletRequest, HttpServletResponse)를 다른 페이지에 전송하여 처리를 요청하고, 자신이 처리한 것처럼 응답한다.
  • 클라이언트가 요청한 url주소(페이지)가 변경되지 않는다.

Servlet 사진

 

2. forward() 구조

Servlet 사진 2

  1. 서버(= Tomcat)를 실행하면 내부 서블릿 컨테이너에 작성한 서블릿이 다 올라가고 doGet, doPost 등 매핑된 url로 연결된다.
  2. HTTP에 의한 요청을 전달하면 헤더의 문자열을 파싱해서 헤더, 데이터, 응답 대상 브라우저 등을 request와 response 객체로 쪼개어 doGet() 또는 doPost() 메소드로 보낸다.
  3. 이때 요청 받은 서블릿에서 다른 서블릿으로 request, response 객체를 담아 forward하면 동일한 속성을 가지고 처리 권한을 위임한다.
  4. 서버 내부에서 다른 서블릿에 위임했으나 요청받은 서블릿이 응답하는 것처럼 처리하므로 위임한 경로를 노출하지 않는다. 즉, 처리하는 서블릿이 변경되었어도 url이 변경되지 않는다.

 

3. forward() 활용 예시

  • 요청받은 서블릿에서 forward하는 코드 예시
request.setAttribute("userId", userId);

RequestDispatcher rd = request.getRequestDispatcher("print");
rd.forward(request, response);
  • 다른 서블릿으로 요청하기 위한 데이터는 request에 setAttribute()로 담아 전달한다.
  • attribute도 일종의 Map 형식으로, key-value 방식으로 값을 저장할 수 있다.
  • RequestDispatcher는 ‘배차관리자’라는 뜻으로, 서블릿 위임 시 어디로 보낼지 결정하는 역할을 한다.

 

  • 위임(forward)받은 서블릿에서 속성 값을 꺼내는 코드 예시
String userId = (String) request.getAttribute("userId");
  • forward 받은 서블릿에서도 요청 방식이 get이면 doGet메소드를, 요청 방식이 post이면 doPost메소드를 호출한다.
  • request에 전달 정보를 담았으므로, 위임받은 서블릿에서 사용하기 위해 request에서 getAttribute()로 담아 전달한다.
  • forward할 때 전달한 request와 response의 모든 정보를 이용해 새로운 request, response를 만들고 그 정보를 이용해 다시 http 메소드에 맞는 서블릿의 doGet 혹은 doPost를 요청하는 방식이다.
  • → 깊은 복사를 이용해 값을 그대로 복사했기 때문에 내부에 존재하는 헤더 정보나 인스턴스는 그대로 유지한다.

 

2. redirect()

1. redirect() 역할

  • 클라이언트 브라우저에게 **“(매개변수로 등록한) 페이지를 재요청하라”**고 응답한다.
  • (응답 상태 코드 : 301, 302)
  • encodeRedirectURL은 매개변수(URL)에 Session ID 정보를 추가하여 재요청 처리한다.
  • 클라이언트가 별도로 다른 페이지 요청을 하지 않아도 url주소(페이지)가 변경된다.
    • (브라우저 요청에 따라 서버가 알아서 해당 페이지를 요청하며, 쿼리스트링으로 별도의 데이터를 전송하지 않으면 요청 데이터가 없다.)
     

 

2. redirect() 구조

Servlet 사진 4

  1. 서버(= Tomcat)를 실행하면 내부 서블릿 컨테이너에 작성한 서블릿이 다 올라가고 doGet, doPost 등 매핑된 url로 연결된다.
  2. HTTP에 의한 요청을 전달하면 헤더의 문자열을 파싱해서 헤더, 데이터, 응답 대상 브라우저 등을 request와 response 객체로 쪼개어 doGet() 또는 doPost() 메소드로 보낸다.
  3. 이때 요청 받은 서블릿에서 재요청할 url을 담아 sendRedirect()로 응답하면 처리하는 서블릿으로 브라우저가 재요청을 보내도록 한다.
    • 302 status code를 보냄으로써 요청 url을 바꿔 다시 요청하라는 의미를 전달한다.
      • 사용자 url 재작성이라고 불리는 redirect 방식은 302번 응답 코드인 경우 요청에 대한 처리를 완료하였고, 사용자의 url을 강제로 redirect 경로로 이동시키라는 의미이다.

 

3. redirect() 활용 예시

  • 요청받은 서블릿에서 sendRedirect하는 코드 예시
    1. 타 사이트로 이동하는 경우
response.sendRedirect("http://www.naver.com");
  • 브라우저의 개발자도구 network 탭을 보면 302번 코드와 함께 naver 사이트로 이동하는 것을 확인할 수 있다.
  • 응답 헤더 작성은 General Header의 302번 코드와 Response header의 Location 헤더값에 redirect할 경로를 포함하여 응답한다.

 

  • 타 서블릿으로 이동하는 경우 (타 서블릿의 url pattern 작성)
response.sendRedirect("redirect");
  • redirect하면 url이 재작성되어 새로고침할 때 redirect된 페이지에 대한 요청을 반복한다.
  • 즉, 이전 요청에 포함된 정보는 남아있지 않고 url이 변경되는 것이 redirect의 특징이다.
  • HTTP 요청은 요청 시 connection을 맺었다 끊고, 응답 시에도 connection을 맺었다 끊으므로 요청 단위당 request객체는 한 개만 생성된다.
  • 따라서 첫 요청 시의 request와 redirect된 페이지의 request는 서로 다른 객체이므로, redirect를 쓰면 이전 서블릿의 request 객체 속성 값을 공유해서 사용할 수 없다. (이를 해결하기 위해 쿠키 및 세션 객체를 활용한다.)

 

3. forward()와 redirect() 비교

1. 구조 비교

Servlet 사진 5

  • forward는 서버 내부에서 요청을 동일한 파라미터 객체로 위임하여 응답하고, 마치 요청받은 서블릿이 응답한 것처럼 보여준다.
  • redirect는 요청한 서블릿이 302 status로 응답을 보내주고, 브라우저가 응답 받은 방향으로 다시 요청을 보내 결과 페이지를 반환 받는 구조이다.

 

2. CRUD 로직에서의 활용

Servlet 사진 6

  • 새로고침을 반복할 때마다 동일한 요청이 반복되는 forward는 대게 조회 기능에 사용한다.
    • select 처리한 조회 값 데이터가 많으면 한번에 많은 값을 전달할 수 있다.
    • 새로고침하면 재조회해서 추가 또는 삭제된 데이터를 반영해서 조회할 수 있다.
  • 새로고침을 하면 재요청된 페이지에 대한 요청이 반복되는 redirect는 주로 삽입, 수정, 삭제 기능에 사용한다.
    • 데이터 insert, update, delete 로직을 수행할 때 해당 기능이 중복 수행되지 않도록 하는 것이 바람직하다.
  • 이처럼 forward와 redirect는 특징을 알고 특징에 맞게 사용해야 하고, 정해진 방식이 있는 것은 아니다.
    • 예를 들어 로그인 기능은 select 기능이지만, redirect 방식을 활용해 로그인 횟수 제한 등을 설정하기도 한다.

 

4. 객체별 공유 데이터 설정

  • 공유 데이터는 Map 형식의 key-value 방식으로 저장된다.
  • 이러한 공유 데이터를 포함한 ServletContext, ServletRequest, HttpSession 객체에서 사용하는 method는 다음 표와 같다.
method 내용
setAttribute(String,Object) 공유 데이터 저장
getAttribute(String) 공유 데이터 가져옴
getAttributeName() 공유 데이터 전체의 명칭 가져옴
removeAttribute(String) 공유 데이터 자체를 삭제

 

728x90
반응형
728x90
반응형

[Servlet] 초보자를 위한 Servlet Mapping과 생명주기, 초기 설정법 총정리

 

[Servlet] 초보자를 위한 Servlet Mapping과 생명주기, 초기 설정법 총정리

[Servlet] Servlet 기초 완벽 가이드 | 동작 방식과 DD(web.xml) 설정법 [Servlet] Servlet 기초 완벽 가이드 | 동작 방식과 DD(web.xml) 설정법[Servlet] 초보 개발자를 위한 Servlet 이해하기 | 웹 서버와 클라이언트

crushed-taro.tistory.com

1. Servlet Method

1. HTTP 데이터 전송 방식

1. 데이터 전송 구조

Servlet 메서드 사진

  • 브라우저에서 요청 정보를 HTTP 객체에 담아 전송한다.
    • 요청과 응답을 주고받는 패킷은 요청 정보와 응답 정보를 직렬화한 Byte단위의 문자열 데이터로, 인코딩과 디코딩이 필요하다.
  • 전달받은 HTTP객체를 서버(=Tomcat)이 해석하여 요청을 처리할 서블릿을 호출한다. 서블릿의 service() 메소드에서는 request, response 요청 정보를 가지고 처리 로직을 거쳐 응답한다.
    • HttpServletRequest는 ServletRequest 타입을 상속 받아 구현하였으며, HTTP 프로토콜의 정보를 담고 있기 때문에 실제 사용 시에는 HttpServletRequest 타입으로 다운캐스팅해서 사용해야 한다.

 

2. 데이터 전송 방식

  • GET 방식
    • URL 주소란에 ?뒤에 데이터를 입력하는 방법, 즉 쿼리스트링(query string) 방식으로 전송한다.
    • 전송 데이터가 여러 개이면 &로 묶어서 보낸다.
      • URL 쿼리스트링 예시
      • <http://localhost:8080/sendMessage?message=abc&code=20>
    • 데이터 검색에 주로 사용되며, 데이터 크기 한계가 있고 보안에 취약하다.
  • POST 방식
    • HTTP header의 내용으로 데이터를 전송한다.
    • 전송 데이터 크기에 제한이 없고, header에 포함해 전송하므로 보안이 뛰어나다.

 

2. 데이터 전송 방식에 따른 Servlet Method

1. Servlet Method

  • 서블릿이 get/post의 두 방식 중 하나로 요청 정보를 전달 받으면, request와 response를 전달하면서 해당하는 처리 메소드(doGet() 메소드 또는 doPost() 메소드)를 호출한다.⇒ 이때, 서블릿 메소드에 대하여 반드시 ServletException 처리를 해야 한다.
  • → 즉, 톰캣 서블릿 컨테이너가 요청 url로 매핑된 서블릿 클래스의 인스턴스를 생성하여 service() method를 호출하고 HttpServlet을 상속받아 오버라이딩한 현재 클래스의 doGet() 또는 doPost() 메소드가 동적바인딩에 의해 호출된다.
  • HTML에서 method 속성을 이용해 방식을 결정하며, default는 get 방식이다.

 

2. doGet() 메서드

  • 클라이언트에서 데이터 전송 방식을 get으로 전송하면 호출되는 메소드이다.
  • GET 방식의 데이터는 HTML charset에 기술된 인코딩 방식으로 브라우저가 한글을 이해하고, ‘%’ 문자로 URLEncoder를 이용해 변환 후 url 요청으로 전송한다.
  • 이때 header의 내용은 ascii 코드로 전송되므로 어떤 언어든 서버의 설정 인코딩 방식과 맞기만 하면 해석하는데 문제가 없으므로 한글이 깨지지 않는다.
  • GET 요청은 보통 서버의 리소스를 가져오는 행위를 요청하는 http 요청 방식이기에 별도의 데이터가 필요 없어 요청 본문(= 페이로드)는 해석하지 않는다.
  • 쿼리스트링으로 보내는 데이터가 노출되므로 보안에 취약하다는 단점이 있으나, 속도면에서 더 빠르므로 검색 기능에 적합하다.

 

3. doPost() 메서드

  • 클라이언트에서 데이터 전송 방식을 post로 전송하면 호출되는 메소드이다.
  • POST 요청은 서버의 리소스에 내용을 추가하는 요청이기 때문에 요청하는 데이터가 필요한 경우가 대부분이다. 서버의 리소스에 추가해야 하는 정보를 페이로드에 key&value 방식으로 담아 전송하는데, 헤더와는 별개로 URLEncoder를 이용하지 않고 페이지 meta에 기술된 charset에 따라 UTF-8로 해석된 데이터를 서버로 전송한다.
  • 기본적으로 서버단에서 페이로드를 디코딩 하는 방식은 지정되어 있지 않고, 인코딩된 방식을 명시하지 않으면 기본 ISO-8859-1로 해석하므로 값을 꺼내오면 한글인 글자가 깨지는 현상이 발생한다.
    • request 객체의 getCharacterEncoding() 메소드를 호출해보면 null을 반환한다.
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	System.out.println(request.getCharacterEncoding());

}
  • 따라서 parameter를 꺼내오기 전에 이를 해석할 인코딩 방식을 UTF-8로 지정해야 브라우저에서 요청한 인코딩 방식과 일치하여 한글 깨짐 현상을 막을 수 있다.
  • request의 body 영역에 데이터를 담아보내므로 데이터를 많이 보낼 수 있으나 비교적 속도가 느리다. 또한 개발자 도구를 통해 담은 데이터를 볼 수 있으므로 보안이 엄청 뛰어난 수준은 아니다.

 

2. Servlet Method Parameter

  • HttpServletRequest : 요청 객체
    • 요청 정보 접근
    • 요청 파라미터 처리
    • 헤더 정보 접근
    • 요청 바디 읽기
    • 세션 관리
  • HttpServletResponse : 응답 객체
    • 응답 상태 코드 설정
    • 응답 헤더 설정
    • 응답 바디 작성
    • 리다이렉션 처리
    • 컨텐츠 타입 설정

 

1. HttpServletRequest

1. HttpServletRequest의 역할

  • HTTP Servlet을 위한 요청 정보(request information)를 제공하는 메소드를 지정한다.
  • Interface 구현은 컨테이너가 알아서 설정하므로 메소드만 이용한다.
  • (상속 받는 인터페이스 : jakarta.servlet.ServletRequest (interface))
  • HttpServletRequest 객체의 주요 메소드
method명 내용
getParameter(String) client가 전송한 값의 명칭이 매개변수와 같은 값 가져옴
getParameterNames() client가 전송한 값의 명칭 가져옴
getParameterValues(String) client가 전송한 값이 여러 개이면 배열로 가져옴
getParameterMap() client가 전송한 값 전체를 Map방식으로 가져옴
setAttribute(String, object) request 객체로 전달할 값을 String 이름-Object 값으로 설정
getAttribute(String) 매개변수와 동일한 객체 속성 값 가져옴
removeAttribute(String) request객체에 저장된 매개변수와 동일한 속성 값 삭제
setCharacterEncoding(String) 전송 받은 request객체 값들의 CharaterSet 설정
getRequestDispatcher(String) 컨테이너 내에서 request, response객체를 전송하여 처리한 컴포넌트(jsp파일 등)를 가져옴 (forward() method와 같이 사용)

 

2. Request Header

  • General header
    • 요청 및 응답 모두에 적용되지만 최종적으로는 body에 전송되는 것과 관련이 없는 헤더이다.
  • Request header
    • Fetch될 리소스나 클라이언트 자체에 대한 상세 정보를 포함하는 헤더이다.
  • Response header
    • 위치나 서버 자체와 같은 응답에 대한 부가적인 정보를 갖는 헤더이다.
  • Entity header
    • 컨텐츠 길이나 MIME타입과 같은 엔티티 body에 대한 상세 정보를 포함하는 헤더이다.
    • (요청 응답 모두 사용되며, 메시지 바디의 컨텐츠를 나타내기에 GET요청은 해당하지 않는다.)
  • getHeader() 메소드로 확인 가능한 값
header 속성
accept 요청을 보낼 때 서버에게 요청할 응답 타입
accept-encoding 응답 시 원하는 인코딩 방식
accept-language 웅답 시 원하는 언어
connection HTTP 통신이 완료된 후에 네트워크 접속을 유지할지 결정 (기본값: keep-alive = 연결을 열린 상태로 유지)
host 서버의 도메인 네임과 서버가 현재 Listening 중인 TCP포트 지정 (반드시 하나가 존재. 없거나 둘 이상이면 404)
referer 이 페이지 이전에 대한 주소
sec-fetch-dest 요청 대상
sec-fetch-mode 요청 모드
sec-fetch-site 출처(origin)와 요청된 resource 사이의 관계
sec-fetch-user 사용자가 시작한 요청일 때만 보내짐 (항상 ?1 값 가짐)
cache-control 캐시 설정
upgrade-insecure-requests HTTP 메시지 전송 시 보안 적용
user-agent
현재 사용자가 어떤 클라이언트(OS, browser 포함)을 이용해 보낸 요청인지 명시

 

2. HttpServletResponse

1. 요청에 대한 Servlet의 역할

  1. 요청을 받는다.
    1. : HTTP method GET/POST 요청에 따라 parameter로 전달받은 데이터를 꺼내어 활용할 수 있다.
  2. 비지니스 로직을 처리(DB접속과 CRUD에 대한 로직 처리)한다.
    1. : 서비스 메소드를 호출하여 처리 로직을 수행하도록 한다.
  3. 응답한다.
    1. : 문자열로 동적인 웹(HTML 태그)페이지를 만들고 스트림을 이용해 내보내거나 응답 데이터를 담아 결과 페이지를 호출한다.

 

2. HttpServletResponse의 역할

  • 요청에 대한 처리 결과를 작성하기 위해 사용하는 객체이다.
  • Interface 구현은 컨테이너가 알아서 설정하므로 메소드만 이용한다.
    • (상속 받는 인터페이스 : jakarta.servlet.ServletResponse (interface))
  • HttpServletResponse 객체의 주요 메소드
method 명 내용
setContentType(String) 응답으로 작성하는 페이지의 MIME type을 설정
setCharacterEncoding(String) 응답하는 데이터의 CharacterSet을 지정
getWriter() 페이지에 문자 전송을 위한 Stream을 가져옴
getOutputStream() 페이지에 byte단위의 전송을 위한 Stream을 가져옴
sendRedirect(String) client가 매개변수의 페이지를 다시 서버에 요청함
💡 MIME 타입(Multipurpose Internet Mail Extensions Type)은 클라이언트(브라우저)에게 서버가 전송하는 데이터의 형식을 알려주는 표준화된 방식이다. MIME 타입은 type/subtype와 같은 형식으로 표기하며, 아래와 같이 수백 가지의 MIME 타입이 존재한다.
  • MIME 타입 목록
    • 텍스트 형식:
      • text/html: HTML 문서
      • text/plain: 일반 텍스트
      • text/css: CSS 스타일시트
      • text/csv: CSV 형식의 데이터
    • 이미지 형식:
      • image/jpeg: JPEG 이미지
      • image/png: PNG 이미지
      • image/gif: GIF 이미지
      • image/svg+xml: SVG 벡터 이미지
    • 애플리케이션 형식:
      • application/json: JSON 데이터
      • application/xml: XML 데이터
      • application/pdf: PDF 문서
      • application/zip: ZIP 압축 파일
      • application/octet-stream: 바이너리 데이터(주로 파일 다운로드에 사용)
    • 오디오/비디오 형식:
      • audio/mpeg: MP3 오디오 파일
      • audio/ogg: OGG 오디오 파일
      • video/mp4: MP4 비디오 파일
      • video/webm: WebM 비디오 파일
    • 폰트 형식:
      • font/ttf: TrueType 폰트
      • font/woff: Web Open Font Format
      • font/woff2: Web Open Font Format 2

 

3. Exception Handler

  • sendError()메소드를 사용하여 에러를 발생시킬 수 있다.
// response.sendError(에러상태 코드, "현출할 에러 메시지");
response.sendError(500, "서버 내부 오류입니다.");
  • web.xml 파일에 <error-page>를 등록하여 에러 상태코드에 해당하는 에러가 발생하면 서블릿에서 가로채도록 설정할 수 있다.
<error-page>
    <error-code>404</error-code>
    <location>/showErrorPage</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/showErrorPage</location>
</error-page>
  • 위 설정은 아래 그림처럼 원래라면 일반적인 오류 페이지가 반환될 시점에 오류 송출을 가로채 지정된 페이지로 사용자에게 반환하는 구조이다.

Servlet Method 사진

 

4. 스트림을 활용한 동적 페이지 응답

  • 응답 헤더의 타입을 설정한다.
response.setContentType("text/html");
  • 기본값은 text/plain이다.
  • HTML태그를 사용하면 요청 시 text/html도 응답으로 수락 가능하도록 헤더 설정이 되어있어 자동으로 text/html로 인식한다.

 

  • 응답할 내용의 인코딩 방식을 설정한다.
response.setCharacterEncoding("UTF-8");
  • 응답 시 별도 인코딩을 지정하지 않으면 기본으로 설정된 인코딩 방식(ISO-8859-1)을 따르므로 한글 데이터가 깨져서 현출된다.
  • 따라서 응답할 인코딩 방식이 UTF-8임을 응답 헤더에 설정하면 브라우저가 이를 해석할 때 UTF-8로 인식하고 해석하여 한글 데이터가 정상적으로 현출된다.
  • 응답 헤더와 인코딩 설정을 동시에 할 수도 있다.
response.setContentType("text/html; charset=UTF-8");

 

  • 응답을 위한 스트림을 가져와 응답을 내보낸다.
PrintWriter out = response.getWriter();
out.print(responseBuilder.toString());

 

  • 응답에 사용한 스트림을 닫는다.
out.close();
728x90
반응형

+ Recent posts