2020-12-02 TIL

6 minute read

html과 서블릿

  • html 폼에 바뀌지 않는다면 html로 만드는 것이 속도 면에선는 더 빠르다
    • html이 웹 서버를 통해 바로 정보를 읽어오는 것이 서블릿을 실행하는 것보다 빠르기 때문이다.
  • 하지만 만약 html 폼이 바뀐다면 서블릿으로 만드는 것이 낫다.

get, post

  • 무엇이 get이고 무엇이 post인지 알아야 한다.
  • 주소창이 변화하는 것, 링크, 검색 등은 get

redirect

  • httpServlet과 관련있는 기술
  • 여기의 루트는 브라우저가 읽어야 하는 루트이기 때문에 서버 루트로 시작한다.

  • 그러므로 앞에 정확하게 이름을 명시해줘야 한다.
  • 컨텍스트 루트 = /bitcamp-java-project-server
ServletContext servletContext = request.getServletContext();
String contextRootPath = servletContext.getContextPath();
// httpResponse.sendRedirect("/bitcamp-java-project-server/auth/form.html");
httpResponse.sendRedirect(contextRootPath + "/auth/form.html");

서블릿 초기화 파라미터

애노테이션으로 지정

  • 서블릿 컨테이너 —호출—> init(ServletConfig config) —호출—> init() <사용
// 서블릿이 사용할 값을 DD 설정으로 지정할 수 있다.
//
//@WebServlet(//
//    value = "/ex06/s3",
//    loadOnStartup = 1, <- 클라이언트가 요청하지 않아도 서블릿 객체를 자동으로 만든다.
//    initParams = {
//        @WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver"),
//        @WebInitParam(name = "jdbc.url", value = "jdbc:mariadb://localhost/studydb"),
//        @WebInitParam(name = "jdbc.username", value = "study"),
//        @WebInitParam(name = "jdbc.password", value = "1111")})
//@SuppressWarnings("serial")
public class Servlet03 extends HttpServlet {

  // @Override
  // public void init(ServletConfig config) throws ServletException {
  // // 서블릿 객체가 생성될 때 뭔가 준비하는 작업을 해야 한다면,
  // // 보통 이 메서드를 오버라이딩 할 것이다.
  // //
  // // 문제는 이 메서드가 호출될 때 넘어오는 값(config)은 나중에 사용할 수 있도록
  // // 인스턴스 필드에 보관해 둔다.
  // // 즉, getServletConfig()가 호출될 때 리턴하도록 코드를 작성한다.
  // this.config = config;

  // // 그런데 init()를 오버라이딩 할 때 마다 이렇게
  // // config 객체를 인스턴스 필드에 저장하도록 코딩하는 것은 매우 번거롭다.
  // //
  // // 이런 불편함을 없애기 위해서 GenericServlet은
  // // 미리 이 메서드에 해당 코드를 작성해 두었다.
  // // 그리고 추가적으로 파라미터 값을 받지 않는 init()를 호출하도록
  // // 구현하였다.
  // //
  // // 결론?
  // // => 그러니 개발자는 서블릿 객체가 생성될 때 뭔가 작업을 수행하고 싶다면,
  // // 이 메서드를 직접 오버라이딩 하지 말고,
  // // 이 메서드가 호출하는 다른 init()를 오버라이딩 하라!
  // //
  // //
  // super.init(config);
  // }


