Servlet과 Apache Tomcat (23.06.28)

·2023년 6월 28일
0

Server

목록 보기
1/35
post-thumbnail

📝 Servlet이란?

  • 웹 서비스를 위한 자바 클래스 (자바를 이용하여 웹을 만들기 위해 필요한 기술)
  • 웹 프로그래밍에서 클라이언트의 요청(Request)을 처리하고 그 결과를 다시 클라이언트에게 응답(Response)하는 Servlet 클래스의 구현 규칙을 지킨 자바 프로그래밍 기술
  • 🔎 예시
    사용자가 로그인을 하려고 할 때 아이디와 비밀번호를 입력하고 로그인 버튼을 누르면 서버는 아이디와 비밀번호를 확인하고 다음 페이지를 띄워주는 역할 수행
    -> 그래서 서블릿은 자바로 구현된 CGI라고 함

💡 CGI(Common Gateway Interface)란?

특별한 라이브러리나 도구를 의미하는 것이 아닌 별도로 제작된 웹 서버와 프로그램 간의 교환 방식

  • CGI방식은 어떠한 프로그래밍 언어로도 구현이 가능함
  • 별도로 만들어 놓은 프로그램에 HTML의 Get 또는 Post 방법으로 클라이언트의 데이터를 환경변수로 전달하고, 프로그램의 표준 출력 결과를 클라이언트에게 전송하는 것

즉, Servlet은 자바 어플리케이션 코딩을 하듯 웹 브라우저용 출력 화면(HTML) 을 만드는 방법이다!


📌 Servlet 특징

  • 클라이언트의 요청에 대해 동적으로 작동하는 웹 애플리케이션 컴포넌트
    -> 클라이언트 요청에 대한 서버 응답 시 미리 만들어 둔 화면(정적)이 아닌, 요청을 받을 때마다 알맞은 화면을 만들어(동적) 응답함

  • HTML을 사용하여 요청에 응답함

  • java thread를 이용하여 동작함 (요청마다 별도 thread가 생성됨)

  • MVC Model2 패턴에서 Controller로 이용함

  • 서블릿 코드를 작성할 클래스는 반드시 javax.servlet.http.HttpServlet 클래스를 상속받아 메소드를 구현해야 함
    -> doGet() / doPost()를 필요한 형태로 오버라이딩 진행

🔎 Servlet 상속 관계도

javax.servlet.Servlet 인터페이스
javax.servlet.GenericServlet 추상클래스
javax.servlet.http.HttpServlet 클래스

🔎 메소드별 오버라이딩

웹 클라이언트의 요청 방식응답 방식
GET 방식doGet() 메소드
POST 방식doPost() 메소드

반드시 doGet()메소드와 doPost()메소드는 오버라이딩 해 주어야 한다.

  • 첫번째 파라미터는 HttpServletRequest 타입
  • 두번째 파라미터는 HttpServletResponse 타입


📌 Servlet 동작 방식

  1. 사용자(클라이언트)가 URL(Uniform Resource Locator)을 클릭하면 HTTP Request(요청)를 Servlet Container로 전송

  2. Http Request를 전송 받은 Servlet Container는 아래 두 객체를 생성
    HttpServletRequest(클라이언트 + 요청 정보가 담긴 객체)
    HttpServletResponse(서버가 클라이언트한테 응답하는 방법을 제공하는 객체)

  3. DD (배포서술자, Deployment Descriptor) = web.xml은 사용자가 요청한 URL을 분석하여 어떤 서블릿 클래스에 요청 내용을 전달할지 찾음

  4. 해당 서블릿에서 init() 메소드를 먼저 호출한 후 service() 메소드를 호출하여
    클라이언트로부터 전송 받은 방식인 GET, POST 여부에 따라 해당 메소드(doXXX())를 호출

  5. doGet() / doPost() 메소드는 동적 페이지를 생성HttpServletResponse 객체에 응답을 보냄

  6. 응답 종료 시 HttpServletRequest, HttpServletResponse 객체 소멸


