React

#10 함수형 컴포넌트 1. intro, useState

Sila 2022. 4. 29. 19:34

이제 클래스형 말고 함수형 컴포넌트를 hook과 함께 사용해 비슷한 일을 해보자.

 

회원가입 기능을 함수형 컴포넌트를 이용해 구현해볼 것이다.

 

새 react 폴더를 만든 후, src 폴더 안에 Form.jsx를 만들고, App에서 이를 import하자.

 

생성될 폴더명은 여기서는 practice로 정했다. 

 

npm init -y
npx create-react-app practice
/*  practice/src/Form/From.jsx  */

import React from 'react'

const Form = () => {
    return(
        <form>
            <ul>
                <li>
                    <label htmlFor='userid'>userid</label>
                    <lnput type='text' name='userid'/>
                </li>
                <li>
                    <label htmlFor='userpw'>password</label>
                    <input type='text' name='userpw'/>
                </li>
                <li><input type='submit' value='register'></li>
            </ul>
        </form>
    )     
}

export default Form

보는것처럼 class가 아닌 화살표 함수로 컴포넌트를 만든 후, 이를 export 했다.

 

form element에 다음과 같이 submitHandler를 추가한다. (여기까진 클래스 컴포넌트와 동일)

 

/*  practice/src/Form/Form.jsx  */

const Form = () => {
    const submitHandler= (e) => {
        e.preventDefault()
    }
    
    return(
        <form onSubmit={submitHandler}>
        // ... 중략
        </form>
    )
}

 

이제 id, pw를 바꿔줘야 하는데, 클래스형 컴포넌트에서는 state를 선언하고,

 

그 안에 변할 수 있는 값들을 전부 넣은 후, state를 바꿔주는 식으로 진행이 되었다.

 

함수형 컴포넌트는 useState라는 함수라는 hook을 사용해 동일한 기능을 수행한다.

 

hook은 react의 내장 hook과 우리가 직접 만들어 사용할 수 있는 custom hook이 있는데,

 

우선 여기서는 내장 hook을 먼저 배워보도록 하자.

 

react를 import하는 첫 줄에서 다음과 같이 useState 함수를 가져온다.

 

/*  practice/src/Form/Form.jsx  */

import React, {useState} from 'react'

 

이름을 보면 알 수 있듯 useState는 state를 바꿀 수 있도록 해주는 가장 기본적인 훅이다.

 

다음과 같이 Form 함수에 이를 활용해보자.

 

일반적으로 useState 함수를 사용할 땐

 

const [바뀔변수, 변수를 바꿔줄 함수] = useState([바뀔 변수의 초기값])

 

과 같은 형식으로 선언된다.

 

예를 들어 userid, userpw 각각에 대해 useState를 이용, setUserid, setUserpw를 선언했다.

 

/*  practice/src/Form/Form.jsx  */

const Form = () => {
    const [ userid, setUserid ] = useState('')
    const [ userpw, setUserpw ] = useState('')

}

 

이러면 초기값 ''을 갖는 userid를 바꿔줄 함수 setUserid를 선언한 것이다.

 

이제 각각의 input에 다음과 같이 변화에 대응하는 함수를 넣는다.

 

/*  practice/src/Form/Form.jsx  */

<input type='text' name='userid' onChange={changeUserid}/>
//...중략
<input type='password' name='userpw' onChange={changeUserpw}/>

 

ChangeUSerid, ChangeUserpw는 다음과 같이 useState를 이용해 선언해준 setUserid, setUserpw를 포함한다.

 

/*  practice/src/Form/Form.jsx  */

const changeUserid = (e) => {
    const value = e.target.value
    setUserid(value)
}

const changeUserpw = (e) => {
    const value = e.target.value
    setUserpw(value)
}

 

여기까지하면 우리가 id, pw를 입력하면 그걸 인식해서 userid, userpw의 값을 바꿔주는 것을 확인할 수 있다.

 

잘 되는지 확인하려면 submitHandler에 다음과 같이 변수를 console.log로 불러와보면 된다.

 

/*  practice/src/Form/Form.jsx  */

const submitHandler = (e) => {
    e.preventDefault()
    console.log(userid, userpw)
}

