메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

한스(Hans)의 10 가지 JSP 팁

한빛미디어

|

2001-07-13

|

by HANBIT

12,006

by 한스 베르그스텐, JavaServer Pages의 저자 JavaServer Pages(JSP)는 자바를 기반으로 하여 웹 애플리케이션을 개발하는 최신의 툴이다. JSP 페이지에는 정규 마크업 코드와 특수 JSP 요소가 들어있다. 페이지가 요청되면, JSP 요소에 의해 생성된 정적인 마크업 코드와 동적인 컨텐트가 결합되어, 요청에 대한 완벽한 답을 만든다. JSP를 효율적으로 사용하는 방법을 이렇게 짧은 기사에서 모두 설명할 수 는 없지만, 여기서는 사람들이 많이 질문 했던 내용을 정리해 볼 것이다. 1. 올바른 인클루드 메커니즘을 선택하라. JSP 페이지는 완성된 응답을 형성하기 위해 다른 파일에서 페이지 프레그먼트를 포함할 수 있다. 예를 들어서 분리된 파일에 있는 헤더(header)나 풋터(footer), 네비게이션 바 컨텐트(navigation bar content) 등을 각각 다른 페이지에 저장하기 위해 JSP 페이지를 사용할 수 있다. 여기에는 인클루드 디렉티브(include directive)와 인클루드 액션(include action)이라는 두 가지 인클루드 메커니즘이 있다. 하지만 둘 중에서 어떤 것을 사용해야 할 지는 상황에 따라 다르다.
JavaServer Pages
<%@include file="filename.inc"%>라는 인클루드 디렉티브에는, 번역을 하는 동안, 즉 페이지가 서블릿으로 변환될 때에, 명기된 파일의 내용(content)을 포함한다. 메인 페이지와 거기에 포함된 파일은 단순히 병합된다. 이것은 하나의 파일에서 정의된 스크립팅 변수를 (과 같은 스크립팅 요소나 액션 요소를 사용하여) 모든 파일에서 볼 수 있고, 스크립팅 변수들이 고유한 이름을 가져야 한다는 것을 의미한다. 어떤 컨테이너에서는 디렉티브와 함께 포함된 파일에서의 변화를 감지할 수 있지만, 특수 사항에서는 그것이 필요하지 않다. 따라서, 운영되고 있는 시스템에서 포함된 파일에서 만든 변화는 즉시 반영되지 않는다. 변화한 내용을 보려면, 메인 JSP 페이지를 업데이트 하거나, 메인 페이지에서 생성된 클래스 파일을 제거해야 할 것이다. 사용자가 페이지를 요청하였을 때, 라는 인클루드 액션에는 명기된 페이지(JSP 페이지나 서블릿)를 실행함으로써 생성되는 응답(response)이 포함된다. 인클루드 디렉티브에 반대되는 것으로서, 페이지 네임은 소위 request-time attribute 값이라고 지정될 수 있다. 이를 통해서 메인 페이지가 요청되었을 경우, 어떤 페이지에 인클루드할 것인지를 결정할 수 있다. 이는 페이지 자체의 컨텐트가 아니라 인클루드 된 페이지에 의해 응답이 생성된 것이기 때문에, 스크립팅 변수는 하나의 파일에서 선언되고 다른 파일에서는 사용할 수 없다. 객체를 여러 페이지에서 공유하기 위해서는 JSP에서 요청, 세션, 애플리케이션 영역 중 한 군데에 객체를 넣어야 한다. 인클루드 액션을 사용하여 인클루드된 페이지를 변화시키면 변화가 즉시 나타날 것이다. 다른 메커니즘을 사용할 때 중요한 규칙은 다음과 같다:
  • 파일이 거의 변하지 않는다면 인클루드 디렉티브를 사용하라. 이것이 가장 빠른 메커니즘이다. 컨테이너가 자동적으로 변화를 탐지하지 않는다면, 메인 페이지 클래스 파일을 삭제함으로써 변화가 적용되도록 할 수 있다.
  • 내용이 자주 바뀌는 경우나 메인 페이지가 요청되기 전까지 어떤 페이지를 인클루드할 것인지 결정할 수 없는 경우에만 인클루드 액션을 사용하라.
