종합 — 안티패턴 진단과 통합 케이스 스터디 · 퀴즈

7 문항 · Bloom: Analyze:2, Evaluate:4, Create:1 · v1.0.0

Q1 Analyze mcq_multi

다음 컴포넌트 코드 조각에 박혀 있는 안티패턴을 모두 고르시오. (정답 3개) ```jsx function ProfileCard({ user, theme }) { const [name, setName] = useState(user.name); // (a) const [fullName, setFullName] = useState(''); // (b) 항상 firstName + ' ' + lastName 으로 채움 function Avatar({ src }) { // (c) return <img src={src} className="avatar" />; } return ( <ThemeContext.Provider value={theme}> {/* (d) 단 한 곳에서만 쓰임 */} <Avatar src={user.avatar} /> <h2>{fullName}</h2> </ThemeContext.Provider> ); } ```

정답: A, B, C
(a) props mirroring은 부모가 prop을 갱신해도 자식 state가 동기화되지 않는 고전 버그(S2.4). (b) fullName은 두 prop으로 매 렌더 계산 가능하므로 state가 아닌 파생값이어야 한다(S2.1). (c) 함수 컴포넌트 안에 컴포넌트를 정의하면 매 렌더마다 새 함수 reference가 생겨 React가 다른 타입으로 보고 자식 state를 리셋한다(S4.4).
오답 해설:
  • D. theming은 S6에서 context의 정당한 사용 사례로 명시됐다. 이 코드의 진짜 문제는 'context를 썼다'가 아니라 다른 세 가지다. (단 한 곳에서만 쓰인다는 점은 prop drilling이 아닐 수 있어 별도 논의 대상이지만, 5대 안티패턴 매핑상 (a)(b)(c)가 명백한 1순위다.)
  • E. useState는 한 컴포넌트에서 여러 번 호출 가능하다. 흔한 입문자 오개념이지만 React 규칙에 어긋나지 않는다.
Q2 Analyze mcq_single

코드 리뷰에서 다음 패턴을 발견했다. 가장 적절한 진단과 처방 매핑은? ```jsx function Counter({ initialCount }) { const [count, setCount] = useState(initialCount); // 부모가 initialCount를 바꿔도 count는 안 바뀐다는 버그 리포트가 들어옴 } ```

정답: B
이 코드는 prop을 useState 초기값으로 한 번만 복사한 뒤 동기화하지 않는 전형적인 mirroring 안티패턴이다(S2.4). 해법은 두 가지: (1) 'initial' 접두사로 의도를 드러내고 리셋이 필요할 때 부모에서 key prop을 바꿔 컴포넌트를 의도적으로 리셋(S4.3), (2) state를 들고 있을 필요가 없다면 props를 직접 쓴다.
오답 해설:
  • A. redundant state는 '다른 state/props로 계산 가능한 값을 별도 state로 둘 때'다. 이 코드는 count가 별도 사용자 입력으로 바뀌므로 redundant가 아니다. 'props에서 직접 사용'은 부분적으로 맞지만 안티패턴 명칭이 틀렸다.
  • C. 코드에 중첩 정의가 없다.
  • D. context는 등장하지도 않는다.
Q3 Evaluate mcq_single

mini Kanban 앱 설계 첫 단계에서 카드 데이터 구조를 정한다. 컬럼 3개, 컬럼당 카드 수십 개가 들어가고, 카드를 다른 컬럼으로 이동·삭제하는 연산이 잦다. 어떤 state shape이 가장 적절한가?

정답: B
S2.5의 정규화 원칙: 깊게 중첩된 트리는 업데이트가 어렵고 중복(같은 카드를 두 컬럼에서 참조하는 등) 시 동기화 버그를 유발한다. id-맵 + childIds 패턴이 이동·삭제·재정렬 연산을 단순화한다.
오답 해설:
  • A. 트리 그대로 보관하면 카드를 한 컬럼에서 빼서 다른 컬럼에 넣을 때 중첩 업데이트(spread 중첩)가 필요하고 불변성 유지가 까다롭다(S2.5 위반).
  • C. 텍스트 prefix는 데이터를 문자열에 인코딩하는 안티패턴 — 파싱 비용·오타 버그·타입 안전성 모두 손실.
  • D. 각 컬럼이 따로 들고 있으면 카드 이동 시 두 컴포넌트가 동기화되어야 하므로 single source of truth(S3.3)에 위배된다.
Q4 Evaluate mcq_single

mini Kanban에서 state shape을 정한 뒤, 'state를 어디에 둘 것인가'를 결정해야 한다. 현재 트리는 App > Board > Column > Card 구조이고, 카드는 컬럼 사이를 이동한다. single source of truth 관점에서 가장 적절한 결정은?

