React Portal은 컴포넌트를 일반적인 컴포넌트 트리 외부에 렌더링하게 해주는 기능이다. 다시 말해서, 컴포넌트를 부모 컴포넌트의 외부, 심지어는 DOM 트리 완전히 다른 부분에 삽입할 수 있도록 해준다.
🤔 그럼 언제 Portal을 사용하면 좋을까?
포탈 기능은 주로 모달, 툴팁, 팝업 등의 UI 구성 요소를 만들 때 사용한다. 이러한 요소들은 DOM 트리에서 독립적으로 동작하면서, 컴포넌트의 배치에 있어 유연성이 요구되는 경우가 많기 때문이다.
<!-- index.html -->
<body>
<div id="root"></div>
<!-- div의 id를 포탈 루트로 설정 -->
<div id="portal-root"></div>
</body>
// App.jsx
import React from 'react';
import ReactDOM from 'react-dom'; // 포탈을 사용하기 위해 react-dom import
function App() {
// ReactDOM.createPortal()을 통해 포탈을 생성할 수 있음
return ReactDOM.createPortal(
<h1>Hello from Portal!</h1>,
// getElementById로 index.html에서 지정한 'portal-root' id를 가진 div 요소를 포탈로 지정
document.getElementById('portal-root')
);
}
export default App;
위의 예제 코드에서 ReactDOM.createPortal()을 사용해 'Hello from Portal!'라는 메시지를 '#portal-root' DOM 노드에 직접 렌더링하고 있다. 이를 통해, 'App' 컴포넌트가 속한 '#root' DOM 노드 밖에 있는 '#portal-root'에 직접 컴포넌트를 렌더링할 수 있다.
🤔 뭐가 좋은지 잘 모르겠는데..?
React DOM의 Portal 기능을 사용하면, 다음과 같은 장점이 있다.
UI 유연성: Portal을 사용하면, DOM 트리의 아무 곳에서나 컴포넌트를 렌더링할 수 있. 이는 모달, 툴팁, 팝업과 같이 페이지의 특정 부분에서 독립적으로 렌더링되어야 하는 UI 요소를 만드는 데 유용하게 적용된다.
CSS 스타일링 용이성: Portal을 사용하면, 부모 컴포넌트의 CSS 스타일에 영향을 받지 않고 독립적으로 스타일링할 수 있다. 이는 복잡한 CSS 상속을 방지하고, UI 요소의 독립성을 유지하는 데 도움이된다.
코드 가독성: Portal을 사용하면, DOM 트리 구조와 상관없이 UI 요소를 렌더링할 수 있다. 이를 통해, 코드의 가독성을 높이고, UI 구조를 더 명확하게 이해할 수 있다.
모달과 같은 UI 요소를 만들 때 portal 기능을 적절히 사용해보자!
React의 useRef 훅은 컴포넌트에서 변경 가능한 값을 안전하게 저장하고 참조할 수 있게 해주는 기능이다. 이 값은 '컴포넌트의 렌더링과는 독립적이며, 해당 값이 변하더라도 컴포넌트가 다시 렌더링되지 않는다.' 는 특징을 가진다.
useRef는 기본적으로 아래와 같이 사용한다.
import React, { useRef } from 'react'; // useRef import
const refContainer = useRef(initialValue);
위 코드에서 initialValue는 생성된 참조의 초기값으로 설정된다. 이 초기값은 .current 프로퍼티를 통해 접근할 수 있다.
가장 일반적인 useRef의 사용 예시 중 하나는 DOM 노드에 접근하는 것이다. 이를 통해 직접적인 DOM 조작, 포커스 관리, 특정 DOM 이벤트의 핸들링 등을 수행할 수 있다.
다음 예제 코드는 useRef를 사용하여 input 요소에 접근하고, 포커싱하는 예시이다.
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null); // inputEl은 input 요소에 대한 참조를 저장하는 데 사용
const onButtonClick = () => {
// `current`는 현재 마운트된 text input 요소를 가리킴
inputEl.current.focus(); // current 프로퍼티를 사용하여 실제 input 요소에 접근하고 focus 메서드를 호출
};
return (
<>
<input ref={inputEl} type="text" /> {/* input 요소에 대한 참조 설정 */}
<button onClick={onButtonClick}>Focus the input</button> {/* 버튼 클릭 시 onButtonClick 핸들러가 호출되어 input 요소에 포커스 */}
</>
);
}
위 예제는 React에서 DOM 요소에 직접 접근하는 방법을 보여준다. useRef를 사용하여 참조(ref)를 생성하고, 이 참조를 DOM 요소의 ref 속성에 할당한다. 이렇게 하면 해당 참조의 current 프로퍼티를 통해 실제 DOM 요소에 접근할 수 있게 되는 것이다.
다음으로 useRef를 통해 컴포넌트 렌더링 사이에 데이터를 유지하는 예시 코드를 살펴보자.
import React, { useRef, useEffect } from 'react';
function Component() {
const renderCount = useRef(0); // renderCount는 렌더링 횟수를 저장하는 데 사용
useEffect(() => {
renderCount.current = renderCount.current + 1; // 컴포넌트가 렌더링될 때마다 renderCount의 값을 1 증가시킴
});
return (
<h1>I have rendered {renderCount.current} times!</h1> // 렌더링 횟수를 화면에 출력함
);
}
위 예제는 useRef가 불변(immutable) 참조를 생성하며, 이 참조의 current 프로퍼티만 변경 가능하다는 특성을 보여준다. 이 특성 덕분에 useRef는 렌더링 사이클에 무관하게 데이터를 유지할 수 있다. 이 예시에서 렌더링 횟수를 저장하는 renderCount는 값이 변경되어도 컴포넌트를 다시 렌더링하지 않는다. 이는 renderCount가 상태가 아닌 참조이기 때문이다.
🤔 너무 복잡해! 이걸 쓰면 뭐가 좋은거야?
useRef를 사용하면 다음과 같은 장점을 가질 수 있다.
DOM 요소 접근: useRef를 사용하면, DOM 요소에 직접 접근할 수 있다. 이를 통해 DOM 요소의 속성을 읽거나 변경하거나, DOM API를 사용하는 등의 작업을 할 수 있다.
렌더링과 독립적인 값 관리: useRef를 사용하면, 컴포넌트 렌더링과 상관 없이 변경 가능한 값을 안전하게 저장하고 참조할 수 있다. 이는 시간 경과에 따라 변경되지만 렌더링에는 영향을 미치지 않는 값의 관리에 유용하다. 예를 들어, 애니메이션, setTimeout, setInterval 등의 작업에서 이전 값을 참조해야 하는 경우에 useRef를 활용해볼 수 있다.
참조의 투명성: useRef는 자신이 참조하는 값을 '.current' 속성으로 노출하게 된다. 이는 참조 대상의 변경 사항이 외부로부터 투명하게 관찰될 수 있음을 의미한다.
🤔 useState와는 어떤 점이 다를까?
useState와 useRef는 둘 다 컴포넌트에서 값을 관리하는데 사용되지만, 값의 변경이 컴포넌트의 렌더링에 미치는 영향이 다르다. useState는 상태가 변경되면 컴포넌트가 다시 렌더링되지만, useRef는 값의 변경이 컴포넌트의 렌더링에 영향을 미치지 않는다.