본문 바로가기
프로젝트/Recipository

[Dev] 22.09.26 signinform.html (feat. Regular Expression, FormData)

by 규글 2022. 9. 26.

signinform.html 작업 중 일어난 일들

 

Regular Expression (정규 표현식)

 회원 가입을 위한 정보 입력에 대한 validation을 위해 사용한 것이다. 문제는 비밀 번호에 대한 validation에서 시작되었다. 원하는 방식은 영문과 숫자를 조합해서 8 ~ 20 자로 작성하도록 하는 것이었다.

 

var regex = /^[a-zA-Z0-9]{8,20}$/;

 처음에는 단순하게 이렇게 작성했던 것으로 기억한다. 다른 여러 오류들을 마주하면서 테스트를 진행하던 도중, 이런 방식은 영문으로만 입력하더라도 validation을 통과할 수 있다.

 

var regex = /^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{8,20}$/;

 이것이 정한 regular expression이다. 이는 검색을 통해 발견한 내용인데, 사용하기에 앞서 이들이 어떻게 읽히길래 영문과 숫자 조합에 대한 validation이 가능한 것인지를 알아야 했다. 문제는 '?=' 로 시작하는 부분이었다.

 

 '?=' 의 정체는 'Positive Lookahead (전방 탐색)' 이라는 것이었다. 문자열이 원하는 형식인지를 파악하기 위해서 regular expression을 사용한다. 전방 탐색, 혹은 후방 탐색은 이 expression에 대해서 원하는 pattern이 있는지 앞에서부터, 혹은 뒤에서부터 확인하는 친구이다. 간단히 테스트 할 수 있는 사이트[각주:1] 에서는 'Matches a group after the main expression without including it in the result.', 결과에는 포함하지 않으면서 main expression의 뒤에서 group을 matches 한다고 설명한다. 간단한 예를 들어보겠다.

 

/^X(?=Y)/
/(?<=Y)X/

 첫 번째 친구를 해석해보면, 가장 앞은 X로 시작하고 그 바로 뒤에 Y가 나오는 문자열을 찾고자 하는 것이다. 두 번째 친구를 해석해보면 일단 X가 나오는데, 그 앞에는 Y가 나오는 문자열을 찾고자 하는 것이다.

 

 다시 마지막으로 채택했던 regular expression을 보자. 전방 탐색 부분이 앞에 나와 있다. 이에 관해 여러 경우의 수를 방금 전 언급한 테스트 사이트에서 작성해봤지만, 뒤쪽에 작성하면 올바른 결과를 얻어낼 수 없었다. 해석해보면 다음과 같다.

 

 "맨 앞쪽에서부터 어떤 문자열이 몇 글자가 나오든 결국 영문이 등장하는지 확인하고, 다시 맨 앞쪽에서부터 어떤 문자열이 몇 글자가 나오든 결국 숫자가 등장하는지 확인한다. 그리고 영문이나 숫자가 최소 8번 최대 20번 반복되는 문자열을 찾아라."

 

 하지만  왜 뒤에 작성할 수 없는지는 알 수 없었다.

 

 이 전방 탐색의 경우 index를 반환하는 것이라고 하는데, 이 내용을 더 찾을 수는 없었다. [각주:2]

 

 정규 표현식을 해석하기 위한 일종의 모식도를 살펴볼 수 있는 페이지도 있어서 기록해둔다.[각주:3]

 

 

