finerss's world!

웹 서버에는 메모리가 없어서 응답을 보낸 다음에는 당신이 누구였는지 조차 잊어버린다.

다음번에 다시요청을해도 그가 이전의 당신이었다는 것을 모른다는 것이다.

즉 웹서버는 당신이 이전에 요청했다는 사실을 기억하지 못하며, 당신에게 응답을 보냈다는 것도 기억하지 못한다.

그러나 프로그램을 하다보면 여러 번의 요청으로 오고 간 클라이언트와 대화 내용(state)을 기억할 필요가 있다 예를들어

고객이 상품을 장바구니에 넣고, 결제하는 동작을 하나의 요청으로 처리할 수는 없지 않은가?

서블릿 API에 이를 해결할 아주 단순한 방법이 있다.

HttpSession을 사용하는 방법이다.

다중 요청간 대화 정보를 유지 하기 위하여 httpSession객체를 사용할수 있다.

즉 특정 사용자의 전체 세션 동안의 정보를 유지할 수 있다는 말인데

클라이언트가 세션이 유지되는 동안 모든 보낸 요청으로부터 얻은 정보를 여기에 저장할 수 있다.

Http 프로토콜은 무상태(stateless)연결이다. 클라이언트인 브라우저는 서버에 연결을 맺고, 요청을 보낸뒤, 응답을 받는다.

그후 연결을 끊는데 연결이라고 하는것은  한번의 요청과 응답을 위해 존재한다고 말할 수 있다.

지속적인 연결이 아니기 떄문에, 클라이언트가 맺는 두번쨰 요청이 똑같은 클라이언트로부터 온 것이라고 할지라도

컨테이너는 이를 모른다. 컨테이너는 단지 각각의 요청이 새로운 클라이언트로부터 왔다고 생각할 뿐이다.

이문제를 해결하는방법은 간단하다.

클라이언트가 제일 처음 요청을 보낼 떄, 컨테이너는 클라이언트의 유일한 세션 ID를 생성 하는 것이다.
그리고 이값을 Response에 넣어 클라이언트로 돌려보내는 것이다. 그 다음부터 클라이언트가 요청을 보낼 떄는
이 세션ID를 함꼐 보내는 것이다. 그러면 컨테이너는 ID를 받아보고 일치하는 세션을 찾아 세션과 연결을 요청한다.


어떻게 클라이언트와 컨테이너는 세션 ID 정보를 공유하는가?

컨테이너는 클라이언트에 Response의 일부로 세션 ID를보낸다.
그러면 클라이언트는 다음 요청부터 Request의 일부로 세션ID을 돌려보낸다.
세션ID정보를 교환하는 가장 간단하며, 일반적인 방법은 쿠키를 이용하는 것이다.


"Set-Cookie"는 Response에 있는 헤더 중 하나이다.

 

"Cookie"는 Request에 있는 헤더 중 하나이다.


컨테이너가 거의 모든 쿠키 관련 작업을 한다.

먼저 컨테이너에게 세션을 만들어 사용하고 싶다고 말하면 컨테이너가 세션 ID를 생성하고 새로운 Cookie객체를 만든다.

그러면 컨테이너는 쿠키 안에 세션 ID 값을 채우고, Response에 쿠키를 설정한다.

그다음 번 클라이언트 요청부터는, 컨테이너는 Request 객체에서 쿠키를 찾아서 세션 ID가 무엇인지 확인한다.

현재 존재하는 세션 가운데에서 세션 ID를 가진놈을 찾아서, 세션과 요청을 연결한다.



Response 객체에 세션 쿠키 보내기

 HttpSession session = request.getSession();

Request 객체로부터 세션 ID 가져오기

HttpSession session = request.getSession();


세션ID를 생성할 떄와, Request에 쿠키를 설정할떄 쓴 코드는 같다.

Request안에 세션ID 쿠키가 들어있으면

ID에 맞는 세션을 찾으면 되고,

그렇지 않고 세션ID쿠키가 없거나 세션 ID에 일치하는 세션이없으면

새로운 세션을 만들면 된다.

모든 쿠키 관련 작업은 내부에서 일어난다.

세션 종료

세션이 종료되는 세가지 이유

>시간이 다 되어서 (타임아웃)
>개발자가 세션 객체에 invalidate() 메소드를 실행하는 경우
>애플리케이션이 다운되는 경우(문제가 생겨 다운되거나, 언 디플로이(undeploy)되는경우)

1.DD에서 세션 타임아웃을 설정하기
DD에서 설정하는 타임아웃은 생성되는 모든 세션에 setMaxInactiveInterval()메소드를 호출하는 것과도 같다.

 <web-app ...>
       <servlet>
        ...
       </servlet>
      <session-config>
            <ssesion-timeout>15</ssession-timeout>
            //15분을의미하며 클라이언트가 15분동안 요청이없으면 세션을  제거하라는 의미이다.
       </session-config>
</web-app>


2.특정 세션만 타임아웃 설정하기
특정 세션 인스턴스만 세션 타임아웃 값을 변경할 수 있다.(다른 세션 타임아웃 값은 바뀌지않는다.)

session.setMaxInactiveInterval(20*60);


*DD에 있는 타임아웃의 단위는 분이고 프로그램적으로 한세션에 설정하는 값은 초단위이다.

'공부 > Sevlets&JSP' 카테고리의 다른 글

JSP 기초  (0) 2011.07.01
Session Listener(세션 리스너)  (0) 2011.06.30
RequestDispatcher  (0) 2011.06.29
동기화  (0) 2011.06.29
Attribute(속성)  (0) 2011.06.28

RequestDispatcher 에는 두개의 메소드, forward()와 include()만 있는데

둘다 Request와 Response 객체를 인자로 한다.(작업을 받은 컴포넌트와 작업을 하려면 이 두 객체가 필요하기떄문).

