Lisplets: Lisp와 Java 서블릿 사이의 격차를 메우다
2000년대 초, 웹 애플리케이션을 구축하려는 Lisp 개발자들은 종종 직접 HTTP 서버와 SSL 핸들러를 작성해 바퀴를 다시 만들 것인지, 언어의 유연성을 포기할 것인지 선택해야 했습니다. 여기서 Lisplets가 등장합니다. Rich Hickey가 2004년에 만든 이 프로젝트는 Java 서블릿에 대한 Lisp 친화적인 인터페이스를 제공하도록 설계되었습니다.
Lisplets는 다리 역할을 하여 개발자들이 실제 애플리케이션 로직을 Common Lisp 또는 Scheme으로 작성하면서 Java 기반 웹 환경(예: Tomcat, Jetty, Resin)의 산업 수준 기능을 활용할 수 있게 합니다. 웹의 '배관' 작업을 Java에 맡김으로써 Lisp 개발자는 확장성이나 보안을 희생하지 않고 언어의 표현력을 집중할 수 있습니다.
Lisplets 작동 방식
핵심적으로 Lisplets는 HttpServlet을 구현하는 단일 Java 클래스로 구성됩니다. 요청을 내부에서 처리하는 대신, Lisplet 서블릿은 TCP 소켓을 통한 s-표현식을 사용해 요청을 전달하고 응답을 수집합니다.
요청 흐름
- Interception: Java 서블릿 컨테이너가 HTTP 요청을 받습니다.
- Packaging: Lisplet 서블릿이 요청 헤더, 파라미터, 쿠키, 세션 데이터, 애플리케이션 상태, 사용자 정보를 하나의 s-표현식으로 패키징합니다.
- Transmission: 이 s-표현식이 TCP 소켓을 통해 대기 중인 Lisp 프로세스로 전송됩니다.
- Processing: Lisp 프로세스가 표준
read함수를 사용해 s-표현식을 읽고 로직을 처리하여 응답을 생성합니다. - Return: Lisp 프로세스가 상태, 헤더, 세션 상태를 포함한 s-표현식을 반환하고, 그 뒤에 원시 문서 내용을 보냅니다.
Lisp 개발자를 위한 주요 장점
Java 서블릿 컨테이너를 프런트엔드로 활용함으로써 Lisplets는 여러 즉각적인 이점을 제공합니다:
- Infrastructure for Free: 개발자는 Java 코드를 한 줄도 작성하지 않고도 전문 HTTP 서버 구현, SSL 지원, 산업 표준 웹 서버와의 통합을 이용할 수 있습니다.
- Enterprise Features: Lisplet API는 세션 관리, 인증, 접근 제어, 사용자 역할, 선언적 구성을 포함한 Servlet API의 전체 기능을 반영합니다.
- Hybrid Applications: 표준 Java 웹 애플리케이션 내에서 동작하기 때문에 Lisp로 생성된 콘텐츠가 Java Server Pages (JSP) 또는 다른 Java 서블릿과 하나의 애플리케이션에서 공존할 수 있습니다.
- Flexible Deployment: 모든 서블릿 컨테이너와 호환되므로 개발자는 비용, 임베드 가능성, 플랫폼 지원 등을 기준으로 환경을 선택할 수 있습니다.
기술 심층 분석: API
web.xml을 통한 설정
Lisplets는 표준 web.xml 파일을 통해 설정됩니다. lisp-host와 lisp-port는 Lisp 프로세스를 찾기 위해 필수이며, 여러 선택적 매개변수를 통해 세부 조정이 가능합니다:
lisp-tag-prefix: 개발자가 태그 앞에 문자열(예::)을 붙여 Common Lisp에서 키워드로 활용할 수 있게 합니다.lisp-create-session: 세션이 없을 경우 자동으로 생성할지 여부를 결정하는 불리언 값입니다.lisp-roles: 특정 역할에 대한 사용자의 멤버십을 조회하는 데 사용되는 콤마 구분 리스트로, Lisp 로직을 Java 인증 시스템과 통합합니다.
요청 및 응답 형식
요청은 리스트의 리스트 형태로 단일 전송됩니다. 각 하위 리스트는 태그(예: :method, :uri, :headers, :session)로 시작합니다. 이 구조는 Lisp 프로세스가 표준 리스트 처리 기법을 사용해 요청을 쉽게 파싱할 수 있게 합니다.
응답도 유사한 구조를 가집니다. Lisp 프로세스는 최소한 빈 리스트 ()를 Java 측에 보내야 합니다. 응답 s-표현식은 다음을 제어할 수 있습니다:
- HTTP Status: 상태 설정(예: 200 OK) 또는 리다이렉트 트리거.
- Cookies: 이름, 값, 도메인, 경로 정의.
- Session/Context Attributes: Java 측의 동적 속성을 업데이트하며, 이는 서블릿 컨테이너에 의해 지속됩니다.
- Logging: 문자열을 직접
ServletContext로그에 전송.
구현 예시
Rich Hickey는 통합의 단순성을 보여주기 위해 Xanalys (LispWorks)용 샘플 서버를 제공했습니다. 서버는 포트에서 대기하고 스트림에서 s-표현식을 읽어 요청을 브라우저에 응답 본문으로 되돌려 줍니다:
(defun handle-lisplet-on-stream (stream)
(unwind-protect
(let ((req (read stream)))
(format stream "~S"
`((:status 200)
(:headers (:content-type "text/plain"))
(:log "Hello from Lisp")))
(pprint req stream)
(force-output stream)
(close stream))))
요약
Lisplets는 고수준의 유연한 언어로 로직을 구현하면서 안정적이고 성능 좋은 인프라 생태계를 활용하는 고전적인 문제에 대한 우아한 해결책을 제공합니다. 프로젝트가 2004년에 시작되었지만, 두 개의 서로 다른 런타임 환경을 연결하기 위해 단순하고 언어에 구애받지 않는 통신 형식(s-표현식)을 사용하는 철학은 여전히 시대를 초과한 아키텍처 패턴으로 남아 있습니다.
Sources
- HNLisplets