Skip to main content

직접 조작

전체 서브트리의 리렌더링을 트리거하기 위해 상태나 프로퍼티를 사용하지 않고 컴포넌트를 직접 변경해야 할 때가 있다. 예를 들어 브라우저에서 React를 사용할 때, DOM 노드를 직접 수정해야 하는 경우가 있으며, 모바일 앱의 뷰에서도 마찬가지다. setNativeProps는 DOM 노드에 직접 프로퍼티를 설정하는 것과 동일한 기능을 React Native에서 제공한다.

caution

빈번한 리렌더링으로 인해 성능 병목 현상이 발생할 때만 setNativeProps를 사용한다!

직접 조작은 자주 사용할 도구가 아니다. 일반적으로 컴포넌트 계층 구조를 렌더링하고 여러 뷰를 조정하는 오버헤드를 피하기 위해 지속적인 애니메이션을 만들 때만 사용한다. setNativeProps는 명령형이며 상태를 네이티브 레이어(DOM, UIView 등)에 저장한다. 이는 React 컴포넌트 내부가 아니므로 코드를 이해하기 어렵게 만든다.

setNativeProps를 사용하기 전에 setStateshouldComponentUpdate를 통해 문제를 해결하려고 노력한다.

TouchableOpacity와 setNativeProps

TouchableOpacity는 내부적으로 setNativeProps를 사용해 자식 컴포넌트의 투명도를 업데이트한다:

tsx
const viewRef = useRef<View>();
const setOpacityTo = useCallback(value => {
// 생략: 애니메이션 관련 코드
viewRef.current.setNativeProps({
opacity: value,
});
}, []);

이를 통해 다음과 같은 코드를 작성할 수 있으며, 자식 컴포넌트가 이 사실을 알 필요 없이 탭에 반응해 투명도가 업데이트된다. 또한 자식 컴포넌트의 구현을 변경할 필요도 없다:

tsx
<TouchableOpacity onPress={handlePress}>
<View>
<Text>Press me!</Text>
</View>
</TouchableOpacity>

만약 setNativeProps를 사용할 수 없다면, 투명도 값을 상태로 저장하고 onPress가 발생할 때마다 그 값을 업데이트하는 방식으로 구현할 수 있다:

tsx
const [buttonOpacity, setButtonOpacity] = useState(1);
return (
<TouchableOpacity
onPressIn={() => setButtonOpacity(0.5)}
onPressOut={() => setButtonOpacity(1)}>
<View style={{opacity: buttonOpacity}}>
<Text>Press me!</Text>
</View>
</TouchableOpacity>
);

이 방식은 원래 예제보다 계산 비용이 더 많이 든다. React는 투명도가 변경될 때마다 컴포넌트 계층을 다시 렌더링해야 한다. 뷰와 그 자식의 다른 속성이 변경되지 않았더라도 말이다. 일반적으로 이 오버헤드는 문제가 되지 않지만, 연속적인 애니메이션을 수행하거나 제스처에 반응할 때는 컴포넌트를 최적화해 애니메이션의 정확도를 높일 수 있다.

setNativeProps의 구현을 NativeMethodsMixin에서 살펴보면, 이는 RCTUIManager.updateView를 감싼 래퍼임을 알 수 있다. 이 함수 호출은 리렌더링 시 발생하는 것과 동일하다. 자세한 내용은 ReactNativeBaseComponent의 receiveComponent를 참고하면 된다.

합성 컴포넌트와 setNativeProps

합성 컴포넌트는 네이티브 뷰로 지원되지 않기 때문에 setNativeProps를 직접 호출할 수 없다. 다음 예제를 살펴보자:

이 코드를 실행하면 Touchable child must either be native or forward setNativeProps to a native component라는 오류가 발생한다. 이 오류는 MyButton이 직접적으로 네이티브 뷰로 지원되지 않아 투명도를 설정할 수 없기 때문이다. 이 상황은 createReactClass로 컴포넌트를 정의할 때 스타일 prop을 설정해도 작동하지 않는 것과 유사하다. 스타일 prop을 자식 컴포넌트로 전달해야 하거나, 네이티브 컴포넌트를 감싸지 않는 한 작동하지 않는다. 마찬가지로, setNativeProps를 네이티브 뷰로 지원되는 자식 컴포넌트로 전달해야 한다.

setNativeProps를 자식 컴포넌트에 전달하기

View 컴포넌트의 ref에는 setNativeProps 메서드가 존재하므로, 커스텀 컴포넌트의 ref를 렌더링하는 <View /> 컴포넌트 중 하나로 전달하기만 하면 된다. 이는 커스텀 컴포넌트에서 setNativeProps를 호출하는 것이, 감싸진 View 컴포넌트 자체에서 setNativeProps를 호출하는 것과 동일한 효과를 가진다는 의미이다.