RequestDispatcher를 얻는 방법은 두가지가 있는 데 첫 번째가 Request고 다른하나는 컨텍스트이다.

둘중 어느 객체로부터 리턴을 받든지 간에 Request를 넘길 웹 컴포넌트가 무엇이다라는 것을 알려주어야 한다.


ServletRequest로부터 RequestDispatcher를 리턴받는경우

RequestDispatcher view = request. getRequestDispatcher("result.jsp");

ServletRequest의 getRequestDispatcher()메소드는 Request를 넘길(foward) 자원에 대한 경로(String)를

인자로 한다. 경로가 /로 시작하는 경우 컨테이너는 "웹 애플리케이션의 루트로 시작하는군" 이라고 생각하며,

/로 시작하지 않으면 "원래 Request의 경로에  상대경로로 시작하는 군" 이라고 생각한다.

또한 현재 웹 애플리케이션의 루트 경로보다 상위 경로로 빠져 나가 다른 곳으로 경로를 지정할수 없다


ServletContext로부터 RequestDispatcher를 리턴받는경우

RequestDispatcher view = getServletContext().getRequestDispatcher("/result.jsp");    
//SerbletContext의 getRequestDispatcher() 메소드에서는 반드시 /가 제일 앞에 와야한다
ServletRequest와 마찬가지로, getRequestDispatcher() 메소드도 Request를 넘길(foward) 자원에 대한 경로(String)를

인자로한다 다른 점이 있다면 상대경로를 사용할 수 없다는 것이다. 즉  반드시 /를 시작으로 절대 경로를 명시하여야한다.

RequestDispatcher의 foward() 메서드 호출하기

view.forward(request, response);

Request로부터 리턴받았든, 컨텍스트로부터 리턴받았든지간에 RequestDispatcher는 Request를 넘길 자원이 무엇인지

이미 알고있다. 이미 getRequestDispatcher() 메소드의 인자로 이를 넘겼기떄문이다.

그렇기떄문에 단지 forward()에 request와 response를 넣어 명시한 컴포넌트로 넘기기만 하면 된다.



*RequestDispatcher의 include() 메서드
include() 메소드는 어떤 작업을 처리하기 위해 다른 곳으로(보통 다른 서블릿) 요청을 넘겼다가, 작업이 완료되면

다시 요청을 받는다. 즉 include() 메소드 요청을 처리하기 위해 도움을 청하는 것이지, 완전히 일을 끝내고 손을 떼는것이 아니다.

forward()는 "이게 다야 더이상 request와 response에 대해 할일이 없다" 라는 것이면

include()는 "request와 response에 대해 이일 좀해줘 근데 일이 끝나면 request와 response에대한 마무리 작업은 내가 할게,
(문론 바로 안끝내고 또 다른 include()나 forward() 작업을 할수도 있다)
그러니 다시 나에게 넘져줘"

라고 볼수있다.


*xx.flush();
클라이언트에게 응답을 보내는 메서드이다. 여기서 응답은 이미 보내졌고, 종료되고, 끝이났다는 말이다

그러므로 이뒤에는 request를 다른곳으로 넘길수없다. forward() 할수없다는뜻이다.

'공부 > Sevlets&JSP' 카테고리의 다른 글

Session Listener(세션 리스너)  (0) 2011.06.30
세션(sisson) 관리  (0) 2011.06.29
동기화  (0) 2011.06.29
Attribute(속성)  (0) 2011.06.28
ServletContextListener  (0) 2011.06.27

동기화

공부/Sevlets&JSP2011. 6. 29. 10:32
컨텍스트 속성 애플리케이션에 있는 누구든지 접근할수 있다는 것을 잊으면안된다.

다양한 서블릿들과 하나의 서블릿이 멀티스레드로 실행되고 있는 환경에서

Context속성에 들어온 요청이 동일한 서블릿으로 온것인지 다른서블릿으로 온것인지

컨테이너는 구분하지 못한다.

어떤 한 서블릿이 Context속성의 값을 변경/사용 하고있더라도

다른 서블릿이 속성 값을 마음대로 변경/사용 할수있는 것이다.

컨테스트에 락을 걸자!

컨텍스트 속성을 보호하는 가장 전형적인 방법은 컨텍스트 객체 자체를 동기화(lock)하는 방법이다.

컨텍스트에 가장 먼저 접근한 객체가 컨텍스트에 락을 건다.

이렇게 하는것은 특정 시점에서 오직 하나의 스레드만이 컨텍스트 속성을 설정하거나 값을 읽는 것을 보장한다는 의미이다.

그러나 여기에도 조건은 있다.

동일 컨텍스트 속성을 다루는 모든 코드들이 마찬가지로 ServletContext에 대하여 락을 걸어야 이것이 작동한다는 것이다.

만약 어떤 코드는 락을 요청하지 않았다면, 여전히 컨텍스트 속성에 자유롭게 접근할수 있기떄문이다.

이를 방지하기위해 웹 애플리ㅔ이션 설계 시 모든 개발자들이 속성에 접근하기 전에 강제적으로 락을 걸도록 만들어야 한다.

밑에 소스를 보면

public void doGet(HttpServletRequest request, Http ServletResponse response)
                                                                    throws IOException, ServletException){
             response.setContentType("Text/html");
             printWriter out = response.getWriter();

             out.println("test context attributes<br>");

        synchronized(getServletContext()){
         //컨텍스트 속성을 보호하기위해 컨텍스트 자체에 락을 걸었다


                    getServletContext().setAttribute("foo", "22");
                    getServletContext().setAttribute("bar", "42");

                    out.println(getServletContext().getAttribute("foo"));
                    out.println(getServletContext().getAttribute("foo"));
        }
}

