어디서나 동작하는 정규 표현식 — 정규식의 호환성 문제와 실전 전략

2026-06-29 · 2026-06-29_regular-expressions-everywhere.md

#regex #cross-platform #POSIX #tooling #developer-experience #education #medicine

원문 출처

어디서나 동작하는 정규 표현식 — 정규식의 호환성 문제와 실전 전략

한 줄 요약

정규 표현식은 도구마다 지원하는 기능과 문법이 달라 '어디서나' 쓸 수 있는 부분집합은 매우 좁으며, 특히 GNU sed/awk/grep + Emacs 조합에서도 예외와 차이가 많다. 이 문제는 단순한 불편을 넘어 문서화 부재, 엔진 간 의미론 차이, 중첩된 이스케이프 지옥까지 이어지며, 실무자와 초보자 모두에게 큰 인지 부담을 준다.

원문 핵심 내용

정규식 구현의 파편화가 낳은 현실

원문 저자(John D. Cook)는 정규 표현식의 가장 큰 좌절감이 구현별 차이 (implementation variance)에서 온다고 강조한다. 자신은 Perl(기능이 가장 풍부한 'maximalist' 환경)에서 배웠기 때문에, 익숙한 기능이 다른 도구에서 없거나 다르게 동작할 때마다 당황했다. 예를 들어 Perl에서 \d는 숫자 한 글자이지만, 많은 정규식 엔진(flavor)은 \d를 아예 지원하지 않는다.

저자는 "소프트웨어를 설치할 수 없는 컴퓨터"에서도 작업해야 하는 상황을 고려하여, 공통 부분집합(common subset)을 찾는 접근을 제안한다. 가장 엄격한 정의(모든 도구)에서 공통인 것은 리터럴, 문자 클래스 […], 특수 문자 . * ^ $ 뿐이다.

실용적 타협: sed · awk · grep · Emacs

저자는 자신이 실제로 가장 많이 쓰는 도구를 sed, awk, grep, Emacs로 좁히고, GNU 버전에 -E 옵션(확장 정규식)을 적용했을 때의 공통 기능을 조사했다.

  • awk가 최소 공통분모(lowest common denominator) 역할을 한다. awk에서 지원하는 기능은 sed와 grep에서도 대체로 통한다. 단, 단어 경계는 awk가 \<, \>인 반면 다른 도구는 \b, \B를 쓰므로 차이가 있다.
  • Emacs는 가장 까다로운 예외(oddball)이다. + ? ( ) { } | 같은 메타문자를 사용하려면 앞에 백슬래시를 붙여야 awk와 같은 의미가 된다. 또한 \s, \S는 Emacs에서 공백/비공백이 아니라 문자 클래스 시작을 뜻하므로, 공백 매치를 위해 \s-, \S-를 써야 한다.

최종적으로 "어디서나" 동작한다고 주장하는 기능들

저자는 위 도구들에서 다음 기능이 작동한다고 정리한다 (단, gawk는 정규식 자체에서 역참조를 지원하지 않고 치환 문자열에서만 지원).

  • . (임의 문자), ^ $ (앵커), […] [^…] (문자 클래스)
  • * (0회 이상 반복), \w \W \s \S (단어/비단어, 공백/비공백)
  • \1~\9 역참조, \b \B (단어 경계)
  • ? (0 or 1), + (1회 이상), | (대안), {n,m} (반복 횟수)
  • (...) (캡처 그룹)

하지만 저자도 YMMV(Your Mileage May Vary)라는 경고를 덧붙였고, 뒤이은 HN 댓글들이 이 목록의 실제 호환성에 대해 많은 반론을 제기한다.

Hacker News 커뮤니티 반응

댓글 처리 기록: HN 댓글 40여 개를 읽음. 주요 논점을 10개 카테고리로 정리.

-E 플래그를 빼먹으면 기본 정규식(BRE)과 달라진다 – LoganDark

