웹 컴파일러 만들기 프로젝트 - 1.웹 컴파일러 제작
* 모든 소스코드는 제 Github에 있습니다.
Jun0zo/comi: Online compiler "코딩에 미치고 싶다": comi (github.com)
웹컴파일러
웹 컴파일러의 주요한 기능은 말그대로 코드를 컴파일하여 실행결과를 보내주는 것입니다.
예전에 리눅스를 사용했을때, gcd를 이용하여 컴파일한 기억이 있어서 서버는 리눅스를 사용했습니다.
대략적인 과정은 아래와 같습니다.
Django와 Node.js 를 분리하여 사용하였는데 그 이유는 Node.js에 편리한 라이브러리가 많았기 때문입니다.
Django는 사용자가 요청을 하면 응답을 해주는 용도로 사용하였고 Node.js 는 Docker로 사용자들의 환경을 분리하고
Docker환경 내에서 컴파일을 할 수 있도록 설계하였습니다.
이렇게 N명의 유저들을 각각 다른 환경에서 실행할 수 있도록 Docker로 컨테이너를 만들고 그 안에서
컴파일을 수행하였습니다.
더욱 구체적인 동작방식은 이렇습니다. 홈페이지에 접속하면 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는 코드 수정페이지의 시작화면입니다. 왼쪽은 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
}
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 정리
-- 추가중 --