From 5b3fd07a64bed8d6ead07fc51d76fec6e03e655e Mon Sep 17 00:00:00 2001 From: cadenzah Date: Sun, 19 Jan 2020 00:04:45 +0900 Subject: [PATCH 1/3] initial draft of translation --- content/docs/concurrent-mode-suspense.md | 378 ++++++++++++----------- 1 file changed, 200 insertions(+), 178 deletions(-) diff --git a/content/docs/concurrent-mode-suspense.md b/content/docs/concurrent-mode-suspense.md index 12ad112b2..7adb9cf65 100644 --- a/content/docs/concurrent-mode-suspense.md +++ b/content/docs/concurrent-mode-suspense.md @@ -1,6 +1,6 @@ --- id: concurrent-mode-suspense -title: Suspense for Data Fetching (Experimental) +title: 데이터를 가져오기 위한 Suspense (실험 단계) permalink: docs/concurrent-mode-suspense.html prev: concurrent-mode-intro.html next: concurrent-mode-patterns.html @@ -15,50 +15,52 @@ next: concurrent-mode-patterns.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/)를 대신 읽으시기 바랍니다.
React 16.6 added a `` component that lets you "wait" for some code to load and declaratively specify a loading state (like a spinner) while we're waiting: +React 16.6 버전에서는 코드를 불러오는 동안 "기다릴 수 있고", 기다리는 동안 로딩 상태(스피너와 같은 것)를 선언적으로 지정할 수 있도록 `` 컴포넌트가 추가되었습니다. + ```jsx -const ProfilePage = React.lazy(() => import('./ProfilePage')); // Lazy-loaded +const ProfilePage = React.lazy(() => import('./ProfilePage')); // 지연 로딩 -// Show a spinner while the profile is loading +// 프로필을 불러오는 동안 스피너를 표시합니다 }> ``` -Suspense for Data Fetching is a new feature that lets you also use `` to **declaratively "wait" for anything else, including data.** This page focuses on the data fetching use case, but it can also wait for images, scripts, or other asynchronous work. - -- [What Is Suspense, Exactly?](#what-is-suspense-exactly) - - [What Suspense Is Not](#what-suspense-is-not) - - [What Suspense Lets You Do](#what-suspense-lets-you-do) -- [Using Suspense in Practice](#using-suspense-in-practice) - - [What If I Don’t Use Relay?](#what-if-i-dont-use-relay) - - [For Library Authors](#for-library-authors) -- [Traditional Approaches vs Suspense](#traditional-approaches-vs-suspense) - - [Approach 1: Fetch-on-Render (not using Suspense)](#approach-1-fetch-on-render-not-using-suspense) - - [Approach 2: Fetch-Then-Render (not using Suspense)](#approach-2-fetch-then-render-not-using-suspense) - - [Approach 3: Render-as-You-Fetch (using Suspense)](#approach-3-render-as-you-fetch-using-suspense) -- [Start Fetching Early](#start-fetching-early) - - [We’re Still Figuring This Out](#were-still-figuring-this-out) -- [Suspense and Race Conditions](#suspense-and-race-conditions) - - [Race Conditions with useEffect](#race-conditions-with-useeffect) - - [Race Conditions with componentDidUpdate](#race-conditions-with-componentdidupdate) - - [The Problem](#the-problem) - - [Solving Race Conditions with Suspense](#solving-race-conditions-with-suspense) -- [Handling Errors](#handling-errors) -- [Next Steps](#next-steps) - -## What Is Suspense, Exactly? {#what-is-suspense-exactly} - -Suspense lets your components "wait" for something before they can render. In [this example](https://codesandbox.io/s/frosty-hermann-bztrp), two components wait for an asynchronous API call to fetch some data: +데이터를 가져오기 위한 Suspense는 ``를 사용하여 선언적으로 데이터를 비롯한 무엇이든 "기다릴" 수 있도록 해주는 새로운 기능입니다. 이 페이지에서는 사용 사례 가운데 데이터 로딩에 초점을 두지만, 이 기능은 이미지, 스크립트, 그 밖의 비동기 작업을 기다리는 데에도 사용될 수 있습니다. + +- [Suspense가 정확히 무엇인가요?](#what-is-suspense-exactly) + - [Suspense가 아닌 것](#what-suspense-is-not) + - [Suspense로 가능한 것](#what-suspense-lets-you-do) +- [실전에서 Suspense 사용하기](#using-suspense-in-practice) + - [Relay를 사용하지 않는 경우에는?](#what-if-i-dont-use-relay) + - [라이브러리 개발자의 경우](#for-library-authors) +- [기존의 접근 방식 vs Suspense](#traditional-approaches-vs-suspense) + - [접근 방식 1: 렌더링 직후 불러오기 (Suspense 미사용)](#approach-1-fetch-on-render-not-using-suspense) + - [접근 방식 2: 불러오기 이후 렌더링 (Suspense 미사용)](#approach-2-fetch-then-render-not-using-suspense) + - [접근 방식 3: 불러올 때 렌더링 (Suspense 사용)](#approach-3-render-as-you-fetch-using-suspense) +- [일찍 불러오기 시작하기](#start-fetching-early) + - [여전히 알아보는 중입니다](#were-still-figuring-this-out) +- [Suspense와 경쟁 상태](#suspense-and-race-conditions) + - [useEffect에 의한 경쟁 상태](#race-conditions-with-useeffect) + - [componentDidUpdate에 의한 경쟁 상태](#race-conditions-with-componentdidupdate) + - [문제점](#the-problem) + - [Suspense로 경쟁 상태 해결하기](#solving-race-conditions-with-suspense) +- [오류 처리하기](#handling-errors) +- [다음 단계](#next-steps) + +## Suspense가 정확히 무엇인가요? {#what-is-suspense-exactly} + +Suspense를 사용하면 컴포넌트가 렌더링되기 전까지 기다릴 수 있습니다. [이 예시](https://codesandbox.io/s/frosty-hermann-bztrp)에서는 두 컴포넌트가 데이터를 불러오는 비동기 API 호출을 기다립니다. ```js const resource = fetchProfileData(); @@ -75,13 +77,13 @@ function ProfilePage() { } function ProfileDetails() { - // Try to read user info, although it might not have loaded yet + // 비록 아직 불러오기가 완료되지 않았겠지만, 사용자 정보 읽기를 시도합니다 const user = resource.user.read(); return