컨텍스트에 락을 걸었기 떄문에 동기화 코드 블럭 안에서는(빠져나가기전까지는) 컨텍스트 속성이 다른 스레드로부터 안전하다는것을 의미한다. "안전하다"라는 말은 "ServletContext에 대한 동기화를 걸어 둔 다른 코드로부터 안전하다"를 의미하지
그렇지 않은 코드에 대해서는 안전하지 못하다

그나마 이 방법이 컨텍스트 속성을 스레드 - 안전하게 만드는 우리가 할수있는 최선이다.

세션(Session) 속성은 스레드 - 안전한가?

Http세션은 뒤에서 자세히 설명하겠지만 간단하게 설명하면

세션은 클라이언트와 대화 상태(Conversational state)를 유지하기 위하여 사용하는 객체이다.

하나의 클라이언트가 어느 정도 시간 간격으로 보내는 여러 번의 요청에도 세션은 유지된다.(하나의 클라이언트라는 말이 중요)

클라이언트 당 세션은 하나이니 어차피 어느 특정 시점에서는 한번에 하나의 요청만 들어오므로 자동적으로

세션이 스레드-안전하다고 생각할수도 있다. 하지만 클라이언트가 하나 이상의 브라우저를 열 겨우 서로 다른 브라우저로부터

온 요청이지만, 컨테이너는 동일한 세션을 사용할 것이다. 그렇게떄문에 세션 속성도 스레드 -안전하지 못하므로 보호되어야한다.

컨텍스트에 락을 걸었던것처럼 세션에도 락을걸면된다.

public void doGet(HttpServletRequest request, Http ServletResponse response)
                                                               throws IOException, ServletException){
                  response.setContentType("Text/html");
                  printWriter out = response.getWriter();

                  out.println("test context attributes<br>");
                  HttpSession session = request.getSession();
            synchronized(session){
//세션 속성을 보호하기위해 HttpSession객체에 락을 걸었다


                          session.setAttribute("foo", "22");
                          session.setAttribute("bar", "42");

                          out.println(session.getAttribute("foo"));
                          out.println(session.getAttribute("foo"));
            }
}

Request속성

단지 Request속성과 지역 변수만이 스레드 안전하다.(메소드 파라미터도 지역 변수에 포함)

이를 제외한 나머지는 멀티스레딩을 중지하지 않는 한, 멀티 스레딩 에 안전하지못하다

Request 속성은 애플리케이션의 다른 컴포넌트가 Request 또는 Request의 일부를 넘겨받기 위해 사용한다.

단순한 MVC 애플리케이션을 보면, 대부분 서블릿 컨트롤러로부터 시작해서 JSP 뷰료 끝이 난다는 것을 알 수 있다.

컨트롤러는 모델과 커뮤니케이션하여 Response를 만들기 위하여 뷰에게 데이터를 넘겨준다. 이떄 데이터를 컨텍스트나 세션 속성

넣을 필요까지 없다. 단지 이번 요청에 대해서만 사용할 데이터니깐 Request에 넣어 두는 것이 낫기 때문이다.

다른 컴퓨넌트에 Request 를 넘기는 방법은 RequestDispatcher 을 사용하면된다.

//doGet()의 일부
BeerExpert be = new BeerExpert();
ArrayList result = be.getBrands(c);

request.setAttribute("styles", result);       //모델 데이터를 request에 넣어둔다
     RequestDispatcher view = request.getRequestDispatcher("result.jsp");
     //JSP 뷰를 위한 디스패처를 리턴 받는다.
view.porward(request, response);
//JSP에게 "Request를 넘겨 받아라" 라고 말하는 것.

'공부 > Sevlets&JSP' 카테고리의 다른 글

세션(sisson) 관리  (0) 2011.06.29
RequestDispatcher  (0) 2011.06.29
Attribute(속성)  (0) 2011.06.28
ServletContextListener  (0) 2011.06.27
ServletConfig 와 ServletContext  (0) 2011.06.27

속성이라는 것은 3개의 서블릿 API객체 즉

ServletContext, HttpServletRequest(또는 ServletRequest), HttpSession 객체 중 하나에 설정해놓는(바인딩(binding)라고함)

객체를 말한다.

간단히 이를 Map 인스턴스(java.util.Map)에 이름/값의 쌍으로 저장하는 것이라고 생각해도 무방하다(이름은 String/ 값 Object)

사실 내부적으로 이것이 어떻게 구현되어 있는지 신경쓸필요없다 .단지 속성이 생존하는 범위(scope)이 중요하다.

속성은 게시판에 핀으로 고정시킨 게시물과 같다.

누군가 게시판에 다른 사람들이 읽으라고 붙여놓은 것이다
여기서 의문을 가져야할 사항은
누가 게시물을 등록하는가?
게시물의 게시기간은 얼마나 되는가?
개발자적인 용어로 바꾸면 속성의 생존범위(scope)는 어떻게 되는가? 이다

서블릿에 입문한지 얼마되지 않았다면, 속성과 파라미터의 차이에대해 고민을 꽤 시간을 낭비했을 것이다
밑에 표로 파라미터와 속성의 차이를 알아보자

속성 파라미터
타입 Application/context
Request
Session
Application/context 초기화 파라미터
Request 파라미터
Servlet 초기화 파라미터
설정 메소드 setAttribute(String name, Object value) 애플리케이션과 서블릿의 초기화 파라미터 값은 런타임 시 설정할 수 업다. 오로지 DD에서 초기화 설정때만 가능하다(Request 파라미터를 가지고, 좀어렵긴하지만 쿼리스트링을 설정 할수있다)
리턴 타입 Object String
참조 메서드 getAttribute(String name)
*속성은 리턴될떄 Object이므로
캐스트(cast)해야한다
getInitParameter(String name)


세 가지 생존 범위 : context, Request, Session