📝 Servlet Container

  • Servlet : 어떤 역할을 수행하는 정의서
  • Servlet Container : 정의된 Servlet을 보고 수행
    -> 클라이언트의 요청을 받을 때마다 새로운 자바 스레드(Thread)를 만들어 요청을 처리하고 응답을 해 줌
  • 배포를 위한 포트 연결, 웹 서버 통신을 위한 소켓, 입/출력 스트림을 생성하는 역할을 함

  • Java로 웹 애플리케이션을 만들기 위해 JSP(Java Server Page)나 서블릿(Servlet)을 사용해 서버와 통신하기 위해서는 서버가 구축이 되어 있어야 함

🔎 참고

아파치(Apache)

  • 아파치 소프트웨어 재단의 오픈 소스 프로젝트
  • 클라이언트 요청에 따라 고정적인(정적) 페이지를 제공(응답)하는 웹 서버
    -> HTML

톰캣(Tomcat)

  • 동적 웹(Dynamic Web)을 만들기 위한 웹 컨테이너(== 서블릿 컨테이너)
  • 정적 페이지를 제외한 요청(Servlet, JSP)에 대한 수신, 응답을 하는 웹 응용 서버
    (WAS : Web Application Server)
  • 톰캣 사용 시 동적 데이터 처리가 가능하므로 DB연결 및 데이터 조작, 다른 응용 프로그램과의 상호 작용이 가능
    -> Servlet

📌 아파치 톰캣 (Apache Tomcat)

톰캣이 처리할 수 없는 정적 페이지 처리를 위하여 아파치의 일부 기능을 가져와 함께 제공하는 서버

  • 다운로드 링크
    http://tomcat.apache.org/
    -> 왼쪽 탭 Download -> Tomcat 9 -> 9.0.xx -> Core -> OS에 맞는 tomcat 다운로드

📌 그외 Servlet 관련 개념

💡 form 태그

내부에 작성된 input 태그의 값을 지정된 서버 또는 페이지로 제출(전달)하는 역할

-> 이때, 어떤 input 태그에 작성된 값이 제출된 건지 알 수 있도록 input태그에 name 속성을 반드시 작성한다!

💡 form 태그 속성

  • action
    input 태그 값을 전달할 서버 또는 페이지 주소를 작성하는 속성

  • method
    데이터 전달 방식 지정 속성(get / post)

💡 웹 서버 주소의 의미

http://localhost:8080 /ServletProject1/

- http://아이피:포트번호 : 통신 규약, 서버 컴퓨터 위치를 지정(고정)

- /ServletProject1/ : 요청하고자 하는 내용을 나타내는 주소

💡 GET 방식

요청하는 주소 뒤에 전달하려는 값(input 태그에 작성된 값)을 문자열 형태로 붙여서 서버로 전달하는 방식

  • form태그, a태그, 주소창 직접 입력하는 모든 것이 GET방식
  • form태그, method 속성 기본값

🔎 장점

단순함, 캐싱 가능(요청 저장 가능 == 북마크, 즐겨찾기)

🔎 단점

보안에 취약함(눈에 다 보임)

🔎 doGet()

GET 방식 요청을 처리하는 메소드

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}

💡 파라미터(Parameter)

요청 시 전달된 input 태그의 값

🔎 req.getParameter("input 태그 name 속성값")

요청 시 전달된 input 태그 값(== Parameter)을 얻어오는 방법

🔎 getParameterValues()

같은 name이 여러 개면 String[]로 반환

🔎 resp.getWriter()

서버가 클라이언트에게 응답할 수 있는 출력 전용 스트림을 얻어옴
Write : 서버가 클라이언트에게 쓰다 == 출력

🔎 resp.setContentType("text/html; charset=UTF-8")

스트림을 통해서 그냥 문자열을 내보내면 정상 출력되지 않는 문제가 발생한다.
따라서 전달되는 응답 데이터가 어떤 형식인지, 문자 인코딩은 어떤 건지를 지정해 줘야 한다.


📌 Dynamic Web Project(동적 웹 프로젝트)

요청에 따라서 응답되는 화면(HTML)을 실시간으로 만들어내서(동적) 클라이언트에게 응답하는 프로젝트


🔎 Servlet 동작 방식 코드로 살펴보기