2. 버퍼 플러슁 문제의 처리 HTTP 응답 메시지에는 헤더와 바디가 포함되어 있다. 헤더는 바디에서 담고 있는 데이터의 타입(HTML 텍스트, 이미지 등), 바디의 크기, 바디가 캐시될 수 있는지 등의 정보를 브라우저에 전달한다. 쿠키를 설정하고 자동으로 다른 페이지나 리디렉트(redirect)된 페이지를 브라우저에서 인식하도록 하는 데에도 헤더를 사용한다. 응답 헤더는 바디를 보내기 전에 모두 브라우저에 전송되어야 한다. 헤더가 설정되기 전에 바디의 일부가 생성되는 것을 허용하려면, 바디는 버퍼링(buffer)되어야 한다. 어떤 것을 바디에 기록하자 마자 브라우저에 응답을 보내지 않고, JSP 컨테이너가 JSP 요소에 의해 생성된 정적인 마크업 코드와 동적인 컨텐트를 모두 작성한다. 버퍼가 가득 차있거나 페이지의 끝에 도달한 경우 에는 컨테이너가 버퍼링된 바디 컨텐트에 이어지는 이미 설정된 헤더들을 보낸다. 서블릿에서는 이것을, 응답을 커밋한다고 부른다. 응답이 커밋된 다음에는 쿠키나 리디렉션과 같은 명령을 헤더에서 설정할 수 없다. 다른 페이지에 요청을 포워드할 수도 없다. 하지만 이런 점이 문제가 되는 경우는 별로 없다. 디폴트 버퍼 사이즈는 8KB인데, 이는 일반적인 페이지를 보관하기에는 충분한 용량이며, 페이지 디렉티브의 버퍼 속성을 늘려 줄 수도 있다. 하지만 인클루드 액션을 한 페이지 안에서 사용한다면, 다른 결과가 나타날 수도 있다. 에서 사용하는 서블릿 특성 갖는 제약 때문에, 타겟 페이지를 불러내기 전에 항상 버퍼 플러슁이 일어난다. 이것은 액션을 사용한 다음에는 헤더를 설정하거나 를 사용할 수 없다는 것을 뜻한다. 자동 플러슁의 부작용은 액션이 올바르게 전달되지 않을 경우에 JSP컨테이너는 포워드 메커니즘을 사용하기 때문에 JSP 요소로 인한 런타임 에러가 일어날 수 있다는 점이다. 요소가 있는 페이지에서 "response already committed"와 같은 에러 메시지가 나타난다면, 인클루드 액션 대신 인클루드 디렉티브를 사용해라. 3. 동일한 요청을 처리하는 페이지 사이에서 데이터 전달하기 하나의 요청을 처리하기 위해 한 개 이상의 JSP 페이지가 사용되는 경우가 많다. 인클루드 액션을 사용하여 일반적인 네비게이션 바를 모든 페이지에서 포함할 때를 예로 들 수 있다. 페이지 하나는 요청을 처리하기 위해 사용하고, 다른 페이지에서는 응답을 생성하기 위해 라는 포워드 액션을 사용하는 경우도 그렇다. 어떤 페이지에서 다른 페이지로 데이터를 보내는 방법은 두 가지가 있다: 요청 파라미터(parameter)를 사용하거나 요청 속성(attribute)을 사용하는 것이다. 우선, 액션을 통해 불러낸 페이지는 첫 번째 페이지에 보내어진 모든 HTTP 요청 파라미터에 접근한다. 게다가 액션을 사용하여 새로운 요청 파라미터를 명기할 수도 있다.

  
      
  
타겟 페이지는 원래 있던 파라미터이든, 나중에 덧붙여진 파라미터이든 상관 없이 모든 요청 파라미터에 동일한 방식으로 접근한다. 요청 파라미터는 문자열 값만을 가질 수 있다. 객체를 보내려면, 가령 UserInfoBean을 다양한 사용자 정보 값들과 함께 전달하려면, 요청 속성(request attribute)처럼 그것을 보내야 한다. 요청 속성은 요청 스코프(scope) 객체와 같은 것이기 때문에, 첫 번째 페이지는 객체를 만들어 내고 모든 특성을 액션으로 설정할 수 있다.

  
     
  
  ...
  
를 통해 불러낸 페이지에서도 빈(bean)을 사용하기 위해서는 불러낸 페이지에서도 액션을 사용해야 한다. 이는 첫 번째 페이지에서 생성된 빈이 특정 id와 함께 놓이고 연결될 수 있기 때문이다. 그렇게 되면, 스크립팅 코드나 와 같은 액션이 새로운 페이지에서 빈을 사용할 수 있게 된다.

  

