메인 컨텐츠로 가기

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

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

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

  • 닫기 [x]

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

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

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

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

  • 닫기 [x]

Java Scripting API에 대해 모르고 있던 5가지 사항

Java 플랫폼에서 더욱 쉽게 스크립트를 작성하는 방법

Ted Neward, Principal, Neward & Associates
Ted Neward는 글로벌 컨설팅 업체인 ThoughtWorks의 컨설턴트이자 Neward & Associates의 회장으로 Java, .NET, XML 서비스 및 기타 플랫폼에 대한 컨설팅, 조언, 교육 및 강연을 한다. 워싱턴 주의 시애틀 근교에 살고 있다.

요약:  Java™ 언어는 일부 프로젝트에서 너무 과하다는 느낌을 주는 반면 스크립팅 언어는 성능이 떨어진다는 느낌을 줍니다. 이 기사에서는 Java Scripting API(javax.script)가 스크립트와 Java 프로그램 간에 상호 호출할 수 있는 기능을 통해 두 환경에 필요한 해결책을 제공하는 방법에 대해 살펴봅니다.

원문 게재일:  2010 년 7 월 27 일 번역 게재일:   2010 년 11 월 16 일
난이도:  초급 영어로:  보기 PDF:  A4 and Letter (32KB | 8 pages)Get Adobe® Reader®
페이지뷰:  3223 회
의견:  


이 시리즈의 정보

Java 프로그래밍에 대해 알고 있다고 생각하는가? 하지만 실제로는 대부분의 개발자가 작업을 수행하기에 충분할 정도만 알고 있을 뿐 Java 플랫폼에 대해서는 자세히 알고 있지 않다. 이 시리즈에서 Ted Neward는 Java 플랫폼의 핵심 기능에 대한 자세한 설명을 통해 까다로운 프로그래밍 과제를 해결하는 데 도움이 되는 알려져 있지 않은 사실을 밝힌다.

오늘날 많은 Java 개발자가 Java 플랫폼에서 스크립팅 언어를 사용하는 것에 관심을 가지고 있지만 Java 바이트코드로 컴파일된 동적 언어를 사용하는 것이 항상 가능하지는 않다. 어떤 경우에는 Java 애플리케이션의 일부만 스크립트로 작성하거나 스크립트 내에서 필요한 특정 Java 오브젝트를 호출하는 것이 더 빠르고 효율적이다.

바로 그 때문에 javax.script가 필요하다. Java 6에 도입된 Java Scripting API는 사용하기 편리한 간단한 스크립팅 언어와 강력한 Java 에코시스템을 연결해 준다. Java Scripting API를 사용하면 거의 모든 스크립팅 언어를 Java 코드와 빠르게 통합할 수 있다. 따라서 간단한 문제점을 해결할 때 상당히 다양한 방법을 활용할 수 있다.

1. jrunscript로 JavaScript 실행하기

각각의 새 Java 플랫폼 릴리스는 JDK의 bin 디렉토리 내에 새 명령행 도구 세트를 포함하고 있다. Java 6도 예외가 아니며 jrunscript는 중요한 Java 플랫폼 유틸리티 중 하나이다.

성능 모니터링을 위한 명령행 스크립트를 작성할 때 발생할 수 있는 기본적인 문제점을 살펴보자. 이 도구에서는 프로세스의 실행 상태를 확인하기 위해 이 시리즈의 이전 기사에서 설명한 jmap을 Java 프로세스에 대해 5초마다 실행한다. 일반적으로 명령행 쉘 스크립트만으로도 충분하겠지만 이 경우에는 서버 애플리케이션을 Windows®와 Linux®를 포함한 다양한 플랫폼에 전개한다. 시스템 관리자에게 물어보면 두 플랫폼에서 모두 실행되는 쉘 스크립트를 작성하는 것이 얼마나 어려운 일인지 잘 알 수 있을 것이다. 일반적으로 Windows 일괄처리 파일과 UNIX® 쉘 스크립트를 작성한 후 두 파일의 동기화를 지속적으로 유지하는 방법이 주로 사용된다.