정답: C
카드 이동은 두 컬럼 모두에 영향을 주므로 두 컬럼의 '가장 가까운 공통 부모'인 Board가 state를 소유해야 한다(S3.1, S3.3). lifting state up의 표준 적용.
오답 해설:
  • A. 각 Column이 따로 들고 이벤트로 동기화하면 단일 진실 공급원이 깨지고 race condition이 발생한다(S3 위반).
  • B. Card가 자기 columnId를 들면 Board는 카드가 어느 컬럼에 있는지 모르게 되어 일관된 렌더가 불가능하다.
  • D. import해서 mutate는 React 데이터 흐름(단방향, 불변) 자체를 깨는 행위다.
Q5 Evaluate mcq_single

Board가 state를 소유하기로 했더니, 카드 추가/삭제/이동/제목수정/컬럼추가까지 setState 호출이 7개로 늘어났고 핸들러가 Board 전반에 흩어졌다. 다음 중 가장 적절한 다음 단계는?

정답: B
S5.4의 트레이드오프: 상호작용이 많고 상태 전이 로직이 복잡해지면 reducer가 더 읽기 쉽고 테스트 가능하다. action.type 컨벤션(S5.3)도 함께 적용된다.
오답 해설:
  • A. 흩어진 setState는 race condition과 중복 갱신 버그를 유발하기 쉽다 — useReducer의 도입 임계점을 지났다.
  • C. useState 한 개로 묶어도 dispatch가 없으면 핸들러는 여전히 흩어진다. 가독성 개선이 미미하다.
  • D. Redux는 미들웨어·devtools·여러 도메인 동기화가 필요할 때(S7.4) 도입한다. 단일 도메인 reducer면 useReducer로 충분하다.
Q6 Evaluate mcq_single

reducer 도입 후, Board → Column → Card 트리에서 Card가 dispatch를 호출하려면 Board에서 Column을 거쳐 Card까지 dispatch prop을 3단계로 내려보내야 한다. 그리고 같은 트리 깊은 곳에 있는 'CardEditor 모달'도 dispatch를 필요로 한다. 가장 적절한 처방은?

정답: B
S7.1~S7.2의 정확한 패턴: state와 dispatch를 한 context에 묶으면 dispatch만 필요한 컴포넌트도 state 변경 시 불필요하게 재렌더된다. 두 context로 분리하면 dispatch는 안정 reference라 dispatch 전용 소비자는 재렌더되지 않는다. 커스텀 훅으로 응집해 사용처에서 깔끔하게 노출한다.
오답 해설:
  • A. 한 context에 묶으면 동작은 하지만 재렌더 최적화와 관심사 분리에서 (B)에 밀린다(S7.1).
  • C. 전역 변수 export는 React 데이터 흐름을 깨고 SSR·테스트에서 누수된다.
  • D. context는 prop drilling을 해소하는 정당한 도구다(S6.4). '무조건 나쁘다'는 흔한 오개념.
Q7 Create short_answer

mini Kanban 위에 (1) 사용자 인증(현재 로그인 사용자 정보), (2) 다크/라이트 테마, (3) 카드 데이터 — 세 도메인이 추가됐다. reducer+context 패턴을 어떻게 확장할지 도메인별 결정과 근거를 3~5문장으로 서술하시오. (강의의 어떤 원칙을 적용했는지 최소 2개 이상 명시)

정답: rubric
모범 답안 골자: 도메인별로 reducer+context 쌍을 분리(TasksProvider / AuthProvider / ThemeProvider)하고 Provider를 트리 상단에 중첩 배치. Tasks는 액션이 많으므로 state/dispatch context 분리(S7.1). Auth는 로그인/로그아웃 두 액션이라 reducer는 가볍게, 또는 useState로도 충분(S5.4 트레이드오프). Theme는 값이 거의 안 바뀌고 dispatch도 단순하므로 단일 context로 충분(S6.4 — context의 정당한 사례). 도메인 경계는 '함께 갱신되는가'(S2.2)로 판단 — 카드 갱신이 인증을 건드리지 않으므로 분리.채점 기준:
  • full_credit_3pt: 세 도메인을 별도 reducer+context로 분리하고, 각 도메인의 결정 근거를 강의 원칙(S7.1 분리/S5.4 트레이드오프/S6.4 context 적합성/S2.2 묶기 기준 중 2개 이상)으로 명시함.
  • partial_2pt: 분리 결정은 옳지만 근거 원칙이 1개만 명시되거나 도메인 중 하나의 처방이 부정확.
  • partial_1pt: 단일 context에 모두 묶거나, 분리는 했지만 근거가 '그냥 깔끔해서' 수준.
  • zero: Redux 같은 외부 라이브러리만 답하거나, prop drilling 유지를 정당화함.