4. 포워드(Forward)냐, 리디렉트(Redirect)냐 한 페이지에서 다른 페이지로 제어를 옮기고 싶다면, 위에서 설명했듯이 포워드나 리디렉트를 사용하면 된다. 그런데 포워드와 리디렉트 사이에는 큰 차이점이 있다. 어떤 페이지를 포워드할 경우에는, 타겟 페이지는 내부적인 메소드 콜을 통해 JSP 컨테이너로 불러낸다. 새로운 페이지는 동일한 요청을 계속 처리하고 브라우저에서는 한 개 이상의 페이지가 연관되어 있다는 것을 인식하지 못한다. 반면, 리디렉트를 사용하면 첫 번째 페이지에서 브라우저에 새로운 요청을 타겟 페이지에 만들라는 것을 알린다. 따라서 리디렉트할 경우에는 브라우저에서 보이는 URL이 새로운 페이지의 URL로 바뀌지만, 포워드를 사용할 경우에는 바뀌지 않는다. 리디렉트를 하면, 브라우저에서 새로운 요청을 만들어야 하기 때문에 포워드보다 속도가 느리다. 또 한 가지 다른 점은 리디렉트한 다음에는 가장 최근에 요청한 데 대한 응답이 나타나기 때문에, 요청의 범위를 나타내는 객체를 더 이상 사용할 수 없다는 것이다. 데이터를 리디렉트하는 페이지로 보내려면, 쿼리 문자열을 사용하고 데이터를 요청 파라미터로 설정해야 할 것이다(아니면 데이터를 세션이나 애플리케이션 범주 객체로 저장해도 된다). 그러면 포워드나 리디렉트 중에서 어떤 것을 사용할 것인가? 포워드는 항상 속도가 더 빠르기 때문에, 사용자는 이것을 우선순위로 두어야 할 것이다. 하지만 포워드 된 다음에도 브라우저의 URL은 시작 페이지의 URL로 남아있기 때문에 페이지를 리로드(reload)할 경우에는 원하지 않는 페이지로 변경될 지도 모른다. 시작 페이지가 쇼핑 카트에 아이템을 덧붙이거나 데이터베이스에 정보를 삽입하는 등 어떤 정보를 업데이트하는 페이지라면, 리로드해서 다시 불러내지 않는 것이 좋다. 따라서 진행되는 과정을 보고싶을 때에는 포워드 대신에 리디렉트를 쓴다. 5. 빈즈와 커스텀 액션 JSP 1.0에서, 자바빈즈는 자바 코드를 싸고, 등의 표준 JSP 액션 요소를 사용하여 불러낼 수 있는 유일한 타입의 컴포넌트였다. JSP 1.1은 커스텀 액션 요소를 컴포넌트 툴박스에 더한다. tag handler라는 자바 클래스는 커스텀 액션의 행위를 구현한다. 형태가 아주 단순할 때에는, 단지 커스텀 액션 요소와, 컨테이너에서 태그 핸들러를 불러내기 위해 사용하는 몇 가지 메소드에 응답하는 속성 접근 메소드를 가진 자바빈즈 클래스일 뿐이다. 요소의 바디를 처리하거나 스크립팅 변수에 할당된 객체를 만들 필요가 있는 태그 핸들러를 개발하려면 약간 힘이 들지만, 개발하는 것이 불가능하지는 않다. 하지만 이에 대해 자세히 설명하려면 너무 길어지므로, 자세한 내용은 생략하도록 하겠다. 내가 쓴 책에서 두 챕터에 걸쳐 커스텀 액션 태그 핸들러를 어떻게 개발하는지 자세히 설명하고 있으니 참고하기 바란다. 소프트웨어를 개발할 때 흔히 있는 일이지만, 빈이나 커스텀 액션이 어느 경우에 더 좋은지 정확히 말하기는 어렵다. 내 생각에는 빈은 정보를 훌륭하게 전달할 때, 그리고 커스텀 액션은 정보를 처리할 때에 좋은 것 같다. 커스텀 액션에서는 빈즈를 인풋(input)과 아웃풋(output)으로서 사용할 수 있다. 예를 들어서, 빈을 사용하여 액션으로 형식 인풋을 캡처(capture)한 다음, 커스텀 액션으로 빈의 속성을 데이터베이스에 저장할 수 있다. 이와 반대의 절차도 가능하다. 6. 빈 클래스에 패키지를 사용하기 JSP 페이지에서 사용하기 위해 빈을 개발한다면, 이름을 붙인 패키지의 일부분으로 만들라고 권하고 싶다. 패키지 상태를 사용하지 않는 자바 클래스는 소위 "unnamed package"로 끝나게 된다. 하지만 JSP 페이지에서 생성된 서블릿 클래스는 전형적으로 이름을 붙인 패키지에 할당된다. 만약 이름을 붙이지 않은 패키지에서 클래스를 조회하려 한다면, 사용자가 그것을 임포트하기 위해 임포트문(import statement)을 사용하지 않는 한 자바에서는 클래스를 찾지 못할 것이다. JSP 페이지에서 그것을 사용하기 위해서는 직접적으로 클래스에 임포트한 페이지와 액션을 둘 다 사용해야 한다.

  <%@ page import="UserInfoBean" %>

