TECH 으로 돌아가기
TECH HACKER NEWS 오늘 6분 읽기 32 READS

Prolog로 짠 제미니 서버 'Sula', 언어 런타임을 함께 고쳐야 했던 이유

'Sula'는 논리형 프로그래밍 언어인 Prolog로 작성된 제미니(Gemini) 프로토콜 서버다. 이름은 스페인어로 캡슐을 뜻하는 'cápsula'의 어미에서 따왔고, /'su.la/로 읽는다. 제미니 프로토콜에서 하나의 서버 공간을 흔히 '캡슐'이라 부르는 관행을 반영한 작명이다. 실무자 관점에서 이 프로젝트가 흥미로운 지점은 '또 하나의 제미니 서버'라는 사실 자체가 아니라, 이를 구현하기 위해 개발자가 애플리케이션 코드뿐 아니라 언어 런타임인 Scryer Prolog 자체를 여러 군데 손봐야 했다는 데 있다.

먼저 배경을 정리해 두면, 제미니는 웹(HTTP)보다 훨씬 단순하고 텍스트 중심적인 대안 프로토콜로, TLS 암호화를 필수로 요구하며 클라이언트 인증서를 신원 확인 수단으로 활용할 수 있다는 특징이 있다. Scryer Prolog는 Rust로 구현된 비교적 현대적인 Prolog 처리계다. 서버라는 것은 결국 소켓을 열고, TLS 핸드셰이크를 하고, 파일 같은 바이너리 스트림을 네트워크로 흘려보내고, 종료 신호를 깔끔하게 처리하는 일의 연속인데, 선언형 언어의 표준 라이브러리는 이런 저수준·스트리밍 작업을 매끄럽게 지원하도록 설계되어 있지 않은 경우가 많다. Sula가 기성 Scryer가 아니라 별도 브랜치(js/fixes)의 패치된 버전에 의존하는 이유가 여기에 있다.

런타임을 고쳐야 했던 다섯 가지 지점

패치 내용을 뜯어보면 어떤 벽에 부딪혔는지가 드러난다. 첫째는 '$copy_stream'/2라는 네이티브 내장 술어의 추가로, 바이너리 파일 본문을 TLS 클라이언트로 스트리밍할 때 그 내용을 Prolog 힙 위에 통째로 올리지 않고 흘려보내기 위한 것이다. 큰 파일을 응답할 때 메모리에 전부 적재하지 않는다는, 서버로서는 기본적이지만 선언형 런타임에서는 자명하지 않은 요건을 해결한 부분이다. 둘째는 library(pio)의 buffer_prepare_for_n/5 수정으로, 프로세스 파이프처럼 at_end_of_stream/1이 결코 참을 반환하지 않는 스트림에서 지연 읽기(lazy read)가 EOF에 도달했을 때 무한 루프에 빠지지 않고 제대로 종료되도록 고쳤다.

셋째는 신호 처리에 관한 것이다. socket_server_accept/4에 논블로킹 폴 루프를 넣어 Scryer의 INTERRUPT 플래그를 주기적으로 확인하도록 함으로써, SIGINT가 블로킹 시스템 콜 뒤에 갇혀버리는 대신 '$interrupt_thrown'이라는 잡을 수 있는 예외로 올라오게 만들었다. 그 덕분에 Ctrl+C를 누르면 리스닝 소켓을 닫고, 최상위 catch가 'Shutting down'을 로깅한 뒤 프로세스가 종료 코드 0으로 깨끗하게 내려가는, 예측 가능한 종료 동작이 성립한다. 넷째와 다섯째는 TLS 계층으로, 백엔드를 rustls로 이식하고 tls_server_negotiate를 고쳐 선택적 클라이언트 인증서를 받아올 수 있게 했다. 클라이언트 인증서가 신원의 핵심인 제미니에서는 사실상 필수적인 변경이다.

실행 방식과 운영상의 함의

구조적으로도 몇 가지 실용적 선택이 눈에 띈다. sula.pl은 다중언어(polyglot) 스크립트로 되어 있어, bash가 PATH에서 scryer-prolog를 찾아내 sula:run과 halt를 진입 목표로 삼아 실행한다. 별도의 래퍼 스크립트 없이 파일 하나로 실행 가능하도록 한 셈이다. 또 시작 시점에 openssl(1)을 호출해 설정된 신원 인증서에서 CN을 읽고 그것이 설정된 호스트명과 일치하는지 검증하기 때문에, openssl 바이너리 역시 PATH에 있어야 한다. 즉 순수 Prolog만으로 모든 것을 처리하지는 않으며, 외부 도구에 일부를 위임한다.

다만 운영 관점에서 주의할 대목도 있다. 명령행 옵션은 순서에 구애받지 않고 받아들이는데, 인식하지 못한 값은 조용히 버려진다는 점이다. 오타나 잘못된 플래그가 오류로 걸러지지 않고 무시된다는 뜻이므로, 의도한 설정이 실제로 반영됐는지는 사용자가 직접 확인해야 한다.

결국 Sula는 특정 언어의 표현력과 실전 서버 요구사항 사이의 간극을 보여주는 사례에 가깝다. 논리형 언어로 네트워크 서버를 쓰는 일이 불가능하지 않다는 것을 증명하는 동시에, 그것을 실제로 돌리려면 스트리밍·EOF 처리·신호 처리·TLS 같은 시스템 프로그래밍의 기본기를 런타임 수준에서 직접 메워야 한다는 현실도 함께 드러낸다. 대체 프로토콜이나 비주류 언어로 실험을 구상하는 실무자라면, 애플리케이션 코드 이전에 이런 런타임 경계 조건을 얼마나 감당할 수 있는지를 먼저 가늠해 보는 편이 낫다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://sagredo.dev/projects/sula/
SHARE
처리 중...