v19.2Latest

값을 참조하기 위한 Refs

컴포넌트가 어떤 정보를 '기억'하게 하고 싶지만, 그 정보가새로운 렌더링을 트리거하지 않게 하려면, ref를 사용할 수 있습니다.

배우게 될 내용
  • 컴포넌트에 ref를 추가하는 방법
  • ref의 값을 업데이트하는 방법
  • ref가 state와 어떻게 다른지
  • ref를 안전하게 사용하는 방법

컴포넌트에 ref 추가하기

React에서 useRefHook을 가져와 컴포넌트에 ref를 추가할 수 있습니다:

컴포넌트 내부에서 useRefHook을 호출하고 참조하려는 초기 값을 유일한 인수로 전달하세요. 예를 들어, 값0에 대한 ref는 다음과 같습니다:

useRef는 다음과 같은 객체를 반환합니다:

'ref'라고 쓰인 주머니에 'current'라고 쓰인 화살표를 쑤셔 넣은 그림.

그림 제공:Rachel Lee Nabors

해당 ref의 현재 값은 ref.current속성을 통해 접근할 수 있습니다. 이 값은 의도적으로 변경 가능합니다. 즉, 읽고 쓸 수 있습니다. 이는 React가 추적하지 않는 컴포넌트의 비밀 주머니와 같습니다. (이것이 React의 단방향 데이터 흐름에서의 '탈출구'를 만드는 이유입니다—자세한 내용은 아래에서 설명합니다!)

여기서 버튼은 클릭할 때마다ref.current를 증가시킵니다:

이 ref는 숫자를 가리키지만,state와 마찬가지로 문자열, 객체, 심지어 함수도 가리킬 수 있습니다. state와 달리 ref는 읽고 수정할 수 있는current속성을 가진 일반 JavaScript 객체입니다.

주목할 점은컴포넌트가 매번 증가할 때마다 다시 렌더링되지 않는다는 것입니다.state와 마찬가지로 ref는 React에 의해 다시 렌더링 사이에 보존됩니다. 그러나 state를 설정하면 컴포넌트가 다시 렌더링됩니다. ref를 변경하는 것은 그렇지 않습니다!

예시: 스톱워치 만들기

단일 컴포넌트에서 ref와 state를 결합할 수 있습니다. 예를 들어, 사용자가 버튼을 눌러 시작하거나 멈출 수 있는 스톱워치를 만들어 봅시다. 사용자가 '시작' 버튼을 누른 후 얼마나 많은 시간이 지났는지 표시하려면, 시작 버튼이 눌린 시점과 현재 시간을 추적해야 합니다.이 정보는 렌더링에 사용되므로 state에 보관합니다:

사용자가 '시작'을 누르면, 10밀리초마다 시간을 업데이트하기 위해setInterval을 사용합니다:

'정지' 버튼이 눌리면, 기존의 interval을 취소하여nowstate 변수의 업데이트를 멈춰야 합니다. 이는clearInterval을 호출하여 수행할 수 있지만, 사용자가 시작을 눌렀을 때setInterval호출로 반환된 이전의 interval ID를 제공해야 합니다. 이 interval ID를 어딘가에 보관해야 합니다.interval ID는 렌더링에 사용되지 않으므로 ref에 보관할 수 있습니다:

정보가 렌더링에 사용될 경우, 상태로 유지하세요. 정보가 이벤트 핸들러에만 필요하고 변경 시 다시 렌더링할 필요가 없는 경우, ref를 사용하는 것이 더 효율적일 수 있습니다.

ref와 state의 차이점

ref가 state보다 덜 "엄격"해 보일 수 있습니다. 예를 들어, 항상 state 설정 함수를 사용하는 대신 이를 직접 변경(mutate)할 수 있기 때문입니다. 하지만 대부분의 경우에는 state를 사용해야 합니다. ref는 자주 필요하지 않은 "탈출구"입니다. state와 ref의 비교는 다음과 같습니다:

refsstate
useRef(initialValue)를 반환합니다.{ current: initialValue }useState(initialValue)는 state 변수의 현재 값과 state 설정 함수([value, setValue])를 반환합니다.
변경해도 다시 렌더링되지 않습니다.변경하면 다시 렌더링됩니다.
변경 가능함—렌더링 과정 외부에서current값을 수정하고 업데이트할 수 있습니다."불변" — state 변수를 수정하려면 다시 렌더링을 대기열에 넣기 위해 state 설정 함수를 사용해야 합니다.
렌더링 중에 current값을 읽거나(쓰거나)해서는 안 됩니다.언제든지 state를 읽을 수 있습니다. 그러나 각 렌더링은 변경되지 않는 자체 state의스냅샷을 가집니다.

다음은 state로 구현된 카운터 버튼입니다:

count값이 표시되기 때문에, state 값을 사용하는 것이 합리적입니다.setCount()로 카운터의 값이 설정되면, React는 컴포넌트를 다시 렌더링하고 새로운 카운트를 반영하도록 화면을 업데이트합니다.

