국비교육(22-23)

47일차(1)/React(12) : jsp 웹페이지 React 로 재작성하기 (회원가입 폼)

서리/Seori 2022. 12. 13. 22:40

47일차(1)/React(12) : jsp 웹페이지 React 로 재작성하기 (회원가입 폼)

 

- signup_form.jsp React App으로 작성해보기

 

- App.js 전체 코드

import React from "react";
//class 속성값을 편하게 제어하기 위한 모듈(install 후 사용할 수 있다
import cn from 'classnames';

function App(){

    //입력한 id, pwd, email 유효성여부를 상태값으로 관리(object)
    const [valid, setValid] = React.useState({
        isIdValid:false,
        isPwdValid:false,
        isEmailValid:false
    });

    //현재 입력된 id, pwd, email을 상태값으로 관리
    const [input, setInput] = React.useState({
        id:"",
        pwd:"",
        pwd2:"",
        email:""
    });

    //입력창이 한번이라도 더렵혀졌는지 여부를 상태값으로 관리
    const [dirty, setDirty] = React.useState({
        isIdDirty:false,
        isPwdDirty:false,
        isEmailDirty:false
    });

    //id가 변경되었을 때 호출하는 함수
    const onIdChange=(e)=>{
        //현재 입력한 아이디를 읽어와서
        let inputId=e.target.value;
        //아이디를 한번이라도 입력하면 아이디가 더럽혀 졌는지 여부를 true 로 바꿔준다.
        setDirty({
            ...dirty,
            isIdDirty:true
        });
        //아이디를 검증할 정규표현식
        const reg=/^[a-z].{4,9}$/;
         //입력한 아이디가 정규표현식과 매칭이 되는지(통과 되는지) 확인한다. 
        const isMatch=reg.test(inputId);
        //만일 매칭되지 않는다면
        if(!isMatch){
            //아이디 유효성 여부를 false로 바꾼다.
            setValid({
                ...valid,
                isIdValid:false
            })
            return; //함수를 여기서 끝내라            
        }
        //2. 외부 tomcat 서버에 페이지 전환없이 전송을 하고 응답을 받는다.
        fetch("http://localhost:8888/Step04_Final/users/checkid.jsp?inputId="+inputId)
        .then(function(response){
            return response.json();
        })
        .then(function(data){
            //3. 사용가능한지 여부에 따라 아이디 입력란에 is-valid or is-invalid 클래스를 적절히 추가, 제거를 한다.
            console.log(data);
            if(data.isExist){   
                setValid({
                    ...valid,
                    isIdValid:false
                });
            }else{
                setValid({
                    ...valid,
                    isIdValid:true
                });
            }
        });        
    }

    //비밀번호를 입력했을 때 실행할 함수 등록
    const onPwdChange=(e)=>{
        //입력한 비밀번호를 읽어와서
        let inputPwd=e.target.value;
        //pwd의 상태값에 반영하고
        setInput({
            ...input,
            pwd: inputPwd
        });
        //더렵혀 졌는지 여부를 true로 바꿔준다.
        setDirty({
            ...dirty,
            isPwdDirty:true
        })
        //비밀번호를 검증할 정규 표현식
        const reg=/[\W]/; 
        //만일 정규표현식 검증을 통과하지 못했다면
        const isPwdValid= !reg.test(inputPwd) ? false : true ;
        setValid({...valid, isPwdValid});        
        //만일 비밀번호 입력란과 확인란이 다르다면
        if(inputPwd != input.pwd2){
            setValid({
                ...valid,
                isPwdValid:false
            }) 
        }else{ //같다면	
            setValid({
                ...valid,	
                isPwdValid:true
            })
        }		
    }

    const onPwdChange2=(e)=>{
        //입력한 비밀번호를 읽어와서
        let inputPwd=e.target.value;
        //pwd2의 상태값에 반영하고
        setInput({
            ...input,
            pwd2: inputPwd
        });
        setDirty({
            ...dirty,
            isPwdDirty:true
        })        
        //비밀번호를 검증할 정규 표현식
        const reg=/[\W]/; 
        //만일 정규표현식 검증을 통과하지 못했다면
        const isPwdValid= !reg.test(inputPwd) ? false : true ;
        setValid({...valid, isPwdValid});
        
        //만일 비밀번호 입력란과 확인란이 다르다면
        if(inputPwd != input.pwd){
            setValid({
                ...valid,
                isPwdValid:false
            }) 
        }else{ //같다면	
            setValid({
                ...valid,	
                isPwdValid:true
            })
        }		
    }

    //이메일을 입력했을 때 호출되는 함수
    const onEmailChange = (e)=>{
        let inputEmail=e.target.value;
        setDirty({
            ...dirty,
            isEmailDirty:true
        })   
        //이메일을 검증할 정규 표현식
        const reg=/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;									
        //만일 입력된 이메일이 정규표현식 검증을 통과하지 못했다면        
        const isEmailValid = !reg.test(inputEmail) ? false : true;
        setValid({...valid, isEmailValid});
    }    

    //폼에 handleSubmit 이벤트가 일어나면 호출되는 함수
    const handleSubmit=(e)=>{
        //여기서 e는 react가 넣어주는 event 객체이기 때문에 원래 event 객체와는 좀 다르다.
        
        //폼 유효성 여부를 알아내서
        let isFormValid = valid.isIdValid && valid.isPwdValid && valid.isEmailValid;
        //만일 폼이 유효하지 않으면
        if(!isFormValid){
            //기본 이벤트(원래는 폼이 전송되는 것)을 막아버린다.
            e.preventDefault();
        }
    }

    return(
        <div className="container">
        <h1>회원 가입 폼입니다.</h1>
        <form action="signup.jsp" method="post" onSubmit={handleSubmit}>
          <div>
            <label htmlFor="id" className="control-label">아이디</label>
            {/* 
                className={`form-control ${valid.isIdValid ? 'is-valid' : 'is-invalid'} `}
                className={cn('form-control', {'is-valid':valid.isIdValid, 'is-invalid':!valid.isIdValid && dirty.isIdDirty} }
            */}
            <input onChange={onIdChange} type="text" className={cn('form-control', {'is-valid':valid.isIdValid, 'is-invalid':!valid.isIdValid && dirty.isIdDirty} )} name="id" id="id" />
            <small className="form-text text-muted">영문자 소문자로 시작하고 5글자~10글자 이내로 입력하세요.</small>
            <div className="valid-feedback">사용할 수 있습니다.</div>
			<div className="invalid-feedback">사용할 수 없는 아이디입니다.</div>
          </div>
          <div>
            <label htmlFor="pwd" className="control-label">비밀번호</label>
            <input onChange={onPwdChange} type="password" className={cn('form-control', {'is-valid':valid.isPwdValid, 'is-invalid':!valid.isPwdValid && dirty.isPwdDirty} )} name="pwd" id="pwd" />            
            <small className="form-text text-muted">특수 문자를 하나 이상 조합하세요.</small>
				<div className="invalid-feedback">비밀번호를 확인하세요.</div>
          </div>
          <div>
            <label htmlFor="pwd2" className="control-label">비밀번호 확인</label>
            <input onChange={onPwdChange2} type="password" className={cn('form-control', {'is-valid':valid.isPwdValid, 'is-invalid':!valid.isPwdValid && dirty.isPwdDirty} )} name="pwd2" id="pwd2" />
          </div>
          <div>
            <label htmlFor="email" className="control-label">이메일</label>
            <input onChange={onEmailChange} type="text" className={cn('form-control', {'is-valid':valid.isEmailValid, 'is-invalid':!valid.isEmailValid && dirty.isEmailDirty} )} name="email" id="email" />
            <div className="invalid-feedback">이메일 형식에 맞게 입력하세요.</div>
          </div>
          <button className="btn btn-outline-primary" type="submit">회원가입</button>          
        </form>         
        </div>
    );
}

