Java의 Nashorn 자바스크립트 엔진 활용하기 🚀

콘텐츠 대표 이미지 - Java의 Nashorn 자바스크립트 엔진 활용하기 🚀

 

 

2025년 최신 트렌드로 알아보는 Nashorn의 모든 것!

안녕! 오늘은 Java의 숨겨진 보석 같은 기능인 Nashorn 자바스크립트 엔진에 대해 함께 알아볼 거야. 자바와 자바스크립트를 함께 사용하면 어떤 마법 같은 일이 벌어질까? 🧙‍♂️ 지금부터 Nashorn의 세계로 함께 떠나보자!

Nashorn Java JavaScript Java + JavaScript = 강력한 개발 경험

🔍 Nashorn이 뭐길래? 기본 개념 알아보기

Nashorn(나스호른)은 독일어로 '코뿔소'라는 뜻이야. 왜 코뿔소 이름을 붙였을까? 아마도 강력하고 빠른 성능을 상징하기 위해서겠지! 😄

Nashorn은 Java 8에서 처음 도입된 자바스크립트 엔진으로, 기존의 Rhino 엔진을 대체했어. Java 가상 머신(JVM) 위에서 자바스크립트 코드를 실행할 수 있게 해주는 엔진이지. 이게 왜 중요하냐고? Java의 강력한 기능과 자바스크립트의 유연성을 함께 사용할 수 있다는 거야! 🎯

2025년 현재, Nashorn은 Java 15부터 공식적으로 deprecated 되었지만, 여전히 많은 레거시 시스템과 특수 용도에서 활용되고 있어. 또한 Nashorn의 개념과 활용법을 이해하면 최신 GraalVM의 JavaScript 엔진도 쉽게 이해할 수 있지!

📌 Nashorn의 주요 특징

  1. JVM 위에서 자바스크립트 실행 가능
  2. Java와 JavaScript 간 쉬운 상호 운용성
  3. jjs 명령줄 도구 제공
  4. ECMAScript 5.1 표준 지원
  5. Java 8부터 기본 탑재 (Java 15부터 deprecated)

⚙️ Nashorn 시작하기: 기본 설정과 사용법

자, 이제 Nashorn을 어떻게 사용하는지 알아볼까? Java 프로젝트에서 Nashorn을 사용하는 건 생각보다 정말 간단해! 😊

1. 기본 설정

Java 8부터 Java 14까지는 별도의 설치 없이 바로 사용할 수 있어. 다음 import만 추가하면 돼:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

2. 간단한 자바스크립트 실행하기

다음은 Nashorn을 사용해 간단한 자바스크립트 코드를 실행하는 예제야:

public class NashornDemo {
    public static void main(String[] args) throws ScriptException {
        // Nashorn 엔진 생성
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        // 자바스크립트 코드 실행
        Object result = engine.eval("'안녕, Nashorn! ' + (5 + 3)");
        System.out.println(result);  // 출력: 안녕, Nashorn! 8
    }
}

정말 간단하지? ScriptEngineManager로 Nashorn 엔진을 가져오고, eval() 메소드로 자바스크립트 코드를 실행하면 끝이야! 🎉

🔄 Java와 JavaScript 간의 데이터 교환

Nashorn의 진짜 매력은 Java와 JavaScript 사이에서 데이터를 주고받을 수 있다는 점이야. 이건 정말 강력한 기능이지! 🚀

Java JavaScript 데이터 전달 결과 반환 List, Map, 객체 메소드, 클래스 배열, 객체 함수, JSON Nashorn을 통한 Java와 JavaScript 데이터 교환

1. Java에서 JavaScript로 데이터 전달하기

Java 객체나 변수를 JavaScript에 전달하는 방법을 알아보자:

import javax.script.*;
import java.util.*;

public class DataExchangeDemo {
    public static void main(String[] args) throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        // Java 데이터 생성
        List<String> fruits = new ArrayList<>();
        fruits.add("사과");
        fruits.add("바나나");
        fruits.add("오렌지");
        
        // JavaScript에 데이터 바인딩
        Bindings bindings = engine.createBindings();
        bindings.put("fruitList", fruits);
        engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        
        // JavaScript에서 Java 데이터 사용
        String script = "print('과일 목록: ' + fruitList);" +
                       "print('첫 번째 과일: ' + fruitList[0]);" +
                       "var fruitCount = fruitList.size();" +
                       "print('과일 개수: ' + fruitCount);";
        
