메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Ajax와 자바 개발을 간단하게, Part 1: JSP 태그 파일로 자바스크립트 코드 동적으로 생성하기

쉽게 만들 수 있는 JSP 태그 파일을 기반으로 재사용 가능한 Ajax와 자바 컴포넌트를 만들라

Andrei Cioroianu, 선임 자바 개발자 겸 컨설턴트, Devsphere
Andrei Cioroianu는 Devsphere 창립자로, 자바 EE 개발과 Ajax/JSF 컨설팅 서비스를 제공하고 있다. Andrei Cioroianu는 자바와 웹 서비스 기술을 1997년부터 사용해 왔고, 10년 동안 복잡한 기술 문제를 해결하고 있으며 상용 제품, 고객 애플리케이션 그리고 오픈 소스 프레임워크 전반을 다루고 있다. devsphere.com에서 Andrei를 만날 수 있을 것이다.

요약:  많은 웹 개발자들은 자바(Java™) EE가 너무 복잡하고, 새로운 웹 컴포넌트를 작성하는 것이 어려우며, 기존의 것을 커스터마이징하는 것이 충분히 간단하지가 않고, 모든 변경 사항들에 대해 재시작을 요하는 것에 불만을 토로합니다. 본 연재는 코드 생성기, 컨벤션, 스크립트 언어, 그리고 최신 JSP(JavaServer Pages™) 기술을 사용하여 이런 어려움들을 해결할 간단한 솔루션을 제공하려고 합니다. 변경 사항이 있을 때, JSP 태그 파일은 애플리케이션을 재시작할 필요 없이, 자바 EE 서버에 의해 자동으로 다시 컴파일됩니다. 게다가, 여러분은 생성된 코드를 완전히 제어할 수 있으며, JSP 문법을 사용하고 있기 때문에 이런 가벼운 컴포넌트들을 쉽게 커스터마이징할 수 있습니다.

이 연재 자세히 보기

원문 게재일:  2008 년 8 월 26 일
난이도:  중급 영어로:  보기
페이지뷰:  2871 회
의견:  


총 4부로 나뉜 연재의 본 첫 번째 기사에서는 자바스크립트 코드를 생성하는 JSP 기반 기술을 보여줄 것이다. 이것으로 손수 작성해야 했던 상당량의 코드를 줄일 수 있다. 예제 애플리케이션에서는 Ajax 요청을 보내고 Ajax 응답을 처리하는 자바스크립트 함수를 생성하는 방법을 보여줄 것이다. 독자들은 여기서 선보인 예제 기술을 사용하여 실제 애플리케이션에서 Ajax 코드를 쉽게 변경하는 데 사용할 수 있을 것이다. 이 글의 전반적인 목표는 JSP 태그 파일을 사용하여 Ajax 루틴이 아닌 범용적인 목적의 자바스크립트 코드를 생성하는 방법을 보여주는 것이다.

프레임워크와 코드 생성기 사용하기

여러분이 원하는 것에 완전히 부합하는 프레임워크 또는 컴포넌트를 찾았다면 그것을 사용하라. 그렇지 않다면 항상 자신만의 솔루션을 개발하거나 기존 코드를 커스터마이징해야 할 것이다. 이 경우, 매개변수를 여러분의 코드에 직접 하드 코딩하는 것이 아니라 코드를 "매개변수화"하여 재사용 가능한 라이브러리로 만드는 것은 매우 좋은 생각이다. 하지만 일반화하는 시도가 개발을 간단하게 하지 않고 오히려 복잡하게 만들어 적합하지 않을 때도 있다. 일반화 코드를 재사용 가능한 컴포넌트에 적용한 다음에는, 코드 생성기를 사용하여 특화된 코드를 더 효율적으로 생성하는 것을 고려하라.

개발 중에 복사와 붙여넣기 피하기

Ajax를 사용해 도시들에 대한 몇 가지 정보를 요청하는 애플리케이션이 필요하다고 가정해 보자. 가장 빠른 해결책(가장 좋은 것은 아니지만)은 Listing 1에 있는 코드처럼, URL을 변경하고 코드를 웹 페이지에 붙여 넣는 자연스러운 코드가 될 것이다. 많은 개발자들이 상당히 많은 유지보수 문제를 야기할 수 있는 방법을 이와 같이 사용하고 있다. 애플리케이션이 수백 개의 페이지로 구성되어 있고, 각각이 Listing 1에 있는 getInfo() 같은 함수를 가지고 있다고 하자. 나쁜 소식은 Ajax 요청 처리 시 에러 처리를 구현하는 등의 변경이나 추가 작업을 할 때마다, 모든 페이지를 손수 수정하고 그 페이지들을 전부 다시 테스트해야 한다는 것이다. 좋은 소식은 라이브러리, 프레임워크, 코드 생성기를 사용해 이런 유지보수 악몽에서 벗어날 수 있다는 것이다.