  @Override
  public void init() throws ServletException {
    System.out.println("/ex06/s3 ==> init()");
    // 이 객체가 생성될 때 DB에 연결한다고 가정하자!
    // DB에 연결하려면 JDBC Driver 이름과 JDBC URL, 사용자 아이디, 암호를
    // 알아야 한다.
    //
    // 그런데 다음과 같이 자바 소스 코드에 그 값을 직접 작성하면,
    // 나중에 DB 연결 정보가 바뀌었을 때
    // 이 소스를 변경하고 다시 컴파일 해야 하는 번거로움이 있다.
    // => 소스에 변할 수 있는 값을 작성하는 것은 바람직하지 않다.
    // => 보통 이렇게 값을 직접 작성하는 것을 "하드(hard) 코딩"이라 부른다.
    // String jdbcDriver = "org.mariadb.jdbc.Driver";
    // String jdbcUrl = "jdbc:mariadb://localhost:3306/studydb";
    // String username = "study";
    // String password = "1111";

    // 위의 코드처럼 언제든 변경될 수 있는 값을
    // 소스코드에 직접 작성하는 방식은 좋지 않다.
    // 해결책?
    // => 이런 변경 값들은 외부에 두는 것이 관리에 편하다.
    // => 값이 바뀌더라도 소스를 변경할 필요가 없다.
    // => 보통 DD 파일(web.xml)에 둔다.
    // => 다만 이 예제에서는 애노테이션으로도 설정할 수 있다는 것을
    // 보여주기 위해 서블릿 상단에 애노테이션으로 설정하였다.
    // => 예)
    // @WebInitParam(name = "jdbc.driver", value = "org.mariadb.jdbc.Driver")
    // => 애노테이션에 설정된 값을 꺼내기 위해서는 ServletConfig 객체가 필요하다.
    //
    ServletConfig config = this.getServletConfig();

    // 이렇게 @WebInitParam()으로 설정된 값을
    // "서블릿 초기화 파라미터"라 부른다.
    String jdbcDriver = config.getInitParameter("jdbc.driver");
    String jdbcUrl = config.getInitParameter("jdbc.url");
    String username = config.getInitParameter("jdbc.username");
    String password = config.getInitParameter("jdbc.password");


    System.out.println(jdbcDriver);
    System.out.println(jdbcUrl);
    System.out.println(username);
    System.out.println(password);

  }
}
  • 값이 변경될 가능성이 있는 데이터를 바로 소스 코드에 넣지 않는다. = 하드코딩
    • 값이 변경되면 컴파일을 다시 해야하고 배포도 다시 해야하기 때문에 불편하다.
    • 변경될 값들은 외부에 둔다.

web.xml에 지정

  • 서블릿 초기화 파라미터는 서블릿을 등록할 때 같이 넣는다.
  • 컨텍스트 초기화 파라미터 값을 꺼내는 방법은 다양한다.
    • 리턴 받은 값은 전부 다 같은 것이다.
    • 웹 애플리케이션 당 서블릿 컨텍스트는 딱 하나 뿐이다.
ServletContext sc = this.getServletContext();
ServletContext sc2 = req.getServletContext();
ServletContext sc3 = this.getServletConfig().getServletContext();

forward, include

image-20201203131508806

forward

image-20201203131546786

  • 웹 브라우저 —요청—> 서블릿 컨테이너 —service()—> S1 —service()(S1에서 버퍼에 출력한 데이터는 버린다)—> S2 ——> (멈추지 않고 바로 서블릿 컨테이너로 이동) 서블릿 컨테이너 —응답—> 웹 브라우저
  • 활용 : 작업을 위임할 때

include

image-20201203131603763

  • 웹 브라우저 —요청—> 서블릿 컨테이너 —service()—> S1 —service()(S1이 버퍼에 출력한 데이터에 이어서 S2가 출력한다)—> S2 ——> (잠시 멈췄다가 서블릿 컨테이너로 이동) 서블릿 컨테이너 —응답—> 웹 브라우저

forward vs include

image-20201203131637767

Refresh

image-20201203131708776

  • 브라우저의 응답을 출력한다.
public class Servlet01 extends HttpServlet {

  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 테스트 방법:
    // => http://localhost:8080/java-web/ex08/s1
    //
    // 리프래시
    // => 서버로부터 응답을 받고 "내용을 출력"한 후
    // 지정된 시간이 경과되면 특정 URL을 자동으로 요청하도록 만들 수 있다.
    // => 보통 웹 페이지를 자동으로 이동시키고 싶을 때 사용한다.
    // => 예
    // 예1: 로그인 후 메인페이지로 자동 이동
    // 예2: 메일을 전송한 후 메일 목록 페이지로 자동 이동
    // 예3: 게시글 등록한 후 게시글 목록으로 자동 이동
    // 예4: 결제 완료 후 결제 상태 페이지로 자동 이동
    //
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("안녕하세요! - /ex08/s1");