만약 빈이 이름이 붙여진 패키지에 속해 있다면, 액션만 사용하면 된다.

  
7. 스크립팅 변수와 범주 변수의 혼합 일반적으로 자바 코드를 JSP에 이식하지 않는 것이 좋다. 스크립팅 요소에서의 코드로는 신택스 에러를 쉽게 찾을 수 없고, 페이지 안에 너무 많은 코드가 있으면 웹 애플리케이션을 쉽게 유지할 수 없다. 자바 코드 스크립틀릿(scriptlet)과 JSP 액션 요소를 동시에 사용할 대 어떤 특정 범위에서 혼란이 일어나는 것은 스크립팅 변수와 객체가 JSP 범주 상호작용에서 액션 요소에 의해 어떻게 생성되는가에 달려 있다. 아니면 상호작용이 일어나지 않을 수도 있다. 다음 예제를 살펴보자.

  
  
  
  <%
     userInfo = new com.mycompany.UserInfoBean();
  %>

여기서 UserInfoBean 인스턴스는 페이지 영역(디폴트)에서 액션에 의해 생성되며, firstName 속성은 같은 이름의 요청 파라미터로서 값이 설정된다. 으로도 빈을 범주 변수와 동일한 이름으로 설정하여, 스크립팅 변수로서 사용 가능하게 할 수 있다. 그 다음에, 스크립트렛은 UserInfoBean 클래스를 생성하고, 그것을 userInfo 스크립팅 변수에 할당한다. 마지막으로, 액션은 빈의 firstName 속성 값을 복구한다. 하지만 는 UserInfoBean의 어떤 인스턴스를 사용하는 것인가? 그것은 바로 페이지 영역에서 사용 가능한 인스턴스이다. 액션이나 그에 관련된 어떤 액션 요소이든지, 표준 JSP 영역에 속해있는 유일한 객체인 스크립팅 변수에 접근할 수 없다. 만약 액션이 접근할 수 있는 객체를 만들기 위해 스크립팅 요소를 사용해야 한다면, 그것을 적절한 영역에 배치해야 한다.

      ...
      <%
          userInfo = new com.mycompany.UserInfoBean();
         pageContext.setAttribute("userInfo", userInfo);
      %>
      
    
여기서 수정한 예제에서는, setAttribute() 메소드가 에 의해 생성된 페이지 영역 객체와 스크립트렛에 의해 생성된 인스턴스를 대체하기 위해 사용되었다. 8. 속성을 빈 문자열 데이터 타입 값에 설정하기 이미 알고 있을 지도 모르지만, 빈 속성은 접근자 메소드에 의해 나타난다(기록할 수 있는 속성을 위한 셋터(setter) 메소드와 읽을 수 있는 속성을 위한 겟터(getter) 메소드). 속성의 데이터 타입은 setter 메소드의 단일 인자와 getter 메소드의 리턴 타입이다. 액션은 빈의 속성을 요청 파라미터의 값에 설정하기 위해 종종 사용된다. 요청 파라미터는 문자열로서 전송된다. 그렇다면, 빈의 속성이 String의 타입과 다르다면 어떻게 될 것인가? JSP 컨테이너는 아래의 표와 같이, String 값을 가장 흔히 사용되는 자바 데이터 타입으로 변환해 준다.

만약 속성이 테이블에 없는 타입으로 되어 있다면, 직접 바꾸어서 사용해 보기 바란다. 프로그램을 짜는 사람의 입장에서 가장 편한 방법은 날짜나 RGB 컬러 값과 같이 논리적으로는 다른 타입일 것이 분명한 속성까지도 String 속성을 사용하고, 빈이 그것을 내부적으로 변경하도록 하는 것이다. 하지만 만약 String이나 위에서 목록을 적어놓은 것 이외의 데이터 타입을 사용하기를 원한다면, 페이지 관리자는 속성 값을 설정하기 위해 올바른 데이터 타입의 값을 구하는 요청 시간 속성값을 사용하면 된다.

  " />
