Projects

웹 컴파일러 만들기 프로젝트 - 1.웹 컴파일러 제작

Jero++ 2020. 6. 11. 23:20

* 모든 소스코드는 제 Github에 있습니다.

 

Jun0zo/comi: Online compiler "코딩에 미치고 싶다": comi (github.com)

 

Jun0zo/comi

Online compiler "코딩에 미치고 싶다": comi. Contribute to Jun0zo/comi development by creating an account on GitHub.

github.com

 

웹컴파일러

웹 컴파일러의 주요한 기능은 말그대로 코드를 컴파일하여 실행결과를 보내주는 것입니다. 

예전에 리눅스를 사용했을때, gcd를 이용하여 컴파일한 기억이 있어서 서버는 리눅스를 사용했습니다.

대략적인 과정은 아래와 같습니다.

 

그림 1

 

Django와 Node.js 를 분리하여 사용하였는데 그 이유는 Node.js에 편리한 라이브러리가 많았기 때문입니다.

Django는 사용자가 요청을 하면 응답을 해주는 용도로 사용하였고 Node.js 는 Docker로 사용자들의 환경을 분리하고

Docker환경 내에서 컴파일을 할 수 있도록 설계하였습니다. 

 

그림 2

 

이렇게 N명의 유저들을 각각 다른 환경에서 실행할 수 있도록 Docker로 컨테이너를 만들고 그 안에서 

컴파일을 수행하였습니다.

 

그림 3

 

더욱 구체적인 동작방식은 이렇습니다. 홈페이지에 접속하면 Django가 인식하여 서버 내부의 Node.js와 socket통신 할 수있는 stream을 만듭니다. 그 때, Docker가 만든 컨테이너의 stream을 socket.io의 스트림 위에 얹어서 socket이 통신하는 길에서 docker 데이터가 전달될 수 있도록 만들었습니다. 그리고 Docker stream을 만든 후, 클라이언트에게 다 만들었다 알려줍니다.

 

이 과정에서 필요한 Node 모듈은 (Server side)

socket.io 

docker.js

입니다.

 

1. Django (View)

Django는 CodeEditor를 제공하여 편하게 코드를 수정할 수 있도록 도와주고 Terminal을 제공하여 코드를 실행했을 때의 결과를 사용자에게 표시할 수 있도록 도와줍니다. Run버튼을 클릭했을 때에는 CodeEditor에 있는 코드들을 그대로 SSH Server에 전송합니다.

 

그림 4

그림 4는 코드 수정페이지의 시작화면입니다. 왼쪽은 Code Editor로 코드를 수정하는 부분입니다.

코드를 모두 작성하고 상단의 Run 버튼을 누르면 결과가 Terminal에 출력됩니다.

Code Editor는 monaco-editor.js 라이브러리를 사용하였습니다. monaco-editor는 js파일과 css파일을 추가하기만 하면 되기 때문에 생략하겠습니다. 

 

 

 

1.1 Socket에 EventListener 추가

setSocket함수는 SSH 서버와 연결하기 위한 함수입니다. io.connect로 반환된 socket으로 해당 서버에게 메세지를 주고 받을 수 있는 역할을 담당합니다.

emit을 통해 특정 메시지를 전달할 수 있고 on 을 통해 메시지를 받을 수 있습니다.

아래 코드를 보면 ssh서버로부터 'show'이름의 메시지를 받았을 때에는 terminal에 받은 값 그대로 write를 합니다.

terminal에 write를 하면 아래 그림 5와 같이 터미널에 write 인자값에 있는 문자열을 표시합니다.

'end'이름의 메시지를 받았을 때에는 소켓연결을 해제합니다.

function setSocket(server_address){
      socket = io.connect(server_address)
      socket.on('show', (data) => {
          term.write(data.replace(/root@[a-z|0-9]+:[^\n]+#/, ''))
      });

      socket.on('end', (status) => {
      	socket.disconnect();
      });
      return socket
}

 

 그림 5 

 

1.2 Terminal 로드

loadTerminal함수가 실행되면서 id값이 terminal-container인 tag를 찾아서 terminal로 만듭니다.

만들어진 터미널에 Eventlistener로 'data' 이벤트를 추가합니다. data이벤트는 키보드 입력을 받았을 때 data에

어느 키보드를 눌렀는지 return 합니다. 위의 코드는 socket에 그대로 키보드 입력값을 보냅니다.

코드는 아래와 같습니다.

function loadTerminal(){
    var terminalContainer = document.getElementById("terminal-container")
    Terminal.applyAddon(fit)

    term = new Terminal({ cursorBlink: true , rows: 100, cols: 100})
    term.open(terminalContainer)
    term.fit()
    term.writable = 1;

    term.on('data', (data) => {
    	socket.emit('cmd', data);
    });
 }

 

 

1.3 코드를 SSH로 전송 (Run 버튼을 눌렀을 때)

Run버튼을 클릭하면 terminal에 존재하는 모든 문자열을 제거합니다. 아래 코드에서는 term.clear() 가 그 역할을 합니다.

execAtServer()함수는 Code Editor에 있는 문자열을 가져와서 SSH서버로 전송하는 역할을 합니다.

editor.getValue()함수가 Code Editor에 있는 문자열을 가져오는 역할을,

Axios가 SSH Server로 코드를 전송하는 역할을 합니다.

function execAtServer() {
    let srccode = editor.getValue()
    axios.post('http://localhost:4000/source', {
        code: srccode
    }).then(res => {

    }).catch(err => {
      if(socket && socket.connected){
          console.log('socket is aleardy exist !! we will delte(in catch)' + socket.id);
          socket.disconnect();
      }
    });
}


$("#run-button").click(() => {
    term.clear()
    execAtServer()
});

 

 

1.4 정리

 

-- 추가중 --