    // 응답 헤더에 Refresh 정보를 추가한다.
    //
    // 위에서 벌써 클라이언트에게 응답을 했는데 어떻게 응답 헤더를 출력할 수 있나요?
    // => 잊지 말자! out.println()이 출력한 것은 출력스트림 버퍼에 보관되어 있다.
    // 따라서 아직 클라이언트에게 응답한 상태가 아니다.
    // 그래서 다음과 같이 출력을 한 후에 응답 헤더 값을 추가하거나 변경할 수 있는 것이다.
    // 메서드 호출이 완료될 때 비로소 클라이언트로 응답헤더와
    // 버퍼에 저장된 message-body가 출력된다.
    //
    // 만약 out.println()/out.printf()/out.print() 등에서 출력한 내용이
    // 버퍼를 꽉 채웠다면 어떻게 하나요?
    // => 그러면 자동으로 클라이언트에게 응답한다.
    // 따라서 일단 클라이언트에게 응답을 하면
    // 헤더를 추가하거나 변경하는 코드는 적용되지 않는다.
    // 즉 응답을 완료한 후에 헤더 값을 변경하거나 바꿀 수 없기 때문이다.
    // 소용이 없다.
    //

    // 다음은 일부러 버퍼를 채우는 코드이다.
    // 버퍼가 꽉차면 자동으로 출력하는 것을 확인해보자!
    //    for (int i = 0; i < 200; i++) {
    //      // 약 40 바이트씩 100번 출력하면 아직 버퍼에 차지 않았기 때문에
    //      // 클라이언트로 출력되지 않는다.
    //      // 따라서 반복문 아래에 있는 응답 헤더 설정이 유효하다.
    //      // 그러나 200번 출력하면 헤더 값과 이전에 출력한 값,
    //      // 그리고 반복문에서 출력한 데이터가 8KB 버퍼를 꽉 채우기 때문에
    //      // 즉시 클라이언트로 응답한다.
    //      // 즉 반복문 다음에 헤더를 설정하기 전에 이미 버퍼 내용이 출력된다.
    //      // 응답이 완료된 후에 응답 헤더의 값을 변경하거나 추가해봐야 소용없다.
    //      //
    //      out.println(i + " ===> 1234567890123456789012345678901234567890");
    //    }

    response.setHeader("Refresh", "3;url=s100");

    // 이 service() 메서드의 호출이 끝나면
    // 비로서 응답 프로토콜에 맞춰
    // 클라이언트로 헤더와 내용을 전송한다.
  }
}


// HTTP 응답 프로토콜 예:
//
// HTTP/1.1 200
// Refresh: 3;url=s100 <--- 웹 브라우저는 이 헤더의 정보에 따라 다시 요청한다.
// Content-Type: text/plain;charset=UTF-8
// Content-Length: 28
// Date: Tue, 07 Apr 2020 06:46:25 GMT
// Keep-Alive: timeout=20
// Connection: keep-alive
//
// 안녕하세요! - /ex08/s1
  • 만약 버퍼가 가득 차도록 메세지 바디를 설정한다면 그 뒤에 헤더 값을 넣어도 소용이 없다.

Redirect

image-20201203131722437

  • 브라우저의 응답을 출력하지 않는다. = 메세지 바디가 없다.
  • 그러므로 브라우저에서 출력하지 않고 바로 다음 서블릿에게 요청을 실행한다.

forward/include, refresh/redirect

image-20201203131749194

보관소

image-20201203131827860

  • ServletContext : 웹 어플리케이션 시작(서버 시작) 시 생성, 웹 어플리케이션 종료(서버 종료) 시 삭제
    • 웹 어플리케이션 하나 당 하나
    • 서비스 객체, Dao 객체 <— 서블릿, 필터 등이 공유하는 객체
  • HttpSession : 세션 생성(로그인) 시 생성, 세션 종료(로그아웃) 시 삭제
    • 로그인 정보 <—특정 클라이언트 전용 객체 저장
    • 한 컴퓨터에 여러 httpSession이 가능하다.
    • 브라우저(클라이언트) 당 httpSession을 하나
      • 크롬 창을 여러개 생성해도 하나의 브라우저로 인식한다.
        • 시크릿 창은 다른 브라우저로 인식
      • 크롬, 사파리, 파이어폭스 브라우저를 띄우면 각각의 클라이언트로 취급하기 때문에 httpSession이 세개 생성된다.
  • ServletRequest : 요청이 들어왔을 때 생성, 응답 완료 시 삭제
    • 요청을 처리하는 동안 공유할 객체
    • 리프레시, 리다이렉트는 요청, 응답 후 다시 요청하기 때문에 ServletRequest 객체를 공유할 수 없다.
  • ServeltContext, HttpSession, ServletRequest는 보관소의 역할을 하기 때문에 set, getAttribute가 존재한다.

Categories:

Updated: