ANSI 이스케이프 코드는 터미널에 색을 입히고, 커서를 움직이고, 화면을 지우는 등의 명령을 특정한 문자열로 전달하는 약속입니다. 프로그램이 화면에 그냥 글자만 찍는 게 아니라 ESC(이스케이프, 아스키 27번)로 시작하는 특별한 시퀀스를 출력하면, 터미널이 그걸 "출력할 글자"가 아니라 "실행할 명령"으로 해석하죠. ls의 색깔, 진행 막대, 로딩 스피너가 전부 이 원리로 동작합니다.
겉보기엔 마법 같지만 뜯어보면 규칙이 꽤 단순합니다. 나도 로그 색을 직접 입히려다 처음 파고들었는데, 알고 나니 진행 막대나 인터랙티브 CLI를 만드는 재미가 붙더군요.
기본 형태 — CSI 시퀀스
대부분의 코드는 ESC[로 시작합니다. 이 두 글자를 CSI(Control Sequence Introducer)라고 부릅니다. 문서에서는 흔히 \033[, \x1b[, \e[로 씁니다. 셋 다 같은 ESC[입니다.
| 표기 | 진법 | 쓰이는 곳 |
|---|---|---|
\033[ | 8진수 27 | C, 셸 printf |
\x1b[ | 16진수 27 | Python, JS |
\e[ | 이스케이프 축약 | bash echo -e |
색 입히기 — SGR 코드
가장 많이 쓰는 건 ESC[...m 형태의 SGR(Select Graphic Rendition)입니다. 숫자로 색과 스타일을 고르고, 끝에 m을 붙입니다. 그리고 반드시 리셋(0m)으로 닫아야 이후 출력까지 물들지 않습니다.
# bash에서 빨간 글씨
printf '\033[31m에러!\033[0m 정상 텍스트\n'
# 굵게 + 초록
printf '\033[1;32m성공\033[0m\n'
# 배경색까지 (41=빨강 배경)
printf '\033[97;41m 경고 \033[0m\n'
주요 SGR 숫자만 외워두면 대부분 커버됩니다. 0 리셋, 1 굵게, 4 밑줄, 전경색 30~37, 배경색 40~47. 세미콜론으로 여러 개를 한 번에 넣을 수 있어요. 256색은 38;5;N, 트루컬러(24비트)는 38;2;R;G;B 형식입니다.
커서와 화면 제어 — 진행 막대의 비밀
같은 줄을 계속 다시 그리는 진행 막대는 커서 제어 코드로 만듭니다. 핵심은 "커서를 줄 맨 앞으로 되돌리고 덮어쓰기"입니다.
ESC[2J 화면 전체 지우기
ESC[H 커서를 좌상단(홈)으로
ESC[K 커서부터 줄 끝까지 지우기
ESC[nA 커서 n칸 위로 (B 아래, C 오른쪽, D 왼쪽)
ESC[?25l 커서 숨기기 / ESC[?25h 다시 보이기
진행 막대의 정석은 매 갱신마다 줄 앞으로 돌아가 다시 그리는 것입니다. 캐리지 리턴(\r)으로 커서를 줄 맨 앞에 보내고 ESC[K로 남은 글자를 지운 뒤 새 막대를 찍으면, 한 줄이 제자리에서 갱신되는 것처럼 보이죠. 스피너를 돌릴 땐 ESC[?25l로 커서를 숨겨두면 훨씬 깔끔합니다.
커서를 숨긴 채로 프로그램이 죽으면 터미널에 커서가 사라진 상태로 남습니다. 나도 Ctrl+C로 스크립트를 죽였다가 커서가 안 보여 당황한 적이 있어요. 그래서 커서를 숨기는 코드를 넣을 땐 종료·인터럽트 시 ESC[?25h로 되살리는 처리를 반드시 짝지어 둡니다.
주의할 점 — 파이프와 폭
- 파이프·파일로 나갈 땐 끄기 — 출력이 터미널이 아니라 파일이나 다른 프로그램으로 갈 때 색 코드를 그대로 뱉으면
^[[31m같은 쓰레기가 섞입니다. 성숙한 CLI는 출력이 터미널인지(isatty)를 검사해 자동으로 색을 끕니다. - 보이지 않는 글자도 폭을 먹는다고 착각 금지 — SGR 코드는 화면 폭을 차지하지 않습니다. 그래서 색을 입힌 문자열의 길이를
len()으로 재면 이스케이프 문자까지 세어져 정렬이 어긋납니다. 폭 계산은 코드를 벗겨낸 순수 텍스트로 해야 합니다. - bash 프롬프트에선 감싸기 —
PS1에 색을 넣을 땐 이스케이프를\[ \]로 감싸야 줄바꿈 계산이 깨지지 않습니다.
자주 묻는 질문
echo로는 왜 색이 안 나오나요?
echo가 \033 같은 이스케이프를 해석하지 않기 때문입니다. bash라면 echo -e를 쓰거나, 이식성이 더 좋은 printf를 쓰세요. printf '\033[31m...\033[0m'가 셸을 가리지 않고 잘 동작합니다.
리셋을 안 하면 어떻게 되나요?
이후에 출력되는 모든 텍스트가, 심지어 프로그램이 끝난 뒤 셸 프롬프트까지 그 색·스타일을 물려받습니다. 터미널이 통째로 빨개지는 사고가 이겁니다. 색을 켰으면 항상 \033[0m으로 닫는 습관을 들이세요. 최악의 경우 reset 명령으로 터미널을 초기화할 수 있습니다.
직접 코드를 짜기보다 라이브러리를 쓰는 게 낫지 않나요?
대개 그렇습니다. 파이썬의 rich·colorama, JS의 chalk, Go의 lipgloss 같은 라이브러리는 터미널 감지, 색상 폴백, 윈도우 호환까지 처리해 줍니다. 다만 원리를 알아두면 라이브러리가 없는 환경이나 로그 포맷을 직접 만질 때 큰 도움이 됩니다.
윈도우 콘솔에서도 되나요?
과거 cmd.exe는 ANSI 코드를 해석하지 못했지만, 요즘 Windows Terminal과 최신 콘솔은 ANSI를 지원합니다. 다만 구형 환경 호환을 위해 colorama 같은 라이브러리가 ANSI 코드를 윈도우 API 호출로 번역해 주는 역할을 여전히 합니다.

댓글 0