본문 바로가기
  • 프론트엔드 개발자 세오세오 | Frontend dev Seo
Learn to Code

[React] 커스텀 훅(Custom Hook)을 사용한 간단한 회원가입 페이지 만들기

by CEOSEO 2021. 6. 5.
728x90
반응형

 

 

React Hook 등장 배경

리액트에서 훅(Hook)이란 함수 컴포넌트에서 리액트의 기능들을 사용하게 해주는 함수이다. 훅은 2019년 2월 16일에 나온 React 16.8부터 사용이 가능하게 되었기 때문에, 리액트가 처음 출시된 2013년 5월부터 2019년까지 약 6년 동안은 리액트의 여러 기능들을 사용하기 위해선 함수 컴포넌트가 아닌 클래스 컴포넌트만을 사용해야한다는 제약이 있었다.

 

 

2018년 React Conf에서 발표하는 댄오라버니 (스티커가 탐난다. 많이 붙일수록 개발력이 상승한다던데)

 

클래스 컴포넌트는 코드를 작성함에 있어서 직관적이지 못하거나 코드를 길게 만드는 요소들이 상당히 많이 있었다. 예를 들어, 똑같은 기능의 코드임에도 불구하고 컴포넌트의 생명주기(lifecycle) 메소드에 맞추어 따로 작성을 해서 로직이 분리되는 경우가 발생한다던가, 아니면 메소드를 사용하기 위해 끊임없이 bind()를 해주어야 한다던가와 같은 여러 불편한 점이 있었다. 리액트 훅의 탄생을 처음으로 공식 발표하던 2018년 10월 React Conf에서 코드 시연을 하던 댄오라버니 조차 bind()를 까먹고 에러를 발생시켜서 관중들이 웃음을 터트릴 정도로, 클래스 컴포넌트에선 하나의 로직을 작성하기 위해 잊지 말고 추가해야 할 프로세스가 여럿 있었다.

 

이렇듯 리액트 사용자들 모두가 (심지어 리액트 팀 조차) 인지하고 있던 클래스 컴포넌트를 사용한 코드 작성의 복잡성을 극복하고자 리액트 훅이 추가되었다. 덕분에(?!) 2021년 현재 새내기 프론트앤드 개발자가 되려는 나와 같은 여러 동지님들은 기본 서비스들에서 여전히 사용중일 클래스 컴포넌트 뿐만 아니라 새롭고 훨씬 편한게 자명한 함수 컴포넌트 및 훅 사용법에 대해서도 빠삭하게 알고 있어야 되도록 환경 세팅이 되어버렸다.

 

리액트 공식 홈페이지에 올라와있는 2018 React Conf 훅 시연 발표를 너무 재밌게 봤고, '저 복잡하고 길던 코드가 저렇게 짧아지다니!!!'하며 할렐루야를 외쳤기 때문에 훅 사용법을 공부하는데에 거부감이 들지 않았다. 그래서 심심풀이로 자잘한 컴포넌트들을 만들다가 문득 아이디, 비밀번호, 그리고 이메일만 받는 초 간단 회원가입 페이지를 만들어보고 싶다는 생각이 들었다. 아주 간단한 커스텀훅(custom hook)을 연습하기에 좋아보였기 때문이다.

 

 

 

 

 

 

Pinterest에서 발견한 예쁜 디자인을 모티브로 디자인 에셋 없이 틀만 만들었다 (https://www.pinterest.co.kr/pin/266416134194350234/)

 

 

 

커스텀훅을 사용하지 않은 경우

아래 코드는 input들에 onChange 이벤트만 설정된 LoginForm 컴포넌트이다. <form> 안에 3개의 <input>이 있는데, 위에서부터 username, password, 그리고 email을 받는 란이다. useState()를 사용하여 3개의 상태 - username, password, email - 를 만들었으며, 각자 input에 걸린 handle 함수들로 상태 변경 사항을 업데이트 시켜준다.

 

 

import { useState } from 'react'


export default function LoginForm() {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [email, setEmail] = useState('')

  function handleUsername (e) {
    setUsername(e.target.value)
  }

  function handlePassword (e) {
    setPassword(e.target.value)
  }

  function handleEmail (e) {
    setEmail(e.target.value)
  }

  return (
    <div className="LoginForm">
      <div className="loginform-header">
        <h2>회원가입</h2>
      </div>
      <form className="loginform">
        <input onChange={handleUsername} type="text" className="loginform-input" placeholder="Username"/>
        <input onChange={handlePassword} type="password" className="loginform-input" placeholder="Password"/>
        <input onChange={handleEmail} type="email" className="loginform-input" placeholder="Email"/>
        <button className="loginform-btn">가입하기</button>
        <div className="loginform-forgotPassword">비밀번호를 잊으셨나요?</div>
      </form>
    </div>
  )
}

 

이와 같이 input의 값을 상태로 지정하고 그 상태 변경을 기다리는 핸들러들은 코드가 거의 동일하다. 상태 이름만 빼고는 반복되는 양상을 보이는 것이다. 커스텀 훅을 사용하면 이와 같은 반복을 줄일 수 있다.

 

 

 

커스텀훅을 사용한 경우

아래 코드는 위와 다르게 useLoginFormInput()이라는 커스텀훅을 만들어서 작성하였다.  (커스텀훅을 작성할 땐 꼭 함수 이름 맨 앞을 use로 해주어야 한다.) 위 코드보다 반복되는 부분들이 상당 수 없어져서 훨씬 짧아진 것을 볼 수 있다.

 

해당 함수 안엔 세 개의 상태들(username, password, email)에 공통적으로 들어가는 코드들을 거푸집처럼(?) 집어다가 만들었다. onChange는 useLoginFormInput에서 리턴으로 뿜어서, spread syntax로 인풋에 걸어주어 작동된다(댄오라버니가 가르쳐주신 걸 사용했다).

 

이러한 커스텀훅은 export하면 같은 파일에 작성된 컴포넌트 외에 다른 컴포넌트에서도 가져다 쓸 수 있어서 코드 재사용성을 높여주는 장점 또한 있다.

 

 

import { useState } from 'react'

export default function LoginForm() {
  const username = useLoginFormInput('')
  const password = useLoginFormInput('')
  const email = useLoginFormInput('')

  return (
    <div className="LoginForm">
      <div className="loginform-header">
        <h2>회원가입</h2>
      </div>
      <form className="loginform">
        <input {...username} type="text" className="loginform-input" placeholder="Username"/>
        <input {...password} type="password" className="loginform-input" placeholder="Password"/>
        <input {...email} type="email" className="loginform-input" placeholder="Email"/>
        <button className="loginform-btn">가입하기</button>
        <div className="loginform-forgotPassword">비밀번호를 잊으셨나요?</div>
      </form>
    </div>
  )
}

function useLoginFormInput (initialValue) {
  const [value, setValue] = useState(initialValue)

  function handleChange (e) {
    setValue(e.target.value)
  }

  return { value, onChange: handleChange }
}

 

 

 

구현 모습

 

작성을 하면

 

Spread syntax로 전달된 onChange가 발동된다

 

 

 

 

728x90
반응형

댓글