<목차>
- useEffect가 2번 실행되는 경우
- 커스텀 훅
- React.memo
4. useEffect가 2번 실행되는 경우
- React 18 버전부터 개발 모드에서 useEffect가 두번 실행되는 현상 관찰
- Strict Mode 활성화
- 자동으로 컴포넌트를 두 번 마운트하고 언마운트하는 동작 때문
- 부수 효과 감지
- 부수효과를 더욱 안정적으로 감지하고 의도치 않은 부수효과가 발생하지 않도록 설계
- 개발자가 코드의 버그를 더 쉽게 발견, 수정
- 상태 관리 오류 방지
- 비정상적으로 초기화되거나 업데이트되는 문제를 조기 발견
- 애플리케이션의 안정성 향상
- 메모리 누수 감지
- 컴포넌트가 언마운트된 후에도 남아 있는 이벤트 리스너나 타이머 같은 리소스를 정리하지 않는 경우 메모리 누수 발생
- 이중렌더링을 통해 이러한 문제를 더욱 명확하게 식별
- 이 동작은 오직 개발 모드에서만 적용
- 프로덕션 빌드에서는 useEffect는 한 번만 실행
- 리액트가 의도한 동작
Q. 리액트의 개발 서버에서는 useEffect가 2번 실행되는 모습이 관찰되는데요. 그 이유가 무엇일까요?
5. 커스텀 훅
- 리액트에서 제공하는 기본 훅을 조합하여 사용자가 직접 만든 재사용 가능 함수
- 커스텀 훅은 특정 로직을 모듈화 하여 여러 컴포넌트 간에 공유하고 재사용할 수 있도록 도움
- 코드의 중복을 축소
- 코드 가독성 향상
- 관심사 분리 촉진
사용 사례
- 데이터 페칭
- API로부터 데이터를 가져오고, 로딩 상태와 에러를 관리하는 로직을 커스텀 훅으로 추출
- 다양한 컴포넌트에서 API 호출 로직을 재사용 가능
- 폼 입력 관리
- 사용자 입력과 폼 검증 로직을 커스텀 훅으로 관리 가능
- 여러 폼 컴포넌트에서 유사한 로직을 쉽게 재사용 가능
- 이벤트 리스너
- 윈도우나 특정 요소에 이벤트 리스너를 추가하고 제거하는 로직을 커스텀 훅으로 작성
- 이벤트 관리 쉽게 가능
- 컨텍스트와 상호작용
- 리액트의 컨텍스트를 사용하여 테마 설정이나 사용자 인증 상태 같은 앱 전반의 상태를 관리하는 로직을 커스텀 훅으로 만들 수 있음
- 코드의 중복 방지
커스텀 훅과 유틸의 차이
-
커스텀 훅 (Custom Hook)
- 리액트의 훅 API(useState, useEffect, useContext등)를 활용하여 만든 재사용 가능 함수
- 컴포넌트 로직 (상태 관리, 사이드 이펙트 처리 등)을 모듈화하고 여러 컴포넌트 간 공유
- 리액트의 상태나 생명주기 기능에 직접적으로 연결되어 있기 때문에 컴포넌트 내부에서만 사용 가능
-
유틸리티 함수 (Utilities)
- 특정한 작업을 수행하는 순수 함수
- 애플리케이션 전반에서 사용
- 어떠한 외부 상태에도 의존하지 않음
- 주어진 입력에 대해서만 결과 반환
- 리액트의 특정 기능과는 독립적이라 리액트 외부에서도 사용 가능
-
차이점
-
사용 범위의 차이 : 커스텀 훅은 리액트 컴포넌트 내에서만 사용. 리액트의 훅 API와 상태 관리 기능을 활용 가능. 유틸리티 함수는 어떤 자바스크립트 환경에서나 사용될 수 있으며, 리액트의 컴포넌트 생명주기나 상태와는 독립적
-
기능: 커스텀 훅은 리액트의 상태 관리, 사이드 이펙트 관리 등에 사용. 유틸리티 함수는 데이터 포맷팅, 계산 로직, 배열 조작 등의 일반적인 작업에 사용.
서비스 레이어와 커스텀 훅의 차이
- 서비스 레이어 (auth.service.ts):
- 주로 순수 JavaScript 또는 TypeScript 함수를 사용하여 데이터르 ㄹ처리
- API 호출 등 로직을 캡슐화
- 리액트의 컴포넌트 라이프사이클이나 상태에 독립적으로 작용
- 리액트 외 환경에서도 사용 가능
ex) 인증 토큰 관리, 로그인 및 로그아웃 처리, 사용자 세션 관리 등의 기능 수행
API 호출 로직
import axios from 'axios';
export const login = async (username: string, password: string) => {
const response = await axios.post('/api/login', { username, password });
if (response.data) {
localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
};
export const logout = () => {
localStorage.removeItem('user');
};
- 커스텀 훅 (예: useAuth):
- 리액트의 useState, useEffect, useContext 등의 훅을 활용하여 컴포넌트의 상태 및 사이드 이펙트를 관리
- 컴포넌트와 밀접한 연관
- 리액트 컴포넌트 내에서만 사용
ex) auth.service.ts에서 정의된 함수를 호출하고, 그 결과를 컴포넌트의 상태로 관리하거나 컴포넌트에 필요한 사이드 이펙트를 처리하는 데 사용될 수 있음
서비스 레이어를 사용하여 컴포넌트의 상태를 관리하는 로직
import { useState, useEffect } from 'react';
import * as AuthService from './auth.service';
function useAuth() {
const [user, setUser] = useState(null);
useEffect(() => {
const storedUser = JSON.parse(localStorage.getItem('user'));
if (storedUser) {
setUser(storedUser);
}
}, []);
const login = async (username, password) => {
const userData = await AuthService.login(username, password);
setUser(userData);
};
const logout = () => {
AuthService.logout();
setUser(null);
};
return { user, login, logout };
}
export default useAuth;
=> auth.service.ts는 데이터를 관리하고 처리하는 비즈니스 로직을 캡슐화하는 반면, useAuth 같은 커스텀 훅은 이러한 서비스 로직을 리액트 컴포넌트에서 사용할 수 있도록 상태 관리와 연동시키는 역할
Q. 커스텀 훅을 사용해보셨나요? 사용해본 경험을 이야기해주세요.
6. React.memo
- 리액트에서 제공하는 고차 컴포넌트
- 컴포넌트의 렌더링 성능을 최적화하기 위해 사용
- 불필요한 렌더링을 방지하는데 효과적
- 컴포넌트의 props가 변경되지 않았다면 리렌더링 생략
- 마지막으로 렌더링된 결과를 재사용함
작동원리
- 컴포넌트로 전달되는 props가 변경되었는지 여부를 얕은 비교를 통해 판단.
- 얕은 비교는 객체의 최상위 수준의 프로퍼티만 비교
예시
import React from 'react';
const MyComponent = React.memo(function MyComponent(props) {
console.log("Rendering");
return <div>{props.children}</div>;
});
주의할 점
- 모든 컴포넌트에 무분별하게 사용되어서는 안됨
- 비교 함수의 복잡성이나 오버헤드가 성능 이득을 상쇄할 수 있기 때문
- 실제로 성능 문제가 있는 컴포넌트에 대해서만 적용하는게 좋음
- 얕은 비교를 수행하므로 객체나 배열과 같은 복잡한 자료형 props를 사용할 때는 커스텀 비교 함수의 정의가 필요
Q. 리액트 컴포넌트의 렌더링 성능 향상에 쓰이는 React.memo에 대해 설명해주세요.