Listing 1. Ajax 함수
                
function getInfo(country, city) {
    var request = null;
    if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    else
        return;

    var url = "CityInfo.jsp?country=" + escape(country) 
                           + "&city=" + escape(city);
    request.open("GET", url, true);

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                // ...
            }
        }
    }

    request.onreadystatechange = processResponse;
    request.send(null);
}

제네릭 함수 개발하기

가능한 많은 코드를 재사용할 수 있는 루틴, 함수, 클래스 또는 컴포넌트로 옮겨 라이브러리나 프레임워크 안으로 묶을 수 있게 하는 것이 좋은 개발 습관이다. 여기서는, XMLHttpRequest 인스턴스를 생성하고 그것에 open()send() 메서드를 호출하는 제네릭 함수가 필요하다.

여러분이 사용하기로 결정한 함수 이름이 xhr()이고 매개변수 다섯 개를 가지고 있다고 하겠다. 정보를 반환할 URL, 요청 매개변수의 이름과 값을 가지고 있는 배열 두 개, HTTP 메서드, Ajax 응답을 처리할 콜백이 각각의 매개변수다. 이제 여러분의 애플리케이션은 Listing 2에 있는 getInfo2() 같이 더 간단한 함수를 가질 수 있고, 덕분에 유지보수를 좀 더 간편하게 할 수 있다. Ajax 요청을 보내는 코드 내에서 무언가를 수정해야 한다면, 반드시 xhr() 함수를 수정해야 한다.


Listing 2. 제네릭 함수 사용하기
                
function xhr(url, paramNames, paramValues, method, callback) {
    // send Ajax request ...
}

function getInfo2(country, city) {
    function processResponse(request) {
        // process Ajax response ...
    }
    xhr("CityInfo.jsp", ["country", "city"], [country, city],
        "GET", processResponse);
}

Listing 2는 xhr()이라는 제네릭 함수와 애플리케이션에 특화된 getInfo2() 함수를 보여주고 있다. 제네릭 코드는 별도의 자바스크립트 파일로 옮겨 그것들을 필요로 하는 여러 페이지에서 임포트(import)해 사용하는 것이 좋다. getInfo2() 함수처럼 특화된 코드는 애플리케이션이 그와 비슷한 패턴의 함수를 많이 필요로 한다면 코드 생성기 사용을 고려해볼 수 있겠다.

코드를 동적으로 생성하기

코드 생성기를 사용하여 웹 애플리케이션을 더 효율적으로 개발하고 유지보수할 수 있다. 예를 들어 JSP, 자바 코드, 또는 다른 언어를 사용하여 템플릿으로부터 자바스크립트 함수를 생성할 수 있다. XML로 생성기의 매개변수들을 기술하여 코드 가독성을 높이고 더 이해하기 쉬운 속성 이름을 사용할 수 있다. 게다가 tag 속성은 자바스크립트 함수나 자바의 메서드처럼 고정된 순서를 필요로 하지 않는다.

비교해 보았을 때, XML 태그 사용의 주목할 만한 장점은 속성의 기본값을 사용할 수 있다는 것이다. 프로그래밍 언어들이 제공하는 메서드 매개변수의 제한적인 가용성과 비교해봤을 때 편리하다. 코드 생성기의 확장성에 내포되어 있는 이런 의미들을 고려해야 한다. 현존하는 코드를 깨트리지 않으면서 메서드 시그너처를 변경하는 것보다 새로운 속성을 추가하는 것이 훨씬 쉽기 때문이다. 이런 모든 문법적인 장점 덕분에 XML과 JSP가 눈에 띄게 된다. 특히 코드 생성기가 많은 속성을 필요로 할 때 말이다.

다음과 같은 이유로 JSP는 서버 쪽에서 자바스크립트를 생성하는 생성기로 좋은 후보자가 된다.

  • 개발자들이 이미 JSP 문법을 알고 있다.
  • JSTL은 조건과 반복구조에 사용할 태그를 제공한다.
  • JSP 페이지는 자바스크립트를 포함한 어떠한 타입의 문자든지 간편하게 생성할 수 있게 해준다.

