Effect로 동기화하기 — 라이프사이클이 아닌 동기화 모델 · 퀴즈

7 문항 · Bloom: Understand:3, Apply:2, Analyze:2

Q1 Understand mcq_single

이 섹션은 useEffect를 'mount/update/unmount 라이프사이클 훅'이 아니라 '외부 시스템과의 동기화 도구'로 재정의했다. 이 재프레이밍을 가장 잘 설명하는 문장은?

정답: B
S3.C1의 핵심 프레이밍이다. Effect의 의미는 '시점(언제)'이 아니라 '관계(무엇과 무엇을 일치시키는가)'에 있다.
오답 해설:
  • A. 메시지 한 번 보내기는 이벤트(handler)에 해당한다. Effect는 '연결 유지'다.
  • C. deps에 따라 cleanup → 재실행이 반복될 수 있으므로 '한 번만'은 틀리다.
  • D. 이것이 바로 이 섹션이 버리라고 한 라이프사이클 멘탈모델이다.
Q2 Understand mcq_single

다음 useEffect 의존성 배열 형태와 콜백 실행 시점을 올바르게 짝지은 것은?

정답: B
S3.C2 시연 그대로다. React는 deps 각 항목을 Object.is로 이전 렌더와 비교한다.
오답 해설:
  • A. 생략과 []의 의미가 정반대로 뒤바뀌었다.
  • C. []도 마운트 시 1회 실행되며, [a,b]는 둘 중 하나만 바뀌어도 재실행된다.
  • D. deps는 boolean 조건이 아니라 변경 감지 키다.
Q3 Apply mcq_single

다음 코드를 fetch race condition으로부터 안전하게 만들기 위해 빈 칸 (1)(2)에 들어갈 코드는? ```js useEffect(() => { let ignore = false; fetchResults(query, page).then(json => { if (___(1)___) { setResults(json); } }); return () => { ___(2)___ }; }, [query, page]); ```

정답: B
ignore는 false로 시작하고, deps 변경/unmount 시 cleanup이 ignore = true로 바꾸면 늦게 도착한 이전 fetch의 응답이 setResults를 호출하지 못하게 막는다. 즉 setResults는 !ignore일 때만 적용한다.
오답 해설:
  • A. (1)이 `ignore === true`면 무시되어야 할 응답에만 적용하는 셈이라 의미가 정반대다.
  • C. cleanup에서 ignore를 false로 두면 플래그가 한 번도 켜지지 않아 race를 막지 못한다.
  • D. (1)이 `ignore`면 무시 대상 응답을 채택하는 꼴이라 race 보호 방향이 거꾸로다.
Q4 Analyze mcq_multi

cleanup 함수를 올바르게 작성한 Effect를 모두 고르시오. (정답 2개)

정답: A, C
A는 connect/disconnect 한 쌍, C는 setInterval/clearInterval 한 쌍이 갖춰져 있다. '연결 → 끊기'가 짝을 이뤄야 동기화가 닫힌다.
오답 해설:
  • B. addEventListener가 있는데 removeEventListener가 없어 누수가 발생한다.
  • D. url이 바뀌면 이전 fetch의 응답이 늦게 도착해 race condition이 생긴다 — ignore flag가 필요.
  • E. connect만 하고 disconnect 없음 → roomId 변경 시 좀비 연결이 누적된다.
Q5 Understand true_false

참/거짓: 개발 모드에서 Strict Mode가 Effect를 두 번 실행해 chat connection이 두 번 연결되는 로그가 보이는 것은 React의 버그이며, 프로덕션에서도 동일한 문제가 발생한다.

정답: B
거짓이다. Strict Mode의 dev 더블 마운트는 'cleanup이 정확한지 자동 검증'하는 의도된 동작으로, 프로덕션에서는 한 번만 실행된다. 두 번 연결처럼 보인다면 그것은 cleanup이 부실하다는 신호다.
오답 해설:
  • A. dev 더블 마운트는 의도된 검증 장치이며 프로덕션에는 적용되지 않는다.
Q6 Analyze mcq_single

한 개발자가 dev에서 Effect가 두 번 실행되는 것이 보기 싫어 다음과 같이 ref guard로 막았다. 이 처방의 가장 큰 문제는? ```js const didMount = useRef(false); useEffect(() => { if (didMount.current) return; didMount.current = true; connection.connect(); return () => connection.disconnect(); }, []); ```

정답: C
S3.C3의 핵심이다. ref guard는 dev 로그의 증상만 가리고, 진짜 unmount/remount 시나리오에서 cleanup의 정확성을 검증할 기회를 잃게 만들어 실제 누수를 숨긴다. 옳은 처방은 cleanup을 견고하게 만드는 것.
오답 해설:
  • A. ref 할당은 Effect 안에서 일어나고 있어 렌더 중 mutation은 아니다.
  • B. Guard 자체에는 useRef가 적절하다 — 문제는 guard라는 접근법 자체다.
  • D. Strict Mode와 무관하게 mount 시 connect는 한 번 실행된다.
Q7 Apply short_answer

다음 Effect의 문제점을 두 가지 짚고, deps와 cleanup을 어떻게 고쳐야 하는지 1~2줄로 설명하시오. ```js useEffect(() => { const conn = createConnection(serverUrl, roomId); conn.connect(); }, []); ```

모범 답안 요지: (1) deps에 [serverUrl, roomId]가 빠져 roomId/serverUrl이 바뀌어도 재동기화되지 않는다(stale 연결). (2) cleanup이 없어 unmount/재실행 시 conn.disconnect()가 호출되지 않아 좀비 연결·중복 구독이 누적된다. 수정: `return () => conn.disconnect();`를 추가하고 deps를 `[serverUrl, roomId]`로 바꾼다.채점 기준:
  • full_credit
  • partial_credit
  • no_credit