본문 바로가기
React Native

🥈 리액트 심화 개념

by DeveloperM 2025. 6. 20.

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