이 예제가 품질이 떨어지는Date 컨스트럭터를 사용하고 에러가 잘 나는 경향이 있다는 점에 주의하라. 나는 단지 예를 들기 위해 이것을 사용한 것이다. 여기서 로 설명된 빈 속성 설정 규칙은 커스텀 액션 속성에도 적용된다. 9. 데이터베이스에 접근하기 썬 마이크로시스템(Sun Microsystems)의 JSP-INTEREST 메일링 리스트를 통해 사람들이 제일 자주 묻는 질문 중 하나가 JSP 페이지에서 데이터베이스에 어떻게 접근하는가 이다. 이에 대해 완전히 설명하는 것은 글의 초점에 맞지 않는다. 하지만 이에 대해서는 간략한 가이드라인을 보여주겠다. 자바 프로그램 안에 있는 모든 것과 마찬가지로, JSP는 자바이다. 데이터베이스에 접근할 때에 아마 당신은 JDBC를 사용했을 것이다. 더 자세한 내용을 보려면 the JDBC API here라는 글을 참고하라. 나는 데이터베이스 접근 코드를 JSP 페이지에 직접적으로 집어넣지 말 것을 권한다. 그 대신, 빈이나 커스텀 액션을 만들어서 이 복잡한 작업들을 처리해라. 내가 쓴 JavaServer Page에서는 데이터베이스에 접근하기 위한 커스텀 액션을 몇 개 포함시켰으며, 인터넷에서 구할 수 있는 다른 예제들도 많이 있다(이 기사의 10 번째 항목을 참고할 것). 복잡한 애플리케이션의 경우, 당신은 데이터베이스에 접근할 때 항상 서블릿을 사용하고, 서블릿으로 결과를 렌더링하는 것만을 다루는 JSP 페이지에 요청을 포워드하기를 원할 지도 모른다. 이 모델은 종종 Model-View Controller(MVC) 모델이나 Model 2라고 간주된다. 이에 대한 예도 내 책에 소개하였다. the Apache Struts project에서도 MVC 모델에 대해 좀 더 배울 수 있다. 데이터베이스 접근은 원래 서버 자원의 관점에서 보면, 비용이 많이 든다. 모든 요청 사이의 데이터베이스 커넥션을 효율적으로 공유하기 위해서는 커넥션 풀을 사용해야 한다. 리소스를 절약할 수 있는 또 한 가지 방법은, 데이터베이스 쿼리의 결과를 가능한 많이 캐시(cache)하는 것이다. 대부분의 경우, 데이터베이스 정보는 거의 변하지 않는다. 가령, 제품 카탈로그 정보는 상품이 추가되거나 삭제될 경우나, 가격이 변할 경우에만 바뀐다. 이러한 타입의 정보에서는 데이터를 일단 얻고, 모든 사용자들이 접근할 수 있는 애플리케이션 영역 객체에 저장할 수 있다. 정보가 변하는 일은 거의 없겠지만, 만약 정보에 변화가 있는 경우에는, 애플리케이션 영역 객체만 새로운 버전으로 바꾸어 주면 된다. 그렇게 하면, 사용자는 세션이 시작할 때 데이터를 보이는 그대로 볼 수 있겠지만, 세션이 진행되는 동안 일어나는 변화는 볼 수 없다. 만약 세션 캐시 전략을 사용한다면, 캐시에 할당되는 메모리의 양(세션당 한 카피)은 데이터베이스 접근이 줄어듦으로 인해 얻는 이득만큼 가치가 있다. 그것은 그만한 가치가 있지만, 서버가 피크 사용량을 견딜 수 있을 만큼 메모리가 충분한 지 먼저 확실히 해야 할 것이다. 캐시를 사용하려고 한다면, JDBC ResultSet 객체 자체를 캐시 객체로 사용하지 마라. ResultSet은 특정 연결에 강하게 링크되어 있기 때문에 커넥션 풀링과 충돌을 일으킨다. 따라서 그 대신에 데이터를 ResultSet으로부터 복사하여, 애플리케이션에 있는 빈에 붙여 넣어라. 10. 더 많은 정보를 얻으려면 나는 이 기사가 JSP 기반의 애플리케이션을 개발하는 데에 도움이 되기를 바란다. 하지만 이 강력한 기술을 최대한 효율적으로 활용하기 위해 알아야 할 것들이 더 많이 있다. 나는 물론 내가 쓴 JavaServer Pages를 읽기를 권한다. 인터넷에도 풍부한 정보가 있다. 초보자가 정보를 얻기 가장 좋은 곳은 썬의 JSP 페이지이다. 그 사이트에 가면 JSP 세부 사항을 알 수 있을 것이며, 썬의 직원들이 작성한 기사와 튜토리얼 및 다른 JSP 관련 사이트나 제품에 대한 레퍼런스도 많이 얻을 수 있다.
TAG :
댓글 입력
자료실

최근 본 책0