export default App;

 

 

- 두개는 같은 것이다. 함수를 만들어 넣으나 상수를 만들어 넣으나 상관 없음.

 

 

- public폴더의 index.html에 bootstrap 추가!

 

- Tomcat 서버도 켜져 있어야 한다 (아이디 중복검사를 위해, DB에 값을 보내고 응답받아야 하므로)

 

- checkId 에서 inputId 값을 받아서 json으로 응답하고 있음

 

- node에서 tomcat으로 값을 보내서 응답받을 것이다.

(원래는 불가능하지만, 어제 cross origin 설정을 해놔서 가능한 것)

 

- react에서 이 서버로 응답 요청을 보낼것이다.

 

- find/replace로 편하게 수정하기

- class→className, for→htmlFor

- Whole word(전체 단어) 옵션을 선택하면 오류 없이 수정된다.

 

- 이전에 Vue를 사용해서 작성한 파일을 보면, data로 10개의 값을 관리하고 있다.

- React에서도 이 10가지를 상태값으로 관리하기!

- object로 관리할 것인지, 하나하나 따로 관리할 것인지 정하면 된다.

 

- 3개의 object 묶음으로 관리하기로!

 

- 3개의 object를 관리해준다.

 

setValid({
	...valid, //기존 값을 펼쳐놓고
	isIdValid: "T/F" //필요한 값만 수정하는 것으로!!
});

 

 

- 위와 같이 object의 기존 내용을 펼쳐놓고 원하는 상태값만 수정하면 된다.

 


- ID 검증 함수 (onIdChange)

