❓ 만약 배달서비스 리뷰에 “닭볶음탕 맛있게 잘먹었어요.“ 란 리뷰를 남겼을 때
금지어 규칙에 걸렸다면 꽤 황당하지 않을 수 없다.
”음탕“이란 단어가 금지어이기 때문이다.
그렇다면 금지어 체크 기준이 어떻길래 “음탕”이란 단어를 필터링 할 수 있는걸까?
금지어 체크하는 방법
react, react-hook-form, yup을 사용하여 금지어 체크를 구현해보았다.
객체 스키마 유효성 검사 라이브러리로 데이터 유효성 검증을 간편하게 수행할 수 있다.
react-hook-form 공식문서에서도 yup과 조합하여 유효성을 검증하는 예제가 있다.
난 금지어
란 단어를 금지하고 싶다.
금지어를 금지하기 위해선 “금지어”
뿐만 아니라 “금 지 어”
, “금지 어”
, “금 지어”
같은 띄어쓰기한 단어도 마찬가지로 금지해야한다.
우선 금지하고 싶은 모든 단어들의 경우를 배열에 담는다.
// 모조리 배열에 담아본다.
const PROHIBITED_WORDS = ["금지어", "금 지 어", "금지 어", "금 지어"];
yup의 addMethod란 메서드로 금지어 체크 custom method를 작성한다.
import * as yup from "yup";
// 정규 표현식에서 사용하기 위해 문자열의 특수 문자를 이스케이프하는 함수
const escapeRegExp = (string: string) => {
// 정규 표현식의 특수 문자 이스케이프
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
// 금지된 단어에 대한 정규 표현식 패턴을 생성하는 함수
const createPattern = (word: string) => {
// 단어를 문자로 분리하고 각 문자를 이스케이프
const chars = word.split("").map((c) => escapeRegExp(c));
// 문자들을 비단어 문자(\W)가 여러 개 있을 수 있는 패턴으로 조합
const patternString = chars.join("[\\W_]*");
// 생성된 패턴으로 정규 표현식 객체를 생성하고 반환 (대소문자 구분 없음)
return new RegExp(patternString, "i");
};
// yup.string에 사용자 정의 검증 메서드를 추가하는 함수
export const addProhibitedWordValidation = (prohibitedWords: string[]) => {
// 각 금지된 단어에 대한 정규 표현식 패턴 배열 생성
const patterns = prohibitedWords.map(createPattern);
// yup.string에 'validateAgainstProhibitedWords'라는 사용자 정의 메서드 추가
yup.addMethod(
yup.string,
"validateAgainstProhibitedWords",
// 사용자 정의 검증을 위한 테스트 정의
function (message: string) {
return this.test("validate-against-prohibited-words", message, function (value: string | undefined) {
const { path, createError } = this;
// 값이 undefined일 경우 검증을 통과시킴
if (!value) return true;
// 값이 금지된 단어 패턴 중 하나와 일치하는지 확인
const containsProhibitedWord = patterns.some((pattern) => pattern.test(value));
// 금지된 단어가 없으면 true를 반환하고, 있으면 오류 생성
return !containsProhibitedWord || createError({ path, message });
});
}
);
};
addProhibitedWordValidation 메소드는 string[]
타입에 해당하는 모든 변수들의 유효성을 체크할 수 있는 함수이다.
만약 서비스마다 금지어 항목이 다르다면 각 서비스에 해당하는 배열을 선언 후 동일한 addProhibitedWordValidation 메소드를 사용하면 된다.
사용하고자하는 특정 컴포넌트 상위에 아래 함수에 작성한 금지어 변수를 매개변수로 받아 실행시킨다.
// component.tsx
addProhibitedWordValidation(PROHIBITED_WORDS);
const Component = () => {
//...
};
만약 특정 form에서 금지어를 체크해야한다면 아래와 같이 yup.object를 이용하여 다른 항목들과 함께 유효성 검사를 할 수 있다.
// component.tsx
// yup으로 금지어 체크시 아래 함수 상위 실행. 사용자 정의 메서드 추가의 역할.
addProhibitedWordValidation(PROHIBITED_WORDS);
const formSchema = yup.object({
content: yup
.string()
.required()
.validateAgainstProhibitedWords("Caution: The entered content contains prohibited language."),
});
const Component = () => {
const { register } = useForm({
defaultValues: { content: "" },
resolver: yupResolver(formSchema),
});
const handleSubmitContent: SubmitHandler<{ content: string }> = (data) => {
// post request
};
return (
<form onSubmit={handleSubmit(handleSubmitContent)}>
<textarea {...register("content")} placeholder="Click here to write a review" />
<button type="submit">submit</button>
</form>
);
};
별첨
타입스크립트 프로젝트라면 폴더 구조 내 타입을 정의하는 파일에 커스텀 타입을 추가한다.
import "yup";
declare module "yup" {
interface StringSchema {
// 금지어 체크를 위해 사용자 정의 메소드 사용.
validateAgainstProhibitedWords(message: string): StringSchema;
}
}
정리
- yup으로 커스텀 메소드를 만든다.
- 금지하고자 하는 단어들을
string[]
타입의 형태로 변수에 담는다. - 금지어 변수와 함수를 조합하여 특정 컴포넌트에서 사용한다.