제타건담
8k
2015-10-22 18:42:04
8
11561

정규표현식에 대해 질문이 있습니다..


안녕하세요..

제가 정규표현식에 많이 약한 관계로..ㅠㅠ..이렇게 질문을 올려봅니다..

비밀번호 정규표현식을 만들고 있는데..

조건은 다음과 같습니다..


1. 글자는 10~16자사이어야 한다

2. 대소문자 가리지 않고 영문자, 숫자, 특수문자를 혼합해야 한다

3. 특수문자는 허용된 특수문자(!@#$%^&*)만 입력받아야 한다..


구글링해보면 여러가지 나오는데..이 부분에 있어 약점이 있더군요..

예를 들어 비밀번호를 abcd12456!@ 이렇게 입력하면 위의 3가지 조건에 만족하니까 문제가 없지만..

허용된 특수문자가 아닌 다른 특수문자..예를 들면 _ 이런거를..

abcd123456!@_ 이렇게 입력하면 _는 입력받으면 안되기 때문에 거절이 되어야 하는데..

패턴 매치이다보니 _ 앞에 abcd123456!@는 만족하게 되니까..

테스트 클래스를 만들어서 테스트를 진행해보면..정규표현식 조건에 만족이 안되어야 한다고 생각하지만..

이게 통과가 되더군요..ㅠㅠ..

그래서 허용된 특수문자와 그렇지 않은 특수문자가 같이 입력되었을 경우 조건에 만족하지 못한다는 의미의 정규식을 만들려면 어떻게 해야 할까요..?

현재는 String 클래스의 matches 메소드를 이용해서 확인하고 있는데..

아예 다른 메소드를 사용해야 하나요..?

이게 나중에 Server Side의 Form Validation에서도 사용할 것이어서..

@Pattern 어노테이션에서도 사용할려고 합니다..


그러면 가르침을 주심 고맙겠습니다..

2
  • 답변 8

  • YD
    51
    2015-10-22 19:02:40
    ^[a-zA-Z0-9!@#$%^&*]{10,16}$

    이렇게 하시면 될 거 같습니다.

  • 제타건담
    8k
    2015-10-23 15:18:51

    YD님 답변 고맙습니다..

    근데 테스트를 진행해봤는데..되질 않았어요..

    제가 테스트 클래스로 만들어서 사용한 테스트 메소드 소스를 보여드리겠습니다..

    @Test
    public void 정규표현식_영어특수문자_테스트(){
        // 특수문자는 !@#$%^&*로 한정한다
        String regex2 = "^[a-zA-Z0-9!@#$%^&*]{10,16}$";
    		
        assertFalse("abcd".matches(regex2));		// 길이가 안됨
        assertFalse("abcd12345678".matches(regex2));	// 영어 + 숫자만 조합이어서 안됨
        assertFalse("12345678!@#$%".matches(regex2));	// 숫자 + 특수문자 조합이어서 안됨
        assertFalse("abcd12345678!_".matches(regex2));	// 영어 + 숫자 + 특수문자 조합이긴 하나 허용된 특수문자가 아니어서 안됨
        assertTrue("abcd12345678!".matches(regex2));	// 모두 만족
    		
    }

    이게 제가 테스트 클래스를 만들어서 사용했던 테스트 메소드인데요..

    첫번째 "abcd"를 대상으로 한 것은 false를 return 하기 때문에 성공합니다..

    근데 두번째의 경우는 특수문자가 들어가지 않았는데도 matches 함수가 true를 return 하기 때문에 assertFalse를 통해서 false를 return 할꺼라고 예상했던 테스트가 실패하게 됩니다..

    세번째의 경우도 테스트가 실패하고 네번째도 마찬가지입니다..

    [ ] 안에 허용되는 글자가 or 개념으로 된다는거죠..숫자가 안들어가도 문자만 들어가면 된다거나 하는 식의..입력글자의 범위를 지정하는 것이지 and 개념이 되지는 않더라구요..

    제가 잘못 테스트한걸까요..?

    답변 주심 고맙겠습니다..

  • YD
    51
    2015-10-23 17:04:35
    ^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{10,16}$

    and 개념이라면 이걸로 한 번 테스트 해보세요.

  • 제타건담
    8k
    2015-10-23 17:18:44

    오..됩니다..고맙습니다..

    근데 정말 염치없습니다만..지금 주신 정규표현식 문자열에 대해 설명 좀 부탁드리면 안될까요..?

    범위 지정인 []나 길이를 정의하는 {}는 알겠는데..

    나머지 부분을 잘 모르겠습니다..ㅠㅠ..

  • YD
    51
    2015-10-24 04:49:57

    정규 표현식 문법부터 설명드리겠습니다.


    ^ $      : 시작과 종료

    [범위]  : 문자 범위 (범위에 해당하는 문자 하나)

    .          : 문자 (공백, 숫자, 영문, 특문 등 의 문자 하나)

    (?=  ) : 긍정형 전방탐색 (positive lookahead) ; 일치하는 문자가 있으면 값이 아닌 인덱스를 반환합니다.


    문자 길이에는 몇 가지 종류가 있습니다.

           ?  : 0 또는 1회

           *   : 0회 이상

           +  : 1회 이상

         {n}  : n회  

       {n, }  : n회 이상

    {n, m} : n회 이상, m회 이하


    표현식에 사용된 문법은 생각보다 적네요.

    댓글을 이어서 달겠습니다.

  • YD
    51
    2015-10-24 07:00:22

    편의상 인덱스를 [i]로 표시하겠습니다. 


    ^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{10,16}$

    전방탐색 (?= )은  위에서 설명했듯이 일치하는 값이 아닌 인덱스[i]를 반환합니다.


    ABCDE에 전방탐색(?= D)을 사용하면 D의 전방.

    즉, ABC[i]DE 의 위치만 반환한다고 생각하시면 됩니다. 

    정규 표현식반환
    BC(?= D)
    ABCDE
    (?= D)DE
    ABCDE
    ABC(?= D)DE
    ABCDE
    ..(?= D)
    ABCDE (.은 문자 하나)
    .*(?= D)
    ABCDE (.*은 문자 0회 이상)

    그렇다면 777ABC777은 정규식 (?=[a-zA-Z])(?=[0-9])에 매칭될까요?

    매칭될 거 같지만 매칭되지 않습니다.


    왜 그런지 더 간단한 조건으로 테스트를 해보면

    정규 표현식반환

    (?=D).*(?=[0-9])

    2ABC[i]DE4
    (?=D).*(?=[0-9])[0-9]
    2ABCDE[k]4

    첫 번째 전방 탐색이 다른 전방 탐색에 영향을 줘서 2가 아닌 4에 매칭이 되는 것을 볼 수 있습니다.

    (일단 매칭 여부를 떠나서 두 번째 전방탐색이 인덱스 0부터 시작하지 않는 것은 문제가 있어 보입니다.)


    그럼 어떻게 첫 번째 탐색이 0을 반환하게 할 수 있을까요?

    ABCDE의 예를 다시 들어 D의 유무만 확인하면 되는 경우,

    애초에 탐색을 (?= ABCD)로 하면 ABCD매칭 후 [i]ABCDE를 반환하지 않을까요?

    정규 표현식반환
    (?= D)ABC[i]DE
    (?= .D)AB[i]CDE
    (?= ..D)A[i]BCDE
    (?= ...D)[i]ABCDE
    (?= .............................D) 오~!???!!
    (?=.*D)[i]ABCDE

    앞에 문자가 몇 개 올지 모르니 우아하게 .*을 사용하면 됩니다.


    덧 붙여서 .+(1개 이상)를 사용할 때 문제점은..

    정규 표현식반환
    (?=.+A)[i]A123A
    (?=.+A)

    A12345 (A앞에 문자 한개 이상을 조건으로 줬는데

                   문자가 없어서 매칭 x, 인덱스 반환 x)


    ^
    (?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{10,16}$

    그럼 이게 도대체 무슨 의미가 있는가 하면

    (?=.*[a-zA-Z]) : 앞에 어떤문자(.)가 몇 개(*) 오든 [a-zA-Z]가 나오면 인덱스 0을 반환, 

    (?=.*[0-9]) : 인덱스 0부터 시작해서 앞에 어떤문자(.)가 몇 개(*) 오든 [0-9]가 나오면 인덱스 0을 반환,

    (?=.*[!@#$%^&*]) : 마찬가지로 검사 후 인덱스 0 반환.

    통과 못 하면 매칭 실패.


    결국 (?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])는 

    영문, 숫자, 특수문자가 하나 이상 포함되어 있음을 확인하는 구문입니다.


    응용하면 이런 것도 만들 수 있겠죠.

    ^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9!@#$%^&*]{10,16}$

    : 특수문자도 허용하지만 필수는 아님, 영문과 숫자는 필수

  • YD
    51
    2015-10-24 07:59:50

    (?=.*[조건])으로 문자 포함 여부를 확인했으니 이제 .{10,16}을 붙이면 될까요?


    (?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*]).{10,16}는 

    영문과 숫자 그리고 특문이 각각 하나 이상 포함된 문자 10~16개를 의미합니다.

    즉, "af가d  나00@@다@"도 검사에 통과해버리죠.

    공백 및 한글도 문자(.)이고 10~16개 범위기 때문입니다.


    그래서 (?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{10,16}를 통해서

    전부 영문, 숫자, 특문으로 구성되어 있는지 체크합니다.


    그리고 마무리

    "abcd123456!@_ 이렇게 입력하면 _는 입력받으면 안되기 때문에 거절이 되어야 하는데.."

    이 부분을 해결하기 위에 ^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{10,16}$

    시작과 끝을 지정해줍니다.



    제가 실력이 후뎝해서 설명이 장황하네요 ( _  _  );;


    개인적으로 추천하는 사이트는 아래와 같습니다.

    http://regexper.com/ : 정규식을 시각화해서 보여줍니다.

    http://regexr.com/ : 간단한 레퍼런스도 제공하고 바로 테스트도 가능합니다.

  • 제타건담
    8k
    2015-10-26 15:53:01

    오..설명 정말 고맙습니다..궁금했던 부분이 많이 해소가 되었네요..

    제가 정말 이상하게 정규표현식은 아무리 공부를 해도 많이 약해서..ㅠㅠ..

    나머지 이해해야 하는 부분은 저의 몫이니 열심히 보겠습니다..

    다시한번 설명 고맙습니다..

  • 로그인을 하시면 답변을 등록할 수 있습니다.