        engine.eval(script);
    }
}

Bindings 객체를 사용하면 Java의 데이터를 JavaScript에서 쉽게 접근할 수 있어. 위 예제에서는 Java의 ArrayList를 JavaScript에 전달했고, JavaScript에서는 이 리스트를 마치 배열처럼 사용할 수 있어! 👍

2. JavaScript에서 Java로 결과 반환하기

이번엔 JavaScript에서 계산한 결과를 Java로 가져와 보자:

public class ReturnDataDemo {
    public static void main(String[] args) throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        // JavaScript에서 계산 후 결과 반환
        String script = "var result = 0;" +
                       "for(var i = 1; i <= 10; i++) {" +
                       "    result += i;" +
                       "}" +
                       "result;";  // 마지막 값이 반환됨
        
        Object sum = engine.eval(script);
        System.out.println("1부터 10까지의 합: " + sum);  // 출력: 55
        
        // JavaScript 객체 생성 후 Java로 가져오기
        String objectScript = "var person = {" +
                             "    name: '홍길동'," +
                             "    age: 25," +
                             "    greeting: function() { return '안녕하세요, ' + this.name + '입니다!'; }" +
                             "};" +
                             "person;";
        
        Object personObj = engine.eval(objectScript);
        // Nashorn의 ScriptObjectMirror로 JavaScript 객체 다루기
        jdk.nashorn.api.scripting.ScriptObjectMirror person = 
            (jdk.nashorn.api.scripting.ScriptObjectMirror) personObj;
        
        System.out.println("이름: " + person.get("name"));
        System.out.println("나이: " + person.get("age"));
        System.out.println("인사: " + person.callMember("greeting"));
    }
}

JavaScript에서 생성한 객체는 ScriptObjectMirror 클래스를 통해 Java에서 접근할 수 있어. 이렇게 하면 JavaScript 객체의 속성과 메소드를 Java에서도 사용할 수 있지! 😎

🛠️ 실용적인 Nashorn 활용 사례

이론은 충분히 알아봤으니, 이제 Nashorn을 실제로 어떻게 활용할 수 있는지 몇 가지 재미있는 예제를 통해 알아보자! 🎮

1. 동적 스크립트 평가 및 실행

런타임에 사용자 입력이나 외부 소스에서 가져온 스크립트를 동적으로 실행할 수 있어:

import javax.script.*;
import java.util.Scanner;

public class DynamicScriptDemo {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        Scanner scanner = new Scanner(System.in);
        System.out.println("계산할 수식을 입력하세요 (예: 2 + 2 * 3):");
        String expression = scanner.nextLine();
        
        try {
            Object result = engine.eval(expression);
            System.out.println("계산 결과: " + result);
        } catch (ScriptException e) {
            System.out.println("오류 발생: " + e.getMessage());
        }
        
        scanner.close();
    }
}

이 예제는 간단한 계산기처럼 작동해. 사용자가 입력한 수식을 Nashorn이 평가하고 결과를 반환하지. 이런 방식으로 동적 규칙 엔진이나 사용자 정의 스크립트 실행 환경을 만들 수 있어! 🧮

2. 템플릿 엔진 구현하기

Nashorn을 사용해 간단한 템플릿 엔진을 만들 수도 있어:

import javax.script.*;
import java.util.*;

public class TemplateEngineDemo {
    public static void main(String[] args) throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        // 템플릿 데이터
        Map<String, Object> data = new HashMap<>();
        data.put("name", "김자바");
        data.put("company", "재능넷");
        data.put("skills", Arrays.asList("Java", "JavaScript", "Nashorn"));
        
        // 템플릿 문자열
        String template = "안녕하세요, ${name}님!\n" +
                         "${company}에서 근무하시는군요.\n" +
                         "보유 기술: ${skills.join(', ')}";
        
        // 템플릿 처리 스크립트
        String script = "function processTemplate(template, data) {" +
                       "    return template.replace(/\\${([^}]+)}/g, function(match, expr) {" +
                       "        return eval('data.' + expr);" +
                       "    });" +
                       "}" +
                       "processTemplate(template, data);";
        
        // Nashorn에 데이터 바인딩
        Bindings bindings = engine.createBindings();
        bindings.put("template", template);
        bindings.put("data", data);
        engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        
        // 템플릿 처리 및 결과 출력
        String result = (String) engine.eval(script);
        System.out.println(result);
    }
}

