2025. 4. 10. 12:56ㆍCoding Study/React
1. Componet 의 생명 주기
1) Mount : 화면이 처음 렌더링 될 때
2) Update : 화면이 리렌더링 될 때
3) unMount : 화면이 제거 될 때
3가지의 상황을 useEffect 를 활용하면 처리가 가능 하다.
2. useEffect
1) 함수 기본 구조
(1) 은 화면에서 사라질때 실항할 코드를 작성한다.
(2)의 의존성 배열은 재 랜더링 시에 값이 달라지면 useEffect 내부의 코드를 실행한다.
useEffect(() => {
// 실행할 코드
return () => {
// 언마운트 시 실행할 정리(clean-up) 함수 (1)
};
}, [의존성배열]); // (2)
return 부와 의존성 배열은 선택 사항으로 작성해도 되고 안해도 된다.
2) 아무 배열도 전달하지 않을때
useEffect(() => {
// 실행할 코드
});
🔁 → 마운트 + 모든 리렌더링 시마다 실행됨
3) 빈 배열 전달
useEffect(() => {
console.log('처음 렌더링될 때만 실행됨');
}, []);
✅ → 한 번만 실행 (Mount 시점)
4) 특정 값만 바뀔 때 실행
useEffect(() => {
console.log('count가 바뀔 때만 실행됨');
}, [count]);
🌀 → count 값이 바뀔 때만 실행
5) 컴포넌트 언마운트 시 정리 작업
useEffect(() => {
console.log('컴포넌트 마운트됨');
return () => {
console.log('컴포넌트 언마운트됨');
};
}, []);
🧹 → 처음 렌더링 시 실행 후, 화면에서 사라질 때 return 함수 실행
클린업 리턴 함수 작성 시 의존성 배열의 유무에서 작동 차이
의존성 배열 있을 시
useEffect(() => {
return () => {
console.log("언마운트");
};
}, []);
이 경우에는 의존성 배열이 빈 배열이기 때문에, 해당 useEffect는 컴포넌트가 마운트될 때 한 번 실행되고, 그 안의 return 함수는 언마운트될 때만 실행됩니다. 즉, 컴포넌트 생애주기 중 "cleanup" 역할을 합니다.
재렌더링 (Update) 될 때에는 실행 되지 않는다.
의존성 배열 없을 시
useEffect(() => {
return () => {
console.log("언마운트");
};
});
이 경우에는 의존성 배열이 아예 없기 때문에, React는 이 useEffect를 모든 렌더링(업데이트) 후에 실행
그리고 그 전에 기존의 이펙트를 클린업하기 위해 return 함수를 먼저 실행
<코드 동작 순서>
- 마운트 후 useEffect 실행
- 리렌더링 전 useEffect 의 return 실행
- 리렌더링 후 다시 useEffect 실행
.
.
.
최종 unmount 시 return 부실행
패치시 useEffect을 자주 사용하는데 useState 의 초기 값으로 콜백에 패치를 적용해도 1번만 패치되는거니까 사용해도 되는거 아닌가?
useEffect 사용하여 데이터 fetch 시 일반적으로 아래와 같이 코드를 작성한다.
const [data, setData] = useState<SomeType[]>([]);
useEffect(() => {
fetch("/api/data")
.then((res) => res.json())
.then(setData);
}, []);
여기서 "의존성 배열이 빈 []"이니까 컴포넌트 마운트 시 딱 한 번만 실행되고
"그럼 useState의 초기값으로 fetch를 호출하면 똑같이 한 번만 실행되는 거 아닌가?" 라는 의문이 생김.
이론적으로는 아래와 같이 useState의 초기값으로 패치를 적용 가능 하다.
const [data, setData] = useState<SomeType[]>(() => {
fetch("/api/data")
.then((res) => res.json())
.then((result) => {
console.log(result);
});
return [];
});
⚠️ 하지만, 그렇게 하면 안 되는 이유
실무나 리액트 설계 철학에서 이 방식은 권장되지 않는다.
1. 데이터 패칭은 사이드 이펙트
- React의 렌더링은 “순수해야” 한다.
즉, 렌더링 시 외부 세계(네트워크, 로컬스토리지 등) 에 영향을 주면 안 된다는 규칙이 있다. - useState의 초기값 콜백은 렌더링 과정에서 실행되기 때문에,
거기서 fetch를 호출하면 렌더링 → 네트워크 요청이라는 부작용(side effect) 이 생겨버린다. - 그래서 React 팀에서도 이런 사용은 안티패턴으로 본다.
2. StrictMode(개발 모드) 에서는 두 번 실행될 수 있다
- React 18 이후 StrictMode가 활성화되어 있으면,
렌더링 과정에서 일부 함수들이 의도적으로 두 번 호출돼서 “부작용 감지”를 한다. - 이때 useState의 lazy initializer 안에 fetch가 있으면,
“한 번만 실행돼야 하는 네트워크 요청”이 두 번 발생할 수 있다 ❌
3. 에러 처리, 로딩 상태 관리가 어렵다
- useEffect 내부에서는 loading, error 상태를 별도로 다루기 쉬운데,
useState 초기화에서는 로딩 상태 분리나 cleanup을 하기 어렵다.
컴포넌트 내 직접 실행 과 useEffect 내부 실행의 차이
function Component() {
console.log("렌더링됨")
}
function Component() {
useEffect(()=>{
console.log("렌더링됨")
})
}
위의 두 코드의 차이점
1. useEffect 는 클라이언트 사이드에서 실행되는 것을 보장한다. 즉 useEffect 내부에서는 window 객체 접근이 가능하다.
2. useEffect 는 컴포넌트 렌더링의 부수효과 이며 렌더링이 완료된 이후에 실행된다.
useEffect 첫 번째 인수에 함수명 부여 하자
useEffect 의 수가 적거나 복잡성이 낮다면 익명함수를 사용해도 문제가 없다.
그러나 useEffect의 코드가 복잡하고 많아질수록 무슨 일을 하는 useEffect 코드인지 파악하기 힘들어진다.
이때 기명함수를 적용하면 useEffect 의 목적을 파악하기가 쉬워진다.
useEffect(
function logActiveUser(){
logging(user.id)
}
},[user.id])
useEffect 에 비동기 함수를 바로 넣을 수 없는 이유?
useEffect(async()=>{
const response = await fetch('http://some.data.com')
const result = await response.json()
setData(result)
},[])
위의 코드를 실행 하면 에러가 발생한다.
⚠️ 참고: 왜 에러가 발생하는가?
다시 한번 상기시켜 드리자면, React의 $useEffect는 **클린업 함수(Cleanup Function)**를 반환할 수 있어야 하는데, async 함수는 항상 Promise를 반환합니다. React는 Promise를 유효한 클린업 함수로 인식하지 못하기 때문에 다음과 같은 경고(Warning) 또는 에러를 발생시킵니다.
Warning: An effect function must not return anything besides a function, which is used as its cleanup. It looks like you wrote 'async function' which returns a Promise.
만약 useEffect 가 비동기 함수가 사용 가능 하다면 비동기 함수의 응답속도에 따라 결과가 이상하게 나타날 수 있다. 첫번째 응답이 10초뒤에 나올 예정이고 두번째 응답이 1초가 걸린다면 응답이 늦게 나온 첫번째 값의 기반으로 결과가 나타날 수 있다.
이런 문제를 useEffect의 경쟁상태( race condtion ) 이라고 한다.
useEffect 내의 올바른 비동기 함수 실행 방법
useEffect(() => {
const fetchData = async () => { // 비동기 함수를 내부에서 정의
const response = await fetch('http://some.data.com');
const result = await response.json();
setData(result);
};
fetchData(); // 즉시 호출
// cleanup 함수를 반환할 수 있음 (선택 사항)
// return () => { /* cleanup code */ };
}, []);
✨ 마무리 요약
| useEffect(() => {}) | 마운트 + 모든 리렌더링 시 실행 |
| useEffect(() => {}, []) | 마운트 시 딱 1번 실행 |
| useEffect(() => {}, [value]) | value 변경 시 실행 |
| useEffect(() => { return () => {} }, []) | 언마운트 시 실행 |
'Coding Study > React' 카테고리의 다른 글
| SCSS - React 에서 스타일링 하기 (1) | 2025.04.15 |
|---|---|
| Custom Hook (0) | 2025.04.11 |
| React Router 를 활용한 동물정보 사이트 만들기 (0) | 2025.04.09 |
| React Router (0) | 2025.04.09 |
| useReducer (0) | 2025.04.08 |