[TIL] 231204_React: React DOM과 Context 이해하기

지코·2023년 12월 3일
0

Today I Learned

목록 보기
5/66

기존 React의 rendering 방식

React DOM이란, Virtual DOM에서 HTML을 생성하는데 필요한 라이브러리 개념으로, 리액트 내 element들을 브라우저에 렌더링하는 데 필요한 모든 것들이 갖춰져 있다.

ReactDOM.render(
  <React.StrictMode>
    <Library /> 
  </React.StrictMode>,
  document.getElementById('root')
);

리액트 프로젝트 내 📁index.js 파일에서 위와 같은 코드를 통해 Library라는 이름의 컴포넌트가 root dom node에 렌더링된다.


ReactDOM.render 함수의 정확한 형태를 살펴보면 다음과 같다.

ReactDOM.render(
  element,
  container,
  [callback]
);
  • element : 화면에 그려질 리액트 element
  • container : element를 어느 container DOM에 렌더링할지
  • callback : 렌더링 후 반환되는 값을 돌려주는 콜백 함수이나, 생략 가능

  • 단점 : 여러 컴포넌트에 걸쳐 자주 사용되는 데이터(Ex> 로그인 여부, 프로필 데이터 등)를 전달하기 위해서는 반복적인 코드가 많이 생기고, 지저분해질 확률이 높다.
    예를 들어, root 노드에서 C 노드로 데이터를 전달하기 위해서는 props를 통해 최소 두 번은 이동해야 한다.

Example Code

function App(props) {
	return <Toolbar theme="dark" />;
}

function Toolbar(props) {
	return (
		<div>
			<ThemedButton theme={props.theme} />
		</div>
	);
}

function ThemedButton(props) {
	return <Button theme={props.theme} />;
}
  • 위 코드에서 App 컴포넌트는 theme 속성을 가지는 Toolbar 컴포넌트를 반환하고, Toolbar 컴포넌트는 props를 통해 받은 theme 값을 ThemedButton 컴포넌트에 전달한다. 마지막으로 ThemedButton 컴포넌트는 props를 통해 받은 theme 값을 Button 컴포넌트에 전달한다.
  • 현재 theme 값을 필요로 하는 컴포넌트들에 대해 이 값을 모두 props로 전달하는 것은 굉장히 비효율적임을 알 수 있다.

Context를 사용한 방식

  • 일일이 props로 데이터를 전달할 필요 없이, 데이터를 필요로 하는 컴포넌트에 곧바로 데이터를 전달할 수 있다.
  • 따라서 React DOM 방식에 비해 코드도 매우 깔끔해지고, 데이터를 한 곳에서 관리하기 때문에 디버깅에도 유리 !!

Example Code

// 데이터를 컴포넌트 트리로 곧바로 전달
// 아래와 같은 방식으로 context를 생성하고 기본 값을 지정
const ThemeContext = React.createContext('light');

// Provider를 사용하여 하위 컴포넌트들에게 현재 테마 데이터를 전달
// 이 때, 컴포넌트 트리 하단에 얼마나 깊이 있는지는 관계 없음
function App(props) {
	return (
		<ThemeContext.Provider value="dark">
			<Toolbar />
		</ThemeContext.Provider>
	);
}

// React DOM 방식처럼 theme 데이터를 하위 컴포넌트로 전달할 필요X
function Toolbar(props) {
	return (
		<div>
			<ThemedButton />
		</div>
	);
}

// 가장 가까운 상위 테마 Provider를 찾아서 해당되는 값을 사용
// 해당되는 Provider가 없을 경우 기본 값을 사용
// 여기서는 상위 Provider가 있기 때문에 현재 컴포넌트에서 사용하는 theme 값은 'dark'
function ThemedButton(props) {
	return (
		<ThemeContext.Consumer>
			{value => <Button theme={value} />}
		</ThemeContext.Consumer>
	);
}

Context를 사용하기 전에 고려할 점

  • 무조건 context를 사용하는 것이 좋은 것은 아닌데, 그 이유는 component와 context가 연결되었을 경우 재사용성이 떨어지기 때문 !
  • 그렇기 때문에 컴포넌트의 본래 목적인 재사용성 극대화와 구조의 효율성을 고려하여 context를 사용하는 것이 중요하다 ⭐️

context를 사용하지 않고 해결할 수 있는 방법 ?
→ Element variable !

/* Example Code 1 */
function Page(props) {
	const user = props.user;
	
	const userLink = (
		<Link href={user.permalink}>
			<Avatar user={user} size={props.avatarSize} />
		</Link>
	);
	
	// Page 컴포넌트는 PageLayout 컴포넌트를 렌더링하는데
	// 이 때 props로 userLink를 함께 전달한다.
	return <PageLayout userLink={userLink} />;
}

// PageLayout 컴포넌트는 NavigationBar 컴포넌트를 렌더링 
<PageLayout userLink={...} />
// NavigationBar 컴포넌트는 props로 전달받은 userLink element를 반환
<NavigationBar userLink={...} />
  • 위 코드에서 user와 avatarSize가 props로 들어간 Avatar 컴포넌트를 userLink라는 변수에 저장한 뒤, 해당 변수를 하위 컴포넌트에 넘기는 방식을 사용한다.
  • 이는 더욱 간결한 코드로 만들며, 최상위 컴포넌트에 더 많은 권한을 부여하게 된다.
  • 하지만 상위 컴포넌트에 너무 많은 데이터가 몰리게 되면 오히려 복잡할 수 있다 ! 😟
/* Example Code 2 */
function Page(props) {
	const user = props.user;
	
	const topBar = (
		<NavigationBar>
			<Link href={user.permalink}>
				<Avatar user={user} size={props.avatarSize} />
			</Link>
		</NavigationBar>
	);
	
	const content = <Feed user={user} />;
	
	return (
		<PageLayout 
			topBar={topBar}
			content={content}
		/>
	);
}
  • 위 코드처럼 하위 컴포넌트를 여러 개의 변수로 나눠서 데이터들을 전달할 수도 있다 !!

Conclusion

컴포넌트 구성 시 데이터를 주고 받는 방식에 대해 제대로 파악하고, 어떤 방법이 효율적일지 꼭 먼저 비교한 후 구성하기 ⭐️

Reference

https://www.inflearn.com/course/처음-만난-리액트
https://lakelouise.tistory.com/85#google_vignette

profile
꾸준하고 성실하게, FE 개발자 역량을 키워보자 !

0개의 댓글