본문 바로가기

IT & AI

Lisp는 왜 언어를 문제에 맞게 바꿀 수 있을까요

728x90

Lisp는 왜 언어를 문제에 맞게 바꿀 수 있을까요

AI 뉴스 썸네일
AI 뉴스 썸네일

Lisp를 처음 보면 괄호부터 눈에 들어와요. 하지만 Lisp의 오래된 매력은 문법의 모양보다 프로그램이 쓰는 언어 자체를 문제에 맞게 확장할 수 있다는 데 있어요. 매크로, 코드와 데이터의 같은 표현 방식, 실행 중인 프로그램을 고치는 REPL 환경이 이 작업을 함께 떠받쳐요. 1

핵심 요약

구분핵심왜 볼 만한가요
언어 확장매크로가 평가 전 코드를 데이터로 받아 새 문법을 만들어요반복 코드를 줄이는 수준을 넘어 문제에 맞는 제어 구조를 정의할 수 있어요
코드와 데이터Lisp 코드는 리스트 형태의 데이터로도 다룰 수 있어요프로그램이 다른 프로그램을 분석하고 변환하기 쉬워져요
개발 방식REPL에서 실행 중인 프로세스에 새 정의를 계속 반영해요상태를 살펴보며 함수와 매크로를 작은 단위로 바꿀 수 있어요
확장성내부 DSL을 사용자에게 열어 제품의 기능을 확장할 수 있어요Emacs와 AutoLISP가 오래된 실제 사례를 보여 줘요

1. Lisp는 프로그램에 필요한 언어부터 만들어요

원문 저자 Elia Scotto는 Lisp를 배우는 과정이 괄호 읽기에 익숙해지는 일로 끝나지 않는다고 설명해요. 패키지와 심볼, 프로젝트 구성, 조건과 재시작, REPL까지 다뤄야 해요. 더 큰 변화는 알고리듬을 표현하는 방법에서 생겨요. 주어진 문법 안에서만 해법을 짜기보다 문제를 잘 표현하는 구성 요소를 언어에 먼저 더할 수 있어요. 2

함수로는 만들기 어려운 제어 구조를 매크로로 만들어요

Common Lisp에는 다른 언어에서 흔히 보는 기본 `while` 연산자가 없어요. 필요한 개발자는 `defmacro`를 이용해 `while`을 직접 정의할 수 있어요. 새 매크로는 조건과 본문을 즉시 실행하지 않고 코드 형태로 받아 `loop`와 `progn`을 조합한 표현식으로 바꿔요. 컴파일러는 변환된 코드를 평범한 Lisp 코드처럼 처리해요.

728x90

같은 일을 일반 함수로 흉내 내면 차이가 드러나요. 함수 호출 전에 인자가 먼저 평가되기 때문에 반복할 본문은 코드가 아니라 계산을 마친 값이 돼요. 이후 함수처럼 호출하려 하면 숫자 같은 값을 받은 자리에서 타입 오류가 생길 수 있어요. 매크로는 평가 시점을 제어하고 코드 구조를 변환할 수 있어서 새 제어 문법을 만들 수 있어요.

이 기능은 단순한 코드 생성보다 범위가 넓어요. 파일이나 데이터베이스 연결을 열고 닫는 규칙, 오류가 났을 때 복구하는 흐름, 특정 업무 영역의 반복 문법을 하나의 구성 요소로 묶을 수 있어요. 호출하는 코드는 세부 절차 대신 업무 의도를 드러내게 돼요.

코드와 데이터가 같은 모양을 가져요

Lisp 프로그램은 s-expression의 연속이에요. 숫자, 문자열, 심볼 같은 원자와 이들을 담은 리스트로 코드를 표현해요. `(+ 1 2)`를 평가하면 덧셈 코드로 작동하지만, 앞에 인용 표시를 붙이면 같은 구조를 리스트 데이터로 얻어요. 코드와 데이터가 같은 표현 방식을 쓰는 성질을 동형성이라고 불러요.

매크로는 이 구조를 평가 전에 받아 필요한 부분을 읽고 새로운 리스트로 돌려줘요. 다른 언어에서도 추상 구문 트리를 다루는 도구를 만들 수 있지만, Lisp에서는 프로그램의 기본 표기가 이미 리스트예요. 문법을 분석하기 위한 별도 표기와 변환 계층이 상대적으로 얇아요.

