Perl 에서 정규표현식에 대해서 익숙해졌는데, 여태까지 JavaScript 에서는 별로 쓸 일이 없어서 사용 안 하고 있다가 문득 필요한 순간이 오니 한 방 얻어맞은 느낌 마냥 하나도 모르겠어서 부끄러웠다. 그래도 나름 제일 집중적으로 사용하고 있는 언어인데, 이런 기본 기능조차 모르고 있으니… 그래서 이번 글에서는 JavaScript에서 정규식을 사용하는 방법과 그 응용을 정리해보자.


정규식 생성

JavaScript 에서 정규식을 생성하기 위해서는 RegExp 객체를 이용하면 된다. RegExp 객체는 JavaScript의 내장 객체로써, new 키워드를 이용하여 생성자로 사용 가능하다.

new RegExp('regex-pattern', 'flags') 와 같이 생성자로 사용하면 RegExp 객체를 반환한다. 짧게는 /'regex-pattern'/'flags' 로 표현할 수도 있다. 마치 배열을 new Array('item1', 'item2') 대신 ['item1', 'item2'] 로 사용할 수 있는 것과 같다.

'regex-pattern' 라고 써진 부분은 잘 아시다시피 정규식의 패턴을 의미한다. 이 사이트에 가시면 정규식의 패턴에 대해 아주 쉽게 이해할 수 있다.

// 다음 두 줄은 같은 의미이다.
new RegExp('.*describe.*', 'g');
/.*describe.*/g;

정규식 사용

JavaScript 에서 정규식을 사용할 때에 재미있는 점은, 정규식 패턴을 이용하여 RegExp 객체를 생성한 후, 해당 객체를 이용하여 여러 문자열에 대해 정규식 패턴을 검사할 수 있다는 점이다.

정규식을 사용하는 데에는 크게 다음의 메서드들이 있다.

정규식 사용; RegExp.prototype

RegExp.prototype 에 정의된 메서드에는 exectest 메서드가 있다.

RegExp.prototype.exec(string)

  • 파라미터: 문자열
  • 반환: 문자열 중 해당 RegExp 객체와 매칭하는 문자열들의 배열

exec 메서드는 파라미터로 문자열을 받으며, 파라미터로 받은 문자열에 해당 RegExp 객체의 패턴이 있는지를 검사한 후, 매칭되는 값들을 배열로 반환한다.

let	specFile = load_as_string_from_somewhere('/somewhere/on/disk/some_spec.js');
let testcaseNames = /.*describe\(\'(.+)\'\)/gm.exec(specFile);

개인적으로 이 메서드는 잘 사용하지 않는다.

RegExp.prototype.test(string)

  • 파라미터: 문자열
  • 반환: true / false

test 메서드는 파라미터로 문자열을 받으며, 파라미터로 받은 문자열에 해당하는 RegExp 객체의 패턴이 있는지를 검사한 후, 있으면 true, 없으면 false를 반환한다.

let	someString = 'I am Korean, and I do love learning something!';
if (/korean/i.test(someString)) {
  console.log('당신은 한국인이군요!');		// 출력!
} else {
  console.log('Okay, bye...');
}

나는 실제로 JavaScript로 정규식을 작성할 때 test 메서드를 많이 사용한다.

정규식 사용; String.prototype

String.prototype 에 정의된 메서드에는 match, search, replacesplit 메서드가 있다.

String.prototype.match(regex-pattern)

  • 파라미터: 정규식 패턴
  • 반환: 정규식 패턴에 매칭되는 문자열의 배열

match 메서드는 파라미터로 정규식 패턴을 받아 문자열을 검사한 후, 매칭되는 문자열의 배열을 반환한다.

let specFile = load_as_string_from_somewhere('/somewhere/on/disk/some_spec.js');
let testcaseNames = specFile.match(/.*describe\(\'(.+)\'\)/gm);

위의 exec 예와 동일하다. 상황에 맞는 것을 쓰면 된다.

한글도 된다. 가령…

console.log("정규식 사용하기".match(/사.+/)); // '사용하기'

유니코드 지원 만세!

String.prototype.search(regex-pattern)

  • 파라미터: 정규식 패턴
  • 반환: 정규식 패턴에 매칭되는 문자열의 인덱스

search 메서드는 파라미터로 정규식 패턴을 받아 문자열을 검사한 후, 첫 번째로 매칭되는 문자열의 인덱스를 반환한다. 만일 매칭되는 문자열이 없으면 -1을 반환한다.

let someString = 'I am Korean, and I do love learning something!';
if (someString.search(/korean/i) !== -1) {
  console.log('당신은 한국인이군요!');	// 출력!
} else {
  console.log('Okay, bye...');
}

문자열의 인덱스가 필요한 경우에는 그렇게 사용하면 되는데, 사실 아직까진 그런 예를 잘 모르겠다. 가령 다음은 6을 반환하는데,

'Hello World!'.search(/world/i)

내가 만들었던 응용 중에서는 이걸 이용해서 뭔가 할 일이 딱히 없었다.

String.prototype.replace(regex-pattern, replace-tobe-string)

  • 파라미터: 정규식 패턴, 교체할 문자열
  • 반환: 교체된 문자열

replace 메서드는 2개의 파라미터를 받으며, 정규식 패턴과 교체할 문자열을 받는다. 만약 해당 패턴과 매칭되는 문자열이 있으면 매칭된 문자열을 교체할 문자열로 교체한다.

이 메서드는 immutable method, 즉 원본을 변경시키지 않는 메서드임을 기억해야 한다.

let someString = 'Hello, World! This world is so good! My world, THE WORLD, is not good :(';
console.log(someString.replace(/world/, '세상')); // 'Hello, World! This 세상 is so good! My world, THE WORLD, is not good :('
console.log(someString.replace(/world/i, '세상'));  // 'Hello, 세상! This world is so good! My world, THE WORLD, is not good :('
console.log(someString.replace(/world/ig, '세상')); // 'Hello, 세상! This 세상 is so good! My 세상, THE 세상, is not good :('

String.prototype.split(regex-pattern)

  • 파라미터: 정규식 패턴
  • 반환: 해당 패턴에 매칭된 문자열을 기준으로 쪼개진 문자열의 배열

아마 이 메서드는 많이 써보셨을 것이다. 파라미터로 문자열 뿐 아니라 정규식 패턴을 받을 수도 있다!

let someString = 'Hello, World! This world is so good! My world, THE WORLD, is not good :(';

let splitString = someString.split(/world/i); // ["Hello, ", "! This ", " is so good! My ", ", THE ", ", is not good :("]

MDN 에 보니 ES6에 새로 정의된 Symbol에 관하여 새롭게 추가된 메서드들도 보였다. 이는 나중에 Symbol에 대해 정리하면서 다시 글을 보강해야겠다.