//id가 변경되었을 때 호출하는 함수
const onIdChange=(e)=>{
    //현재 입력한 아이디를 읽어와서
    let inputId=e.target.value;
    //아이디를 한번이라도 입력하면 아이디가 더럽혀 졌는지 여부를 true 로 바꿔준다.
    setDirty({
        ...dirty,
        isIdDirty:true
    });
    //아이디를 검증할 정규표현식
    const reg=/^[a-z].{4,9}$/;
    //입력한 아이디가 정규표현식과 매칭이 되는지(통과 되는지) 확인한다. 
    const isMatch=reg.test(inputId);
    //만일 매칭되지 않는다면
    if(!isMatch){
        //아이디 유효성 여부를 false로 바꾼다.
        setValid({
            ...valid,
            isIdValid:false
        })
        return; //함수를 여기서 끝내라            
    }
    //2. 외부 tomcat 서버에 페이지 전환없이 전송을 하고 응답을 받는다.
    fetch("http://localhost:8888/Step04_Final/users/checkid.jsp?inputId="+inputId)
    .then(function(response){
        return response.json();
    })
    .then(function(data){
        //3. 사용가능한지 여부에 따라 아이디 입력란에 is-valid or is-invalid 클래스를 적절히 추가, 제거를 한다.
        console.log(data);
        if(data.isExist){   
            setValid({
                ...valid,
                isIdValid:false
            });
        }else{
            setValid({
                ...valid,
                isIdValid:true
            });
        }
    });        
}

 

- 아이디 유효성을 참조하는 방법: valid.isIdValid 하면 된다. 점찍어서 참조하기

 

- id를 한번이라도 입력한적이 있는지 여부(isIdDirty)도 관리

 

- input 요소에 입력한 아이디를 기존의 값과 매칭하기

 

- class를 좀더 편하게 제어하기 위해 classnames를 설치함

- 파일에서도 import하기

 

className={`form-control ${valid.isIdValid ? 'is-valid' : 'is-invalid'} `}

- id label의 className에 추가될 내용 : id-valid 또는 id-invalid

- 이것이 id 유효성 여부에 따라서 달라진다.

 

 

- classNames 모듈을 사용하지 않을 경우 위와 같이 작성

 

