리액트 고급 - Portal, Profiler API 성능 측정, 엄격모드, AbortController

Stella·2026년 3월 31일

React정리

목록 보기
1/1

리액트의 고급 개념

  • 포털 : 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 특정 DOM 노드에 자식 컴포넌트를 렌더링할 수 있게 해주는 기능입니다.

  • 포털을 사용하는 이유 : CSS 레이아웃 제약 해결, 시각적 계층 구조와 논리적 구조의 분리, 접근성 및 스크린 리더 최적화 (부모 크기에 상관없이 전체화면 활용 가능)

1) 모달 창, 다이얼로그 컴포넌트
2) 툴팁(말풍선), 로딩 창
3) 팝오버 : 사용자에게 빠르게 컨텍스트 빠르게 제공
4) 쿠키 얼럿
5) 드롭다운 메뉴 : 부모 컴포넌트 내부에 드롭다운 메뉴가 위치 -> overflow 스타일에 의해 보이지 않을 수도 있기 때문에 포털을 사용한다.

import { createPortal } from 'react-dom'

const ModalWindow = ({ description, isOpen, onClose }) => {
	if (!isOpen) return null
    return createPortal (
    	<div className="modal">
        	<span>{description}</span>
            <button onClick={onClose}>Close</button>
        </div>,
        document.body
    )
}

react-dom 패키지에서 불러온 createPortal을 호출해서 생성할 수 있다.

DOMNode : 포털 내용을 렌더링할 DOM노드
key : 컴포넌트 트리에서 포털을 구분할 수 있는 고유 식별자, 선택사항

HTML(DOM) 트리 : 브라우저가 보는 구조 portal을 쓰면 root를 벗어나 body바로 아래 붙는다.
React 트리 : React가 관리하는 컴포넌트 계층이다. 부모 컴포넌트의 자식이다.

- 이벤트 버블링의 흐름

portal 내부 버튼 ->
브라우저 레벨 : html -> body 올라간다. (부모 컴포넌트x)
React 레벨 : React가 이벤트를 가로채서 자신이 알고있는 컴포넌트 트리를 따라 이벤트 전파
-> Portal밖의 부모 컴포넌트에 설정된 핸들러 실행

- 포털에서 관리하는 접근성 주의사항?

다이얼로그나 모달이 열릴 때 포커스는 다이얼로그 내부의 요소로 이동,

- 에러 바운더리

1) try~catch문
2) 에러 바운더리 : 특정 작업 목록이 담긴 리액트 컴포넌트일 뿐이다.
자식 컴포넌트 트리에서 발생할 수 있는 자바스크립트 오류를 잡아내고, 특정 오류를 기록한 다음, 화면을 대체 UI로 리디렉션해서 오류 상태에서 복구하는 데 사용된다.

전체 컴포넌트 트리의 생성자에서 발생하는 오류를 잡아낸다. 에러 바운더리는 클래스 컴포넌트로 생명주기 메서드 중 하나 이상을 사용해서 생성될 수 있다.

static getDerivedStateFromError : 오류가 발생한 후 대체 UI를 렌더링
componentDidCatch : 오류 정보를 기록하는 데 사용한다.

class MyErrorBoundary extends Component {
	constructor(props) {
    	super(props)
        this.state = { isErrorThron: false }
   	}
    
    static getDerivedStateFromError(error) { // 오류가 발생한 대체 UI
    	return { isErrorThrown: true }
    }
    
    componentDidCatch(error, errorInfo) {
    	logErrorToReposrtingService(error, errorInfo)
    }
    
    render() {
    	if (this.state.isErrorThrown) { 
        	return <h1>Oops, the application is unavaialble.</h1>
        }
        return this.props.children
    }
}

에러 컴포넌트를 사용할 컴포넌트에 감싸면 된다.

  • suspense와 error boundary 사용 장점
    1) 서비스 전체의 화이트 아웃 방지 : 에러가 난 부분만 격리 가능하다. (방화벽 역할)

2) 폭포수 현상 해결을 통한 성능 최적화 : 기존 방식 useEffect는 부모 데이터 로딩이 끝나야 자식 데이터 로딩이 시작되는 폭포수 현상이 생기기 쉽다. Suspense를 사용하면 데이터 요청을 병렬로 처리할 수 있다. = 빠르고, 매끄러운 앱 구현 가능

3) 선언적 에러 복구 : 페이지 전체를 새로고침 하지 않아도, ui만 새로고침 하도록 만들 수 있다.

  • Jest같은 테스트 프레임워크로 단위 테스트 작성 가능하다.

  • 에러 바운더리를 함수 컴포넌트로 생성하는 것이 불가능하다.
    클래스 컴포넌트를 사용해 에러 바운더리를 만들 수 있다.

  • 에러 바운더리를 사용하지 못하는 경우
    이벤트 핸들러, 비동기 코드(지연로딩, requestAnimationFrame 등) 비동기 콜백 내부 에러, SSR, 에러 바운더리 내부에서 에러가 발생한 경우(에러 바운더리 자체에서 발생한 에러)

