목차
포털(차원 이동)
포털(Portal)을 사용하면 애플리케이션 영역을 벗어나 특정 위치에 컴포넌트를 렌더링할 수 있다. 포털을 통해 컴포넌트가 어느 위치에든 렌더링될 수 있으면서 다른 리액트 컴포넌트와 동일하게 작동한다. 렌도링된 컴포넌트가 DOM의 어느 위치에 있더라도 리액트 컴포넌트 트리에 포함되기 때문이다. 즉, 원하는 DOM 위치에 컴포넌트를 자유롭게 렌더링할 수 있다는 뜻이다.
포털을 사용하려면 우선 포털 전용으로 사용할 DOM 요소를 만들어야 한다. <body>에 직접 추가하는 것보다는 별도로 요소를 만들어서 그 안에 렌더링하는 것이 좋다. 그렇게 하면 코드 디버깅도 쉬워지고, <body> 하위에 여러 요소가 뒤섞여 혼란스러워지는 것도 막을 수 있기 때문이다.
ReactDOM의 createPortal 함수를 사용하여 자식(하위) 요소를 특정 DOM 노드에 렌더링할 수 있다. 그러면 컴포넌트 트리 구조와 관계 없이 원하는 DOM 위치에 UI를 표시할 수 있다. 이 방식은 부모(상위) 컴포넌트의 스타일이나 레이아웃에 영향을 받지 않고 전역적으로 UI를 띄울 때 유용하다.
import { createPortal } from 'react-dom'
const portalRoot = document.getElementById('alert-message')
function AlertPortal({ children }) {
return createPortal(children, portalRoot)
}
transform, perspective, filter 등의 스타일이 들어가면 레이아웃이 깨질 수 있다. 하지만 포털을 사용하면 부모 요소의 스타일이나 레이아웃에 영향을 받지 않고 언제나 의도한 위치에 나타나서 다른 컴포넌트의 구조나 스타일에 영향을 받지 않는다.
포털은 모달, 토스트, 드롭다운처럼 화면 위에 독립적으로 떠야 하는 UI를 만들 때 유용하다. 이런 요소들은 기존 컴포넌트 트리 구조나 스타일에 영향을 받지 않고, 항상 원하는 위치에 표시되어야 한다. 포털을 이용하면 UI를 별도의 DOM 컨테이너에 렌더링할 수 있어서 화면에서 UI의 위치와 계층을 더욱 명확하고 쉽게 제어할 수 있다. 덕분에 복잡한 레이아웃에서도 UI 요소들이 서로 겹치거나 의도하지 않은 곳에 나타나는 문제를 효과적으로 방지할 수 있다.
참조 객체 전달
리액트에서 ref는 DOM 요소나 컴포넌트에 직접 접근할 때 사용한다. 기본적으로 HTML 컴포넌트에는 ref를 바로 전달할 수 있다.
커스텀 컴포넌트에 ref를 전달하고 싶을 때 리액트 19 이전 버전까지는 ref가 기본적으로 속성(props)으로 전달되지 않았기 때문에 콘솔에 오류가 뜨면서 ref가 연결되지 않았다. ref를 전달하기 위해 forwardRef 고차 함수를 사용해야 했다.
하지만 리액트 19 버전부터는 ref를 일반 속성(props)처럼 바로 전달할 수 있게 됐다. ref도 다른 속성처럼 바로 받아서 사용할 수 있다.
import { useId } from 'react'
function FormInput({ ref, ...restProps }) {
const id = useId()
return (
<div className="form-control">
<label htmlFor={id}>이름</label>
<input
ref={ref}
type="text"
name="name"
id={id}
autoComplete="name"
required
{...restProps}
/>
</div>
)
}
오늘 하루를 돌아보며
오늘 실습 시간에는 모달 다이얼로그를 만들어 봤다. HTML/CSS 때도 그렇고, JavaScript 때도 그렇고 React 때도 그렇고 항상 강조하시는 것은 같다. 모달 다이얼로그가 표시되면 사용자는 해당 모달에서 요구하는 작업을 완료하거나 닫기 전까지 배경의 다른 UI 요소와 상호작용할 수 없도록 해야 하고, 모달은 일시적으로 메인 흐름을 중단시키고 특정 작업에만 집중하도록 만들어야 한다. 하지만 도움말 창이나 채팅창과 같은 비모달 다이얼로그는 배경의 다른 요소와 동시에 상호작용할 수 있다. 다이얼로그가 모달인지 비모달인지에 따라 배경의 다른 요소와 상호작용을 하도록 하거나 하지 못하도록 해야 한다.
그리고 중요한 것은 키보드 운용와 초점 관리다. 다이얼로그가 열리면 다이얼로그 내부 요소로 초점이 이동되고, 닫히면 다이얼로그를 연 트리거 요소에 초점을 되돌리는 것이 타당하다. 그리고 다이얼로그가 열렸을 때 처음 초점이 가는 곳은 닫기 버튼이 아닌 첫 번째 상호작용 가능한 요소일 것이다. 초점은 논리적 순서대로 이동하도록 해야 한다. 닫기 버튼은 마지막에 초점이 가는 것이 맞다.
여기에서 많이 하는 실수 중 하나가 포커스 트랩(focus-trap)이다. 태버블(tabbable, 탭 이동이 가능한) 요소로 초점이 이동되다가 마지막 요소에서 누르면 다이얼로그 외부로 빠져나가는 경우가 있는데, 그렇게 되면 안 된다. 다이얼로그 안으로 들어왔으니 빠져나가기 위해서는 Escape 키를 누르거나 닫기 버튼을 눌러야만 나갈 수 있도록 해야 한다. 따라서, 마지막 요소에서 다음으로 초점을 이동하면 다이얼로그 내부의 첫 번째 태버블 요소로 초점을 이동해야 한다.