장점만 있는 것은 아니에요. 팀마다 매크로를 과하게 만들면 표준 Common Lisp를 읽는 것만으로 코드를 이해하기 어려워질 수 있어요. 매크로가 숨기는 평가 순서와 부수 효과도 살펴야 해요. 새 문법은 반복되는 문제를 분명히 줄이고, 이름과 문서, 확장 결과를 함께 관리할 수 있을 때 도움이 돼요.

REPL은 실행 중인 프로그램과 연결돼요

여기서 말하는 REPL은 계산식 몇 개를 입력하는 별도 콘솔에 그치지 않아요. 편집기와 실행 중인 Lisp 프로세스를 연결하고, 특정 함수나 표현식을 그 환경에서 바로 평가하는 작업 방식에 가까워요. 개발자는 현재 변수와 객체를 살펴보고 함수를 다시 정의하며 결과를 확인해요.

프로세스를 매번 종료하고 처음부터 실행하지 않아도 정의를 바꿀 수 있어요. 오랫동안 살아 있는 프로그램의 상태를 유지한 채 작은 수정부터 시험할 수 있다는 뜻이에요. Common Lisp의 조건과 재시작 기능까지 익히면 오류가 난 지점에서 가능한 복구 방법을 선택하고 작업을 이어 갈 수도 있어요.

오늘날 핫 리로드와 대화형 셸 자체는 Lisp만의 기능이 아니에요. 웹 프런트엔드, Flutter, 여러 인터프리터 언어도 빠른 피드백을 제공해요. Lisp에서 눈여겨볼 부분은 REPL, 매크로, 동형성이 한 환경에 결합돼 있다는 점이에요. 실행 상태를 관찰하면서 언어의 구성 요소까지 바꾸는 흐름이 자연스럽게 이어져요.

내부 DSL을 제품의 확장 기능으로 열 수 있어요

언어 확장 기능은 도메인 특화 언어를 만드는 데 쓰여요. 웹 콘텐츠를 표현하는 매크로나 수학 그래프를 그리는 표현식을 만들고, 이를 제품 사용자에게 확장 인터페이스로 제공할 수 있어요. 사용자는 별도 템플릿 문법에 갇히지 않고 Lisp의 조건문, 반복, 함수, 디버깅 도구를 함께 사용할 수 있어요.

Emacs는 이 접근을 보여 주는 대표 사례예요. 사용자는 Emacs Lisp로 편집기의 동작을 바꾸고 이메일, Git, 터미널, 문서 읽기 같은 기능을 더해요. AutoCAD의 AutoLISP도 반복 작업과 기하 도형 생성을 자동화하는 데 쓰여 왔어요. 두 사례 모두 애플리케이션이 정해진 기능만 제공하는 대신, 사용자가 내부 언어로 동작을 바꿀 수 있게 해요.

DSL은 유지보수 부담도 만들어요. 만든 사람이 떠난 뒤 문서가 부족하면 다음 개발자가 낯선 문법부터 해석해야 해요. 표준 함수와 자료구조로 충분한 문제까지 새 문법으로 감싸면 읽기 비용이 커져요. 매크로 확장 결과를 쉽게 확인할 수 있다는 Lisp의 장점이 있어도, 어떤 추상화를 팀의 공용 언어로 받아들일지는 코드 리뷰와 문서화로 정해야 해요.

왜 중요한가요

Lisp가 곧 주류 언어가 될 가능성을 따지는 것보다, 언어와 도구를 설계하는 관점을 배우는 편이 실용적이에요. TypeScript 변환기, Rust 절차 매크로, 컴파일러 플러그인, 코드 생성 도구처럼 오늘날의 개발 환경에도 코드를 데이터로 다루는 작업이 널리 쓰여요. Lisp는 이 생각을 작고 일관된 문법 안에서 직접 경험하게 해요. 2

다만 강한 표현력은 팀의 공통 이해를 자동으로 만들어 주지 않아요. 새 문법이 실제 반복을 줄이는지, 평가 순서가 명확한지, 확장 결과를 확인할 수 있는지, 표준 기능보다 유지보수가 쉬운지를 함께 봐야 해요. Lisp를 배우며 얻을 수 있는 가장 현실적인 감각은 모든 문제에 매크로를 쓰는 법보다 추상화의 경계를 어디에 둘지 판단하는 법에 가까워요.

참고 자료

  1. Lisp로 가는 길: 왜 Lisp인가 — GeekNews
  2. A road to Lisp: Why Lisp — Elia Scotto
728x90