"아니 X발 무슨 다 경력직만 뽑으면 나 같은 신입은 어디서 경력은 쌓나?"
오래 전부터 유행했던 밈 중 하나죠.
코미디언 유병재씨는 'SNL 코리아'에서 경력직을 선호하는 기업이 증가함에 따라 신입들의 채용 기회가 줄어드는 현상을 비꼬며 코믹한 방식으로 이야기했습니다.
예전에는 이 밈이 마냥 웃기기만 했는데, 신입으로 구직 시장에 뛰어든 요즘에는 웃기면서도 슬프네요,,
암튼 각설하고, 실제 대면 상황에서는 그런 직설적인 욕설을 하는 사람들은 드물겠지만, 익명의 온라인 공간에서는 욕설을 쓰는 경우가 상당히 흔합니다. 그런 상황을 대비하기 위해 앱에서 욕설을 필터링하는 로직이 필요한 경우가 있겠죠.
그래서 이번 포스팅에서는 정규 표현식
을 이용하여 욕설·비속어를 필터링하는 로직에 관하여 다루어 보려고 합니다.
글 후반부에
정규 표현식
을 이용한닉네임 유효성 검사
예제와 관련 코드 그리고욕설·비속어
를 효과적으로식별
할 수 있는 Flutter 패키지를 소개해 드리고 있으니 참고하실 수 있기를 바랍니다.
본 글에서는 욕설·비속어 필터링 로직을 예제들 다루기 위해 일부 비속어가 사용된다는 점 양해 부탁드립니다.
"아니 시발 무슨 다 경력직만 뽑으면 나 같은 신입은 어디서 경력은 쌓나?"
위 비속어가 들어간 문장을 필터링해 본다고 가정해 봅시다. 어떻게 '시발'이라는 비속어가 문자열에 포함되어 있는지 확인할 수 있을까요?
const String comment = '아니 시발 무슨 다 경력직만 뽑으면 나 같은 신입은 어디서 경력은 쌓나?';
bool containsFWord = comment.contains('시발');
print(containsFWord); // true
1차원적으로 접근해 보면 dart에서 제공하는 continas
라는 String extension 메소드를 이용하여 '시발'라는 단어가 포함되어 있는지 확인해 볼 수 있습니다.
하지만 이런 경우는 어떻게 해야 할까요?
씌빨
무슨 다 경력직만 뽑으면 ...시2발
무슨 다 경럭직만 뽑으면 ...비속어가 변형된다면 고려해야 하는 경우의 수
가 너무 많아지기 때문에 contains
메소드만을 이용해서는 비속어를 감지하기 힘들 겁니다.
이럴 때 정규 표현식
을 사용하면 여러 경우의 비속어들을 필터링할 수 있습니다.
먼저 정규 표현식에 대한 개념을 가볍게 짚고 넘어갑시다. 사전적 정의는 아래와 같습니다.
특정한 패턴을 가진 문자열의 집합을 표현하기 위해 쓰이는 형식 언어
정규 표현식은 문자열에서 패턴을 검색하고 치환하는데 사용되는 하나의 도구
라고 이해할 수 있습니다.
즉, 정규 표현식을 이용하기 위해서는 문자열의 특정 패턴
을 잘 정의하면 됩니다.
시발, 씌발, 시2발
그럼, 이전에 예시로든 변형이 된 비속어들은 어떤 패턴
이 있는지 확인해 볼까요. 아래와 같이 정의 해볼 수 있을 것 같습니다.
이제 파악한 패턴들을 기반으로 Flutter에서 정규 표현식을 사용하는 데 도움을 주는 RegExp
클래스 사용하여 해당 규칙들을 정의해 주면 됩니다.
const String comment0 = '아니 시발 무슨 다 경력직만 뽑으면 ...';
const String comment1 = '아니 시2발 무슨 다 경력직만 뽑으면 ...';
const String comment2 = '아니 씌벌 무슨 다 경력직만 뽑으면 ...';
for (var comment in [comment0, comment1, comment2]) {
bool containsFWord = RegExp(
r'[시씨씌슈쓔쉬쉽쒸쓉]([0-9]*)[바발벌빠빡빨뻘파팔펄]').hasMatch(comment);
print(containsFWord); // true
}
이렇게 정규 표현식을 사용하면 여러 변형된 비속어들이 들어간 문자열을 간결하게 필터링할 수 있습니다.
const String comment0 = '아니 시발 무슨 다 경력직만 뽑으면 ...';
RegExp(r'[시씨씌슈쓔쉬쉽쒸쓉]([0-9]*)[바발벌빠빡빨뻘파팔펄]').hasMatch(comment);
정규 표현식에서 사용되는 기호를Meta문자
라고 말하는데요. 표현식에서 내부적으로 특정 의미를 가지는 문자를 말하는데 위에 정규식 어떤 의미를 가지고 있는 확인 해보시죠.
1. r
원시 문자열(raw string)의 시작
을 나타내는 표시입니다.
2. [시씨씌슈쓔쉬쉽쒸쓉] & [바발벌빠빡빨뻘파팔펄]
이 부분은 문자 집합(character set)을 나타냅니다. [...]
는 문자 집합
을 정의하며, 내부에 있는 문자 중 하나와 일치하는 것을 찾습니다. 즉 '시', '씨', '씌', '슈', '쓔', '쉬', '쉽', '쒸', '쓉' 중 어떤 문자와 일치하도록 패턴을 정의한다고 볼 수 있습니다.
3. ([0-9]*)
이 부분은 괄호로 둘러싸인 그룹(group)을 나타냅니다. ([0-9]*)는 0에서 9 사이의 숫자로 이루어진 부분 문자열을 찾으며, 이 부분은 그룹화됩니다.
' * '는 0회 이상의 반복을 의미하므로
, 숫자가 없을 수도 있고 여러 개일 수도 있음을 의미합니다..
시발
시2발
시12345발
즉 음절과 음절 사이에 숫자가 여러개 있거나 없어도 정규식으로 잡아낼 수 있게 됩니다.
4. hasMatch
주어진 입력 문자열에서 정규 표현식 패턴과 일치
하는 부분이 있는지 확인하는 메서드입니다. 이 메서드는 true 또는 false를 반환합니다.
정규 표현식은 여러 기능에서 사용될 수 있는데요. 닉네임이 유효성 검사 로직
을 예로 들어보겠습니다
일반적으로 닉네임을 설정할 때 권장되는 규칙이 있습니다.
그리고 이런 규칙들이 준수되었는지 닉네임 문자열을 식별하려고 할 때 정규식 표현
이 적극 사용됩니다.
import 'package:korean_profanity_filter/korean_profanity_filter.dart';
abstract class Regex {
Regex._();
// 공백 존재 여부
static bool hasSpaceOnString(String value) {
return RegExp(r'\s').hasMatch(value);
}
/// 적합한 문자 사용 여부
/// 한글, 알파벳, 숫자, 언더스코어(_), 하이픈(-)만 사용할 수 있음
static bool hasProperCharacter(String value) {
return !RegExp(r'^[a-zA-Z0-9ㄱ-ㅎ가-힣_-]+$').hasMatch(value.trim());
}
/// 비속어, 욕설 단어 포함 여부
static bool hasFWords(String value) {
return value.containsBadWords;
}
}
관련 유효성을 검증할 수 있는 코드입니다. 정규식 없이도 관련 기능을 만들 수 있습니다. 다만 정규식을 사용하지 않았다면 위에 예제 코드보다 몇 배는 많은 유효성 검사 코드가 필요했을 겁니다.
참고로 위 닉네임 유효성 검사 로직들은 닉네임 유효성 검사 예제 사이트 에 접속하셔서 테스트 해보실 수 있고, 관련 코드는 해당 깃허브 레포지토리 확인하실 수 있습니다.
또한 정규식을 기반으로 욕설·비속어를 식별하고 필터링할 수 있도록 도와주는 Korean Profanity Filter 패키지를 배포했습니다. 욕설·비속어 필터링 기능이 필요하신 분들에게 도움이 되었으면 좋겠네요 :)
요약하자면, 정규 표현식은 특정 형태를 보이는 문자열
을 효과적으로 식별
하기 위해서 사용된다고 볼 수 있습니다. 앞서 언급드린 것처럼 정규 표현식을 사용하지 않고도 패턴을 가지고 있는 문자열을 식별할 수 있지만, 정규식이 제공하는 간편한 편리성
때문에 많은 기능들에서 정규식이 사용되고 있는 것이죠.
그리고...
요즘 정말 개발자 채용 혹한기
시즌이 온 것 같습니다. 특히 신입으로서 취업의 장벽이 더 높아진 것 같다는 생각이 듭니다.
상황이 쉽지 않지만, 그래도 잘 헤쳐 나가야겠죠.
모든 취준생분들 화이팅입니다🔥