Java - 18. Servlet Programming

갓김치·2020년 10월 20일
0

고급자바

목록 보기
41/47

참고

Servlet

  • JSP표준에 앞서 자바에서 웹 어플리케이션 개발을 위해 만들어진 표준
  • 컨테이너(서블릿 엔진)에 의해서 관리되는 자바기반 웹 컴포넌트(=재사용 가능)로서, 동적인 웹컨텐츠 생성을 가능하게 해준다.
    • Servlet Engine: 서블릿 돌려주는 고마운 프로그램
      • Servlet Container, Web Application Server)
      • Apache Tomcat

Servlet 특징

  • html을 사용하여 요청에 응답
  • Java Thread를 이용하여 동작
  • MVC 패턴에서 Controller로 이용됨
  • HTTP프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속받음
  • UDP보다 처리속도가 느림
  • HTML 변경 시 Servlet을 재컴파일해야하는 단점이 있음

Servlet Container 역할

  • 웹 서버와의 통신 지원
    • 서블릿과 웹서버가 손쉽게 통신할 수 있도록 도움
      • ex: 소켓 만들고 listen, accept 해야하나 서블릿 컨테이너는 이런 기능을 API로 제공해 복잡한 과정을 생략하게 해줌
  • 서블릿 생명주기 관리
    • 서블릿 클래스를 로딩하여 인스턴스화
    • 초기화 메소드호출
    • 요청이 들어오면 적절한 서블릿 메서드 호출
    • 생명이 다 하면 가비지 컬렉션 진행
  • 멀티스레드 지원 및 관리
    • 원래는 스레드를 관리해야 하지만 HTTP 서비스 메서드를 실행하고 나면 스레드가 자동으로 죽기때문에 스레드 안전성에 대한 걱정을 덜 수 있음
  • 선언적인 보안 관리
    • 자바 소스코드를 수정하여 재컴파일 하지않아도 보안관리용 XML 배포 서술자에서 관리할 수 있음

Servlet 개발 과정

  • 1) 서블릿 규약에 따라 자바 코드를 작성
  • 2) 자바 코드를 컴파일 하여 클래스 파일 생성
  • 3) 클래스 파일을 /WEB-INF/classes 디렉토리에 패키지 구조에 따라 저장
  • 4) web.xml 파일에 서블릿 클래스 등록 및 리퀘스트와의 매핑
    • 서블릿3.0 규약부터 @WebServlet 어노테이션으로 대체
  • 5) 톰캣등의 컨테이너 재 실행(서블릿 리로딩 기능이 있는 경우 생략)
  • 6) 웹 브라우저에서 요청 처리 결과 확인

Servlet 동작 방식

  • 1) 사용자(클라이언트)가 URL을 클릭하면 HTTP Request를 Servlet Container로 전송(요청)한다.
  • 2) 컨테이너는 web.xml에 정의된 url패턴을 확인하여 어느 서블릿을 통해 처리해야할지를 검색한다
    • 로딩이 안 된 경우에는 로딩함. 로딩시, init() 호출됨
  • 3) Servlet Container는 요청을 처리할 개별 스레드 객체를 생성하여 해당 서블릿 객체의 service()메서드를 호출한다.
    • 이때 HttpServletRequestHttpServletResponse 객체를 생성하여 파라미터로 넘겨준다.
  • 4) service() 메서드는 메서드 타입을 체크하여 적절한 메서드를 호출한다.
    • doGet, doPost, doPut, doDelete...
  • 5) 요청 처리가 완료되면, HttpServletRequest 및 HttpServletResponse객체는 소멸 된다.
  • 6) 컨테이너로부터 서블릿이 제거되는 경우에 destroy()메서드가 호출

Dynamic Web Project

프로젝트만들기



  • Context: 컨텍스트라는 개념의 객체를 이용해 웹 어플리케이션의 정보를 관리
    • ex: 환경설정

폴더 설명

  • Java Resource : 자바 소스
  • build: 컴파일 후 소스(이클립스 내에서)
  • WebContent
    • META-INF
    • WEB-INF
      • 웹관련 설정파일: jar나 web.xml (서블릿3.0에서 필수는 아니지만 없는 경우는 거의 없음) 아래사친처럼하면 web.xml생김
      • lib 폴더 (무조건 소문자): 여기에 넣는 jar파일은 tomcat이 나중에 알아서 build path잡고 읽는다
      • classes : 컴파일 후 소스(배포시 build-classes에서 war로 압축되어서 여기로온다) 그 모양 그대로 tomcat에 갔다가 압축 풀면 땡?

예제1 서블릿 라이프사이클

T01_ServletLifeCycle

  • ★ HttpServlet 상속받아야함

메서드

  • init()
    • 서블릿을 통해 객체가 만들어지면 init이 한번 호출
    • 객체 초기화설정 처럼 기본적으로 1번만 할 작업들
      • ex. jdbc의 connection
  • service()
    • init() 다음 실행
    • 실제적인 작업 수행이 시작되는 지점
    • 자바의 메인 메서드 역할
    • service는 잘 안쓰고 do로 시작하는 메서드에 주로 코딩
  • doGet(req, resp), doPost(req, resp)...
    • req = HttpServletRequest resp = HttpServletResponse 컨테이너가 넣어주는것
    • 사용자 요청방식에 따라 do메서드가 달라짐
  • destroy()
    • 객체 소멸시 (= 컨테이너로부터 서블릿 객체 제거시) 작동될 코드
    • 코드 변경(파일 수정 후 저장)되면 작동
    • 마지막에 한 번만 해주면 될 작업들 작성
      • ex. 자원반납

