본문 바로가기

Lecture/React Native

2021-07-05 :: React Native 함수 컴포넌트와 리액트 훅

04-1 리액트 훅 맛보기

기존 클래스 컴포넌트를 구현할 때의 복잡함을 덜고자 컴포넌트를 함수 형태로 만들 수 있게 하였다. 또한, 함수 컴포넌트가 어떤 값을 유지할 수 있도록 새로운 개념의 데이터 캐시 시스템을 만들었다.  그리고 개발자로 하여금 이 새로운 데이터 캐시 시스템을 쉽게  사용할 수 있도록 접두사 use로 시작하는 여러 개의 API를 제공하며, 이 API를 리액트 훅 함수라고 이름 붙였다.

 

클래스 컴포넌트는 너무 많은 기능이 숨겨져 있고, 라이프사이클 매서드 역시 복잡하다. 이를 극복하고자 리액트 훅이 탄생했다. 

 

리액트 훅을 사용하여 어떤 컴포넌트를 구현한 다음 구현 로직을 커스텀 훅으로 만들면 비슷한 기능이 필요한 다른 컴포넌트를 구현할 때 그냥 가져다 쓰면 된다. 리액트 훅 사용시 주의해야 할 점은 아래와 같다.

  1. 같은 리액트 훅을 여러 번 호출할 수 있다.
  2. 함수 컴포넌트 몸통이 아닌, 몸통 안 복합 실행문의 {}에서는 호출할 수 없다.
  3. 비동기 함수는 콜백 함수로 사용할 수 없다.

훅은 if문, for문의 몸체와 같은 블록 형태 안에서 사용할 수 없다.

export default function App() {
	useEffect(async () => {
    	await Promise.resolve(1)
    }
}

위의 코드는 틀린 예문이다. 안에 콜백함수에서 리턴은 뒷정리 함수이기 때문에 위와 같은 사용은 잘못되었다.

 

폰트를 추가하고 싶으면 폰트 파일(.ttf)을 디렉터리에 넣어두고, react-native.config.js 파일에 넣어주면 된다. 또한 다음 명령을 통해 네이티브 쪽에 반영되도록 하자

npx react-native link

 

setInterval 함수는 컴포넌트가 처음 렌더링될 때 한 번만 호출해야한다. 그러므로 useEffect와 함께 사용하자.

+뒷정리함수안에서 clearInterval 넣어주기

여러가지 훅 처리들을 하나로 모아서 커스텀 ㅊ훅을 정의할 수 있다.

 

전역 변수와 캐시

지금까지 컴포넌트의 데이터들은 컴포넌트 바깥쪽에서 전역 변수 형태로 구현했다. 그 이유는 데이터가 한 번 만들어진 후에 바뀌지 않게 하고 싶기 때문이였다. 

const people = D.makeArray(2).map(D.createRandomPerson)

 

만약 위 코드를 컴포넌트의 안쪽에 두면 컴포넌트가 리렌더링될 때 마다 랜덤한 D.IPerson 타입 데이터 2개가 계속 새로 생겨난다.

리액트 훅의 탄생 배경은 로컬 변수인 people을 마치 전역 변수처럼 사용하고 싶다는 데서 시작했다. 리액트 팀은 로컬 변수 people이 실제 데이터를 가지지 않고 실제 데이터는 어디인가 캐시하고 나서 로컬 변수 people에는 필요할 때 이 캐시한 데이터를 찾을 수 있는 일종의 키를 저장하는 방법을 고안했다.

const cache: Record<string, any> = {}

export const createOrUse = <T>(key: string, callback: ()=> {
	if(!cache[key])
    	cache[key] = callback()
    return cache[key]
}

이 코드의 첫 줄에 cache라는 이름의 전역 변수가 있다. 이 변수의 타입은 Record<string, any>인데, Record는 타입스크립트가 기본으로 제공하는 타입이다. 이 타입은 cache[key]형태 코드를 컴파일 오류 없이 실행하게 해주며 Record는 Record<key_type, value_type> 형태로 사용하는 제너릭 타입이다.

요컨데 cache의 키 타입은 string, 값 타입은 any이다. 즉 키 타입은 문자열이고 값 타입은 어떤 것도 설정할 수 있는 색인 타입이다. 코드는 cache[key]에 값이 있으면 그 값을, 없으면 callback을 호출하여 cache[key]에 값을 저장하고 이 값을 반환하는 것으로, 이는 캐시를 구현하는 전형적인 방법이다.

 

이제 다시 해당 훅을 컴포넌트의 내부에서 사용 해보자.

const people = createOrUse('people', ()=> 
	D.makeArray(2).map(D.createRandomPerson)
)

위 두 코드의 내용이 리액트 훅의 구현 원리이다. 리액트 훅의 구현 방식이 뛰어난 점은 'people'같은 키 부분을 사용자 코드에서 관리하지 않고 리액트 프레임워크 내부에서 관리하여 리액트 훅 호출 부분을 매우 간결하게 해준다는 데 있다.

다음 컴포넌트 코드의 한 가지 문제점은 컴포넌트를 렌더링할 때마다 people 데이터도 반복해서 생성한다는 점이다. 이 데이터는 컴포넌트를 처음 렌더링할 때 한 번만 생성하는 것이 효율적이다.

따라서 아래와 같이 useMemo를 이용해 한 번에 처리를 하도록 하자.

const people = useMemo(()=> 
	D.makeArray(2).map(D.createRandomPerson), []
)
export const fibonacci = (n: number): number => {
	if(n==0) return 0
    else if(n==1) return 1
    return fibonacci(n-1) + fibonacci(n-2)
}
const memoizedFibonacci = useMemo(() => fibonacci, [])
const fibos = useMemo(
	()=>
    	D.makeArray(20+1).map((notUsed, index)=> ({
        	number: index,
            fibonacci: memoizedFibonacci(index)
        })),
	[]
)