ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 온라인 자바 컴파일러 만들기 (Judge0 API)
    Project 2024. 7. 19. 09:50

    https://judge0.com

     

    Judge0 - Where code happens.

    Robust, scalable, and open-source online code execution system.

    judge0.com


    온라인 자바 컴파일러를 만들어 보고 싶었다. 찾아 보니 사용자가 입력한 코드를 컴파일해주는 Judge0 API가 이미 있어서 사용해보기로 했다. 위 사이트로 접속하여 다음 단계를 밟으면 API 키를 발급 받을 수 있다. 무료 버전을 사용하면 하루에 50개의 요청만 가능한 것 같다.

     

    1. Get Started

    2. Subscribe

    3. Start Free Plan

    4. API 키 확인


    스프링 부트 프로젝트 생성 - build.gradle

    plugins {
        id 'java'
        id 'org.springframework.boot' version '3.3.1'
        id 'io.spring.dependency-management' version '1.1.5'
    }
    
    group = 'project'
    version = '0.0.1-SNAPSHOT'
    
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(17)
        }
    }
    
    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-webflux'
        compileOnly 'org.projectlombok:lombok'
        developmentOnly 'org.springframework.boot:spring-boot-devtools'
        annotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    
        if (isAppleSilicon()) {
            runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64")
        }
    }
    
    def isAppleSilicon() {
        return System.getProperty("os.name") == "Mac OS X" && System.getProperty("os.arch") == "aarch64"
    }
    
    tasks.named('test') {
        useJUnitPlatform()
    }

     

    CompilerController

    @Controller
    public class CompilerController {
    
        private final WebClient webClient;
    
        public CompilerController(WebClient.Builder webClientBuilder, @Value("${apiKey}") String key) {
            this.webClient = webClientBuilder
                    .baseUrl("https://judge0-ce.p.rapidapi.com")
                    .defaultHeader("x-rapidapi-key", key)
                    .defaultHeader("x-rapidapi-host", "judge0-ce.p.rapidapi.com")
                    .build();
        }
    
        @GetMapping("/")
        public String index(Model model) {
            String defaultCode = """
                public class Main {
                    public static void main(String[] args) {
                        System.out.println("Hello, World!");
                    }
                }
                """;
            model.addAttribute("sourceCode", defaultCode);
            return "index";
        }
    
        @PostMapping("/compile")
        public Mono<ResponseEntity<Map<String, String>>> compileCode(@RequestBody Map<String, String> requestBody) {
            String sourceCode = requestBody.get("sourceCode");
            String encodedSourceCode = Base64.getEncoder().encodeToString(sourceCode.getBytes());
    
            Map<String, String> requestPayload = new HashMap<>();
            requestPayload.put("language_id", "62"); // Java
            requestPayload.put("source_code", encodedSourceCode);
            requestPayload.put("stdin", "");
    
            return this.webClient.post()
                    .uri("/submissions?base64_encoded=true&wait=false")
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyValue(requestPayload)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .flatMap(response -> {
                        String token = (String) response.get("token");
                        return checkResult(token);
                    })
                    .map(result -> {
                        Map<String, String> responseMap = new HashMap<>();
                        StringBuilder output = new StringBuilder();
                        String stdout = (String) result.get("stdout");
                        String stderr = (String) result.get("stderr");
                        String compileOutput = (String) result.get("compile_output");
                        String message = (String) result.get("message");
    
    
                        if (stdout != null) {
                            output.append(new String(Base64.getDecoder().decode(stdout)));
                        }
                        if (stderr != null) {
                            output.append(new String(Base64.getDecoder().decode(stderr)));
                        }
                        if (compileOutput != null) {
                            output.append(new String(Base64.getDecoder().decode(compileOutput)));
                        }
                        if (message != null) {
                            output.append(message);
                        }
    
                        responseMap.put("output", output.toString());
                        return ResponseEntity.ok(responseMap);
                    })
                    .onErrorResume(e -> {
                        Map<String, String> responseMap = new HashMap<>();
                        responseMap.put("output", "An error occurred: " + e.getMessage());
                        return Mono.just(ResponseEntity.ok(responseMap));
                    });
        }
    
        private Mono<Map> checkResult(String token) {
            return this.webClient.get()
                    .uri("/submissions/" + token + "?base64_encoded=true")
                    .retrieve()
                    .bodyToMono(Map.class);
        }
    }

     

    index.html

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Online Java Compiler</title>
        <link rel="icon" href="/icon/duke.ico" type="image/x-icon">
        <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
        <script>
            $(document).ready(function() {
                $("form").on("submit", function(event) {
                    event.preventDefault();
                    $.ajax({
                        type: "POST",
                        url: "/compile",
                        contentType: "application/json",
                        data: JSON.stringify({
                            sourceCode: $("#sourceCode").val()
                        }),
                        success: function(response) {
                            $("#output").text(response.output);
                        },
                        error: function() {
                            $("#output").text("An error occurred while compiling the code.");
                        }
                    });
                });
            });
        </script>
    </head>
    <body>
    <h1>Online Java Compiler!!</h1>
    <form>
        <textarea id="sourceCode" name="sourceCode" rows="20" cols="80" placeholder="Enter your Java code here" th:text="${sourceCode}"></textarea><br/>
        <button type="submit">Compile</button>
    </form>
    <h2>Result:</h2>
    <pre id="output" th:text="${output}"></pre>
    </body>
    </html>