하지만 The Pragmatic Programmer를 읽어본 독자라면 알겠지만 이 방법은 DRY(Don't Repeat Yourself) 원칙을 심각하게 위반할 뿐만 아니라 버그 및 결함의 배양소가 될 수 있다. 우리가 진정으로 원하는 바는 모든 플랫폼에서 실행할 수 있는 일종의 OS 중립적 스크립트를 작성하는 것이다.

물론 Java 언어가 플랫폼 중립적이기는 하지만 지금 여기에서 다루고자 하는 바는 "시스템" 언어가 아니다. 우리에게 필요한 것은 JavaScript와 같은 스크립팅 언어이다.

먼저 Listing 1에서는 이 기사에서 설명하려는 기능의 기본 쉘을 보여 준다.


Listing 1. periodic.js
while (true)
{
    echo("Hello, world!");
}

웹 브라우저와의 상호 작용이 중요해 지면서 대부분은 아닐지라도 많은 Java 개발자가 JavaScript(또는 ECMAScript)를 이미 알고 있다. (JavaScript는 Netscape 소유 ECMAScript의 또 다른 이름이다.) 따라서 이제는 시스템 관리자가 이 스크립트를 어떻게 실행할 수 있는가라는 질문에 대한 답을 찾을 차례이다.

물론 그 답은 JDK에 포함된 jrunscript 유틸리티이다(Listing 2 참조).


Listing 2. jrunscript
C:\developerWorks\5things-scripting\code\jssrc>jrunscript periodic.js
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
...

for 루프를 사용해서도 종료하기 전까지 지정된 횟수 동안 스크립트를 실행할 수 있다. 기본적으로 jrunscript를 사용하면 JavaScript로 수행할 수 있는 거의 모든 작업을 수행할 수 있다. 유일한 예외는 환경이 브라우저가 아니기 때문에 DOM이 없다는 것이다. 따라서 사용할 수 있는 최상위 함수 및 오브젝트가 약간 다르다.

Java 6에는 Rhino ECMAScript 엔진이 JDK의 일부로 포함되어 있기 때문에 jrunscript가 이 기사에서 보듯이 파일로부터 제공되거나 REPL("Read-Evaluate-Print-Loop")이라는 대화식 쉘 환경에서 제공되는 ECMAScript 코드를 실행할 수 있다. jrunscript를 실행하여 REPL 쉘에 액세스할 수 있다.


2. 스크립트에서 Java 오브젝트에 액세스하기

JavaScript/ECMAScript 코드를 작성하는 것도 좋지만 이 기사에서는 Java 언어에서 사용하는 모든 기능을 처음부터 다시 빌드하는 것을 원하지 않는다. 왜냐하면 목적에 부합하지 않기 때문이다. 다행스럽게도 Java Scripting API 엔진을 사용하는 기능은 본질적으로 Java 바이트코드이므로 전체 Java 에코시스템에 액세스할 수 있다. 따라서 앞의 문제점을 다시 생각해 보면, 일반적인 Runtime.exec() 호출을 사용하여 Java 플랫폼의 프로세스를 실행할 수 있다(Listing 3 참조).


Listing 3. Runtime.exec()로 jmap 실행하기
var p = java.lang.Runtime.getRuntime().exec("jmap", [ "-histo", arguments[0] ])
p.waitFor()

arguments 배열은 이 함수에 전달되는 인수에 대한 ECMAScript 표준 내장 참조이다. 최상위 스크립트 환경의 경우 이 배열은 스크립트 자체에 전달되는 인수 배열(명령행 매개변수)이다. 따라서 Listing 3의 스크립트는 맵핑할 Java 프로세스의 VMID가 포함된 인수를 기대한다.

또는 jmap이 Java 클래스라는 점을 감안하여 이 클래스의 main() 메소드를 호출할 수도 있다(Listing 4 참조). 이 방법을 사용하면 Process 오브젝트의 in/out/err 스트림을 "파이프로 연결"하지 않아도 된다.


Listing 4. JMap.main()
var args = [ "-histo", arguments[0] ]
Packages.sun.tools.jmap.JMap.main(args)

Packages 구문은 Rhino 내에 이미 설정되어 있는 핵심 java.* 패키지에 포함되지 않은 Java 패키지를 참조하는 데 사용되는 Rhino ECMAScript 표기법이다.


3. Java 코드에서 스크립트 호출하기

스크립트에서 Java 오브젝트를 호출하는 것은 단지 반쪽에 불과하다. Java 스크립팅 환경은 Java 코드 내에서 스크립트를 호출하는 기능도 제공한다. 이를 위해서는 ScriptEngine을 인스턴스화한 후 스크립트를 로드하고 평가하기만 하면 된다(Listing 5 참조).


Listing 5. Java 플랫폼에서의 스크립팅
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
            for (String arg : args)
            {
                FileReader fr = new FileReader(arg);
                engine.eval(fr);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

eval() 메소드는 String에 대해서도 작동하므로 스크립트를 파일 시스템에 있는 파일에서 가져오지 않아도 되며 데이터베이스나 사용자로부터 가져오거나 애플리케이션 내에서 환경 및 사용자 조치를 기반으로 작성할 수도 있다.


4. 스크립트 공간에 Java 오브젝트 바인드하기

스크립트를 호출하는 것만으로는 충분하지 않다. 스크립트에서 Java 환경 내에서 작성된 오브젝트와 상호 작용해야 하는 경우도 있다. 이러한 경우 Java 호스트 환경에서는 오브젝트를 작성하고 바인드해야 한다. 이렇게 하면 스크립트에서 오브젝트를 쉽게 찾아서 사용할 수 있다. 이는 ScriptContext 오브젝트에 대한 태스크이다(Listing 6 참조).


Listing 6. 스크립트에 대한 오브젝트 바인드하기
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
                
            for (String arg : args)
            {
                Bindings bindings = new SimpleBindings();
                bindings.put("author", new Person("Ted", "Neward", 39));
                bindings.put("title", "5 Things You Didn't Know");
                
                FileReader fr = new FileReader(arg);
                engine.eval(fr, bindings);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

바인드된 오브젝트에 액세스하는 방법은 간단하다. 바인드된 오브젝트의 이름이 스크립트 레벨에서 글로벌 네임스페이스의 멤버로 소개되므로 Listing 7과 같이 손쉽게 Rhino의 Person 오브젝트를 사용할 수 있다.


Listing 7. 이 기사의 필자는 누구인가?
println("Hello from inside scripting!")

println("author.firstName = " + author.firstName)

위 Listing에서 보듯이 JavaBeans 스타일 특성은 마치 필드처럼 직접적인 이름 액세서로 축소된다.


5. 자주 사용하는 스크립트 컴파일하기

스크립팅 언어의 단점은 항상 성능에 있다. 그 이유는 대부분의 스크립팅 언어가 "즉석에서" 해석되고 실행될 때마다 구문 분석 및 유효성 검증을 수행하기 위해 시간과 CPU 주기를 소비해야 하기 때문이다. JVM에서 실행되는 많은 스크립팅 언어는 적어도 스크립트가 처음으로 구문 분석 및 유효성 검증될 때 수신 코드에서 Java 바이트코드로 변환되기 마련이며, 이 즉석 컴파일은 Java 프로그램이 종료될 때 삭제된다. 자주 사용하는 스크립트를 바이트코드 양식으로 유지하면 상당한 성능 향상 효과를 얻을 수 있다.

Java Scripting API를 사용하여 자연스럽고 의미 있는 방식으로 이 작업을 수행할 수 있다. 리턴된 ScriptEngineCompilable 인터페이스가 구현되어 있다면 이 인터페이스의 compile 메소드를 사용하여 스크립트(String 또는 Reader로 전달됨)를 CompiledScript 인스턴스로 컴파일할 수 있다. 그런 다음 이 인스턴스를 사용하여 다양한 바인딩을 가지고 반복적으로 컴파일된 코드에 대한 eval()을 수행할 수 있다. Listing 8에서 이 작업을 보여 준다.


Listing 8. 해석된 코드 컴파일하기
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
                
            for (String arg : args)
            {
                Bindings bindings = new SimpleBindings();
                bindings.put("author", new Person("Ted", "Neward", 39));
                bindings.put("title", "5 Things You Didn't Know");
                
                FileReader fr = new FileReader(arg);
                if (engine instanceof Compilable)
                {
                    System.out.println("Compiling....");
                    Compilable compEngine = (Compilable)engine;
                    CompiledScript cs = compEngine.compile(fr);
                    cs.eval(bindings);
                }
                else
                    engine.eval(fr, bindings);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

대부분의 경우 동일한 스크립트를 반복해서 다시 컴파일하지 않으려면 CompiledScript 인스턴스를 장기 저장소(예: servlet-context)에 저장해야 한다. 하지만 스크립트 내용을 변경해야 하는 경우에는 새 CompiledScript를 작성하여 변경을 반영해야 한다. 컴파일된 CompiledScript는 더 이상 원본 스크립트 파일의 내용을 실행하지 않는다.


결론

Java Scripting API는 Java 프로그램의 지평과 기능을 넓힐 수 있는 중요한 도약대이며 Java 환경에서도 스크립팅 언어의 생산성을 얻을 수 있는 기회를 제공한다. 프로그램을 작성하는 것보다 훨씬 쉽게 사용할 수 있는 jrunscript와 함께 javax.script는 Java 개발자에게 Java 환경의 에코시스템과 확장성을 포기하지 않고도 Ruby(JRuby) 및 ECMAScript(Rhino)와 같은 스크립팅 언어를 활용할 수 있는 방법을 제공한다.

5가지 사항 시리즈의 다음 주제는 JDBC이다.


참고자료

교육

  • 모르고 있던 5가지 사항: Java 플랫폼에 대해 모르는 것이 많았다는 것을 일깨워 주는 이 시리즈에서는 사소하게 여겼던 Java 기술을 유용한 프로그래밍 팁으로 바꿔준다.

  • "동적인 언어를 동적으로 호출하기, Part 1: Java Scripting API"(Tom McQueeney 저, developerWorks, 2007년 9월): 두 편의 기사로 이 시리즈의 Part 1에서는 Java Scripting API의 기능을 소개하고, Part 2에서는 이 API의 활용 방법을 자세히 설명한다.

  • "JavaScript EE, Part 3: Use Java scripting API with JSP"(Andrei Cioroianu 저, developerWorks, 2009년 6월): JavaScript와 Java 플랫폼을 결합하는 방법과 웹 브라우저에서 JavaScript가 사용되지 않을 때도 기능이 유지되는 Ajax 사용자 인터페이스를 빌드하는 방법에 대해 알아보자.

  • JDK Tools and Utilities: 성능 모니터링과 관련하여 5가지 사항에서 중점적으로 다루고 있는 jmap을 포함한 여러 가지 실험 수준의 모니터링 및 문제점 해결 도구에 대해 알아보자.

  • developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자.

토론

  • My developerWorks 커뮤니티에 참여하자. 개발자가 이끌고 있는 블로그, 포럼, 그룹 및 Wiki를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

필자소개

Ted Neward는 글로벌 컨설팅 업체인 ThoughtWorks의 컨설턴트이자 Neward & Associates의 회장으로 Java, .NET, XML 서비스 및 기타 플랫폼에 대한 컨설팅, 조언, 교육 및 강연을 한다. 워싱턴 주의 시애틀 근교에 살고 있다.

잘못된 도움말 신고

부정사용 신고

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


잘못된 도움말 신고

부정사용 신고

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


디벨로퍼웍스 로그인


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=580241
ArticleTitle=Java Scripting API에 대해 모르고 있던 5가지 사항
publish-date=07272010
author1-email=ted@tedneward.com
author1-email-cc=

태그

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

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

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

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

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