주장: 원문이 . * ^ $가 공통이라고 말했지만, GNU grep이나 sed는 기본적으로 기본 정규식(BRE, Basic Regular Expression)을 사용하므로 . * ^ $에 특별한 의미가 없거나 이스케이프가 필요하다. -E 옵션을 써야 확장 정규식(ERE)이 적용된다. 원문이 이 점을 충분히 강조하지 않았다.

근거: GNU coreutils 매뉴얼을 보면 BRE에서는 *만 메타문자이고, +, ?, {, }, (, ), |는 리터럴로 취급된다.

반론/대댓글: 다른 댓글러는 원문이 GNU 버전과 -E 사용을 전제로 했지만 독자에게 명시적으로 알리지 않았다고 지적.

대표 작성자: LoganDark

내 판단: 초보자가 가장 흔히 하는 실수 중 하나. 원문이 "GNU 버전의 sed, awk, grep을 쓰고 -E를 적용하라"고 언급은 했지만, 독자에게 '쉽게 간과될 수 있는 전제'라는 점에서 LoganDark의 지적은 타당하다.

POSIX BRE는 진정한 "anywhere"인가? – JdeBP vs oleganza

주장(JdeBP): 저자의 논지는 결국 POSIX 기본 정규식(BRE)이 어디서나 동작한다는 말에 가깝지만, 정작 POSIX도 Single UNIX Specification 8판에서 BRE에 3개의 새로운 백슬래시 이스케이프를 추가했기 때문에 "모든 곳"이 아니다. 모든 시스템이 8판을 따라잡은 것은 아니다.

근거: SUSv8(POSIX.1-2017 이후)에서 BRE에 \d, \w 같은 이스케이프가 허용되었지만, 오래된 시스템은 이를 모른다.

반론(oleganza): JdeBP의 지적은 저자에게 불공평하다. 만약 그런 단서가 없었다면 애초에 그 글을 쓸 필요가 없었을 것이다. 원문은 "실용적인 부분집합"을 찾는 것이 목적이므로 완벽한 보편성을 요구하는 것은 과도하다.

대댓글(JdeBP): 저자는 SUSv8의 변화를 전혀 다루지 않았으므로, 그 단서를 포함했다고 보기 어렵다.

대표 작성자: JdeBP, oleganza

내 판단: 원문이 '어디서나'라는 표현을 느슨하게 사용한 점은 사실이지만, JdeBP의 기준은 지나치게 엄격하다. 다만, 저자가 POSIX 명세의 최신 변화를 언급하지 않은 것은 약점이다.

정규식 엔진의 의미론 차이가 더 근본적이다 – MathMonkeyMan, jonstewart

주장: 문법뿐 아니라 매칭 의미론(matching semantics)도 중요하다. Perl/PCRE는 탐욕적(greedy)이면서 leftmost maximal 우선이지만, POSIX ERE는 leftmost longest를 강제한다. 또한 어떤 도구가 부분 문자열을 매치하는지, 전체 문자열을 매치하는지, 한 줄을 매치하는지도 명확히 해야 한다.

근거: MathMonkeyMan은 이런 세부 사항을 문서에 꼭 명시해야 한다고 주장하며, C++ regex 라이브러리의 여러 문법 옵션과 POSIX 명세 링크를 공유했다. jonstewart는 Perl/PCRE와 POSIX의 차이가 상당하다고 덧붙였다.

반론/대댓글: 별도의 반론보다는 공감과 확장 논의가 이어짐.

대표 작성자: MathMonkeyMan, jonstewart

내 판단: 이 주장은 원문이 놓친 가장 중요한 차원이다. 문법 호환성만 따질 것이 아니라, 매칭 결과 자체가 달라질 수 있음을 인지해야 한다. 특히 의료 애플리케이션에서 데이터 검증에 regex를 쓴다면 의미론 차이가 오류를 유발할 수 있다.