ref로 구현하려고 했다면, React는 컴포넌트를 다시 렌더링하지 않으므로 카운트 변경을 절대 볼 수 없을 것입니다! 이 버튼을 클릭해도텍스트가 업데이트되지 않습니다:

이것이 렌더링 중에ref.current를 읽으면 신뢰할 수 없는 코드가 되는 이유입니다. 해당 기능이 필요하다면 대신 state를 사용하세요.

Deep Dive
useRef는 내부적으로 어떻게 동작하나요?

Ref를 사용하는 경우

일반적으로, 컴포넌트가 React 외부로 "나가서" 외부 API(주로 컴포넌트의 외관에 영향을 주지 않는 브라우저 API)와 통신해야 할 때 ref를 사용하게 됩니다. 다음은 그런 드문 상황들 중 몇 가지입니다:

  • 저장타임아웃 ID
  • 저장 및 조작(다음 페이지에서 다룹니다)DOM 요소
  • JSX 계산에 필요하지 않은 다른 객체 저장.

컴포넌트가 어떤 값을 저장해야 하지만, 그 값이 렌더링 로직에 영향을 주지 않는 경우 ref를 선택하세요.

Ref에 대한 모범 사례

다음 원칙을 따르면 컴포넌트를 더 예측 가능하게 만들 수 있습니다:

  • Ref를 비상구로 취급하세요.Ref는 외부 시스템이나 브라우저 API와 작업할 때 유용합니다. 애플리케이션 로직과 데이터 흐름의 많은 부분이 ref에 의존한다면 접근 방식을 재고해볼 필요가 있습니다.
  • 렌더링 중에ref.current를 읽거나 쓰지 마세요.렌더링 중에 필요한 정보가 있다면state를 대신 사용하세요. React는ref.current가 언제 변하는지 알지 못하기 때문에, 렌더링 중에 이를 읽는 것만으로도 컴포넌트의 동작을 예측하기 어렵게 만듭니다. (이에 대한 유일한 예외는if (!ref.current) ref.current = new Thing()와 같은 코드로, 이는 첫 렌더링 중에만 ref를 한 번 설정합니다.)

React state의 제한 사항은 ref에는 적용되지 않습니다. 예를 들어, state는각 렌더에 대한 스냅샷처럼 동작하고동기적으로 업데이트되지 않습니다.하지만 ref의 현재 값을 변형(mutate)하면 즉시 변경됩니다:

이는 ref 자체가 일반 JavaScript 객체이기 때문이며,그에 따라 동작하기 때문입니다.

또한 ref를 다룰 때는변형(mutation) 피하기에 대해 걱정할 필요가 없습니다. 변형하는 객체가 렌더링에 사용되지 않는 한, React는 ref나 그 내용물로 무엇을 하든 상관하지 않습니다.

Ref와 DOM

ref는 어떤 값이든 가리킬 수 있습니다. 그러나 ref의 가장 일반적인 사용 사례는 DOM 요소에 접근하는 것입니다. 예를 들어, 입력(input)을 프로그래밍 방식으로 포커스하고 싶을 때 유용합니다. JSX에서ref속성에 ref를 전달하면, 예를 들어<div ref={myRef}>와 같이, React는 해당 DOM 요소를myRef.current에 넣을 것입니다. 요소가 DOM에서 제거되면, React는myRef.currentnull로 업데이트할 것입니다. 이에 대해 더 알아보려면를 읽어보세요.Ref로 DOM 조작하기

요약

  • Ref는 렌더링에 사용되지 않는 값들을 보관하기 위한 탈출구입니다. 자주 필요하지는 않습니다.
  • ref는 current라는 단일 프로퍼티를 가진 일반 JavaScript 객체입니다. 이 값을 읽거나 설정할 수 있습니다.
  • React에게 ref를 요청하려면useRef훅을 호출하면 됩니다.
  • state와 마찬가지로, ref는 컴포넌트의 리렌더링 사이에 정보를 유지하게 해줍니다.
  • state와 달리, ref의current값을 설정해도 리렌더링이 촉발되지 않습니다.
  • 렌더링 중에ref.current를 읽거나 쓰지 마세요. 이렇게 하면 컴포넌트의 동작을 예측하기 어려워집니다.

몇 가지 도전 과제를 시도해 보세요

Challenge 1 of 4:Fix a broken chat input #

메시지를 입력하고 “Send”를 클릭하세요. ‘Sent!’ 알림이 표시되기 전에 3초 지연이 있다는 것을 알 수 있습니다. 이 지연 동안 ‘Undo’ 버튼을 볼 수 있습니다. 그것을 클릭하세요. 이 ‘Undo’ 버튼은 clearTimeout를 호출하여 ‘Sent!’ 메시지가 나타나는 것을 멈추도록 설계되었습니다. 이것은 handleSend 동안 저장된 타임아웃 ID에 대해 수행됩니다. 그러나 “Undo”를 클릭한 후에도 ‘Sent!’ 메시지는 여전히 나타납니다. 왜 작동하지 않는지 찾아서 수정하세요.


Refs로 값 참조하기 | React Learn - Reflow Hub