노무현 대통령 배너

Make A Wish!!

SERAPHMATe's LifeLog


출처 : http://www.aptana.com/node/224


Callbacks


Jaxer에서 페이지의 lifecycle은 두 파트로 구성된다. : Jaxer는 그들이 브라우저에 서비스를 하기 전에 페이지를 처리한다. 그리고 Jaxer는 브라우저에서 서버측의 기능으로 돌아오는 호출을 처리한다. 두 번째 파트를 보자 : 어떻게 Jaxer는 브라우저가 서버측 기능을 호출하기 쉽게 만들어주는지.


자, 간단한 예제를 보자. 만약 우리가 순수한 브라우저 측 어플리케이션을 작성한다면, 우리는 이렇게 작성할 수 있다. :

<script>
        function getData() {
                return 42; 
        }

</
script>
<
input type="button" value="get it" onclick="alert(getData())">

브라우저에서 이 페이지를 볼때, 이 페이지는 하나의 버튼을 가지고, 당신이 그것을 클릭하면, "42"를 출력하는 alert를 보게 된다. 이것 대신에 서버로부터 데이터를 얻고 싶다면, 당신은 단순히 script 태그에 값을 "server-proxy"를 가진 "runat" 이라는 Attribute만 추가하면 된다.("server"는 코드를 서버에서 실행하기 위해, "proxy"는 브라우저가 호출할 수 있게 하기 위해 사용한다.)

<script runat="server-proxy">
        function getData() {
                return 42; // or do something more interesting, like a database query
        }
</script>
<input type="button" value="get it" onclick="alert(getData())">

이것이 어떻게 동작을 할까? Jaxer가 클라이언트로 페이지를 전송하기 전 그것을 처음 처리할 때, Jaxer는 서버에서 동작하도록 구성된 어떠한 Javascript코드를 실행한다. : 특히, "server", "both", 또는 변수들 중 하나의 값을 가진 runat Attribute를 가진 script 블록이 있는. 당신은 callback이 가능하도록 처리하는 동안 정의된 몇몇의 기능을 디자인 할 수 있다. 이러한 기능은 오로지 해당 페이지로부터의 callback에게만 가능하다.

브라우저가 호출할 수 있도록 디자인 된 함수란 어떤것인가? script블록 내에 runat="server-proxy" attribute를 가지고 있거나, true 값을 가진 "proxy" 프로퍼티가 있거나, 페이지에 Jaxer.proxies 배열을 가진 것이다. 만약 이것들 중 하나가 있다면, 그 함수는 페이지 처리 과정 마지막에 서버에 캐시(저장)된다. 그리고 그 안에서 같은 이름을 가진 proxy 함수가 브라우저 영역의 페이지에 삽입된다. proxy 함수가 호출될 때, Jaxer로 돌려주는 XMLHttpRequest가 브라우저에서 생성된다.; Jaxer는 원래 함수를 찾아서 그것을 실행한다.; 그러면, proxy 함수가 처리한 것 처럼 결과 값이 브라우저로 리턴된다.


사실은, 모든 호출가능한 서버측 함수마다 두개의 proxy함수가 브라우저 영역의 페이지에 삽입된다.: 하나는 서버측 함수와 동일한 이름을, 나머지는 그것의 이름에 "Async"가 첨부된 것이다. 두번째 것은 비동기 호출을 생성하는데 사용되고, 나머지 하나는 요청이 서버에서 생성되고, 처리되고, 돌아올 때까지 브라우저가 멈춰있지 않도록 한다. 비동기 버전은 추가적인 첫 인자를 취한다. : 서버가 응답을 받았을 때, 호출되는 함수. 그 함수는 그 자신의 서버측 함수의 반환된 결과를 취한다.


예를 들어보자 :
<script runat="server-proxy">
        function getLastName(firstName) {
                var lastName = Jaxer.DB.execute(
                "SELECT lastName FROM names WHERE firstName = ?", firstName).singleResult;
                return lastName;
        }
</script>
<script>
        function showFullNameNow() {
                var firstName = document.getElementById("first").value;
                var lastName = getLastName(firstName);
                show(firstName, lastName);
        }

        function showFullNameSoon() {
                var firstName = document.getElementById("first").value;
                getLastNameAsync(

                        function(lastName) { show(firstName, lastName); },
                        
// this is called when getLastName() returns
                        firstName);
        }

        function show(firstName, lastName) {
                alert(firstName + " " + lastName);
        }
</script>
<input type="button" value="Show Now" onclick="showFullNameNow()">
<input type="button" value="Show Soon" onclick="showFullNameSoon()">

getLastName() 함수를 서버에서 호출하는 것은 브라우저에서 호출하는 것 만큼 쉽다는 걸 기억하자. - 왜냐하면, 사실은 브라우저에 서버의 진짜 getLastName() 함수를 위한 proxy기능을 하는 getLastName() 함수가 있기 때문이다. getLastNameAsync()를 사용하기 위해, 비동기 흐름을 허용하는 약간의 소스코드의 재구성이 필요하지만, 이것은 사용자 편의성의 향상을 종종 의미하곤 한다.