버튼 클릭 시 발생하는 API 에러 등은 try ~ catch로 잡아서 얼럿을 띄우는 게 맞습니다. 데이터를 렌더링하다가 예상치 못한 undefined 참조 등으로 화면이 깨지는 상황은 에러 바운더리가 방어해야 한다.

- Suspense API를 활용한 비동기 작업 관리

로딩 인디케이터 같은 대체 UI를 표시하는 데 사용된다.

  • 모든 데이터 불러오기에서 서스펜스 컴포넌트 사용 가능한가?
    lazy API사용한 지연로딩, 릴레이, Next.js, 리믹스, 하이드로겐 등의 서스펜스 기능을 지원하는 프레임워크를 활용한 데이터 불러오기

  • 업데이트 중 불필요한 폴백 방지
    리액트의 Suspense는 하위 컴포넌트가 아직 준비되지 않았을 때(데이터 패칭 중 등) 가장 가까운 fallback UI를 보여줍니다. 문제는 이미 렌더링 된 상태에서 새로운 데이터를 가져올 때 발생합니다.

function navigate(url) {
	startTransition(() => {
    	setPage(url) 
    })
}

페이지를 탐색하고 페이지 업데이트에 전환을 적용해 불필요한 폴백 방지

- 동시성 렌더링을 활용한 렌더링 성능 최적화

비동기적으로 렌더링 하도록 보장하는 동시성 렌더링 도입했다.

  • 동시성 렌더링 가능하게 하는 방법?
    react 및 react-dom 패키지를 18버전으로 업데이트해야 한다.
    ReactDOM.render 메서드를 ReactDOM.createRoot 메서드로 교체해야 한다.

애플리케이션이 복잡해지면 성능을 분석하는 데 상당한 시간을 투자해야 한다.

- Profiler API를 활용한 리액트 애플리케이션 디버깅

컴포넌트가 얼마나 자주 리렌더링되는지 각각의 리렌더링 비용을 추적하는 것이 애플리케이션 내의 문제 영역이나, 부분을 파악하는 데 도움이 된다. (컴포넌트의 병목을 수치로 확인하여 최적화의 근거를 찾는 과정)

1) React Profiler API

  • 렌더링 성능을 측정하는 방법
    Profiler API를 제공한다. UI 일부를 식별하는 데 사용되는 id prop, 트리 업데이트 시 호출되는 onRender 콜백 두가지
// id, phase, actualDuration, baseDuration, startTime, commitTime 인수를 받아 렌더링 시간 기록
<App>
	<Profiler id="bio" onRender={onRender}> // 렌더링 성능을 보고 싶다면 전체를 감싼다.
    	<AuthorBio />
    </Profiler>
    <Posts />
</App>

2) 리액트 개발자 도구

- 엄격 모드

잠재적인 버그와 이슈를 식별하기 위한 디버깅 도구 = strict mode
리액트 API의 StrictMode 컴포넌트로 사용한다.

컴포넌트 mount -> unmount -> mount 되기 때문에 fetch가 2번 호출된다.

cleanup없이 2번 호출되면 메모리 누수 or 중복요청 존재

- AbortController를 사용하기 (CSR)에서만 작동

useEffect의 cleanup함수와 짝을 이뤄서, 진행 중인 요청을 자동으로 취소하는 패턴이다.
메모리 누수를 방지한다. = 최신 요청만 유효, 1번만 출력한다.
fetch, axios 등 비동기 네트워크 요청일 경우에만

const controller = new AbortController()

controller.signal  // ← fetch에 연결하는 신호선
controller.abort() // ← 취소 명령

- 동작 방식
mount → unmount → mount  (Strict Mode가 의도적으로 실행)
         ↑
     abort() 호출 → 첫번째 요청 취소 → 두번째 요청만 유효

const root = createRoot(document.getElementById('root'))
root.render(
	<StrictMode>
    	<App/>
    </StrictMode>,
)

렌더링 영역에서 부적절한 로직, 정리 코드 부재 문제를 엄격 모드를 통해 파악한다.
자주 발생하는 버그를 찾기 위한 개발 전용 점검 항목을 활성화한다.

1) 순수하지 않은 렌더링으로 인한 버그를 찾기 위해 컴포넌트를 한 번 더 리렌더링
2) 이펙트에 대한 cleanup 함수 누락에 의한 버그를 찾기 위해 컴포넌트가 한 번 더 effect실행
3) 사용되는 API 중 더 이상사용되지 않는 API를 확인하고 경고

  • 두 번 호출되는 함수는?
    1) 함수 컴포넌트의 내부
    2) 훅에 전달된 함수 useState, useReducer, useMemo
    3) state updater 함수
    4) constructor, render 등 클래스 컴포넌트 메서드

- 정적 타입 체크

라이브러리 prop-types
타입스크립트, Flow로 두 가지 방법으로 정적 타입 검사를 구현한다.

1) 런타임 이전에 타입 오류를 식별
2) 초기 단계에서 버그와 오류를 감지
3) 최적화와 코드 가독성 향상
3) IDE 지원 향상
4) 문서 생성

profile
공부 기록

0개의 댓글