Numpilot
손안의 수치해석/2부 언어의 문제 — DSL

DSL이란 무엇인가 — 좁은 언어의 힘

08 / 30
2026-07-31 · 읽는 데 9분

무엇이든 할 수 있는 언어의 문제

앞 편에서 결론이 이렇게 났다. 우리에게 필요한 것은 의도적으로 좁은 언어다.

그런데 좁다는 것은 보통 단점으로 여겨진다. 프로그래밍 언어를 고를 때 사람들은 “이 언어로 무엇을 할 수 있나”를 묻는다. 많이 할 수 있는 쪽이 좋아 보인다.

파이썬으로는 웹서버도 만들고 데이터 분석도 하고 게임도 만든다. 무엇이든 할 수 있다. 그럼 해석 조건도 파이썬으로 쓰면 되지 않을까.

실제로 많은 해석 도구가 그렇게 한다. 파이썬 스크립트로 조건을 정의하는 방식이다.

문제는 이렇다. 무엇이든 할 수 있는 언어는, 무엇이 들어올지 알 수 없는 언어이기도 하다.

사용자가 조건을 파이썬으로 쓴다면, 그 안에 반복문이 있을 수도 있고 파일을 읽을 수도 있고 무한 루프에 빠질 수도 있다. 시스템은 그것이 무엇을 뜻하는지 미리 알 수 없다. 실행해 봐야 안다.

그리고 실행해 봐야 아는 것은 검사할 수 없다. 우리가 필요한 것은 정반대였다. 사용자가 준 조건이 완전한지 확인하고, 빈 곳을 채우고, 채운 값을 사용자에게 보여주는 것.

DSL: 한 가지만 잘하는 언어

DSL(Domain-Specific Language, 도메인 특화 언어)은 하나의 영역만 다루도록 설계된 언어다.

낯선 개념이 아니다. 우리는 이미 여러 DSL을 쓰고 있다.

요리 레시피. “중불에서 3분간 볶는다”는 문장은 형식이 정해져 있다. 동작, 세기, 시간. 이 형식 안에서 수천 가지 요리를 표현할 수 있지만, 이걸로 세금 계산은 못 한다. 그래서 좋은 레시피 언어다.

악보. 음의 높이와 길이와 세기를 적는다. 이 형식 밖의 것은 표현할 수 없다. 대신 그 안에서는 대단히 정밀하다.

지도의 범례. 선의 종류로 도로 등급을 표현한다. 정해진 몇 가지뿐이지만, 그래서 누가 봐도 같게 읽힌다.

세 경우의 공통점은 이렇다. 표현 범위가 좁고, 그 좁음 때문에 오해가 없다.

좁음이 만들어내는 것

좁은 언어는 세 가지를 가능하게 한다. 이게 우리에게 필요했던 것들과 정확히 맞았다.

1. 검사할 수 있다

레시피에 “볶는다”라고만 적혀 있고 시간이 없으면, 우리는 그것이 불완전하다는 걸 안다. 형식이 정해져 있으므로 무엇이 빠졌는지 알 수 있다.

파이썬 스크립트에서는 이걸 못 한다. 무엇이 빠졌는지 판단할 기준이 없기 때문이다.

우리 문제로 옮기면, DSL로 표현된 해석 조건은 실행하기 전에 완전성을 검사할 수 있다. “재료는 있지만 열전도율이 없다”, “왼쪽 변 조건은 있지만 오른쪽 변은 없다” 같은 판단이 가능하다.

2. 기본값을 정할 수 있다

형식이 정해져 있으면 각 자리에 무엇이 오는지 안다. 그러면 빈 자리에 무엇을 채워야 하는지도 안다.

“주변 공기 온도” 자리가 비었다면 실온을 넣는다. 이런 판단이 가능한 이유는 그 자리가 무엇을 뜻하는지 시스템이 알기 때문이다.

앞 편에서 세어본 스무 개의 생략된 값들. DSL의 각 자리에 기본값을 붙여두면 그것들이 자동으로 채워진다.

3. 사람이 읽을 수 있다

이게 가장 중요하다.

DSL은 좁기 때문에 문법이 단순하다. 그리고 단순한 문법은 배우지 않고도 읽힌다. 악보를 못 읽는 사람도 음이 올라가는지 내려가는지는 본다.

우리가 필요했던 다섯 조건 중 마지막 두 개 — 사람에게 보여주고, 사람이 고칠 수 있게 — 는 이 성질에 기댄다.

왜 자연어로 바로 안 되나

여기서 자연스러운 반문이 있다. AI가 언어를 잘 다루게 된 시대에, 왜 중간 언어가 필요한가. 문장을 받아서 바로 계산하면 안 되나.

이유는 문장이 상태를 담지 못하기 때문이다.

사용자가 열 번 조건을 바꿔가며 시도한다고 하자. 매번 문장으로만 소통한다면, 지금 무엇이 설정되어 있는지 어디에도 적혀 있지 않다. 대화 기록을 거슬러 올라가며 재구성해야 한다.

그리고 그 재구성이 조금이라도 틀리면, 사용자는 자기가 무엇을 계산했는지 모르게 된다. 결과는 나오는데 그 결과가 어떤 조건에서 나온 것인지 확신할 수 없다.

해석에서 이건 치명적이다. 온도가 48도로 나왔다는 결과는, 어떤 조건에서 48도인지 모르면 아무 의미가 없다.

그래서 상태를 명시적으로 적어두는 곳이 필요하다. 그게 DSL 문서다.

정리하면 역할이 이렇게 나뉜다.

하는 일 잘하는 것
자연어 사용자의 의도 표현 편하고 빠르다
DSL 현재 상태의 명시적 기록 정확하고 검사 가능하다
솔버 계산 실행 빠르고 반복 가능하다

AI는 첫 층에서 둘째 층으로 옮기는 일을 한다. 계산을 대신하는 것이 아니라 번역을 한다.

그런데 좁게 만드는 게 어렵다

여기까지는 방향이 명확하다. 문제는 실행이다.

“좁게 만들자”는 말은 무엇을 뺄지 정하자는 말이다. 그리고 뺄 것을 정하는 일은 넣을 것을 정하는 일보다 훨씬 어렵다.

넣는 결정은 되돌리기 쉽다. 나중에 안 쓰면 그냥 안 쓰면 된다. 빼는 결정은 되돌리기 어렵다. 어떤 사용자에게는 그것이 유일하게 필요한 기능이었을 수 있다.

그리고 잘못 빼면 언어 전체가 쓸모없어진다. 예를 들어 재료를 몇 종류만 허용한다고 하자. 목록에 없는 재료를 다루는 사용자에게 이 도구는 존재하지 않는 것과 같다.

반대로 잘못 넣으면 좁음의 이점이 사라진다. 무엇이든 표현할 수 있게 만들면 검사도 기본값도 불가능해진다.

이 균형을 잡는 것이 우리 설계의 핵심 작업이었다. 다음 세 편에서 그 결정들을 하나씩 짚는다. 무엇을 표현하게 할지, 애매함을 어떻게 다룰지, 그리고 단위라는 함정을 어떻게 피할지.


다음 편 예고 — 설계 ①: 무엇을 표현하고 무엇을 버릴까. 실제로 넣은 것과 뺀 것의 목록, 그리고 각 결정의 근거를 공개한다.