게다가, JSP 기술은 이미 실행 가능한 코드를 커스텀 태그로 만들 수 있는 막강한 메커니즘을 가지고 있다. 따라서 JSP 문법에 기반을 둔 템플릿을 위한 별도의 파서(parser)를 구현할 필요가 없다. 그리고 마지막으로, 변경을 할 때마다 코드를 다시 생성하는 외부 도구도 필요하지 않다.

Listing 3은 본 기사의 다음 절에서 살펴볼 애플리케이션의 코드 일부를 보여주고 있다. 커스텀 JSP 태그 <da:xhr>을 사용하여 getInfo3() 함수를 만들고 있다. 이 함수는 사용자가 Get Info 레이블이 붙은 버튼을 클릭하면, 웹 브라우저에 의해 호출된다.


Listing 3. JSP 태그 파일을 사용하여 Ajax 함수 생성하기
                
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>
...
<script type="text/javascript">
    <da:xhr function="getInfo3(country, city)" url="CityInfo.jsp" method="GET">
        // process Ajax response ...
    </da:xhr>
</script>
...
<button ... onclick="getInfo3(...)">Get Info</button>

생성된 자바스크립트 코드는 Listing 3에서 생성한 함수를 호출하는 웹 페이지의 <script> 엘리먼트 안에 위치하게 된다. 여러 웹 페이지가 같은 자바스크립트 코드를 필요로 한다면, 코드를 동적으로 생성하는 별도의 JSP 파일을 여러 웹 페이지에서 임포트할 수 있다. 마치 다른 자바스크립트 파일을 사용하듯이, <script> 엘리먼트의 src 속성에 해당 URI를 기술하면 된다(Listing 4 참조).


Listing 4. JSP 페이지가 생성한 자바스크립트 임포트하기
                
<script src="DynamicJavaScript.jsp" type="text/javascript">
</script>

생성한 코드 캐시

모든 요청마다 자바스크립트 코드를 생성하는 것이 개발할 때는 별 문제가 되지 않는다. 하지만 실제 사용 환경에서는 성능상의 불이익을 고려하게 될 것이다. 이것에 대한 해결책은 Listing 5에 보이는 것처럼 JSTL을 사용하여 생성된 코드를 JSP의 application 스코프에 넣어 코드를 캐시하는 것이다. 그런 다음, EL을 사용하여 ${applicationScope.cachedCode} 이런 식으로, 캐시한 코드를 웹 페이지에 넣을 수 있다.


Listing 5. 생성한 코드 캐시하기
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:if test="${empty cachedCode}">
    <c:set var="cachedCode" scope="application">
        alert("Cached Code");
    </c:set>
</c:if>

${applicationScope.cachedCode}

간단한 Ajax 애플리케이션 만들기

이번 절에서는 예제 애플리케이션의 JSP 페이지들을 설명한다. CityForm.jsp 페이지는 Ajax를 사용하여 웹 서버로 정보를 보낼 폼을 가지고 있다. CityInfo.jsp 페이지는 Ajax 응답을 생성한다.

Ajax 페이지 만들기

예제 애플리케이션의 CityForm.jsp 페이지는 두 개의 커스텀 태그 <da:xhr><da:innerHTML>을 사용한다. 이것들은 JSP 태그 파일로 구현했다. xhr.tag 파일은 Ajax 요청을 보내는 자바스크립트 함수를 생성한다. innerHTML.taginnerHTML 속성을 사용하여 HTML 엘리먼트 안에 특정 내용을 집어 넣는 코드를 한 줄 생성한다. 두 개의 태그 파일에 있는 JSP 코드는 뒤에서 살펴볼 것이다.

JSP 페이지(Listing 6 참조)는 사용한 태그 라이브러리 JSTL Core(c 접두어)와 태그 파일들의 라이브러리(da 접두어)를 선언하고 있다. CityForm.jsp 페이지는 또한 <da:xhr><da:innerHTML>에 의해 생성된 코드가 호출할 함수들을 가지고 있는 두 개의 자바스크립트 파일 ajax.jsencode.js를 임포트하고 있다. 이 커스텀 태그들은 getInfo()라는 자바스크립트 함수 코드를 생성하므로 <script> 엘리먼트 안에서 사용했다.


Listing 6. CityForm.jsp 예제
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>

<html>
<head>
<title>Ajax Demo</title>
    <script src="ajax.js" type="text/javascript">
    </script>
    <script src="encode.js" type="text/javascript">
    </script>
    <script type="text/javascript">
        <da:xhr function="getInfo(country, city)" method="GET"
                url="CityInfo.jsp" sync="false" json="true" cpr="true">
            <da:innerHTML id="info" value="json.info" encode="true"/>
        </da:xhr>
        
        function getInfo2(form) {
            var country = form.country.options[form.country.selectedIndex].text;
            var city = form.city.value;
            getInfo(country, city);
        }
    </script>
