diff --git a/content/docs/concurrent-mode-patterns.md b/content/docs/concurrent-mode-patterns.md index f591c97b9..54625cf93 100644 --- a/content/docs/concurrent-mode-patterns.md +++ b/content/docs/concurrent-mode-patterns.md @@ -1,6 +1,6 @@ --- id: concurrent-mode-patterns -title: Concurrent UI Patterns (Experimental) +title: 컨커런트 UI 패턴 (실험) permalink: docs/concurrent-mode-patterns.html prev: concurrent-mode-suspense.html next: concurrent-mode-adoption.html @@ -15,63 +15,63 @@ next: concurrent-mode-adoption.html
->Caution: +>경고 > ->This page describes **experimental features that are [not yet available](/docs/concurrent-mode-adoption.html) in a stable release**. Don't rely on experimental builds of React in production apps. These features may change significantly and without a warning before they become a part of React. +> 이 페이지의 내용은 **안정된 배포판에서 [사용할 수 없는](/docs/concurrent-mode-adoption.html) 실험적인 기능**입니다. 프로덕션 애플리케이션에 실험적인 React 빌드를 의존하지 마십시오. 이 기능은 React에 편입되기 전에 경고 없이 크게 변경될 수 있습니다. > ->This documentation is aimed at early adopters and people who are curious. **If you're new to React, don't worry about these features** -- you don't need to learn them right now. For example, if you're looking for a data fetching tutorial that works today, read [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) instead. +> 이 문서는 얼리어답터와 호기심 넘치는 사람들을 위해 작성되었습니다. **React 초기 입문자라면 이 기능에 신경 쓰지 않아도 괜찮습니다.** -- 이 내용을 지금 당장 배울 필요는 없습니다. 데이터 패칭을 위한 튜토리얼을 찾는다면 [이 문서](https://www.robinwieruch.de/react-hooks-fetch-data/)를 보세요.
-Usually, when we update the state, we expect to see changes on the screen immediately. This makes sense because we want to keep our app responsive to user input. However, there are cases where we might prefer to **defer an update from appearing on the screen**. - -For example, if we switch from one page to another, and none of the code or data for the next screen has loaded yet, it might be frustrating to immediately see a blank page with a loading indicator. We might prefer to stay longer on the previous screen. Implementing this pattern has historically been difficult in React. Concurrent Mode offers a new set of tools to do that. - -- [Transitions](#transitions) - - [Wrapping setState in a Transition](#wrapping-setstate-in-a-transition) - - [Adding a Pending Indicator](#adding-a-pending-indicator) - - [Reviewing the Changes](#reviewing-the-changes) - - [Where Does the Update Happen?](#where-does-the-update-happen) - - [Transitions Are Everywhere](#transitions-are-everywhere) - - [Baking Transitions Into the Design System](#baking-transitions-into-the-design-system) -- [The Three Steps](#the-three-steps) - - [Default: Receded → Skeleton → Complete](#default-receded-skeleton-complete) - - [Preferred: Pending → Skeleton → Complete](#preferred-pending-skeleton-complete) - - [Wrap Lazy Features in ``](#wrap-lazy-features-in-suspense) - - [Suspense Reveal “Train”](#suspense-reveal-train) - - [Delaying a Pending Indicator](#delaying-a-pending-indicator) - - [Recap](#recap) -- [Other Patterns](#other-patterns) - - [Splitting High and Low Priority State](#splitting-high-and-low-priority-state) - - [Deferring a Value](#deferring-a-value) +일반적으로 상태가 갱신될 때 화면의 즉각적인 변화를 기대합니다. 애플리케이션이 사용자 입력에 반응하는 것을 유지하고 싶기 때문입니다. 하지만 **화면에 나타나는 변화를 지연하고** 싶은 경우도 있습니다. + +예를 들어 한 페이지에서 다른 페이지로 전환할 때 다음 화면에 필요한 코드나 데이터가 전혀 준비되어 있지 않으면 순간적으로 빈 화면에 로딩 중인 모습이 보이고 답답할 수 있습니다. 이전 화면을 좀 더 길게 보여주고 싶을 때도 있습니다. React에서 이런 패턴을 구현하기란 오랫동안 어려웠습니다. 컨커런트 모드는 이 문제를 해결하기 위한 새로운 도구를 제공합니다. + +- [트랜지션](#transitions) + - [setState를 트랜지션에 래핑하기](#wrapping-setstate-in-a-transition) + - [지연 인디케이터 추가하기](#adding-a-pending-indicator) + - [변화 살펴보기](#reviewing-the-changes) + - [어디에서 갱신이 발생하나요?](#where-does-the-update-happen) + - [트랜지션은 모든 곳에 있습니다](#transitions-are-everywhere) + - [디자인 시스템에 트랜지션 구축하기](#baking-transitions-into-the-design-system) +- [세 단계](#the-three-steps) + - [기본: 후퇴 → 스켈레톤 → 완료](#default-receded-skeleton-complete) + - [권장: 보류 → 스켈레톤 → 완료](#preferred-pending-skeleton-complete) + - [지연평가 요소를 ``로 래핑하기](#wrap-lazy-features-in-suspense) + - [서스펜스 공개 '기차'](#suspense-reveal-train) + - [보류 인디케이터 지연하기](#delaying-a-pending-indicator) + - [요약](#recap) +- [기타 패턴](#other-patterns) + - [낮은 우선순위 상태와 높은 우선순위 상태 분할하기](#splitting-high-and-low-priority-state) + - [값 지연하기](#deferring-a-value) - [SuspenseList](#suspenselist) -- [Next Steps](#next-steps) +- [다음 단계](#next-steps) -## Transitions {#transitions} +## 트랜지션 {#transitions} -Let's revisit [this demo](https://codesandbox.io/s/infallible-feather-xjtbu) from the previous page about [Suspense for Data Fetching](/docs/concurrent-mode-suspense.html). +이전 [데이터를 가져오기 위한 서스펜스](/docs/concurrent-mode-suspense.html) 페이지의 [데모를](https://codesandbox.io/s/infallible-feather-xjtbu) 다시 살펴봅시다. -When we click the "Next" button to switch the active profile, the existing page data immediately disappears, and we see the loading indicator for the whole page again. We can call this an "undesirable" loading state. **It would be nice if we could "skip" it and wait for some content to load before transitioning to the new screen.** +프로필을 활성화하기 위해 "Next" 버튼을 누르면 페이지의 데이터가 바로 사라지고 전체 화면에 로딩 화면을 다시 보게 됩니다. '의도치 않은' 로딩 상태라고 할 수 있습니다. **새 화면을 위한 콘텐츠를 불러오는 동안 화면 전환을 생략할 수 있다면 좋을 것입니다.** -React offers a new built-in `useTransition()` Hook to help with this. +React는 이 문제를 해결하기 위해 새로운 `useTransition()` 내장 훅을 제공합니다. -We can use it in three steps. +세 단계에 걸쳐 사용할 수 있습니다. -First, we'll make sure that we're actually using Concurrent Mode. We'll talk more about [adopting Concurrent Mode](/docs/concurrent-mode-adoption.html) later, but for now it's sufficient to know that we need to use `ReactDOM.createRoot()` rather than `ReactDOM.render()` for this feature to work: +먼저 컨커런트 모드를 사용해야 합니다. [컨커런트 모드 채택](/docs/concurrent-mode-adoption.html)에 대해서는 이후 더 많은 이야기를 나눌 것입니다. 지금은 이 기능이 작동하려면 `ReactDOM.render()` 대신 `ReactDOM.createRoot()`를 사용해야 함을 아는 것으로 충분합니다. ```js const rootElement = document.getElementById("root"); -// Opt into Concurrent Mode +// 컨커런트 모드로 설정 ReactDOM.createRoot(rootElement).render(); ``` -Next, we'll add an import for the `useTransition` Hook from React: +그 다음 React에서 `useTransition` 훅을 가져와서 사용합니다. ```js import React, { useState, useTransition, Suspense } from "react"; ``` -Finally, we'll use it inside the `App` component: +마지막으로 `App` 컴포넌트에서 사용합니다. ```js{3-5} function App() { @@ -82,18 +82,18 @@ function App() { // ... ``` -**By itself, this code doesn't do anything yet.** We will need to use this Hook's return values to set up our state transition. There are two values returned from `useTransition`: +**이 코드만으로는 아무것도 실행하지 않습니다.** 상태를 갱신하기 위해 훅의 반환 값을 사용해야 합니다. `useTransition`의 반환 값은 두가지입니다. -* `startTransition` is a function. We'll use it to tell React *which* state update we want to defer. -* `isPending` is a boolean. It's React telling us whether that transition is ongoing at the moment. +* `startTransition`는 함수입니다. React에 **어떤** 상태변화를 지연하고 싶은지 지정할 수 있습니다. +* `isPending`는 불리언 값입니다. 트랜지션 진행 여부를 알 수 있습니다. -We will use them right below. +바로 아래에서 사용하겠습니다. -Note we passed a configuration object to `useTransition`. Its `timeoutMs` property specifies **how long we're willing to wait for the transition to finish**. By passing `{timeoutMs: 3000}`, we say "If the next profile takes more than 3 seconds to load, show the big spinner -- but before that timeout it's okay to keep showing the previous screen". +`useTransition` 훅에 설정 객체를 전달했다는 것을 명심하세요. `timeoutMs` 프로퍼티는 **트랜지션이 완료될 때까지 얼마나 오랫동안 기다릴 것인지** 결정합니다. `{timeoutMs: 3000}` 를 전달한다면 "다음 프로필을 불러오는 데 3초보다 오래 걸린다면 로딩 상태를 보여주고 그전까진 계속 이전 화면을 보여줘도 괜찮아"라는 의미입니다. -### Wrapping setState in a Transition {#wrapping-setstate-in-a-transition} +### setState를 트랜지션에 래핑하기 {#wrapping-setstate-in-a-transition} -Our "Next" button click handler sets the state that switches the current profile in the state: +"Next" 버튼 클릭 이벤트 핸들러는 현재 프로필 상태를 설정합니다. ```js{4} - {isPending ? " Loading..." : null} + {isPending ? "Loading..." : null} ); ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/jovial-lalande-26yep)** +**[CodeSandbox에서 시도해보세요](https://codesandbox.io/s/jovial-lalande-26yep)** -Now, this feels a lot better! When we click Next, it gets disabled because clicking it multiple times doesn't make sense. And the new "Loading..." tells the user that the app didn't freeze. +이제 훨씬 나아 보이네요! 버튼을 여러 번 누르는 것은 이상하기 때문에 버튼을 클릭하면 비활성화됩니다. 그리고 사용자에게 앱이 멈추지 않았다는 것을 알려주기 위해 "Loading..."이라고 알려줍니다. -### Reviewing the Changes {#reviewing-the-changes} +### 변화 살펴보기 {#reviewing-the-changes} -Let's take another look at all the changes we've made since the [original example](https://codesandbox.io/s/infallible-feather-xjtbu): +[원본 예제](https://codesandbox.io/s/infallible-feather-xjtbu) 이후로 변경된 모든 사항을 살펴보겠습니다. ```js{3-5,9,11,14,19} function App() { @@ -182,47 +182,47 @@ function App() { > Next - {isPending ? " Loading..." : null} + {isPending ? "Loading..." : null} ); } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/jovial-lalande-26yep)** +**[CodeSandbox에서 시도해보세요](https://codesandbox.io/s/jovial-lalande-26yep)** -It took us only seven lines of code to add this transition: +7줄의 코드만으로 트랜지션을 추가했습니다. -* We've imported the `useTransition` Hook and used it the component that updates the state. -* We've passed `{timeoutMs: 3000}` to stay on the previous screen for at most 3 seconds. -* We've wrapped our state update into `startTransition` to tell React it's okay to delay it. -* We're using `isPending` to communicate the state transition progress to the user and to disable the button. +* `useTransition` 훅을 가져와 컴포넌트의 상태 갱신 부분에 사용했습니다. +* `{timeoutMs: 3000}` 옵션을 전달하여 최대 3초간 이전 화면을 유지하도록 설정했습니다. +* React가 상태 갱신을 지연할 수 있도록 상태 갱신 코드를 `startTransition` 함수로 래핑했습니다. +* `isPending`을 이용하여 사용자에게 작업 상황을 알리고 버튼을 비활성화합니다. -As a result, clicking "Next" doesn't perform an immediate state transition to an "undesirable" loading state, but instead stays on the previous screen and communicates progress there. +결과적으로 "Next" 버튼을 눌러도 의도하지 않은 로딩 상태로 바로 전환되지 않고 이전 화면에서 진행 상태를 알려줍니다. -### Where Does the Update Happen? {#where-does-the-update-happen} +### 어디에서 갱신이 발생하나요? {#where-does-the-update-happen} -This wasn't very difficult to implement. However, if you start thinking about how this could possibly work, it might become a little mindbending. If we set the state, how come we don't see the result right away? *Where* is the next `` rendering? +위 예제를 구현하는 것은 엄청 어렵진 않았습니다. 하지만 어떻게 이게 작동하는지에 대해서 생각하기 시작하면 약간 어지러울 수 있습니다. 상태를 설정했는데 어떻게 그 결과를 바로 볼 수 없는 걸까요? **어디에서** 다음 `` 렌더링이 어디에서 일어날까요? -Clearly, both "versions" of `` exist at the same time. We know the old one exists because we see it on the screen and even display a progress indicator on it. And we know the new version also exists *somewhere*, because it's the one that we're waiting for! +두 '버전'의 ``가 동시에 존재하는 것은 명확합니다. 이전 스크린 화면에서 로딩 상태까지 보여주고 있기 때문에 이전 버전이 존재한다는 것을 알 수 있습니다. 그리고 새 버전 또한 **어디엔가** 존재한다는 것을 압니다. 왜냐하면 그것을 기다리고 있기 때문이죠! -**But how can two versions of the same component exist at the same time?** +**하지만 어떻게 두 가지 버전의 같은 컴포넌트가 동시에 존재할 수 있는 걸까요?** -This gets at the root of what Concurrent Mode is. We've [previously said](/docs/concurrent-mode-intro.html#intentional-loading-sequences) it's a bit like React working on state update on a "branch". Another way we can conceptualize is that wrapping a state update in `startTransition` begins rendering it *"in a different universe"*, much like in science fiction movies. We don't "see" that universe directly -- but we can get a signal from it that tells us something is happening (`isPending`). When the update is ready, our "universes" merge back together, and we see the result on the screen! +이것이 컨커런트 모드의 존재 이유입니다. React의 작업은 '브랜치'의 상태 갱신과 비슷하다고 [앞서 언급했습니다](/docs/concurrent-mode-intro.html#intentional-loading-sequences). 이 개념을 잡기 위한 또 다른 방법은 `startTransition` 함수로 상태 갱신 코드를 래핑하는 것은 공상과학 영화처럼 **다른 평행 우주**에서 렌더링한다고 생각하는 것입니다. 다른 우주를 직접 "볼" 수는 없습니다. 하지만 무언가 일어나고 있다는 신호(`isPending`)를 들을 수 있습니다. 갱신이 준비되면 '우주들'이 다시 병합되고 그 결과를 화면에서 볼 수 있습니다! -Play a bit more with the [demo](https://codesandbox.io/s/jovial-lalande-26yep), and try to imagine it happening. +[이 데모](https://codesandbox.io/s/jovial-lalande-26yep)를 좀 더 가지고 놀고 무엇이 일어나는지 상상해보세요. -Of course, two versions of the tree rendering *at the same time* is an illusion, just like the idea that all programs run on your computer at the same time is an illusion. An operating system switches between different applications very fast. Similarly, React can switch between the version of the tree you see on the screen and the version that it's "preparing" to show next. +물론 두 버전의 트리 렌더링이 **동시에** 일어나진 않습니다. 컴퓨터의 모든 프로그램이 동시에 실행된다는 것이 허상인 것처럼요. 운영체제는 다른 애플리케이션들을 매우 빠르게 전환합니다. 비슷하게 React도 화면에 보이는 트리 버전과 다음에 노출하기 위해 "준비중"인 버전을 전환할 수 있습니다. -An API like `useTransition` lets you focus on the desired user experience, and not think about the mechanics of how it's implemented. Still, it can be a helpful metaphor to imagine that updates wrapped in `startTransition` happen "on a branch" or "in a different world". +`useTransition` 같은 API를 사용하면 원하는 사용자 경험에 초점을 맞출 수 있고 어떻게 구현했는지 생각 하지 않아도 됩니다. `startTransition`에 래핑된 트랜지션이 "브랜치"나 "다른 세계"에서 일어난다는 비유는 이해에 도움이 될 수 있습니다. -### Transitions Are Everywhere {#transitions-are-everywhere} +### 트랜지션은 모든 곳에 있습니다. {#transitions-are-everywhere} -As we learned from the [Suspense walkthrough](/docs/concurrent-mode-suspense.html), any component can "suspend" any time if some data it needs is not ready yet. We can strategically place `` boundaries in different parts of the tree to handle this, but it won't always be enough. +[Suspense walkthrough](/docs/concurrent-mode-suspense.html)에서 어떤 컴포넌트라도 추가적인 데이터가 필요하지만 준비되지 않았다면 언제든지 '서스펜드' 할 수 있다는 것을 배웠습니다. 중단 상태를 처리하기 위해 ``를 트리의 다른 부분에 전략적으로 배치할 수는 있지만 항상 충분하지는 않습니다. -Let's get back to our [first Suspense demo](https://codesandbox.io/s/frosty-hermann-bztrp) where there was just one profile. Currently, it fetches the data only once. We'll add a "Refresh" button to check for server updates. +하나의 프로필만 있던 [첫 번째 서스펜스 데모](https://codesandbox.io/s/frosty-hermann-bztrp)로 돌아가 봅시다. 이 예제는 오직 데이터를 한 번만 페치합니다. 서버 변경사항을 검사하기 위한 "Refresh" 버튼을 추가하겠습니다. -Our first attempt might look like this: +첫 번째 시도는 다음과 같이 생겼습니다. ```js{6-8,13-15} const initialResource = fetchUserAndPosts(); @@ -248,13 +248,13 @@ function ProfilePage() { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/boring-shadow-100tf)** +**[CodeSandbox에서 시도해보세요](https://codesandbox.io/s/boring-shadow-100tf)** -In this example, we start data fetching at the load *and* every time you press "Refresh". We put the result of calling `fetchUserAndPosts()` into state so that components below can start reading the new data from the request we just kicked off. +이 예제에선 페이지가 로드되거나 "Refresh" 버튼을 누를 때 마다 데이터를 가져옵니다. `fetchUserAndPosts()`의 반환값을 상태에 저장하여 하위 컴포넌트들이 요청에서 가져온 데이터를 읽을 수 있게 하겠습니다. -We can see in [this example](https://codesandbox.io/s/boring-shadow-100tf) that pressing "Refresh" works. The `` and `` components receive a new `resource` prop that represents the fresh data, they "suspend" because we don't have a response yet, and we see the fallbacks. When the response loads, we can see the updated posts (our fake API adds them every 3 seconds). +[이 예제](https://codesandbox.io/s/boring-shadow-100tf)를 보면 "Refresh" 버튼을 누르는 것은 동작합니다. `` 와 `` 컴포넌트들은 새로운 최신 데이터를 표현하는 `resource` 프로퍼티를 전달받습니다. `fetchUserAndPosts` 호출 직후에 아무런 응답을 받지 못했기 때문에 컴포넌트는 바로 '서스펜드' 상태가 되고 화면에는 폴백을 보게 됩니다. 응답을 받은 뒤엔 새롭게 갱신된 포스트를 볼 수 있습니다. (우리의 목 API는 3초 마다 새로운 포스트를 추가합니다.) -However, the experience feels really jarring. We were browsing a page, but it got replaced by a loading state right as we were interacting with it. It's disorienting. **Just like before, to avoid showing an undesirable loading state, we can wrap the state update in a transition:** +하지만 위 경험은 자연스럽지 않습니다. 우리는 한 페이지를 브라우징하고 있었는데 버튼을 클릭한 직후에 바로 로딩 상태로 전환되어 사용자를 혼란스럽게 합니다. **이전처럼, 의도치 않은 로딩 상태를 숨기기 위해서 상태 갱신을 트랜지션에 래핑할 수 있습니다:** ```js{2-5,9-11,21} function ProfilePage() { @@ -287,15 +287,15 @@ function ProfilePage() { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/sleepy-field-mohzb)** +**[CodeSandbox에서 시도해보세요](https://codesandbox.io/s/sleepy-field-mohzb)** -This feels a lot better! Clicking "Refresh" doesn't pull us away from the page we're browsing anymore. We see something is loading "inline", and when the data is ready, it's displayed. +훨씬 나아 보입니다! "Refresh" 버튼을 클릭해도 우리가 브라우징하고 있는 페이지가 사라지지 않습니다. 우리는 인라인으로 뭔가 로딩되고 있다는 것을 보고 데이터가 준비된 이후에 새로운 데이터가 보입니다. -### Baking Transitions Into the Design System {#baking-transitions-into-the-design-system} +### 디자인시스템에 트랜지션 구축하기 {#baking-transitions-into-the-design-system} -We can now see that the need for `useTransition` is *very* common. Pretty much any button click or interaction that can lead to a component suspending needs to be wrapped in `useTransition` to avoid accidentally hiding something the user is interacting with. +이제 `useTransition`의 필요성이 **매우** 일반적이라는 걸 알 수 있습니다. 사용자가 상호작용하는 대상을 실수로 숨기지 않도록 컴포넌트를 서스펜드 상태로 만들 수 있는 대부분 버튼클릭이나 상호작용은 `useTransition`으로 래핑해야 합니다. -This can lead to a lot of repetitive code across components. This is why **we generally recommend to bake `useTransition` into the *design system* components of your app**. For example, we can extract the transition logic into our own `