React Hook의 모든 것 (1)
Introduce
먼저 React와 React Hooks의 기본 원리에 대해 배울 것입니다.
- React의 기본 원리
- React Hooks의 필요 동기
- overview of various Hooks
React의 원칙
React는 세 가지 원칙을 기반하고 있습니다.
1. Declarative
React에게 일을 하는 방법을 알려주는 대신 우리가 하고 싶은 일을 알려줍니다. 결과적으로 애플리케이션을 쉽게 설계할 수 있고 React는 데이터가 변경될 때 올바른 구성 요소를 효율적으로 업데이트하고 렌더링 할 수 있다.
예) 선언적 코드 예시
const input = ['a', 'b', 'c']
let result = input.map(str => str + str)
console.log(result) // prints: [ 'aa', 'bb', 'cc' ]
2. Component-based
React는 자체 상태와 보기를 관리하는 구성 요소를 캡슐화한 다음 복잡한 사용자 인터페이스를 만들기 위해 구성할 수 있도록 합니다.
1. Function components : props를 인수로 사용하고 사용자 인터페이스를 반환하는 JavaScript 함수
2. Class components : render methode를 통해 인터페이스를 반환하는 JavaScript 클래스
function components 요소는 정의하고 이해하기가 더 쉽지만 class components의 state, context 및 더 많은 React의 고급 기능을 사용하지 못했습니다. 그러나 React Hooks를 사용하면 클래스 구성 요소 없이도 React의 고급 기능을 다룰 수 있습니다!
이제 React Hook에 대해서 더 알아봅시다.
React Hooks 사용하게 되는 이유
우리는 사용자 인터페이스를 디자인하는 것보다 더 많은 경우에 적용할 수 있는 소프트웨어 디자인 패턴을 배울 것이 중요합니다.
React는 성능을 보장하고 개발자 경험을 매끄럽게 하기 위해 노력했습니다. 하지만, 수년간 React는 사용되면서 몇 가지 문제를 맞이했습니다.
1. confusing class
과거에는 라이프 사이클 메서드라는 특수 기능이 있는 클래스 구성 요소를 사용해야 했습니다.
( ex : componentDidUpdate, this.setState)
React 클래스 컴포넌트는 this context를 인간과 기계 모두 이해하기 어려운 점이 있습니다.
this는 자바스크립트에서 특별한 키워드입니다.
- 메소드에서, this는 클래스 객체를 참조합니다.
- 이벤트 핸들러에서, this는 그 이벤트를 받은 element를 참조합니다.
- function이나 홀로 쓰일 때는, this는 window object와 같은 global객체를 참조합니다.
- Strict mode에서는, 함수 안에서 undefined입니다.
- 추가적으로 call() and apply() 같은 메서드는 참조 객체를 어떤 객체로든 바꿀 수가 있습니다.
인간에게는, 클래스가 어려운 이유가 this는 항상 다른 것을 참조하기 때문에 우리는 수동적으로 클래스에 다시 바인딩을 해줘야 합니다. 기계에게는 클래스의 어떤 메서드가 어떻게 호출될지 몰라서 성능을 최적화하고 사용하지 않는 코드를 구분하기 어렵습니다.
2. Wrapper Hell
Hooks전에는, 우리는 상태관리를 캡슐화하기 원한다면, HOC을 사용해야만 하고 props에 따라 렌더링 했었습니다. 예를 들어보겠습니다.
<AuthenticationContext.Consumer>
{user => (
<LanguageContext.Consumer>
{language => (
<StatusContext.Consumer>
{status => (
...
)}
</StatusContext.Consumer>
)}
</LanguageContext.Consumer>
)}
</AuthenticationContext.Consumer>
위 코드는 사용자 인증을 처리하는 React를 Context를 사용하여 구성하였습니다.
우리는 이렇게 logic추가 되며 많은 하위 트리가 생성될 수 있습니다. ( Wrapper Hell )
이러한 코드는 읽고 쓰기도 쉽지 않을 뿐더러 변경할 경우 오류가 발생하기 쉽습니다. ( 디버깅의 어려움도 따라온다.)
이러한 문제들로 인해 Hooks를 만들게 됩니다.
Hooks를 이용한다면?
React Hooks는 React와 동일한 기본 원칙을 기반으로 합니다. Hooks는 기존 JavaScript기능을 사용하여 상태 관리를 캡슐화할 수 있습니다. 결과적으로 더 이상 특수화된 React 기능을 배우고 이해할 필요가 없습니다. Hooks를 사용하기 위해 기존 JavaScript 지식을 활용하기만 하면 됩니다. ( 바닐라 JS의 중요성 )
위의 문제를 해결해보도록 하겠습니다.
const Example = ({name}) => {
useEffect(() => {
fetch(`http://my.api/${this.props.name}`)
.then(...)
}, [ name ])
// .....
}
이렇게 구성하면서 더 이상 고차 구성 요소를 만들지 않고도 stateful logic을 재사용할 수 있습니다.
또한, useContext를 사용하면 wrapper hell도 해결이 가능합니다.
const user = useContext(AuthenticationContext)
const language = useContext(LanguageContext)
const status = useContext(StatusContext)
핵심 ) 커스텀 훅은 컴포넌트 분할과는 달리 컴포넌트 로직 자체를 분할하거나 재사용할 수 있습니다.
Hooks Mindeset
Hooks의 주요 목표는 상태 기반 로직을 렌더링 로직에서 분리하는 것입니다. 이를 통해 별도의 기능에서 논리를 정의하고 여러 구성 요소에서 재사용할 수 있습니다. Hooks를 사용하면 상태 저장 논리를 구현하기 위해 컴포넌트 계층을 변경할 필요가 없습니다. 이에 따라 더 이상 여러 구성 요소에 상태 논리를 제공하는 별도의 구성 요소를 정의할 필요가 없습니다.
간단하게 Hook을 사용하면 됩니다!
또한, Hooks을 사용하여 개발을 한다면, 데이터 흐름에 대해 생각해야 합니다. ( 구성 요소의 생명 주기는 중요해지지 않는다. ) 특히, props나 state의 값이 변경될 때 trigger를 사용하는 것에 대해 잘 파악하는 것이 중요합니다.
Hooks 규칙
Hooks 사용에는 특정 제한 사항이 있으며 항상 염두에 두어야 합니다.
- Function Components에서만 사용해야합니다.
- hooks는 정의 순서가 매우 중요하며 동일해야 합니다. 따라서 조건부, 루프, 중첩 함수를 사용하지 못합니다.
ex) 조건문으로 useEffect를 동작시키면 안 된다
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
} // 이 처럼 조건으로 Hooks를 사용하면 안됨
Basic Hooks
Basic Hooks는 Stateful React apps에서 가장 일반적으로 필요한 기능을 제공합니다.
가장 많이 사용하는 Hook들인 만큼 뒤에서 자세히 다루겠습니다.
1. useState
상태 저장 값 및 setter 기능 값을 업데이트하기 위해 사용
import { useState } from 'react'
const [ state, setState ] = useState(initialState)
2. useEffect
Effect Hook은 componentDidMount그리고componentDidUpdate와 유사하게 작동합니다.
그리고 unmount시 CleanUp 할 수 있다( componentWillUnmount 대체).
3. useContext
import { useContext } from 'react'
const value = useContext(MyContext)
- 이 Hook은 context 객체를 받아들이고 현재 context 값을 반환합니다.
4. Additional Hooks
1. useRef
useRef는 React에서 elements와 components에 대한 참조를 다룰 때 사용됩니다.
import { useRef } from 'react'
const refContainer = useRef(initialValue)
<ComponentName ref={refContainer} />
2. useReducer
useReducer는 복잡한 상태 로직을 다룰 때 사용한다.
import { useReducer } from 'react'
const [ state, dispatch ] = useReducer(reducer, initialArg, init)
3. useMemo
메모이제이션은 함수 호출의 결과를 캐시 한 다음 동일한 입력이 다시 발생할 때 반환되는 최적화 기법으로 이 기법을 사용하게 해 주는 게 useMemo.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
비용이 많이 드는 작업을 다시 실행하지 않으려는 경우 최적화에 유용합니다.
4. useCallback
인자로 콜백 함수와 종속성 배열을 전달받아 콜백 함수를 memorized version을 반환한다. useMemo와 비슷하지만 콜백 함수용이다.
import { useCallback } from 'react'
const memoizedCallback = useCallback(
() => {
doSomething(a, b)
},
[a, b] )
등 다양한 Hooks들이 있지만 차차 알아보며 공부하겠습니다.
마무리
정말 많은 hook들이 커뮤니티에서 만들어지고 있다. 잘 보고 자기에 맞게 사용하고 또, 커스텀 hook을 만들어보는 건 좋은 경험이 된다고 생각합니다.
이번 장에서 다룬 것은
- React의 기본 원리와 React가 제공하는 구성 요소 유형
- 클래스 구성 요소의 일반적인 문제, React의 기존 기능 사용 및 기본 원칙을 깨는 방법에 대해 학습 (커스텀 훅은 컴포넌트 분할과는 달리 ‘컴포넌트 로직 자체를 분할하거나 재사용’할 수 있습니다. )
- hooks를 왜 만들게 되었는지
- 다양한 Hooks를 처음으로 접함
이제 Hooks의 고급 개념으로 넘어갈 수 있습니다. 다음 장에서는 State Hook을 처음부터 다시 구현하여 작동하는 방식에 대한 심층적인 지식을 얻을 것입니다. 그렇게 함으로써 우리는 Hooks가 내부적으로 어떻게 작동하는지, 그리고 그 한계가 무엇인지 파악할 것입니다. 이후에는 State Hook을 사용하여 작은 블로그 애플리케이션을 만들 것입니다!