"regex"라고만 문서에 적는 습관의 폐해 – quotemstr, zahlman, bartread, xigoi

주장(quotemstr): 많은 프로젝트가 설정값이나 검색에서 "regex"라고만 말하고 어떤 방언(dialect)인지 명시하지 않아 혼란을 준다. 특히 Rust, JavaScript, Python 커뮤니티에서 이런 태도가 흔하다.

근거: 사용자는 그 도구가 어떤 언어로 작성되었는지 모르거나 신경 쓸 이유가 없다. Google Sheets는 정확히 RE2를 사용한다고 문서화하고, Excel은 PCRE를 명시한다.

반론(zahlman): 내부 구현 언어의 정규식 방언을 문서화하는 것은 당연하다. 설정 파일에 regex를 받는 소프트웨어는 사용자가 프로그래머일 것을 암시한다. 또한 사용자 입력 regex를 받는 경우는 sanitization 문제가 더 중요하다.

대댓글(bartread): 사용자 입력 regex를 받을 때 방언을 명시하지 않으면 보안 문제와 역호환성 문제가 생긴다. "regex"라고만 써 놓는 개발자일수록 sanitization이나 catastrophic backtracking에 무심한 경향이 있다.

대표 작성자: quotemstr, zahlman, bartread

내 판단: quotemstr의 주장이 더 현실적이다. Google Sheets나 Excel 같은 비개발자 대상 도구도 방언을 명시하는데, 다양한 사용자를 가진 오픈소스 프로젝트가 "regex"로 모호함을 남기는 것은 책임 회피다. 이는 교육 현장에서도 중요한 함의를 준다.

Emacs 정규식의 이스케이프 지옥 – rtpg, frou_dh, brookst, afiori

주장(rtpg): Emacs에서 무엇을 이스케이프해야 하는지는 찍어 맞히는 느낌이다. rx라는 구조화된 대안이 있지만 사용하기 즐겁지 않다. 게다가 셸 스크립트에서 regex를 쓰면 이스케이프가 중첩되고, Python에서 raw string을 잊으면 문제가 생기는 등 이스케이프 계층(escaping layering)이 큰 장벽이다.

근거: brookst는 "Python으로 셸 스크립트를 생성하는데 그 안에 regex가 들어가면 중첩된 이스케이프가 미친다"고 덧붙였다. afiori는 "정규식은 여러 DSL의 잡탕이 아니라 구조화된 언어였어야 한다"고 비판했다.

반론(frou_dh): 억지로 장점을 찾자면 Emacs에서 Elisp 코드를 검색할 때 ()가 문자 그대로 매치되어 편리하다는 점이 있다.

대표 작성자: rtpg, brookst, afiori

내 판단: 이 논의는 원문의 핵심인 "Emacs가 oddball"이라는 점을 생생하게 뒷받침한다. 특히 의료 분야에서 Emacs 사용자가 많지는 않겠지만, 어떤 환경에서든 중첩 이스케이프 문제는 regex 학습의 진입 장벽을 높인다.

Go의 RE2 엔진과 역참조 문제 – ok_dad, masklinn

주장(ok_dad): Go 표준 라이브러리의 regexp 패키지는 RE2 엔진을 사용하므로 역참조(backreferences)를 매칭에 쓸 수 없다. 치환 문자열에서는 가능하지만 매칭 패턴 자체는 지원하지 않는다.

근거: Go 공식 문서에서도 명시되어 있다.

반론(masklinn): regexp는 RE2를 그대로 가져온 것이 아니라, 같은 개념을 별도로 구현한 것이다.

대표 작성자: ok_dad, masklinn

내 판단: 역참조는 원문의 공통 기능 목록에 포함되어 있었지만, Go 사용자에게는 이 목록이 "어디서나"가 아님을 보여준다. 이는 원문의 YMMV 경고를 더 강화한다.

