예전부터 정규식 개체(Regexp)를 사용할 때 값이 제대로 일치하다 말다 한다는 생각을 많이 했습니다.
그래서 가급적 정규식 개체를 사용하지 않았는데......
의외로 원인은 쉬운 곳에 있었습니다.
아래와 같이 정규식 개체를 만들고
/** 정규식 개체 */
reg = /\{\{[a-zA-Z0-9]+:[a-zA-Z0-9]+\}\}|\{\{[a-zA-Z0-9]+\}\}/g
/** 테스트 문자열 */
testString = "{{test001}}, {{test001:test1}}, {{test001:a}}, {{aa}}";
아래와 같이 정규식 개체를 사용하는 함수를 만들어 줍니다.
private F1(): void
{
console.log("F1 : " + this.reg.exec(this.testString));
}
private F2(): void
{
console.log("F2 : " + this.reg.exec(this.testString));
}
private F3(): void
{
console.log("F3 : " + this.reg.exec(this.testString));
}
이제 이 함수를 각각 호출해 봅시다.
for (let i = 0; i < 1; ++i)
{
console.log("----- -----");
this.F1();
this.F2();
this.F3();
}
"다 같은 값이 나와야 하는 게 아닌가?"
라는 생각을 한다면 'Regexp.exec'를 잘못 이해하고 있는 겁니다 ㅎㅎㅎㅎ
(글로벌 플래그(/g)가 없을 때만 같은 값이 나옵니다.)
'Regexp.exec'는 글로벌 플래그(/g)로 선언되어 있으면 호출될 때마다 일치하는 것을 하나씩 리턴합니다.
문제는 위와 같은 예제는 호출 시점이 한눈에 보이니 큰문제가 안 되는데
프로젝트가 커지면 누가 먼저 호출하는지 알 방법이 없습니다.
그러다 보니 '범위 안에서는 자동으로 처음부터 검색하는 게 아닌가?'라는 착각을 하게 됩니다.
무조건 자신이 맨 처음 호출하게 하고 싶으면 라스트 인덱스(lastindex)를 0으로 초기화해서 사용해야 합니다.
결국 우리는 정규식 개체에 대해 착각하고 있던 겁니다.
아래 코드를 실행해 봅시다.
for (let i = 0; i < 1; ++i)
{
console.log("----- -----");
this.F1();
console.log("F1 - lastIndex : " + this.reg.lastIndex);
this.F2();
console.log("F2 - lastIndex : " + this.reg.lastIndex);
this.F3();
console.log("F3 - lastIndex : " + this.reg.lastIndex);
this.F1();
console.log("F1 - lastIndex : " + this.reg.lastIndex);
this.F2();
console.log("F2 - lastIndex : " + this.reg.lastIndex);
this.F3();
console.log("F3 - lastIndex : " + this.reg.lastIndex);
}
이 결과로 두가지의 조심해야 할 것이 보이는데....
1) 'Regexp.exec'는 일치하는 결과를 하나씩 리턴하고 라스트 인덱스가 저장된다.
라스트 인덱스는 일치하는 값이 시작 위치가 저장됩니다.
2) 일치 값이 맨 마지막에 있다면 다음 검색은 무조건 'null' 나온다.
라스트 인덱스 뒤로 일치하는 값이 없다면 'null'이 나오므로 맨 마지막 일치 값 다음 값은 무조건 'null'입니다.
일치하는 값이 1개 이상 있다고 'null'이 안 나오겠지하고 예외 처리 안 하면 에러 난다는 의미입니다.
테스트 프로젝트 : github - dang-gun/HtmlJavascriptSamples/JavascriptRegexObject/
참고 :
stackoverflow - RegExp.exec() returns NULL sporadically - Frode님 답변
MDN - RegExp.prototype.exec()
이렇게 중요한 내용이 국내외 막론하고 정규식 개체를 설명할 때 언급하는 경우를 거의 못 봤습니다.
그나마 문서에는 뭐에 쓰는 건지 정도의 언급만......