context 속성 : 애플리 케이션에 있는 것이라면 누구나 접근할 수 있다.(get/set)
Session 속성 : 특정 HttpSession에 접근 권한을 가진 녀석만 접근할 수 있다.(get/set)
REQUEST 속성 : 특정 ServletRequest에 접근 권한을 가진 녀석만 접근할 수 있다.(get/set)



속성들의 API를 살펴보면

모든 인터페이스에서 속성의 API 메소드는 동일한 것을 볼수있다.

속성범위

   접근(누가볼수있는가) 생존범위
(얼마나 생존해 있는가) 
 이곳이 가장 적당한 곳
 Context

스레드-안전(x)
웹 애플리케이션에 있는것은
어떤 것이든. 서블릿,JSP,
ServletContextListener,
servletContextAttribute,
Listener
 애플리케이션 생존시간과 동일
서버나 애플리케이션이 내려가면 컨텍스트도 소멸
 전체 애플리케이션에서 공유하고자하는 자원.
데이터베이서Connection,
JNDI검색명(lookup name),
이메일 주소 등.
 HttpSession

스레드-안전(x)
특정 세션에 접근할 수 있는
어떤 서블릿이나 JSP 모두.
세션은 여러개의 요청에 대하여 정보를 간직할 수 있다.
한 서블릿뿐만아니라 여러 서블릿에 대해서도 가능하다. 
 세션의 생존시간, 세션을 프로그램적으로 소멸시킬수 있으며, 간단히 시간이 만료될 수도 있다.  하나의 Request에만 관련된 것이 아니라 클라이언트 세션에 관련된 데이터와 자원.
클라이언트와 지속적인 대화를 유지하고 싶은것들. 장바구니가 가장 좋은 예.
 Request

스레드-안전(o)
 애플리케이션 내에서 Request객체에 직접 접근이 가능한것 모두. RequestDispatcher를 사용하여 요청을 처리할 수 있는 서블릿이나 JSP를 대부분 의미. 문론 Request관련 리스너포함  Request의 생존시간. 서블릿의 서비스 메소드가 끝날 떄까지이다. 즉 스래드가 Request를 핸들링 할때까지만 살아있는것이다  모델 정보를 컨트롤러에서 뷰로 넘길떄, 또는 특정 요청에만 관련된 데이터 등.



'공부 > Sevlets&JSP' 카테고리의 다른 글

RequestDispatcher  (0) 2011.06.29
동기화  (0) 2011.06.29
ServletContextListener  (0) 2011.06.27
ServletConfig 와 ServletContext  (0) 2011.06.27
Request, Response  (0) 2011.06.27


컨텍스트 파라미터에는 String 밖에 저장할 수 없다. 객체를 XML 배포 서술자에 넣을 수 없다

예를들어 애플리케이션에 있는 모든 서블릿이 데이터베이서 Connection객체를 공유하고 싶을 떄는 어떻게할까?

오늘날 컨텍스트 파라미터를 이용한 가장 일반적인 방법은 컨텍스트 파라미터에
 
DataSource 검색명(lookup name)을 저장하는 것이다.

그런다음 누가 String인 검색명을 실제 DataSource 객체로 바꾸면 되는데 이것을 누가 하느냐가 문제이다.

서블릿안에 넣자니 어떤 서블릿이 가장 먼저 호출된지 알수없다. 만약 가장먼저 호출되지않으면 그보다 호출이 먼저된

서블릿은 DataSource 객체를 공유받지 못할것이다.

방법은 컨텍스트 초기화 이벤트에 리스닝 하는 것이다. 이를 이용하면 컨텍스트 초기화 파라미터를 읽은 다음

애플리케이션이 클라이언트에게 서비스하기 전에 특정 코드를 실행할 수 있다.

하지만 이방법을 누가 하느냐가 문제인데 서블릿도 JSP도 아닌 클래스가 필요하다

이클래스는 다음의 두가지 ServletContext이벤트(초기화(생성)와 소멸)에 귀 기울이고 있어야한다.

즉 다음의 기능을 가진 클래스여야한다

 - 컨텍스트가 초기화(애플리케이션 배포)되는 걸 알아차릴수 있어야한다
ServletContext로부터 컨텍스트 초기화 파라미터를 읽는다.
데이터베이스를 연결하기 위하여 초기화 파라미터 검색명(lookup name)을 사용한다
데이터베이스 Connection 객체를 속성(Attribute)에 저장한다.
-컨텍스트가 종료(애플리케이션 서비스가 내려가는)되는 걸 알아차릴수있어야한다
데이터베이스 연결을 닫는다.
 ServletContextListener 클래스

import javax.servlet.*;

public class MyServletContextListener implementes ServletcontextListener
                                                                    //컨텍스트 리스너는 ServletContextListener만 구현하면된다
       public void contextInitialized(ServletContextEvent event){
              //여기에 데이터베이스 연결을 초기화 하는 코딩을한다.
              //그리고 이를 컨텍스트 속성에 저장한다.
       }

       public void contextDestroyed(ServletContextEvent event){
            //여기에 데이터베이스 연결을 닫는 코딩을 한다.
      }
}

리스너 작성후 컨테이너에게 우리가 리스너를 만들었음을 알려주기위해

DD(web.xml)에 <listener>요소에 클래스 이름만 기재하면 된다.

<servlet> 
        <servlet-name>xxx</servlet-name>
        <servlet-class>xxx</servlet-class>
</servlet>

<Context-param>
      <param-name>adminEmail</param-name>
      <param-value>abc@daum.com</param-value>
</Context-param>

<listener>
      <listener-class>
            com.example.MyServletContextListener
      </listener-class>
</listener>




전체적인 그림을 보면
애플리케이션 시작(초기화)에서부터 마지막(서블릿 실행) 까지 한번보면


