ANSI 이스케이프 코드 표준
원문은 Julia Evans님이 에 게재했습니다. 이 블로그 구독하기
안녕하세요! 오늘은 ANSI 이스케이프 코드에 대해 이야기해보려고 합니다.
오랫동안 ANSI 이스케이프 코드를 어렴풋이 알고만 있었습니다(“터미널에서 텍스트를 빨갛게 만들고 하는 그거” 정도) 어디에 정의되어 있어야 하는지, 표준이 있는지조차 제대로 이해하지 못했죠. 그냥 막연히 “여기엔 위험이 도사리고 있다”는 느낌만 있었습니다. 올해 터미널에 대해 공부하면서 알게 된 것은 다음과 같습니다:
- ANSI 이스케이프 코드는 터미널의 사용성을 크게 개선하는 많은 기능들을 담당합니다(원격 머신에 SSH로 접속한 상태에서도 시스템 클립보드로 복사할 수 있는 방법이 있다는 걸 아셨나요?? OSC 52라는 이스케이프 코드입니다!)
- 완전히 표준화되어 있지 않아서 항상 안정적으로 동작하지는 않습니다. 게다가 눈에 보이지도 않아서 이스케이프 코드 문제를 해결하는 과정은 극도로 답답합니다.
그래서 이스케이프 코드와 관련된 현존하는 표준들을 스스로 정리해 보고 싶었습니다. 이스케이프 코드가 꼭 이렇게 불안정하고 답답하게 느껴져야만 하는 건지, 아니면 우리가 좀 더 자신 있게 의지할 수 있는 미래가 있을지 알고 싶었기 때문입니다.
이스케이프 코드란?
터미널에서 왼쪽 화살표 키를 눌렀는데 ^[[D가 보이는 걸 본 적 있나요? 그게 바로 이스케이프 코드입니다! 첫 번째 문자가 “escape” 문자이기 때문에 “이스케이프 코드”라고 불리는데, 보통 ESC, \x1b, \E, \033 또는 ^[로 표기합니다.
이스케이프 코드는 터미널 에뮬레이터가 터미널에서 실행 중인 프로그램과 다양한 종류의 정보(색상, 마우스 움직임 등)를 주고받는 방식입니다. 이스케이프 코드에는 두 가지 종류가 있습니다:
- 입력 코드는 유니코드로 표현할 수 없는 키 입력이나 마우스 움직임을 터미널 에뮬레이터가 전송하는 코드입니다. 예를 들어 “왼쪽 화살표 키”는
ESC[D, “Ctrl+왼쪽 화살표”는ESC[1;5D일 수 있고, 마우스 클릭은ESC[M :3같은 형태일 수 있습니다. - 출력 코드는 프로그램이 텍스트에 색을 입히거나, 커서를 움직이거나, 화면을 지우거나, 커서를 숨기거나, 텍스트를 클립보드에 복사하거나, 마우스 리포팅을 활성화하거나, 창 제목을 설정하는 등 다양한 작업을 위해 출력할 수 있는 코드입니다.
이제 표준에 대해 이야기해 보겠습니다!
ECMA-48
이스케이프 코드와 관련해 제가 처음 찾은 표준은 ECMA-48로, 1976년에 처음 출판되었습니다.
ECMA-48은 두 가지 역할을 합니다:
- 이스케이프 코드에 대한 일반적인 형식을 정의합니다(예를 들어
ESC[+ 무언가 형태의 “CSI” 코드나ESC]+ 무언가 형태의 “OSC” 코드 등) - “커서를 왼쪽으로 이동”이
ESC[D이거나 “텍스트를 빨갛게 만들기”가ESC[31m인 것처럼 구체적인 이스케이프 코드들을 정의합니다. 스펙에서는 “커서 왼쪽” 코드를CURSOR LEFT, 색상을 바꾸는 코드를SELECT GRAPHIC RENDITION이라고 부릅니다.
형식은 확장 가능해서 다른 이들이 앞으로 더 많은 이스케이프 코드를 정의할 여지가 있습니다. 오늘날 널리 쓰이는 많은 이스케이프 코드가 ECMA-48에는 정의되어 있지 않습니다. 예를 들어 vim, htop, tmux 같은 터미널 애플리케이션에서 마우스를 지원하는 것은 꽤 흔한 일이지만, ECMA-48은 마우스를 위한 이스케이프 코드를 정의하지 않습니다.
xterm 제어 시퀀스
ECMA-48에 정의되어 있지 않은 이스케이프 코드들이 많이 있습니다. 예를 들면:
- 마우스 리포팅 활성화(터미널 어디를 클릭했는지)
- 브라켓 붙여넣기(bracketed paste, 텍스트를 붙여넣은 건지 직접 입력한 건지 구분)
- OSC 52(터미널 애플리케이션이 시스템 클립보드로 텍스트를 복사하는 데 사용할 수 있는 코드)
제가 알기로는(틀렸다면 정정해 주세요!) 이 코드들을 비롯한 몇 가지는 xterm에서 유래했고, XTerm Control Sequences에 문서화되어 있으며, 다른 터미널 에뮬레이터들에서도 널리 구현되어 있습니다.
이 “xterm이 지원하는 것들” 목록이 정확히 표준은 아니지만, xterm이 엄청나게 영향력이 크기 때문에 중요한 문서로 보입니다.
terminfo
1980년대에는(어느 정도는 지금도 그렇지만, 제가 이해하기로는 80년대에 그 차이가 훨씬 더 극심했습니다) 터미널마다 실제로 지원하는 이스케이프 코드의 편차가 매우 컸습니다.
이 문제를 해결하기 위해 다양한 터미널의 이스케이프 코드를 모아 놓은 “terminfo”라는 데이터베이스가 있습니다.
terminfo의 표준은 X/Open Curses라고 불리는 것 같습니다. 이상하게도 그 표준을 보려면 계정을 만들어야 합니다. 이 표준은 데이터베이스 형식과 데이터베이스에 접근하기 위한 C 라이브러리 인터페이스(“curses”)를 정의합니다.
예를 들어, 시스템이 알고 있는 모든 터미널에 대해 “화면 지우기”에 해당하는 모든 이스케이프 코드를 보려면 다음과 같은 bash 스니펫을 실행할 수 있습니다:
for term in $(toe -a | awk '{print $1}')
do
echo $term
infocmp -1 -T "$term" 2>/dev/null | grep 'clear=' | sed 's/clear=//g;s/,//g'
done
제 시스템에서는(그리고 아마 제가 써 본 모든 시스템에서?) terminfo 데이터베이스가 ncurses에 의해 관리됩니다.
프로그램은 terminfo를 사용해야 할까?
애플리케이션들이 ANSI 이스케이프 코드를 다루는 방식에는 크게 두 가지 접근법이 있다는 점이 흥미롭다고 생각합니다:
TERM환경 변수에 들어 있는 값을 기준으로 terminfo 데이터베이스를 이용해 어떤 이스케이프 코드를 쓸지 알아내는 방법. 예를 들어 Fish가 이렇게 합니다.- “충분히 많은” 터미널 에뮬레이터에서 동작하는 “단일 공통 집합”의 이스케이프 코드를 파악해 그냥 하드코딩하는 방법.
접근법 2(“terminfo를 사용하지 않음”)를 취하는 프로그램/라이브러리의 예시는 다음과 같습니다:
사람들이 왜 terminfo에서 멀어지고 있을까 궁금해서 찾아보다가, fish 메인테이너 중 한 명이 쓴 terminfo에 대한 매우 흥미롭고 극도로 상세한 글을 찾았는데, 그 글은 이렇게 주장합니다:
[terminfo 저자들은] 당시에는 극도로 중요하고 도움이 되는 많은 작업을 해냈습니다. 제 요점은 그것이 더 이상 그렇지 않다는 것입니다.
제가 제대로 전달할 자신이 없어서 요약하지 않겠습니다. 읽어 볼 가치가 있다고 생각합니다.
“단일 공통 집합”의 이스케이프 코드가 존재할까?
방금 대부분의 사람들에게 통할 “공통 집합”의 이스케이프 코드를 사용할 수 있다는 아이디어에 대해 이야기했습니다. 그런데 그 집합이 정확히 무엇일까요? 합의된 바가 있을까요?
저는 이 질문에 대한 답을 전혀 모르지만, 이것저것 읽어 본 바로는 대략 다음과 같은 조합인 것 같습니다:
- VT100이 지원했던 코드들(다만 일부는 현대 터미널에서는 더 이상 의미가 없습니다)
- ECMA-48에 들어 있는 것들(이 역시 더 이상 의미 없는 것들이 일부 있다고 생각합니다)
- xterm이 지원하는 것들(다만 그 안의 모든 것이 실제로 충분히 널리 지원되는 것은 아닐 거라고 추측합니다)
그리고 궁극적으로는 “사용자들이 가장 자주 쓸 것 같은 터미널 에뮬레이터를 파악해 거기서 테스트하라”는 방식일지도 모릅니다. 웹 개발자들이 어떤 CSS 기능을 써도 괜찮을지 결정할 때와 같은 방식이죠.
하지만 터미널에는 Can I use…?나 Baseline 같은 리소스가 있는 것 같지는 않습니다. (이론상 terminfo가 터미널의 “caniuse” 역할을 해야 하지만, 사람들이 새로운 터미널 기능을 만들면 이를 추가하는 데 10년 이상이 걸리는 경우가 많아 매우 제한적입니다)
terminfo를 사용할 몇 가지 이유
저는 Mastodon에서도 2025년에 사람들이 terminfo를 왜 가치 있다고 생각하는지 물어봤고, 납득이 가는 몇 가지 이유를 들었습니다:
- 어떤 사람들은
TERM환경 변수를 이용해 프로그램 동작을 제어할 수 있기를 기대하고(예를 들어TERM=dumb처럼), post-terminfo 세상에서 그게 어떻게 동작해야 하는지에 대한 표준이 없습니다 - 80년대보다 터미널 에뮬레이터 간의 편차가 줄었다고는 해도 전혀 없는 것은 아닙니다. 그래픽 터미널, Linux 프레임버퍼 콘솔, 시리얼 콘솔로 서버에 접속했을 때의 상황, Emacs 셸 모드 등, 제가 놓치고 있는 경우도 더 있을 겁니다
- “단일 공통 집합”의 이스케이프 코드가 무엇인지에 대한 단일 표준이 없으며, 때때로 프로그램들은 실제로 충분히 널리 지원되지 않는 이스케이프 코드를 사용하기도 합니다
terminfo와 사용자 에이전트 감지
ncurses가 어떤 이스케이프 코드를 사용할지 결정하기 위해 TERM 환경 변수를 사용하는 방식은, 웹 서버가 예전에 브라우저의 사용자 에이전트를 이용해 어떤 버전의 웹사이트를 제공할지 결정하던 방식을 떠올리게 합니다.
결과도 비슷하게 나타난 것 같습니다 — iTerm2가 자신을 “xterm-256color”라고 보고하는 방식은 Safari의 사용자 에이전트가 “Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15”인 것과 비슷하게 느껴집니다. 두 경우 모두 터미널 에뮬레이터/브라우저가 제대로 동작하지 않는 사용자 에이전트 감지를 우회하기 위해 결국 자신의 사용자 에이전트를 바꾸게 됩니다.
웹에서는 결국 사용자 에이전트 감지가 좋은 관행이 아니라는 결론을 내리고, 대신 표준화에 집중해 모든 브라우저에 동일한 HTML/CSS를 제공할 수 있게 되었습니다. 하지만 터미널에서도 같은 접근법이 미래가 될지는 모르겠습니다 — 오늘날 터미널 환경은 웹이 그 어떤 때보다 훨씬 더 파편화되어 있고 자금 지원도 훨씬 적다고 생각합니다.
기타 문서/표준들
이스케이프 코드와 관련된 몇 가지 문서와 표준을 순서 없이 더 소개합니다:
- Linux가 지원하는 이스케이프 코드를 문서화한 Linux console_codes 맨 페이지
- VT 100이 이스케이프 코드와 제어 시퀀스를 처리하는 방식
- kitty 키보드 프로토콜
- 터미널 내 링크를 위한 OSC 8 (및 채택 현황에 대한 노트)
- tmux의 ANSI 표준 요약
- iTerm의 터미널 기능 보고 사양
- sixel 그래픽
이 주제가 흥미로운 이유
가끔 사람들이 유닉스 터미널이 “시대에 뒤떨어졌다”고 말하는 걸 보는데, 저는 터미널을 너무 좋아해서 어떤 점진적인 변화가 터미널을 덜 “뒤떨어져” 보이게 만들 수 있을지 항상 궁금합니다.
만약 우리가 (웹에서처럼!) 더 명확한 표준 지형을 갖추게 된다면, 터미널 에뮬레이터 개발자가 새로운 기능을 만들고 터미널 애플리케이션 작성자가 그 기능들을 더 자신 있게 채택하기가 쉬워져서, 우리 모두가 그 혜택을 누리고 터미널에서 더 풍부한 경험을 할 수 있을지도 모릅니다.
물론 ANSI 이스케이프 코드를 표준화하는 것은 쉽지 않습니다(ECMA-48이 처음 출판된 지 거의 50년이 지났는데도 아직 해결되지 않았습니다!). 저는 어떤 어려움들이 있는지조차 다 알지 못합니다. 하지만 HTML/CSS/JS 상황도 예전에는 극도로 엉망이었지만 지금은 훨씬 나아졌으니, 어쩌면 희망이 있을지도 모릅니다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기