RFC 9485 I-Regexp와 IETF의 표준화 시도 – tonyg

주장: RFC 9485 I-Regexp는 상호운용 가능한 정규식 포맷을 정의하려는 노력이다. 이런 표준이 중요하다.

근거: IETF 문서 링크 제공.

반론/대댓글: 별도의 논쟁 없음.

대표 작성자: tonyg

내 판단: 상호운용성 표준화는 바람직하지만, I-Regexp이 실제 업계에 얼마나 채택될지는 불투명하다. 사용자가 의료 데이터 처리에 regex를 쓸 때 참고 가능한 후보로 생각해볼 만하다.

고전적 대안: SNOBOL과 구조화된 패턴 매칭 – gilrain, rramadass

주장(gilrain): 정규식의 한계를 넘어서기 위해 SNOBOL4 패턴 매칭을 다시 조명할 필요가 있다. SNOBOL 패턴은 문맥자유문법을 표현할 수 있어 정규식보다 강력하다.

근거: SNOBOL은 백트래킹 알고리즘을 사용하며, 당시 AWK와 Perl이 등장하면서 밀려났지만 여전히 강력하다.

반론/대댓글: rramadass가 흥미를 보이며 더 자세한 경험을 요청.

대표 작성자: gilrain, rramadass

내 판단: 역사적 관점에서 의미 있지만, 현대 소프트웨어 생태계에서 SNOBOL을 채택하기는 현실적으로 어렵다. 다만, "정규식보다 더 나은 패턴 매칭이 무엇일까"라는 사고 실험은 교육적으로 가치 있다.

정규식의 조합성 부재 – galaxyLogic, wwind123, ystlum, woadwarrior01

주장(galaxyLogic): 정규식은 작은 정규식을 조합하여 큰 정규식을 만들 수 없고, 정규식이 다른 정규식을 "호출"할 수 없다.

근거: JavaScript에서 /abc/ + /def/처럼 문자열 덧셈처럼 할 수 없음을 예시로 듦.

반론(wwind123): 정규식 엔진은 패턴을 컴파일해야 하므로 런타임 호출은 비효율적이지만, 문자열 결합을 통해 컴파일 전에 구성하는 것은 가능하다. ystlum은 "define blocks" 같이 가독성을 위한 확장을 언급. woadwarrior01은 Swift의 RegexBuilder처럼 DSL을 통해 조합형 인터페이스를 제공하는 사례를 소개.

대표 작성자: galaxyLogic, woadwarrior01

내 판단: 조합성 문제는 정규식의 표현력 한계와도 연결된다. 하지만 현대 언어들은 컴파일 타임에 문자열로 결합하거나 빌더 패턴으로 해결하고 있으므로, 문제가 해결 불가능한 수준은 아니다. 다만 배우기 쉽지 않은 것은 분명하다.

macOS/BSD sed에서의 실제 호환성 문제 – semanticc, chasil

주장(semanticc): 원문의 공통 기능 목록에 \w \W \s \S \b \B가 포함되어 있지만, macOS/BSD sed에서는 -E 플래그를 줘도 이들을 지원하지 않는다. 대신 POSIX 문자 클래스 [[:alnum:]][[:<:]], [[:>:]]를 써야 한다.

근거: macOS는 BSD 계열 sed를 사용하며 GNU 확장이 없다.

반론/대댓글: chasil은 Microsoft FINDSTR.EXE+ 연산자도 지원하지 않는다고 보충.

대표 작성자: semanticc, chasil

내 판단: 원문이 "GNU 버전"이라는 전제를 했음에도, 독자들이 macOS나 BSD를 사용할 가능성을 고려하지 못한 한계가 드러난 부분이다. 의료계에서도 macOS를 쓰는 연구자들이 많으므로, 이 차이를 인지해야 한다.

새로운 시각

정규식 호환성은 '표준'보다 '문화'의 문제다

