공유기가 중간에 있으면 원격제거하기 위해 포트포워딩을 해줍니다. 디폴더 원격제거 포트번호 3389
[출처 : http://okjsp.pe.kr/doc/jspCodeConvention.html]
Code Conventions for the JavaServer PagesTM Version 1.x Language

JavaServer PagesTM (JSPTM)가 점점 널리 웹기반 어플리케이션으로 자리잡아가면서, 개발과 유지보수에 종사하는 많은 JSP 프로그래머들과 웹 개발자들은, 예전에 자바프로그래머들이 겪었던 것과 같은 딜레마에 직면했다. "어떻게 JSP 코드를 짜면 읽기, 쓰기, 유지보수하기를 일관적으로 쉽게 할 수 있을까?"

이 글은, 웹 컴포넌트를 사용하는 전형적인 소프트웨어 프로젝트에서 준수해야 할 JSP(1.1 과 1.2버전) 표준 작성요령을 제안한다. 이 문서의 모체는 자바 언어 코드 작성요령이다. (JSP 와 관련된) 코드 작성 방법 스펙에 소개된 다양하고 중요한 요소들을 확인할 수 있는 템플릿으로 삼았다. 특히, 파일명, 구성, 들여쓰기, 주석, 지시자, 선언문, 스크립틀릿, 표현식, 공백, 명명법(이름 짓는 법), 프로그래밍 습관 등을 소개한다. 처음으로 시도되는 JSP 코드 작성요령이기 때문에, 이 권고 문서에 대한 여러분들의 관심과 피드백을 기다린다. 연락처는 jsp-codeconv-comments@sun.com이다.

JSP 2.0 스펙에서는 1.2 버전을 모두 포용하지만, 스크립트가 없는 프로그래밍 스타일(선언문, 스크립틀릿, 표현식이 없는)을 지향하고, 수많은 새 특징들을 갖고 있어서, 이 작성요령을 더 진보시켜야 될 필요가 있다. 가능한 한, 이 문서는 새로운 JSP 2.0 특징에 도움을 주는 작성방식을 선택했다.

마지막으로, 이 글을 읽는 수준은 JSP, Java, 자바 코드 작성요령에 익숙해야 하고, 이미 프로젝트를 수행하는 팀에 적용이 되어 있어야 된다. 그렇지 않다면, 이 링크를 읽어 보기를 추천한다. Java 링크 JSP 링크.

 

왜 코드 작성요령이 필요한가? Why Have Code Conventions?

코드 작성요령은 프로그래머와 웹 컨텐츠 개발자에게 여러 가지 이유로 중요하다:

  1. 소프트웨어 산출물의 가독성을 향상시킨다.
  2. 견습 관리와 노력을 경감한다.
  3. 표준화에 대한 조직적 참여도를 배가시킨다.

파일 이름과 위치 File Names and Locations

파일 이름짓기는 툴 벤더와 웹 컨테이너로 하여금 파일 타입을 인식하고, 각각에 맞게 해석할 수 있는 방법을 제공한다. 다음 테이블은 추천하는 방식의 파일 확장자와 위치 목록이다. 

File Type File 확장자 추천 위치
JSP .jsp <context root>/<subsystem path>/
JSP fragment .jsp <context root>/<subsystem path>/
.jspf <context root>/WEB-INF/jspf/<subsystem path>/
cascading style sheet .css <context root>/css/
javascript .js <context root>/js/
HTML page .html <context root>/<subsystem path>/
web resource .gif, .jpg, etc. <context root>/images/
tag library descriptor .tld <context root>/WEB-INF/tld/

위 테이블을 볼 때 몇 가지 유념할 것이 있다. 우선, <context root> 는 웹 어플리케이션(.war 파일 내의 루트 디렉토리) 컨텍스트의 루트이다. 둘째로, <subsystem path> 는 동적, 정적 웹페이지 컨텐츠의 논리적인 그룹으로 나누도록 한다. 작은 웹 어플리케이션일 경우, 이게 필요하지 않을 수도 있다. (한 곳에 모두 놓아도 된다.)

세째로, 다른 JSP 파일에 포함되는 JSP 를 언급할 때 JSP fragment (JSP 프래그먼트 조각)라는 용어를 사용한다. 주의할 것은 JSP 2.0 에서는 "JSP fragment" 용어가 겹치기 때문에 "JSP 세그먼트(JSP segment)" 용어가 대신 사용된다. JSP fragment 는 확장자로 .jsp 또는 .jspf 를 사용하고, /WEB-INF/jspf나 다른 정적인 컨텐츠와 함께 각각 위치하면 된다. 불완전한 JSP fragment는 언제나 .jspf 확장자를 사용하고, /WEB-INF/jspf에 위치해야 된다. 네째로, JSP 스펙에서는 JSP fragment 확장자로 .jspf.jsp 양쪽 모두를 권장하지만, 추천하는 것은 .jspf 인데, .jsfJavaServer Faces™ 스펙에서 사용될 것이기 때문이다.

끝으로, 태그 라이브러리 디스크립터 파일이나 다른 비공개적인 컨텐츠는 WEB-INF/ 또는 이 하위 디렉토리에 놓아두는 것은 보편적으로 좋은 습관이다. 웹 컨테이너는 WEB-INF/ 아래 있는 어떤 파일도 송출하지 않기 때문에, 이 방법을 사용하면, 클라이언트가 접근하거나 볼 수 없게 된다.

배치 설명자(web.xml)의 welcome-file 엘리먼트에 선언되어 있는 웰컴 파일의 이름은 동적인 컨텐츠를 생성하려면, index.jsp 가 되어야 되고, 정적인 웰컴 페이지라면 index.html 로 해야 한다. 

JSP 파일을 다국어로 작성할 때, 로케일에 따라 디렉토리를 나누어 그룹 짓기를 추천한다. 예를 들어서 index.jsp 의 US English 버전은 /en_US/index.jsp 에, 같은 파일의 일본어판은 /ja_JP/index.jsp  에 있게 한다. 일반적으로 자바 코드의 국제화에 관한 추가적인 정보는 Java Tutorial 에 있고, 웹 어플리케이션의 국제화에 대한 정보는 Designing Enterprise Applications with the J2EETM Platform 책에 나와있다.

파일 구조 File Organization

구조가 잘 갖춰진 소스 코드 파일은 읽기 쉽고, 파일 사이에 정보를 더 빨리 놓아둘 수 있다. 이 섹션에서, JSP와 태그 라이브러리 디스크립터 파일 모두를 위한 구조를 소개하려한다.

JSP 파일 / JSP Fragment 파일

JSP 파일은 다음의 섹션들이 나열한 순서대로 구성된다:

  1. 도입 주석
  2. JSP 페이지 지시자(directive)
  3. 선택적 태그 라이브러리 지시자
  4. 선택적 JSP 선언문
  5. HTML 과 JSP 코드

도입 주석 Opening Comments

JSP 파일 또는 프래그먼트 파일은 서버측 주석으로 시작한다:

<%--
  - 작성자:
  - 일자:
  - 저작권 표시:
  - @(#)
  - 설명:
  --%>

이 주석은 서버측에서만 볼 수 있다. JSP 변환 과정에서 제거되기 때문이다. 여기에는 웹 개발자를 위한 JSP에 대한 작성자, 일자, 개정판의 저작권, 증명과 설명이 포함된다. "@(#) " 문자 조합은 증명의 시작을 지시하는 것으로 어떤 프로그램에서 인식된다. 이런 표식을 사용하는 프로그램들은 많이 사용되지 않지만, 있어도 해가 될 것은 없다. 추가로, 이 조합은 때때로 "$Id$" 를 덧붙여서, 어떤 종류의 버전 관리 프로그램에서는 자동으로 증명 정보를 JSP에 기록하도록 한다. 설명 부분은 JSP의 목적에 관한 축약된 정보를 제공한다. 한 문단 이상 넘어가지 않도록 한다.

어떤 상황에서는, 도입 주석이 JSP 변환과정을 지나서 출처가 확실하다는 뜻과 법적인 목적으로 클라이언트의 브라우저에 보여질 필요가 있다. 이럴 경우 주석 부분을 두개로 나눠서 우선 클라이언트측 스타일의 주석을 단다:

<!--
  - Author(s):
  - Date:
  - Copyright Notice:
  --%>

그리고 나서 짧아진 나머지 서버측 스타일의 주석을 단다:

<%--
  - @(#)
  - Description:
  --%>

JSP 페이지 지시자 JSP Page Directive(s)

JSP 페이지 지시자는 JSP 변환 시점에 관련된 속성을 정의한다. JSP 스펙은 같은 페이지에 JSP 페이지 지시자의 수량을 제한하지 않는다. 그래서 다음 두 개의 코드 예제는 똑같다.(다른 점이 있는데, 첫 예제는 출력시에 맨 위에 두 개의 빈 줄이 포함된다):

코드 예제 1:

<%@ page session="false" %>
<%@ page import="java.util.*" %>
<%@ page errorPage="/common/errorPage.jsp" %>

페이지 지시자 같이 지시자의 길이가 일반적인 JSP 폭(80 문자)을 넘어갈 경우 여러 줄로 나눠진다:

코드 예제 2:

<%@ page    session="false"
            import="java.util.*"
            errorPage="/common/errorPage.jsp"
%>

일반적으로, 코드 예제 2는 코드 예제 1보다 즐겨 사용된다. 예외가 있는데, 매우 긴 import 속성을 쓰게 되는, 여러 개의 자바 패키지가 JSP페이지에서 필요할 경우이다:

<%@ page    session="false"
               import="java.util.*,java.text.*,
                   com.mycorp.myapp.taglib.*,
                       com.mycorp.myapp.sql.*, ..."
...
%>

이런 경우, 페이지 지시자를 다음과 같이 나누는 것이 즐겨사용된다:

<%-- import를 제외한 모든 속성 --%>
<%@ page
...
%>
<%-- import 속성들은 여기부터 시작 --%>
<%@ page import="java.util.*" %>
<%@ page import="java.text.*" %>
...

주의할 점은, 일반적으로 import문은 자바의 지역 코드 작성요령을 준수한다. 예를 들어, 같은 패키지에서 세 개의 클래스까지만 사용되면, import 는 패키지를 선언하기 보다는, 클래스를 일일이 선언한다. 세 개를 넘어가면, 클래스를 일일이 지정할 것인지 아니면, ".*" 표시를 사용할 것인지는 웹 개발자 마음이다. 전자의 경우, 외부 클래스가 무엇이 사용되는지를 쉽게 알수 있다. 특히 버그가 많은 클래스를 위치시키려고 하거나, JSP 가 자바 코드와 어떻게 상호작용하는지를 이해하려할 경우에 유용하다. 예를 들어서, 아래처럼 임포트한 자바 패키지들에 관해 알지 못하면, 웹 개발자는 Customer 클래스를 찾기 위해서 이 모든 패키지들을 검색해봐야 된다:

<%@ page import="com.mycorp.bank.savings.*" %>
<%@ page import="com.thirdpartycorp.cashmanagement.*" %>
<%@ page import="com.mycorp.bank.foreignexchange.*" %>
...

후자의 경우, 작성된 JSP 가 깔끔하지만, 사용한 클래스가 무엇인지 아는 것은 어려워진다. 일반적으로, JSP 에 임포트 지시자가 너무 많으면, 자바 코드가 너무 많이 들어가 있기 쉽다. 보다 나은 선택은 JSP 태그를 더 사용하는 것이다.(후에 언급하겠다).

선택적 태그 라이브러리 지시자 Optional Tag Library Directive(s)

태그 라이브러리 지시자는 JSP 에서 사용하는 커스텀 태그 라이브러리를 선언한다. 짧은 지시자는 하나의 줄에 선언할 수 있다. 여러 개의 태그 라이브러리 지시자들은 JSP 의 한 곳에 같이 모아둔다:

<%@ taglib uri="URI1" prefix="tagPrefix1" %>
<%@ taglib uri="URI2" prefix="tagPrefix2" %>
...

페이지 지시자처럼, 태그 라이브러리 지시자의 길이가 일반적인 JSP 폭(80 문자)을 넘어갈 경우 여러 줄로 나눠진다:

<%@ taglib
    uri="URI2"
    prefix="tagPrefix2"
%>

오직 페이지에서 사용하는 태그 라이브러리만 임포트되어야 한다.

JSP 1.2 부터, 웹 어플리케이션의 JSP에 JSTL;JSP 표준 태그 라이브러리사용하기를 적극 권장한다. 페이지에서 JSP 스크립틀릿 사용할 필요를 줄여주기 때문이다. 일반적으로, JSTL을 사용하는 페이지는 소스읽기와 유지보수가 보다 편하다.

선택적 JSP 선언문 Optional JSP Declaration(s)

JSP 선언문은 JSP에서 소속된 메소드와 변수를 선언한다. 이들 메소드와 변수는 자바 프로그램에서의 선언문과 다를바 없다. 그래서 적절한 코드 작성요령을 따르기만 하면 된다. 선언문은 <%! ... %> JSP 선언문 블록 하나에 모두 몰아서 한 곳에 집중해 놓는 것이 좋다. 예를 들면:

다른 선언문 블록들 바람직한 선언문 블록
    <%! private int hitCount; %>
    <%! private Date today; %>
    ...
    <%! public int getHitCount() {
            return hitCount;
        }
    %>
    
    <%!
        private int hitCount;
        private Date today;

        public int getHitCount() {
            return hitCount;
        }
    %>

HTML 과 JSP 코드 HTML and JSP Code

이 섹션은 JSP 코드와 HTML 이 섞여있는 페이지에 대해 설명한다. JSP 표현식, 스크립틀릿, 자바빈즈 지시자 등이 해당된다.

태그 라이브러리 설명서 Tag Library Descriptor

태그 라이브러리 설명서(TLD)는 적당한 XML선언과 올바른 DTD 문으로 시작한다. 예를 들어서, JSP 1.2 TLD 는 다음과 같이 시작해야 된다:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

바로 뒤이어 서버측 주석과 같은 스타일의 주석을 기입한다. 작성자, 일자, 저작권, 증명 정보, 라이브러리에 대한 짧은 설명이다:

<!--
  - 작성자:
  - 일자:
  - 저작권 표시:
  - @(#)
  - 설명:
  -->
 

여기에 관한 규칙과 가이드라인은 JSP 파일/프래그먼트 파일에서 정의된 것과 동일하다.

태그 라이브러리 파일의 나머지는 다음에 나오는 순서대로 이루어져 있다:

  • 태그 라이브러리 검증기 하나의 선택적 선언문
  • 이벤트 리스너들의 선택적 선언문
  • 하나 이상의 가능한 태그들의 선언

TLD 에서 엘리먼트 마지막에 선택적 서브 엘리먼트를 항상 덧붙이기를 추천한다. 이 선택적 서브 엘리먼트는 태그 디자이너가 TLD의 습성과 추가적인 정보를 문서화할 장소를 제공한다. 이것은 웹 컴포넌트 개발자들에게는 보이지 않게 된다.

TLD Element JSP 1.2 Recommended
Sub-element
JSP 1.1 Recommended
Sub-element
attribute (JSP 1.2) description  
init-param (JSP 1.2) description  
tag display-name, description, example name, info
taglib uri, display-name, description uri, info
validator (JSP 1.2) description  
variable (JSP 1.2) description  

들여쓰기 Indentation

들여쓰기는 반드시 공백문자로 채워야한다. 탭은 사용하는 에디터마다 표시하는 공백의 수가 다르기 때문에 JSP 내에서 들여쓰기로 사용하기에는 적합하지 않다. 특별히 통합개발환경(IDE) 툴에서 제한되지 않았다면, 기본 들여쓰기 단위는 4개의 공백문자로 한다. 다음은 그 사용예이다:

<myTagLib:forEach var="client" items="${clients}">
    <myTagLib:mail value="${client}" />
</myTagLib:forEach>

연속 들여쓰기는 이전 줄의 적당한 지점에서 시작한다. 연속 들여쓰기의 공백은 기본 들여쓰기 공백의 배수이다. (4개의 공백문자의 배수):

<%@ page    attribute1="value1"
            attribute2="value2"
            ...
            attributeN="valueN"
%>

스크립트 엘리먼트의 들여쓰기 Indentation of Scripting Elements

JSP 스크립트 엘리먼트(선언문, 스크립틀릿, 표현식과 같은)가 한 줄에 맞지 않는 경우, 스크립트 언어의 들여쓰기 작성요령이 엘리먼트 내에 적용된다. 몸체는 엘리먼트의 여는 기호 <%=와 같은 줄에서 시작한다. 다음줄의 기준도 여는 기호 <%=가 된다. 몸체의 끝은 다른 줄에 있는 엘리먼트의 닫는 기호(%>)로 마무리된다. 예제이다:

<%= (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
        = Calendar.SUNDAY) ?
    "Sleep in" :
    "Go to work"
%>

첫줄과 마지막 줄을 제외한 몸체의 가운데 줄들은 단위 들여쓰기 공백(앞 예제에서  로 보인다)이 앞에 있기 때문에 나머지 JSP 보다 확연히 드러나게 된다.

JSP, HTML, JAVA의 복합 들여쓰기 Compound Indentation with JSP, HTML and Java

자바의 스크립트 코드와 템플릿 텍스트(HTML)가 섞여있는 JSP 엘리먼트를 위해서 복합 들여쓰기가 필요한 이유는 JSP 소스를 이해하기 위해 드는 노력을 덜어주기 위해서이다. 기존의 들여쓰기 요령은 JSP 소스파일을 더 알아보기 어렵게 할 수 있기 때문이다. 보편적인 규칙에 따라서, 다른 소스 사이에 끼어 있을 경우마다 보통 들여쓰기에 특별한 단위를 할당한다. 클라이언트에 보여지는 들여쓰기에 영향을 준다. 추가적인 들여쓰기는 (브라우저에 의해) 보통 무시되고, 브라우저에 나타나는 결과는 차이가 없다. 예를 들어, <TABLE> 태그 앞에 공백이 몇 개 더 추가된다고 해서, 브라우저에서 보여지는 테이블의 위치가 달라지는 것은 아니다. 그래서 이러한 작성요령을 사용하는 것이 더 좋아 보인다:

    <table>
        <%  if { tableHeaderRequired ) { %>
            <tr>
                <th>Last Name</th>
                <th>First Name</th>
            </tr>
        <%  } %>
        <c:forEach var="customer" items="${customers}">
            <tr>
                <td><c:out value="${customer.lastName}"/></td>
                <td><c:out value="${customer.firstName}"/></td>
            </tr>
        </c:forEach>
    </table>

위 방식보다는 아래 방식이 낫다:

    <table>
        <%  if { tableHeaderRequired ) { %>
        <tr>
            <th>Last Name</th>
            <th>First Name</th>
        </tr>
        <% } %>
        <c:forEach var="customer" items="${customers}">
        <tr>
            <td><c:out value="${customer.lastName}"/></td>
            <td><c:out value="${customer.firstName}"/></td>
        </tr>
        </c:forEach>
    </table>

주석 Comments

주석은 추가 정보 전달이나 코드를 둘러쌀 목적으로 사용한다. 여기서는 JSP에서 사용하는 두가지 형태의 주석을 볼 것이다: JSP 측과 클라이언트 측 주석이다.

JSP 주석 JSP Comments

JSP 주석(또는 서버 측 주석)은 서버에서만 볼 수 있다(즉, 클라이언트로 전파되지 않는다). 순수 JSP 주석을 스크립팅 언어와 섞여있는 JSP 주석보다 즐겨 사용한다. 전자가 기반 스크립트 언어에 덜 종속적이기 때문이고, JSP 2.0 으로 진화하기 더 쉽기 때문이다. 다음 도표는 이것을 설명한다:

Line JSP 스크립틀릿과 스크립트 언어 주석 순수 JSP 주석
한 줄
    <% /**  ...  */ %>
    <% /*  ...  */ %>
    <% //  ...  %>
    
    <%--  ...  --%>
    
여러 줄
    <%
    /*
     *
    ...
     *
     */
    %>
    
    <%--
      -
     ...
      -
      -- %>
    <%
    //
    //
    ...
    //
    %>
    

클라이언트 측 주석 Client Side Comments

클라이언트 측 주석(<!-- ... -->)은 응답에 대해서 부가적인 정보를 클라이언트로 보내기 위해 응답에 주석을 다는 것이다. 서버 어플리케이션의 행동이나 내부적인 구조에 대한 것과 응답 내용을 생성하는 코드가 포함되어서는 안된다.

클라이언트/사용자의 입장에서 서버에서 보내온 응답을 해석하기 위해서 이런 종류의 주석을 직접 볼 필요가 없기 때문에, 클라이언트 측 주석은 보편적으로 사용되지 않는다. 앞서 얘기한대로 증명이나, 저작권 정보 등의 법적인 목적의 증명에 사용될 경우는 예외가 되겠다. 다른 예외적인 경우는 HTML 저작도구들이 HTML 문서 구조의 틀을 잡아주기 위해 작은 양의 HTML 주석들을 사용하는 경우가 되겠다. 예를 들면 다음과 같다:

<!-- 툴바 섹션 -->
    ...
<!-- 왼쪽 네비게이션 메뉴 -->
    ...
<!-- main body -->
    ...
<!-- footer -->
    ...

여러 줄의 주석 Multiline Comment Block

JSP 또는 클라이언트 측 모두, 여러 줄의 주석은 대시 문자 "-" 로 꾸며진다. XML 스펙에서, 더블-대시 문자열 "--"은 XML 주석 문 내에 허용되지 않는다. 그러므로, 이 스펙에 호환성과 일관성을 갖기 위해서, 더블-대시 문자열은 여러줄의 주석문 내에 사용되면 안된다. 다음 도표는 클라이언트 측 여러 줄 주석 사용법을 보여준다:

권장 XML과 맞지 않는 법
    <!--
      - line 1
      - line 2
    ...
      -->
    
    <!--
      -- line 1
      -- line 2
    ...
      -->
    

JSP 선언문 JSP Declarations

자바 코드 작성요령에 따라서, 같은 타입의 변수 선언문은 다른 줄에서 이루어져야 된다:

비 추천 추천
    <%! private int x, y; %>
    
    <%! private int x; %>
    <%! private int y; %>
    

자바빈즈는 <jsp:useBean> 액션 태그를 사용하는 대신, JSP 선언문을 통해서 선언되고 인스턴스화되면 안된다.

일반적으로, 변수를 위한 JSP 선언문은 그 자체가 스크립트 언어의 사용을 부추기기 때문에 사용하지 않는 것이 좋다. JSP 는 프리젠테이션 목적으로 설계되었는데, 여기에 비지니스 로직과 자바코드를 섞어놓기 때문이다. 그리고, 변수의 스코프 관리에 따른 부담도 생기게 된다.

JSP 스크립틀릿 JSP Scriptlets

가급적, 태그라이브러리로 동일한 기능을 구현할 수 있으면, JSP 스클립틀릿은 피하기 바란다. 이렇게 하면 페이지 가독성도 높이고, 유지보수도 쉬워진다. 비지니스 로직을 프리진테이션 로직에서 분리하는데 일조하며, JSP 2.0 스타일의 페이지로 쉽게 옮겨갈 수 있다(JSP 2.0 도 스크립틀릿을 지원하지만 덜 강조한다). 다음 예제는 customers 표시 방법이 데이터 형에 따라서 다른 스크립틀릿을 사용하는 것을 보여준다:

customers 는 Customers의 배열

    <table>
        <%  for ( int i=0; i<customers.length; i++ ) { %>
            <tr>
                <td><%= customers[i].getLastName() %></td>
                <td><%= customers[i].getFirstName() %></td>
            </tr>
        <%  } %>
    </table>

customers 는 Enumeration

    <table>
        <%  for ( Enumeration e = customers.elements();
                e.hasMoreElements(); ) {
                Customer customer = (Customer)e.nextElement();
        %>
            <tr>
                <td><%= customer.getLastName() %></td>
                <td><%= customer.getFirstName() %></td>
            </tr>
        <%  } %>
    </table>

그러나, 공통 태그라이브러리를 사용하면, customers 의 데이터 형이 달라도 보다 높은 유연성이 생긴다. 예를 들어서, JSTL 에서는customers 를 나타내기 위해서 배열과 Enumeration 양쪽 모두 지원하는 다음과 같은 코드로 표시할 수 있다:

    <table>
        <c:forEach var="customer" items="${customers}">
            <tr>
                <td><c:out value="${customer.lastName}"/></td>
                <td><c:out value="${customer.firstName}"/></td>
            </tr>
        </c:forEach>
    </table>

비지니스 로직과 프리젠테이션 층과의 결합도을 줄이기 위한 모델-뷰-컨트롤러(MVC) 디자인 패턴을 적용하려고 한다면, JSP 스크립틀릿은 비지니스 로직을 구현하기 위해서 사용되어서는 안된다. 오히려, JSP 스크립틀릿은 클라이언트의 요청을 처리한 뒤에 나오는 데이터("가치 객체 value objects"로 불리는)를 적절한 클라이언트의 준비된 형식에 전송하기 위해 필요하다면 사용된다. 심지어 그럴 경우에도, 프론트 컨트롤러 서블릿이나 커스텀 태그로 하는 게 더 낫다. 예를 들면, 다음의 코드는 데이터베이스에서 직접 customers 의 이름을 가져와 클라이언트에 보여준다:

<%
    // 스크립틀릿을 이용하는 것은 추천하지 않음!

    Connection conn = null;
    try {
        // Get connection
        InitialContext ctx = new InitialContext();
        DataSource ds = (DataSource)ctx.lookup("customerDS");
        conn = ds.getConnection();

        // Get customer names
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT name FROM customer");

        // Display names
        while ( rs.next() ) {
            out.println( rs.getString("name") + "<br>");
        }
    } catch (SQLException e) {
        out.println("Could not retrieve customer names:" + e);
    } finally {
        if ( conn != null )
            conn.close();
    }
%>

다음의 JSP 코드는 데이터베이스 처리부분을 내부적으로 데이터베이스 코드의 의존성을 숨기고 캡슐화한 커스텀태그myTags:dataSource에 위임하고 있기 때문에 더 보기 좋다:

<myTags:dataSource
    name="customerDS"
    table="customer"
    columns="name"
    var="result" />
<c:forEach var="row" items="${result.rows}">
    <c:out value="${row.name}" />
    <br />
</c:forEach>

result 는 커스텀 태그 myTags:dataSource가 customer 데이터베이스에서 customer의 이름들 뽑아낸 결과를 담으려고 만들어 낸 스크립팅 변수이다. JSP 코드는 클라이언트의 요구에 따라 동적으로 다른 종류의 출력(HTML, XML, WML)을 생성하기 위해 확장할 수 있다. 그것도 백엔드 코드(dataSource 태그를 위한)를 변경하지 않고도 가능하다. 더 나은 선택은 이것을 프론트 컨트롤러 서블릿에 위임하는 것이다. 이 서블릿은 데이터를 불러와서 그 결과를 request 스코프의 속성을 통해서 JSP에 제공한다. 예제는 자바 BluePrint의 엔터프라이즈 섹션을 참고하기 바란다.

요약하면 :

  • 이상적으로, 스크립트 언어로부터 독립하기 위해서 JSP 스크립틀릿은 JSP 내에서 사라져야 되고, JSP 내의 비지니스 로직 구현은 피해야 된다.
  • 그것이 불가하면, 서버 측에서 정보를 전달하는데 값 객체(자바빈즈)를 사용하고, JSP 스크립틀릿은 클라이언트 출력에 이 값 객체를 전달하는 데 사용한다.
  • 가능하면 커스텀 태그(태그 핸들러)를 사용해서 서버측에서 정보를 처리하도록 한다.

JSP 표현식 JSP Expressions

JSP 표현식은 JSP 스크립틀릿처럼 가능하면 삼가는 것이 좋다. 동일한 작업을 수행하는 다음 3가지 예제가 잘 설명해준다:

Example 1 (자바 코드 사용):

    <%= myBean.getName() %>

Example 2 (JSP 태그 사용):

    <jsp:getProperty name="myBean" property="name" />

Example 3 (JSTL 태그 사용):

    <c:out value="${myBean.name}" />

Example 1 은 myBean 이라는 스크립트 변수가 선언된 것이라고 가정한다. 다른 두 예제에서 myBean은 어떤 스코프의 속성으로 PageContext.findAttribute()을 통해서 접근할 수 있다고 가정한다. 두 번째 예제는 또한 <jsp:useBean> 에 의해서 페이지에 소개된 myBean이라고 가정한다.

셋 중에서 JSTL 태그 예제가 낫다. JSP 표현식 만큼 짧고, 가독성도 좋고, 유지보수하기도 보다 편하다. 그리고, 자바 스크립틀릿에 의존하지 않는다(스크립틀릿은 자바 언어와 API 함수들에 익숙해야 다룰 수 있다.) 더우기, JSP 2.0 스타일 프로그램으로 쉽게 이동할 수 있는 장점이 있다. 어떤 선택이 채택되든, 모든 개발자들의 동의를 얻어서, 같은 프로젝트에 있는 JSP 결과물들은 전체적으로 일관된 방식을 사용해야 된다.  주의할 점은, JSTL 예제는 로컬 자바 스크립트 변수로부터가 아닌, page 컨텍스트로부터 myBean 의 값을 가져온다는 것이 실질적으로 조금 다른 점이다.

마지막으로, JSP 표현식은 기반 스크립팅 언어의 문법에 의존하는, 같은 기능의 JSP 스크립틀릿보다 낫다. 예를 들어,

    <%= x %>

로 하는 게 아래 방법보다 좋다.

    <% out.print( x ); %>

공백 White Space

코드의 이해와 유지보수에 드는 노력을 줄이기 위해서 JSP 코드를 보기 좋게 들여쓰기하는데 공백을 사용한다. 특히, 빈 줄과 공백은 필요한 곳에서 JSP 의 여러 곳에 삽입되어야 한다.

빈 줄 Blank Lines

빈 줄은 출력에 원하지 않는 영향만 주지 않는다면 JSP의 가독성을 높이기 위해서 가끔 사용된다. 아래 예제에서, HTML <PRE> 블록 내에 두 개의 JSP 표현식 사이에 있는 빈 줄은 클라이언트의 브라우저에 HTML 출력 결과에서 한줄 더 생기게 만든다. 그러나 빈 줄이 <PRE> 블록 사이에 있지 않다면, 브라우저의 출력에 나타나지 않는다.

JSP 구문 클라이언트의 HTML 출력
    <pre>
    <%= customer.getFirstName() %>
    <%= customer.getLastName() %>
    </pre>
    
    Joe
    Block
    
    <pre>
    <%= customer.getFirstName() %>

    <%= customer.getLastName() %>
    </pre>
    
    Joe

    Block
    
    <%= customer.getFirstName() %>

    <%= customer.getLastName() %>
    
    Joe Block
    

빈 문자열 Blank Spaces

공백 문자( ; 역자주:공백은 길이가 1, 빈 문자열은 ""처럼 길이가 0)는 JSP 태그와 몸체 사이에 쓰여져야 된다. 예를 들어 다음

    <%= customer.getName() %>

코드는 아래 것보다 좋다.

    <%=customer.getName()%>

JSP 주석 태그와 주석을 분리하는 공백 문자들이 있어야 된다:

<%--
  - 여러 줄의 주석이 나뉘어지면, 각각의 줄은
  - 한 줄씩 차지한다.
  --%>
<%-- 짧은 주석 --%>

이름짓는 요령 Naming Conventions

이름짓는 요령을 적용하면, 프로젝트 내의 웹 컴포넌트 엘리먼트들을 쉽게 확인, 분류, 통합할 수 있다. 이 섹션에서, JSP에 적합한 요령을 살펴보자.

JSP 이름 JSP Names

JSP (파일) 이름은 언제나 소문자로 시작해야 한다. 여러 개의 단어로 구성될 수 있고, 이러한 경우 단어들은 모두 붙여쓰고, 이어지는 단어의 첫글자는 대문자로 쓴다. JSP 이름은 간단한 명사나 짧은 문장이 될 수 있다. 동사뿐인 JSP 명은 개발자에게 충분한 의미를 전달하지 못하기 때문에, 피하도록 한다. 예를 들면:

    perform.jsp

파일은 다음 파일보다 명확하지 않다.

    performLogin.jsp

JSP 이름에 동사가 들어가는 경우, 은연 중에 뒷쪽에서 처리하고 있다는 표시로 현재형을 쓴다:

    showAccountDetails.jsp

로 쓰는 게 아래 파일명 보다 좋다

    showingAccountDetails.jsp

태그 이름 Tag Names

태그 핸들러와 관련된 클래스에 대한 이름짓는 요령은 다음과 같다:

설명 클래스 명
XXX tag extra info (javax.servlet.jsp.tagext.TagExtraInfo를 상속함) XXXTEI
XXX tag library validator (javax.servlet.jsp.tagext.TagLibraryValidator를 상속함) XXXTLV
XXX tag handler interface (javax.servlet.jsp.tagext.Tag/IterationTag/BodyTag를 상속함) XXXTag
XXX tag handler implementation XXXTag

덧붙여, 태그 이름은 자바에 관련된 코드작성법에서 정한 클래스와 인터페이스 이름 짓는 요령을 따라서 정해야 된다.

태그 관련된 클래스와 다른 클래스와 더 분명하게 구분하기 위해서, 패키지 접미사(tags 또는 taglib)를 클래스의 패키지명에 적용한다. 예를 들면:

    com.mycorp.myapp.tags.XXXTag

태그 prefix 이름 Tag Prefix Names

태그 prefix 는 짧지만 의미가 담긴 명사를 사용해야 하고, 첫문자는 소문자로 해야한다. 태그 prefix 는 알파벳 문자만 포함해야 된다. 몇 가지 예를 들면 다음과 같다:

OK?
mytaglib no
myTagLib yes
MyTagLib no
MyTagLib1 no
My_Tag_Lib no
My$Tag$Lib no

XML 문법의 JSP 페이지 JSP Pages in XML Syntax

JSP 는 두가지 다른 문법을 제공한다: JSP 페이지를 작성하는 '표준 문법'과 JSP를 XML문서로 작성하는 'XML 문법'이 있다. 표준 문법을 사용하는 JSP 페이지들이 'JSP 페이지'로 많이 쓰인다. XML 문법으로 작성된 JSP는 'JSP 문서'라고 일컫는다. 이 기사는 우선적으로 JSP 페이지를 소개하지만, 많은 부분의 개념들이 JSP 문서에도 역시 적용될 수 있다. XML이 좀 더 유행하면 JSP 문서 사용도 많아질 것이라고 기대된다. JSP 2.0 스펙에서는 더 많은 XML 문법 친화적인 것들이 소개될 것이다.

주의할 것은 XML 문법을 이용해서 JSP를 만드는 것과 JSP로 XML 결과(view)를 만들어 내는 것과 혼동해서는 안된다. 페이지 제작도구는 JSP를 만들기 위해 표준 또는 XML 문법을 사용한다. 그리고 나서 컨테이너는 JSP를 XML 결과로 변환하고, 이것은 태그 라이브러리 밸리데이터(유효성 검증기)에서 확인한다.

JSP 문서 구조 JSP Document Structure

JSP 문서는 다음과 같은 기본 구조를 갖는다:

    <? xml version="1.0" ?>
    <!--
      - Author(s):
      - Date:
      - Copyright Notice:
      - @(#)
      - Description:
      -->
    <jsp:root   xmlns:jsp="http://java.sun.com/JSP/Page"
                xmlns:prefix1="URI-for-taglib1"
                xmlns:prefix2="URI-for-taglib2"
                version="1.2">
        JSP Document ...
    </jsp:root>

첫 줄은, 이 페이지가 XML 문서라고 정의하는 선택적 XML 프롤로그이다. 다음으로 오는 것은 문서에 대한 주석이다. <jsp:root> 엘리먼트는 이것이 JSP 문서임을 정의하고, 반드시 이 엘리먼트가 루트 엘리먼트로 나타나야 된다. jsp 이름공간은 반드시 가져와야 되고, 모든 태그 라이브러리는 이 루트 엘리먼트를 사용해서 가져와야 된다. 버전 속성이 필요하고, 어떤 버전을 사용할 지를 정해준다. JSP 문서의 실제 내용은 <jsp:root> 엘리먼트의 서브엘리먼트에 나타난다. 표준 XML 들여쓰기 룰에 따라서 문서 전체적으로 일관되게 적용하는 들여쓰기 단위는 4개의 공백문자이다.

JSP 문서는 형식이 잘 갖춰진 XML 문서이기 때문에, <% %> 와 같은 엘리먼트는 <jsp:scriptlet /> 같이 XML로 바꿔서 표기해야 된다. 자세한 것은 JSP 스펙을 보기 바란다.

XML 주석 XML Comments

JSP 스펙은 XML 스타일의 주석을 어떻게 처리할 지에 대해 불분명하기 때문에, 주석이 클라이언트로 안전하게 전달되기 위해서는, <jsp:text>로 다음과 같이 감싸주어야 된다:

    ...
    <jsp:text><![CDATA[
        <!--
          - Multiline comment
          - to be sent to client.
          -->
    ]]></jsp:text>
    ...

JSP 문서 내의 자바 코드 Java Code in JSP Documents

선언문, 스크립틀릿, 표현식에서 자바 코드를 쓰려고 할 때, 코드가 문서의 구조를 망가뜨리지 않는다면, CDATA엘리먼트를 사용한다.

    ...
    <jsp:scriptlet>
        for( int level = 0; level < 3; level++ ) {
    </jsp:scriptlet>
    <tr>
        <td>
            <jsp:expression><![CDATA[
                "<h" + level + ">Text</h" + level + ">"
            ]]></jsp:expression>
        </td>
    </tr>
    <jsp:scriptlet>
        }
    </jsp:scriptlet>
    ...

표준 문법에서와 다르게, XML 들여쓰기 법칙은 엘리먼트의 내용과 상관없이 준수되어야 한다.

프로그래밍 습관 Programming Practices

일반적으로, 다음과 같은 이유로 JSP 페이지에 자바 코드(선언문, 스크립틀릿, 표현식) 쓰기를 피한다:

  • JSP 페이지의 자바 코드 문법 에러는 페이지가 배치되기 전까지 발견되지 않는다. 반면에, 태그 라이브러리와 서블릿에 있는 문법 에러는 배치 이전에 발견된다.
  • JSP 페이지 내의 자바 코드는 디버깅하기 힘들다.
  • JSP 페이지 내의 자바 코드는 유지보수하기 힘들다. 특히 자바 전문가가 아니라면 더욱 더 그렇다.
  • 보통 복잡한 비즈니스 로직과 프리젠테이션 로직을 섞어놓지 않는 것이 납득이 가는 습관이다. JSP는 우선적으로 프리젠테이션 로직을 목적으로 한다.
  • 자바 코드가 포함된 코드와 HTML과 다른 스크립트 명령어가 섞여있다면 더 읽기 어려워진다.
  • JSP 2.0 은 더 단순한 표현 언어를 덕분에 스크립틀릿의 중요도를 낮추었다. JSP 에서 JSP 2.0 스타일로 쉽게 옮기기 위해서는 페이지에서 자바 코드를 사용하지 않는 것이 좋다.

자바 BluePrints의 엔터프라이즈 섹션에 있는 더 자세한 정보와 가이드라인을 참고하기 바란다.

자바빈즈 초기화 JavaBeans Initialization

JSP는 JavaBeanTM의 프로퍼티로 증명되는 모든 PropertyDescriptor를 초기화하기 위해 편리한 엘리먼트를 제공한다. 예를 들면:

    <jsp:setProperty name="bankClient" property="*"/>

그러나, 주의해서 사용해야 한다. 첫째로, 빈이 프로퍼티를 갖고 있다, 즉 amount 프로퍼티가 있다고 하고, 현재 ServletRequest 객체에 파라미터(amount)가 없거나 파라미터 값이 ""라고 한다면, 아무것도 하지 않는다: JSP는 빈의 특정 프로퍼티에 null를 할당하지도 않는다. bankClient 빈에 amount의 기존 값이 무엇이든지 간에 영향을 받지 않는다. 둘째로, PropertyEditors를 선언하지 않은 비-요소적인 프로퍼티는 ServletRequest 객체의 스트링 값에서 암묵적으로 초기화 되지 않기 때문에, 명시적인 변환이 필요하다. 세째로, 만일 어플리케이션이 주의 깊게 설계되지 않았다면, 악의를 가진 사용자는 추가적으로 요청 파라미터를 덧붙여서 빈의 사용하지 말아야할 프로퍼티 값을 설정할 수 있다.

깔끔한 코드를 위해서 jsp:setProperty 태그에 property="*"를 사용한다면, jsp:setProperty 태그 이전에 ServletRequest 객체를 통해서 받아와야 할, 빈을 초기화하기 위한 파라미터들에 대해 주석을 달기를 권한다. 다음 예제에서는 bankClient 빈을 초기화하기 위해서 firstNamelastName 파라미터가 필요하다는 것을 주석에서 읽을 수 있다:

<%--
  -  ServletRequest에서 firstName와 lastName를 요구
  --%>
<jsp:setProperty name="bankClient" property="*" />

JSP 내장 객체 JSP Implicit Objects

API 호출을 통해서 객체를 참조하기 보다는 JSP 내장 객체를 직접 사용하는 것이 좋다. 그래서 ServletContext 인스턴스에 의해 제공되는 초기 파라미터에 접근하기 위해 다음을 쓰는 대신에

    getServletConfig().getServletContext().getInitParameter("param")

기존 내장 객체를 사용하는 게 낫다:

    application.getInitParameter("param")

초기 파라미터 값이 출력이 되어야 하는 경우에는, JSTL로 접근해서 출력하는 것이 더 좋다:

    <c:out value="${initParam['param']}" />

인용부호 Quoting

일정한 인용부호의 사용을 채택한다. 인용부호는 작은따옴표 ' 대신 큰따옴표 " 를 사용한다.

불규칙한 인용부호 좋은 인용부호 사용
<%@ page import='javabeans.*'%>
<%@ page import="java.util.*" %>
<%@ page import="javabeans.*" %>
<%@ page import="java.util.*" %>

예외적인 경우는 작은따옴표가 필요할 때이다. 예를 들어서 스크립트 언어에서 큰따옴표를 사용하는 경우를 들 수 있다:

    <jsp:include page='<%= getFoodMenuBar("Monday") %>' />

커스텀 태그 사용하기 Using Custom Tags

만일 커스텀태그가 몸체가 없다면, 컨텐트는 (생략해서 "JSP" 기본값으로 하지 말고) empty라고 명확하게 선언되어야 한다. 태그 라이브러리 설명서에서 다음과 같이 정해준다:

<tag>
    <name>hello</name>
    <tag-class>com.mycorp.util.taglib.HelloTagSupport</tag-class>
    <body-content>empty</body-content>
    ...
</tag>

이렇게 하면 JSP 컨테이너는 태그 몸체가 무조건 비어야 되며 파싱할 어떤 JSP 문법도 포함하지 않아야 된다고 인식한다. 효과는 빈 몸체를 파싱하기 위해서 불필요하게 자원의 할당이 이뤄지는 것을 제거한다.

빈 태그는, 가독성향상을 위해 열고 닫는 XML 엘리먼트 쌍으로 표시하기보다는, 짧은 XML 엘리먼트로 표시한다. 그래서

    <myTag:hello />

라고 표기하는 것이 아래보다 낫다.

    <myTag:hello></myTag:hello>

TagExtraInfo와 TagLibraryValidator 사용 Use of TagExtraInfo and TagLibraryValidator

때때로, 태그 라이브러리를 사용하는데 TLD 하나로 유효성을 검증할 수 없는 경우가 있다. 그 때는 TLD에 등록된 TagExtraInfo 클래스나 TagLibraryValidator 클래스를 사용해서, 변환할 때 태그 라이브러리에 있는 에러를 잡아낼 수 있다.

자바스크립트 사용 Use of JavaScriptTM

스크립트가 제대로 돌아가기 위해서는 자바스크립트는 브라우저 타입의 특성과 관계없어야 된다.

동작을 한다면, JSP에서 자바스크립트 코드를 독립적인 파일로 분리하고, 다음과 같이 JSP에서 자바스크립트를 불러쓰는 방식은 좋은 생각이다:

<script language=javascript src="/js/main.js">

이렇게 하면 자바스크립트의 재사용성도 향상되고, 여러 JSP 사이에 일관된 습성의 자바스크립트를 유지할 수 있고, JSP페이지의 복잡성을 줄여준다.

캐스케이딩 스타일 시트 CSS Cascading Style Sheets

CSS를 사용해서 헤딩, 테이블 등의 공통적인 특성을 중앙에서 제어하도록 한다. 이 방법은 사용자들에게 프리젠테이션의 일관성을 향상시키고, 유지보수 노력과 JSP 페이지의 코드 사이즈를 줄여준다. 그래서, 다음과 같이 HTML 태그에서 스타일 정보를 끼어쓰지 말고:

<H1><FONT color="blue">Chapter 1</FONT></H1>
...
<H1><FONT color="blue">Chapter 2</FONT></H1>
...

하나의 스타일 시트 파일myJspStyle.css에 다음과 같은 스타일 정보를 정의한다:

H1 { color: blue }

그리고 JSP 페이지에 스타일 시트를 적용한다:

<link rel="stylesheet" href="css/myJspStyle.css" type="text/css">
...
<H1>Chapter 1</H1>
...
<H1>Chapter 2</H1>
...

컴포짓 뷰 패턴 사용 Use of Composite View Patterns

여러 JSP 페이지에서 반복되는 고정되고 복잡한 구조가 요구될 때, 이것을 다루는 방법으로 컴포짓 뷰 패턴(Composite View pattern;자바 Blueprints 의 패턴 섹션 참고)을 사용해서, 조각조각으로 나눠서 처리하는 법이 있다. 예를 들어, 프리젠테이션 구조에서 JSP 는 때때로 다음과 같은 로직 레이아웃을 갖고 있다:

header
menu bar main body
footnote
footer

이런 방식으로, 컴포짓 JSP 는 각각 분리된 JSP로 구현되는 다른 모듈로 나뉠 수 있다. 구성성분이 되는 JSP는 서블릿으로 변환할 때나 요청시에 include JSP 태그를 사용해서 컴포짓 JSP 의 적당한 위치에 놓이게 된다. 보편적으로, 독립적으로 요청되지 않는 페이지를 불러쓰는, 정적인 include 지시자를 사용할 때는 .jspf 확장자를 사용하고, 웹 어플리케이션 (war)에서 /WEB-INF/jspf/ 디렉토리에 놓아둘 것을 기억하기 바란다. 예를 들면 다음과 같다:

<%@ include file="/WEB-INF/jspf/header.jspf" %>
...
<%@ include file="/WEB-INF/jspf/menuBar.jspf" %>
...
<jsp:include page="<%= currentBody %>" />
...
<%@ include file="/WEB-INF/jspf/footnote.jspf" %>
...
<%@ include file="/WEB-INF/jspf/footer.jspf" %>
...

다른 추천사항 Other Recommendations

이 문서에서, JSP 코드와 웹 컴포넌트를 작성하는데 보다 유지보수하기 쉽고, 일관된 추천 코드 작성법 한 세트를 제시했다. 이 주제에 보다 더 관심있다면, 더 많은 최고의 작성법이 존재한다. 예를 들어, JSP 1.2 스펙에서는 다음을 추천한다:

  • 새로운 내장 객체 선언하기
  • 벤더 고유의 안내서 읽기
  • 커스텀 태그 라이브러리 사용하기

덧붙여서, 자바 BluePrints 는 더 큰 스케일에서 최고의 작성법을 제공한다. (패턴 섹션에서 보여지는) 모델-뷰-컨트롤러 패턴과 같은 것을 예로 들 수 있다.

우리는 이 문서에서 제시한 작성요령에 대한 여러분의 의견에 관심이 있습니다. JSP 코드 작성요령에 대해서 여러분의 다른 의견이 있다면 나누면 좋겠습니다. 피드백은 여기로 보내기 바랍니다. jsp-codeconv-comments@sun.com.

아래에, 위에서 설명한 코드 작성요령을 적용해 놓은 완전한 웹 어플리케이션의 소스를 소개한다. 이 어플리케이션의 WAR 파일을 여기 에서 다운로드 받을 수 있다.

코드 예제 Code Examples

여기에 있는 웹 어플리케이션 예제는 이 문서에 있는 내용이 어떻게 적용되는지 보여주기 위한 것이다. .war 파일에는 다음의 소스코드 파일과 디렉토리 구조로 되어 있다:

/index.jsp
/WEB-INF/classes/codeconv/GetResultsTag.class
/WEB-INF/jspf/footer.jspf
/WEB-INF/lib/jstl.jar
/WEB-INF/lib/standard.jar
/WEB-INF/tld/lotterylib.tld
/WEB-INF/web.xml

index.jsp 페이지에서는 오늘까지 포함된 이번 달의 모의 복권 추첨결과를 보여주는 커스텀 태그 라이브러리( lotterylib.tld )를 사용한다. 그리고, HTML 에 보여질 결과를 형식화하고, 반복하기 위해서 JSTL을 사용한다.

예제는 JSP 1.2 과 JSTL 1.0 을 사용한다.

/index.jsp 파일 소스

<%--
  - Author: Mark Roth
  - Date: January 17, 2003
  -
  - Copyright 2003 Sun Microsystems, Inc.  All Rights Reserved.
  -
  - This software is the proprietary information of
  - Sun Microsystems, Inc.
  - Use is subject to license terms.
  -
  - @(#)
  - Description: Renders fake lottery results for the current month.
  --%>

<%@ taglib prefix="c"
    uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt"
    uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="lottery"
    uri="http://codeconv/lotterylib" %>

<html>
    <head>
        <title>Fake Lottery Results for this Month
        </title>
    </head>
    <body>
        <jsp:useBean id="now" class="java.util.Date" />
        <h1>
            Fake Lottery Results for
            <fmt:formatDate value="${now}"
                pattern="MMMM yyyy" />
        </h1>
        <hr />
        <table border="1" cellpadding="5">
            <%-- Store current month and year --%>
            <c:set var="currentMonth">
                <fmt:formatDate value="${now}" pattern="M" />
            </c:set>
            <c:set var="currentYear">
                <fmt:formatDate value="${now}"
                    pattern="yyyy" />
            </c:set>

            <%-- Determine last day to report results for --%>
            <c:set var="endDay">
                <fmt:formatDate value="${now}" pattern="d" />
            </c:set>

            <%-- Display one row for each day --%>
            <c:forEach var="day" begin="1" end="${endDay}">
                <%-- Construct date --%>
                <c:set var="dateString">
                    <c:out value="${currentMonth}/${day}/${
                        currentYear}" />
                </c:set>

                <fmt:parseDate  var="date"
				    value="${dateString}"
                        pattern="M/d/yyyy" />
                <jsp:useBean id="date" class="java.util.Date"
                    />

                <tr>
                    <td>
                        <c:out value="${dateString}" />
                    </td>
                    <%-- Retrieve and display fake results
                            for this day --%>
                    <lottery:getResults var="numbers"
                        date="<%= date %>" />
                    <c:forEach var="number" items="${numbers}">
                        <td>
                            <c:out value="${number}" />
                        </td>
                    </c:forEach>
                </tr>
            </c:forEach>
        </table>
        <%@ include file="/WEB-INF/jspf/footer.jspf" %>
    </body>
</html>

/WEB-INF/classes/codeconv/GetResultsTag.java 소스

/*
 * codeconv.GetResultsTag
 *
 * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the proprietary information of
 * Sun Microsystems, Inc.
 * Use is subject to license terms.
 *
 * Version: @(#)
 *
 * Date: January 13, 2003
 *
 */

package codeconv;

import java.util.Date;
import java.util.Random;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * Retrieves fake lottery results for the given date and
 * returns the result in the given attribute
 *
 * @version @(#)
 * @author Mark Roth
 */
public class GetResultsTag extends TagSupport {

    /** The maximum number that can be drawn */
    private static final int MAX_NUMBER = 58;

    /** The number of numbers that are drawn */
    private static final int NUMBER_COUNT = 6;

    /** The variable to store results to */
    private String var;

    /** The date on which the numbers were drawn */
    private Date date;

    /** The PageContext, used to return the result */
    private PageContext pageContext;

    /**
     * Remember the PageContext so it can be used later
     * to return the result.
     *
     * @param pageContext The page context of the calling page
     */
    public void setPageContext( PageContext pageContext ) {
        this.pageContext = pageContext;
    }

    /**
     * Returns fake lottery results for the given date.
     *
     * @return EVAL_PAGE so the rest of the page is evaluated.
     */
    public int doEndTag() throws JspException {
        // Generate the (fake) results.
        Random random = new Random( this.date.getTime() );
        int[] result = new int[NUMBER_COUNT];
        for( int i = 0; i < NUMBER_COUNT; i++ ) {
            result[i] = random.nextInt( MAX_NUMBER ) + 1;
        }

        // Place the result in the scoped attribute named by 'var'.
        pageContext.setAttribute( this.var, result );

        return EVAL_PAGE;
    }

    /**
     * Setter for date
     *
     * @param date The fake date on which the lottery numbers
     * were drawn
     */
    public void setDate( Date date ) {
        this.date = date;
    }

    /**
     * Getter for date
     *
     * @return The fake date on which the lottery numbers
     * were drawn
     */
    public Date getDate() {
        return this.date;
    }

    /**
     * Setter for var
     *
     * @param var The name of the variable to store the result in
     */
    public void setVar( String var ) {
        this.var = var;
    }

    /**
     * Getter for var
     *
     * @return The name of the variable to restore the result in
     */
    public String getVar() {
        return this.var;
    }
}

/WEB-INF/jspf/footer.jspf 소스

<%--
  - Author: Mark Roth
  - Date: January 17, 2003
  -
  - Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
  -
  - This software is the proprietary information of
  - Sun Microsystems, Inc.
  - Use is subject to license terms.
  -
  - @(#)
  - Description: Sample Footer that can be included on pages.
  - Assumes JSTL fmt taglib is imported.
  --%>
<!-- common footer -->
<hr />
<jsp:useBean id="footerNow" class="java.util.Date" />
<i>
    Last Updated:
    <fmt:formatDate type="both" value="${footerNow}"
                    dateStyle="full" timeStyle="full" />
</i>

/WEB-INF/tld/lotterylib.tld 파일 소스

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">

<!--
  - Author: Mark Roth
  - Date: January 17, 2003
  -
  - Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
  -
  - This software is the proprietary information of
  - Sun Microsystems, Inc.
  - Use is subject to license terms.
  -
  - @(#)
  - Description: Tag Library for fake lottery results.
  -->

<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>lottery</short-name>
    <uri>http://codeconv/lotterylib</uri>
    <display-name>FakeLotteryTaglib</display-name>
    <description>
        Tag Library for fake lottery results.
    </description>

    <tag>
        <name>getResults</name>
        <tag-class>codeconv.GetResultsTag</tag-class>
        <body-content>empty</body-content>
        <display-name>getResults</display-name>
        <description>
            Retrieve an array of fake lottery results for the given
            date and store them in the given scoped attribute.
        </description>
        <variable>
            <name-from-attribute>var
                </name-from-attribute>
            <declare>true</declare>
            <description>
                The fake lottery results for the given date.
            </description>
        </variable>
        <attribute>
            <name>var</name>
            <required>true</required>
            <description>
                The name of the variable to store the results in.
            </description>
        </attribute>
        <attribute>
            <name>date</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>java.util.Date</type>
            <description>
                The date on which the fake lottery numbers
                    were drawn.
            </description>
        </attribute>
        <example><![CDATA[
            <lottery:getResults var="numbers"
                date="${date}" />
        ]]></example>
    </tag>
</taglib>

/WEB-INF/web.xml 소스

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<!--
  - Author: Mark Roth
  - Date: January 17, 2003
  -
  - Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
  -
  - This software is the proprietary information of
  - Sun Microsystems, Inc.
  - Use is subject to license terms.
  -
  - @(#)
  - Description: Web Deployment Descriptor for JSP 1.2 fake
  - lottery example
  -->

<web-app>
    <display-name>FakeLotteryExample</display-name>
    <description>
        JSP 1.2 example application that illustrates correct JSP
        coding style.
    </description>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <taglib>
        <taglib-uri>http://codeconv/lotterylib</taglib-uri>
        <taglib-location>/WEB-INF/tld/lotterylib.tld
            </taglib-location>
    </taglib>
</web-app>

감사의 말 Acknowledgments

이 문서의 이전 드래프트에 소중한 조언을 제공해 주신 Gregory Murray (Java BluePrints Team), Damian Fauth (Sun Java Center), Kate Morris (Sun Java Center) 님들께 감사드립니다.

References

  1. Code Conventions for the Java Programming Language.
  2. JSP best practices.
  3. Hans's Top Ten JSP Tips.
  4. JavaServer Pages Specification version 1.2.
  5. Alur D., Crupi J., Malks D., Core J2EE Patterns: Best Practices and Design Strategies, Sun Microsystems Press, Prentice Hall Inc., 2001.
  6. Naming Conventions for Enterprise Applications.
  7. JavaServer Pages Standard Tag Library.
  8. Gamma E., Helm R., Johnson R., Vlissides J., Design Patterns: Elements of Reusable Software, Addison-Wesley, 1995.
  9. JavaServerTM Faces Technology.
  10. Java BluePrints.
  11. Designing Enterprise Applications with the J2EETM Platform, 2nd ed..


출처 : 아포스티유 서비스 업체
http://ustaxkim.net/


아포스티유란?
1961년 (1961년 10월 6일) 많은 국가들은 협약국가의 공문서 상호인증을 용이하게 하기 위해, 외국 공관의 영사확인 등 복잡한 인증절차를 없애고 대신 공문서 발행 국가가 이를 법률상으로 공인 (Legalization)된 것으로 확인해 주는 “아포스티유” 협약을 채택했습니다.

일명 Hague Convention으로 알려져 있는 “아포스티유” 협약은 현재 캐나다 및 중국 등을 제외한 전 세계 92개국이 가입해 있습니다.

대한민국 정부도 최근에 협약 사무국인 네덜란드 외교부에 “아포스티유” 협약 가입서를 기탁함으로 Hague Convention 국가 중 하나가 되었습니다.
 
아포스티유 협약에 따른 효과
아포스티유 협약 가입에 따라서 문서가 제출될 국가 대사관이나 영사관의 영사의 인증이 폐지되고 “아포스티유”를 통한 법률적 공인 (Legalization)을 받으면 되기 때문에 시간과 비용이 절감됩니다. 
 
아포스티유 발급 가능한 문서 
  - 신원 조회서
  - 납세 사실 증명서, 의료 진단서, 의학품 허가 확인서
  - 결혼 증명서 이혼 판결문
  - 출생 증명서, 사망 증명서
  - 위임장 ? General, Special, Finance, Real Estate etc.
  - Notarized copies of Passport, Affidavits, and other documents
  - Papers for ADOPTION purposes
  - 성적 증명서 (School, College, University, or any other issuing agency)
  - 재학 증명서 (Enrollment)
  - 졸업 증명서 (Graduation Certificate or statement)
  - 졸업장, Degree Certificates
  - Corporate documents, such as
     * Articles of Incorporation
     * Merger Agreements
     * Authorization letter for Bank, Finance, Office, and related company matters
     * Certificates of Amendment
     * Certificate of good standing
     * Power of Attorney
 

이상의 목록들은 가장 보편적인 아포스티유 신청서류들이고, 이 외에 수많은 서류들을 “아포스티유”로 인증 받을 수 있습니다.
 
 


 

정찬주 기반기획팀 연구원

 

 

 

지난 강좌에서는 공개키 기반구조가 사이버 세상에서 신뢰관계를 형성해 주는 역할을 한다는 것을 알아보았다. 공개키 기반구조에서 개인에게 발급되는 사이버 신분증인 공개키 인증서도 신뢰기관이 전자 서명한 데이터이다. 이번 강좌에서는 신뢰 관계를 형성하는데 있어 기반 기술로 활용되고 있는 전자서명 기술의 원리, 전자서명 생성 검증 절차, 그리고 이용 분야에 대하여 알아보자.

 

TV 드라마를 보면 부모와 어린 자식이 헤어지는 경우 거울이나 동전을 2개로 쪼개어 부모와 어린 자식이 하나씩 나눠 가진 , 세월이 흐른 쪼개진 거울이나 동전을 붙여 서로를 확인하는 것을 있다. 여기서 쪼개진 2개의 거울(동전) 상대방의 신원을 확인하기 어려운 사이버 공간에서 서로를 알아볼 있는 쌍의 정보(공개키와 비밀키) 사용하여 신원을 증명하는 전자서명의 원리와 비슷하다. 쪼개진 2개의 거울(동전) 전자서명을 생성하고 검증하는데 사용되는 2개의 정보인 비밀키 (전자서명 생성키) 공개키 (전자서명 검증키) 비유할 있다.

 

[그림 1] 동전을 통한 전자서명 원리를 이해하도록 돕는다. 공개키와 비밀키는 수학적으로 합치하는 하나의 쌍이라는 것을 지난 공개키 암호방식에 대한 설명을 통해 알아봤다.

 

 



 

송신자가 전자문서를 수신자에게 인터넷을 통해 전달하는 과정을 예로 들어 전자서명 생성 검증 절차에 대하여 알아보자. [그림2] 전자문서에 대하여 전자서명을 생성하는 절차를 나타낸 것이다.


 

 

송신자는 자신이 작성한 전자문서에 대한 전자서명을 생성하기 위하여 송신자의 공개키 인증서의 공개키와 수학적으로 일치하는 비밀키를 갖고 있어야 한다. [그림 2]에서 보는 것과 같이 자물쇠를 비밀키라고 한다면, 자물쇠에 맞는 열쇠를 공개키라고 하자. 송신자는 전자문서를 해쉬 함수에 넣어 압축된 유일한 값을 만든다. 해쉬 함수는 임의의 길이의 메시지를 고정된 길이(, 160 비트) 출력하는 함수이다.

해쉬 함수의 특성으로는 일방향성, 충돌회피성 등이 있다. 일방향성은 해쉬 값으로부터 해쉬되기 전의 원본데이터로 복구할 없다는 특성이다. 충돌회피성은 동일한 해쉬 값을 갖는 서로 다른 원본데이터를 찾을 없다는 특성이다. 이러한 특성 때문에 [그림 2]에서 해쉬 함수로부터 압축되어 나온 해쉬 값을 사람마다 다른 유일한 정보인 지문으로 표현하였다. 해쉬 값과 송신자의 비밀키를 전자서명 알고리즘에 입력하여 전자서명 값을 얻는 과정이 전자서명 생성 절차이다. 이렇게 생성된 전자서명 값과, 전자서명 하고자 했던 원본 전자문서, 그리고 전자서명을 생성하는데 사용한 비밀키에 수학적으로 일치하는 공개키 인증서를 수신자에게 인터넷을 통해 전달한다.

 

[그림 3] 전달받은 전자문서의 전자서명을 공개키 인증서를 이용하여 검증하는 절차이다.

 

 

수신자는 전달받은 정보(전자문서, 전자서명 , 송신자의 공개키 인증서) 전자문서를 해쉬 함수에 넣어 수신자가 압축한 해쉬 값을 계산한다. 그리고 전달받은 정보 전자서명 값과 송신자의 공개키 인증서를 전자서명 알고리즘에 입력하여 송신자가 보내온 원본 해쉬 값을 계산한다. 마지막으로 공개키 인증서의 소유자인 송신자가 전자서명을 수행했는지 확인하기 위하여 수신자가 계산한 해쉬 값과 원본 해쉬 값을 비교한다. 개의 값이 동일할 수신자는 공개키 인증서의 소유자가 해당 전자문서에 대한 전자서명을 생성한 것으로 믿는다. 만약 개의 값이 다르다면, 수신자는 위·변조된 전자문서로 인식하여 전자서명을 믿지 않게 된다.

 

지난 강좌에서 공개키 인증서도 신뢰된 인증기관의 비밀키를 사용하여 전자서명 되어 있다고 하였다. 어떤 절차에 따라 공개키 인증서가 전자서명 되는지 알아보자.

 

강좌를 읽고 있는 독자의 컴퓨터 운영체제가 Windows 계열이라면, C:\Program Files\NPKI 라는 폴더를 열어보기 바란다. NPKI 하위 폴더에는 KISA, SignGate, SignKorea, yessign 등의 폴더가 있을 것이다. 만약 이런 폴더가 없다면 전자서명을 이용하는 전자거래 사이트에 접속하지 않은 것이다. KISA라는 폴더는 국내 전자서명인증관리체계 최상위 인증기관인 한국정보보호 진흥원의 공개키 인증서가 들어 있는 폴더이다. SignGate, SignKorea, yessign 등의 폴더는 국내 공인인증기관의 폴더이고, 해당 공인인증기관으로부터 공개키 인증서를 발급받았다면, 하위에 User라는 폴더가 있다. User 폴더를 열어보면 CN=홍길동(Hong Kildong)xxxx, ou=XXX, ou=xxxx, o=yessign, c=kr라는 폴더가 있다. 폴더를 열어보면, 해당 공인인증기관으로부터 발급 받은 독자의 공개키 인증서(signCert.der) 암호화된 비밀키(signPri.key) 파일이 있다. 공개키 인증서를 발급 받지 않은 독자들의 이해를 돕기 위하여 최상위인증기관의 인증서를 가지고 설명하도록 한다. 최상위인증기관의 공개키 인증서는 C:\Program Files\NPKI\KISA\KISA.der 파일형태로 저장되어 있다.



[그림 4] 최상위인증기관 공개키 인증서의

 

[그림 4] 왼쪽은 KISA.der 파일을 더블 클릭하면 나타나고, 오른쪽은 왼쪽 그림의 메뉴 자세히라는 탭을 선택하면 나타나게 된다. 왼쪽의 그림을 보면, 발급 대상이 공개키 인증서의 소유자에 해당하고, 발급자는 공개키 인증서를 발급한 기관을 알려준다. 위의 예와 같이 발급 대상과 발급자가 같은 공개키 인증서를 자체 서명된(Self-Signed) 공개키 인증서라고 한다. 일반적으로 최상위인증기관 인증서는 인증서를 발급해주는 상위 기관이 없어 자체 서명된 공개키 인증서를 사용한다. 유효기간은 공개키 인증서를 사용할 있는 기간을 의미한다. 실제로 공개키 인증서가 어떻게 전자서명 되어 저장되는 지를 알아보자. [그림 4] 오른쪽 그림에서 필드 해당하는 버전, 일련번호, 서명 알고리즘, 발급자, 유효기간, 주체 등에 해당하는 각각의 값들이 해쉬 함수에 입력되고, [그림 2]에서처럼 해쉬된 값과 공개키에 대응하는 비밀키를 전자서명 알고리즘에 입력하여 전자서명 값이 나온다. 전자서명 값을 공개키 인증서의 전자서명 필드에 저장하여 완전한 하나의 공개키 인증서가 나오게 된다.

 

공개키 인증서에 대한 전자서명 검증은 [그림 4] 오른쪽에 있는 모든 필드의 값을 해쉬 함수에 입력하여 얻은 해쉬 값과 공개키 인증서의 전자서명 값과 공개키를 전자서명 알고리즘에 입력하여 계산한 해쉬 값이 동일한지를 비교하여 동일하다면 올바른 공개키 인증서로 받아들이고, 값이 틀리다면 위조 또는 변조된 공개키 인증서로 받아들이게 된다. 이와 같은 전자서명 생성 검증 과정은 사용자가 공개키 인증서를 이용하여 은행, 증권 등의 사이트에 로그인, 이체 주문할 때마다 일어난다.

 

전자서명은 인터넷을 이용한 은행업무, 증권업무, 쇼핑, 민원서비스 일상생활의 대부분의 업무에 이용할 있다. 전자서명을 이용한 은행업무의 경우 은행을 직접 방문하지 않고도 인터넷을 통하여 계좌이체, 송금 대출 등과 같은 은행 업무를 안전하게 처리할 있다. [그림 5] 전자서명을 이용한 은행 업무를 나타낸 것이다.


 

 

 

인터넷을 통해 이용자는 은행 사이트에 접속하게 된다. 접속한 이용자는 로그인 과정을 전자서명 과정을 통하여 수행한다. 일반적인 사이트에서 이용자는 아이디와 패스워드를 이용하여 접속하지만, 공개키 인증서를 이용하여 접속하는 사용자는 은행 사이트로부터 전달받은 난수 (random value) [그림 2]에서와 같은 방식으로 전자서명을 수행하여 전자서명 값을 은행사이트로 전달한다. 은행은 전달받은 전자서명 값과 사용자의 공개키 인증서를 전자서명 알고리즘에 입력하여 얻은 해쉬 값과 은행사이트가 사용자에게 전달한 난수 값을 해쉬 값과 비교하여 동일하다면 정당한 사용자로 받아들여 해당 이용자의 계좌정보를 보여주게 된다. 위와 같은 사용자 확인 과정을 거친 이용자가 계좌이체를 전자서명을 통하여 수행하는 경우에는 원본문서(출금계좌번호, 출금계좌비밀번호, 입금계좌번호, 입금은행명, 이체금액 ) 대하여 전자서명을 [그림 2] 같이 수행하고, 원본문서, 전자서명 값과 공개키 인증서를 은행사이트로 전달한다. 정보를 받은 은행 사이트는 [그림 3]에서와 같이 전자서명 검증을 수행하고, 공개키 인증서에 대한 검증도 수행하여 올바른 값이라면 은행은 입금계좌로 이체금액을 이체 해준다.

증권사 객장을 방문하거나 전화 이용 없이도 투자자의 신원확인과 거래 사실이 증명할 있는 증권업무에도 전자서명이 이용된다. [그림 6] 전자서명을 이용한 증권업무를 나타낸 것이다. 로그인 주문 신청의 상세 과정은 은행업무의 설명과 비슷하다. 또한, 주민등록등본, 호적등본 발급 각종 행정민원서비스를 위해 해당기관(구청, 동사무소 ) 방문하지 않고도 쉽게 처리할 있는 민원업무에도 전자서명이 이용된다. [그림 7] 전자서명을 이용한 민원업무를 나타낸 것이다. 

 


 
 
 

 

이외에도 인터넷 쇼핑, 온라인 게임, 인터넷 보험, 전자계약, 온라인 세무신고 납부 다양한 분야에서 전자서명이 사용되고 있다.

 

전자서명이 앞으로 실생활에서 쓰임새가 더욱 확대될 것이고, 나아가 안전한 인터넷 세상을 만드는데 중요한 역할을 것이다. 지난 번의 강좌가 독자들이 암호, PKI 전자서명에 대한 기본적인 개념을 이해하는 기회가 되었기를 바란다.

 

 

 

 

[출처: 정보보호뉴스 2005 4월호]

+ Recent posts