1. 컨테이너는 DD를 읽는다. 물론<listener> 요소와 <context-param> 요소도 읽는다.
2. 컨테이너는 ServletContext 객체를 생성한다. 애플리케이션에서 이객체를 공유한다.
3. 컨테이너는 컨텍스트 초기화 파라미터의 이름/값 쌍(String 쌍)을만든다.(여기선하나만있다고가정)




4. 컨테이너는 생성한 컨텍스트 초기화 파라미터의 String 쌍을 ServletContext 객체에 설정한다.
5. 컨테이너는 MyServletContextListener 클래스 인스턴스를 만든다.
6. 컨테이너는 리스너의 contextInitialized() 메소드를 호출합니다. 인자로 ServletContextEvent를 넘긴다.
   이 이벤트 객체를 가지고 ServletContext에 접근한다. 코드에서는 ServletContextEvent로 접근한
   ServletContect로 컨텍스트 초기화 파라미터 값을 읽는다.



7. 리스너가 ServletContextEvent에게 ServletContext에 대한 참조를 요청한다..
8. 리스너가 ServletContext에게 컨텍스트 초기화 파라미터 breed에 대한 값을 요청한다..
9. 리스너는 초기화 파라미터를 가지고 Dog객체를 생성한다.
10. 리스너는 ServletContext의 속성으로 Dog를 설정한다.


11. 컨테이너는 새로운 서블릿을 생성한다( 풀어서 말하면 초기화 파라미터로 ServletConfig를 생성하고,
      여기에 ServletContext에 대한 참조를 설정한 다음,
      서블릿 init() 메소드를 호출한다).
12. 서블릿은 요청을 받고는, ServletContext에게 dog속성에 매핑된 객체 인스턴스를 요청한다.
13. 서블릿은 Dog객체의 getBreed()메소드를 호출한다. (그다음 HttpResponse에 이내용을 쓴다).

'공부 > Sevlets&JSP' 카테고리의 다른 글

동기화  (0) 2011.06.29
Attribute(속성)  (0) 2011.06.28
ServletConfig 와 ServletContext  (0) 2011.06.27
Request, Response  (0) 2011.06.27
서블릿 생명주기와 API  (0) 2011.06.24

ServletConfig

서블릿에 이렇게 하드코딩하는것보다

printWriter out = response.getWriter();
out.println(abc@daum.com);

이메일주소를 배포서술자안에 넣은후 서블릿이 DD에서 이메일 주소를 읽어오게하는 방법이
나중에 web.xml 만 수정하면되니까 서블릿을 손 끝하나 대지않아도 원하는것을 할수있다.

DD(web.xml) 파일에서

 <servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>xxx</servlet-class>

<init-param>
                <param-name>adminEmail</param-name>
                <param-value>abc@daum.com</param-value>
          </init-param>


</servlet>
//servlet 항목안에 설정해준다


서블릿 코드에서

