thenullpage.com

로그에서 error를 찾았는데 Error랑 ERROR는 안 잡혀서 당황하신 적 있으신가요. 여러 줄짜리 문자열 앞에 ^를 붙였더니 첫 줄만 걸리는 경우도 흔하고요. 패턴은 분명 맞는 것 같은데 결과가 이상할 때, 범인이 패턴이 아니라 플래그(flag)인 경우가 정말 많아요. 플래그는 엔진이 기호를 해석하는 방식 자체를 바꾸는 스위치인데, 다섯 개만 알면 실무에서 만날 상황은 거의 다 덮여요.


08.1 대소문자를 무시하고 찾을 수는 없나요?

파이썬에서는 패턴과 문자열 뒤에 자리가 하나 더 있어요. 거기에 플래그를 적어주면 돼요.


import re
로그 = "Error 발생\nerror 재발\nERROR 종료"
re.findall(r"error", 로그)
결과: ['error']
re.findall(r"error", 로그, re.IGNORECASE)
결과: ['Error', 'error', 'ERROR']


같은 패턴인데 셋을 다 잡았죠. re.IGNORECASEre.I라고 짧게 써도 완전히 같아요. 오늘 나오는 플래그는 전부 긴 이름과 한 글자 별칭을 같이 가지고 있어요.


주의할 점이 하나 있어요. 이렇게 적은 플래그는 패턴 전체에 걸려요. 앞부분만 대소문자를 무시하고 싶다면 이 방식으로는 안 되는데, 그 방법은 08.6에서 볼게요.


08.2 여러 줄인데 왜 첫 줄만 잡히나요?

줄마다 앞 단어를 뽑아보면 바로 보여요.


성적 = "kim 90\nlee 85\npark 70"
re.findall(r"^\w+", 성적)
결과: ['kim']
re.findall(r"^\w+", 성적, re.MULTILINE)
결과: ['kim', 'lee', 'park']


기본 상태에서 ^문자열 전체의 맨 앞 딱 한 자리만 가리켜요. 그래서 두 번째 줄부터는 아예 후보에 오르지도 못한 거예요. re.MULTILINE(줄여서 re.M)을 켜면 이 뜻이 각 줄의 시작으로 바뀌고, $도 같이 각 줄의 끝을 가리키게 돼요. 파일을 통째로 읽어온 뒤 줄 단위로 훑을 때 없으면 안 되는 플래그예요.


반대로 \A\Z는 이 플래그의 영향을 안 받아요. 무슨 일이 있어도 문자열 전체의 처음과 끝이어야 한다면 이 둘이 안전해요.


08.3 점이 줄바꿈을 못 넘어가던데요?

맞아요. 그게 .의 유일한 예외거든요.


글 = "제목\n본문 내용"
re.findall(r"제목.본문", 글)
결과: []
re.findall(r"제목.본문", 글, re.DOTALL)
결과: ['제목\n본문']


아무 문자 하나를 뜻하는 .이 딱 하나 못 잡는 게 줄바꿈이에요. re.DOTALL(re.S)을 켜면 그 예외가 사라져서 진짜 아무 문자나 되고요.


여기서 re.Mre.S를 헷갈리는 분이 아주 많은데 담당 구역이 아예 달라요. M은 앵커 담당, S는 점 담당이에요. M을 켠다고 점이 줄바꿈을 넘어가지 않고, S를 켠다고 ^가 줄마다 생기지도 않아요.


플래그 자리를 건드릴 수 없을 때 쓰는 요령도 있어요. [\s\S]라고 적으면 공백이거나 공백이 아닌 것, 그러니까 결국 모든 문자가 되거든요.


08.4 플래그를 두 개 같이 켤 수도 있나요?

세로줄로 이어 붙이면 돼요. 개수 제한은 없어요.


문서 = "TITLE: 안내\nbody: 내용"
re.findall(r"^title.+", 문서, re.I | re.M)
결과: ['TITLE: 안내']


대소문자 무시와 줄 단위 앵커가 동시에 걸려서, 소문자로 적은 패턴이 대문자 제목 줄을 잡아냈어요. 같은 패턴을 여러 번 쓸 거라면 re.compile로 미리 만들어두는 편이 나아요. 플래그를 부를 때마다 적지 않아도 되니 빠뜨리는 실수가 줄거든요.


패턴 = re.compile(r"^\w+", re.M)
패턴.findall("kim 90\nlee 85")
결과: ['kim', 'lee']