Form data 전송

 사실 국비 교육을 받으면서 한 프로젝트는 강사분이 제공해준 js file 하나를 사용하고 있었다. 해당 file는 ajax request를 위해 사용하는 것이었는데, 프로젝트의 refactoring을 하면서도 그렇고 이번에도 해당 js file을 사용하지 않으려고 했다. 그리고 위기에 봉착하고 말았다.

 위기는 간단하다. Form element에 입력한 data를 controller로 보내야 했다.

 

    const queryString=new URLSearchParams(new FormData(form)).toString();

    promise=fetch(url,{
        method:"POST",
        headers:{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8"},
        body:queryString
    });

 제공받았던 js file에서는 위와 같은 방식을 사용하고 있었다. 하지만 해당 방식은 이제 잘 사용하지 않는 방식이라고 한다.[각주:4] 필자는 headers의 content-type은 그대로 작성한 채로 URLSearchParams를 사용하지 않으면서 단순히 FormData를 전송하려고 시도했고, 역시 계속해서 controller에서 data를 null로 받는 상황을 마주했다.

 

 이를 해결하기 위해 검색을 계속했다. 많은 곳에서는 jQuery를 사용하여 FormData를 serialize 해서 data를 전송하고 있었지만, 필자는 jQuery를 사용하고 싶지 않아서 다른 방식을 계속 찾아보려고 했다.

 이외에도 jQuery를 사용하고 있기는 하나 content-type을 multipart/form-data로 하여 controller 쪽에서 MultipartResolver를 사용하는 방식을 소개하는 곳도 있었다.[각주:5] [각주:6] 사실 필자는 content-type이 multipart/form-data 인 경우를 작성해본 적이 있는데, 그것은 바로 form을 통해 file 자체를 전송할 경우였다. 그래서 이를 사용하고 싶지 않았고, 다음 게시글에서 사용하지 않아도 될 것이라는 것도 확신하게 되었다. [각주:7]

 

 필자는 그래서 다음의 두 가지 방식을 시도하기로 했고, 결국 두 가지 방식 모두 성공했다.

  1. 어떻게든 FormData 를 보낸다.
  2. FormData를 JSON data로 바꿔서 보낸다.
            var promise = fetch(url, {
                method: "POST",
                body: formData
            });

 첫 번째 방식은 의외로 어이없게 해결되었다. FormData를 전송하면서 headers에 content-type 정보를 지정하지 않았더니 바로 해결된 것이다. 허탈했다. FormData를 보낼 때는 browser가 header 를 자동으로 설정해주기때문에 따로 지정하지 않아도 된다는 언급을 보았다.[각주:8]

 

    // form data to JSON
    function formToJSON(formData){
        var obj = {};
        var data = formData.entries();
        formData.forEach((value, key) => {
           obj[key] = value;
        });

        return obj;
    }

 두 번째 방식은 다음 블로그를 참고했다.[각주:9] FormData를 forEach로 들어가는 매개변수를 출력해보면 순서대로 value와 key 임을 알 수 있었다. 이를 이용해서 FormData의 값을 object 형식으로 만들어서 JSON.stringify에 넣을 수 있도록 했다.

 

 

Signin Security

 해당 내용은 마주한 문제는 아니고, 단지 기록이 필요해서 작성한다.

 사실 지금 결제한 강의를 들은지 시간이 조금 지났다. JPA에 대해 막 안 시점으로부터 시간도 조금 지났다. 강의 내용에는 Spring Security에 대한 내용이 들어있는데, 지금은 지난 프로젝트에서 사용했던 BcyrptPasswordEncoder를 사용하려고 했다.

 

	  	<!-- Spring Security 관련 라이브러리 -->
	  	<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-web</artifactId>
		    <version>4.0.0.RELEASE</version>
		</dependency>
        
        (Spring pom.xml)
        -----------------------------------------------------
        (SpringBoot build.gradle)
        
	// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
	implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.7.4'

 때문에 Spring project에서 pom.xml에 작성했던 spring-security-web 대신 spring-boot-starter-security를 dependency로 추가했다. 그리고 실행했을 때 자동으로 로그인 화면이 구현되어 출력되는 상황이 발생했다. 이를 찾아보니 서버를 실행하고 browser를 열면 기본적으로 출력하는 프롬프트라고 한다.[각주:10] 따라서 이러한 기본 설정들을 disable 시켜주어야 한다는 정보를 얻었다. 필자의 경우는 그냥 BCryptPasswordEncoder를 new 로 생성해서 사용했는데, 다른 사람들은 @Bean annotation으로 PasswordEncoder interface를 bean으로 등록해서 사용하는 경우를 여럿 발견할 수 있었다. [각주:11] [각주:12] 일단 기록해두고, 이에 대해서는 추후 자세히 알아보도록 하겠다.

 

Reference

댓글