</head>
<body>
    <form name="data">
        Country:
        <select name="country" size="1">
            <option>Canada</option>
            <option>UK</option>
            <option selected>USA</option>
        </select>
        City: <input name="city" size="20">
        <button type="button" onclick="getInfo2(this.form)">Get Info</button>
    </form>
    <div id="info"></div>
</body>
</html>

CityForm.jsp 페이지의 웹 폼은 Get Info 버튼, 나라 목록 그리고 사용자가 도시 이름을 입력할 수 있는 입력 필드로 구성되어 있다. 사용자가 버튼을 클릭하면, 웹 브라우저는 getInfo2() 함수를 호출하고 이 호출은 onclick 속성에 코딩을 했다. 이 함수는 웹 폼에 있는 countrycity 필드의 값을 가져온 다음, 이 값들을 getInfo() 함수로 보내고 그 안에서 Ajax 요청을 서버로 보내게 된다. Ajax 응답은 웹 폼 아래에 위치한 <div> 엘리먼트에 삽입될 정보를 가지고 있다.

<da:xhr> 태그의 속성으로 생성할 자바스크립트 함수 헤더, Ajax 요청을 보낼 HTTP 메서드, 그리고 Ajax 응답을 생성할 페이지의 URL을 기술한다. 그밖에 sync, json, cpr 같은 속성으로 생성할 코드의 다양한 기능을 기술할 수 있다.

synctrue면 정보를 동기적으로 요청한다. 이것은 서버로부터 데이터를 가져올 때까지 사용자 인터페이스(UI)가 잠시 대기한다는 것이다. syncfalse면 요청은 비동기적으로 처리된다. 웹 서버에 의해 해당 요청이 처리되는 동안에도 사용자는 네트워크를 넘나들며 정보를 주고받는 작업을 계속 할 수 있다는 것을 뜻한다.

json 속성이 true라면, 코드 생성기는 eval(request.responseText)를 사용해 Ajax 응답을 분석하는 자바스크립트를 추가한다. 마지막으로, <da:xhr> 태그의 cpr 속성이 truexhr.tag 파일은 새로운 요청을 보내기 전에 앞서 보낸 요청을 닫는 자바스크립트 코드를 추가한다. 이 기능들에 대한 자세한 내용은 ajax.jsxhr.tag 파일을 다룰 때 살펴보겠다.

생성된 코드

Listing 7은 CityForm.jsp 페이지에 생성된 getInfo(country, city) 함수를 보여주고 있다. 사용한 HTTP 메서드는 GET이고, Ajax 응답을 만든 페이지 URL은 CityInfo.jsp, sync 속성은 false, 그리고 jsoncprtrue다. 생성된 자바스크립트 코드는 ajax.js 파일에 있는 openRequest(), sendRequest(), closeRequest(), httpError() 함수를 사용하고 있으며 encode.jsappendParam()htmlEncode() 함수를 사용하고 있다.


Listing 7. GET을 사용한 Ajax 요청을 처리할 용도로 생성된 함수
                
var getInfoRequest = null;

function getInfo(country, city) {
    if (getInfoRequest) closeRequest(getInfoRequest);

    var url = "CityInfo.jsp";
    url += '?'; 
    url = appendParam(url, "country", country);
    url = appendParam(url, "city", city);

    var request = openRequest("GET", url, true);
    getInfoRequest = request;
    if (request == null) return null;

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                eval(request.responseText);
                document.getElementById("info").innerHTML 
                    = htmlEncode(json.info);
            } else {
                httpError(request);
                document.location = url;
            }
        }
    }

    request.onreadystatechange = processResponse;
    sendRequest(request, null);
    return request;
}

HTTP 방식을 POST로 변경하면 생성된 코드는 Listing 8처럼 그에 따라 바뀐다. 요청 매개변수를 url에 추가하는 대신, getInfo() 함수는 이 매개변수들을 body라는 이름의 변수에 추가하고 있다. 이 변수는 나중에 ajax.js 파일에 있는 sendRequest() 함수로 전달된다.


Listing 8. POST를 사용한 Ajax 요청을 처리할 용도로 생성된 함수
                
