[React] Styled Component, useMemo, useCallback, Custom Hook
🚀 들어가며
이번 시간에는 리액트에서 컴포넌트에 스타일을 적용하는 법과, useMemo, React.memo, useCallback, Custom Hook를 학습했다. 상당히 유익했던 시간들이었다. 그동안 주로 useState와 useEffect, useRef hook만 사용했고 오늘 학습한 hook들은 개념만 대충 알고 있었던 상태였는데 이번 기회에 정확하게 알 수 있었다. 또, 실무에서 많이 사용된다는 styled 컴포넌트를 직접 사용하면서 사용법을 익히고 얼마나 편리한지 몸소 체감할 수 있었다.
✅ 스타일 적용하기
리액트에서 컴포넌트에 스타일을 적용하는 방법은 총 3가지가 있다.
- 스타일 시트 사용하기
- 인라인 스타일 적용하기
- JS 파일 내에서 작성한 CSS 적용하기
▪︎ JS 파일 내에서 작성한 CSS 적용하기
위의 두 가지 방법은 여러 번 사용해봤기 때문에 생략하고, JS 파일 내에서 작성한 CSS를 적용해보는 법을 집중적으로 학습하기로 했다.
먼저, 라이브러리를 사용해야 한다. 강의에서는 emotion 라이브러리를 사용했다.
emotion은 CSS 스타일을 JS 파일 내에서 작성하도록 도와주는 라이브러리이다.
Emotion – Introduction
(Edit code to see changes)
emotion.sh
가장 먼저 터미널 명령어를 통해 emotion 라이브러리를 설치한다.
npm install --save @emotion/react
emotion 라이브러리를 사용하려면 Babel 플러그인도 설치해주어야 한다.
npm install --save-dev @emotion/babel-plugin
emotion 라이브러리를 사용해서 JS 파일 내에서 작성한 CSS를 적용하는 것은 또 두 가지 다른 방법으로 나뉘는데, CSS 함수를 사용하는 방식과 스타일드 컴포넌트를 사용하는 방식이 있다.
▪︎ css 함수 사용하기
babel을 적용하기 위해서는 .babelrc 파일을 생성 후 옵션을 지정해줘야 하는데, create-react-app 으로 생성한 프로젝트 내에서는 .babelrc가 정상적으로 적용이 되지 않는다.
이를 위해 JS 파일 상단에 프래그마를 직접 붙여주는 식으로 해결할 수 있다. 프래그마란 컴파일러에게 이 파일을 어떻게 컴파일할지 알려주는 것이다.
/** @jsxImportSource @emotion/react */
하지만 매번 프래그마를 붙여주면 상당히 불편하다. 이럴 때 사용할 수 있는 것이 craco 라이브러리이다. craco 라이브러리는 create-react-app-config-override의 약자로, 설정을 override할 수 있는 라이브러리이다.
craco.config.js 파일에 아래와 같이 작성하면 babel 설정을 override해서 넣어줄 수 있다.
module.exports = {
babel: {
presets: ['@emotion/babel-preset-css-prop'],
},
};
이제 실행할 때 react-scripts로 실행하는 것이 아니라 craco로 실행하면 된다.
별도의 프래그마를 상단에 추가하지 않아도 정상적으로 동작한다.
▪︎ styled 컴포넌트
styled 컴포넌트를 사용하려면 패키지를 설치해주어야 한다.
npm install --save @emotion/styled
스타일이 적용된 컴포넌트를 생성할 수 있다.
✅ useMemo
- 함수 컴포넌트는 자신의 상태가 변경될 때 리렌더링된다
- 부모 컴포넌트로부터 받는 prop이 변경될 때 리렌더링 된다
- 부모 컴포넌트의 상태가 변경되면 리렌더링 된다.
특히 부모 컴포넌트의 상태가 변경될 때 자기 자신도 리렌더링되면서 초기 렌더링시 했던 연산이 리렌더링 되면서 불필요하게 반복될 수 있다. 이럴때 사용할 수 있는 것이 useMemo 훅이다. 먼저 다음의 코드를 보자.
App.js의 상태가 변경되면 자식 컴포넌트인 ShowSum.js가 리렌더링되게 된다.
자식 컴포넌트가 리렌더링되면서 한 번 연산된 부분(sum 함수)을 다시 연산하게 된다. 이럴 때 useMemo 훅을 사용한다.
useMemo 훅의 첫 번째 인수로는 기록해둘 표현식, 두 번째 인수로는 감시 대상을 전달한다. 즉, 두 번째 인수의 값이 변화될 때만 첫 번째 인수의 표현식이 사용된다.
✅ React.memo
React.memo를 활용하여 부모 컴포넌트가 리렌더링되어도 자식 컴포넌트가 리렌더링되는 것을 막을 수 있다. 사용 예시를 살펴보자.
부모 컴포넌트인 App.js의 버튼을 클릭하면 상태가 변경되면서 자식 컴포넌트인 Box.js까지 같이 리렌더링된다.
이를 방지하기 위해 React.memo를 사용할 수 있다.
- line 1 : import
- line 3 : 함수 컴포넌트 전체를 React.memo로 감싸준다.
자식 컴포넌트에 React.memo를 활용해서 부모 컴포넌트가 렌더링되어도 자식 컴포넌트가 렌더링되는 것을 막을 수 있다.
✅ useCallback
함수가 다시 연산되는 것을 막기 위해 useMemo를 사용했던 것처럼 함수를 다시 정의하는 것을 막기 위해 useCallback을 사용할 수 있다.
자식 컴포넌트를 React.memo로 감싸줬음에도 불구하고 부모 컴포넌트인 App.js의 상태를 변경시키는 이벤트 핸들러 함수가 실행되면 자식 컴포넌트들이 리렌더링된다.
이는 부모 컴포넌트의 상태가 변경되면서 즉, 다시 리렌더링 되면서 함수가 다시 재정의되고, 재정의된 함수가 자식 컴포넌트의 props로 전달되기 때문이다. 즉, 자식 컴포넌트 입장에서는 이전과는 다른 새로운 함수를 전달받는 것과 마찬가지이다. 이를 막기 위해 useCallback 훅을 사용할 수 있다.
useCallback 훅의 사용법은 useMemo 훅의 사용법과 거의 비슷하다.
useCallback 훅으로 감싸준 함수 표현식은 기억되게 되고, 리렌더링되어도 함수가 재정의되지 않는다.
✅ Custom Hook
Custom hook은 기존 hook들을 조합해서 사용할 수 있다. 자주 사용할 수 있는 상태로직을 Custom hook으로 빼서 사용하면 중복 코드를 제거할 수 있고, 편하게 사용이 가능하다.
먼저 간단한 예시를 살펴보자.
초기 상태를 전달받는 useToggle hook을 만들었다.
- line 4 : 커스텀 훅 내부에서 상태가 관리된다.
- line 5 : 함수를 정의한다. 정의된 함수는 현재 상태를 바꿔주는 로직을 가지고 있고, useCallback으로 감싸줌으로써 리렌더링 시에도 함수가 재정의되지 않도록 한다.
- line 7 : 현재 상태와 정의한 함수를 반환한다.
- line 4 : 커스텀 훅에서 반환된 배열을 구조분해할당을 통해 꺼내쓴다. 변수 on에는 state가 할당되어 있고, 변수 toggle은 버튼 클릭 이벤트가 발생했을 때 이벤트 핸들러 함수이다.
🛸 마치며
리액트에서 많이 사용되는 useMemo, useCallback hook을 학습할 수 있었다. 이 두 가지 hook의 개념은 어느정도 이해가 되어 어느정도 반복 학습하면 많이 익숙해질 것 같다는 느낌이 들었다.
하지만 Custom hook의 경우에는 정말 많은 연습이 필요하다는 것을 느꼈다. 로그인, 회원가입 Form을 useRef hook을 사용하지 않고 Custom hook을 사용하여 구현한 강의가 있는데, 로직이 조금 더 복잡해지니까 한 번에 이해하기 어렵다는 느낌을 받았다. 어렵겠지만 혼자서 Custom hook을 많이 사용해보면서 익숙해져야겠다.