From f8a8fcfbea4f98818a304982a643f916a419cfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Tue, 2 Jun 2026 20:34:24 +0900 Subject: [PATCH 1/5] docs: update `lifecycle-of-reactive-effects` --- .../learn/lifecycle-of-reactive-effects.md | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 0a1d9ff78..5c62d841f 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -14,7 +14,7 @@ effects는 컴포넌트와 다른 생명주기를 가집니다. 컴포넌트는 - 각 effect를 개별적으로 생각하는 방법 - effect를 다시 동기화해야 하는 시기와 그 이유 - effect의 의존성이 결정되는 방법 -- 값이 유동적이라는 것의 의미 +- 값이 반응형이라는 것의 의미 - 빈 의존성 배열이 의미하는 것 - React가 린터로 의존성이 올바른지 확인하는 방법 - 린터에 동의하지 않을 때 해야 할 일 @@ -169,7 +169,7 @@ function ChatRoom({ roomId /* "travel" */ }) { 1. `roomId`가 `"general"`으로 설정되어 마운트된 `ChatRoom` 2. `roomId`가 `"travel"`으로 설정되어 업데이트된 `ChatRoom` 3. `roomId`가 `"music"`으로 설정되어 업데이트된 `ChatRoom` -3. 마운트 해제된 `ChatRoom` +4. 마운트 해제된 `ChatRoom` 컴포넌트의 생명주기에서 이러한 각 시점에서 effect는 다른 작업을 수행했습니다. @@ -204,7 +204,7 @@ function ChatRoom({ roomId /* "travel" */ }) { 이렇게 하면 JSX를 생성하는 렌더링 로직을 작성할 때 컴포넌트가 마운트되는지 업데이트되는지 생각하지 않는 방법을 떠올릴 수 있습니다. 화면에 무엇이 표시되어야 하는지 설명하면 [나머지는 React가 알아서 처리합니다.](/learn/reacting-to-input-with-state) -### React가 effect를 다시 동기화될 수 있는지 확인하는 방법 {/*how-react-verifies-that-your-effect-can-re-synchronize*/} +### React가 effect를 다시 동기화할 수 있는지 확인하는 방법 {/*how-react-verifies-that-your-effect-can-re-synchronize*/} 다음은 여러분이 플레이할 수 있는 라이브 예시입니다. "채팅 열기"를 눌러 `ChatRoom` 컴포넌트를 마운트합니다. @@ -275,7 +275,7 @@ button { margin-left: 10px; } 컴포넌트가 처음 마운트될 때 3개의 로그가 표시됩니다. 1. `✅ https://localhost:1234... 에서 "general" 방에 연결 중입니다.` *(개발 전용)* -2. `❌ https://localhost:1234에서 "일반" 방에서 연결 해제되었습니다.` *(개발 전용)* +2. `❌ https://localhost:1234에서 "general" 방에서 연결 해제되었습니다.` *(개발 전용)* 3. `✅ https://localhost:1234... 에서 "general" 방에 연결 중입니다.` 처음 두 개의 로그는 개발 전용입니다. 개발 시 React는 항상 각 컴포넌트를 한 번씩 다시 마운트합니다. @@ -288,7 +288,7 @@ button { margin-left: 10px; } ### React가 effect를 다시 동기화해야 한다는 것을 인식하는 방법 {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/} -`roomId`가 변경된 후 effect를 다시 동기화해야 한다는 것을 어떻게 React가 알았는지 궁금할 것입니다. 그것은 여러분이 [종속성 목록](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies)에 `roomId`를 포함함으로써 해당 코드가 `roomId`에 종속되어 있다고 React에 알렸기 때문입니다. +`roomId`가 변경된 후 effect를 다시 동기화해야 한다는 것을 어떻게 React가 알았는지 궁금할 것입니다. 그것은 여러분이 [의존성 목록](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies)에 `roomId`를 포함함으로써 해당 코드가 `roomId`에 의존한다고 React에 알렸기 때문입니다. ```js {1,3,8} function ChatRoom({ roomId }) { // roomId prop은 시간이 지남에 따라 변경될 수 있습니다. @@ -307,15 +307,15 @@ function ChatRoom({ roomId }) { // roomId prop은 시간이 지남에 따라 변 1. `roomId`가 `prop`이므로 시간이 지남에 따라 변경될 수 있다는 것을 알고 있습니다. 2. effect가 `roomId`를 읽는다는 것을 알았습니다.(따라서 로직이 나중에 변경될 수 있는 값에 따라 달라집니다.) -3. 그렇기 때문에 `roomId`를 effect의 종속성으로 지정한 것입니다 (`roomId` 가 변경되면 다시 동기화되도록). +3. 그렇기 때문에 `roomId`를 effect의 의존성으로 지정한 것입니다 (`roomId`가 변경되면 다시 동기화되도록). -컴포넌트가 다시 렌더링 될 때마다 React는 전달한 의존성 배열을 살펴봅니다. 배열의 값 중 하나라도 이전 렌더링 중에 전달한 동일한 지점의 값과 다르면 React는 effect를 다시 동기화합니다. +컴포넌트가 다시 렌더링될 때마다 React는 전달한 의존성 배열을 살펴봅니다. 배열의 값 중 하나라도 이전 렌더링 중에 전달한 동일한 지점의 값과 다르면 React는 effect를 다시 동기화합니다. -예를 들어, 초기 렌더링 중에 `["general"]`을 전달했는데 나중에 다음 렌더링 중에 `["travel"]`을 전달한 경우, React는 `"general"`과 `"travel"`을 비교합니다. 이 값들은 ([`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)로 비교) 다른 값이기 때문에 React는 effect를 다시 동기화합니다. 반면에 컴포넌트가 다시 렌더링 되지만 `roomId`가 변경되지 않은 경우, effect는 동일한 방에 연결된 상태로 유지됩니다. +예를 들어, 초기 렌더링 중에 `["general"]`을 전달했는데 나중에 다음 렌더링 중에 `["travel"]`을 전달한 경우, React는 `"general"`과 `"travel"`을 비교합니다. 이 값들은 ([`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)로 비교) 다른 값이기 때문에 React는 effect를 다시 동기화합니다. 반면에 컴포넌트가 다시 렌더링되지만 `roomId`가 변경되지 않은 경우, effect는 동일한 방에 연결된 상태로 유지됩니다. ### 각 effect는 별도의 동기화 프로세스를 나타냅니다. {/*each-effect-represents-a-separate-synchronization-process*/} -이 로직은 이미 작성한 effect와 동시에 실행되어야 하므로 관련 없는 로직을 effect에 추가하지 마세요. 예를 들어 사용자가 회의실을 방문할 때 분석 이벤트를 전송하고 싶다고 가정해 보겠습니다. 이미 `roomId`에 의존하는 effect가 있으므로 거기에 분석 호출을 추가하고 싶을 수 있습니다. +이 로직은 이미 작성한 effect와 동시에 실행되어야 하므로 관련 없는 로직을 effect에 추가하지 마세요. 예를 들어 사용자가 채팅방을 방문할 때 분석 이벤트를 전송하고 싶다고 가정해 보겠습니다. 이미 `roomId`에 의존하는 effect가 있으므로 거기에 분석 호출을 추가하고 싶을 수 있습니다. ```js {3} function ChatRoom({ roomId }) { @@ -331,7 +331,7 @@ function ChatRoom({ roomId }) { } ``` -하지만 나중에 이 effect에 연결을 다시 설정해야 하는 다른 종속성을 추가한다고 가정해 보겠습니다. 이 effect가 다시 동기화되면 의도하지 않은 동일한 방에 대해 `logVisit(roomId)`도 호출합니다. 방문을 기록하는 것은 연결과는 **별개의 프로세스**입니다. 두 개의 개별 effect로 작성하세요. +하지만 나중에 이 effect에 연결을 다시 설정해야 하는 다른 의존성을 추가한다고 가정해 보겠습니다. 이 effect가 다시 동기화되면 의도하지 않은 동일한 방에 대해 `logVisit(roomId)`도 호출합니다. 방문을 기록하는 것은 연결과는 **별개의 프로세스**입니다. 두 개의 개별 effect로 작성하세요. ```js {2-4} function ChatRoom({ roomId }) { @@ -353,7 +353,7 @@ function ChatRoom({ roomId }) { ## 반응형 값에 "반응"하는 effect {/*effects-react-to-reactive-values*/} -effect에서 두 개의 변수(`serverUrl` 및 `roomId`)를 읽지만 종속성으로 `roomId`만 지정했습니다. +effect에서 두 개의 변수(`serverUrl` 및 `roomId`)를 읽지만 의존성으로 `roomId`만 지정했습니다. ```js {5,10} const serverUrl = 'https://localhost:1234'; @@ -370,13 +370,13 @@ function ChatRoom({ roomId }) { } ``` -`serverUrl`이 종속성이 될 필요가 없는 이유는 무엇인가요? +`serverUrl`이 의존성이 될 필요가 없는 이유는 무엇인가요? -이는 재 렌더링으로 인해 `serverUrl`이 변경되지 않기 때문입니다. 컴포넌트가 몇 번이나 다시 렌더링하든, 그 이유와 상관없이 항상 동일합니다. `serverUrl`은 절대 변경되지 않으므로 종속성으로 지정하는 것은 의미가 없습니다. 결국 종속성은 시간이 지남에 따라 변경될 때만 무언가를 수행합니다! +이는 재 렌더링으로 인해 `serverUrl`이 변경되지 않기 때문입니다. 컴포넌트가 몇 번이나 다시 렌더링하든, 그 이유와 상관없이 항상 동일합니다. `serverUrl`은 절대 변경되지 않으므로 의존성으로 지정하는 것은 의미가 없습니다. 결국 의존성은 시간이 지남에 따라 변경될 때만 무언가를 수행합니다! -반면에 `roomId`는 다시 렌더링할 때 달라질 수 있습니다. 컴포넌트 내부에서 선언된 **Props, state 및 기타값은 렌더링 중에 계산되고 React 데이터 흐름에 참여하기 때문에 _반응형_ 입니다.** +반면에 `roomId`는 다시 렌더링할 때 달라질 수 있습니다. 컴포넌트 내부에서 선언된 **Props, state 및 기타 값은 렌더링 중에 계산되고 React 데이터 흐름에 참여하기 때문에 _반응형_ 입니다.** -`serverUrl`이 state 변수라면 반응형일 것입니다. 반응형 값은 종속성에 포함되어야 합니다. +`serverUrl`이 state 변수라면 반응형일 것입니다. 반응형 값은 의존성에 포함되어야 합니다. ```js {2,5,10} function ChatRoom({ roomId }) { // Props는 시간이 지남에 따라 변화합니다. @@ -393,7 +393,7 @@ function ChatRoom({ roomId }) { // Props는 시간이 지남에 따라 변화합 } ``` -`serverUrl`을 종속성으로 포함하면 effect가 변경된 후 다시 동기화되도록 할 수 있습니다. +`serverUrl`을 의존성으로 포함하면 effect가 변경된 후 다시 동기화되도록 할 수 있습니다. 이 sandbox에서 선택한 대화방을 변경하거나 서버 URL을 수정해 보세요. @@ -471,7 +471,7 @@ button { margin-left: 10px; } `roomId` 또는 `serverUrl`과 같은 반응형 값을 변경할 때마다 effect가 채팅 서버에 다시 연결합니다. -### 빈 종속성이 있는 effect의 의미 {/*what-an-effect-with-empty-dependencies-means*/} +### 빈 의존성이 있는 effect의 의미 {/*what-an-effect-with-empty-dependencies-means*/} `serverUrl`과 `roomId`를 모두 컴포넌트 외부로 이동하면 어떻게 되나요? @@ -486,12 +486,12 @@ function ChatRoom() { return () => { connection.disconnect(); }; - }, []); // ✅ 선언된 모든 종속성 + }, []); // ✅ 선언된 모든 의존성 // ... } ``` -이제 effect의 코드는 *어떤* 반응형 값도 사용하지 않으므로 종속성이 비어 있을 수 있습니다(`[]`). +이제 effect의 코드는 *어떤* 반응형 값도 사용하지 않으므로 의존성이 비어 있을 수 있습니다(`[]`). 컴포넌트의 관점에서 생각해 보면, 빈 `[]` 의존성 배열은 이 effect가 컴포넌트가 마운트될 때만 채팅방에 연결되고 컴포넌트가 마운트 해제될 때만 연결이 끊어진다는 것을 의미합니다. (React는 로직을 스트레스 테스트하기 위해 개발 단계에서 [한 번 더 동기화](#how-react-verifies-that-your-effect-can-re-synchronize)한다는 점을 기억하세요.) @@ -549,20 +549,20 @@ button { margin-left: 10px; } -하지만 [effect의 관점에서 생각](#thinking-from-the-effects-perspective)하면 마운트 및 마운트 해제에 대해 전혀 생각할 필요가 없습니다. 중요한 것은 effect가 동기화를 시작하고 중지하는 작업을 지정한 것입니다. 현재는 반응형 종속성이 없습니다. 하지만 사용자가 시간이 지남에 따라 `roomId` 또는 `serverUrl`을 변경하려는 경우(그리고 반응형이 되는 경우) effect의 코드는 변경되지 않습니다. 종속성에 추가하기만 하면 됩니다. +하지만 [effect의 관점에서 생각](#thinking-from-the-effects-perspective)하면 마운트 및 마운트 해제에 대해 전혀 생각할 필요가 없습니다. 중요한 것은 effect가 동기화를 시작하고 중지하는 작업을 지정한 것입니다. 현재는 반응형 의존성이 없습니다. 하지만 사용자가 시간이 지남에 따라 `roomId` 또는 `serverUrl`을 변경하려는 경우(그리고 반응형이 되는 경우) effect의 코드는 변경되지 않습니다. 의존성에 추가하기만 하면 됩니다. ### 컴포넌트 본문에서 선언된 모든 변수는 반응형입니다. {/*all-variables-declared-in-the-component-body-are-reactive*/} -props와 state만 반응형 값인 것은 아닙니다. 이들로부터 계산하는 값도 반응형입니다. props나 state가 변경되면 컴포넌트가 다시 렌더링 되고 그로부터 계산된 값도 변경됩니다. 이 때문에 effect에서 사용하는 컴포넌트 본문의 모든 변수는 effect 종속성 목록에 있어야 합니다. +props와 state만 반응형 값인 것은 아닙니다. 이들로부터 계산하는 값도 반응형입니다. props나 state가 변경되면 컴포넌트가 다시 렌더링되고 그로부터 계산된 값도 변경됩니다. 이 때문에 effect에서 사용하는 컴포넌트 본문의 모든 변수는 effect 의존성 목록에 있어야 합니다. 사용자가 드롭다운에서 채팅 서버를 선택할 수 있지만 설정에서 기본 서버를 구성할 수도 있다고 가정해 봅시다. 이미 settings state를 [context](/learn/scaling-up-with-reducer-and-context)에 넣어서 해당 context에서 `settings`를 읽었다고 가정해 보겠습니다. 이제 props에서 선택한 서버와 기본 서버를 기준으로 `serverUrl`을 계산합니다. ```js {3,5,10} function ChatRoom({ roomId, selectedServerUrl }) { // roomId는 반응형입니다. const settings = useContext(SettingsContext); // settings는 반응형입니다. - const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl는 반응형입니다. + const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl은 반응형입니다. useEffect(() => { - const connection = createConnection(serverUrl, roomId); // effect는 roomId 와 serverUrl를 읽습니다. + const connection = createConnection(serverUrl, roomId); // effect는 roomId와 serverUrl을 읽습니다. connection.connect(); return () => { connection.disconnect(); @@ -574,27 +574,27 @@ function ChatRoom({ roomId, selectedServerUrl }) { // roomId는 반응형입니 이 예시에서 `serverUrl`은 prop이나 state 변수가 아닙니다. 렌더링 중에 계산하는 일반 변수입니다. 하지만 렌더링 중에 계산되므로 재 렌더링으로 인해 변경될 수 있습니다. 이것이 바로 반응형인 이유입니다. -**컴포넌트 내부의 모든 값(컴포넌트 본문의 props, state, 변수 포함)은 반응형입니다. 모든 반응형 값은 다시 렌더링할 때 변경될 수 있으므로 반응형 값을 effect의 종속 요소로 포함해야 합니다.** +**컴포넌트 내부의 모든 값(컴포넌트 본문의 props, state, 변수 포함)은 반응형입니다. 모든 반응형 값은 다시 렌더링할 때 변경될 수 있으므로 반응형 값을 effect의 의존성으로 포함해야 합니다.** 즉, effect는 컴포넌트 본문의 모든 값에 "반응"합니다. -#### 전역 또는 변경할 수 있는 값이 종속성이 될 수 있나요? {/*can-global-or-mutable-values-be-dependencies*/} +#### 전역 또는 변경할 수 있는 값이 의존성이 될 수 있나요? {/*can-global-or-mutable-values-be-dependencies*/} 변경할 수 있는 값(전역 변수 포함)은 반응하지 않습니다. -**[`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname)과 같은 변경 가능한 값은 종속성이 될 수 없습니다.** 이 값은 변경할 수 있으므로 React 렌더링 데이터 흐름 외부에서 언제든지 변경할 수 있습니다. 이 값을 변경해도 컴포넌트가 다시 렌더링 되지는 않습니다. 따라서 종속성에서 지정했더라도 React는 effect가 변경될 때 effect를 다시 동기화할지 *알 수 없습니다.* 또한 렌더링 도중(의존성을 계산할 때) 변경할 수 있는 데이터를 읽는 것은 [렌더링의 순수성](/learn/keeping-components-pure)을 깨뜨리기 때문에 React의 규칙을 위반합니다. 대신, [`useSyncExternalStore`](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store)를 사용하여 외부 변경할 수 있는 값을 읽고 구독해야 합니다. +**[`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname)과 같은 변경 가능한 값은 의존성이 될 수 없습니다.** 이 값은 변경할 수 있으므로 React 렌더링 데이터 흐름 외부에서 언제든지 변경할 수 있습니다. 이 값을 변경해도 컴포넌트가 다시 렌더링되지는 않습니다. 따라서 의존성으로 지정했더라도 React는 effect가 변경될 때 effect를 다시 동기화할지 *알 수 없습니다.* 또한 렌더링 도중(의존성을 계산할 때) 변경할 수 있는 데이터를 읽는 것은 [렌더링의 순수성](/learn/keeping-components-pure)을 깨뜨리기 때문에 React의 규칙을 위반합니다. 대신, [`useSyncExternalStore`](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store)를 사용하여 외부 변경할 수 있는 값을 읽고 구독해야 합니다. -**[`ref.current`](/reference/react/useRef#reference)와 같이 변경 가능한 값이나 이 값에서 읽은 것 역시 종속성이 될 수 없습니다.** `useRef`가 반환하는 `ref` 객체 자체는 종속성이 될 수 있지만 `current` prop는 의도적으로 변경할 수 있습니다. 이를 통해 [재 렌더링을 트리거하지 않고도 무언가를 추적할 수 있습니다.](/learn/referencing-values-with-refs) 하지만 변경해도 다시 렌더링이 트리거되지 않기 때문에 반응형 값이 아니며, React는 이 값이 변경될 때 effect를 다시 실행할지 알지 못합니다. +**[`ref.current`](/reference/react/useRef#reference)와 같이 변경 가능한 값이나 이 값에서 읽은 것 역시 의존성이 될 수 없습니다.** `useRef`가 반환하는 `ref` 객체 자체는 의존성이 될 수 있지만 `current` prop는 의도적으로 변경할 수 있습니다. 이를 통해 [재 렌더링을 트리거하지 않고도 무언가를 추적할 수 있습니다.](/learn/referencing-values-with-refs) 하지만 변경해도 다시 렌더링이 트리거되지 않기 때문에 반응형 값이 아니며, React는 이 값이 변경될 때 effect를 다시 실행할지 알지 못합니다. 이 페이지에서 아래에서 배우게 되겠지만, 린터는 이러한 문제를 자동으로 확인합니다. -### React는 모든 반응형 값을 종속성으로 지정했는지 확인합니다. {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/} +### React는 모든 반응형 값을 의존성으로 지정했는지 확인합니다. {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/} -린터가 [React에 대해 구성](/learn/editor-setup#linting)된 경우, effect의 코드에서 사용되는 모든 반응형 값이 종속성으로 선언되었는지 확인합니다. 예를 들어, `roomId`와 `serverUrl`이 모두 반응형이기 때문에 이것은 린트 오류입니다. +린터가 [React에 대해 구성](/learn/editor-setup#linting)된 경우, effect의 코드에서 사용되는 모든 반응형 값이 의존성으로 선언되었는지 확인합니다. 예를 들어, `roomId`와 `serverUrl`이 모두 반응형이기 때문에 이것은 린트 오류입니다. @@ -603,7 +603,7 @@ import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; function ChatRoom({ roomId }) { // roomId는 반응형입니다. - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl는 반응형입니다. + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl은 반응형입니다. useEffect(() => { const connection = createConnection(serverUrl, roomId); @@ -670,18 +670,18 @@ button { margin-left: 10px; } 이것은 React 오류처럼 보일 수 있지만 실제로는 코드의 버그를 지적하는 것입니다. `roomId`와 `serverUrl`은 시간이 지남에 따라 변경될 수 있지만, 변경 시 effect를 다시 동기화하는 것을 잊어버리고 있습니다. 사용자가 UI에서 다른 값을 선택한 후에도 초기 `roomId`와 `serverUrl`에 연결된 상태로 유지됩니다. -버그를 수정하려면 린터의 제안에 따라 effect의 종속 요소로 `roomId` 및 `serverUrl`을 지정하세요. +버그를 수정하려면 린터의 제안에 따라 effect의 의존성으로 `roomId` 및 `serverUrl`을 지정하세요. ```js {9} function ChatRoom({ roomId }) { // roomId는 반응형입니다. - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl는 반응형입니다. + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl은 반응형입니다. useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; - }, [serverUrl, roomId]); // ✅ 선언된 모든 종속성 + }, [serverUrl, roomId]); // ✅ 선언된 모든 의존성 // ... } ``` @@ -696,12 +696,12 @@ function ChatRoom({ roomId }) { // roomId는 반응형입니다. ### 다시 동기화하지 않으려는 경우 어떻게 해야 하나요? {/*what-to-do-when-you-dont-want-to-re-synchronize*/} -이전 예시에서는 `roomId`와 `serverUrl`을 종속성으로 나열하여 린트 오류를 수정했습니다. +이전 예시에서는 `roomId`와 `serverUrl`을 의존성으로 나열하여 린트 오류를 수정했습니다. -**그러나 대신 이러한 값이 반응형 값이 아니라는 것, 즉 재 렌더링의 결과로 변경*될 수 없다*는 것을 린터에 "증명"할 수 있습니다.** 예를 들어 `serverUrl`과 `roomId`가 렌더링에 의존하지 않고 항상 같은 값을 갖는다면 컴포넌트 외부로 옮길 수 있습니다. 이제 종속성이 될 필요가 없습니다. +**그러나 대신 이러한 값이 반응형 값이 아니라는 것, 즉 재 렌더링의 결과로 변경*될 수 없다*는 것을 린터에 "증명"할 수 있습니다.** 예를 들어 `serverUrl`과 `roomId`가 렌더링에 의존하지 않고 항상 같은 값을 갖는다면 컴포넌트 외부로 옮길 수 있습니다. 이제 의존성이 될 필요가 없습니다. ```js {1,2,11} -const serverUrl = 'https://localhost:1234'; // serverUrl는 반응형이 아닙니다. +const serverUrl = 'https://localhost:1234'; // serverUrl은 반응형이 아닙니다. const roomId = 'general'; // roomId는 반응형이 아닙니다. function ChatRoom() { @@ -711,7 +711,7 @@ function ChatRoom() { return () => { connection.disconnect(); }; - }, []); // ✅ 선언된 모든 종속성 + }, []); // ✅ 선언된 모든 의존성 // ... } ``` @@ -721,31 +721,31 @@ function ChatRoom() { ```js {3,4,10} function ChatRoom() { useEffect(() => { - const serverUrl = 'https://localhost:1234'; // serverUrl는 반응형이 아닙니다. + const serverUrl = 'https://localhost:1234'; // serverUrl은 반응형이 아닙니다. const roomId = 'general'; // roomId는 반응형이 아닙니다. const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; - }, []); // ✅ 선언된 모든 종속성 + }, []); // ✅ 선언된 모든 의존성 // ... } ``` **effect는 반응형 코드 블록입니다.** 내부에서 읽은 값이 변경되면 다시 동기화됩니다. 상호작용당 한 번만 실행되는 이벤트 핸들러와 달리 effect는 동기화가 필요할 때마다 실행됩니다. -**종속성을 "선택"할 수 없습니다.** 종속성에는 effect에서 읽은 모든 [반응형 값](#all-variables-declared-in-the-component-body-are-reactive)이 포함되어야 합니다. 린터가 이를 강제합니다. 때때로 이에 따라 무한 루프와 같은 문제가 발생하거나 effect가 너무 자주 다시 동기화될 수 있습니다. 린터를 억제하여 이러한 문제를 해결하지 마세요! 대신 시도할 방법은 다음과 같습니다. +**의존성을 "선택"할 수 없습니다.** 의존성에는 effect에서 읽은 모든 [반응형 값](#all-variables-declared-in-the-component-body-are-reactive)이 포함되어야 합니다. 린터가 이를 강제합니다. 때때로 이에 따라 무한 루프와 같은 문제가 발생하거나 effect가 너무 자주 다시 동기화될 수 있습니다. 린터를 억제하여 이러한 문제를 해결하지 마세요! 대신 시도할 방법은 다음과 같습니다. * **effect가 독립적인 동기화 프로세스를 나타내는지 확인하세요.** effect가 아무것도 동기화하지 않는다면 [불필요한 것일 수 있습니다.](/learn/you-might-not-need-an-effect) 여러 개의 독립적인 것을 동기화하는 경우 [분할](#each-effect-represents-a-separate-synchronization-process)하세요. * **props나 state에 "반응"하지 않고 effect를 다시 동기화하지 않고 최신 값을 읽으려면** effect를 반응하는 부분(effect에 유지할 것)과 반응하지 않는 부분(_effect 이벤트_ 라고 하는 것으로 추출할 수 있는 것)으로 분리하면 됩니다. [이벤트와 effect를 분리하는 방법을 읽어보세요.](/learn/separating-events-from-effects) -* **객체와 함수를 종속성으로 사용하지 마세요.** 렌더링 중에 오브젝트와 함수를 생성한 다음 effect에서 읽으면 렌더링할 때마다 오브젝트와 함수가 달라집니다. 그러면 매번 effect를 다시 동기화해야 합니다. [effect에서 불필요한 종속성을 제거하는 방법에 대해 자세히 읽어보세요.](/learn/removing-effect-dependencies) +* **객체와 함수를 의존성으로 사용하지 마세요.** 렌더링 중에 객체와 함수를 생성한 다음 effect에서 읽으면 렌더링할 때마다 객체와 함수가 달라집니다. 그러면 매번 effect를 다시 동기화해야 합니다. [effect에서 불필요한 의존성을 제거하는 방법에 대해 자세히 읽어보세요.](/learn/removing-effect-dependencies) -린터는 여러분의 친구이지만 그 힘은 제한되어 있습니다. 린터는 종속성이 *잘못*되었을 때만 알 수 있습니다. 각 사례를 해결하는 *최선*의 방법은 알지 못합니다. 만약 린터가 종속성을 제안하지만 이를 추가하면 루프가 발생한다고 해서 린터를 무시해야 한다는 의미는 아닙니다. 해당 값이 반응적이지 않고 종속성이 될 *필요*가 없도록 effect 내부(또는 외부)의 코드를 변경해야 합니다. +린터는 여러분의 친구이지만 그 힘은 제한되어 있습니다. 린터는 의존성이 *잘못*되었을 때만 알 수 있습니다. 각 사례를 해결하는 *최선*의 방법은 알지 못합니다. 만약 린터가 의존성을 제안하지만 이를 추가하면 루프가 발생한다고 해서 린터를 무시해야 한다는 의미는 아닙니다. 해당 값이 반응적이지 않고 의존성이 될 *필요*가 없도록 effect 내부(또는 외부)의 코드를 변경해야 합니다. 기존 코드베이스가 있는 경우 이처럼 린터를 억제하는 effect가 있을 수 있습니다. @@ -769,8 +769,8 @@ useEffect(() => { - effect를 작성하고 읽을 때는 컴포넌트의 관점(마운트, 업데이트 또는 마운트 해제 방법)이 아닌 개별 effect의 관점(동기화 *시작* 및 *중지* 방법)에서 생각하세요. - 컴포넌트 본문 내부에 선언된 값은 "반응형"입니다. - 반응형 값은 시간이 지남에 따라 변경될 수 있으므로 effect를 다시 동기화해야 합니다. -- 린터는 effect 내부에서 사용된 모든 반응형 값이 종속성으로 지정되었는지 확인합니다. -- 린터에 의해 플래그가 지정된 모든 오류는 합법적인 오류입니다. 규칙을 위반하지 않도록 코드를 수정할 방법은 항상 있습니다. +- 린터는 effect 내부에서 사용된 모든 반응형 값이 의존성으로 지정되었는지 확인합니다. +- 린터가 표시한 모든 오류는 실제 오류입니다. 규칙을 위반하지 않도록 코드를 수정할 방법은 항상 있습니다. @@ -784,7 +784,7 @@ useEffect(() => { -이 effect에 대한 종속성 배열을 추가해야 할 수도 있습니다. 어떤 종속성이 있어야 할까요? +이 effect에 대한 의존성 배열을 추가해야 할 수도 있습니다. 어떤 의존성이 있어야 할까요? @@ -861,7 +861,7 @@ button { margin-left: 10px; } -이 effect에는 종속성 배열이 전혀 없었기 때문에 렌더링할 때마다 다시 동기화되었습니다. 먼저 종속성 배열을 추가합니다. 그런 다음 effect에서 사용하는 모든 반응형 값이 배열에 지정되어 있는지 확인합니다. 예를 들어 `roomId`는 반응형이므로(props이므로) 배열에 포함되어야 합니다. 이렇게 하면 사용자가 다른 방을 선택해도 채팅이 다시 연결됩니다. 반면 `serverUrl`은 컴포넌트 외부에 정의됩니다. 그렇기 때문에 배열에 포함할 필요가 없습니다. +이 effect에는 의존성 배열이 전혀 없었기 때문에 렌더링할 때마다 다시 동기화되었습니다. 먼저 의존성 배열을 추가합니다. 그런 다음 effect에서 사용하는 모든 반응형 값이 배열에 지정되어 있는지 확인합니다. 예를 들어 `roomId`는 반응형이므로(props이므로) 배열에 포함되어야 합니다. 이렇게 하면 사용자가 다른 방을 선택해도 채팅이 다시 연결됩니다. 반면 `serverUrl`은 컴포넌트 외부에 정의됩니다. 그렇기 때문에 배열에 포함할 필요가 없습니다. @@ -940,7 +940,7 @@ button { margin-left: 10px; } 이 예시에서 effect는 창 [`pointermove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event) 이벤트를 구독하여 화면에서 분홍색 점을 이동합니다. 미리 보기 영역 위로 마우스를 가져가서(또는 모바일 장치에서 화면을 터치하여) 분홍색 점이 어떻게 움직이는지 확인해 보세요. -체크박스도 있습니다. 체크 박스를 선택하면 `canMove` state 변수가 토글되지만 이 state 변수는 코드의 어느 곳에서도 사용되지 않습니다. 여러분의 임무는 `canMove`가 `false`일 때(체크박스가 체크된 상태) 점의 이동이 중지되도록 코드를 변경하는 것입니다. 체크 박스를 다시 켜고 `canMove`를 `true`로 설정하면 상자가 다시 움직여야 합니다. 즉, 점이 움직일 수 있는지는 체크 박스의 체크 여부와 동기화 state를 유지해야 합니다. +체크박스도 있습니다. 체크박스를 선택하면 `canMove` state 변수가 토글되지만 이 state 변수는 코드의 어느 곳에서도 사용되지 않습니다. 여러분의 임무는 `canMove`가 `false`일 때(체크박스가 체크된 상태) 점의 이동이 중지되도록 코드를 변경하는 것입니다. 체크박스를 다시 켜고 `canMove`를 `true`로 설정하면 상자가 다시 움직여야 합니다. 즉, 점이 움직일 수 있는지는 체크박스의 체크 여부와 동기화 state를 유지해야 합니다. @@ -1114,7 +1114,7 @@ body { -이 두 경우 모두 `canMove`는 effect 내부에서 읽는 반응형 변수입니다. 그렇기 때문에 effect 종속성 목록에 지정해야 합니다. 이렇게 하면 값이 변경될 때마다 effect가 다시 동기화됩니다. +이 두 경우 모두 `canMove`는 effect 내부에서 읽는 반응형 변수입니다. 그렇기 때문에 effect 의존성 목록에 지정해야 합니다. 이렇게 하면 값이 변경될 때마다 effect가 다시 동기화됩니다. @@ -1188,13 +1188,13 @@ body { -원래 코드의 문제는 의존성 린터를 억제하는 것이었습니다. 억제를 제거하면 이 effect가 `handleMove` 함수에 의존한다는 것을 알 수 있습니다. `handleMove`는 컴포넌트 본문 내부에서 선언되어 반응형 값이 되기 때문입니다. 모든 반응형 값은 종속성으로 지정해야 하며, 그렇지 않으면 시간이 지나면 낡아질 수 있습니다! +원래 코드의 문제는 의존성 린터를 억제하는 것이었습니다. 억제를 제거하면 이 effect가 `handleMove` 함수에 의존한다는 것을 알 수 있습니다. `handleMove`는 컴포넌트 본문 내부에서 선언되어 반응형 값이 되기 때문입니다. 모든 반응형 값은 의존성으로 지정해야 하며, 그렇지 않으면 시간이 지나며 오래된 값을 참조할 수 있습니다! 원본 코드 작성자는 effect가 어떤 반응형 값에도 의존(`[]`)하지 않는다고 말함으로써 React에 "거짓말"을 했습니다. 이것이 바로 React가 `canMove`가 변경된 후 effect를 다시 동기화하지 않은 이유입니다(그리고 `handleMove`도 함께). React가 effect를 재동기화하지 않았기 때문에 리스너로 첨부된 `handleMove`는 초기 렌더링 중에 생성된 `handleMove` 함수입니다. 초기 렌더링하는 동안 `canMove`는 `true`이었기 때문에 초기 렌더링의 `handleMove`는 영원히 그 값을 보게 됩니다. **린터를 억제하지 않으면 오래된 값으로 인한 문제가 발생하지 않습니다.** 이 버그를 해결하는 방법에는 몇 가지가 있지만 항상 린터 억제를 제거하는 것부터 시작해야 합니다. 그런 다음 코드를 변경하여 린트 오류를 수정하세요. -effect 종속성을 `[handleMove]`로 변경할 수 있지만, 렌더링할 때마다 새로 정의되는 함수가 *될 것*이므로 종속성 배열을 모두 제거하는 것이 좋습니다. 그러면 렌더링할 때마다 effect가 다시 동기화됩니다. +effect 의존성을 `[handleMove]`로 변경할 수 있지만, 렌더링할 때마다 새로 정의되는 함수가 *될 것*이므로 의존성 배열을 모두 제거하는 것이 좋습니다. 그러면 렌더링할 때마다 effect가 다시 동기화됩니다. @@ -1253,7 +1253,7 @@ body { 이 솔루션은 작동하지만 이상적이지는 않습니다. effect 안에 `console.log('Resubscribing')`를 넣으면 렌더링할 때마다 재구독하는 것을 확인할 수 있습니다. 재구독은 빠르지만 너무 자주 하는 것은 피하는 것이 좋습니다. -더 나은 해결책은 `handleMove` 함수를 effect *내부*로 옮기는 것입니다. 그러면 `handleMove`는 반응형 값이 아니므로 effect가 함수에 종속되지 않습니다. 대신, 이제 코드가 effect 내부에서 읽는 `canMove`에 의존해야 합니다. 이제 effect가 `canMove`의 값과 동기화 state를 유지하므로 원하는 동작과 일치합니다. +더 나은 해결책은 `handleMove` 함수를 effect *내부*로 옮기는 것입니다. 그러면 `handleMove`는 반응형 값이 아니므로 effect가 함수에 의존하지 않습니다. 대신, 이제 코드가 effect 내부에서 읽는 `canMove`에 의존해야 합니다. 이제 effect가 `canMove`의 값과 동기화 상태를 유지하므로 원하는 동작과 일치합니다. @@ -1310,7 +1310,7 @@ body { -effect 본문에 `console.log('Resubscribing')`를 추가하면 이제 체크 박스를 토글 하거나(`canMove` 변경 사항) 코드를 편집할 때만 다시 구독하는 것을 확인할 수 있습니다. 이렇게 하면 항상 재구독하던 이전 접근 방식보다 개선되었습니다. +effect 본문에 `console.log('Resubscribing')`를 추가하면 이제 체크박스를 토글하거나(`canMove` 변경 사항) 코드를 편집할 때만 다시 구독하는 것을 확인할 수 있습니다. 이렇게 하면 항상 재구독하던 이전 접근 방식보다 개선되었습니다. [이벤트와 effect 분리하기](/learn/separating-events-from-effects)에서 이러한 유형의 문제에 대한 보다 일반적인 접근 방식을 배우게 됩니다. @@ -1320,7 +1320,7 @@ effect 본문에 `console.log('Resubscribing')`를 추가하면 이제 체크 이 예시에서 `chat.js`의 채팅 서비스는 `createEncryptedConnection`과 `createUnencryptedConnection`이라는 두 개의 서로 다른 API를 노출합니다. 루트 `App` 컴포넌트는 사용자가 암호화 사용 여부를 선택할 수 있도록 한 다음, 해당 API 메서드를 하위 `ChatRoom` 컴포넌트에 `createConnection` prop으로 전달합니다. -처음에는 콘솔 로그에 연결이 암호화되지 않았다고 표시됩니다. 체크 박스를 켜면 아무 일도 일어나지 않습니다. 그러나 그 후에 선택한 대화방을 변경하면 채팅이 다시 연결*되고* 콘솔 메시지에서 볼 수 있듯이 암호화가 활성화됩니다. 이것은 버그입니다. 체크 박스를 토글해*도* 채팅이 다시 연결되도록 버그를 수정하세요. +처음에는 콘솔 로그에 연결이 암호화되지 않았다고 표시됩니다. 체크박스를 켜면 아무 일도 일어나지 않습니다. 그러나 그 후에 선택한 대화방을 변경하면 채팅이 다시 연결*되고* 콘솔 메시지에서 볼 수 있듯이 암호화가 활성화됩니다. 이것은 버그입니다. 체크박스를 토글해*도* 채팅이 다시 연결되도록 버그를 수정하세요. @@ -1424,7 +1424,7 @@ label { display: block; margin-bottom: 10px; } -린터 억제를 제거하면 린트 오류가 표시됩니다. 문제는 `createConnection`이 props이기 때문에 반응형 값이라는 것입니다. 시간이 지남에 따라 변경될 수 있습니다! (실제로 사용자가 체크박스를 선택하면 부모 컴포넌트가 다른 값의 `createConnection` prop을 전달합니다) 실제로 그래야 합니다. 이것이 바로 종속성이 되어야 하는 이유입니다. 목록에 포함해 버그를 수정하세요. +린터 억제를 제거하면 린트 오류가 표시됩니다. 문제는 `createConnection`이 prop이기 때문에 반응형 값이라는 것입니다. 시간이 지남에 따라 변경될 수 있습니다! (실제로 사용자가 체크박스를 선택하면 부모 컴포넌트가 다른 값의 `createConnection` prop을 전달합니다) 실제로 그래야 합니다. 이것이 바로 의존성이 되어야 하는 이유입니다. 목록에 포함해 버그를 수정하세요. @@ -1519,7 +1519,7 @@ label { display: block; margin-bottom: 10px; } -`createConnection`이 종속성이라는 것은 맞습니다. 하지만 누군가 이 프로퍼티의 값으로 인라인 함수를 전달하도록 `App` 컴포넌트를 편집할 수 있기 때문에 이 코드는 약간 취약합니다. 이 경우 `App` 컴포넌트가 다시 렌더링할 때마다 값이 달라지므로 effect가 너무 자주 다시 동기화될 수 있습니다. 이를 방지하려면 대신 `isEncrypted`를 전달할 수 있습니다. +`createConnection`이 의존성이라는 것은 맞습니다. 하지만 누군가 이 프로퍼티의 값으로 인라인 함수를 전달하도록 `App` 컴포넌트를 편집할 수 있기 때문에 이 코드는 약간 취약합니다. 이 경우 `App` 컴포넌트가 다시 렌더링할 때마다 값이 달라지므로 effect가 너무 자주 다시 동기화될 수 있습니다. 이를 방지하려면 대신 `isEncrypted`를 전달할 수 있습니다. @@ -1614,7 +1614,7 @@ label { display: block; margin-bottom: 10px; } -이 버전에서는 `App` 컴포넌트가 함수 대신 boolean prop을 전달합니다. effect 내에서 어떤 함수를 사용할지 결정합니다. `createEncryptedConnection`과 `createUnencryptedConnection`은 모두 컴포넌트 외부에서 선언되므로 반응형이 아니므로 종속성이 될 필요가 없습니다. 이에 대한 자세한 내용은 [effect 종속성 제거하기](/learn/removing-effect-dependencies)에서 확인할 수 있습니다. +이 버전에서는 `App` 컴포넌트가 함수 대신 boolean prop을 전달합니다. effect 내에서 어떤 함수를 사용할지 결정합니다. `createEncryptedConnection`과 `createUnencryptedConnection`은 모두 컴포넌트 외부에서 선언되므로 반응형이 아니므로 의존성이 될 필요가 없습니다. 이에 대한 자세한 내용은 [effect의 의존성 제거하기](/learn/removing-effect-dependencies)에서 확인할 수 있습니다. @@ -1940,7 +1940,7 @@ label { display: block; margin-bottom: 10px; } -이 코드는 약간 반복적입니다. 하지만 그렇다고 해서 이를 하나의 effect로 결합해야 하는 이유는 없습니다! 이렇게 하면 두 effect의 종속성을 하나의 목록으로 결합한 다음 행성을 변경하면 모든 행성 목록을 다시 가져와야 합니다. effect는 코드 재사용을 위한 도구가 아닙니다. +이 코드는 약간 반복적입니다. 하지만 그렇다고 해서 이를 하나의 effect로 결합해야 하는 이유는 없습니다! 이렇게 하면 두 effect의 의존성을 하나의 목록으로 결합한 다음 행성을 변경하면 모든 행성 목록을 다시 가져와야 합니다. effect는 코드 재사용을 위한 도구가 아닙니다. 대신 반복을 줄이기 위해 아래의 `useSelectOptions`와 같은 커스텀 Hook에 일부 로직을 추출할 수 있습니다. @@ -2103,7 +2103,7 @@ label { display: block; margin-bottom: 10px; } -sandbox에서 `useSelectOptions.js` 탭을 확인하여 작동 방식을 확인하세요. 이상적으로는 애플리케이션의 대부분 effect는 사용자가 직접 작성했든 커뮤니티에서 작성했든 결국 커스텀 hook으로 대체되어야 합니다. 커스텀 hook은 동기화 로직을 숨기므로 호출 컴포넌트는 effect에 대해 알지 못합니다. 앱을 계속 개발하다 보면 선택할 수 있는 Hook 팔레트를 개발하게 될 것이고, 결국에는 컴포넌트에 effect를 자주 작성할 필요가 없게 될 것입니다. +sandbox에서 `useSelectOptions.js` 탭을 확인하여 작동 방식을 확인하세요. 이상적으로는 애플리케이션의 대부분의 effect는 사용자가 직접 작성했든 커뮤니티에서 작성했든 결국 커스텀 Hook으로 대체되어야 합니다. 커스텀 Hook은 동기화 로직을 숨기므로 호출 컴포넌트는 effect에 대해 알지 못합니다. 앱을 계속 개발하다 보면 선택할 수 있는 Hook 팔레트를 개발하게 될 것이고, 결국에는 컴포넌트에 effect를 자주 작성할 필요가 없게 될 것입니다. From 924a0166ceccfbd8c82976efdce5321a5bba6da7 Mon Sep 17 00:00:00 2001 From: lumir Date: Tue, 2 Jun 2026 20:38:20 +0900 Subject: [PATCH 2/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/content/learn/lifecycle-of-reactive-effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 5c62d841f..1f74b0e73 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -745,7 +745,7 @@ function ChatRoom() { -린터는 여러분의 친구이지만 그 힘은 제한되어 있습니다. 린터는 의존성이 *잘못*되었을 때만 알 수 있습니다. 각 사례를 해결하는 *최선*의 방법은 알지 못합니다. 만약 린터가 의존성을 제안하지만 이를 추가하면 루프가 발생한다고 해서 린터를 무시해야 한다는 의미는 아닙니다. 해당 값이 반응적이지 않고 의존성이 될 *필요*가 없도록 effect 내부(또는 외부)의 코드를 변경해야 합니다. +린터는 여러분의 친구이지만 그 힘은 제한되어 있습니다. 린터는 의존성이 *잘못*되었을 때만 알 수 있습니다. 각 사례를 해결하는 *최선*의 방법은 알지 못합니다. 만약 린터가 의존성을 제안하지만 이를 추가하면 루프가 발생한다고 해서 린터를 무시해야 한다는 의미는 아닙니다. 해당 값이 반응형이 아니고 의존성이 될 *필요*가 없도록 effect 내부(또는 외부)의 코드를 변경해야 합니다. 기존 코드베이스가 있는 경우 이처럼 린터를 억제하는 effect가 있을 수 있습니다. From 95499e596e09c5232f23a77c59b0a97010e305a1 Mon Sep 17 00:00:00 2001 From: lumir Date: Tue, 2 Jun 2026 20:38:56 +0900 Subject: [PATCH 3/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/content/learn/lifecycle-of-reactive-effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 1f74b0e73..318cce6e9 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -553,7 +553,7 @@ button { margin-left: 10px; } ### 컴포넌트 본문에서 선언된 모든 변수는 반응형입니다. {/*all-variables-declared-in-the-component-body-are-reactive*/} -props와 state만 반응형 값인 것은 아닙니다. 이들로부터 계산하는 값도 반응형입니다. props나 state가 변경되면 컴포넌트가 다시 렌더링되고 그로부터 계산된 값도 변경됩니다. 이 때문에 effect에서 사용하는 컴포넌트 본문의 모든 변수는 effect 의존성 목록에 있어야 합니다. +props와 state만 반응형 값인 것은 아닙니다. 이들로부터 계산하는 값도 반응형입니다. props나 state가 변경되면 컴포넌트가 다시 렌더링되고 그로부터 계산된 값도 변경됩니다. 이 때문에 effect에서 사용하는 컴포넌트 본문의 모든 변수는 effect의 의존성 목록에 있어야 합니다. 사용자가 드롭다운에서 채팅 서버를 선택할 수 있지만 설정에서 기본 서버를 구성할 수도 있다고 가정해 봅시다. 이미 settings state를 [context](/learn/scaling-up-with-reducer-and-context)에 넣어서 해당 context에서 `settings`를 읽었다고 가정해 보겠습니다. 이제 props에서 선택한 서버와 기본 서버를 기준으로 `serverUrl`을 계산합니다. From 6c86971a37f56854a2eaf1cd7eb84b1926d66b1d Mon Sep 17 00:00:00 2001 From: lumir Date: Tue, 2 Jun 2026 20:39:21 +0900 Subject: [PATCH 4/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/content/learn/lifecycle-of-reactive-effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 318cce6e9..6a478e7fd 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -1114,7 +1114,7 @@ body { -이 두 경우 모두 `canMove`는 effect 내부에서 읽는 반응형 변수입니다. 그렇기 때문에 effect 의존성 목록에 지정해야 합니다. 이렇게 하면 값이 변경될 때마다 effect가 다시 동기화됩니다. +이 두 경우 모두 `canMove`는 effect 내부에서 읽는 반응형 변수입니다. 그렇기 때문에 effect의 의존성 목록에 지정해야 합니다. 이렇게 하면 값이 변경될 때마다 effect가 다시 동기화됩니다. From 7551f4a211fb34673c87c260f5a70ccd183acdd9 Mon Sep 17 00:00:00 2001 From: lumir Date: Tue, 2 Jun 2026 20:39:41 +0900 Subject: [PATCH 5/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/content/learn/lifecycle-of-reactive-effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 6a478e7fd..4a11e1dd2 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -1194,7 +1194,7 @@ body { **린터를 억제하지 않으면 오래된 값으로 인한 문제가 발생하지 않습니다.** 이 버그를 해결하는 방법에는 몇 가지가 있지만 항상 린터 억제를 제거하는 것부터 시작해야 합니다. 그런 다음 코드를 변경하여 린트 오류를 수정하세요. -effect 의존성을 `[handleMove]`로 변경할 수 있지만, 렌더링할 때마다 새로 정의되는 함수가 *될 것*이므로 의존성 배열을 모두 제거하는 것이 좋습니다. 그러면 렌더링할 때마다 effect가 다시 동기화됩니다. +effect의 의존성을 `[handleMove]`로 변경할 수 있지만, 렌더링할 때마다 새로 정의되는 함수가 *될 것*이므로 의존성 배열을 모두 제거하는 것이 좋습니다. 그러면 렌더링할 때마다 effect가 다시 동기화됩니다.