서블릿의 일생
1. 웹 컨테이너가 서블릿 클래스 로딩
2. 서블릿 인스턴스화(생성자 실행)
3. init() 메서드 호출(일생에 한번 호출 됨)
4. service() 메서드를 호출하여 스레드 생성 또는 스레드 풀로부터 하나를 가져 옴(요청마다 호출 됨)
5. doGet(), doPost() 호출
5. destroy()(일생에 한번 호출 됨)
* 서블릿 클래스 로딩은 대부분 톰캣이 startup 될 때 이루어진다. 최초 클라이언트 접근시 로딩하는 컨테이너도 가끔 있다.
* init() 메서드를 재정의 해서 DB 접속, 다른 객체에 서블릿을 등록하는 일등을 할 수 있다.
* init() 메서드에서 ServletConfig와 ServletContext 객체에 접근이 가능하다.
* 컨테이너는 서블릿 하나에 대한 다수의 요청을 처리하기 위하여 다수의 스레드를 실행한다.(다수의 인스턴스를 생성하는 것이 아님.)
ServletConfig 객체, ServletContext 객체
1. ServletConfig
- 서블릿 당 ServelConfig 객체 하나
- 서블릿 배포 시 설정된 정보를 서블릿으로 넘겨주기 위함.(서블릿 안에 하드코딩하길 원지 않는 정보들. 예를들면, DB나 EJB 참조 이름 같은 것 등)
- ServletContext에 접근하기 위해서 이 객체를 사용
- 파라미터값은 배포 서술자(DD)에서 설정 가능.
2. ServletContext
- 웹 애플리케이션 당 하나의 ServletContext 존재(이름을 AppContext 라고 하는게 더 나을뻔 함)
- 웹 애플리케이션의 파라미터 정보를 읽어오기 위하여 사용.(파라미터 정보는 DD안에 설정되어 있음)
- 이것은 일종의 애플리케이션용 게시판같은 것임. 여기에 메시지(Attribute)를 적어놓으면 다른 애플리케이션의 다른 녀석들이 이를 읽을 수 있음.
- 서버 정보를 파악하기 위해 사용(컨테이너 이름 및 버전, 지원하는 API 버전 등)
* HTTPServeltRequest, HttpServletResponse는 인터페이스인데 누가 이것을 구현해서 서블릿에 넘겨 주는것임?
: 컨테이너가 이를 구현해서 서블릿에 넘겨주는 것이다. 서블릿은 해당 객체를 인터페이스로 받아서 사용할 뿐, 해당 객체가 어떤 객체인지는 몰라도 된다.
HTTP 메소드
- GET: URL 자원 또는 파일을 달라고 요청함
- POST: Request에 첨부한 몸체 정보를 서버로 보내서 요청한 URL로 이 정보를 넘겨주라고 요청함.
- HEAD: GET이 무엇을 리턴하든 간에 헤더 정보만 요청함. 이는 Response의 몸체 정보가 없다는 것만 빼면 GET과 동일함. 요청한 URL로부터 요청한 정보는 빼고 헤더 정보만 가져오는 것임.
- TRACE: 요청한 메시지의 루프백(loopback) 테스트를 요청함. 서버쪽에서 무엇을 받았는지를 알고 싶을 때 하는 테스트. 테스트 목적 또는 문제 해결을 위해 사용함.
- PUT: 동봉한 몸체 정보를 요청한 URL:로 올리기 위해 사용함.
- DELETE: 요청한 URL에 있는 자원이나 파일을삭제하기 위해 사용함.
- OPTIONS: 요청한 URL이 응답할 수 있는 HTTP 메소드가 무엇인지 요청함.
- CONNECT: 터널링의 목적으로 연결을 요청함.
GET과 POST의 차이점
1. 보안
- 노출하고 싶지 않은 정보를 포함할 때 POST 사용.
2. 데이터의 크기
- GET은 URL뒤 Query String으로 데이터를 전달해야 되기 때문에 데이터 크기에 한계가 있다.
3. 즐겨찾기
- GET 요청을 즐겨찾기에 추가하면 GET 요청 그대로 즐겨찾기에 추가되기 때문에 검색 페이지 같은 경우 검색 조건도 그대로 즐겨찾기에 추가된다. POST는 그렇지 않음.
4. 사용되는 용도
- GET 요청은 서버에 있는 자원을 단순히 가져오기 위한 요청인데 반해, POST는 서버에 데이터를 수정하면서 자원을 가져오기 위한 요청이다.
5. 멱등(Idempotent)
- 멱등이란, 동일한 연산이 여러번 적용되더라도 결과가 달라지지 않는 것을 말한다.
- GET 요청은 단순히 서버의 자원을 요청하는 것이기 때문에, 서버 자원의 상태가 달라지지 않으므로 멱등 요청이라고 할 수 있다. 그에반해 POST는 서버 데이터를 변경하면서 서버의 자원을 요청하기 때문에 같은 POST 요청이 여러번들어가면 서버 데이터가 계속 바뀌기 때문에 멱등 요청이라고 할 수 없다.
그렇기 때문에, 결제같은 POST 요청을 수행할 때 서버로직을 잘 구성해야 한다. 예를들어, 한 사용자가 결제 버튼을 연타로 계속 눌렀다고 가정할 경우 서버 POST 로직에서 요청이 여러번이더라도 한 트랜잭션으로 간주해서 적절히 처리하지 않으면 결제가 여러번 되는 불상사가 생길 수 있다.
HttpServletReqeust에서 얻을 수 있는 정보
- 파라미터 정보
- 클라이언트 플랫폼 정보 및 브라우저 정보
: String client = request.getHeader("User-Agent");
- Request에 관련된 쿠키
: Cookie[] cookies = request.getCookies();
- 클라이언트의 세션 정보
: HttpSession session = request.getSession();
- Request의 HTTP 메소드
: String theMethod = request.getMethod();
- Request의 입력 스트림
: InputStream input = request.getInputStream();
- 그 밖에 많은 정보들
: 실제 프로젝트를 해보면 Request API중 15% 정도밖에 사용하지 않는다.
* Reqeust에 있는 입력 스트림은 언제 사용함?
: GET 메소드는 헤더정보 밖에 없기 때문에 사용할 일이 없지만, POST에 경우 몸체에 단순 파라미터 정보가 아닌 문장이나 바이러니 같은 큰 데이터의 경우도 있기 때문에 이를 다루기 위해서는 getReader나 getInputStream 메소드를 사용해야 한다. 물론 이들 스트림에는 Request 헤더는 빼고 몸체만 들어있다.
두가지 출력 방식: 문자와 바이트
: 이 주제는 java.io의 기본적인 내용이다. ServletResponse는 두 가지 스트림을 제공한다.
1) PrintWriter
- 텍스트를 출력할 때 사용. OuputStream도 문자를 쓸 수 있지만 PrintWriter가 바로 문자를 사용하기 위해 만들어진 녀석이다.
2) ServletOutputStream
- 무엇이든 여기에 써도 무방하다.
* PrintWriter는 사실 ServletOutputStream을 wrapping한 것이다. 즉, PrintWriter는 내부에 ServletOutputStream에 대한 참조를 가지고 있다.
사실 클라이언트에 대한 출력 스트림인 오직 하나이지만, PrintWriter는 문자를 좀 더 쉽게 핸들링 할 수 있는 메소드들로 ServletOutputStream을 꾸민 것이라고 생각하면 된다.
Redirect, Request Dispatch
1) Redirect
: 요청에 대한 응답을 다른 URL로 방향을 바꾸는 것.
- 브라우저 주소 창에 URL 입력
- 서버/컨테이너로 요청이 날아감
- Response 객체의 sendRedirect() 메소드로 Redirect할 URL을 명시하여 호출
- 컨테이너가 HTTPReponse에 상태코드 301과 Redirect URL을 클라에게 전달.
- 클라는 Redirect URL을 다시 웹 서버에게 요청
2) Request Dispatch
: 요청에 대한 응답을 웹 애플리케이션의 다른 컴포넌트로 위임시키는 것.
- 브라우저 주소 창에 URL 입력.
- 서버/컨테이너로 요청이 날아감
- 서버가 해당 요청을 다른 컴포넌트로 위임(지금 예에서는 JSP로 위임)
* HTTPServletResponse의 setHeader() setIntHeader(), addHeader()
- setHeader(): 기존 헤더가 있으면 값을 수정하고 없으면 헤더를 하나 추가한다.
- setIntHeader(): 기존 헤더에 Int 값을 set 할때 쓰는 편리한 메소드이다. 헤더 값이 없으면 헤더를 하나 추가한다.
- addHeader(): setHeader()와 다른 점은 기존 헤더가 있더라도 헤더를 하나 더 추가한다.
* getServerPort(), getLocalPort(), getRemotePort() ?
- getRemotePort()는 어디가 remote 인지만 판단하면 된다. 서버입장에서의 remote는 클라이언트이기 때문에 클라이언트의 Port이다.
- 사용자의 요청은 서버가 리스닝하고 있는 서버의 포트를 통해 들어온다. 요청이 한 두개 들어오는 것이 아니기 때문에 서버는 각각의 요청을 새로운 포트를 만들어 스레드를 할당하여 처리한다. 여기서 처음 들어오는 포트가 바로 ServerPort이고 스레드를 위한 Port가 LocalPort이다.
* 서블릿에서 doGet()과 doPost()를 둘다 지원하려면?
: public void doPost(...) throws ... {
doGet(request, response);
}
* 일반적으로 사용하는 컨텐츠 타입(MIME)이 어떤것이 있는지 어떻게 앎?
: 브라우저의 설정(Preference)에 보면 브라우저에 설정되어 있는 MIME 리스트를 볼 수 있다. 웹 서버 설정 파일도 체크해보면 관련 정보가 있는 것을 알 수 있다.
'웹 개발 > 서버' 카테고리의 다른 글
웹 서버, 웹 컨테이너, 서블릿, JSP (0) | 2017.08.26 |
---|