function getInfo(country, city) {
    ...
    var url = "CityInfo.jsp";
    
    var body = "";
    body = appendParam(body, "country", country);
    body = appendParam(body, "city", city);

    var request = openRequest("POST", url, true);
    ...
    sendRequest(request, body);
    return request;
}

JSON 응답 생성하기

CityInfo.jsp 페이지(Listing 9 참조)는 자바스크립트 객체 노테이션(JSON)을 사용하여 Ajax 요청에 대한 응답을 생성한다. 페이지를 스크립트가 없는 상태로 유지하기 위해, 자바 코드는 두 개의 JSP 태그 파일인 noCache.tagjstring.tag로 옮겼다. 이것들은 JSP 페이지의 <da:noCache><da:jstring>에 의해 호출된다. 자바 코드를 JSP 태그 파일에 두는 것은 태그 핸들러를 개발하는 것보다 훨씬 쉽다. JSP 컨테이너가 그 클래스들을 만들어 줄 것이기 때문이다. 그리고 자동으로 변경 사항이 있는 자바 코드를 다시 컴파일해 애플리케이션을 재시작할 필요가 없다.


Listing 9. CityInfo.jsp 예제
                
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>

<da:noCache/>

json = {
    country: <da:jstring value="${param.country}"/>,
    city: <da:jstring value="${param.city}"/>,
    info: <da:jstring>Info on ${param.city}, ${param.country}</da:jstring>
}

Listing 10은 JSON 응답을 보여주고 있다.


Listing 10. 생성된 JSON
                
json = {
    country: "UK",
    city: "London",
    info: "Info on London, UK"
}

noCache.tag 파일(Listing 11 참조)은 HTTP 응답의 Cache-Control 헤더를 설정하는 자바 코드 한 줄로 구성되어 있다.


Listing 11. noCache.tag 파일
                
<% response.setHeader("Cache-Control", "no-cache"); %>

jstring.tag 파일(Listing 12 참조)은 자바스크립트 문자열 값을 태그 파일에 속성이나 몸체 내용으로 전달될 수 있는 형태로 인코딩한다. value 속성이 기술되어 있지 않으면, <jsp:doBody> 액션이 <da:jstring></da:jstring> 사이에 포함된 JSP 코드를 실행하고, value 변수를 page 스코프로 설정한다. 두 경우 모두, 자바 코드는 jspContext.getAttribute()를 사용해 문자열 값을 가져오고 문자를 하나씩 꺼낸 다음, 특수문자와 ASCII 문자가 아닌 것들을 제거한다.


Listing 12. jstring.tag 파일
                
<%@ attribute name="value" required="false" rtexprvalue="true" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:if test="${empty value}">
    <jsp:doBody var="value"/>
</c:if>

<%
    String value = (String) jspContext.getAttribute("value");
    out.write('"');
    int len = value.length();
    for (int i = 0; i < len; i++) {
        char ch = value.charAt(i);
        switch (ch) {
            case '\\': out.write("\\\\"); break;
            case '\n': out.write("\\n"); break;
            case '\r': out.write("\\r"); break;
            case '\t': out.write("\\t"); break;
            case '"':  out.write("\\\""); break;
            default: {
                if (' ' <= ch && ch <= '~')
                    out.write(ch);
                else {
                    out.write("\\u");
                    for (int j = 3; j >= 0; j--) {
                        int k = (((int) ch) >> (j << 2)) & 0x0f;
                        out.write((char) (k < 10 ? k + 48 : k + 55));
                    }
                }
            }
        }
    }
    out.write('"');
%>

자바스크립트 함수 개발하기

이번 절에서는 xhr.taginnerHTML.tag 파일에 의해 생성된 자바스크립트 코드가 호출하는 함수들을 가지고 있는 ajax.jsencode.js 파일을 살펴볼 것이다.

XMLHttpRequest 관련 함수

ajax.js(Listing 13 참조)에 있는 openRequest() 함수는 매개변수 세 개(method, url, async)를 가지고 XMLHttpRequest 객체를 생성한다. 그런 다음, open() 메서드를 호출하고 초기화한 request 객체를 반환한다. sendRequest() 함수는 body 매개변수가 null이 아닐 때, Content-Type 헤더를 설정한다. 그리고 request 객체의 send() 메서드를 호출한다.


Listing 13. ajax.js 파일
                
function openRequest(method, url, async) {
    var request = null;
    if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    if (request)
        request.open(method, url, async);
    return request;
}

function sendRequest(request, body) {
    if (body)
        request.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded");
    request.send(body);
}

function closeRequest(request) {
    request.onreadystatechange = function() { };
    request.abort();
    delete request;
}

