Skip to content
문서
시작하기

시작하기

설치

React 프로젝트 폴더 안에서 다음을 실행하세요.

pnpm add swr

빠른 시작

JSON 데이터를 사용하는 일반적인 RESTful API라면 먼저 네이티브 fetch의 단순한 래퍼인 fetcher 함수를 생성해야 합니다.

const fetcher = (...args) => fetch(...args).then(res => res.json())
💡

GraphQL API 또는 Axios와 같은 라이브러리를 사용하려면 여러분만의 fetcher 함수를 생성하면 됩니다. 여기에서 더 많은 예시를 확인하세요.

그 다음, useSWR을 임포트하고 함수 컴포넌트 내에서 사용하여 시작하면 됩니다.

import useSWR from 'swr'
 
function Profile () {
  const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
 
  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
 
  // 데이터 렌더링
  return <div>hello {data.name}!</div>
}

일반적으로 요청에는 "loading", "ready", "error"의 세 가지 상태가 있습니다. data, error, isLoading 값을 사용하여 요청의 현재 상태를 확인하고 해당 UI를 반환합니다.

재사용 가능하게 만들기

웹 앱을 구축할 때, UI의 많은 곳에서 데이터를 재사용할 필요가 있을 것입니다. SWR 위에서는 재사용 가능한 데이터 hook을 만드는 것이 믿을 수 없을 정도로 쉽습니다.

function useUser (id) {
  const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
 
  return {
    user: data,
    isLoading,
    isError: error
  }
}

그리고 컴포넌트에서 사용합니다.

function Avatar ({ id }) {
  const { user, isLoading, isError } = useUser(id)
 
  if (isLoading) return <Spinner />
  if (isError) return <Error />
  return <img src={user.avatar} />
}

이 패턴을 적용함으로써 명령형 방식으로 데이터를 가져오는 것에 대해 잊을 수 있습니다: 요청을 시작, 로딩 상태를 업데이트, 최종 결과를 반환. 대신, 코드는 더 선언적입니다: 컴포넌트에서 사용되는 데이터가 무엇인지만 명시하면 됩니다.

예시

실제 예시로, 저희 웹 사이트는 모두 user에 의존하는 navbar와 그 콘텐츠를 보여줍니다.

전통적으로는 최상위 레벨 컴포넌트에서 useEffect를 사용해 데이터를 한 번 가져오고, 이를 props를 통해 자식 컴포넌트에 전달합니다(현재는 에러 상태를 처리하지 않습니다)

// 페이지 컴포넌트
 
function Page () {
  const [user, setUser] = useState(null)
 
  // 데이터 가져오기
  useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data))
  }, [])
 
  // 전역 로딩 상태
  if (!user) return <Spinner/>
 
  return <div>
    <Navbar user={user} />
    <Content user={user} />
  </div>
}
 
// 자식 컴포넌트
 
function Navbar ({ user }) {
  return <div>
    ...
    <Avatar user={user} />
  </div>
}
 
function Content ({ user }) {
  return <h1>Welcome back, {user.name}</h1>
}
 
function Avatar ({ user }) {
  return <img src={user.avatar} alt={user.name} />
}

보통 최상위 레벨 컴포넌트에서 가져온 모든 데이터를 유지하고 트리 아래의 모든 자식 컴포넌트의 props로 추가해야 합니다. 페이지에 더 많은 데이터 의존성을 추가한다면 코드는 점점 유지하기가 힘들어집니다.

Context (opens in a new tab)를 사용하여 props 전달을 피할 수 있습니다만 동적 콘텐츠 문제가 여전히 존재합니다: 페이지 콘텐츠 내 컴포넌트들은 동적일 수 있으며, 최상위 레벨 컴포넌트는 그 자식 컴포넌트가 필요로하는 데이터가 무엇인지 알 수 없을 수도 있습니다.

SWR은 이 문제를 완벽하게 해결합니다. 우리가 막 생성한 useUser hook을 사용해 다음과 같이 리팩토링할 수 있습니다.

// 페이지 컴포넌트
 
function Page () {
  return <div>
    <Navbar />
    <Content />
  </div>
}
 
// 자식 컴포넌트
 
function Navbar () {
  return <div>
    ...
    <Avatar />
  </div>
}
 
function Content () {
  const { user, isLoading } = useUser()
  if (isLoading) return <Spinner />
  return <h1>Welcome back, {user.name}</h1>
}
 
function Avatar () {
  const { user, isLoading } = useUser()
  if (isLoading) return <Spinner />
  return <img src={user.avatar} alt={user.name} />
}

데이터는 이제 데이터가 필요한 컴포넌트로 범위가 제한되었으며 모든 컴포넌트는 서로에게 독립적입니다. 모든 부모 컴포넌트들은 데이터나 데이터 전달에 관련된 것들을 알 필요가 없습니다. 그냥 렌더링할 뿐입니다. 코드는 이제 유지하기에 더 간단하고 쉽습니다.

가장 아름다운 것은 이들이 동일한 SWR 키를 사용하며 그 요청이 자동으로 중복 제거, 캐시, 공유되므로, 단 한 번의 요청만 API로 전송된다는 것입니다.

또한, 애플리케이션은 이제 사용자 포커스나 네트워크 재연결 시에 데이터를 갱신할 수 있습니다! 이는 사용자의 노트북이 슬립으로부터 깨어나거나 브라우저 탭을 전환할 때 자동으로 데이터가 갱신된다는 것을 의미합니다.