포스트

Issue - 여러 줄 문자열 검증하기

발생 이슈

요약: content 필드로 들어오는 값을 필터링하여 공백 문자만 들어있거나 값이 없으면 예외를 던지게 하고 싶었는데, 개행 문자를 포함한 여러 줄 글자가 입력 될 경우 예외 메세지를 응답 받게 되었다

Spring Boot 프로젝트로 회원 관리를 하는 게시판을 만들어보다가 테스트 게시글 생성에 대한 테스트 코드를 작성하면서 java 15부터 기본으로 제공 되는 text blocks 기능을 사용해보게 되었다.

text blocks로 작성한 content 필드를 api 요청을 통해 전송 받아 공백이 아닌 문자열이 1글자 이상 들어있다면 DB에 저장하고 그렇지 않다면 저장하지 않고 예외를 던지도록 구현했는데 테스트 하면서 text blocks로 개행 문자를 포함한 여러 줄 글자가 입력 될 경우 예외 메세지를 응답 받게 되었다 🫨


원인 분석

검증에는 아래 보이는 정규식을 사용하도록 했었는데 이 부분에 많은 문제가 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ContentValidator implements ConstraintValidator<Content, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {

        if (value == null)
            return false;

        String regex = "^(?! )(?=\\S)(.{1,})(?<=\\S)(?<! )$";
        Matcher patternMatcher = Pattern.compile(regex).matcher(value);

        return patternMatcher.matches();
    }

}


ChatGPT를 통해 사용된 정규식을 분석해보면 다음과 같다.

  1. ^ : 문자열 시작
  2. (?! ): 공백으로 시작하지 않는 문자열을 의미하는 전방 부정 탐색(negative lookahead)로, 첫 번째 문자가 공백이 아니어야 한다.
  3. (?=\\S): 공백이 아닌 문자가 있어야 함을 의미하는 전방 부정 탐색(negative lookahead)로, 이 위치 이후에 공백이 아닌 문자가 존재해야 한다.
  4. (.{1,}): 하나 이상의 임의의 문자(줄바꿈 문자 제외)를 의미
  5. (?<=\\S): 공백이 아닌 문자로 끝나야 한다는 의미의 후방 탐색(positive lookbehind)로, 이 위치 앞에 공백이 아닌 문자가 있어야 한다.
  6. (?<! ): 문자열의 끝이 공백이 아니어야 한다는 의미의 후방 부정 탐색(negative lookbehind)로, 마지막 문자가 공백이 아니어야 한다.
  7. $: 문자열의 끝을 의미


이제와 살펴보니 title 필드를 검사 할 때 사용하던 정규식을 그대로 사용해서 content 필드에는 적용되지 않았어야 할 부분이 몇 가지 눈에 띄었다.

2번, 3번, 4번, 5번, 6번 ..?!

content 필드니까 공백만 있거나 값이 없는 상태만 아니면 입력 가능해야 하는데 공백 문자에 대한 제약 사항이 너무 많았고 개행 문자를 생각하지 못한 부분이 문제였다.


해결

조건을 변경해서 정규식으로 줄 바꿈 문자(개행 문자)를 포함하도록 하려면 다음의 코드를 사용해야 한다.

변경한 조건 : 공백이 아닌 문자가 1개 이상 포함된 여러 줄 문자열을 검증

1
2
3
4
5
6
7
if (value == null)  
    return false;  
  
String regex = "(?s).*\\S.*";  
Matcher patternMatcher = Pattern.compile(regex).matcher(value);  
  
return patternMatcher.matches();

정규식 분석

  1. (?s): DOTALL 모드 플래그로, .개행 문자를 포함한 모든 문자를 매칭하도록 설정
  2. .*: 모든 문자를 0번 이상 매칭
  3. \\S: 공백이 아닌 문자를 매칭
  4. \\S.*: 공백이 아닌 문자 뒤에 나올 수 있는 모든 문자를 0번 이상 매칭


이와 같은 조건을 가지는 content 필드는 정규식을 사용하지 않아도 java에서 제공하는 String 메서드로 간단하게 검증할 수 있다.

1
return value != null && !value.trim().isEmpty(); 




참고한 사이트

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.