function httpError(request) {
    alert("Http Error: " + request.status);
}

closeRequest() 메서드는 onreadystatechange 속성을 비어 있는 함수로 설정하고, abort() 메서드를 호출한다. 그리고 자바스크립트의 delete 연산자를 사용하여 request 객체가 차지한 메모리를 반환한다. 이 함수는 Ajax 응답을 처리한 다음 반드시 모든 XMLHttpRequest 객체에 호출해주어야 한다. 그렇지 않으면, 웹 브라우저에서 메모리 부족을 겪게 될 것이다. ajax.js 파일에 있는 마지막 함수는 httpError()로, 경고 창으로 요청의 상태를 보여준다.

HTML과 URL 인코딩 함수

encode.js 파일(Listing 14 참조)의 htmlEncode() 함수는 문자열 매개변수를 받아 &, <, > 문자를 &amp;, &lt;, &gt;로 변환한다. attrEncode() 함수는 같은 일을 한 뒤 " 문자를 &quot;로 바꿔 인코딩된 문자열을 속성의 값으로 사용할 수 있게 한다.

자바스크립트의 escape() 함수는 보통 Ajax 요청의 request 매개변수를 인코딩할 때 사용한다. escape()+ 문자를 인코딩하지 않고 그대로 둔다는 것을 알아야 한다. 서버에서는 +를 공백 문자로 디코딩하기 때문에 이것으로 문제가 발생할 수도 있다.

위에서 언급한 문제를 encode.js 파일에 있는 urlEncode() 함수에서 해결했다. 이 함수는 자바스크립트의 escape()를 실행하여 URL 인코딩을 한 다음 모든 + 문자를 %2B로 인코딩하여 서버 쪽에서 제대로 디코딩할 수 있게 한다.


Listing 14. encode.js 파일
                
function htmlEncode(value) {
    return value ? value.replace(/&/g, "&amp;")
        .replace(/</g, "&lt;").replace(/>/g, "&gt;") : "";
}

function attrEncode(value) {
    return htmlEncode(value).replace(/"/g, "&quot;");
}

function urlEncode(str) {
    return str ? escape(str).replace(/\+/g, "%2B") : "";
}

function isArray(a) {
    return a.sort ? true : false;
}

function appendParam(url, name, value) {
    if (isArray(value)) {
        for (var i = 0; i < value.length; i++)
            url = appendParam(url, name, value[i]);
    } else {
        if (url && url.length > 0) {
            if (url.charAt(url.length-1) != '?')
                url += "&";
        } else
            url = "";
        url += urlEncode(name) + "=" + urlEncode(value);
    }
    return url;
}

appendParam() 함수는 이름/값 쌍을 주어진 URL에 추가한다. 만약 세 번째 매개변수가 배열이면, 자바스크립트 코드는 모든 요소를 순회하면서 appendParam()을 호출하여 각 배열에 있는 이름/값 요소 쌍을 url에 추가한다.

JSP 태그 파일을 사용하여 자바스크립트 코드 생성하기

이번 절에서는 xhr.taginnerHTML.tag를 살펴보겠다. 앞의 것이 Ajax 요청을 서버로 보내는 자바스크립트 함수를 생성한다. 그리고 뒤에 있는 태그는 웹 브라우저 내부에서 Ajax 응답을 처리하는 콜백 함수 코드를 생성한다.

Ajax 요청 보내기

<da:xhr> 태그는 여섯 개 속성을 받는다. 자바스크립트 함수 헤더, HTTP 방식, Ajax 요청에 대한 응답을 돌려줄 페이지 URL, 요청을 동기 또는 비동기로 보낼지 여부를 나타내는 Boolean 속성, 응답이 JSON 형식인지 나타내는 또 다른 Boolean 속성, 마지막 속성(cpr)은 본 기사에서 "이전 요청 닫기"라고 이름을 붙인 기능 사용 여부를 나타낸다. 이 모든 속성는 xhr.tag 파일(Listing 15 참조)에 <%@attribute%> 지시자를 사용하여 선언하고 있다.

다음으로, 태그 파일은 <%@taglib%> 지시자를 사용하여 사용할 태그 라이브러리를 선언한다. 그리고 method 속성의 문자들을 전부 대문자로 바꾸고, reqVarName이라는 JSP 변수를 선언하고 그 값을 자바스크립트 함수 이름과 Request 문자열로 설정한다. 그런 다음, xhr.tag 태그 파일은 자바스크립트 코드를 생성하기 시작한다. cpr 속성이 true라면, 자바스크립트 변수를 선언하고 null로 초기화한다. 이 변수는 앞선 요청을 담아두는 데 사용한다. 이 요청은 생성된 함수가 다음 번에 호출될 때 반드시 "닫혀"야 한다. ajax.js 파일의 closeRequest() 함수는 이전 절에서 살펴보았다.

xhr.tag 파일에서 <dau:appendParams> 태그를 사용하여 methodGET일 경우에는 요청 매개변수를 url 변수에 추가하고, methodPOST일 때는, body 변수에 추가한다. ajax.js 파일의 openRequest() 함수를 사용하여 XMLHttpRequest 객체를 생성하고 초기화한다. 같은 ajax.js 파일 내에 위치한 sendRequest() 함수를 사용하여 Ajax 요청을 보낸다.


Listing 15. xhr.tag 파일
                
<%@ attribute name="function" required="true" rtexprvalue="true" %>
<%@ attribute name="method" required="false" rtexprvalue="true" %>
<%@ attribute name="url" required="true" rtexprvalue="true" %>
<%@ attribute name="sync" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>
<%@ attribute name="json" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>
<%@ attribute name="cpr" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="dau" tagdir="/WEB-INF/tags/dynamic/ajax/util" %>

<c:set var="method" value="${empty method ? 'GET' : fn:toUpperCase(method)}"/>
<c:set var="reqVarName"
    value="${fn:trim(fn:substringBefore(function, '('))}Request"/>

<c:if test="${cpr}">var ${reqVarName} = null;</c:if>

function ${function} {
    <c:if test="${cpr}">if (${reqVarName}) closeRequest(${reqVarName});</c:if>

    var url = "${url}";
    <c:if test="${method == 'GET'}">
        url += '?';
        <dau:appendParams jsVarName="url" function="${function}"/>
    </c:if>
    <c:if test="${method == 'POST'}">
        var body = "";
        <dau:appendParams jsVarName="body" function="${function}"/>
    </c:if>

    var request = openRequest("${method}", url, ${!sync});
    <c:if test="${cpr}">${reqVarName} = request;</c:if>
    if (request == null) return null;

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                <c:if test="${json}">eval(request.responseText);</c:if>
                <jsp:doBody/>
            } else {
                httpError(request);
                <c:if test="${method == 'POST'}">url += '?' + body;</c:if>
                document.location = url;
            }
        }
    }

    request.onreadystatechange = processResponse;
    <c:if test="${method == 'GET'}">sendRequest(request, null);</c:if>
    <c:if test="${method == 'POST'}">sendRequest(request, body);</c:if>
    return request;
}

