(리액트) Context API
Props를 층층이 내리지 않고도 데이터를 공유하는 React의 내장 기능을 다룹니다.
상태는 흘러가야 합니다
앞 문서에서 Flux 패턴을 봤습니다.
상태가 바뀌면 View가 다시 그려진다 — 이 단방향 흐름이 React의 기본 원리입니다.
그런데 흐름이 단방향이라는 건, 부모에서 자식으로만 데이터를 넘길 수 있다는 뜻이기도 합니다.
React에서 부모가 자식에게 데이터를 넘기는 방법이 props입니다.
깊어지면 불편해집니다
컴포넌트가 이렇게 중첩되어 있다고 합시다.
App → Layout → Sidebar → UserInfo → AvatarApp에 있는 사용자 정보를 Avatar에서 쓰려면, 중간에 있는 Layout, Sidebar, UserInfo가 전부 props를 받아서 다음으로 넘겨야 합니다.
function App() {
const user = { name: '홍길동', avatar: '/hong.png' };
return <Layout user={user} />;
}
function Layout({ user }) {
return <Sidebar user={user} />;
}
function Sidebar({ user }) {
return <UserInfo user={user} />;
}
function UserInfo({ user }) {
return <Avatar src={user.avatar} />;
}Layout이나 Sidebar는 user 데이터를 직접 쓰지도 않는데, 전달만 하고 있습니다.
이걸 props drilling이라고 부릅니다.
컴포넌트가 많아질수록 중간 경유지가 늘어나고, props 이름이 바뀌면 경로 전체를 수정해야 합니다.
Context — 건너뛰는 통로
React의 Context API는 이 문제를 해결합니다.
중간 컴포넌트를 거치지 않고, 필요한 컴포넌트가 직접 데이터를 꺼내 쓸 수 있습니다.
점선처럼, App에서 제공한 데이터를 Avatar가 중간 단계 없이 바로 읽습니다.
사용법 — 세 단계
1단계: Context 만들기
import { createContext } from 'react';
const UserContext = createContext(null);createContext로 Context 객체를 만듭니다.
2단계: Provider로 감싸기
function App() {
const user = { name: '홍길동', avatar: '/hong.png' };
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}Provider의 value에 공유할 데이터를 넣습니다.
이 Provider 안에 있는 모든 하위 컴포넌트가 이 데이터에 접근할 수 있습니다.
3단계: useContext로 꺼내 쓰기
import { useContext } from 'react';
function Avatar() {
const user = useContext(UserContext);
return <img src={user.avatar} alt={user.name} />;
}useContext에 Context를 넣으면, Provider의 value가 바로 반환됩니다.
중간 컴포넌트들은 user props를 받을 필요가 없어집니다.
function Layout() {
return <Sidebar />;
}
function Sidebar() {
return <UserInfo />;
}
function UserInfo() {
return <Avatar />;
}props drilling이 사라졌습니다.
Context는 데이터를 묶어두는 방법입니다
Context의 진짜 장점은 "먼 곳에 전달한다"가 아니라, 데이터를 특정 컴포넌트 범위 안에 명시적으로 잡아둘 수 있다는 점입니다.
Provider로 감싼 범위 안에서만 데이터가 살아 있고, 그 바깥으로는 나가지 않습니다.
테마 데이터는 테마 Provider 안에, 인증 데이터는 인증 Provider 안에.
관련된 데이터와 그걸 쓰는 컴포넌트가 한 덩어리로 묶이니, 관심사가 자연스럽게 응집됩니다.
다만 범위를 너무 넓게 잡으면 주의가 필요합니다.
Provider의 value가 바뀌면 그 안의 컴포넌트가 전부 다시 그려질 수 있고, 여러 종류의 데이터를 하나의 Context에 몰아넣으면 관심사가 오히려 뒤섞입니다.
작게, 역할별로 나누는 게 좋습니다.
직접 해보기
- 테마(다크/라이트)를 Context로 만들어서 여러 컴포넌트에서 읽어보기
- Provider 없이
useContext를 호출하면 어떤 값이 나오는지 확인해보기