1. useEffect: 컴포넌트의 라이프사이클 제어
useEffect()는 컴포넌트가 렌더링된 이후에 실행되는 코드를 작성할 때 사용합니다.
ex) API 호출, 이벤트 등록, 타이머 설정 등
✅ 의존성 배열이란?
useEffect(() => {
// effect 내부 코드
}, [의존성]);
- 이 배열에 있는 값이 변할 때마다 useEffect 내부 코드가 실행됩니다.
🧠 상황별 정리
1. [] (빈 배열)
useEffect(() => {
console.log('✅ 마운트될 때만 실행');
return () => {
console.log('🧹 언마운트될 때 정리');
};
}, []);
- 컴포넌트가 처음 렌더링될 때 한 번만 실행
- 배열 안에 아무 의존성도 없으므로, 이후 업데이트 시 실행되지 않음
- return은 언마운트 시 한 번 실행됨 (정리 함수)
2. [value]
const [value, setValue] = useState(false);
useEffect(() => {
console.log('🔄 value가 바뀔 때마다 실행됨:', value);
}, [value]);
- value가 false → true 등 값이 바뀔 때마다 effect 실행
- 의존성 배열이 변경될 때마다 useEffect 내부 코드가 다시 실행
- 이건 아주 흔히 사용하는 방식 (ex: 사용자 입력 감지, 상태 기반 동작 등)
3. value가 boolean, string, number 다 가능할까?
✅ 네! 모두 가능합니다.
useEffect는 의존성 배열 안에 어떤 JavaScript 변수든 올 수 있어요, 예를 들어:
const [isModalOpen, setIsModalOpen] = useState(false);
const [username, setUsername] = useState('');
const [count, setCount] = useState(0);
useEffect(() => {
console.log('🔥 특정 값이 바뀔 때마다 실행');
}, [isModalOpen, username, count]);
- Boolean (true / false)
- String ('hello')
- Number (1, 0)
모두 의존성 배열에 쓸 수 있고, 이 값이 이전 렌더링과 다르면 useEffect가 실행됩니다.
📦 예제 코드
function Example() {
const [visible, setVisible] = useState(false);
useEffect(() => {
console.log('📢 visible 상태가 바뀜:', visible);
return () => {
console.log('🧼 visible이 바뀌기 전 cleanup');
};
}, [visible]);
return (
<button onClick={() => setVisible((prev) => !prev)}>
{visible ? '숨기기' : '보이기'}
</button>
);
}
2. 컴포넌트 생명주기
컴포넌트는 React 앱에서 화면에 나타났다 사라지는 과정에서 특정 타이밍마다 동작할 수 있는 기회를 제공합니다.
이 과정을 컴포넌트 생명주기(lifecycle) 라고 부릅니다.
함수형 컴포넌트에서는 이 흐름을 useEffect 훅으로 컨트롤합니다.
🔄 생명주기의 3단계
1️⃣ Mount (마운트 – 생성)
컴포넌트가 처음 화면에 나타날 때 발생합니다.
→ 렌더링 후 한 번 실행됨
💡 이 시점에 보통 다음 작업을 합니다:
- API 요청
- 이벤트 리스너 등록
- 타이머 시작
- 초기 상태 설정 등
useEffect(() => {
console.log('✅ 마운트 시 실행 (한 번만)');
// 예: API 호출
fetchData();
}, []); // 빈 배열: 마운트 시 한 번만 실행됨
2️⃣ Update (업데이트 – 다시 렌더링)
컴포넌트의 props 또는 state가 변경되면 다시 렌더링되며, 그때마다 실행됩니다.
💡 주로 다음 상황에서 사용합니다:
- 특정 state나 props 값이 바뀌었을 때 후속 작업
- 상태 변화에 따른 사이드 이펙트
const [count, setCount] = useState(0);
useEffect(() => {
console.log('🔁 count가 바뀔 때마다 실행:', count);
}, [count]); // count가 바뀔 때만 실행됨
🚨 의존성 배열이 없으면 모든 렌더링마다 실행되어 성능 이슈가 생깁니다.
3️⃣ Unmount (언마운트 – 제거)
컴포넌트가 화면에서 사라질 때 발생합니다.
→ useEffect의 cleanup(return 함수) 안에서 처리합니다.
💡 예시:
- 타이머 제거
- 이벤트 리스너 해제
- 메모리 정리 등
useEffect(() => {
const interval = setInterval(() => {
console.log('⏱ 타이머 동작 중...');
}, 1000);
return () => {
clearInterval(interval); // 🔥 언마운트 시 타이머 정리
console.log('🧹 언마운트됨: 정리 완료');
};
}, []);
⚙️ React 18 이후: 클래스 vs 함수형 변화
구분 | 클래스 컴포넌트 (이전) | 함수형 컴포넌트 (현재) |
Mount | componentDidMount() | useEffect(() => {}, []) |
Update | componentDidUpdate() | useEffect(() => {}, [deps]) |
Unmount | componentWillUnmount() | useEffect(() => { return () => {} }, []) |
🎯 React 18 이후에는 함수형 컴포넌트 + 훅이 공식 표준
→ 클래스 컴포넌트는 더 이상 권장되지 않음
3. 컴포넌트 분리와 재사용
큰 컴포넌트를 계속 만들면 가독성, 유지보수, 테스트가 어려워집니다.
역할 단위로 분리해서 작고 재사용 가능한 컴포넌트로 쪼개세요.
// Too Big
<ProfileCard user={user} />
// 분리
<Avatar src={user.avatarUrl} />
<ProfileInfo name={user.name} />
<FollowButton userId={user.id} />
👉 기능별로 나누고, props로 조립하면 훨씬 유연해집니다.
4. 폼 다루기
입력폼은 대부분 state로 제어되는 컴포넌트입니다.
const [name, setName] = useState('');
return (
<input value={name} onChange={e => setName(e.target.value)} />
);
- value는 입력값 상태
- onChange로 사용자 입력을 받아 setState
폼 제출 처리 예시:
const handleSubmit = (e) => {
e.preventDefault();
console.log('폼 제출:', name);
};
form 전송 시 preventDefault() 꼭 써야 새로고침 방지됩니다.
5. useRef: 렌더링 없이 상태 저장 or DOM 제어
useRef()는 두 가지 주요 용도로 사용됩니다:
📌 1. DOM 직접 접근
const inputRef = useRef(null);
return (
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>포커스</button>
);
📌 2. 렌더링 없이 값 저장
const countRef = useRef(0);
countRef.current += 1; // 값은 저장되지만 렌더링 안 됨
컴포넌트 내에서 변수처럼 쓸 수 있지만, 렌더링에 영향을 주지 않는 값을 저장할 때 매우 유용합니다.
6. 컨테이너 vs 프리젠테이셔널 컴포넌트
역할에 따라 컴포넌트를 분리하는 아키텍처 패턴입니다.
컨테이너 컴포넌트 | 데이터 처리, 상태 관리, 로직 처리 |
프리젠테이셔널 컴포넌트 | UI 렌더링만 담당, props로 데이터 받음 |
예시:
// 컨테이너
function UserContainer() {
const user = useUser(); // 훅으로 데이터 fetch
return <UserProfile user={user} />;
}
// 프리젠테이션
function UserProfile({ user }) {
return <p>{user.name}</p>;
}
이 패턴을 적용하면 테스트 가능성, 재사용성, 역할 분담이 명확해져 실무에서 많이 사용됩니다.
7. Props Drilling
상위 → 중간 → 하위 컴포넌트로 계속 props를 전달하는 현상입니다.
<GrandParent>
<Parent>
<Child someData={someData} />
</Parent>
</GrandParent>
- 중간 컴포넌트는 실제로 someData를 쓰지도 않는데, 전달만 하는 역할을 하게 됨
- 이게 반복되면 구조가 복잡해지고 유지보수가 어려워짐
해결 방법?
- useContext를 쓰면 전역으로 값을 공유할 수 있음
- 또는 상태관리 라이브러리(Zustand, Redux 등) 사용
'React Native' 카테고리의 다른 글
RN + Native (1) | 2025.06.24 |
---|---|
🥇 리액트 기본기 (반드시 알아야 할 핵심) (0) | 2025.06.20 |
Query vs Mutation (0) | 2025.06.20 |
코드 푸쉬 (0) | 2025.06.20 |
React Native IOS 기본 명령어 (0) | 2025.06.17 |