이제 TouchableOpacity 안에서 MyButton을 사용할 수 있다!

{...props}를 사용해 모든 props를 자식 뷰에 전달한 것을 눈치챘을 것이다. 이는 TouchableOpacity가 실제로 합성(composite) 컴포넌트이기 때문이다. 따라서 자식 컴포넌트의 setNativeProps에 의존할 뿐만 아니라, 자식 컴포넌트가 터치 처리를 수행하도록 요구한다. 이를 위해 TouchableOpacity 컴포넌트로 다시 호출되는 다양한 props를 전달한다. 반면 TouchableHighlight는 네이티브 뷰를 기반으로 하며, 오직 setNativeProps만 구현하면 된다.

TextInput 값 수정을 위한 setNativeProps 사용

setNativeProps의 또 다른 일반적인 사용 사례는 TextInput의 값을 수정하는 것이다. TextInput의 controlled prop은 bufferDelay가 낮고 사용자가 매우 빠르게 입력할 때 문자를 누락시킬 수 있다. 일부 개발자는 이 prop을 완전히 생략하고, 필요할 때 setNativeProps를 사용해 TextInput 값을 직접 조작하는 것을 선호한다. 예를 들어, 다음 코드는 버튼을 탭할 때 입력 값을 수정하는 방법을 보여준다:

clear 메서드를 사용해 TextInput을 지울 수도 있다. 이 방법은 동일한 접근 방식을 사용해 현재 입력된 텍스트를 지운다.

렌더 함수와의 충돌 방지

렌더 함수에서 관리하는 속성을 업데이트하면 예측하기 어렵고 혼란스러운 버그가 발생할 수 있다. 컴포넌트가 리렌더링되고 해당 속성이 변경될 때마다, setNativeProps로 이전에 설정된 값은 완전히 무시되고 덮어쓰이기 때문이다.

setNativeProps와 shouldComponentUpdate

shouldComponentUpdate지능적으로 적용하면 변경되지 않은 컴포넌트 하위 트리를 조정하는 데 드는 불필요한 오버헤드를 피할 수 있다. 이는 setNativeProps 대신 setState를 사용해도 충분히 좋은 성능을 낼 수 있는 수준까지 이르게 한다.

기본 제공 메서드

여기서 설명하는 메서드는 React Native가 기본으로 제공하는 컴포넌트 대부분에서 사용할 수 있다. 하지만 네이티브 뷰로 직접 지원되지 않는 합성 컴포넌트에서는 사용할 수 없다는 점에 유의해야 한다. 일반적으로 여러분이 직접 정의한 대부분의 컴포넌트는 이에 해당한다.

measure(callback)

주어진 뷰의 화면 상 위치, 너비, 높이를 뷰포트 기준으로 측정하고, 비동기 콜백을 통해 값을 반환한다. 성공적으로 측정되면 콜백은 다음과 같은 인자와 함께 호출된다:

  • x
  • y
  • width
  • height
  • pageX
  • pageY

이 측정값은 네이티브에서 렌더링이 완료된 후에만 사용할 수 있다. 가능한 한 빨리 측정값이 필요하고 pageXpageY가 필요하지 않다면, onLayout 속성을 대신 사용하는 것을 고려해 본다.

또한 measure()가 반환하는 너비와 높이는 뷰포트 내 컴포넌트의 크기다. 실제 컴포넌트 크기가 필요하다면 onLayout 속성을 사용하는 것이 더 적합하다.

measureInWindow(callback)

주어진 뷰의 윈도우 내 위치를 측정하고, 비동기 콜백을 통해 값을 반환한다. 만약 React 루트 뷰가 다른 네이티브 뷰에 포함되어 있다면, 절대 좌표를 제공한다. 성공적으로 측정되면 콜백은 다음과 같은 인자와 함께 호출된다:

  • x
  • y
  • width
  • height

measureLayout(relativeToNativeComponentRef, onSuccess, onFail)measure()와 유사하지만, relativeToNativeComponentRef로 지정된 상위 컴포넌트를 기준으로 뷰의 위치를 측정한다. 반환된 좌표는 상위 뷰의 원점 x, y를 기준으로 상대적이다.

note

이 메서드는 relativeToNativeNode 핸들러(참조 대신)를 사용해 호출할 수도 있지만, 새로운 아키텍처에서는 이 방식이 더 이상 사용되지 않는다.

focus()

주어진 입력 또는 뷰에 포커스를 설정한다. 트리거되는 정확한 동작은 플랫폼과 뷰의 타입에 따라 달라진다.

blur()는 입력 필드나 뷰에서 포커스를 제거한다. 이 메서드는 focus()와 반대되는 기능을 수행한다.