https://github.com/IMGyuGo/sqlProcessor
GitHub - IMGyuGo/sqlProcessor: sqlProcessor (Sql 처리기) - 팀원 : [김규민, 김세민, 김규태, 남동현]
sqlProcessor (Sql 처리기) - 팀원 : [김규민, 김세민, 김규태, 남동현] - IMGyuGo/sqlProcessor
github.com
위 주소는 C 기반 미니 SQL 처리기와 이를 시각화한 웹 데모입니다.
xterm.js 라이브러리에 대해서
https://gyumingomin.tistory.com/67
xterm.js 라이브러리란?
https://xtermjs.org/docs/ DocumentationTerminal front-end component written in JavaScript that works in the browser.xtermjs.org위 문서는 xterm js 문서 페이지이긴 한데 읽기 너무 불편하게 만들어져 있다.그래서 쪼금 정리하고
gyumingomin.tistory.com
실제 C엔진을 웹상에서 보여주기 위해 xterm.js 라는 라이브러리를 사용했습니다.
이 xterm.js와 백엔드 간의 실시간 통신 처리를 위해 WebSocket을 직접 구현해보았는데 이것에 대해 자세한 설명과 사용법에 대한 설명을 드리고자 합니다.

왼쪽 패널은 실제 터미널 프로그램 자체를 브라우저에 띄운 것이 아니라, xterm.js를 이용해 브라우저 안에 터미널 UI를 렌더링한 것이다.
실제 셸 프로세스와 C 엔진은 백엔드(Node.js)에서 실행되고, 브라우저는 그 세션에 원격으로 연결된 형태로 동작한다.
이때 WebSocket은 브라우저의 터미널 화면과 백엔드의 실제 셸 세션을 실시간으로 이어주는 역할을 한다.
사용자가 입력한 키 값은 WebSocket을 통해 백엔드로 전달되고, 백엔드에서 실행 중인 셸과 C 엔진의 출력은 다시 WebSocket을 통해 브라우저로 스트리밍된다.
즉 xterm.js는 터미널의 “화면과 입력 경험”을 담당하고, WebSocket은 그 화면 뒤에 있는 실제 셸 세션과의 “실시간 통신”을 담당한다.
여기서 의문❔
직접 터미널을 가져올 순 없는가?
바로바로 답변 ‼️ : (없습니다. ㅜㅜ)
ⓐ. 브라우저는 보안상 직접 shell 프로세스를 실행할 수 없기 때문이다!
- 그래서 실제 PowerShell/bash/zsh/C 엔진은 백엔드에서 실행한다.
- 브라우저는 xterm.js로 터미널처럼 보이는 UI만 렌더링
- 사용자의 키 입력은 백엔드로 전송 후 백엔드에서 나온 출력은 다시 브라우저로 보내 마치 실제 터미널인 것처럼 보이게 만드는 것이다.
<Structure>
브라우저의 xterm.js
<-> WebSocket
<-> Node.js 백엔드
<-> node-pty로 띄운 실제 shell / C 엔진
왜 WebSocket이 필요한가❔
먼저 터미널에 대해서 생각해 보자.
우리가 원하는 게 단순한 요청-응답인가? 아니면 계속 연결된 터미널 세션인가?
터미널은 단순한 요청-응답이 아니라고 알고 있다.
터미널에선
ⓐ. 사용자가 한 글자씩 입력
ⓑ. 백엔드는 그 입력을 바로 셸에 전달
ⓒ. 셸은 중간 출력, 프롬프트, 에러 메시지 등을 지속적으로 응답
ⓓ. 창 크기가 바뀌면 리사이즈도 알려줘야 함
ⓔ. 세션 상태로 계속 유지
즉, 터미널은 양방향 실시간 통신이 필요하다.
어짜피 한번 요청을 보내는 거면 HTTP 프로토콜 통신으로 상관없지 않나❔
이런 생각을 가질 수도 있을 것 같다. (저도 그랬습니담.. ㅎ)
하지만, 이런 경우 터미널처럼 동작하는 게 힘들다.
실제 터미널처럼 보이지 않으니 어색한 부분도 많이 생겨 이것을 수정하는데도 시간이 많이 걸릴 것 같다. (아닌가요? ㅎ)
그래서 터미널 같은 인터랙션을 지속적으로 지원해주는 xterm.js와 WebSocket을 사용해 실제로 터미널처럼 보이게 만들 수 있었다.
다음으로 구현한 WebSocket의 흐름에 대해서
여기까지 정리하면 xterm.js 가 터미널의 화면과 입력 경험을 담당하고, WebSocket이 화면 뒤에 있는 실제 셸 세션과의 실시간 통신을 담당한다는 것을 이해할 수 있다.
그렇다면 이제 자연스럽게 이런 질문이 생긴다⁉️
"실제로 어떻게 동작하는가?"
이 부분을 이해해야 다른 프로젝트에서도 비슷한 구조를 직접 짤 수 있다.
핵심은 단순하다.
브라우저에 띄우 터미널 UI와 백엔드에서 실행 중인 셸 프로세스를 끊기지 않는 하나의 통로로 계속 연결하는 것이다.
전체 구조를 다시 보면
브라우저의 xterm.js
<-> WebSocket
<-> Node.js 백엔드
<-> node-pty로 띄운 실제 shell / C 엔진
이 구조를 다시 따라가 보겠다.
1. 브라우저가 먼저 WebSocket 연결을 오픈
const socket = new WebSocket("ws://localhost:3001/ws/terminal");
먼저 사용자가 페이지에 들어오면, 프론트엔드는 먼저 백엔드의 WebSocket 주소로 연결을 연다.
이 연결은 일반적인 HTTP 요청처럼 한 번 보내고 끝나는 것이 아닌 한 번 연결되면 계속 살아 있으면서, 이후의 모든 터미널 입력과 출력이 이 연결을 통해 오가게 된다.
이 시점부터 브라우저의 xterm.js는 단순한 UI 화면이 아닌 백엔드의 실제 셸 세션에 연결된 원격 터미널 창처럼 동작하게 된다.
즉, 사용자는 브라우저 안에서 동작하지만, 실제 처리는 브라우저가 아니라 백엔드에서 이뤄진다.
2. 서버는 연결이 올 때마다 독립된 터미널 세션을 오픈
const server = http.createServer(app);
const wss = new WebSocketServer({ server, path: "/ws/terminal" });
wss.on("connection", (socket) => {
// 연결된 브라우저 1개에 대한 터미널 세션 생성
});
이제 백엔드는 브라우저가 연결해 온 WebSocket을 받아야 한다.
여기서 중요한 점은 브라우저 연결 1개가 터미널 세션 1개와 대응된다는 점이다.
- 브라우저 탭 하나를 열면 터미널 세션 하나
- 다른 탭을 열면 또 다른 터미널 세션 하나
이렇게 해야 사용자의 세션이 서로 섞이지 않고, 각 브라우저가 독립적으로 자신의 셸 상태를 유지할 수 있다.
3. 서버는 실제 셸 프로세스를 실행
브라우저는 보안상 PowerShell, bash, zsh 같은 셸 프로그램을 직접 실행할 수 없다.
그 대신 백엔드에서 실제 셸을 띄어야 한다.
따라서 이 프로젝트에서는 node-pty를 사용해 PTY 기반 셸 세션을 만든다.
const ptyProcess = pty.spawn(shell, args, options);
여기서 PTY는 터미널처럼 동작하는 가상 입출력 환경이라고 보면 된다.
이걸 사용하면
ⓐ. 실제 터미널 처럼 입력 받음
ⓑ. 출력도 터미널 형식으로 받을 수 있음
ⓒ. 프롬프트나 줄바꿈, 리사이즈 같은 동작도 자연스럽게 처리 가능
이 셸 안에서 다시 C 엔진을 실행 한다.
<실행 흐름>
Node.js
-> 셸 실행
-> 셸 내부에서 C 엔진 실행
-> 이후 사용자의 입력은 이 셸/엔진으로 전달
이 구조를 사용하면 브라우저에서 입력한 내용이 정말로 백엔드의 셸로 들어가고, 그 결과가 다시 돌아오는 형태가 된다.
4. 사용자의 키 입력은 한 글자씩 서버로 전달
터미널은 일반 폼 입력창과 다르다.
보통 <input> 태그 안에 문자열을 다 넣고 제출하는 방식이 익숙하지만, 터미널에서는
ⓐ. 사용자가 한 글자 입력
ⓑ. 백스페이스 입력
ⓒ. 엔터 입력
ⓓ. 방향키 입력
이런 이벤트가 계속 발생한다.
xterm.js는 이런 입력을 onData()로 받을 수 있다.
term.onData((data) => {
socket.send(
JSON.stringify({
type: "terminal-input",
data,
})
);
});
즉 브라우저는 이런 사용자의 키 입력 흐름 자체를 서버로 전송한다.
서버는 그 값을 받아 실제 셸에 그대로 쓴다.
socket.on("message", (raw) => {
const message = JSON.parse(raw.toString());
if (message.type === "terminal-input") {
ptyProcess.write(message.data);
}
});
이 과정 덕분에 사용자는 브라우저 안에서도 실제 터미널을 다루는 것처럼 느끼게 된다.
5. 셸의 출력은 다시 브라우저로 스트리밍 된다.
입력만 보내는 것으로 터미널이 완성되지 않는다.
셸에서 나오는 출력도 다시 브라우저에 보여줘야 한다.
node-pty는 셸의 출력을 onData()로 받을 수 있다.
ptyProcess.onData((text) => {
socket.send(
JSON.stringify({
type: "terminal-output",
data: text,
})
);
});
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === "terminal-output") {
term.write(message.data);
}
};
이렇게 되면 백엔드에서 실제로 실행된 결과가 실시간으로 브라우저에 표시된다.
6. 상태도 함께 관리해야 한다.
터미널을 웹으로 옮기면 단순히 텍스트만 주고받는다고 끝나지 않음.
현재 세션이 어떤 상태인지도 관리해야 함
ⓐ. 아직 셸만 열린 상태인가?
ⓑ. C 엔진이 실행되어 SQL 입력을 받을 준비가 된 상태인가?
ⓒ. 엔진이 종료되고 셸만 남은 상태인가?
ⓓ. 에러가 발생했는가?
ⓔ. 세션이 닫혔는가?
이런 상태가 있어야 프론트에서도 적절한 UI를 보여줄 수 있다.
그래서 실제 구현에선 단순히 terminal-output만 보내지 않고, 상태까지 함께 보냄
socket.send(JSON.stringify({
type: "session-status",
status: "connected",
message: "SQL engine is running."
}));
이렇게 웹소켓은 소켓을 통해 통신을 주고 받고 실제 셸 세션과 실시간으로 이어주는 역할을 한다.
근데 코드를 실제로 구현해본 분들은 잘 알겠지만,
소켓통신으로 이뤄져 있는 것 보면, TCP로 통신한다고 예상할 수 있다.

처음에는 HTTP/HTTPS handshake로 연결을 오픈한 뒤,
그 연결을 WebSocket 프로토콜로 업그레이드해서 계속 사용한다.
정리하면,
ws:// = WebSocket over TCP
wss:// = WebSocket over TLS over TCP그래서 현재 프로젝트의 WebSocket도 결국 지속되는 TCP 연결 하나를 유지하면서 양방향 통신하는 구조다.
(TCP 위에서 동작하는 애플리케이션 계층 프로토콜)
'정글캠프-WIL > 서브아이템' 카테고리의 다른 글
| xterm.js 라이브러리란? (0) | 2026.04.09 |
|---|---|
| React의 React Flow 라이브러리란? (0) | 2026.04.09 |
| Express.js 프레임워크란? (2) | 2026.04.09 |
| 코덱스의 자동화 기능 (0) | 2026.04.02 |
| 윈도우 mklink + 심볼릭 링크에 대해 (0) | 2026.04.02 |