className={`form-control ${valid.isIdValid ? 'is-valid' : 'is-invalid'} `}
className={cn('form-control', {'is-valid':valid.isIdValid, 'is-invalid':!valid.isIdValid} }

 

- classnames 를 사용하지 않았을 경우 / 사용했을 경우

 

cn(기본으로 넣을 클래스명 , {T/F 여부에 따라 선택적으로 넣을 클래스명} )

 

- form-control : 고정으로 항상 넣어놓을 것은 그냥 바로 텍스트 입력

 

- is-valid / is-invalid : 선택적으로 넣을 방의 이름으로 넣어두고, 값에 boolean 값을 넣어준다.

- True이면 is-valid 가 추가되고, False면 is-invalid가 추가되도록

 

- 여기에 false조건에는 && dirty.isIdDirty 를 추가해준다.(조건 2가지를 따져서 일치하면 is-invalid를 추가하도록)

- 처음 페이지 로딩시부터 사용할 수 없는 아이디라고 나오지 않고, 한번이라도 입력한 이후에 나오도록!

 


 

- pwd 검증 함수 (onPwdChange, onPwdChange2)

//비밀번호를 입력했을 때 실행할 함수 등록
const onPwdChange=(e)=>{
    //입력한 비밀번호를 읽어와서
    let inputPwd=e.target.value;
    //pwd의 상태값에 반영하고
    setInput({
        ...input,
        pwd: inputPwd
    });
    //더렵혀 졌는지 여부를 true로 바꿔준다.
    setDirty({
        ...dirty,
        isPwdDirty:true
    })
    //비밀번호를 검증할 정규 표현식
    const reg=/[\W]/; 
    //만일 정규표현식 검증을 통과하지 못했다면
    if(!reg.test(inputPwd)){	    				
        setValid({
            ...valid,
            isPwdValid: false
        })            
        return;	//함수를 여기서 끝내라     
    }
    //만일 비밀번호 입력란과 확인란이 다르다면
    if(inputPwd != input.pwd2){
        setValid({
            ...valid,
            isPwdValid:false
        }) 
    }else{ //같다면	
        setValid({
            ...valid,	
            isPwdValid:true
        })
    }		
}

const onPwdChange2=(e)=>{
    //입력한 비밀번호를 읽어와서
    let inputPwd=e.target.value;
    //pwd2의 상태값에 반영하고
    setInput({
        ...input,
        pwd2: inputPwd
    });
    setDirty({
        ...dirty,
        isPwdDirty:true
    })        
    //비밀번호를 검증할 정규 표현식
    const reg=/[\W]/; 
    //만일 정규표현식 검증을 통과하지 못했다면
    if(!reg.test(inputPwd)){	    				
        setValid({
            ...valid,
            isPwdValid: false
        })            
        return;	//함수를 여기서 끝내라     
    }
    //만일 비밀번호 입력란과 확인란이 다르다면
    if(inputPwd != input.pwd){
        setValid({
            ...valid,
            isPwdValid:false
        }) 
    }else{ //같다면	
        setValid({
            ...valid,	
            isPwdValid:true
        })
    }		
}

 

- 이전에 작성한 Vue에서는 각각의 input요소의 모델명이 다르기때문에,

  동일한 함수를 호출하더라도 구분해서 각자의 값을 따로 읽어오기가 가능했다.

 

- 하지만 react에서는 모델을 지정하지 않으므로...

 2개의 input요소(pwd, pwd2)에 입력되었을 때의 함수를 각각 다르게 만들어 따로 호출하기로 함!

 

const onPwdChange=(e)=>{}
const onPwdChange2=(e)=>{}

 

- onPwdChange

if(input.pwd != input.pwd2){
    setValid({
        ...valid,
        isPwdValid:false
    }) 
}else{ //같다면	
    setValid({
        ...valid,	
        isPwdValid:true
    })
}

- 주의!! 여기에서 비교대상을 input.pwd 대신 inputPwd 사용

 

- state를 set으로 반영하는 데에 시간이 좀 걸려서, 이 값을 읽어낼 때 변경된 값이 아직 반영되지 않은 상태일 수 있다.

- 따라서 inputPwd (입력받은 내용) 의 값과 비교하는 것이 좋다.

 


 

- Email 검증 함수

//이메일을 입력했을 때 호출되는 함수
const onEmailChange = (e)=>{
    let inputEmail=e.target.value;
    setDirty({
        ...dirty,
        isEmailDirty:true
    })   
    //이메일을 검증할 정규 표현식
    const reg=/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;									
    //만일 입력된 이메일이 정규표현식 검증을 통과하지 못했다면
    if(!reg.test(inputEmail)){
        setValid({
            ...valid,
            isEmailValid:false
        })            
    }else{
        setValid({
            ...valid,
            isEmailValid:true
        })
    };    
}

 

- input요소에 입력된 값을 읽어와 검증하는 test() 에 넣어준다.

 

- !reg.test() 의 T/F여부에 따라 if /else 로 수행할 코드인데,

 3항 연산자를 활용해서 좀더 간단하게 작성할 수 있다.

 

- isEmailValid에 !reg.test() 를 연산한 결과물을 넣어줌

 

 

if(!reg.test(inputEmail)){
    setValid({
        ...valid,
        isEmailValid:false
    })            
}else{
    setValid({
        ...valid,
        isEmailValid:true
    })
};
/* 간략화 */
const isEmailValid = !reg.test(inputEmail) ? false : true;
setValid({...valid, isEmailValid});

 

- 아래와 같이 작성하면 코드를 좀더 간략화할 수 있다!

 

- 원래는 setValid({...valid, isEmailValid : isEmailValid}) 라고 써야 하지만,

  변수명과 똑같은 이름으로 방도 만들고 값도 들어가게하는 js문법을 사용해서 이렇게 작성한 것!

 

 


 

- 모든 검증식이 완료되었으면 폼을 전송할 함수 handleSubmit 생성

- 폼 전체가 유효하지 않으면 submit을 막아버리도록 한다.

 

const handleSubmit=(e)=>{
    let isFormValid = valid.isIdValid && valid.isPwdValid && valid.isEmailValid;
    if(!isFormValid){
        e.preventDefault();
    }
}

 

- 3개가 모두 Valid 인 상태이도록 && 으로 조건을 넣어준다.

 

 

- 이것은 오리지널 이벤트가 아니고 리액트가 넣어주는 이벤트이다.

 (하지만 preventDefault 함수는 똑같으므로 차이 없이 사용해도 무방하다)

 

 


 

 

- 완성되면 build 해서 이클립스의 users 폴더안에 넣을 예정!

 

- json 파일안에 homepage 경로 설정!

 

- npm run build로 만들어진 폴더이다.

 

 

- 이 폴더 내용 전체를 복사해서 경로에 붙여넣는다.

- homepage 에서 지정한 위치에 복사한 파일들을 전부 넣어준다.

 

- 다른 jsp 페이지를 만들어 index.html에 들어있는 한줄짜리  코드를 넣는다.

 (위 이미지는 한줄짜리를 보기 편하게 줄바꿈한 것)

 

- 이 부분이 React에서 만든 앱을 compile한 것이다.

 

- 홈페이지 설정으로 인해 build 된 파일에 들어가게 된 내용! 

 

 

- react를 사용해서 브라우저에서 로딩하는 클라이언트 사이드 렌더링으로 만들어진 페이지이지만

 jsp로 만들었던 페이지와 똑같이 만들 수 있다.