Emacs에서는 모든 것이 서비스처럼 보인다

2 hours ago 2
  • Emacs는 운영체제 자체라기보다 커널 위의 애플리케이션과 유틸리티를 조율하며, 파일 시스템·네트워크·외부 프로그램을 활용해 다양한 서비스의 클라이언트가 됨
  • 클라이언트에 필요한 UI·통신 계층·로컬 데이터 저장소를 미니버퍼, 버퍼, URL, TCP/UDP, JSON, SQLite 같은 내장 라이브러리로 구성할 수 있음
  • 동적 언어인 Emacs Lisp(Elisp) 는 Elisp 함수와 셸 명령을 실행 중에 결합해, Emacs가 접근할 수 있는 기능을 유연하게 조율함
  • 예제 wttr 명령은 위치를 입력받아 wttr.in에 HTTP 요청을 보내고, JSON에서 지역·기온·날씨를 추출해 미니버퍼에 표시한 뒤 kill ring에도 저장함
  • 전체 wttr.el은 cloc 기준 67줄이며, 네트워크 요청과 JSON 처리를 Python 같은 명령줄 도구에 맡기면 셸 명령 자체를 서비스로 삼는 더 짧은 Elisp 클라이언트도 만들 수 있음

운영체제 위에서 서비스를 조율하는 Emacs

  • Emacs를 운영체제라고 부르는 표현은 정확하지 않지만, 운영체제 커널 위에서 애플리케이션과 유틸리티를 조율하는 능력 때문에 이런 비교가 생김
  • 파일 시스템과 네트워크 같은 운영체제 서비스에 내장 기능으로 접근하고 다른 프로그램도 실행할 수 있어, Emacs 안에서 클라이언트 동작을 즉석에서 구성하기 쉬움
  • 여러 클라이언트 모드로 많은 컴퓨팅 작업을 처리할 수 있으므로 사용자가 “Emacs 안에서만 생활한다”는 발상도 성립함

요청과 응답으로 보는 클라이언트-서버 모델

  • 클라이언트-서버 모델은 자원을 제공하는 서비스와 이를 요청하는 클라이언트가 작업을 나누는 상호작용 패턴임
    • 클라이언트가 요청을 보냄
    • 서버가 요청을 처리한 뒤 응답을 반환함
  • 요청과 응답으로 이뤄진 트랜잭션은 네트워크를 통하거나 한 시스템 안에서 로컬로 수행될 수 있음
  • 네트워크 기반 모델은 REST 스타일 소프트웨어 아키텍처에서 폭넓게 발전함

Emacs 클라이언트를 이루는 세 영역

  • 일반적인 클라이언트는 다음 세 영역을 담당함
    • UI: 필요에 따라 사용자 입력과 출력을 처리함
    • 클라이언트 경계(Client Edge): 서비스와 통신하며, 네트워크 클라이언트에서는 네트워크 하위 시스템이 이 역할을 맡음
    • 로컬 데이터베이스: 서버와 교환하거나 동기화할 데이터를 표현하며, 구체적인 관리 방식은 구현 요구사항에 따라 달라짐
  • 사용자 인터페이스 구성

  • 서비스 통신과 데이터 변환

    • 서비스에 연결되는 클라이언트 경계에는 다음 기능을 활용할 수 있음
    • 직렬화와 역직렬화에는 내장 파서를 이용함
  • 로컬 데이터 관리

    • 서버 데이터의 로컬 표현에는 Elisp 컬렉션을 활용할 수 있음
    • 영속적인 로컬 저장소가 필요하면 SQLite를 사용할 수 있음
    • 실제 클라이언트의 복잡도는 요구사항에 따라 달라지며, 무거운 처리를 담당할 기존 명령줄 유틸리티가 있다면 셸 호출로 접근하는 서비스로 재구성할 수 있음

Elisp로 기능을 결합하는 방식

  • 앞의 라이브러리는 모두 Emacs Lisp(Elisp)에서 접근할 수 있음
  • Elisp는 실행 중 높은 수준의 즉흥적 구성을 허용하는 동적 프로그래밍 언어
  • Elisp 함수부터 셸 명령까지 Emacs가 사용할 수 있는 동작을 결합해 복잡한 흐름을 조율할 수 있음

wttr.in 날씨 클라이언트 구현

  • wttr.in은 콘솔 중심의 일기예보 웹 서비스이며 JSON 출력을 지원함
  • Emacs용 wttr 명령은 다음 순서로 동작함
    1. 사용자에게 위치를 입력받음
    2. wttr.in 요청 URL을 생성함
    3. HTTP 요청을 전송함
    4. JSON 응답을 처리함
    5. 결과를 미니버퍼에 표시하고 kill ring에 저장함
    6. 처리 중 오류가 발생하면 미니버퍼에 오류 메시지를 표시함
  • 요청 URL 만들기

    • wttr--request-url은 https://wttr.in을 기본 URL로 사용함
    • 입력한 위치의 공백을 +로 바꾸고 /<location>?0&format=j1 형태의 쿼리를 생성함
    • 완성된 URL은 이후 JSON 가져오기 함수로 전달됨
  • HTTP 요청과 JSON 파싱

    • fetch-json-as-hash-table은 url-retrieve-synchronously로 URL을 동기적으로 가져옴
    • 데이터를 받지 못하면 Failed to fetch data from <URL> 오류를 발생시킴
    • 응답 버퍼에서 HTTP 메타데이터 헤더 뒤로 이동한 다음, json-parse-buffer로 나머지 내용을 Elisp hash-table 로 변환함
    • 처리가 끝나면 내려받은 네트워크 버퍼를 항상 제거해 메모리 누수를 방지함
  • 날씨 메시지 조합

    • wttr--report-message는 JSON 데이터에서 다음 값을 추출함
      • 가장 가까운 지역의 이름
      • 지역
      • 국가
      • 현재 섭씨 기온 temp_C
      • 현재 화씨 기온 temp_F
      • 날씨 설명 weatherDesc
    • 비어 있지 않은 위치 요소를 쉼표로 연결하고 위치: 섭씨, 화씨 날씨 설명 형식의 결과를 만듦
    • 전체 구현은 wttr.el 소스에서 확인할 수 있음

명령줄 도구도 서비스로 활용하기

  • Emacs API는 높은 수준의 추상화를 제공하며, HTTP 요청과 JSON 처리를 직접 구현한 wttr.el도 cloc 측정 기준 67줄
  • 더 단순한 대안은 실제 네트워크 요청과 JSON 처리를 weather라는 Python 스크립트에 맡기는 방식임
    • Elisp의 weather 명령이 사용자에게 위치를 입력받음
    • 위치를 인자로 붙여 weather 셸 명령을 구성함
    • shell-command-to-string으로 명령을 실행함
    • 반환 결과를 kill ring에 저장하고 미니버퍼에 표시함
  • 이 구성에서는 셸 명령이 요청을 받는 서비스가 되고 Elisp 함수가 클라이언트 역할을 맡음
  • Elisp의 동적 특성 덕분에 Elisp 라이브러리와 명령줄 유틸리티를 필요에 따라 결합할 수 있으며, 이런 통합 가능성이 Emacs 안에서 모든 것을 서비스처럼 다룰 수 있게 함
Read Entire Article