본문 바로가기

Development/Javascript

[Typescript] RegExp의 함수를 사용할 때 주의할 점

728x90

서론

Typescript나 Javascript에서 RegExp 객체를 변수화한 뒤에 test()나 exec() 함수를 사용할 때는 특별히 주의할 점이 있다. 바로 lastIndex. 정규표현식을 사용할 때 flag로 넣을 수 있는 옵션 중에 g가 있는데 이것은 전체 찾기를 하겠다는 옵션이다. 이걸 넣어서 사용하면 내부적으로 lastIndex를 사용하게 되는데 lastIndex 속성은 함수를 실행할 때마다 값을 기억하고 다시 시작할 때 그 위치에서 검색을 시작한다. 그러니까 대상자 하나를 가지고 검색할 때는 괜찮을 수 있으나, 단순 검색으로 여러 대상을 가지고 검색을 할 때는 100% 문제가 된다.

원인

 

 

위의 코드를 보면 결과값이 true, true, 가 나오는 것으로 기대하지만 실제로는 true, false 로 나온다. 왜 이런 차이가 발생하는 것일까? 서론에서도 말한 것과 같이 Global Flag가 설정되면 전체 단위로 찾기 때문에 한 번만 찾는 게 아니게 된다. 따라서 여러 개를 찾아야 하기 때문에 현재까지 찾은 index를 내부적으로 가지고 있는데 그게 바로 lastIndex 속성이다. 위와 같은 소스에서 중간 중간에 lastIndex를 찍어보자.

 

 

결과값을 보면 알겠지만 0, true, 12, false, 0 이렇게 나온다. 중간에 12라는 값은 첫 번째에 검색했던 You are such a great에서 such의 마지막 Index 값이다. 그러니까 h의 index이다. 그러고 나서 두 번째 검색할 때는 lastIndex가 12가 된 상태에서 시작하기 때문에 12번째 자릿수 뒤로부터 검색하니 testValue2의 test 함수 리턴값은 false가 나온 것이다. 만약에 testValue2의 such가 12번째 이후에 있었다면 true가 나와서 잘못 사용했지만 버그는 발견되지 않는 아주 나쁜 상황이 될 수도 있다. 따라서 다음과 같이 사용해야 한다.

해결

해결 방법에는 당연히 여러가지가 있겠지만 크게 분류하자면 두 가지정도로 나누어질 듯 싶다. (I think), (제가 몰랐던 아름답고 멋진 코드는 언제나 환영입니다~~)

해결 1. Regex 리터럴 사용

 

 

변수화(상수화 포함) 하지 않고 리터럴을 사용하여 test함수를 사용하는 방법이다. 이 방법의 단점은 상수화하지 못 해 반복적인 코드일 때 단일화할 수 없다는 것과 선언적 코드가 로직 중간에 들어가게 되는 단점이 발생한다. 그래서 나는 주로 두 번째 해결 방법을 사용한다.

해결2. 새롭게 Regex 객체를 생성하여 사용

 

 

이 방법도 맘에 들지 않는다는 단점이 있지만 (생성하고 또 생성한다는 게 맘에 안들어요ㅜㅜ) 그나마 깔끔한 코드인 듯 싶다. Constructor Parameter로는 RegExp 객체도 들어가고 String도 들어가기 때문에 구분없이 사용해도 된다.

해결3. lastIndex 강제 초기화

DEFAULT_REGEXP.lastIndex = 0;
const [ s, p1, p2, p3, p4, p5, offset, others ] = DEFAULT_REGEXP.exec( inputStr );

Array에 index를 0으로 주입하면 전체 삭제가 되는 효과를 얻을 수 있는 것처럼 lastIndex를 0으로 바꾸면 위의 문제를 해결할 수 있다.

 

참고

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D

 

정규 표현식

정규 표현식은 문자열에 나타는 특정 문자 조합과 대응시키기 위해 사용되는 패턴입니다. 자바스크립트에서, 정규 표현식 또한 객체입니다.  이 패턴들은 RegExp의 exec 메소드와 test 메소드  ,그리고 String의  match메소드 , replace메소드 , search메소드 ,  split 메소드와 함께 쓰입니다 . 이 장에서는 자바스크립트의 정규식에 대하여 설명합니다.

developer.mozilla.org

 

반응형
  • 오홍 2022.04.25 17:04 댓글주소 수정/삭제 댓글쓰기

    g옵션을 빼면 되지 않나요?
    단일 스트링을 테스트하는데 Global Flag를 넣는 이유가 있을까요?

    • 알고계신대로 단일 검색만 한다면 g 옵션을 빼면 됩니다!
      제 의도는 다양한 위치의 값을 검색하려고 할 때의 케이스를 표현한 것이었는데 그런 내용이 제대로 전달되지 않게 글을 써 혼란을 드렸던 것 같습니다.