out.println(getServletConfig().getInitParameter("adminEmail");
//모든 서블릿에는 상속받은 getServletConfig()가 있다.



서블릿에는 상속받은 getServletConfig() 메소드가 있다.

컨테이너가 서블릿을 만들떄, DD를 읽어 이름/값의 쌍으로 ServletConfig를 생성한다.
컨테이너는 이 초기화 파라미터를 두번 다시 읽지 않는다. 이름/값 쌍이 ServletConfig 안에 기록이 되면,
서블릿이 다시 배포 되지 않는 한 DD를 수정한다고 바뀌지 않는다.


ServletConfig는 말 그대로 서블릿 설정이다. DD에 있는 서블릿 초기화 파라미터 정보를  웹 애플리케이션의
다른 컴포넌트와 공유하려면, 몇 가지 작업을 해야한다.

 //doPost() 메소드안
String color = request.getPrameter("color"); //Request 객체에서 클라이언트가 선택한 색정보를 읽어온다

BeerExpert be = newBeerExpert();
List rusult = be.getBrands(color); // 그런다음 모델을 인스턴스와하고 뷰에필요한 정보 설정

request.setAttribute("styles", result);
// Request 객체의 속성의 결과를 설정하고, Request 객체를 JSP가 처리하도록 넘김(forward)

Request 객체는 속성을 설정하는 메소드를 제공하는데(setAttribute()) 여기서 값은 어떤 객체든지 가능하다

ServletContext

컨텍스트(Context) 초기화 파라미터의 작동방식은 서블릿 초기화 파라미터와 동일하다.
그러나 컨텍스트(Context) 초기화 파라미터는 특정 하나의 서블릿만 사용하는 것이 아니라
모든 웹 애플리케이션에서 이용할 수 있다는 차이가 있다.
웹 애플리케이션에 있는 모든 JSP 서블릿에서 별다른 코딩없이도 컨텍스트 초기화 파라미터 정보에 접근할수 있으며, 그렇다고 모든 서블릿의 DD를 수정하지 않아도 된다. 또한 그 값을 수정하는 경우도 DD만 수정하면 다른곳도 자동으로 반영되니 편하다.

DD(web.xml) 파일에서

<servlet>
           <servlet-name>xxx</servlet-name>
           <servlet-class>xxx</servlet-class>
</servlet>
//<servlet> 항목 안에 있던 <init-param>을 모두 들어내고
<Context-param>
                <param-name>adminEmail</param-name>
                <param-value>abc@daum.com</param-value>
          </init-param>

//<Context-param>은 전체 애플리케이션을 위한 항목이다. 따라서 <servlet>항목안에 들어가지 않는다
<web-app> 항목에 포함시시키되 <servlet>항목안에 두지 않는다



서블릿 코드에서

out.println(getServletContext().getInitParameter("adminEmail"));
//모든 서블릿에는 상속받은 getServletContext() 메소드가 있다.




ServletConfig는 서블릿 당 하나

ServletContext는 웹 애플리케이션 당 하나

웹 애플리케이션은 오직 하나의 ServletContext으로 전체 웹 애플리케이션에서 정보를 공유한다. 하지만 웹 애플리케이션 안에 있는 서블릿은 자민만의 ServletConfig를 가진다!

또한 둘은 초기화 시켜주는 목적이다. 초기화 파라미터를 배포시 정해진 상수라고 생각하고
실행 시에 이 값을 읽을 수는 있지만, 수정할수는 없다. getInitParameter 있지만 setInitParameter() 는 없다!!!!



'공부 > Sevlets&JSP' 카테고리의 다른 글

Attribute(속성)  (0) 2011.06.28
ServletContextListener  (0) 2011.06.27
Request, Response  (0) 2011.06.27
서블릿 생명주기와 API  (0) 2011.06.24
MVC 패턴  (0) 2011.06.24

HttpServletRequest

HttpServlet의 service() 메소드는 HttpServletRequest와 HttpServletResponse를 인자로 한다.
service(HttpServletRequest,HttpServletResponse)

service() 메소드는 HTTP Request의 HTTP 메소드(GET,POST 등)에 따라 doGet()을 호출할지, 아니면 doPost()를 호출할지 결정
HTTP Request 메소드는 GET,POST,HEAD,TRACE,PUT,DELETE,OPTION,CONNECT 총 8가지가 있다.

Post 요청에는 몸체가 있다. GET 요청에는 몸체가 없다. GET파라미터는 URL의 실제 주소 뒤에 연결된다. 보통 이를 쿼리스트링 이라고 부른다.

HTML 폼에 "method=POST" 를 코딩하지 않았다면  이 요청은 POST가 아니라 GET이다.
즉 디폴트는 HTTP GET이다

Request 객체에서 파라미터를 뽑아내기 위하여 getParameter("파라메터이름") 메소드를 호출한다
리턴값은 언제나 String이다

이외에 Request 객체에서 헤더 정보, 쿠키정보, 세션정보, 쿼리 스트링, 입력스트림 관련 메소드를 사용할수있다.
이메소드에관한건 뒤에 서서히 살펴보자!

HttpServletResponse

클라이언트로 데이터를 보내기 위하여 Response 객체를 사용한다.

Response 객체(HttpServletResponse)에서 가장 많이 사용하는 메소드는 setContentType()과 getWriter()이다.

많은 개발자들이 착각하고 있는 것중 하나가 GetPrintWriter()이라고 생각하는데 getWriter()이 맞다.

getWriter()메소드로 HTML을 작성하는 것과 같은 문자 I/O작업을 한다.

Response 객체로 헤더를 설정하고, 오류를 전송하며 쿠키도 추가할수 있다.

실제 프로젝트에서 HTML응답을 보내기위해 JSP를 사용하지만 JAR파일 같은 바이너리를 전송하기 위해 Response 스트림을 사용한다.

Response에서 바이너리 스트림을 리턴받는 메소드는 getOutputStream이다.

브라우저가 응답으로 들어오는 데이터를 어떻게 핸들링 할지 알려주는 메소드는 setContentType()이다.
일반적으로 컨텐츠 타입은 text/html, application/pdf, image/jpeg 등이 있다.


요청에응답하지 않고, 요청을 다른 URL로 리다이렉트 할수 있다.
이경우 브라우저는 서버에서 정한 URL을 받아 새로운 요청을 보낸다.

리다이렉트와 디스패치는 다르다. 요청을 디스패치하는것은 서버에서 일어나는거고 리다이렉트는 클라이언트에서 일어난다.
디스패치 작업은 서버상에있는 다른 컴포넌트로 작업을 넘기는것이라면 리다이렉트는 완전히 다른 URL로 가라고 브라우저에게 지시하는 것이다


'공부 > Sevlets&JSP' 카테고리의 다른 글

ServletContextListener  (0) 2011.06.27
ServletConfig 와 ServletContext  (0) 2011.06.27
서블릿 생명주기와 API  (0) 2011.06.24
MVC 패턴  (0) 2011.06.24
서블릿 매핑, 배포 서술자(DD, Deployment Descriptor)  (0) 2011.06.23

언제 서블릿 클래스를 로딩하는가?
서블릿의 생성자는 언제 호출되는가?
얼마나 올랫동안 서블릿은 살아있는가?
서블릿은 언제 자원을 초기화하는가?
또 사용한 자원은 언제 꺠끗이 청소 하는가?

서블릿의 인생은 사실 매우 간단하다.
왜냐하면 서블릿은 오직 하나의 중요한 상태를 가지기 떄문인데, 바로 초기화(initialized)를 말한다.
서블릿이 초기화 되지 않았다는 말은, 초기화되는중(생성자를 실행하거나 init()메소드를실행하거나)이거나
아니면 소멸되는 중(destroy() 메소드를 실행)이거나, 그것도 아니면 존재하지 않음 (does not exist) 중 하나이다.

(위 그림에서보면 처음 init()으로 서블릿을 생성하고나면 다음 이서블릿을 호출할때는 생성하는것이아니라 스레드를 만들어 계속 service() 한다.) 


당신이 작성한 서블릿은 생명주기 메서드를 상속 받아야하는데
서블릿 API를 살펴보면


Servlet 인터페이스에선 service(), init(), destroy() 이 세개가 생명주기(라이프사이클) 메소드이다.
GenericServlet 클래스는 추상클래스이다. 필요한 대부분의 서블릿 메소드를 구현하였으며, 여기에는 Servlet 인터페이스에 정의된 것도있다. 이클래스를 상속받아 클래스를 생성할 일은 거의 없지만 대부분 서블릿의 '서블릿 행위' 라고하는 것들은 바로 이클래스로부터 나왔다는 것을 기억하자.
HttpServlet 클래스 역시 추상클래스이다. HTTP적인 측면을 반영하기 위해 service()메소드를 재정이한것이다. 즉 service() 메소드는 오로지 HTTP request와 HTTP response만 받아들이고 다른 어떤 서블릿 Request 와 response는 받지않는다.


서블릿 일생에 있어 3번의 중요한 순간들

1. init()

 컨테이너는 서블릿 인스턴스를 생성한 다음 init() 메소드를 호출한다. 이메소드는 service()메소드전에 실행되어야 한다.
클라이언트의 요청을 처리하기전에 서블릿을 초기화할 기회를 주는것이다.
초기화할 코드가 있다면 init()메소드를 재정의할수있다( 데이터베이스에대한 접속, 다른객체에 서블릿을 등록하는 등)


2. service()

 최초 클라이언트의 요청을 받았을때, 컨테이너는 새로운 스레드를 생성하거나 스레드풀로부터 서블릿을 가져와서 서블릿의 service() 메소드를 호출한다
클라이언트의 HTTP 메소드(GET, POST 등)를 참조하여 doGet() / doPost() 혹은 다른 메소드를 호출할지 판단한다.
재정의는 하지않으며 doGet()/ doPost() 를 재정의하여 HttpServlet의 service()가 이를 실행하도록 한다.


3.doGet()혹은 doPost()

 service() 메소드가 클라이언트의 HTTP 메소드(GET, POST등) 를 참조하여 doGet()/ doPost()를 호출한다.
여기서 doGet()/ doPost() 만 언급하는이유는 이것말고 나머지메소드는 사용할 경우가 거의없기떄문이다
이 메도드안에서 코딩작업을 하면된다.
doGet()/ doPost() 둘중 하나는 반드시 재정의해야한다.



서블릿초기화 : 객체가 서블릿이 되는 순간

서블릿의 일생은 컨테이너가 서블릿 파일을 찾아서 로딩할 떄부터 이다.(생성하는게아니다)
이작업은 컨테이너가 시작할때(예로 톰캣이 실행될때) 이루어지는데 컨테이너는 배포된 웹 애플리케이션이 어떤것이
있는지 체크하고 관련 서블릿 클래스 파일들을 검색한다.

첫번째 단계가 클래스를 찾는것인 셈이다.

클래스를 로딩하는것은 두번째 단계이다. 이작업은 컨테이너가 시작될떄 로딩되기도하고, 최초 클라이언트가 접슨시 로딩되기도 한다. 서블릿이 일찍 로딩하든 아니면 실행시 로딩하든 상관없이, service() 메소드는
서블릿 초기화가(서블릿 생성자 -> init()) 완전히 완료된다음 실행 된다.

생성자의 실행은 서블릿이 '존재하지 않음(dose not exist)' 상태에서 '초기화됨' 상태로 옮겨감을 의미한다
이는 클라이언트의 요청에 서비스할 준비가 되었다는 말이다. 하지만 생성자는 단지 객체를 만드는것이지 서블릿을 만드는 것이 아니다. 서블릿이 되기 위해서는 서블릿적인(servletness)을 내려 받아야 한다.

객체가 서블릿이 된다는 것은 서블릿에 고유한 권한을 가진다는것을 의미하는데 예를 들어 ServletContext를 가지고
컨테이너로부터 정보를 읽어올 수있는 능력 같은 것이다.(이는 뒤에 설명)

'공부 > Sevlets&JSP' 카테고리의 다른 글

ServletConfig 와 ServletContext  (0) 2011.06.27
Request, Response  (0) 2011.06.27
MVC 패턴  (0) 2011.06.24
서블릿 매핑, 배포 서술자(DD, Deployment Descriptor)  (0) 2011.06.23
서블릿 컨테이너(Servlet Container)  (1) 2011.06.22

MVC 패턴

공부/Sevlets&JSP2011. 6. 24. 14:47

비지니스 로직과 프리젠테이션 로직을 깔끔하게 분리하는 문제는 모든 소프트웨어 개발에서 해결해야 하는 아주 중요한문제이다.

특히 웹 애플리케이션에서 이부분은 더욱 그런데 왜냐하면 비지니스 로직 자차게 웹 인터페이스를 통해서만 접근된다고 단정할수

없기 떄문이다. 또 한가지를 들자면 소프트웨어 개발에 있어서 개발환경이나 내부구조등의 스펙은 항상 변할수 있기때문이다. 


서블릿, JSP 환경에서 MVC

 뷰(view)
프리젠테이션에 대한 책임을 지며
컨트롤러로부터 모델 정보를 읽어온다.
뷰는 또한 사용자가 입력한 정보를 컨트롤러에게 넘겨주어야한다.
JSP 가 이역활을한다

컨트롤러(Controller)
Request 객체에서 사용자가 입력한 정보를 뽑아내어, 모델에 대하여 어떤작업을 해야하는지 알아낸다.
모델 정보를 수정한다든지, 뷰(JSP)에게 넘겨줄 새로운 모델을 만든다든지 등과 같은작업을 한다
서블릿이 이역활을 한다

모델(Model)
비즈니스 로직이 바로 여기에 들어간다. 모델 정보(state)를 읽어오거나(getter) 수정하는(setter) 로직도 여기 포함된다
MVC패턴에서 모델은 데이터베이스와 통신하는 유일한 곳이다( 물론 DB통신만을 전담하는 객체를 따로 빼낼수도 있다)
자바 빈(java Bean)이 이역활을 한다.

'공부 > Sevlets&JSP' 카테고리의 다른 글

Request, Response  (0) 2011.06.27
서블릿 생명주기와 API  (0) 2011.06.24
서블릿 매핑, 배포 서술자(DD, Deployment Descriptor)  (0) 2011.06.23
서블릿 컨테이너(Servlet Container)  (1) 2011.06.22
CGI 와 Servlet 의차이  (0) 2011.06.22

컨테이너는 클라이언트가 날린 요청에 들어 있는 URL을 가지고 어떤 서블릿인지 찾아낸다.
URL과 서블릿을 매핑하는 방법은 개발자가 이를 어떻게 설정하는가에 따라 달라진다.


서블릿은 세가지 이름을 가질수있는데

1. classes/registration/SignUpServlet.class 처럼 파일 위치를 알려주는 이름인 파일 위치명(file path name)

2. 서블릿 배포명 - 이이름은 내부적으로만 사용되는 이름이며, 클래스 이름이나 파일 이름과 같을 필요는 없다.

3. URL 이름 - 공공의(public) 이름으로 누구나 다알아도 되는이름이다. 이이름은 HTML 코드 안에 코딩하는 이름이며, 사용자가 클릭해서 서블릿을 호출할 때 사용하는 이름이다. 이 URL 이름이 HTTP 요청 안에 포함되어 서버로 전송되는 이름이다.

본래에 파일위치명 을 놔두고 다름이름을 만들어서 헷갈리게 하는이유는
서블릿 이름을 다른 이름으로 매핑하면, 애플리케이션의 유연성, 보안성이 좋아지기 떄문이다.

가령 JSP나 HTML 안에 서블릿의 실제 경로와 파일 이름을 하드 코딩한다고 해보자
만약 그서블릿을 담고있는 디렉토리 구조를 바꿔야 하는 상황이 벌어졌다면, 개발자는 파일이란 파일은 다 검색해서 하드코딩된 부분을 찾아 수정해야 하는 번거러움이 있다.

하지만 DD에 매핑을 하게되면 실제 파일명을 하드코딩하는 것이 아니라 DD에 매핑된 URL 이름만 명시 해놓기 떄문에
디렉토리 구조가 변경되었다하더라도 파일은 수정할필요없이 DD만 수정하면 되는 것이다.

보안문제를 보면 라이언트가 파일 위치명을 알게된다면, 실제 경로를 알수 있다는 말인데 이는 직접 브라우저 주소란에 이정보를 입력하여 접근 할수 있다는 것이다. 클라이언트가 여러분의 서버 디렉토리 구조랑 파일이름들을 모두 알수있다는 뜻이며 여러분이 설정한 방식으로 폼이나 페이지에 접근하지 않고, 클라이언트가 직접 접근하게 되기때문에 보안상 문제가 발생할수있는것이다.


배포 서술자(DD, Deployment Descriptor)에에서
URL을 서블릿에 매핑하기

웹컨테이너도 서블릿을 배포하려면, 배포서술자(이하 DD) 라는 XML 파일을 먼저 만들어야 한다(있다면 수정하면 된다).
DD 파일에는 서블릿과 JSP를 어떻게 실행하느냐에 관한 많은 정보들이 들어 있는데 우선 URL과 서블릿 매핑에 대해 알아보자.
매핑을 하려면 두 가지 작업을 해야 하는데, 먼저 URL 이름을 내부에서만 사용하는 이름으로 매핑하고, 그다음 내부 이름을 실제 클래스 이름으로 매핑하면 작업 끝이다.

URL 매핑을 위한 두 가지 항목

1. <servlet>
      내부에서만 사용하는 이름과 완전한(패키지 이름까지 포함하여) 클래스명과 서로 매핑한다.
2. <servlet-mapping>
      내부에서 사용하는 이름과 URL 이름을 서로 매핑한다.

DD형식을 보면

 <web-app ...>
   
    <servlet>
          <servlet-name>Internal name 1</servlet-name>
          <servlet-class>foo.Servlet1</servlet-class>
    </servlet>

    <servlet>
          <servlet-name>Internal name 2</servlet-name>
          <servlet-class>foo.Servlet2</servlet-class>
    </servlet>
..............................................
.............................................

     <servlet-mapping>
           <servlet-name>Internal name 1</servlet-name>
           <url-pattern>/public1</url-pattern>
      </servlet-mapping>

      <servlet-mapping>
           <servlet-name>Inrernal name 2</servlet-name>
           <url-pattern>/public2</url-pattern>
       </servlet-mapping>

</web-app>

각각을 살펴보면




클라이언트로부터 요청이 들어오면 컨테이너는 DD에서 위에 순서대로(1->2->3->4) 서블릿을 검색한다
/public2 라는 URL 이름으로 요청이들어왔다면 DD내에서 매핑된이름인 Internal name2로 실제 서블릿 명(실제위치)을 찾게 되는 것이다.

URL명과 실제 서블릿 명을 서로 매핑하는 일 말고도 DD에서 할수 있는 일은 많다.
보안역활(Security Role) 설정, 오류페이지 설정, 항목 라이브러리, 초기화 구성 및 관련 정보 설정 등 무척 많다.
그리고 서버가 J2EE의 모든 규격을 구현한 서버라면 EJB 선언 및 접근에 관련된 내용도 여기서 설정한다.



DD의 이점

- 이미 테스트된 소스 코드의 대한 수정 최소화
- 소스 코드가 없더라도, 애플리케이션을 목적에 맞게 수정할 수 있다.
- 코드 변경이나 컴파일을 다시 하지 않아도 서버 자원(예를 들면 데이터베이스 연결)을 바꿀수 있다.
- 접근 제어 목록(ACL, Access Control List), 보안 역활(Security Role)과 같은 보안에 관련된 업무도 쉽게 관리할 수 있다.
-프로그래머가 아닌 사람이 웹 애플리케이션을 배포하고 설정을 수정하는데 수월하다.










'공부 > Sevlets&JSP' 카테고리의 다른 글

서블릿 생명주기와 API  (0) 2011.06.24
MVC 패턴  (0) 2011.06.24
서블릿 컨테이너(Servlet Container)  (1) 2011.06.22
CGI 와 Servlet 의차이  (0) 2011.06.22
CGI(Common Gateway Interface)  (0) 2011.06.22