React · 2026-02-03

React + TypeScript: ForwardRef와 Ref 전달 패턴

React와 TypeScript 환경에서 forwardRef와 ref 전달 방법을 실무적으로 이해하기 위한 개념 설명과 코드 예제 모음

작성일 : 2026-02-03 ㆍ 작성자 : 관리자
post
목차

개요

컴포넌트 트리에서 하위 DOM이나 인스턴스에 접근해야 할 때 ref가 필요하다. 하지만 중간 컴포넌트가 있으면 그대로 전달할 수 없다. 이때 사용하는 것이 forwardRef다. TypeScript와 함께 쓰면 타입 안전성을 확보할 수 있다. 본문에서는 기본 개념, useRef 타입 선언 방법, forwardRef 사용법, 그리고 실전에 자주 쓰이는 패턴을 코드와 함께 설명한다.

ref와 forwardRef의 기본 개념

ref가 필요한 이유

ref는 포커스 제어, 스크롤, 외부 라이브러리 연동 등 DOM 노드나 컴포넌트 인스턴스 직접 조작이 필요할 때 사용한다. 함수형 컴포넌트는 인스턴스가 없기 때문에 ref를 직접 받을 수 없다. 그래서 React는 forwardRef를 제공한다.

forwardRef의 역할

forwardRef는 부모로부터 전달된 ref를 자식의 특정 DOM 또는 컴포넌트 내부 요소에 연결하는 래퍼다. 중간 컴포넌트가 ref를 받아서 하위 요소로 전달할 수 있게 만든다.

TypeScript에서 useRef 타입 선언

useRef에는 제네릭을 사용해 타입을 명시한다. DOM 노드의 경우 HTMLElement의 구체 타입을 지정하면 된다. 예를 들면 아래와 같다.

const inputRef = React.useRef<HTMLInputElement | null>(null);

// 사용 예
React.useEffect(() => {
  if (inputRef.current) {
    inputRef.current.focus();
  }
}, []);

이처럼 null을 허용하는 타입을 같이 선언해야 초기에 null 값을 가질 수 있다. 컴포넌트 내부에서 타입 안전한 접근이 가능해진다.

forwardRef 사용법: 기본 예제

아래 예제는 버튼 컴포넌트를 외부에서 ref로 제어하는 방법을 보여준다. JSX 태그는 < >로 이스케이프 처리했다.

type ButtonProps = {
  children?: React.ReactNode;
};

const FancyButton = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
  return (
    <button ref={ref} className="fancy">{props.children}</button>
  );
});

// 사용
function Parent() {
  const btnRef = React.useRef<HTMLButtonElement | null>(null);
  React.useEffect(() => {
    btnRef.current?.focus();
  }, []);
  return <FancyButton ref={btnRef}>클릭</FancyButton>;
}

중간 컴포넌트가 있을 때 ref 전달 방법

단순 전달 외에 중간에서 추가 로직이 필요한 경우도 있다. 이때는 forwardRef와 useImperativeHandle을 함께 사용해 외부에 노출할 인터페이스를 제한할 수 있다.

useImperativeHandle 예제

type InputHandle = {
  focus: () => void;
};

const TextInput = React.forwardRef<InputHandle, {}>((_, ref) => {
  const inputRef = React.useRef<HTMLInputElement | null>(null);

  React.useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus() ?? undefined,
  }), []);

  return <input ref={inputRef} />;
});

// 사용
function Form() {
  const handleRef = React.useRef<InputHandle | null>(null);
  return (
    <div>
      <TextInput ref={handleRef} />
      <button onClick={() => handleRef.current?.focus()}>포커스</button>
    </div>
  );
}

이 패턴은 내부 구현을 숨기면서 필요한 동작만 노출할 때 유용하다.

실무에서 자주 만나는 주의점

  • 타입을 정확히 명시한다: 잘못된 타입은 런타임 에러로 이어질 수 있다.
  • null 초기화: useRef의 초기값을 null로 설정하면 안전하다.
  • 불필요한 ref 전달은 피한다: ref는 외부 조작이 필요할 때만 사용한다.
  • 함수형 컴포넌트에 ref를 직접 두지 않는다: forwardRef로 명시적으로 전달한다.

요약

forwardRef는 ref 전달 문제를 해결하는 공식 방법이다. TypeScript와 결합하면 타입 안정성을 확보할 수 있다. useRef 타입 선언은 제네릭을 사용하고, 필요 시 useImperativeHandle로 외부 인터페이스를 제어한다. 이 패턴을 익히면 컴포넌트 재사용성과 안정성을 동시에 높일 수 있다.

react forwardRef typescript useRef 타입 선언 react ref 전달 방법 리액트 react ref forwarding typescript react ref forwardRef 예제 useImperativeHandle 리액트 컴포넌트 패턴