📗목차

지난 HTML을 리액트로 변환하기(1)에서 Username과 Password 입력란에 입력의 유무의 따라 LOGIN 버튼의 배경색이 달라지는 것을 jQuery로 작성한 것을 보였다.

이번에는 jQuery로 작성한 기능을 리액트의 Hook를 사용하여 리액트로 변환할 것이다.

변환하기 전에 아래의 코드를 다시 살펴보자.

jQuery 코드

//ID와 PASSWORD의 입력 유무에 따른 버튼 이벤트
$('#name-input').on('input', checkInput);
$('#password-input').on('input', checkInput);

function checkInput() {
    let id = $('#name-input').val();
    let password = $('#password-input').val();
    let btnLogin = $('#btn-login');

    if(id === '' || password === '')
        btnLogin.removeClass('on');
    else 
        btnLogin.addClass('on');
}

//로그인을 한 후 다음 페이지로 넘어감
$('#btn-login').on('click', login);
$(window).on('keypress', function(e) {
    if(e.keyCode === 13) // 13: 엔터키 
        login();
});

function login() {
    let id = $('#name-input').val();
    let password = $('#password-input').val();
 
    if(id === '생략' && password === '생략') 
        window.open('https://home-commemoration.netlify.app/');
    else if(id !== '' || password !== '')
        alert('아이디 또는 패스워드가 잘못 입력 되었습니다.');
}

위의 코드처럼 먼저 버튼의 활성화 여부 기능을 만들기 위해서는 input의 입력값을 알아야 한다. 그래서 제일 먼저 해줘야 되는 작업은 useState Hook이다.
useState로 id와 password의 상태 변화 값을 주는 작업을 해보자.

1. useState를 이용한 onChange 이벤트 핸들링

1. 각 상태값마다 useState 함수 호출하기

const [ id, setId ] = useState('');
const [ password, setPassword ] = useState('');

// 입력값 변경을 처리하는 함수
const handleInputChange = e => {
    const { name, value } = e.target;
    if (name === 'id') {
        setId(value);
    } else if (name === 'password') {
        setPassword(value);
    }
};

useState 함수를 호출하여 함수의 파라미터는 기본값을 ''로 넣고, 상태값인 id와 password를 선언하였다.

handleInputChange는 input 태그의 속성인 onChange 이벤트에 넣을 함수이다.
input의 입력란에 값을 입력하면 그 값이 상태 값에 넣어진다.
아래의 첫 번째 input과 두 번째 input을 봐보자.

첫번 째 input

<input 
    type="text" 
    name="id" 
    value={id}
    onChange={handleInputChange}
    id="name-input" 
    required 
/>

input 속성에 name="id"를 넣고, value에 상태값인 id를 넣었다.

두번 째 input

<input 
    type="password" 
    name="password"  
    value={password}
    onChange={handleInputChange}
    id="password-input"
    required 
/>

input 속성에 name="password"를 넣고, value에 상태값인 password를 넣었다.

위의 기능을 한번 코드에 적용해보자.

import React, { useState } from 'react';
import '../style/LoginBox.css';