processResponse()라는 내부 함수는 onreadystatechange 속성을 사용하여 request 객체에 넘기는 콜백이다. 이 콜백 함수는 Ajax 요청 라이프 사이클 도중 몇 번 호출된다. Ajax 요청의 현재 상태는 readyState 속성을 통해 얻어올 수 있다. 요청이 완성되면 readyState4가 된다.

HTTP 상태 코드가 200이면, 에러가 없고 Ajax 응답을 <da:xhr></da:xhr> 사이에 있는 JSP 코드로 처리할 수 있게 된다. HTTP 에러가 발생한 경우, 생성된 코드는 xhr.tag 파일에 있는 httpError() 함수를 호출하고 브라우저는 Ajax 응답을 생성한 URL로 리다이렉트해 개발자가 HTTP 에러의 원인이 되는 서버 쪽 에러를 볼 수 있게 한다. 예를 들어, 만약 HTTP 에러 코드가 500이면(내부 에러), 자바 스택 트레이스를 봐야 할 것이다.

Listing 16은 appendParams.tag 파일을 보여주고 있다. 이 파일은 주어진 함수 헤더의 매개변수를 순회하면서 encode.js 파일에 있는 appendParam() 함수를 호출하는 자바스크립트를 생성한다. 78은 본 기사의 앞에서 생성된 코드를 살펴볼 때 나왔다.


Listing 16. appendParams.tag 파일
                
<%@ attribute name="jsVarName" required="true" rtexprvalue="true" %>
<%@ attribute name="function" required="true" rtexprvalue="true" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<c:set var="paramList"
    value="${fn:substringBefore(fn:substringAfter(function, '('), ')')}"/>

<c:forEach var="paramName" items="${paramList}">
    <c:set var="paramName" value="${fn:trim(paramName)}"/>
    ${jsVarName} = appendParam(${jsVarName}, "${paramName}", ${paramName});
</c:forEach>

