Ref
- 컴포넌트가 일부 정보를 기억하고 싶지만 해당 정보가 렌더링을 유발하지 않도록 할 때 사용한다.
컴포넌트에 ref 추가하기
import { useRef } from 'react';
const ref = useRef(0);
반환값
{
current: 0 // useRef에 전달한 값
}
ref.current 프로퍼티를 통해 ref의 current 값에 접근할 수 있으며, 의도적으로 변경할 수 있으므로 읽고 쓸 수 있다.
- 여기선 숫자를 가리키지만 문자열, 객체, 함수 등 모든 것을 가리킬 수 있다.
- React 리렌더에 의해 유지되지만 ref 변경 시 다시 렌더링 되지 않는다.
ref vs state

Ref로 DOM 조작하기
ref로 노드 가져오기
import { useRef } from 'react';
const myRef = useRef(null);
<div ref={myRef}>
- React가
<div>에 대한 DOM 노드를 생성할 때, React는 이 노드에 대한 참조를 myRef.current에 넣고 이 DOM 노드를 이벤트 핸들러에서 접근하거나 노드에 정의된 브라우저 API를 사용할 수 있다.
myRef.current.scrollIntoView();
Effect
- 렌더링 후 특정 코드를 실행하여 React 외부의 시스템과 컴포넌트를 동기화할 수 있다.
- 렌더링 자체에 의해 발생하는 부수 효과를 특정하는 것으로, 특정 이벤트가 아닌 렌더링에 의해 직접 발생한다.
Effect 작성
- Effect 선언
- 기본적으로 Effect는 모든 commit 이후에 실행된다.
- Effect 의존성 지정
- 대부분의 Effect는 필요할 때만 다시 실행되어야 하므로 의존성을 지정해야 한다.
- 필요한 경우 클린업 함수 추가
- 일부 Effect는 수행 중이던 작업을 중지, 취소 또는 정리하는 방법을 지정하려면 Effect에서 클린업 함수(cleanup function)를 반환한다.
Effect 선언
- useEffect 훅을 import한 후, 컴포넌트 최상위 레벨에서 호출한다.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
});
return <div />;
}
- useEffect는 화면에 렌더링이 반영될 때까지 코드 실행을 지연시킨다.
Effect 의존성 지정
- React에게 Effect를 불필요하게 다시 실행하지 않도록 지시하려면 useEffect 호출의 두 번째 인자로 의존성 배열을 지정해야 한다.
useEffect(() => {
}, []);
- Effect 내부의 코드가 어떤 작업을 수행할지 결정하기 위해 prop에 의존하지만 이 의존성이 명시적으로 선언되지 않으면 에러가 발생하므로 의존성 배열에 추가해야한다.
useEffect(() => {
if (isPlaying) {
} else {
}
}, [isPlaying]);
- 이전 렌더링 중에 isPlaying이 이전과 동일하다면 Effect를 다시 실행하지 않는다.
필요 시 클린업 추가
- 클린업 함수
- Effect가 어떤 것을 구독한다면, 클린업 함수에서 구독을 해지해야 한다.
- Effect가 어떤 요소를 애니메이션으로 표시하는 경우, 클린업 함수에서 애니메이션을 초기 값으로 재설정해야 한다.
- Effect가 어떤 데이터를 가져온다면, 클린업 함수에서는 fetch를 중단하거나 결과를 무시해야 한다.
사용자에게 표시될 때 채팅 서버에 연결해야하는 컴포넌트를 예시로 보자.
- Effect 작성
useEffect(() => {
const connection = createConnection();
connection.connect();
}, []);
- 매번 재렌더링 후 채팅 서버에 연결하는 것은 느리므로 의존성 배열을 추가한다.
- Effect 내부의 코드는 어떠한 props나 상태도 사용하지 않으므로, 의존성 배열은 [] (빈 배열)이다.
- React는 이 코드를 컴포넌트가 “마운트”될 때만 실행한다.(즉, 화면에 처음으로 나타날 때에만 실행된다.)
import { useEffect } from 'react';
import { createConnection } from './chat.js';
export default function ChatRoom() {
useEffect(() => {
const connection = createConnection();
connection.connect();
}, []);
return <h1>채팅에 오신걸 환영합니다!</h1>;
}
export function createConnection() {
return {
connect() {
console.log('✅ 연결 중...');
},
disconnect() {
console.log('❌ 연결이 끊겼습니다.');
}
};
}
- 컴포넌트가 마운트 해제될 때 연결을 닫지 않기 때문에 사용자가 앱을 탐색하는 동안 연결은 종료되지 않고 계속 쌓인다.
- 따라서, 클린업 함수를 반환해야 한다.
useEffect(() => {
const connection = createConnection();
connection.connect();
return () => {
connection.disconnect();
};
}, []);
- 개발 모드에서는 React가 개발 중에 코드를 검사하여 버그를 찾는 과정에서 연결/해제 호출이 하나 더 있으므로 한번 더 출력 될 것이지만, 배포 환경에서는 정상적으로 동작할 것이다.
불필요한 Effect 제거
- 렌더링을 위해 데이터를 변환하는 데 Effect가 필요하지 않다.
- 사용자 이벤트를 처리하는 데 Effect가 필요하지 않다.
Effect의 생명주기
- 각 effect는 주변 컴포넌트와 별도의 생명주기를 가진다.
- 각 effect는 시작 및 중지할 수 있는 별도의 동기화 프로세스를 나타낸다.
- effect를 작성하고 읽을 때는 컴포넌트의 관점(마운트, 업데이트 또는 마운트 해제 방법)이 아닌 개별 effect의 관점(동기화 시작 및 중지 방법)에서 생각해야 한다.
- 컴포넌트 본문 내부에 선언된 값은 “반응형”이며 반응형 값은 시간이 지남에 따라 변경될 수 있으므로 effect를 다시 동기화해야 한다.
- 린터는 effect 내부에서 사용된 모든 반응형 값이 종속성으로 지정되었는지 확인한다.
- 린터에 의해 플래그가 지정된 모든 오류는 합법적인 오류이므로 규칙을 위반하지 않도록 코드를 수정해야 한다.
React 컴포넌트의 생명주기
- 컴포넌트는 화면에 추가될 때 마운트된다.
- 컴포넌트는 일반적으로 상호작용에 대한 응답으로 새로운 props나 state를 수신하면 업데이트된다.
- 컴포넌트가 화면에서 제거되면 마운트가 해제된다.
Effect와 이벤트
- 이벤트 핸들러는 특정 상호작용에 대한 응답으로 실행된다.
- Effect는 동기화가 필요할 때마다 실행된다.
- 이벤트 핸들러 내부의 로직은 반응형이 아니다.
- Effect 내부의 로직은 반응형이다.
- Effect의 비반응형 로직은 Effect 이벤트로 옮길 수 있다.
- Effect 이벤트는 Effect 내부에서만 호출해야 한다.
- Effect 이벤트를 다른 컴포넌트나 Hook에 전달하면 안된다.
Effect의 의존성 제거
- 의존성은 항상 코드와 일치해야 한다.
- 의존성이 마음에 들지 않으면 코드를 수정해야 한다.
- 린터를 억제하면 매우 혼란스러운 버그가 발생하므로 항상 피해야 한다.
- 의존성을 제거하려면 해당 의존성이 필요하지 않다는 것을 린터에게 “증명”해야 한다.
- 특정 상호작용에 대한 응답으로 일부 코드가 실행되어야 하는 경우 해당 코드를 이벤트 핸들러로 이동해야 한다.
- Effect의 다른 부분이 다른 이유로 다시 실행되어야 하는 경우 여러 개의 Effect로 분할해야 한다.
- 이전 State를 기반으로 일부 State를 업데이트하려면 업데이터 함수를 전달하면 된다.
- ”반응”하지 않고 최신 값을 읽으려면 Effect에서 Effect 이벤트를 추출하면 된다.