콘솔

web.xml

  • servlet + servlet-mapping 을 통해 사용자가 url-pattern에 설정된 url을 요청하면 매핑된 서블릿이 구동되도록 설정한다
  • load-on-startup : 우선순위 설정
    • 1로 해줬더니, 사용자 요청 이전에 init()실행(객체생성됨)
      • = 객체가 먼저 만들어져서 서블릿 컨테이너에 미리 로드됐단 소리

서버에 프로젝트 add

브라우저에서 실행

  • localhost:9090/P16_ServletTest/
    • P16_ServletTest 까지는 그냥 애플리케이션 구분하는 context root
    • 이다음 슬래시부터 진짜주소다 = 실제url경로부터 루트시작
  • localhost:9090/P16_ServletTest/T01_ServletLifeCycle
    • 이걸 통해 매핑된 서블릿이 작동

예제2 - 요청받아 응답 결과물 전송

리다이렉트포워드
resp.sendRedirect("index.html");RequestDispatcher dispatcher = req.getRequestDispatcher("index.html");
dispatcher.forward(req, resp);
너 여기로 요청하는게 아니고 index.html 여기에 요청해야지!- 요청 받은 서블릿은 response안하고 바로 index.html한테 할일 넘겨버림
- 처리된 다음에 그 화면을 보여주는 것
URL변경: OURL변경: X
객체 재사용: X객체 재사용: O
시스템(session, DB)에 변화가 생기는 요청(로그인, 회원가입, 글쓰기)시스템에 변화가 생기지 않는 단순 조회(리스트보기, 검색)
예시:
- 사용자 : 나 이것좀 암호화 처리해줘
- 서블렛: 오호; 나 할줄모르는데;; 고대로 암호화처리하는 서블릿한테 넘겨야겠당

T02_ServletTest

public class T02_ServletTest extends HTTPServlet{

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    // 1. Post 방식으로 넘어오는 Body 데이터 인코딩 처리
    //    (get방식은 톰캣의 URIEncoding 설정을 이용함.)
    //    request 객체에서 값을 가져오기 전(getParameter)에 먼저 설정해야 적용됨 ★
    req.setCharacterEncoding("utf-8");

    // 2. 요청정보로부터 name값을 가져오기
    String name = req.getParameter("name");

    System.out.println("name => " + name);

    // 3. 응답메시지 인코딩 설정
    //    (Content-Type의 charset=UTF-8과 동일)
    resp.setCharacterEncoding("UTF-8");

    // 4. 응답메시지의 Content-Type 설정
    resp.setContentType("text/plain");

    // 5. 실제 수행할 로직(기능)이 시작되는 부분.
    Printwriter out = resp.getWriter();

    out.println("name => " + name);
    out.println("서블릿 경로 => " + req.getServletPath());
    out.println("컨텍스트 경로 => " + req.getContextPath());
    
  //out.flush();
    // 방출과 동시에 상대방에게 response하게 되기 때문에 연결 끊어짐 (=>http는 비연결)
    // java.lang.IllegalStateException:
    // Cannot forward after response has been committed
    
    // 6. 리다이렉트 - URL바뀜, 결과를 아예 다른 걸로 셋팅해서 응답
    resp.sendRedirect("index.html"); //T02_Servlet을 요청했는데 요기로 납치
    
    // 7. 포워드
  //RequestDispatcher dispatcher = req.getRequestDispatcher("index.html");
  //dispatcher.forward(req, resp); // 객체 재사용
  }

  @Override
  protected void doPost(HttpServletRequest res, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp); // doPost로 오던 doGet으로 오던 같은 로직 실행
  }
}

결과

리다이렉트

포워드

예제 3 - 요청 객체로부터 파라미터 데이터 가져오기

  • getParameter(String param) : 파라미터 value값이 한개인 경우
  • getParameterValues(String param) : 파라미터의 value 값이 여러개 (ex: 체크박스)
  • getParameterNames(): request 객체에 존재하는 모든 파라미터정보(이름)을 가져올 때 사용

T03_ServletParameterTest

public class T03_ServletParameterTest extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    // 1. 객체에서 꺼내기 전 인코딩 설정
    req.setCharacterEncoding("utf-8");

    // 2. 요청 객체로부터 파라미터의 데이터 가져오기
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    String gender = req.getParameter("gender");
    String hobby = req.getParameter("hobby"); // getParameterValues()로 해줘야 체크한 것 전부 날라옴
    String[] food = req.getParameterValues("food");

    // 3. 응답 헤더를 위한 인코딩
    resp.setContentType("text/html;charset=utf-8");

    // 4. 응답해더 출력
    PrintWriter pw = resp.getWriter();

    pw.println("<html>");
    pw.println("<body>");
    pw.println("<p>username : " + username + "</p>");
    pw.println("<p>password : " + password + "</p>");
    pw.println("<p>gender : " + gender + "</p>");
    pw.println("<p>hobby : " + hobby + "</p>");
    pw.println("<p>rlgn : " + rlgn + "</p>");

    if (food != null) {
      for (String s : food) {
        pw.println("<p>food : " + s + "</p>");
      }
    }

    Enumeration<String> params = req.getParameterNames();
    while(params.hasMoreElements()) {
      String param = params.nextElement();
      pw.println("<p>파라미터 이름 : " + param + "</p>");
    }

    pw.println("</body>");
    pw.println("</html>");

  } // doGet()

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // POST로 받아도 GET과 같은 방식으로 처리하고 싶으면 GET으로 보내면 됨
    doGet(req, resp);
  }
} // class
profile
갈 길이 멀다

0개의 댓글