그런데 잘 보면 userid를 인식해 바꾸는 함수나 userpw를 바꾸는 함수나 변수명만 같을 뿐

 

완전히 동일한 기능을 가지고 있다.

 

이 두 함수를 하나로 통합하는 작업을 해보자.

 

우선 필요한 두 변수 userid, userpw를 하나의 객체로 묶고 각 input의 name을 통해

 

우리가 input에 정보를 타이핑할때 어떤 변수의 값이 변할지를 name값을 통해 구분해준다.

 

/*  practice/src/Form/Form.jsx  */

import React, {useState} from 'react'

const Form = () => {
    const [userInfo, setUserInfo] = useState({userid:'', userpw;''})
    // SetUserid, setUserpw를 하나로 통합

    const submitHandler = (e) =>{
        e.preventDefault()
        console.log(userid, userpw)
    }
    
    const changeInfo = (e) => {
        const value = e.target.value
        const name = e.target.name
        setUserInfo({...userInfo, [name]:value})
    }
    // changeUserid, changeUserpw를 하나로 통합

    return(
        <form onSubmit={submitHandler}>
            <ul>
                <li>
                    <label htmlFor='userid'>userid</label>
                    <input type='text' name='userid' onChange={changeUserid}/>
                </li>
                <li>
                    <label htmlFor='userpw'>userpw</label>
                    <input type='text' name='password' onChange={changeUserpw}/>
                </li>
                <li><input type='submit' value='submit'/></li>
            </ul>
        </form>
    )
}

export default Form

 

changeInfo 함수를 보면 함수 안에서 value와 name을 target의 value, name으로 지정해 값을 준다.

 

name을 통해 우리는 id, pw중 뭐가 바뀌는지를 특정해 줄 수 있다.

 

id를 바꾼다면 해당 input의 name인 userid가 e.target.name의 값이 될 것이다.

 

이제 setUserInfo를 보면 인자값에 원래의 값인 userInfo를 깊은 복사해온 것 다음에

 

[name]: value 라는 것이 추가되었는데,

 

이는 name의 값을 지정해 그 name의 value만을 바꿔주겠다는 것을 의미한다.

 

userid 입력에 뭔가 입력하면 changeInfo가 발동해 value, name을 지정하고,

 

초기값인 객체 {userid:'', userpw:''} 에서 [name]과 동일한 값을 가진 userid의 value만이 변한다.


여기서 한 단계 더 나가서 입력값에 대한 함수를 아예 Form 컴포넌트 밖으로 분리해보자.

 

/*  practice/src/Form/Form.jsx  */

import React, {useState} from 'react'

const useInput = (defaultValue) => {
    const [ userInfo, setUserInfo ] = useState(defaultValue)
    const onChange = (e) => {
        setUserInfo(e.target.value)
    }
    
    return {
        userInfo,
        onChange
    }
}

 

useInput 함수를 실행하면 return 값으로 userInfo값과 onChange 함수를 돌려준다.

 

이를 각각의 input에 attribute로 전달해줄 것이다.

 

이 함수를 바로 줄 수는 없고, 변수에 넣는 단계를 거쳐야 한다.

 

/*  practice/src/Form/Form.jsx  */

const Form = () => {
    const id = useInput('')
    const pw = useInput('')
    
    const SubmitHandler (e) => {
    //...중략
    }
    
    return(
        //...중략
        <input type='text' name='userid' {...id}/>
        //... 중략
        <input type='password' name='userpw' {...pw}/>
    )
}

 

id, pw에 각각 useInput을 넣어주면 변수 안에는 useInput의 return값인 userInfo와 onChange 함수가

 

객체 형태로 담긴다.

 

이 객체를 깊은 복사해서 input element에 넣어주면 결과적으로 element에 함수와 값이 통째로 전달된다.

 

지금까지 한 작업물을 정리하면 다음과 같이 코드가 작성되었을 것이다.

 

/*  practice/src/Form/Form.jsx  */

import React, {useState} from 'react'

const useInput = (defaultValue) => {
    const [userInfo, setUserInfo] = useState(defaultValue)
    const onChange = (e) => {
        setUserInfo(e.target.value)
    }

    return {
        userInfo,
        onChange
    }
}