{user.name}

; } function ProfileTimeline() { - // Try to read posts, although they might not have loaded yet + // 비록 아직 불러오기가 완료되지 않았겠지만, 게시글 읽기를 시도합니다 const posts = resource.posts.read(); return (
    @@ -93,91 +95,101 @@ function ProfileTimeline() { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/frosty-hermann-bztrp)** +**[CodeSandbox에서 따라해보기](https://codesandbox.io/s/frosty-hermann-bztrp)** + +이 데모는 일부분에 불과합니다. 아직 잘 이해가 가지 않아도 걱정하지 마세요. 어떤 식으로 작동하는지 아래에서 더 자세하게 이야기할 겁니다. Suspense는 *메커니즘*에 가까운 것이고, 위 예시에 등장하는 `fetchProfileData()` 또는 `resource.posts.read()`와 같은 특정 API는 여기서 그렇게 중요하지 않다는 것만 유념하시기 바랍니다. 더 궁금하시다면, 각 API의 정의를 [샌드박스 데모](https://codesandbox.io/s/frosty-hermann-bztrp)에서 확인할 수 있습니다. -This demo is a teaser. Don't worry if it doesn't quite make sense yet. We'll talk more about how it works below. Keep in mind that Suspense is more of a *mechanism*, and particular APIs like `fetchProfileData()` or `resource.posts.read()` in the above example are not very important. If you're curious, you can find their definitions right in the [demo sandbox](https://codesandbox.io/s/frosty-hermann-bztrp). +Suspense는 데이터 불러오기 라이브러리가 아닙니다. Suspense는 *컴포넌트가 읽어들이고 있는 데이터가 아직 준비되지 않았다*고 React에 알려줄 수 있는, **데이터 불러오기 라이브러리에서 사용할 수 있는 메커니즘**입니다. 이후에 React는 데이터가 준비되기를 기다렸다가 UI를 갱신할 수 있습니다. Facebook에서는 Relay와 Relay가 제공하는 [새로운 Suspense 통합 기능](https://relay.dev/docs/en/experimental/step-by-step)을 사용하고 있습니다. Apollo와 같은 다른 라이브러리에서도 유사한 통합 기능을 제공할 것으로 기대합니다. -Suspense is not a data fetching library. It's a **mechanism for data fetching libraries** to communicate to React that *the data a component is reading is not ready yet*. React can then wait for it to be ready and update the UI. At Facebook, we use Relay and its [new Suspense integration](https://relay.dev/docs/en/experimental/step-by-step). We expect that other libraries like Apollo can provide similar integrations. +장기적인 관점으로는, Suspense가 데이터 출처와 상관없이 컴포넌트로부터 비동기 데이터를 읽는 데에 사용되는 주된 방식으로 거듭나길 바라고 있습니다. -In the long term, we intend Suspense to become the primary way to read asynchronous data from components -- no matter where that data is coming from. +### Suspense가 아닌 것 {#what-suspense-is-not} -### What Suspense Is Not {#what-suspense-is-not} +Suspense는 위의 문제에 대한 기존의 접근 방식과는 상당히 다르기 때문에, 처음 접할 때 종종 오해를 만들어냅니다. 가장 흔한 오해들을 명확히 짚어보겠습니다. -Suspense is significantly different from existing approaches to these problems, so reading about it for the first time often leads to misconceptions. Let's clarify the most common ones: + * **Suspense는 데이터 불러오기에 대한 구현이 아닙니다.** 당신이 GraphQL, REST, 또는 그 이외의 특정한 데이터 형식, 라이브러리, 통신 방식, 프로토콜 등 * **It is not a data fetching implementation.** It does not assume that you use GraphQL, REST, or any other particular data format, library, transport, or protocol. - * **It is not a ready-to-use client.** You can't "replace" `fetch` or Relay with Suspense. But you can use a library that's integrated with Suspense (for example, [new Relay APIs](https://relay.dev/docs/en/experimental/api-reference)). + * **Suspense는 바로 사용할 수 있는 클라이언트가 아닙니다.** `fetch` 또는 Relay를 Suspense로 "대체"할 수 없습니다. 다만, Suspense로 통합된 라이브러리를 사용할 수는 있습니다(예를 들어, [새로운 Relay API](https://relay.dev/docs/en/experimental/api-reference)와 같은 것이 있습니다). - * **It does not couple data fetching to the view layer.** It helps orchestrate displaying the loading states in your UI, but it doesn't tie your network logic to React components. + * **Suspense는 데이터 불러오기 작업과 뷰 레이어를 결합해주지 않습니다.** UI 상에 로딩 상태를 표시할 수 있도록 조정하는 것을 돕지만, 이는 네트워크 로직을 React 컴포넌트에 종속시키는 것은 아닙니다. -### What Suspense Lets You Do {#what-suspense-lets-you-do} +### Suspense로 가능한 것 {#what-suspense-lets-you-do} +<<<<<<< HEAD So what's the point of Suspense? There are a few ways we can answer this: +======= +그렇다면 Suspense는 왜 사용하는 것일까요? 이에 대한 몇 가지 답이 있습니다. +>>>>>>> initial draft of translation -* **It lets data fetching libraries deeply integrate with React.** If a data fetching library implements Suspense support, using it from React components feels very natural. +* **데이터 불러오기 라이브러리들이 React와 깊게 결합할 수 있도록 해줍니다.** 데이터 불러오기 라이브러리가 Suspense 지원을 구현한다면, React 컴포넌트에서 이를 사용하는 것이 아주 자연스럽게 느껴질 것입니다. -* **It lets you orchestrate intentionally designed loading states.** It doesn't say _how_ the data is fetched, but it lets you closely control the visual loading sequence of your app. +* **의도적으로 설계된 로딩 상태를 조정할 수 있도록 해줍니다.** Suspense는 데이터가 _어떻게_ 불러져야 하는지를 정하지 않고, 당신이 앱의 시각적인 로딩 단계를 밀접하게 통제할 수 있도록 해줍니다. +<<<<<<< HEAD * **It helps you avoid race conditions.** Even with `await`, asynchronous code is often error-prone. Suspense feels more like reading data *synchronously* — as if it were already loaded. +======= +* **경쟁 상태(Race Condition)를 피할 수 있도록 돕습니다.** `await`를 사용하더라도 비동기 코드는 종종 오류가 발생하기 쉽습니다. Suspense를 사용하면 데이터를 *동기적으로* 읽어오는 것처럼 느껴지게 해줍니다. 마치 이미 불러오기가 완료된 것처럼 말입니다. +>>>>>>> initial draft of translation -## Using Suspense in Practice {#using-suspense-in-practice} +## 실전에서 Suspense 사용하기 {#using-suspense-in-practice} -At Facebook, so far we have only used the Relay integration with Suspense in production. **If you're looking for a practical guide to get started today, [check out the Relay Guide](https://relay.dev/docs/en/experimental/step-by-step)!** It demonstrates patterns that have already worked well for us in production. +Facebook에서는 현재 Suspense를 사용한 Relay 통합만을 프로덕션 환경에서 사용하고 있습니다. **바로 시작할 수 있는 실무 가이드를 찾고 계시다면, [Relay 가이드를 확인하시기 바랍니다](https://relay.dev/docs/en/experimental/step-by-step)!** 이 문서에서는 프로덕션 환경에서 이미 잘 작동하고 있는 패턴들을 설명합니다. -**The code demos on this page use a "fake" API implementation rather than Relay.** This makes them easier to understand if you're not familiar with GraphQL, but they won't tell you the "right way" to build an app with Suspense. This page is more conceptual and is intended to help you see *why* Suspense works in a certain way, and which problems it solves. +**이 페이지의 코드 데모는 Relay가 아닌 "가짜" API 구현을 사용합니다.** 이 데모는 당신이 GraphQL에 익숙하지 않아도 이해하기 쉽지만, Suspense를 사용하여 앱을 만드는 "올바른 방법"을 알려주지는 않습니다. 이 페이지는 보다 개념적인 측면이 강하며, Suspense가 *왜* 특정 방식으로 작동하는지, 어떤 문제를 해결하고자 하는지를 당신이 이해하도록 돕고자 하는 목적을 가집니다. -### What If I Don't Use Relay? {#what-if-i-dont-use-relay} +### Relay를 사용하지 않는 경우에는? {#what-if-i-dont-use-relay} -If you don't use Relay today, you might have to wait before you can really try Suspense in your app. So far, it's the only implementation that we tested in production and are confident in. +당장 Relay를 사용할 것이 아니라면, 앱에서 Suspense를 실제로 사용할 수 있게 될 때까지 기다려야 할 겁니다. 현재까지 프로덕션 환경에서의 테스트를 완료하고, 사용에 문제가 없다고 자신할 수 있는 구현은 Relay 뿐입니다. -Over the next several months, many libraries will appear with different takes on Suspense APIs. **If you prefer to learn when things are more stable, you might prefer to ignore this work for now, and come back when the Suspense ecosystem is more mature.** +앞으로 수 개월 동안, Suspense API들을 사용하는 다양한 형태의 라이브러리들이 등장할 것입니다. **기능들이 안정된 뒤에 배우고 싶으시다면, 이와 관련된 작업은 잠시 잊고 계시다가 Suspense의 생태계가 보다 성숙한 뒤에 돌아오는 것이 좋을 겁니다.** -You can also write your own integration for a data fetching library, if you'd like. +원하신다면, 데이터 불러오기 라이브러리를 위한 통합 기능을 직접 작성해보는 것도 좋습니다. -### For Library Authors {#for-library-authors} +### 라이브러리 개발자의 경우 {#for-library-authors} -We expect to see a lot of experimentation in the community with other libraries. There is one important thing to note for data fetching library authors. +우리는 라이브러리를 개발하는 커뮤니티에서 다양한 실험이 이루어지기를 기대하고 있습니다. 데이터 불러오기 라이브러리를 개발하는 분들께 한 가지 말씀드릴 점이 있습니다. -Although it's technically doable, Suspense is **not** currently intended as a way to start fetching data when a component renders. Rather, it lets components express that they're "waiting" for data that is *already being fetched*. **[Building Great User Experiences with Concurrent Mode and Suspense](/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.html) describes why this matters and how to implement this pattern in practice.** +Suspense는 기술적으로는 사용 가능한 상태이지만, 컴포넌트가 렌더링될 때 Suspense를 사용하여 데이터 불러오기를 시작하는 것은 현재 의도된 사용 방식이 **아닙니다.** 오히려, Suspense는 컴포넌트로 하여금 *이미 불러오기가 완료된* 데이터를 "기다리는 중"임을 나타내도록 해줍니다. **[Concurrent 모드와 Suspense를 사용하여 좋은 사용자 경험 만들기](/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.html)** 문서에서 왜 이 사안이 중요한지, 그리고 이러한 패턴을 실무에서 어떻게 구현하는지를 설명합니다. -Unless you have a solution that helps prevent waterfalls, we suggest to prefer APIs that favor or enforce fetching before render. For a concrete example, you can look at how [Relay Suspense API](https://relay.dev/docs/en/experimental/api-reference#usepreloadedquery) enforces preloading. Our messaging about this hasn't been very consistent in the past. Suspense for Data Fetching is still experimental, so you can expect our recommendations to change over time as we learn more from production usage and understand the problem space better. +워터폴(Waterfall) 문제를 방지할 해결책이 있는 것이 아니라면, 렌더링 이전에 불러오기를 먼저 수행하는 API의 사용을 권장합니다. 구체적인 예시를 보려면, [Relay Suspense API](https://relay.dev/docs/en/experimental/api-reference#usepreloadedquery)가 프리 로딩(Preloading)을 수행하는 방식을 살펴보시기 바랍니다. 이 사안에 대한 우리의 입장은 지금까지 그렇게 일관적이지 않았습니다. 데이터 불러오기를 위한 Suspense는 현재에도 실험 단계이므로, 프로덕션 사용 예시와 이 사안에 대한 연구가 더 이루어지는 과정에서 우리가 제시하는 의견이 달라질 수도 있습니다. -## Traditional Approaches vs Suspense {#traditional-approaches-vs-suspense} +## 기존의 접근 방식 vs Suspense {#traditional-approaches-vs-suspense} -We could introduce Suspense without mentioning the popular data fetching approaches. However, this makes it more difficult to see which problems Suspense solves, why these problems are worth solving, and how Suspense is different from the existing solutions. +Suspense를 소개할 때 대중적인 데이터 불러오기 방식을 언급하지 않을 수도 있을 것입니다. 하지만, 그러면 Suspense가 해결하고자 하는 문제가 무엇인지, 왜 그 문제가 해결할 가치를 가지는지, Suspense가 기존의 해결책과 다른 점이 무엇인지 이해하기 어려울 것입니다. -Instead, we'll look at Suspense as a logical next step in a sequence of approaches: +대신, Suspense를 일련의 접근 방식들에서 논리적인 다음 단계로 바라보겠습니다. -* **Fetch-on-render (for example, `fetch` in `useEffect`):** Start rendering components. Each of these components may trigger data fetching in their effects and lifecycle methods. This approach often leads to "waterfalls". -* **Fetch-then-render (for example, Relay without Suspense):** Start fetching all the data for the next screen as early as possible. When the data is ready, render the new screen. We can't do anything until the data arrives. -* **Render-as-you-fetch (for example, Relay with Suspense):** Start fetching all the required data for the next screen as early as possible, and start rendering the new screen *immediately — before we get a network response*. As data streams in, React retries rendering components that still need data until they're all ready. +* **렌더링 직후 불러오기 (예를 들어, `useEffect` 내에서 `fetch`):** 컴포넌트 렌더링을 시작합니다. 각각의 컴포넌트는 Effect와 생명 주기 메서드 내에서 데이터 불러오기를 발동시킵니다. 이 접근법은 종종 "워터폴"로 이어집니다. +* **불러오기 이후 렌더링 (예를 들어, Suspense 없이 Relay 사용):** 최대한 일찍 다음 화면을 위한 데이터 불러오기를 시작합니다. 데이터가 준비되었을 때 화면을 렌더링합니다. 데이터가 도착하기 전까지는 아무 것도 할 수 없습니다. +* **불러올 때 렌더링 (예를 들어, Suspense와 함께 Relay 사용):** 최대한 일찍 다음 화면에서 필요한 데이터 불러오기를 시작하고, 다음 화면 렌더링을 *네트워크 응답을 받기 전에 즉시* 시작합니다. 데이터가 흘러들어옴에 따라, React는 모든 데이터가 준비될 때까지 데이터를 필요로 하는 컴포넌트의 렌더링을 다시 시도합니다. ->Note +>주의 > ->This is a bit simplified, and in practice solutions tend to use a mix of different approaches. Still, we will look at them in isolation to better contrast their tradeoffs. +>위의 설명은 다소 단순화된 것으로, 실제 해결 방안은 다양한 접근 방식을 혼합하여 사용하게 됩니다. 하지만 장단점을 잘 비교할 수 있도록 각각을 분리하여 생각해보겠습니다. -To compare these approaches, we'll implement a profile page with each of them. +각 접근 방식을 비교하기 위하여, 각각을 사용하여 프로필 페이지를 구현하겠습니다. -### Approach 1: Fetch-on-Render (not using Suspense) {#approach-1-fetch-on-render-not-using-suspense} +### 접근 방식 1: 렌더링 직후 불러오기 (Suspense 미사용) {#approach-1-fetch-on-render-not-using-suspense} -A common way to fetch data in React apps today is to use an effect: +React 앱에서 데이터를 불러오는 가장 흔한 방식은 Effect를 사용하는 것입니다. ```js -// In a function component: +// 함수 컴포넌트에서: useEffect(() => { fetchSomething(); }, []); -// Or, in a class component: +// 또는, 클래스 컴포넌트에서: componentDidMount() { fetchSomething(); } ``` -We call this approach "fetch-on-render" because it doesn't start fetching until *after* the component has rendered on the screen. This leads to a problem known as a "waterfall". +이러한 접근 방식을 "렌더링 직후 불러오기"라고 부릅니다. 왜냐하면 화면 상에 컴포넌트가 렌더링 완료된 *후에* 비로소 데이터 불러오기를 시작하기 때문입니다. 이는 "워터폴"이라고 부르는 문제로 이어집니다. -Consider these `` and `` components: +아래의 ``와 `` 컴포넌트를 보시기 바랍니다. ```js{4-6,22-24} function ProfilePage() { @@ -218,26 +230,30 @@ function ProfileTimeline() { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/fragrant-glade-8huj6)** +**[CodeSandbox에서 따라해보기](https://codesandbox.io/s/fragrant-glade-8huj6)** -If you run this code and watch the console logs, you'll notice the sequence is: +위의 코드를 실행하고 콘솔 로그를 살펴보면, 아래와 같은 일련의 결과를 확인할 수 있습니다. -1. We start fetching user details -2. We wait... -3. We finish fetching user details -4. We start fetching posts -5. We wait... -6. We finish fetching posts +1. 사용자 정보 불러오기 시작 +2. 기다리기... +3. 사용자 정보 불러오기 완료 +4. 게시글 불러오기 +5. 기다리기... +6. 게시글 불러오기 완료 -If fetching user details takes three seconds, we'll only *start* fetching the posts after three seconds! That's a "waterfall": an unintentional *sequence* that should have been parallelized. +만약 사용자 정보 불러오기가 3초 소요된다면, 3초가 지난 뒤에야 비로소 게시글 불러오기를 *시작*할 수 있는 것입니다! 이것이 바로 "워터폴"로, 병렬화될 수 있었으나 의도하지 않게 *순차적으로* 실행되는 현상입니다. +<<<<<<< HEAD Waterfalls are common in code that fetches data on render. They're possible to solve, but as the product grows, many people prefer to use a solution that guards against this problem. +======= +워터폴은 렌더링 직후 데이터를 불러오는 코드에서 흔히 발생합니다. 이를 고치는 것은 가능하지만, 앱이 거대해짐에 따라 많은 사람들은 이 문제를 방지할 수 있는 해결책을 원할 것입니다. +>>>>>>> initial draft of translation -### Approach 2: Fetch-Then-Render (not using Suspense) {#approach-2-fetch-then-render-not-using-suspense} +### 접근 방식 2: 불러오기 이후 렌더링 (Suspense 미사용) {#approach-2-fetch-then-render-not-using-suspense} -Libraries can prevent waterfalls by offering a more centralized way to do data fetching. For example, Relay solves this problem by moving the information about the data a component needs to statically analyzable *fragments*, which later get composed into a single query. +라이브러리는 데이터를 불러오는 데에 있어 보다 중앙화된 방식을 제공하는 것으로 워터폴을 방지할 수 있습니다. 예를 들어 Relay의 경우, 컴포넌트가 필요로 하는 데이터에 대한 정보를 정적으로 분석할 수 있는 *부분들*로 옮겨서 이 문제를 해결합니다. 이 부분들은 이후에 하나의 단일 쿼리로 통합됩니다. -On this page, we don't assume knowledge of Relay, so we won't be using it for this example. Instead, we'll write something similar manually by combining our data fetching methods: +이 페이지에서는 Relay에 대한 배경 지식이 없다고 가정하므로, Relay를 예시로 들지 않겠습니다. 대신, 데이터 불러오기 메서드를 하나로 합쳐서, 비슷한 앞서 설명한 것과 유사한 코드를 직접 작성해보겠습니다. ```js function fetchProfileData() { @@ -250,10 +266,14 @@ function fetchProfileData() { } ``` +<<<<<<< HEAD In this example, `` waits for both requests but starts them in parallel: +======= +아래의 예시에서는 ``가 두 요청을 기다리는데, 두 요청은 동시에 시작됩니다. +>>>>>>> initial draft of translation ```js{1,2,8-13} -// Kick off fetching as early as possible +// 최대한 일찍 불러오기를 발동시킵니다 const promise = fetchProfileData(); function ProfilePage() { @@ -278,7 +298,7 @@ function ProfilePage() { ); } -// The child doesn't trigger fetching anymore +// 자식 컴포넌트들은 더 이상 불러오기를 발동시키지 않습니다 function ProfileTimeline({ posts }) { if (posts === null) { return

    Loading posts...

    ; @@ -293,38 +313,38 @@ function ProfileTimeline({ posts }) { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/wandering-morning-ev6r0)** +**[CodeSandbox에서 따라해보기](https://codesandbox.io/s/wandering-morning-ev6r0)** -The event sequence now becomes like this: +이벤트가 발동하는 순서는 이제 아래와 같이 바뀝니다. -1. We start fetching user details -2. We start fetching posts -3. We wait... -4. We finish fetching user details -5. We finish fetching posts +1. 사용자 정보를 불러오기 시작 +2. 게시글 불러오기 시작 +3. 기다리기... +4. 사용자 정보 불러오기 완료 +5. 게시글 불러오기 완료 -We've solved the previous network "waterfall", but accidentally introduced a different one. We wait for *all* data to come back with `Promise.all()` inside `fetchProfileData`, so now we can't render profile details until the posts have been fetched too. We have to wait for both. +기존에 존재했던 네트워크 "워터폴" 현상은 고쳤지만, 의도하지 않은 또다른 문제를 만들었습니다. `fetchProfileData` 내에서 `Promise.all()`을 사용하는 과정에서 *모든* 데이터가 반환되기를 기다려야 합니다. 따라서 게시글들을 모두 불러오기 전까지는 프로필 정보를 렌더링할 수 없습니다. 둘 다 기다려야 합니다. -Of course, this is possible to fix in this particular example. We could remove the `Promise.all()` call, and wait for both Promises separately. However, this approach gets progressively more difficult as the complexity of our data and component tree grows. It's hard to write reliable components when arbitrary parts of the data tree may be missing or stale. So fetching all data for the new screen and *then* rendering is often a more practical option. +물론, 이 예시에서는 이를 고칠 수 있습니다. `Promise.all()` 호출을 없애고, 두 프라미스를 따로 기다리면 됩니다. 하지만, 이러한 접근 방식은 데이터와 컴포넌트 트리의 복잡도가 커짐에 따라 점점 더 어려워집니다. 데이터 트리 내의 임의 부분이 사라지거나 오래될 수 있는 상황에서는 신뢰할 수 있는 컴포넌트를 작성하기 어렵습니다. 따라서 새로운 화면을 위한 데이터를 모두 불러오고 *그 다음에* 렌더링하는 것이 종종 보다 현실적인 선택지입니다. -### Approach 3: Render-as-You-Fetch (using Suspense) {#approach-3-render-as-you-fetch-using-suspense} +### 접근 방식 3: 불러올 때 렌더링 (Suspense 사용) {#approach-3-render-as-you-fetch-using-suspense} -In the previous approach, we fetched data before we called `setState`: +직전의 접근 방식에서는 아래와 같이, `setState`를 호출하기 전에 데이터를 불러왔습니다. -1. Start fetching -2. Finish fetching -3. Start rendering +1. 불러오기 시작 +2. 불러오기 완료 +3. 렌더링 시작 -With Suspense, we still start fetching first, but we flip the last two steps around: +Suspense를 사용하면, 불러오기를 먼저 시작하면서도 아래와 같이 마지막 두 단계의 순서를 바꿔줄 수 있습니다. -1. Start fetching -2. **Start rendering** -3. **Finish fetching** +1. 불러오기 시작 +2. **불러오기 완료** +3. **렌더링 시작** -**With Suspense, we don't wait for the response to come back before we start rendering.** In fact, we start rendering *pretty much immediately* after kicking off the network request: +**Suspense를 사용하면, 렌더링을 시작하기 전에 응답이 오기를 기다리지 않아도 됩니다.** 사실 네트워크 요청을 발동시키고서, 아래와 같이 *상당히 바로* 렌더링을 발동시킵니다. ```js{2,17,23} -// This is not a Promise. It's a special object from our Suspense integration. +// 이것은 프라미스가 아닙니다. Suspense 통합에서 만들어낸 특별한 객체입니다. const resource = fetchProfileData(); function ProfilePage() { @@ -339,13 +359,13 @@ function ProfilePage() { } function ProfileDetails() { - // Try to read user info, although it might not have loaded yet + // 아직 로딩이 완료되지 않았더라도, 사용자 정보 읽기를 시도합니다 const user = resource.user.read(); return

    {user.name}

    ; } function ProfileTimeline() { - // Try to read posts, although they might not have loaded yet + // 아직 로딩이 완료되지 않았더라도, 게시글 읽기를 시도합니다 const posts = resource.posts.read(); return (
      @@ -357,49 +377,51 @@ function ProfileTimeline() { } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/frosty-hermann-bztrp)** +**[CodeSandbox에서 따라해보기](https://codesandbox.io/s/frosty-hermann-bztrp)** -Here's what happens when we render `` on the screen: +화면 상에 ``를 렌더링할 때에 아래와 같은 일들이 벌어집니다. -1. We've already kicked off the requests in `fetchProfileData()`. It gave us a special "resource" instead of a Promise. In a realistic example, it would be provided by our data library's Suspense integration, like Relay. -2. React tries to render ``. It returns `` and `` as children. -3. React tries to render ``. It calls `resource.user.read()`. None of the data is fetched yet, so this component "suspends". React skips over it, and tries rendering other components in the tree. -4. React tries to render ``. It calls `resource.posts.read()`. Again, there's no data yet, so this component also "suspends". React skips over it too, and tries rendering other components in the tree. -5. There's nothing left to try rendering. Because `` suspended, React shows the closest `` fallback above it in the tree: `

      Loading profile...

      `. We're done for now. +1. 이미 `fetchProfileData()` 내에서 요청을 발동시켰습니다. 이 함수는 프라미스가 아니라 특별한 "자원"을 돌려줍니다. 보다 현실적인 예시에서는, Relay와 같은 데이터 라이브러리에서 제공하는 Suspense 통합을 제공할 겁니다. +2. React는 ``의 렌더링을 시도합니다. 자식 컴포넌트로 ``와 ``을 반환합니다. +3. React는 ``의 렌더링을 시도합니다. `resource.user.read()`를 호출합니다. 아직 불러온 데이터가 아무 것도 없으므로, 이 컴포넌트는 "정지합니다". React는 이 컴포넌트를 넘기고, 트리 상의 다른 컴포넌트의 렌더링을 시도합니다. +4. React는 ``의 렌더링을 시도합니다. `resource.posts.read()`를 호출합니다. 또 한번, 아직 데이터가 없으므로, 이 컴포넌트 또한 "정지합니다". React는 이 컴포넌트도 넘기고, 트리 상의 다른 컴포넌트릐 렌더링을 시도합니다. +5. 렌더링을 시도할 컴포넌트가 남아있지 않습니다. ``가 정지된 상태이므로, React는 트리 상에서 `` 위에 존재하는 것 중 가장 가까운 `` Fallback을 찾습니다. 그것은 `

      Loading profile...

      `입니다. 일단, 지금으로서는 할 일이 다 끝났습니다. -This `resource` object represents the data that isn't there yet, but might eventually get loaded. When we call `read()`, we either get the data, or the component "suspends". +여기에서 `resource` 객체는 아직은 존재하지 않지만, 결국엔 로딩이 이루어질 데이터를 나타냅니다. `read()`를 호출할 경우, 데이터를 얻거나, 또는 컴포넌트가 "정지합니다". -**As more data streams in, React will retry rendering, and each time it might be able to progress "deeper".** When `resource.user` is fetched, the `` component will render successfully and we'll no longer need the `

      Loading profile...

      ` fallback. Eventually, we'll get all the data, and there will be no fallbacks on the screen. +**데이터가 계속 흘러들어옴에 따라, React는 렌더링을 다시 시도하며, 그 때마다 React가 "더 깊은 곳까지" 처리할 수 있게 될 겁니다.** `resource.user`를 불러오고 나면, `` 컴포넌트는 성공적으로 렌더링이 이루어지고 `

      Loading profile...

      ` Fallback은 더 이상 필요가 없어집니다. 결국 모든 데이터가 준비될 것이고, 화면 상에는 Fallback이 사라질 것입니다. -This has an interesting implication. Even if we use a GraphQL client that collects all data requirements in a single request, *streaming the response lets us show more content sooner*. Because we render-*as-we-fetch* (as opposed to *after* fetching), if `user` appears in the response earlier than `posts`, we'll be able to "unlock" the outer `` boundary before the response even finishes. We might have missed this earlier, but even the fetch-then-render solution contained a waterfall: between fetching and rendering. Suspense doesn't inherently suffer from this waterfall, and libraries like Relay take advantage of this. +이것은 아주 흥미로운 의미를 지닙니다. 설령 한번의 요청으로 모든 데이터 요구 사항을 충족시킬 수 있는 GraphQL 클라이언트를 사용할지라도, *응답이 계속 흘러들어오도록 하면 컨텐츠를 더 일찍 표시할 수 있게 해줍니다.* (불러오기 *이후*가 아니라) *불러올 때에* 렌더링을 수행하기 때문에, `user`가 `posts`보다 응답에 먼저 들어있을 경우, 응답이 완료되기도 전에 바깥의 `` 경계를 해제할 수 있습니다. 우리가 이 부분을 처음에 놓치고 지나갔겠지만, 불러오기 이후에 렌더링을 하는 해결 방식에서도 워터폴은 나타납니다. 바로 불러오기와 렌더링 사이에 말입니다. Suspense을 사용하면 애초부터 이러한 워터폴을 경험하지 않을 수 있고, Relay와 같은 라이브러리들은 이러한 이점을 활용하고 있습니다. -Note how we eliminated the `if (...)` "is loading" checks from our components. This doesn't only remove boilerplate code, but it also simplifies making quick design changes. For example, if we wanted profile details and posts to always "pop in" together, we could delete the `` boundary between them. Or we could make them independent from each other by giving each *its own* `` boundary. Suspense lets us change the granularity of our loading states and orchestrate their sequencing without invasive changes to our code. +컴포넌트에서 "로딩 여부를 확인하는" `if (...)` 검사가 제거된 것을 유의하시기 바랍니다. 이렇게 하면 보일러플레이트 코드를 제거할 뿐만 아니라, 간단한 절차만으로 신속한 디자인 변화를 만들 수 있게 해줍니다. 예를 들어, 프로필 정보와 게시글이 항상 함께 "나타나도록" 해야 한다면, 그 둘 사이의 `` 경계를 제거해주면 됩니다. 또는 각 컴포넌트에게 *고유한* `` 경계를 부여하여 각각을 독립시켜줄 수도 있습니다. Suspense는 로딩 상태의 기본 단위를 변경할 수 있고, 코드를 크게 변경하지 않고도 로딩 상태의 배치를 조정할 수 있도록 해줍니다. -## Start Fetching Early {#start-fetching-early} +## 일찍 불러오기 시작하기 {#start-fetching-early} -If you're working on a data fetching library, there's a crucial aspect of Render-as-You-Fetch you don't want to miss. **We kick off fetching _before_ rendering.** Look at this code example closer: +만약 당신이 데이터 불러오기 라이브러리를 개발 중이라면, 불러올 때 렌더링을 다루는 데에 있어 주목해야 하는 아주 중요한 측면이 있습니다. **렌더링을 수행하기 _전에_ 불러오기를 발동시킵니다.** 아래의 코드 예시를 자세히 보십시오. ```js -// Start fetching early! +// 불러오기를 일찍 시작! const resource = fetchProfileData(); // ... function ProfileDetails() { - // Try to read user info + // 사용자 정보 읽기 시도 const user = resource.user.read(); return

      {user.name}

      ; } ``` -**[Try it on CodeSandbox](https://codesandbox.io/s/frosty-hermann-bztrp)** +**[CodeSandbox에서 따라해보기](https://codesandbox.io/s/frosty-hermann-bztrp)** + +위 예시에서 `read()` 호출은 불러오기를 *시작*시키지 않습니다. 단지 **이미 불러오기가 완료된** 데이터를 읽어들이기 시도할 뿐입니다. 이 차이는 Suspense를 사용하여 빠른 어플리케이션을 만들고자 할 때에 아주 중요합니다. 컴포넌트가 렌더링을 시작할 때까지 데이터 불러오기를 미루고 싶지 않기 때문입니다. 데이터 불러오기 라이브러리의 개발자로서, 불러오기가 시작되기 전에는 `resource` 객체를 사용할 수 없도록 하여서 이러한 정책을 강제할 수 있습니다. 이 페이지에서 "가짜 API 구현"을 사용하는 모든 데모에서는 이를 강제하고 있습니다. -Note that the `read()` call in this example doesn't *start* fetching. It only tries to read the data that is **already being fetched**. This difference is crucial to creating fast applications with Suspense. We don't want to delay loading data until a component starts rendering. As a data fetching library author, you can enforce this by making it impossible to get a `resource` object without also starting a fetch. Every demo on this page using our "fake API" enforces this. +위 예시와 같이 "최상위 수준에서" 불러오기를 수행하는 것은 현실적이지 않다고 반박할 수도 있을 것입니다. 만약 다른 프로필 페이지로 이동한다면 어떻게 할까요? Props에 기반하여 불러오기를 수행해야 할 수도 있습니다. 이에 대한 답은 **그 대신, 이벤트 핸들러에서 불러오기를 시작하는 것**입니다. 아래는 사용자 페이지 간에 전환하는 예시의 간단한 버전입니다. You might object that fetching "at the top level" like in this example is impractical. What are we going to do if we navigate to another profile's page? We might want to fetch based on props. The answer to this is **we want to start fetching in the event handlers instead**. Here is a simplified example of navigating between user's pages: ```js{1,2,10,11} -// First fetch: as soon as possible +// 첫번째 불러오기: 최대한 일찍 const initialResource = fetchProfileData(0); function App() { @@ -408,7 +430,7 @@ function App() { <>