안녕하세요 YB 이진혁입니다!
이번에는 react prop 중 특별한 친구 children에 대한 아티클입니다. 개념을 대충만 알고 있었던 children의 정확한 정의가 무엇인지, 그리고 어떻게 사용하면 좋을지 내부를 조금 더 자세히 들여다보고자 합니다.
🌊 children이란 무엇인가?
children은 컴포넌트의 데이터 전달 개체인 props의 일종이다. 컴포넌트가 다른 컴포넌트를 포함할 때, 그 안에 들어가는 내용을 모두 children으로 접근할 수 있다. 즉 일반적인 props처럼 정적인 데이터가 아니라 동적인 데이터를 컴포넌트 간에 넘겨줘야 하는 상황이 있다면 바로 이 children으로 접근이 가능하다.
텍스트로 설명하기보다 바로 코드 예시로 살펴보자.
import Webby from './Webby.js';
function Card({ children }) {
return (
<div>
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Webby
color={blue}
person={{
name: 'Jinhyeok',
role: 'YB'
}}
/>
</Card>
);
}
위 코드를 보면 Card 컴포넌트 안에 Webby 컴포넌트를 두었고 그걸 Card 컴포넌트를 정의하는 곳에서 { children } 의 형태로 받아 div 안에 렌더링 시켜주는 것을 볼 수 있다. 이 형태는 이전에 일반 props를 받을 때의 모습과 동일하다. children도 props의 일종이기 때문에 사용할 때의 모습이 유사한 것이다.
이렇게 하면 Card 컴포넌트에서는 감싼 Webby라는 컴포넌트가 props로 들어오게 된다. 예시 코드는 <Card></Card>안에 단순 Webby 컴포넌트가 단일로 들어가있지만 안에 여러 컴포넌트가 동적으로 추가, 삭제 된다고 해도 Card 컴포넌트에서는 언제나 props로 이 컴포넌트들을 받아올 수 있게 된다. 즉 부모 컴포넌트로 감싼 내부 컴포넌트(데이터)는 자식이라는 의미로 children이라는 props로 받아서 사용이 가능하다는 것이다.
본 형태는 props.children이지만 역시 구조분해할당으로 { children } 형태로 단축이 가능하다.
그러면 children prop이 가진 장점은 무엇이 있을까?
🌊 children의 장점
1. 재사용성
children prop은 위에 예시를 봐도 알 수 있듯이 부모 컴포넌트가 자식 컴포넌트의 내부 구조를 커스터마이징할 수 있어, 하나의 컴포넌트를 다양한 내용으로 재사용이 가능하다.
const Card = ({ children }) => (
<div className="card">
{children}
</div>
);
// 재사용 예시
<Card>
<h2>Title</h2>
<p>SOPT WEB 화이팅🌊</p>
</Card>
<Card>
<img src="webby.png" alt="웨비들" />
</Card>
만약 카드에 여러 정보들을 띄워야 하는데 그 데이터들이 형식이 다르다면 (=다른 컴포넌트/태그) props로 데이터를 넘기고 Card 컴포넌트 내에 조건 분기를 하거나, Card를 재사용하지 못하고 각각 다른 컴포넌트를 만들어야 할 것이다.
특히 리액트 공식 문서에는 이런 문장이 존재한다.
그 내부에서 무엇이 렌더링 되는지 “알” 필요는 없습니다. 이 유연한 패턴은 많은 곳에서 볼 수 있습니다.
어떻게 보면 정말 추상적인 문장 같지만 굉장히 중요한 원칙이다. 컴포넌트도 결국에 하나의 함수이고, 함수는 각 기능을 가지고 있다. 그런데 여러 기능을 가진 함수는 좋지 못한 설계라고 생각한다. 그렇기 때문에 컴포넌트도 이름에 맞는 하나의 기능을 가지고 있어야 하고 여러 기능이 있다면 분리해야한다. 그 때 이 컴포넌트는 무슨 값을 외부에서 받는지는 중요하지 않다. 단순히 받는 값을 띄워줘야 한다면 띄워주기만 하면 되고, 계산 과정이 있다면 계산만 해주면 된다. 그 자체의 기능에 집중하면 된다는 말이다.
children도 무슨 값을 넣든 그 내부에서 "알" 필요가 없다. 즉 리액트 공식문서에서 말하는 "알" 필요가 없다는 말이 재사용성을 고려할 수 있다는 말이지 않을까 개인적으로 생각한다.
🤔함수(컴포넌트) 설계 원칙??
정답도 아니고 무조건 맞다도 아니지만 책에서 읽어서 좋다고 생각한 내용 중 함수(결국 컴포넌트도)를 설계할 때 아래와 같은 규칙을 지향해보려고 노력한다.
- 반복되는 코드가 있다면 함수로 분리한다.
- 하나의 함수는 하나의 역할만을 해야 한다. (단일 책임 원칙)
- 함수 내부의 줄 수는 9줄은 넘지 않는다. (분리)
- 거의 모든 경우에 확장성을 고려한다.
이런 문장을 보고 내가 구현한 코드를 보면 정말.. 심각하다. React라면 컴포넌트의 분리가, 파이썬이라면 모듈의 분리가 제대로 되지 않는 느낌이다. 앞으로는 조금 더 이 규칙들을 생각해서 개발을 해보자..!
2. 가독성 & 유지보수성
단어 의미처럼 children을 통해 컴포넌트 내부에 삽입할 JSX를 명확하게 표현할 수 있기 때문에, 부모-자식 관계가 직관적으로 보이게 된다. 이로 인해 가독성이 높아지고, 유지보수가 용이해진다고 볼 수 있다. 물론 간단한 내용에 대해서는 일반 props도 이는 해당되겠지만 컴포넌트 구조가 조금 복잡해질 경우나 prop으로 JSX를 직접 넘겨야 한다면 이를 고려해볼만 할 것 같다.
3. 함수형 자식 사용 가능
children을 통해 함수형 자식을 전달하면, 부모의 상태나 데이터를 활용한 동적인 콘텐츠 생성이 가능하다.
const List = ({ items, children }) => (
<ul>
{items.map((item, index) => (
<li key={index}>{children(item)}</li>
))}
</ul>
);
<List items={['apple', 'banana', 'cherry']}>
{(item) => <span>{item.toUpperCase()}</span>}
</List>
이렇게 children의 장점을 살펴보니 동적인 데이터를 컴포넌트간에 전달할 때 사용하면 잘 사용이 가능하 것 같다. 그런데 이렇게 코드를 살펴보다보니 궁금한점이 생긴다. 일반 데이터들이 아니라 JSX를 children으로 어떻게 받는 것일까? 대체 무엇이 전달이 되는 것일까?
🌊 JSX의 children은 어떻게 생겼을까?
JSX를 children prop로 받을 때 구조는 console.log로 그리 어렵지 않게 확인이 가능했다. 아래 사진과 같이 콘솔에 찍어보니 children으로 object가 들어오는 것을 볼 수 있다. 하지만 더 자세한 내용은 react에서 JSX로 element를 만드는 원리와 관련되어 지식이 조금 더 필요했다.
🤔 react - JSX의 요소(element) 생성
react도 JSX라는 문법을 사용하기는 하지만 JSX도 JS 문법을 확장해 HTML을 같이 활용하도록 해주는 것이다. 그래서 결국 React의 JSX는 React.createElement 함수를 통해서 요소(element)를 생성한다. 물론 JSX가 실제 JavaScript는 아니기 때문에 React가 JavaScript로 변환하는 과정이 필요하지만 말이다.
- JSX 작성
- Babel 등의 컴파일러가 JSX를 JavaScript로 변환
- Babel과 같은 도구가 JSX 코드를 React.createElement 호출로 변환한다.
- React.createElement 함수 실행
- React.createElement는 객체(Object)를 반환한다.
아래는 위 과정의 예시이다.
const element = <h1>Hello, world!</h1>; // JSX 작성
const element = React.createElement('h1', null, 'Hello, world!');
// Babel 등의 컴파일러가 JSX를 JavaScript로 변환
// React.createElement 함수 실행
{
type: 'h1',
props: {
children: 'Hello, world!'
},
key: null,
ref: null,
// 기타 내부용 프로퍼티들
}
이렇게 반환된 Object는 React가 인식할 수 있는 "React Element"로 DOM에 직접 그려지지는 않는다고 한다. 하지만 React가 이 정보를 기반으로 가상 DOM을 만들고 렌더링 과정을 통해 실제 DOM을 업데이트한다.
$$typeof
React는 element.$$typeof를 체크하며 해당 키가 없거나 무효하면 React element 생성을 거부한다. 즉 이 값이 있으면 element를 생성한다는 의미이다.
symbol(react.element)
보안과 일관성을 위해서 symbol을 사용한다.
symbol을 react element 생성에 사용한 이유를 더 자세히 살펴보고 싶다면 아래 링크를 참고해보세요! 왜 React Element에는 $$typeof 프로퍼티가 있을까 라는 원문을 번역한 글입니다! 궁금하시면 한번 읽어보셔도 좋을 것 같습니다 :) (조금 어렵긴합니다.....)
[번역] 왜 React Element에는 $$typeof 프로퍼티가 있을까?
최근 개인 블로그 [Overreacted](https://overreacted.io/)를 통해 양질의 포스트를 쏟아내고 있는 [Dan Abramov](https://overreacted.io/)의 [Why Do React Elements Have a $$typeof Property?](https://overreacted.io/why-do-react-elements-hav
velog.io
https://github.com/facebook/react/pull/4832
Use a Symbol to tag every ReactElement by sebmarkbage · Pull Request #4832 · facebook/react
Fixes #3473 I tag each React element with $$typeof: Symbol.for('react.element'). We need this to be able to safely distinguish these from plain objects that might have come from user provid...
github.com
'3주차' 카테고리의 다른 글
명령형 프로그래밍과 선언형 프로그래밍 (0) | 2024.11.02 |
---|---|
React에서 중복 로직을 해결하는 법 (0) | 2024.11.02 |
React Dev Tools? 이것 뭐예요~?? (0) | 2024.11.02 |
DOM과 React 랜더링 (0) | 2024.11.02 |
리액트 Hooks - useState, useEffect (0) | 2024.11.02 |