VS Code

  • index.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>index라는 이름이 붙은 파일은 메인 페이지 역할을 합니다.</h1>
    
    
    <hr>

    <h3>테스트 1</h3>

    <pre>
        form 태그 : 
            내부에 작성된 input 태그의 값을 지정된 서버 또는 페이지로 
            제출(전달)하는 역할

        * 이때, 어떤 input 태그에 작성된 값이 제출된 건지 알 수 있도록
        input태그에 name 속성을 반드시 작성한다!!

        * form 태그 속성
        - action : input 태그 값을 전달할 서버 또는 페이지 주소를 작성하는 속성

        - method : 데이터 전달 방식 지정 속성(get / post)

    </pre>

    <!-- http://localhost:8080              /ServletProject1/
        
        - http://아이피:포트번호 : 통신 규약, 서버 컴퓨터 위치를 지정(고정)

        - /ServletProject1/ : 요청하고자 하는 내용을 나타내는 주소
    
    -->

    <form action="/ServletProject1/example1.do">
        <!-- ServletProject1 프로젝트에 example.do 라는 요청을 전달한다 
                -> 예제 1번을 수행해서 결과를 응답할 예정
        -->

        이름 : <input type="text" name="inputName"><br>
        나이 : <input type="text" name="inputAge"><br>

        <button type="submit">서버로 제출하기</button>
    </form>

    <hr>

    <h3>GET방식 요청 및 응답</h3>
    
    <pre>
        GET방식 : 요청하는 주소 뒤에
                  전달하려는 값(input 태그에 작성된 값)을
                  문자열 형태로 붙여서 서버로 전달하는 방식

        - 장점 : 단순함, 캐싱 가능(요청 저장 가능 == 북마크, 즐겨찾기)
        - 단점 : 보안에 취약함(눈에 다 보임)

        * form태그, a태그, 주소창 직접 입력하는 모든 것이 GET방식
        * form태그, method 속성 기본값
    </pre>

    <form action="/ServletProject1/example2.do" method="get">

        주문자 : <input type="text" name="orderer"><br>
        <label>
            <input type="checkbox" name="coffee" value="아이스 아메리카노">
            아이스 아메리카노
        </label>
        <br>
        <label>
            <input type="checkbox" name="coffee" value="카라멜 마끼야또">
            카라멜 마끼야또
        </label>
        <br>
        <label>
            <input type="checkbox" name="coffee" value="녹차 라떼">
            녹차 라떼
        </label>
        <br>
        <label>
            <input type="checkbox" name="coffee" value="초코칩 프라페">
            초코칩 프라페
        </label>
        <br>
        <label>
            <input type="checkbox" name="coffee" value="민트초코 프라페">
            민트초코 프라페
        </label>
        <br>
       
        <button>주문서 제출하기</button>

    </form>


</body>
</html>

Eclipse

  • web.xml
<?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>ServletProject1</display-name>
  
  <!-- 메인 페이지 목록 -->
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
  
  <!-- ServletEx1 클래스 Servlet으로 등록(자격 부여) -->
  <servlet>
     <servlet-name>example1</servlet-name>
     
     <!-- Servlet으로 등록하고자 하는 클래스 파일의 패키지명 + 클래스명 -->
     <servlet-class>edu.kh.servlet.ServletEx1</servlet-class>
  
  </servlet>
  
  <!-- 요청 주소를 처리할 Servlet과 연결을 지정 -->
  <servlet-mapping>
    <servlet-name>example1</servlet-name>
    <url-pattern>/example1.do</url-pattern>
  </servlet-mapping>
  
  <!-- ============================================================ -->
  
  <!-- 클래스르 Servlet으로 등록하고 Servlet 이름 지정 -->
  <servlet>
  	<servlet-name>example2</servlet-name>
  	<servlet-class>edu.kh.servlet.ServletEx2</servlet-class>
  </servlet>
  
  <!-- 특정 요청을 처리할 Servlet을 지정 -->
  <servlet-mapping>
  	<servlet-name>example2</servlet-name>
  	<url-pattern>/example2.do</url-pattern>
  </servlet-mapping>
 
</web-app>
  • ServletEx1.java
package edu.kh.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Servlet 클래스를 만들기 위해서는 
// javax.servlet.http.HttpServlet 추상 클래스를
// 반드시 상속 받아야 한다!
// -> doGet() / doPost()를 필요한 형태로 오버라이딩 진행