이 예제는 간단한 템플릿 엔진을 구현한 거야. JavaScript의 정규식과 문자열 처리 기능을 활용해 템플릿에 데이터를 채워넣었어. 재능넷 같은 플랫폼에서 동적 콘텐츠 생성에 이런 방식을 활용할 수 있지! 📝

3. 자바 애플리케이션에 스크립트 기능 추가하기

사용자가 직접 스크립트를 작성해 애플리케이션의 동작을 확장할 수 있게 해보자:

import javax.script.*;
import java.io.*;

public class ScriptableAppDemo {
    // 사용자 정의 클래스
    public static class Calculator {
        public double add(double a, double b) { return a + b; }
        public double subtract(double a, double b) { return a - b; }
        public double multiply(double a, double b) { return a * b; }
        public double divide(double a, double b) { return a / b; }
    }
    
    public static void main(String[] args) {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");
            
            // Java 객체를 JavaScript에 노출
            Calculator calc = new Calculator();
            engine.put("calculator", calc);
            
            // 사용자 스크립트 파일 읽기 (예시)
            String userScript = "// 사용자 정의 함수\n" +
                               "function calculateArea(radius) {\n" +
                               "    return Math.PI * radius * radius;\n" +
                               "}\n\n" +
                               "// 기본 계산기 기능 사용\n" +
                               "var sum = calculator.add(10, 20);\n" +
                               "print('10 + 20 = ' + sum);\n\n" +
                               "// 사용자 정의 함수 사용\n" +
                               "var radius = 5;\n" +
                               "var area = calculateArea(radius);\n" +
                               "print('반지름이 ' + radius + '인 원의 넓이: ' + area.toFixed(2));\n";
            
            // 스크립트 실행
            engine.eval(userScript);
            
            // 사용자 정의 함수 Java에서 호출
            Invocable invocable = (Invocable) engine;
            Object result = invocable.invokeFunction("calculateArea", 10);
            System.out.println("Java에서 호출: 반지름이 10인 원의 넓이: " + result);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

이 예제에서는 Java 애플리케이션에 스크립팅 기능을 추가했어. 사용자가 작성한 JavaScript 함수를 Java 코드에서 직접 호출할 수도 있지. 이런 방식으로 플러그인 시스템이나 확장 가능한 애플리케이션을 구현할 수 있어! 🔌

📊 Nashorn의 성능과 최적화

Nashorn을 실제 프로젝트에 적용하기 전에 성능 특성과 최적화 방법도 알아두면 좋을 것 같아! 🏎️

최적화 기법 성능 향상 기본 사용 컴파일 캐싱 타입 힌트 직접 호출 멀티스레딩 Nashorn 성능 최적화 기법 비교

1. 스크립트 컴파일 캐싱

반복적으로 같은 스크립트를 실행한다면, 매번 컴파일하지 말고 미리 컴파일해두는 것이 좋아:

import javax.script.*;

public class CompiledScriptDemo {
    public static void main(String[] args) {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");
            
            // 컴파일 가능한 엔진으로 캐스팅
            Compilable compilable = (Compilable) engine;
            
            // 스크립트 컴파일
            String script = "function factorial(n) { " +
                           "    if (n <= 1) return 1; " +
                           "    return n * factorial(n-1); " +
                           "} " +
                           "factorial(n);";
            
            CompiledScript compiledScript = compilable.compile(script);
            
            // 여러 번 실행 (다른 매개변수로)
            long startTime = System.nanoTime();
            
            for (int i = 1; i <= 20; i++) {
                Bindings bindings = engine.createBindings();
                bindings.put("n", i);
                Object result = compiledScript.eval(bindings);
                System.out.println(i + "! = " + result);
            }
            
            long endTime = System.nanoTime();
            System.out.println("실행 시간: " + (endTime - startTime) / 1_000_000 + "ms");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CompiledScript를 사용하면 스크립트를 한 번만 컴파일하고 여러 번 재사용할 수 있어. 이는 반복적으로 스크립트를 실행할 때 성능을 크게 향상시켜줘! 🚀

2. 타입 힌트 사용하기

JavaScript는 동적 타입 언어지만, Nashorn에서는 타입 힌트를 제공해 성능을 개선할 수 있어:

// 타입 힌트가 없는 함수
var slowSum = function(a, b) {
    return a + b;
};

// 타입 힌트가 있는 함수
var fastSum = function(a, b) {
    "use strict";
    // @param {int} a
    // @param {int} b
    // @returns {int}
    return a + b;
};

주석으로 타입 힌트를 제공하면 Nashorn이 최적화된 코드를 생성할 수 있어. 특히 숫자 계산이 많은 코드에서 성능 차이가 크게 나타날 수 있지! 🔢

3. 멀티스레딩 환경에서의 Nashorn

Nashorn 엔진은 기본적으로 스레드 안전하지 않아. 멀티스레드 환경에서는 각 스레드마다 별도의 엔진 인스턴스를 생성해야 해:

import javax.script.*;
import java.util.concurrent.*;

public class ThreadSafeNashornDemo {
    public static void main(String[] args) {
        // 스레드 풀 생성
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // 여러 작업 제출
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    // 각 스레드마다 새로운 엔진 인스턴스 생성
                    ScriptEngineManager manager = new ScriptEngineManager();
                    ScriptEngine engine = manager.getEngineByName("nashorn");
                    
                    // 스크립트 실행
                    engine.put("taskId", taskId);
                    Object result = engine.eval("'Task ' + taskId + ' 실행 중: ' + Math.random()");
                    System.out.println(result);
                    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 스레드 풀 종료
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

각 스레드마다 별도의 ScriptEngine 인스턴스를 생성하면 멀티스레드 환경에서도 안전하게 Nashorn을 사용할 수 있어! 🧵

🔒 보안 고려사항과 제한사항

Nashorn을 사용할 때는 보안 측면도 반드시 고려해야 해. 특히 외부에서 제공된 스크립트를 실행할 때는 더욱 주의가 필요해! 🛡️

1. 보안 관리자 설정

Java의 SecurityManager를 사용해 스크립트의 권한을 제한할 수 있어:

import javax.script.*;
import java.security.*;

public class SecurityDemo {
    public static void main(String[] args) {
        try {
            // 보안 관리자 설정
            System.setSecurityManager(new SecurityManager());
            
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");
            
            try {
                // 파일 시스템 접근 시도
                engine.eval("var file = new java.io.File('/etc/passwd'); print(file.exists());");
            } catch (Exception e) {
                System.out.println("보안 예외 발생: " + e.getMessage());
            }
            
            // 안전한 연산은 허용
            Object result = engine.eval("'안전한 계산: ' + (2 + 2)");
            System.out.println(result);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

SecurityManager를 설정하면 스크립트가 파일 시스템이나 네트워크 같은 민감한 리소스에 접근하는 것을 제한할 수 있어. 이는 외부 스크립트를 실행할 때 특히 중요해! 🔐

2. 클래스 필터링

스크립트에서 접근할 수 있는 Java 클래스를 제한할 수도 있어:

import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.*;

public class ClassFilterDemo {
    // 클래스 필터 구현
    static class MyClassFilter implements ClassFilter {
        @Override
        public boolean exposeToScripts(String className) {
            // java.util 패키지는 허용, java.io와 java.net은 거부
            return className.startsWith("java.util") && 
                  !className.startsWith("java.io") && 
                  !className.startsWith("java.net");
        }
    }
    
    public static void main(String[] args) {
        try {
            // 필터가 적용된 Nashorn 엔진 생성
            NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
            ScriptEngine engine = factory.getScriptEngine(new MyClassFilter());
            
            // 허용된 클래스 사용
            engine.eval("var list = new java.util.ArrayList(); " +
                       "list.add('항목 1'); " +
                       "print('ArrayList 사용: ' + list);");
            
            try {
                // 거부된 클래스 사용 시도
                engine.eval("var file = new java.io.File('test.txt');");
            } catch (Exception e) {
                System.out.println("예상된 예외 발생: " + e.getMessage());
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ClassFilter를 구현하면 스크립트에서 접근할 수 있는 Java 클래스를 명시적으로 제한할 수 있어. 이렇게 하면 스크립트가 민감한 시스템 기능에 접근하는 것을 방지할 수 있지! 🚫

3. 타임아웃 설정

무한 루프 같은 악의적인 스크립트로부터 시스템을 보호하기 위해 실행 시간 제한을 설정할 수 있어:

import javax.script.*;
import java.util.concurrent.*;

public class TimeoutDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        // 무한 루프 스크립트
        String infiniteLoopScript = "while(true) { }";
        
        Future<Object> future = executor.submit(() -> {
            try {
                return engine.eval(infiniteLoopScript);
            } catch (ScriptException e) {
                return e.getMessage();
            }
        });
        
        try {
            // 3초 타임아웃 설정
            Object result = future.get(3, TimeUnit.SECONDS);
            System.out.println("결과: " + result);
        } catch (TimeoutException e) {
            System.out.println("스크립트 실행 시간이 너무 깁니다. 중단합니다.");
            future.cancel(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        executor.shutdownNow();
    }
}

ExecutorService와 Future를 사용하면 스크립트 실행 시간을 제한할 수 있어. 이렇게 하면 무한 루프나 리소스를 과도하게 사용하는 스크립트로부터 시스템을 보호할 수 있지! ⏱️

🔄 Nashorn의 대안과 미래

Nashorn이 Java 15부터 deprecated 되었다고 했지? 그렇다면 대안은 무엇이고, 앞으로 어떻게 발전할까? 🔮

1. GraalVM JavaScript

Nashorn의 가장 강력한 대안은 GraalVM의 JavaScript 엔진이야:

// Maven 의존성
<dependency>
    <groupId>org.graalvm.js</groupId>
    <artifactId>js</artifactId>
    <version>23.0.0</version>
</dependency>
<dependency>
    <groupId>org.graalvm.js</groupId>
    <artifactId>js-scriptengine</artifactId>
    <version>23.0.0</version>
</dependency>

GraalVM JavaScript는 ECMAScript 2022 표준을 지원하고, Nashorn보다 훨씬 뛰어난 성능을 제공해. 또한 Node.js 모듈도 사용할 수 있어서 호환성이 매우 뛰어나지! 💪

import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;

public class GraalJSDemo {
    public static void main(String[] args) {
        try (Context context = Context.create("js")) {
            // JavaScript 코드 실행
            Value result = context.eval("js", "({ message: '안녕, GraalVM!', answer: 42 })");
            
            // JavaScript 객체의 속성 접근
            System.out.println(result.getMember("message").asString());  // 안녕, GraalVM!
            System.out.println(result.getMember("answer").asInt());      // 42
            
            // Java 객체를 JavaScript에 바인딩
            context.getBindings("js").putMember("javaObj", new MyJavaClass());
            
            // JavaScript에서 Java 메소드 호출
            context.eval("js", "javaObj.hello('GraalVM 사용자')");
        }
    }
    
    static class MyJavaClass {
        public void hello(String name) {
            System.out.println("Java에서 인사: 안녕하세요, " + name + "님!");
        }
    }
}

GraalVM은 다양한 언어를 지원하는 폴리글랏 가상 머신이야. JavaScript 외에도 Python, Ruby, R 등 여러 언어를 함께 사용할 수 있지! 🌐

2. 다른 대안들

Nashorn 외에도 Java에서 JavaScript를 실행할 수 있는 다른 방법들이 있어:

  1. Rhino: Nashorn 이전에 사용되던 Mozilla의 JavaScript 엔진
  2. J2V8: V8 JavaScript 엔진의 Java 바인딩
  3. TeaVM: Java 코드를 JavaScript로 변환하는 도구
  4. Vert.x: JavaScript를 포함한 다양한 언어를 지원하는 이벤트 기반 프레임워크

각 도구마다 장단점이 있으니, 프로젝트의 요구사항에 맞는 도구를 선택하는 것이 중요해! 🔧

3. 2025년 현재 JavaScript와 Java 통합의 미래

2025년 현재, JavaScript와 Java의 통합은 더욱 발전하고 있어:

  • GraalVM이 표준 JDK에 통합되는 움직임
  • WebAssembly를 통한 Java와 JavaScript 간의 새로운 상호운용성
  • Project Loom의 가상 스레드와 JavaScript 비동기 프로그래밍 모델의 통합
  • Spring Framework에서 GraalVM JavaScript를 활용한 스크립팅 지원 강화

Java와 JavaScript의 경계는 점점 더 흐려지고 있어. 두 언어의 장점을 결합한 하이브리드 애플리케이션 개발이 더욱 쉬워지고 있지! 🌉

💼 실제 프로젝트에서의 Nashorn 활용 사례

이론적인 내용은 충분히 다뤘으니, 이제 실제 업계에서 Nashorn이 어떻게 활용되고 있는지 알아보자! 🏢

1. 웹 애플리케이션에서의 서버 사이드 렌더링

React, Vue 같은 JavaScript 프레임워크로 만든 UI를 서버에서 렌더링할 때 Nashorn이 사용돼:

import javax.script.*;
import java.io.*;

public class ServerSideRenderingDemo {
    public static void main(String[] args) {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");
            
            // React 라이브러리 로드 (간소화된 예제)
            engine.eval(new FileReader("react.js"));
            engine.eval(new FileReader("react-dom-server.js"));
            
            // React 컴포넌트 정의
            String reactComponent = 
                "const MyComponent = () => {" +
                "  return React.createElement('div', null, " +
                "    React.createElement('h1', null, '안녕하세요, SSR!'), " +
                "    React.createElement('p', null, '이 페이지는 서버에서 렌더링되었습니다.') " +
                "  );" +
                "};" +
                "ReactDOMServer.renderToString(React.createElement(MyComponent));";
            
            // 서버에서 HTML 렌더링
            String html = (String) engine.eval(reactComponent);
            System.out.println("렌더링된 HTML:");
            System.out.println(html);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

이 방식으로 JavaScript 프레임워크로 만든 UI를 서버에서 미리 렌더링하면 초기 로딩 속도가 빨라지고 SEO도 개선돼! 🖥️

2. 규칙 엔진 및 비즈니스 로직

복잡한 비즈니스 규칙을 JavaScript로 작성하고 Java 애플리케이션에서 실행하는 사례도 많아:

import javax.script.*;
import java.util.*;

public class BusinessRulesDemo {
    // 주문 클래스
    static class Order {
        private String customerId;
        private List<Item> items = new ArrayList<>();
        private double totalAmount;
        
        // 생성자, getter, setter 생략...
        
        public void addItem(Item item) {
            items.add(item);
            totalAmount += item.getPrice() * item.getQuantity();
        }
        
        // 기타 메소드 생략...
    }
    
    static class Item {
        private String productId;
        private double price;
        private int quantity;
        
        // 생성자, getter, setter 생략...
    }
    
    public static void main(String[] args) {
        try {
            // 주문 생성 (예시)
            Order order = new Order();
            order.setCustomerId("CUST001");
            order.addItem(new Item("PROD001", 29.99, 2));
            order.addItem(new Item("PROD002", 49.99, 1));
            
            // Nashorn 엔진 설정
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("nashorn");
            
            // 할인 규칙 스크립트 (외부 파일이나 데이터베이스에서 로드 가능)
            String discountRules = 
                "function calculateDiscount(order) {" +
                "  var discount = 0;" +
                "  " +
                "  // 총 주문액이 100달러 이상이면 10% 할인" +
                "  if (order.totalAmount >= 100) {" +
                "    discount += order.totalAmount * 0.1;" +
                "  }" +
                "  " +
                "  // VIP 고객이면 추가 5% 할인" +
                "  if (isVipCustomer(order.customerId)) {" +
                "    discount += order.totalAmount * 0.05;" +
                "  }" +
                "  " +
                "  return discount;" +
                "}" +
                "" +
                "function isVipCustomer(customerId) {" +
                "  // 실제로는 데이터베이스 조회 등의 로직이 있을 수 있음" +
                "  return customerId === 'CUST001';" +
                "}";
            
            // 스크립트 평가
            engine.eval(discountRules);
            
            // 주문 객체를 JavaScript에 전달
            engine.put("order", order);
            
            // 할인 계산 함수 호출
            Invocable invocable = (Invocable) engine;
            double discount = (double) invocable.invokeFunction("calculateDiscount", order);
            
            // 결과 출력
            System.out.println("주문 총액: $" + order.totalAmount);
            System.out.println("적용된 할인: $" + discount);
            System.out.println("최종 결제 금액: $" + (order.totalAmount - discount));
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

이런 방식으로 비즈니스 규칙을 Java 코드와 분리하면 개발자가 아닌 사람도 규칙을 수정할 수 있고, 애플리케이션을 재배포하지 않고도 규칙을 업데이트할 수 있어! 📝

3. 플러그인 시스템

사용자가 직접 플러그인을 작성할 수 있는 확장 가능한 애플리케이션을 만들 때도 Nashorn이 유용해: