Python 3.15, re.match() 소프트 디프리케이트… 명시적 이름 re.prefixmatch() 등장
Python 3.15에서 re.match()가 소프트 디프리케이트(soft deprecation) 대상이 되고, 같은 동작을 하되 의미가 이름에 드러나는 re.prefixmatch()가 새 별칭으로 추가됐다. 함수가 사라지는 것은 아니고 경고도 발생하지 않는다. 문서 차원에서 "새 코드에는 쓰지 말자"는 권고가 붙는 수준의 변화다.
핵심은 re.match()의 앵커 범위다. 이 함수는 패턴을 문자열 맨 앞에만 고정하고 끝은 전혀 확인하지 않는다. 그래서 re.match("pi", "pi")와 re.match("pi", "pie")는 매치되지만, re.match("pi", "api")나 re.match("pi", "magpie")는 매치되지 않는다. 문자열 어디에든 나타나면 잡아내는 re.search()(Python 1.5부터), 시작과 끝을 모두 고정하는 re.fullmatch()(3.4부터)와 비교하면 이 "반쪽 앵커"가 유독 헷갈리는 지점이다.
새로 들어온 re.prefixmatch()는 re.match()와 동작이 완전히 같다. 차이는 이름뿐이다. "prefix"라는 단어가 앞에 붙어 있으면 접두사 매칭이라는 의도가 즉시 읽히지만, "match"만 보면 시작 앵커인지 전체 매칭인지 독자가 한 번 더 고민하게 된다. Python의 격언집인 Zen of Python이 강조하는 "명시적인 것이 암묵적인 것보다 낫다"는 원칙을 API 이름에 적용한 셈이다.
소프트 디프리케이트는 PEP 387이 정의한 완충 장치다. 새 코드에서는 쓰지 않는 게 좋지만 기존 코드에서 계속 쓰는 것은 안전하며, 문서와 테스트는 유지되지만 기능 개선은 이뤄지지 않는다. 경고를 띄우지 않고, 향후 제거를 전제하지도 않는다. 소프트 디프리케이트가 정식(하드) 디프리케이트로 "승격"되는 경로 자체가 없다는 점도 분명히 해둔다. re.match()의 제거 계획은 없다.
실무에서 달라지는 것은 제한적이다. 구버전 Python을 함께 지원해야 하는 코드는 계속 re.match()를 써도 된다. 새로 작성하는 코드라면 의도에 따라 셋 중 하나를 고르면 된다. 접두사만 확인하려면 re.prefixmatch(), 문자열 어디든 찾으려면 re.search(), 전체 문자열이 정확히 일치해야 하면 re.fullmatch()다.
기존 코드에서 re.match()를 걷어내고 싶다면 린터로 막을 수 있다. flake8-tidy-imports의 banned-api 설정에 "re.match"를 등록하고 TID251 규칙을 켜면 된다. Ruff에서는 --select TID251과 함께 lint.flake8-tidy-imports.banned-api."re.match".msg 옵션으로 대체 사용을 안내하는 메시지를 붙일 수 있다. 프로젝트 규칙으로 고정해두면 리뷰에서 같은 논쟁을 반복하지 않아도 된다.
정규식 표기 습관도 함께 점검할 가치가 있다. 특수 문자를 쓰지 않는 함수 호출이 일반적으로 조금 더 빠르다는 점이 원문에서 언급된다. 또 Seth Larson은 Python 정규식에서 ^…$ 대신 \A…\z를 쓰라고 권한다. ^와 $는 MULTILINE 플래그나 문자열 끝의 개행 처리 때문에 의도와 다르게 동작할 여지가 있어, 시작과 끝을 확실히 고정하려면 \A와 \z가 더 안전하다.
정리하면 이번 변화는 "이름을 명확하게 만들고, 옛 이름은 조용히 권고 대상으로 남긴다"는 성격이다. 당장 코드를 고쳐야 하는 압박은 없지만, 새 코드에서 re.match()를 볼 이유도 사실상 사라졌다. 팀 차원에서 re.prefixmatch()·re.search()·re.fullmatch() 중 무엇을 언제 쓰는지 한 줄 규칙을 정해두는 것이 이번 소식을 활용하는 가장 현실적인 방법이다.