developerWorks Ajax 참고자료 센터
Ajax 참고자료 센터를 참조하여 무료 도구, 코드, Ajax 애플리케이션 개발에 필요한 정보를 찾아보기 바란다. Ajax 전문가 Jack Herrington이 담당하는 활발한 Ajax 커뮤니티 포럼을 통해 여러분의 궁금증에 대한 답을 바로 알려줄 수 있는 동료들과 의사소통할 수 있다.

Ajax 응답 처리하기

innerHTML.tag 파일(Listing 17 참조)은 자바스크립트 코드 한 줄을 생성한다. 해당 코드는 document.getElementById()를 사용하여 주어진 id로 HTML 엘리먼트를 표현하고 있는 DOM 객체를 가져온다. 그런 다음 innerHTML 속성을 세팅해 이 엘리먼트의 내용을 바꾼다. innerHTML 속성의 새 값은 <da:innerHTML> 태그의 value 속성을 통해서나 태그 본문 안에서 전달될 것이다. encode 속성값이 true면, 생성된 코드는 encode.js 파일의 htmlEncode() 함수를 호출한다.


Listing 17. innerHTML.tag 파일
                
<%@ attribute name="id" required="true" rtexprvalue="true" %>
<%@ attribute name="value" required="false" rtexprvalue="true" %>
<%@ attribute name="encode" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:if test="${empty value}">
    <jsp:doBody var="value"/>
</c:if>
<c:if test="${encode}">
    document.getElementById("${id}").innerHTML = htmlEncode(${value});
</c:if>
<c:if test="${!encode}">
    document.getElementById("${id}").innerHTML = ${value};
</c:if>

<da:innerHTML> 태그는 <da:xhr>과 같이 사용해 본 기사 예제 애플리케이션의 Ajax 응답을 처리할 수 있다. 여러분은 이와 비슷한 태그 파일을 만들어 애플리케이션이 필요로 하는 동작을 하도록 만들 수 있다.

결론

본 기사에서 JSP 태그 파일을 사용하여 Ajax 루틴을 생성하는 방법을 익혔다. 같은 기술을 사용하여 서버 쪽에서 동작하는 다른 타입의 자바스크립트를 생성할 수 있다. 템플릿처럼 동작하는 태그 파일을 변경할 때마다 코드가 자동으로 다시 생성되기 때문에, 이 기능은 기능을 추가하거나 수정하기 더 쉽게 해준다. 다음 기사를 계속 살펴보길 바란다. 계속해서 데이터 바인딩, 페이지 네비게이션, 그리고 설정과 셋업을 최소화하는 스타일 규약(Convention)을 익힐 수 있을 것이다.



다운로드 하십시오

설명이름크기다운로드 방식
Sample application for this articlewa-aj-simplejava1.zip6KBHTTP

다운로드 방식에 대한 정보


참고자료

교육

  • developerWorks 웹 개발 영역에는 웹 2.0 개발 도구와 정보가 많이 있다.

  • developerWorks Ajax 참고자료 센터에는 계속해서 Ajax 관련 내용이 늘어나고 있으며, 오늘 당장 Ajax 애플리케이션 개발을 시작할 수 있는 자료를 가지고 있다.

  • 자바스크립트 도구와 문서를 찾고 있다면 developer.mozilla.org를 참조하라.

  • JSP 홈페이지에 가서 더 많은 JSP에 관한 자료를 참조하라.

  • developerWorks에서 Andrei Cioroianu의 다른 기사를 참조하여, Ajax와 관련된 초급부터 고급에 이르는 기술을 통해 여러분의 애플리케이션 개발을 향상시킬 수 있다.

  • developerWorks 기술 행사와 웹 캐스트를 통해 최신 정보를 접하라.

  • 기술 서점에서 다른 기술 주제와 관련된 책을 살펴보라.

토론

필자소개

Andrei Cioroianu는 Devsphere 창립자로, 자바 EE 개발과 Ajax/JSF 컨설팅 서비스를 제공하고 있다. Andrei Cioroianu는 자바와 웹 서비스 기술을 1997년부터 사용해 왔고, 10년 동안 복잡한 기술 문제를 해결하고 있으며 상용 제품, 고객 애플리케이션 그리고 오픈 소스 프레임워크 전반을 다루고 있다. devsphere.com에서 Andrei를 만날 수 있을 것이다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=웹 개발, 자바
ArticleID=332822
ArticleTitle=Ajax와 자바 개발을 간단하게, Part 1: JSP 태그 파일로 자바스크립트 코드 동적으로 생성하기
publish-date=08262008
author1-email=andcio@gmail.com
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.