02. 지역 상태와 전역 상태 사용하기
About: 끌어올리기 패턴, 전역 상태란?, 언제 전역 상태를 사용하는 것이 좋을까?
🔖 1. 우리 프로젝트 코드의 문제와 해결 방향
책 내용
지역 상태를 만들고 컴포넌트와 자식 컴포넌트에서 해당 상태를 사용하기만 한다면, 지역성과 재사용성 측면에서 좋다. 따라서 보통 이런 전략을 따르는 것이 권장된다.
하지만 특정 상황에서는 트리 내 서로 멀리 떨어져 있는 둘 이상의 컴포넌트에 공통적인 상태가 필요한 경우가 있다. 이런 경우 전역 상태가 필요하다. 전역 상태는 특정 컴포넌트에 속해있지 않으므로 전역 상태를 저장하는 위치를 고려해야 한다.
현재: Context로 관리
트리 내 서로 멀리 떨어져 있는 Header, Main, Panel 컴포넌트 등에 공통의 상태가 필요할 것이라 예상되어 상태들을 전역으로 관리
e.g. 현재 디렉토리 경로를 나타내는 상태(
curDir
)를 전역으로 사용하며,StorageContext.tsx
내StorageContextProvider
가 존재
변경: Prop으로 관리
전역 상태 관리가 남용되어 상태의 추적이 어려워짐
상태가 비교적 단순하고 업데이트가 자주 발생하지 않기에 오버 엔지니어링이라 판단
따라서 props로 관리하는 것이 더 간단하고 효율적이라 판단
따라서 상위 컴포넌트(
Storage.tsx
)에서 지역 상태를 만들고 컴포넌트와 자식 컴포넌트(Header, Main, Panel)에서 인수, 즉 prop을 내려 받아 사용하는 방식으로 변경필요하다면 Context를 부분적으로 사용하거나 혼합하는 방식을 고려
e.g.
curDir
을 경로 관리가 필요한 각 컴포넌트에서만 사용 가능
책 내용과 연결짓기
[!NOTE]
우리 코드에서 사용되는 전역 상태는 무엇이 있을까?
그리고 그 전역 상태가 얼마나, 어디서 쓰이고 있을까?
불필요한 렌더링이 일어나는 컴포넌트는 무엇이 있나?
🔖 2. 순수 함수 vs. 콜백 함수
책 내용
전역 변수에 의존하는 함수는 변수가 변경되지 않는 한 동일하게 작동한다. 외부에서 함수 작동 방식을 변경할 수 있으므로 강력한 기능이라 할 수 있다. 하지만 단점으로는 이 함수가 외부 변수에 의존한다는 사실을 모르고 다른 곳에 사용될 수 있다는 점이다.
변수가 싱글턴인 경우, 코드 재사용성을 더 높이기 위해 컨테이너 객체를 만드는 것이 더 모듈화된 접근 방식이다.
요즘 리팩토링하고 있던 것
요즘 우리 프로젝트 코드를 전체적으로 모듈화 또는 모듈화가 잘 되어 있는지 점검하면서 아래의 문제를 발견해 리팩토링 중이다.
순수 함수 내부에 콜백 함수가 포함되어 함수의 책임이 늘어나고 side effect가 발생했다.
예를 들어,
renameDirectory
함수 내부에서getDirectory
를 콜백으로 호출해,renameDirectory
함수는 디렉토리 이름 변경뿐만 아니라 변경된 디렉토리 정보를 가져오는 역할까지 하게 되어 단일 책임 원칙을 위반했다.더불어 함수의 재사용성을 떨어뜨리고, 함수 간의 의존성을 높여 코드의 모듈화와 유지 보수성을 저해했다.
기존: 콜백 함수를 포함한 순수 함수
function renameDirectory(oldName, newName, callback) {
// 디렉토리 이름 변경 로직
callback(newName);
}
// 사용 예시
renameDirectory('oldDir', 'newDir', (updatedName) => {
const directoryInfo = getDirectory(updatedName);
// 추가 로직
});
리팩토링: 순수 함수의 결합
function renameDirectory(oldName, newName) {
// 디렉토리 이름 변경 로직
return newName;
}
function getDirectory(directoryName) {
// 디렉토리 정보 가져오기 로직
return directoryInfo;
}
// 함수 결합
const newDirName = renameDirectory('oldDir', 'newDir');
const directoryInfo = getDirectory(newDirName);
책 내용과 연결짓기
[!NOTE] 전역 변수가 싱글턴인 경우, 컨테이너 객체를 만들어 전역 변수를 감싸는 것이 더 모듈화된 접근 방식이다. 이는 위의 예시 같이 순수 함수를 결합하여 필요한 데이터를 전달하는 것과 유사하다. 컨테이너 객체를 사용하면 함수들은 컨테이너를 통해 필요한 데이터에 접근하므로, 전역 변수에 직접 의존하지 않고 의존성이 명확해진다.
🔖 3. '지역성을 보장한다'의 의미
책 내용과 연결짓기
책에서 "
useState
가 포함된 함수는 변수를 함수 선언 범위 내에서만 사용할 수 있기 때문에 '억제' 되었다고 할 수 있다." 라고 말하며, 그 이유로 함수 밖에서 변수를 변경하는 것은 불가능하기 때문이라 했다.이 점을 미루어 보았을 때, 컴포넌트 외부의 그 어떤 것도 영향을 미치지 않을 때, 컴포넌트만의 독립성을 보장할 때 곧, 지역성을 보장한다고 할 수 있다.
🔖 4. 언제 전역 상태를 사용할까?
prop을 전달하는 것이 적절하지 않을 때
컴포넌트 트리를 그려보자.
이 떄, 서로 멀리 떨어진 두 컴포넌트 간에 상태를 공유해야 한다면?
심지어 depth 3에 있는 상태를 루트까지 끌어올려야 한다면?
prop을 중간 컴포넌트를 거쳐 전달해야 하므로 이는 매우 비효울적이다.
예를 들어,
StorageHeader.tsx
를 위해Storage.tsx
와StorageMain.tsx
,StorageSub.tsx
을 거쳐 동일한 prop을 전달해야 한다면 (심지어 나열한 컴포넌트들에서는 해당 prop을 사용하지 않는데도) 처음 그 코드를 보는 사람은 어떻게 느낄까?
이미 리액트 외부에 상태가 있을 때
예를 들어, 리액트 없이 획득한 사용자 인증 관련 정보가 있을 수 있다. 이 때는 전역 상태가 리액트 외부에 존재할 수 있다.
이 외에도 서버로부터 실시간으로 수신되는 websocket, 네트워크 상태, 클립보드 데이터 등도 전역에서 관리해야 할 수 있다.
🔖 5. 1장의 핵심
코드를 짤 때 아래의 질문을 던져보자
[!NOTE] 각 컴포넌트가 독립적으로 존재하는가 성능에 영향을 미치지 않도록 불필요한 의존을 하고 있진 않은가
Last updated