정규표현식에 대해서...
웹 개발을 하다 보면 보안적인 이유 뿐 아니라 여러 다양한 상황에 따라서 문자열을 조작하거나, 원하는 문자열을 추출하는 등의 기능이 필요할 때가 있습니다. 아마 신입사원 사전과제를 진행하면서 이메일 체크 부분은 대부분 정규표현식을 사용하였을 것이라고 생각합니다. 저도 이번 교육 프로젝트를 진행 중, 태그 추출 기능을 구현하면서 정규표현식(Regular Expression)을 사용하였습니다. 이를 통해 좀 더 효율적인 문자열 Parsing이 가능했습니다. 아마 추가 과제로 Crawl 기능을 구현하셨던 분들이 계신다면, 이러한 정규표현식을 이용해 html 페이지에서 원하는 태그만 추출하는 방식으로 구현했을 것이라고 생각합니다. 아직 저도 정규표현식을 능숙하게 다루지는 못하지만 저와 같은 초보자 분들께 도움이 되었으면 좋겠습니다.
정규표현식이란?
정규표현식의 사전적인 의미로는 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어입니다. 주로 Programming Language나 Text Editor 등 에서 문자열의 검색과 치환을 위한 용도로 쓰이고 있습니다. 입력한 문자열에서 특정한 조건을 표현할 경우 일반적인 조건문으로는 다소 복잡할 수도 있지만, 정규표현식을 이용하면 매우 간단하게 표현 할 수 있습니다. 하지만 코드가 간단한 만큼 가독성이 떨어져서 표현식을 숙지하지 않으면 이해하기 힘들다는 단점도 가지고 있습니다. 실제로 제가 이번 프로젝트에서 태그 추출을 구현하면서 코딩하였던 일반적인 조건문의 상황과 정규표현식을 이용한 상황을 보시면 얼마나 차이가 나는지 바로 이해하실 수 있을 것입니다.
* 일반적인 조건문을 사용한 경우
//text에서 어떤 tag가 있는지 모조리 뽑아내서 list에 담아 반환
private List<String> tagPicker(String text) {
List<String> tagList = new ArrayList<String>();
int textLength = text.length();
for(int i=0; i<textLength; i++){
StringBuilder sb = new StringBuilder();
if(text.charAt(i)=='#'){
for(int j=i+1; j<textLength; j++){
if(text.charAt(j)=='\n' || text.charAt(j)==' ' || text.charAt(j)=='#' || text.charAt(j)=='%'
|| text.charAt(j)=='.' || text.charAt(j)==',' || j==(textLength-1) ){
if(sb.length()!=0){
if(j==(textLength-1))
sb.append(text.charAt(j));
tagList.add(sb.toString());
}
i=j-1;
break;
} else {
sb.append(text.charAt(j));
}
LOGGER.debug("TAG : " + sb.toString());
}
}
}
return tagList;
}
* 정규표현식을 사용한 경우
//text에서 어떤 tag가 있는지 모조리 뽑아내서 list에 담아 반환
private List<String> getTagListFromText(String text) {
List<String> tagList = new ArrayList<String>();
Pattern p = Pattern.compile("#([a-zA-Z0-9가-힣]+)");
Matcher mc = p.matcher(text);
while(mc.find()){
tagList.add(mc.group(1));
}
return tagList;
}
위의 그림만으로도 정규표현식이 얼마나 코드의 양을 줄일 수 있는지 느낄수 있을 것입니다. 또한 조건문의 경우 코딩을 했던 제가 아니고서는 이해하기가 상당히 어려울 수 있지만, 정규표현식의 경우 보다 쉽게 어떤 문자열의 추출을 원하는지 파악하실수 있을 것입니다. 그럼 지금부터 간단히 정규표현식의 표현 방법에 대해 알아보겠습니다.
정규표현식 표현방법
사실 저도 정규표현식에 대해 아는 것이 많지 않아 웹 검색을 해본 결과, 정규표현식은 표준인 POSIX의 정규표현식과 POSIX 정규표현식에서 확장된 Perl방식의 PCRE가 대표적이며, 이외에도 수많은 정규표현식이 존재하고 정규표현식 간에는 약간의 차이점이 있으나 거의 비슷하다고 합니다. 또한 정규표현식에서 사용하는 기호를 Meta문자라고 합니다. Meta문자는 표현식 내부에서 특정한 의미를 갖는 문자를 말하며, 공통적인 기본 Meta문자의 종류로는 다음 그림과 같습니다.
Meta 문자중에 독특한 성질을 지니고 있는 문자클래스’[ ]‘라는 문자가 있습니다. 문자클래스는 그 내부에 해당하는 문자열의 범위 중 한 문자만 선택한다는 의미이며, 문자클래스 내부에서는 Meta문자를 사용할 수 없거나 의미가 다르게 사용됩니다.
다음으로 POSIX에서만 사용하는 문자클래스가 있는데, 단축키처럼 편리하게 사용할 수 있다고 합니다. 하지만 저는 아직까지 이 문자클래스를 이용하는 기능을 구현한 적이 없기에 직접적인 사용은 해보지 못했지만, 여러분들 중 이를 이용할 필요가 있을때 사용해보시면 좋겠습니다. 대표적인 POSIX 문자클래스는 다음과 같으며 대괄호’[ ]‘ 가 붙어있는 모양 자체가 표현식이므로 실제로 문자클래스로 사용할 때에는 대괄호를 씌워서 사용해야만 정상적인 결과를 얻을 수 있습니다.
이밖에도 [:cntrl:] : 아스키 제어문자(0~31번, 127번), [:print:] : 출력 가능한 모든 문자, [:xdigit:] : 모든 16진수 숫자 등이 있습니다.
정규표현식을 이용하는 방법은 개발 언어에 따라서 사용방법이 다릅니다. 아마 대부분 프로젝트를 수행하시면서 JavaScript와 Java에서 조금씩 사용법의 차이를 느끼셨을 것입니다. 특히, 사용하는 JavaScript 버전이 1.1이하 버전일 경우에는 정규표현식을 사용할 수 없다고 하니 이점은 유의해서 이용하시길 바랍니다.
Flag의 종류
정규표현식에서는 다음과 같이 Flag라는 것을 이용하는 방법도 있습니다.
var regExp = /정규표현식/[Flag];
자주 사용하는 Flag는 밑의 3종류가 있으며 Flag를 사용을 하지 않을 수도 있습니다. 만약 Flag를 설정 하지 않을 경우에는 문자열 내에서 검색대상이 많더라도 한번만 찾고 끝나게 됩니다.
이 외에도 공백을 무시하고 주석을 허용하는 x, 개행문자도 포함해서 찾는 s 등 다양한 Flag들이 있습니다.
테스트 하기
위와 같이 정규표현식을 이용해 코드를 작성하였다면, 이 정규표현식이 정상적으로 작동하는지 테스트를 해보아야 합니다. 하지만 이를 직접 코드를 실행해가면서 테스트를 한다면, 아마 여러 번의 시행착오를 겪으며 테스트를 진행하셔야 할 것입니다. 저 같은 경우에는 이 정규표현식을 간단히 테스트 해볼 수 있는 사이트를 이용하여 상당히 많은 도움을 얻을 수 있었습니다.
위의 사이트는 자신이 만든 정규표현식을 직접 시뮬레이션 해볼 수 있기 때문에, 직접 이용해 보시면 많은 도움을 얻을 수 있을 것이라고 생각합니다.
다음은 표현식을 쉽게 이해할 수 있도록 도식화하여 보여주는 사이트입니다. 위의 표현방법 이미지들 모두가 이 사이트의 도식 결과를 이용하여 작성되었습니다. 이 사이트를 이용하면 복잡한 표현식을 해석하고 이해하기 쉬우실 겁니다. 이를 통해 프로젝트를 진행하면서 직접 구현한 표현식이 제대로 작성되는지 간단히 Test 해볼 수 있습니다.
마치며
사실 입사 전까지 정규표현식이라는 것이 있는지도 몰랐던 저였기에, 이번 프로젝트에서 정규표현식을 이용하며 정규표현식의 가치에 대해 굉장히 높게 생각하고 있습니다. 아마 앞으로 여러 웹 개발을 진행하면서 더더욱 정규표현식의 사용이 늘어날 것이라고 생각됩니다. 따라서 이번 계기를 통해 정규표현식의 개념에 대해 간단히라도 정리를 하시는 기회가 되어 좋았고, 자주 사용하는 표현식같은 경우에는 머리 속에 기억에 두면 좋겠다고 생각을 하였습니다. 이 글을 읽는 여러분들도 이번 기회를 통해 정규표현식에 대한 정리를 하게 되셨으면 좋겠습니다.