const LoginBox = () => {
    const [ id, setId ] = useState('');
    const [ password, setPassword ] = useState('');

    // 입력값 변경을 처리하는 함수
    const handleInputChange = e => {
        const { name, value } = e.target;
        if (name === 'id') {
            setId(value);
        else if (name === 'password') {
            setPassword(value);
        }
    };      
    return (
        <>
            <section className="login-form">
                <h1>추억을 로그인</h1>
                <form action="">
                    <div className="int-area">
                        <input 
                            type="text" 
                            name="id"  
      						value={id}
      						onChange={handleInputChange}
  							id="name-input"
                            required />
                        <label id="id">Username</label>
                    </div>
                    <div className="int-area">
                        <input 
                            type="password" 
                            name="password"  
  							value={password}
  							onChange={handleInputChange}
                            id="password-input"
                            required 
                            />
                        <label id="pw">Password</label>
                    </div>
                </form>    

                <button id="btn-login" type="submit">LOGIN</button>
                <div id="hint">
                  <a href="hint.html" target='_blank'>Click to get a hint</a>
  				</div>
            </section>
        </>
    )
};

export default LoginBox;

이제 input에 값을 입력할 때마다 value에 값이 업데이트 된다.


2. 상태값을 객체로 묶어서 useState 함수 호출하기

이번에는 useState를 통해 사용하는 상태에 문자열이 아닌 객체를 넣어 보는 방식을 해보겠다.

const [ form, setForm ] = useState({
    id: '',
    password: ''
});
const { id, password } = form;

const handleInputChange = e => {
    const nextForm = {
        ...form,
        [e.target.name]: e.target.value
    };
    setForm(nextForm);
}

form이라는 상태값을 선언하여 그 안에 key를 id와 password를 만들었다.
그 다음 form을 각각 id와 password로 구조 분해 할당하였다.

✔️ 필자는 이 방식이 깔끔하고 보기 편해서 이 방식을 택하여 진행하겠다!
이 방식은 input이 여러 개일 때 유용하다.

2. 렌더링 성능 최적화

위에서 작성한 handleInputChange 함수의 렌더링 성능 최적화를 해볼 것이다.
렌더링 성능 최적화를 위해 함수형 업데이트를 사용할 것이다.

먼저 함수형 업데이트를 사용해야 되는 이유를 나열해보겠다.

  • 안전한 이전 상태 참조
  • 불변성 유지
  • 최적화
    등등...

1. 각 상태값마다 useState 함수 호출하기부터 함수형 업데이트를 적용해보겠다.

const handleInputChange = e => {
    const { name, value } = e.target;
    if (name === 'id') {
        setId(prevId => value);
    } else if (name === 'password') {
        setPassword(prevPw => value);
    }
};

이렇게 하면 현재 상태 값을 직접 참조하지 않고, React가 제공하는 prevId와 prevPw를 사용하여 안전하게 상태를 업데이트할 수 있다.

다음은
2. 상태값을 객체로 묶어서 useState 함수 호출하기를 함수형 업데이트 해볼 것이다.

2번 handleInputChange를 다시 한번 봐보자.

const handleInputChange = e => {
    const nextForm = {
        ...form,
        [e.target.name]: e.target.value
    };
    setForm(nextForm);
}

위의 handleInputChange는 setForm을 호출할 때 form 객체를 복사하고 새로운 값을 추가하는 방식으로 상태를 업데이트하였다.
setForm을 호출할 때 불필요한 객체 생성을 피해볼 것이다. 이를 함수형 업데이트가 도와줄 것이다.

const handleInputChange = e => {
    setForm(prevForm => ({
        ...prevForm,
        [e.target.name]: e.target.value
    }));
};

✔️ 그럼 최종적으로 필자는 이 handleInputChange를 사용할 것이다.


3. 버튼 활성화

(24.03.12. 글 수정)

그 동안 리액트에서 상태 업데이트는 무조건 useState를 활용해야 되는 건 줄만 알았다. 그래서 버튼 색상 활성화도 useState로 구현을 했더니 비동기적으로 버튼 색상이 바뀌었다. 동기적으로 작동시키기 위해서 useEffect 함수를 사용했더니 버튼 색상이 동기적으로 잘 작동했다.

하지만, 굳이 useState로 상태 업데이트를 할 필요가 없었다...ㅜ
항상 도움을 주시고 정말 좋으신 멘토분께서 피드백을 남겨주셔서 알게 되었다. useState로 상태 값을 선언하지 말고, 그냥 일반 변수를 선언해야 되는 것이다.
이게 무슨 말인지 지금부터 살펴보도록하자.

id와 password 값의 유무에 따른 버튼 색상을 지정해줄 것이다.
id와 password 값이 둘 다 있어야만 LOGIN 버튼 색상이 노란색으로 바뀔 것이다.

위의 결과처럼 나오기 위해서 버튼 활성화 변수 하나를 선언해줄 것이다.

const isActive = id !== '' && password !== ''

이 변수는 id와 password의 값이 비어있으면 false, 값이 모두 있으면 true로 설정한다.

위에서 isActive를 일반 변수로 선언했지만, 이전에 나는 isActive 변수를 상태 변수로 선언했다. 바로 아래처럼 말이다.

const [ isActive, setIsActive ] = useState(false);

하지만 이게 아니었다. 멘토님께서 말씀하시길...

isActive라는 상태에 true/false중 어떤 값이 들어갈 지는 자기가 정하는 게 아니라, 철저히 form.id 와 form.password에 의해서 정해집니다.
이렇게 다른 state나 prop에 의해서 자신의 운명이 결정된다면 이는 별도의 state로 만들 필요가 없습니다.
"

이력서에 useState를 활용한 상태 업데이트가 비동기적으로 작동하는 것을 useEffect 훅을 사용해서 동기적으로 만들었다는 내용을 기재했었다. 그래서 면접에서 이 부분을 짚으셨던 분이 계셨었는데 내가 뭔가 잘못한 게 있을까 해서 멘토님께 여쭤보니 위와 같이 중요한 개념을 알려주셨다.
게다가 동기적으로 작동시키기 위해서 useEffect 훅을 사용하는 것도 맞는 말이 아니라고 하셨다.

이것도 모르고 있었다는 자괴감이 들지만 지금이라도 멘토님한테서 중요한 걸 배웠다라는 것에 감사히 여겨야겠다.

아래의 코드로 isActive 변수를 가지고 어떻게 버튼을 활성화할 수 있는지 지금부터 살펴보도록 하자.

const [ form, setForm ] = useState({
    id: '',
    password: ''
});
const { id, password } = form;
const isActive = id !== '' && password !== ''; // id와 password 값의 유무에 따른 활성화 상태 

const handleInputChange = e => {
    setForm(prevForm => ({
        ...prevForm,
        [e.target.name]: e.target.value
    }));
};

4. 버튼 스타일링

기존 버튼 태그 속성

<button id="btn-login" type="submit">LOGIN</button>

스타일링한 버튼 태그 속성

<button 
    id="btn-login"   
    type="submit"						 // 노란색 : 회색
    style={{ backgroundColor: isActive ? "#d8db31" : "rgba(209, 206, 206, 0.733)" }} // style 동적으로 변경
>
    LOGIN
</button>

isActive가 true일 때 버튼 배경색이#d8db31
false일 때 버튼 배경색이 rgba(209, 206, 206, 0.733)
이렇게 style을 지정해주면 id와 password 값이 둘 다 입력되었을 때 버튼색이 노랗게 변할 것이다.

코드를 저장하고 실행하면 정상적으로 작동하는 것을 볼 수 있다!

LoginBox 전체 코드

import React, { useState, useEffect } from 'react';
import '../style/LoginBox.css';

const LoginBox = () => {
    const [ form, setForm ] = useState({
        id: '',
        password: ''
    });
    const { id, password } = form;
  	const isActive = id !== '' && password !== ''; // id와 password 값의 유무에 따른 활성화 상태 
  
    const handleInputChange = e => {
        setForm(prevForm => ({
            ...prevForm,
            [e.target.name]: e.target.value
        }));
    };

    return (
        <>
            <section className="login-form">
                <h1>추억을 로그인</h1>
                <form action="">
                    <div className="int-area">
                        <input 
                            type="text" 
                            name="id" 
                            value={id}
                            onChange={handleInputChange}
                            id="name-input" 
                            required 
                        />
                        <label>Username</label>
                    </div>
                    <div className="int-area">
                        <input 
                            type="password" 
                            name="password" 
                            value={password}
                            onChange={handleInputChange}
                            id="password-input" 
                            required 
                        />
                        <label>Password</label>
                    </div>
                </form>    

                <button 
                    id="btn-login"   
                    type="submit"						 // 노란색 : 회색
                    style={{ backgroundColor: isActive ? "#d8db31" : "rgba(209, 206, 206, 0.733)" }} // style 동적으로 변경
                >
                    LOGIN
                </button>
                <div id="hint">
                  <a href="hint.html" target='_blank'>Click to get a hint</a>
  				</div>
            </section>
        </>
    )
};

export default LoginBox;

다음은 react-router-dom을 이용하여 Click to get a hint를 눌렀을 때의 화면 전환하는 과정을 보이도록 하겠다.

profile
프론트엔드 개발자입니다.

0개의 댓글