Element라는 영어 단어는 요소, 성분이라는 뜻을 갖고 있습니다. 어떤 물체를 구성하는 성분을 영어로 element
라고 부릅니다. 마찬가지로 리액트의 element
도 리액트 앱을 구성하는 요소를 의미합니다. 리액트 공식 홈페이지에서는 element
를 아래와 같이 정의하고 있습니다.
Elements are the smallest building blocks of React apps.
위 문장을 한글로 번역해보면 Element는 리액트 앱의 가장 작은 빌딩 블록들이라는 의미가 됩니다. 즉, 리액트 앱을 구성하는 가장 작은 블록들을 element
라고 부르는 것이죠. 이전에 웹 개발을 했던 분들은 이미 element
라는 용어를 많이 들어봤을 겁니다. element
는 원래 웹사이트에 대한 모든 정보를 담고있는 객체인 DOM(Document Object Model)
에서 사용하는 용어입니다. 그래서 기존에 element
라고 하면 DOM Element
를 의미했습니다. DOM Element
가 어떻게 생겼는지 한 번 볼까요? 아래 그림은 우리가 앞에서 만든 리액트 애플리케이션을 실행한 뒤에, 크롬 브라우저의 개발자 도구에서 'Elements' 탭을 누른 모습입니다.
위 그림에서 보이는 것처럼 탭 이름부터 Elements로 되어 있기 때문에, element
들을 모아놓은 것이라는 것을 알 수 있습니다. 하지만 여기에 나타난 element
들은 리액트 element
가 아니라 DOM Element
이며, HTML 요소를 나타냅니다. 실제로 우리가 화면에서 볼 수 있는 것들이죠. 그렇다면 리액트 element
와 DOM Element
는 어떤 차이가 있을까요?
리액트가 개발되기 시작한 아주 초창기에, 화면에 나타나는 내용들을 기술하는 자바스크립트 객체를 일컫는 용어가 필요했습니다. 그래서 처음에는 '기술한다'는 그 의미 그대로 descriptor
라는 이름으로 불렀습니다. 하지만 descriptor
가 결국 최종적으로 나타나는 형태는 DOM Element
였기 때문에, DOM과의 통일성을 위해서 element
라고 부르기로 결정하였습니다. 아래 그림은 리액트 element
와 DOM Element
를 나타낸 것입니다.
앞 장에서 우리가 리액트의 Virtual DOM에 대해서 배웠습니다. 실제 브라우저의 DOM에 존재하는 element
는 DOM Element
가 되는 것이고, 리액트의 Virtual DOM에 존재하는 element
가 바로 리액트 element
가 되는 것입니다. 결국 리액트 element
는 DOM Element
의 가상 표현이라고 볼 수 있습니다. 그리고 DOM Element
는 리액트 element
에 비해서 많은 정보를 담고 있기 때문에 상대적으로 크고 무겁습니다. 앞으로 이 문서에서 말하는 element
는 특별한 언급이 없는 한 리액트 element
를 의미한다고 보면 됩니다.
리액트 element
는 화면에서 보이는 것들을 기술합니다. element
가 기술한 내용을 토대로 실제 우리가 화면에서 보게되는 DOM Element
가 만들어지는 것입니다. 앞 장에서 JSX를 배울 때 살펴봤던 예제 코드를 다시 한 번 보도록 하겠습니다.
const element = <h1>Hello, world</h1>;
이 코드는 JSX를 사용하여 작성된 코드입니다. 앞에서는 element
에 대해 배우지 않았기 때문에 그냥 지나쳤지만, 대입 연산자('=') 왼쪽 부분에 나오는 변수의 이름이 element
로 되어 있는 것을 볼 수 있습니다. 이 코드가 실행될 때, 대입 연산자의 오른쪽 부분은 리액트의 createElement()
함수를 사용하여 element
를 생성하게 됩니다. 결국 이렇게 생성된 것이 바로 리액트 element
가 되는 것입니다. 그리고 리액트는 이 element
를 이용해서 실제 우리가 화면에서 보게 될 DOM Element
를 생성합니다.
그렇다면 리액트 element
는 실제로 어떻게 생겼을까요? element
가 화면에 보이는 것들을 기술한다고 했는데, 실제로 element
는 어떤 형태로 존재하는지 의문이 들 수 있습니다. 결론부터 말하면, 리액트 element
는 자바스크립트 객체 형태로 존재합니다. element
는 컴포넌트 유형(예: Button
)과 속성(예: color
) 및 내부의 모든 자식(children
)에 대한 정보를 포함하고 있는 일반적인 자바스크립트 객체입니다. 그리고 뒤에서 나오겠지만 이 객체는 맘대로 변경할 수 없는 불변성(immutability) 을 갖고 있습니다. 한 번 생성되면 바꿀 수 없다는 뜻이죠. 그럼 element
의 실제 모습을 한 번 보도록 할까요?
{
type: 'button',
props: {
className: 'bg-green',
children: {
type: 'b',
props: {
children: 'Hello, element!'
}
}
}
}
위 코드는 버튼을 나타내기 위한 element
입니다. 단순한 자바스크립트 객체임을 알 수 있죠? 위 코드처럼 type
에 HTML 태그 이름이 문자열로 들어가는 경우에 element
는 해당 태그 이름을 가진 DOM Node
를 나타내고 props
는 속성에 해당합니다. props
에 대해서는 뒤에서 자세히 배울 예정이니 지금은 그냥 넘어가도 됩니다. 그리고 위 element
가 실제로 렌더링이 된다면 아래와 같은 DOM Element
가 될 것입니다.
<button class='bg-green'>
<b>
Hello, element!
</b>
</button>
그렇다면 element
의 type
에 HTML 태그 이름이 문자열로 들어가는 것이 아닌 경우에는 어떻게 될까요? 아래 자바스크립트 코드는 리액트의 Component Element
를 나타낸 것입니다. 이 역시도 일반적인 자바스크립트 객체입니다. 다만, 위에 나왔던 element
와 한 가지 다른 점은 type
에 HTML태그가 아닌 리액트 컴포넌트의 이름이 들어갔다는 점입니다.
{
type: Button,
props: {
color: 'green',
children: 'Hello, element!'
}
}
이처럼 리액트 element
는 자바스크립트 객체 형태로 존재합니다. 그리고 이 객체를 만드는 역할을 하는 것이 바로 앞에서 나왔던 createElement()
함수입니다. 앞에서 createElement()
함수를 호출 할 때 세 가지의 파라미터를 넣었었는데, 그 부분을 다시 한 번 보도록 할까요?
React.createElement(
type,
[props],
[...children]
)
첫 번째 파라미터로는 타입이 들어갑니다. 여기에는 HTML태그 이름이 문자열로 들어가거나, 또 다른 리액트 컴포넌트가 들어가게 됩니다. 이것이 결국 아까 우리가 개발자 도구를 통해서 보았던 HTML태그가 되는 것이죠. 만약 여기에 HTML태그가 아닌 리액트 컴포넌트를 넣으면 어떻게 될까요? 모든 리액트 컴포넌트는 최종적으로는 HTML태그를 사용하게 되어있습니다. 하나의 컴포넌트는 여러개의 자식 컴포넌트들을 포함할 수 있고, 자식 컴포넌트들을 모두 쭉 분해해보면 결국 HTML태그가 나오는 것이죠.
두 번째 파라미터로는 props
라는 것이 들어갔었습니다. 우리가 아직 props
에 대해서 배우지 않았기 때문에, 이 부분은 그냥 간단하게 element
의 속성이라고 설명해보도록 하겠습니다. 아까 개발자 도구의 그림에서 HTML태그가 있고, 해당 태그에 여러가지 속성들이 들어가 있었죠? 예를 들면, className
이나 style
같은 것들 말이죠. 이런 속성을 attributes
라고 부릅니다. 그리고 props
는 attributes
보다는 조금 더 상위에 있는 복잡한 개념이지만 지금은 일단 element
의 속성이라고만 이해하고 넘어가도록 하겠습니다.
그리고 세 번째 파라미터로는 children
이 들어가게 됩니다. 해당 element
의 자식 element
들이 이 부분에 들어가게 됩니다. 실제 개발자 도구의 그림에서는 하나의 HTML태그 하위에 또 여러개의 HTML태그가 나오는 것을 볼 수 있었습니다. 이러한 HTML태그들이 결국 자식 element
가 되는 것입니다.
이제 실제로 createElement()
함수가 동작하는 과정을 코드와 함께 살펴보도록 하겠습니다. 아래 예시 코드를 한 번 보겠습니다.
function Button(props) {
return (
<button className={`bg-${props.color}`}>
<b>
{props.children}
</b>
</button>
)
}
function ConfirmDialog(props) {
return (
<div>
<p>내용을 확인하셨으면 확인 버튼을 눌러주세요.</p>
<Button color='green'>확인</Button>
</div>
)
}
위 코드에는 Button
컴포넌트와 ConfirmDialog
컴포넌트가 있으며, ConfirmDialog
컴포넌트가 Button
컴포넌트를 포함하고 있습니다. 여기서 ConfirmDialog
컴포넌트의 element
는 어떤 모습이 될까요? 아마도 아래와 같은 형태가 될 것입니다.
{
type: 'div',
props: {
children: [
{
type: 'p',
props: {
children: '내용을 확인하셨으면 확인 버튼을 눌러주세요.'
}
},
{
type: Button,
props: {
color: 'green',
children: '확인'
}
}
]
}
}
여기서 첫 번째 children
은 type
이 HTML태그인 p
태그이기 때문에 곧바로 렌더링이 될 수 있는 상태입니다. 하지만 두 번째 children
의 type
은 HTML태그가 아니라 리액트 컴포넌트 이름인 Button
입니다. 이 경우에 리액트는 Button
컴포넌트의 element
를 생성해서 합치게 됩니다. 그래서 최종적으로 element
는 아래와 같은 모습이 될 것입니다.
{
type: 'div',
props: {
children: [
{
type: 'p',
props: {
children: '내용을 확인하셨으면 확인 버튼을 눌러주세요.'
}
},
{
type: 'button',
props: {
className: 'bg-green',
children: {
type: 'b',
props: {
children: '확인'
}
}
}
}
]
}
}
이처럼 컴포넌트 렌더링을 위해서 모든 컴포넌트가 createElement()
함수를 통해 element
로 변환된다는 것을 기억하시기 바랍니다. 지금까지 리액트의 element
는 실제로 어떻게 생겼는지 알아보았습니다. 리액트의 element
는 우리 눈에 실제로 보이는 것들을 기술한다는 사실을 기억하면서 다음으로 넘어가도록 하겠습니다.
리액트의 element
는 굉장히 중요한 특징을 갖고 있습니다. 바로 불변성(immutability) 입니다. 불변성은 말 그대로 변하지 않는 성질을 의미합니다. 즉, element
가 불변성을 갖고 있다는 것은 한 번 생성된 element
는 변하지 않는다는 것입니다. 다른 말로 표현하면, element
생성 후에는 children
이나 attributes
를 바꿀 수 없다는 말입니다.
그렇다면 여기서 의문이 하나 생기게 됩니다. 리액트의 element
라는 것은 우리 눈에 보이는 것을 기술한다고 했는데, 'element
가 변할 수 없다면 화면 갱신이 안되는 것 아닌가?' 라는 의문을 가질 수 있습니다. 이러한 의문을 해소하려면 element
의 불변성에 관한 설명을 다시 한 번 자세히 읽어보아야 합니다.
element
생성 후에는 children
이나 attributes
를 바꿀 수 없다
여기서 우리가 빠트린 부분이 있죠. 바로 element
생성 후에는이라는 부분입니다. 즉, element
는 다양한 모습으로 존재할 수 있지만, 한 번 생성된 다음에는 변경이 불가능하다는 뜻입니다. 우리가 붕어빵 가게를 가보면 붕어빵 틀에 반죽을 넣고 시간이 지나면 붕어빵이 구워져서 나오는데, 한 번 구워져서 나온 붕어빵의 속 내용을 바꿀 수 없는 것과 같은 이치라고 생각하면 됩니다. 아래 그림처럼 말이죠.
위 그림에는 리액트의 컴포넌트와 element
의 관계가 나타나 있습니다. 뒷 장에서 컴포넌트를 배울 때 자세히 다루겠지만 컴포넌트는 일종의 붕어빵 틀이라고 보면 됩니다. 그리고 붕어빵이 구워져서 밖으로 나오는 과정이 element
를 생성하는 과정이고, 붕어빵이 모두 완성되면 element
가 생성이 끝난 것이기 때문에 변경할 수 없는 것이죠.
그렇다면 화면에 변경된 element
들을 보여주기 위해서는 어떻게 해야할까요? 그런 경우에는 기존 element
를 변경하는 것이 아니라, 새로운 element
를 만들면 됩니다. 새로운 element
를 만들어서 기존 element
와 바꿔치기 하는 것이죠. 우리가 앞에서 리액트의 장점중 하나로 빠른 렌더링 속도가 있다는 것을 배웠습니다. 그리고 그걸 달성하기 위해서 내부적으로 Virtual DOM이라는 것을 사용한다고 했었죠. Virtual DOM의 개념도를 다시 한 번 살펴보도록 하겠습니다.
위 그림에서 보이는 것처럼 화면에 새로운 내용을 보여주기 위해서 Virtual DOM은 변경된 부분을 계산(Compute Diff)하고, 해당 부분만을 다시 렌더링 합니다. 여기서 동그란 각 원들이 바로 element
입니다. 그리고 빨간색으로 표시된 원들은 변경된 element
들이 되는 것이죠. element
는 불변성을 갖고 있기 때문에, 화면에 새로운 내용을 보여주기 위해서는 새로운 element
를 만들어서 기존 element
가 연결되어 있는 부분에 바꿔서 달면 됩니다.
지금 배운 리액트 element
의 불변성이라는 특징을 잘 기억해두시기 바랍니다. 실제로 리액트를 사용해서 개발하다 보면 상태 관리와 더불어 화면이 얼마나 자주 갱신되는지가 성능에 큰 영향을 미치는데, 그 과정이 모두 element
가 새롭게 생성된다는 것을 이해하고 있으면 좀 더 원리를 잘 이해하고 효율적으로 개발할 수 있습니다.
마지막 업데이트: 2025년 08월 21일 07시 59분
이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.