원문과 댓글을 종합해보면, 정규식 호환성 문제는 단순한 기술적 결핍이 아니라 도구 사용 문화의 차이에서 비롯된 부분이 크다. Perl 커뮤니티는 기능을 계속 추가해왔고, POSIX는 수십 년째 거의 변하지 않았다. 각 언어/도구가 자신의 생태계에 맞춰 정규식을 발전시킨 결과, "어디서나 동작하는 부분집합"이라는 개념 자체가 근본적으로 모순될 수 있다. 진정한 해결책은 하나의 보편적 문법을 만드는 것이 아니라, 정규식 대신 좀 더 구조화된 패턴 언어(예: Rust의 regex 크레이트의 builder, Swift의 RegexBuilder, 혹은 parser combinator)를 표준으로 삼는 방향일 수도 있다.

'최소 공통분모'는 학습 커리큘럼의 출발점이 되어야 한다

의료 분야 종사자이자 교육에 관심 많은 사용자에게 시사하는 점: 정규식을 처음 가르칠 때 (.*?) 같은 고급 기법부터 가르치지 말고, BRE의 . * ^ $ [...]와 POSIX 문자 클래스만으로도 할 수 있는 일이 많다는 점을 먼저 체득시키는 것이 효과적이다. 이후 각 환경의 확장 기능을 점진적으로 익히게 하면, 호환성 좌절을 덜 겪을 것이다.

의료 데이터에서 정규식의 위험 – 보이지 않는 오류

의료 정보 시스템에서 환자 ID, 진단 코드, 약물 이름을 정규식으로 검증하거나 추출할 때, 엔진 간 의미론 차이가 치명적인 오류를 낳을 수 있다. 예를 들어, 어떤 엔진은 \d를 ASCII 숫자만 매치하는 반면, 유니코드 숫자(예: ٢)를 포함하는 엔진도 있다. 내시경 기록에서 날짜 패턴을 매치할 때 \b의 동작 차이로 인해 경계 조건이 잘못 해석될 수 있다. 따라서 의료용 로직에는 정규식보다 구조화된 파서정규식에 대한 엄격한 단위 테스트가 필수적이다.

자녀와 미래에 대한 시사점

디지털 문해력의 기초로서 '정규식 사고' 가르치기

아이들이 미래에 어떤 직업을 갖든, 패턴 매칭적인 사고(pattern-matching thinking)는 유용하다. 정규식 자체를 가르치는 것보다, "같은 기능을 하는 코드가 환경에 따라 다르게 동작할 수 있다"는 이식성(potability) 개념을 먼저 이해시키는 것이 중요하다. 이는 단순히 코딩 기술을 넘어, 복잡한 시스템에서 가정을 확인하고 예외를 예상하는 태도를 기르는 교육으로 이어진다.

의료계 진로를 고려하는 아이에게 권할 학습 경로

의료 정보학(health informatics)에 관심 있는 아이에게는 다음 단계를 권한다:

  1. BRE로 시작sed, grep 기본기.
  2. POSIX 문자 클래스 익히기 – 국제화된 데이터(유니코드, 악센트 문자) 처리 필수.
  3. 정규식을 '마지막 도구'로 인식 – 복잡한 데이터는 전용 파서나 라이브러리(예: 의료용 HL7 파서)를 쓰는 것이 더 안전하고 유지보수하기 쉽다는 점을 강조.

사용자 자신(의료인)에게 주는 시사점

의료 현장에서 자주 접하는 데이터(진단 코드, 약물 이름, 내시경 보고서의 패턴)를 정규식으로 처리할 때는 반드시 테스트 환경을 분리하고, 예상치 못한 입력에 대한 fallback을 마련해야 한다. 또한 동료나 후배에게 코드를 전달할 때는 사용된 정규식 방언을 명시하는 습관을 의료 IT 문화로 정착시키는 것이 바람직하다.

---