08.5 복잡한 패턴에 주석을 달 수 있나요?

달 수 있어요. re.VERBOSE(re.X)를 켜면 패턴 안의 공백과 줄바꿈을 엔진이 무시해줘요. 덕분에 여러 줄로 늘어놓고 우물 정 기호로 설명을 붙일 수 있어요.


패턴 = re.compile(r"""
\d{4} # 연도
- # 구분자
\d{2} # 월
""", re.VERBOSE)
패턴.findall("가입일 2026-07")
결과: ['2026-07']


한 줄로 적었으면 암호처럼 보였을 패턴이 읽을 만해졌죠. 정규식이 길어지기 시작하면 이 모드가 나중의 자신을 살려줘요.


대신 함정이 하나 있어요. 진짜 공백을 찾고 싶어도 무시당한다는 거예요.


re.findall(r"\d+ 개", "사과 3 개", re.X)
결과: []
re.findall(r"\d+[ ]개", "사과 3 개", re.X)
결과: ['3 개']


공백을 대괄호로 감싸서 이건 무시하지 말라고 표시해준 거예요. \s로 바꿔 적어도 되고요. 같은 이유로 진짜 우물 정 문자를 찾을 때는 \#처럼 백슬래시를 붙여야 해요. 안 그러면 거기부터 줄 끝까지가 통째로 주석으로 날아가요.


08.6 패턴 일부에만 플래그를 걸 수도 있나요?

08.1에서 미뤄둔 이야기예요. 괄호 안에 플래그를 적는 방식이 따로 있어요.


re.findall(r"(?i:hello) World", "HELLO World hello world")
결과: ['HELLO World']


앞의 hello는 대문자든 소문자든 통과했지만 뒤의 World는 첫 글자가 대문자여야 했어요. 그래서 뒤쪽의 소문자 world는 탈락했고요. (?i:...)괄호 안에만 효과가 미치는 형태라 이렇게 구역을 나눠 쓸 수 있어요.


콜론 없이 (?i)라고만 적는 형태도 있는데 이건 성격이 달라요. 괄호 안이 아니라 패턴 전체에 걸리거든요. 헷갈림이 심해서 파이썬은 3.11부터 아예 위치를 제한했어요.


re.findall(r"\d+(?i)", "a1")
결과: error: global flags not at the start of the expression


맨 앞이 아닌 자리에 적었다고 에러가 났어요. 전체에 걸 거면 맨 앞에, 일부에만 걸 거면 콜론이 붙은 쪽을 쓴다고 나눠두시면 돼요.


알아두면 좋은 플래그가 하나 더 있어요. re.ASCII(re.A)예요. 파이썬3의 \w\d는 기본이 유니코드 기준이라 한글도 글자로 쳐주거든요.


re.findall(r"\w+", "한글abc")
결과: ['한글abc']
re.findall(r"\w+", "한글abc", re.A)
결과: ['abc']


영문과 숫자, 밑줄만 글자로 치고 싶을 때 켜면 돼요.


08.7 다른 도구에서도 똑같이 쓰나요?

이름은 비슷한데 적는 자리가 달라요. 자바스크립트는 슬래시 뒤에 한 글자씩 붙여서 /error/gi처럼 쓰고, 터미널의 grep은 grep -i처럼 옵션으로 줘요.


자바스크립트에는 파이썬에 없는 g 플래그가 있는데, 이게 초보자를 제일 많이 울려요.


const 정규식 = /\d+/g;
정규식.test("a1")
결과: true
정규식.test("a1")
결과: false


같은 문자열을 같은 정규식으로 두 번 검사했는데 답이 뒤집혔어요. g가 붙은 정규식은 어디까지 찾았는지를 자기 안에 기억해두고 다음 호출 때 그 자리부터 이어서 보기 때문이에요. 반복해서 검사만 할 정규식에는 g를 빼거나, 검사할 때마다 정규식을 새로 만들면 안 겪어요.


정리하면 플래그는 엔진의 기본 규칙을 갈아끼우는 스위치예요. 대소문자를 푸는 I, 앵커를 줄 단위로 바꾸는 M, 점의 예외를 없애는 S, 패턴에 주석을 허용하는 X, 글자 범위를 영문으로 좁히는 A까지 다섯 개면 충분해요. 패턴은 맞는 것 같은데 결과가 이상하다면 플래그부터 한번 확인해보세요.