많은 경우에, Callback하는 동안 서버에 있는 다른 함수들이 필요하게 된다. 예를 들어, getLastName()은 어떠한 접근권한을 필요로 한다고 하자. 그 권한은 getCurrentUser() 함수를 호출하는데 필요하고, isAuthorized()에서 호출된다. 그러나 getCurrentUser()와 isAuthorized()는 보안 문제 등으로 인해 브라우저에서 바로 호출되어서는 안된다. 이것을 해결하기 위해서, Jaxer는 페이지 처리 마지막 시점에 자동으로 다른 함수들을 캐시한다.(명시적으로 runat="server-nocache"또는 runat="both-nocache"라고 작성되지 않았다면.), 그리고, 그 함수들을 Callback 중에 다른 함수가 사용 가능하도록 한다. - 하지만 브라우저에서는 사용이 불가능하다. (우리는 단순화를 위해 비동기 버전을 생략했다.) :
<script runat="server">
        function getCurrentUser() {
                return Jaxer.session.get("user");
                // this will be available to other functions during a callback
        }

        function isAuthorized(user) {
                // this will be available to other functions during a callback
                return user.authorizationLevel > 4; // substitute some logic here
        }

        function getLastName(firstName) {
                var user = getCurrentUser();
                if( !isAuthorized(user)) throw "You are not authorized";
                var lastName = Jaxer.DB.execute(
                "SELECT lastName FROM names WHERE firstName = ?", firstName).singleResult;
                return lastName;
        }
        getLastName.proxy = true;
</script>
First name : <input type="text" id="first">
<input type="button" onclick="showFullName()">

모든 서버측 함수 getCurrentUser(), isAuthorized(), 그리고 getLastName()은 페이지 처리 후 저장되고, callback중에 사용이 가능하다.; 그러나 단 하나, getLastName()은 브라우저에서 직접 호출이 가능하도록 허용되어 있다. 그리고 필요에 따라 다른 함수를 호출할 수 있다. 브라우저를 통해 사용자와 상호작용하는 흐름에 실질적으로 필요한 단 하나의 proxied 함수를 한정짓는 방법에 대한 좋은 예제이다.


callback에 대해 좀 더 상세히 이해하고 싶다면 페이지의 lifecycle을 고려해라. Jaxer는 페이지를 읽고, 그것을 HTML으로부터 DOM으로 생성하는 처리를 하고, 몇몇의 함수를 생성하고, DOM을 조작하는 서버측 Javascript로 구동한다. 페이지의 끝 부분에는 HTML DOM이 HTML로 변환되고, 브라우저로 전송된다. 그러면서 Jaxer는 Callback 하는 동안 필요한 함수를 캐시하고, 서버 측의 global "window" Javascript 컨텍스트를 제거하고, 그 컨텍스트 내의 모든 Javascript 함수와 데이터 역시 DOM으로 변경한다. 그리고 그것에 대한 다음 요청을 준비한다. 캐시된 함수들은 데이터베이스에 저장되지만, 메모리에도 bytecode로 동시에 저장되기 때문에 효율적이다.(Jaxer 인스턴스의 메모리). 그러면 매번 callback때 마다 빈 DOM을 가진 빈 "window" 오브젝트가 가용하게 되고, 해당 페이지를 위해 캐시된 함수들이 그 "window"에 재구성된다.(필요하다면, database로부터 가져온 후, 메모리 내의 bytecode를 이용한다.) 마지막으로, Callback에서 한정된 특별한 함수가 실행된다. (우리는 함수의 versioning, 브라우저로, 브라우저에서의 데이터 패키징, 예외처리 등에 관해 상세한 언급은 하지 않겠다.)


이러한 흐름에 따라, 당신은 Callback하는 동안 가용한 환경이, 최초 페이지의 그것과 동일하지 않다는 것을 볼 수 있었을 것이다. 기본적으로, 단지 캐시된 함수만 가용하다. 대부분의 경우에, 동작이 원활하고, 매우 효율적이다.: 함수를 호출하기 위해서 기존 페이지 전체를 재 구성할 필요가 없고, callback 도중에 브라우저에서 변경될 가능성이 있는 DOM에 대해 어떻게든 그 DOM과 동기화 시키기 위한 복잡함도 없다. 그리고 그 페이지에서 사용자 별로 한정된 데이터가 다른 사용자에게 개방되는 위험성 등에 대한 문제도 없다. 그러다 가끔 당신이 좀 더 컨트롤 해야할 필요가 있을 때가 있는데, 예를 들면, 서버 측 라이브러리를 가용하게 만들때가 그러하다. 만약 서버 측 라이브러리가 순수하게 함수로만 구성되어 있다면, 아마도 페이지가 초기에 처리 되는 동안 로드될 때 자동으로 캐시가 되며, 모든 것이 정상 동작하게 된다. 그러나 만약 비-함수적인 객체가 존재할 필요성이 있을 경우, 당신은 Callback되는 동안 그러한 것들을 스스로 재구성 할 필요가 있다. 어떻게 그것을 할 수 있을까? 만약 그 라이브러리가 외부 Javascript 파일을 포함할 경우, 가장 쉬운 방법은, "<script src="myLibrary.js" runat="server" autorun="true"></script>"를 통해 로드하는 것이다. autorun attribute는 Jaxer가 이 페이지에 대한 모든 요청 뿐 아니라 이 페이지에 대한 모든 callback에도 해당 라이브러리를 실행하도록 한다.; 효율성을 위해, 라이브러리는 bytecode로 컴파일 되고, 메모리에 저장된다.(Jaxer 인스턴스의 메모리). 다른 방법으로, 당신은 oncallback 이라고 불리는 특별한 함수를 정의할 수 있다.이 특별한 함수는 당신이 callback을 수행하도록 한 함수 전에, 이 페이지에 대한 모든 callback에서 실행된다. 당신의 oncallback() 함수는 환경이 세팅되어 잇는지, 그렇지 않으면 어떤것을 할 수 있는지 등을 체크할 수 있다. 좀 더 상세한 내용을 보고 싶다면, "the FAQ entry on oncallback()"을 보라.
Leave a comment
« Previous : 1 : 2 : 3 : 4 : 5 : 6 : 7 : Next »