정규표현식에서 Global flag를 사용할때, 주의해야 할점 (feat. lastIndex)

정성연·2023년 8월 22일
1

문제

정규표현식의 global flag는 특정 시나리오에서 주의해서 사용해야한다.

const regex = /\d{4}-\d{2}-\d{2}/g;
console.log(regex.test('2020-08-06'));
console.log(regex.test('2019-10-10')); 

위 결과가 어떻게 나올까 ? (여기서 정규표현식 문법을 설명하지는 않는다.)

정답을 이미 안다면 이 글을 읽지 않아도 괜찮다.

정답은 true, false 이다.

만약 true, true 가 나온다고 생각했다면 이 글이 앞으로 나올(또는 앞으로 개발하면서 생길) 문제 해결을 하는데 도움이 될 것이다.

왜 true, false 일까 ?

왜 앞의 정규표현식의 매칭 결과가 다르게 나올까?

앞의 문자열은 정규표현식은 정규표현식에 부합하고 뒤의 문자열은 부합하지 않아서일까 ?

하지만 순서를 바꿔서 적용하더라도 false, true가 아닌 true, false 가 찍할 것이다.

자바스크립트의 정규식은 객체이다.

전역 플래그(/g)는 정규식 객체 내에서 상태를 가진다.
전역 플래그를 사용하는 정규식 객체는 여러 작업을 진행 할때, 마지막 일치 위치(lastIndex)를 객체의 내부 상태로 유지한다.

다음 처럼 lastIndex값이 변경된 상태에서 한번 더 정규표현식의 exec나 test를 사용하면 문자열의 앞부분은 무시한채 뒤의 문자열에서 정규표현식이 부합하는 문자열을 찾게 된다.

lastIndex 동작 원리

lastIndex는 global flag(/g) 또는 고정 검색(\y)가 적용 되었을 때에 사용 할 수 있는 속성이다.

exec가 호출 될때, lastIndex는 다음과 같이 변경된다.
1. exec에서 문자열을 찾으면 문자열의 끝 항목이 lastIndex에 설정된다.
2. exec에서 문자열의 길이보다 lastIndex길이가 그면 항목을 찾이 못하고 0으로 설정된다.
3. 일치하는 항목을 찾지 못하면 0 lastIndex으로 설정된다.

const regex = /\d{4}-\d{2}-\d{2}/g;
console.log(regex.test('2020-08-06')); 
// 1. exec에서 문자열을 찾으면 문자열의 끝 항목이 lastIndex에 설정된다.
//lastIndex === 10

console.log(regex.test('2019-10-10')); 
// 2. exec에서 문자열의 길이보다 lastIndex길이가 그면 항목을 찾이 못하고 0으로 설정된다.
//lastIndex === 0

console.log(regex.test('2019-10-11')); 
// 다시 1번을 반복한다.

언제 주의 해야할까 ?

iterator 한 로직에서 실수로 global flag를 사용한 정규표현식이 있지 않은지 주의해야할 필요가 있다.

아래는 예시지만, 저자는 아래와 같은 로직을 작성하다 문제를 발견하고 원인을 찾아봤다.

const exampleTextList = ['2020-01-02', '2020-01-03']
const DATE_REGEXP = /\d{4}-\d{2}-\d{2}/g;

const result = exampleTextList.map((text)=> {
  return DATE_REGEXP.test(text)
})

console.log(result) // [true, false]

이렇게 global flag 와 반복문을 같이 사용하는 경우 (원인을 모른다면) 알수 없는 원인에 만났다고 생각이 들 수 있다.

해결 방법

  1. global flag를 사용하지 않는 방법

  2. exec를 사용중이라면 match 로 대체 가능하다.

  3. lastIndex 를 0 으로 초기화 해주는 방법도 있다.
    (속성 lastIndex는 쓰기가 가능한 속성이다.)

참고

profile
개발자

1개의 댓글

comment-user-thumbnail
2023년 9월 8일

Thanks for the information. TellTims

답글 달기