const Form = () => {
    const id = useInput('')
    const pw = useInput('')
    // 동일한 함수를 각각에 대해 실행하면 다른 메모리를 차지하므로 각각이 다른 결과를 줌

    const submitHandler = (e) =>{
        e.preventDefault()
        console.log(id.userInfo, pw.userInfo)
        // 결과물을 확인하고 싶다면 다음과 같이 console.log로 불러온다.
    }
    
    return(
        <form onSubmit={submitHandler}>
            <ul>
                <li>
                    <label htmlFor='userid'>userid</label>
                    <input type='text' name='userid' {...id} />
                    {/* input의 children들을 객체로 한 번에 묶어온다. */}
                </li>
                <li>
                    <label htmlFor='userpw'>userpw</label>
                    <input type='text' name='userpw' {...pw}/>
                </li>
                <li><input type='submit' value='submit'/></li>
            </ul>
        </form>
    )
}

export default Form

마지막으로 입력한 id, pw가 조건에 맞는지를 검증하는 validate 함수를 추가해보자.

 

회원가입을 위해 id, pw를 입력후 submit 할 때,

 

id는 email 형식이고, pw는 8글자 이상을 포함하는 조건을 만족하면 가입이 잘 되었다는 알림 창을,

 

그렇지 않으면 실패 이유를 화면에 렌더링해주도록 하고싶다.

 

우선 검증을 해주는 함수를 선언한다.

 

/*  practice/src/Form/Form.jsx  */

const validate = ({userid, userpw}) => {
    const errors = {}

    if(!userid) {
        errors.userid='email을 입력해주세요'
    } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(userid)) {
        errors.userid = 'invalid email address'
    }

    if (!userpw) {
        errors.userpw = 'password를 입력해주세요'
    } else if (userpw.length < 8) {
        errors.userpw = 'password는 8글자 이상이어야 합니다.'
    }

    return errors
}

 

userid, userpw 각각에 대해 검증을 거친 후, error가 있다면 errors 객체에

 

에러 내용을 담아 return해주는 방식으로 작동한다.

 

이제 errors의 값을 바꿔줄 훅(useState)을 Form 함수 안에 추가한다.

 

/*  practice/src/Form/Form.jsx  */

const [errors, setErrors] = useState({})
// 초기값은 빈 객체, 이후 id/pw에 문제가 있다면 각각의 key, value가 추가된다.

 

이 setErrors 함수를 Form 함수의 submitHandler 함수안에서 실행한다.

 

/*  practice/src/Form/Form.jsx  */

const submitHandler = (e) => {
    e.preventDefault()
    
    const input = {
        userid:id.userInfo,
        userpw:pw.userInfo
    }
    
    setErrors(validate(input))
}

 

submit을 클릭하면 submitHandler가 발동된다.

 

여기서 입력한 id, pw값을 모아서 검증 함수 validate안에 매개변수로 넣어준다.

 

전달된 매개변수 id, pw에 대해 각각 검증 과정을 거치면 errors 객체가 완성되어 return 된다.

 

이 return된 값을 setErrors에 넣어 실행해 (Form안에서 선언한) errors의 값을 바꿔준다.

 

이제 이 errors의 내용을 사용자에게 보여줄 수 있다.

 

조건문을 이용해 에러가 있다면 그때만 그 내용을 보여주도록 코드를 추가한다.

 

/*  practice/src/Form/Form.jsx  */

//...중략

return(
    <form onSubmit={submitHandler}>
        <ul>
            <li>
                <label htmlFor='userid'>userid</label>
                <input type='text' name='userid' {...id}/>
                {errors.userid && <span>{errors.userid}</span>
            </li>
            <li>
                <label htmlFor='userpw'>password</label>
                <input type='password' name='userpw' {...pw}/>
                {errors.userpw && <span>{errors.userpw}</span>}
            </li>
            <li><input type='submit' value='submit'></li>
        </ul>
    </form>
)

'React' 카테고리의 다른 글

#12 useContext  (0) 2022.04.29
#11 함수형 컴포넌트 2. useEffect  (0) 2022.04.29
#9 댓글 기능 만들기 part2  (0) 2022.04.24
#8 댓글 기능 만들기 part1  (0) 2022.04.24
#7 곱셈 프로그램 만들기  (0) 2022.04.24