public class ServletEx1 extends HttpServlet{
   // /ServletProject1/example1.do 주소로 요청이 왔을 때
   // 해당 클래스가 응답할 수 있도록 연결하는 작업이 필요함
   
   // -> web.xml 파일에서 연결 작업 수행

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
      // HttpServletRequest : 클라이언트 + 요청 정보가 담긴 객체
      // HttpServletResponse : 서버가 클라이언트 한테 응답하는 방법을 제공하는 객체
      
      // 요청 시 전달된 input 태그 값(== Parameter)을 얻어오는 방법
      // req.getParameter("input 태그 name 속성값")
      
      String inputName = req.getParameter("inputName");
      String inputAge = req.getParameter("inputAge");
      
      System.out.println(inputName);
      System.out.println(inputAge);
   
   }
   
}
  • ServletEx2.java
package edu.kh.servlet;

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

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Servlet 클래스를 만들 때는 
// 반드시 HttpServlet을 상속받아야 한다.
public class ServletEx2 extends HttpServlet{

	// Get 방식 요청을 처리하는(do) 메소드
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		// 파라미터(Parameter) == 요청 시 전달된 input 태그의 값
		
		String orderer = req.getParameter("orderer");
		
		// -> getParameter()는 전달된 iput태그의 name이 하나일 때만 가능
		
		// -> 같은 name이 여러 개면 String[]로 반환하는
		// 	  getParameterValues()를 사용
		
		String[] coffee = req.getParameterValues("coffee");
		// 체크박스에 체크된 메뉴들이 모두 배열에 담김
		// --> 체크가 안 되면 배열에 하나도 담기지 않음
		
		if(coffee != null) { // 체크된 메뉴가 있는지 검사
			
			// 향상된 for문
			for(String c : coffee) {
				System.out.println( c );
			}
		}

		System.out.println("주문자 : " + orderer);
		
		// HttpServletRequest : 클라이언트 정보 + 전달된 값
		// HttpSErvletResponse : 서버가 클라이언트에게 응답할 방법을 제공
		
		// Write : 서버가 클라이언트에게 쓰다 == 출력
		// resp.getWriter() : 서버가 클라이언트에게 응답할 수 있는
		//					  출력 전용 스트림을 얻어옴
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		// ** 스트림을 통해서 그냥 문자열을 내보내면 정상 출력되지 않는 문제 발생 **
		// 왜? 전달되는 응답 데이터가
		// 	   어떤 형식인지, 문자 인코딩은 어떤 건지를 지정해 주지 않아서
		
		// ***********************************************************
		/* Dynamic Web Project(동적 웹 프로젝트)
		 * 
		 * - 요청에 따라서 응답되는 화면(HTML)을 실시간으로 만들어내서(동적)
		 * 	 클라이언트에게 응답하는 프로젝트
		 * */
		// ***********************************************************
		
		// HTML 코드를 자바(Servlet)에서 작성하여
		// 클라이언트와 연결된 응답 출력용 스트림(out)을 이용해 출력
		
		out.println("<!DOCTYPE html>");
		out.println("<html>");
		
		out.println("<head>");
		out.println("<title>" + orderer + " 님의 주문 목록</title>");
		out.println("</head>");
		
		out.println("<body>");
		
		out.println("<ul>");
		
		if(coffee != null) {
			for(String c :coffee) {
				
				out.println("<li>"+ c +"</li>");
			}
		}
		
		out.println("</ul>");
		
		out.println("</body>");
		
		out.println("</html>");
		
		
		
	}
}

🔎 출력 화면

ServletEx1 출력 화면

  • 웹 화면

  • Eclipse 내부 콘솔창

-> System.out.println(inputName)과 System.out.println(inputAge)로 인해 콘솔에 이름과 나이가 출력되는 모습을 볼 수 있다.

ServletEx2 출력 화면

  • 웹 화면

  • '주문서 제출하기' 버튼을 클릭했을 때

-> 주문자 이름이 title에, 주문한 메뉴 목록이 body에 보여지는 모습이다.

  • Eclipse 내부 콘솔창

profile
풀스택 개발자 기록집 📁

0개의 댓글