폼 데이터를 관리하다가 제어 컴포넌트와 비제어 컴포넌트에 대해 공부해보게되었다.

📌 제어 컴포넌트와 비제어 컴포넌트
제어 컴포넌트
리액트 상태에 의해 값이 제어되는 컴포넌트이다.
사용자 입력값으로 자신의 상태를 관리하고 업데이트하고, 상태가 업데이트 될 때마다 컴포넌트는 리렌더링된다.
폼 데이터가 상태에 저장되어 있어 데이터의 흐름이 명확하고, 로직 구현도 수월하지만, 빈번한 리렌더링으로 인해 불필요한 api 호출 등 자원 낭비가 발생할 수 있다.
import React, { useState } from 'react';
function ControlledComponent() {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
};
return (
<form>
<input type="text" value={inputValue} onChange={handleChange} />
</form>
);
}
비제어 컴포넌트
입력 요소 자체가 값을 제어하며 리액트와 독립적으로 동작하는 컴포넌트이다. 입력값이 DOM 요소에 저장되기 때문에 DOM에서 이루어지는 컴포넌트라고 할 수 있다.
입력값을 가져오고 싶은 경우, ref를 사용하여 입력 값에 접근하고 제어한다.
사용자 입력에 따라 상태가 변경되지 않기 때문에 이벤트마다 리렌더링은 발생하지 않으나 폼 데이터 변경 추적이 어렵다는 단점이 있다.
import React, { useRef } from 'react';
function UncontrolledComponent() {
const inputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
console.log('Submitted value:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}
❓ 각각 언제 사용되는가?
제어 컴포넌트는 데이터 흐름이 예측 가능하고 관리하기 쉬워 복잡한 폼이나 유효성이 필요한 경우에 적합하고, 비제어 컴포넌트는 간단한 입력 폼이나 초기값 설정이 중요한 경우에 성능면에서 제어 컴포넌트보다 유리할 수 있다.
하지만 제어 컴포넌트 최적화가 가능하다면?
📌 제어 컴포넌트 최적화 방법
여러 방법의 제어 컴포넌트 최적화로 성능을 개선시킬 수 있다.
1. 쓰로틀링, 디바운싱
가장 흔하게 사용되는 제어 컴포넌트 최적화 방법으로, 불필요한 연속 호출을 줄이는 데 효과적이다.
우선, 쓰로틀링은 이벤트 호출이 반복될 때 일정한 시간 간격동안 특정함수를 한번만 실행되도록 제어하는 방법이다.
function throttle(func, delay) {
let lastCall = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
return func(...args);
};
}
디바운싱은 연속적으로 발생하는 이벤트 중에서 마지막 이벤트만 처리하도록 하는 방법이다.
function debounce(func, delay) {
let debounceTimer;
return function(...args) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func(...args), delay);
};
}
2. 트라이 자료구조
문자열 검색을 위한 트리형태의 자료구조로, 문자열 탐색 및 자동 완성 기능 구현에 유용하다. 공통적인 접두사를 공유하는 문자열들을 효율적으로 저장하고 검색하여 성능을 개선시킨다.
3. useDeferedValue
일부 상태 업데이트를 지연시켜 UI 성능을 개선할 수 있는 리액트 훅이다.
우선순위가 낮은 상태 업데이트를 뒤로 미뤄 중요한 작업부터 업데이트를 진행시킬 수 있다.
import { useDeferredValue } from 'react';
function MyComponent({ value }) {
const deferredValue = useDeferredValue(value);
return (
<div>
<p>Immediate value: {value}</p>
<p>Deferred value: {deferredValue}</p>
</div>
);
}
4. useTransition : UI를 차단하지 않고 상태를 업데이트
UI를 차단하지 않으면서 비동기적으로 상태 업데이트를 시킬 수 있는 리액트 훅이다.
중요하지 않은 상태 업데이트는 중요한 작업이 끝난 후 반영되기 때문에 UI가 부드럽게 동작할 수 있다.
아래의 예시는 사용자 입력 시 호출되는 handleChange 함수 내부의 setValue가 트랜지션으로 처리되는 과정이다. 이를 통해 value는 비동기적으로 업데이트 된다.
import { useTransition, useState } from 'react';
function MyComponent() {
const [isPending, startTransition] = useTransition();
const [value, setValue] = useState('');
const handleChange = (e) => {
startTransition(() => {
setValue(e.target.value);
});
};
return (
<div>
<input type="text" onChange={handleChange} />
{isPending ? <p>Loading...</p> : <p>{value}</p>}
</div>
);
}
'Tech > ReactJS' 카테고리의 다른 글
에러 바운더리를 사용한 선언적 에러 핸들링 (0) | 2024.05.21 |
---|---|
상세 상품 prefetch로 인한 불필요한 네트워크 요청 해결 (0) | 2024.05.20 |
컴파운드 컴포넌트로 재사용성과 가독성 높이기 (0) | 2024.05.09 |
Grid 아이템 이미지 로딩 후 리플로우 이슈 해결 (0) | 2024.05.06 |
결제 후 장바구니 속 아이템이 삭제되었다가 재생성되는 이슈 (0) | 2024.04.29 |
폼 데이터를 관리하다가 제어 컴포넌트와 비제어 컴포넌트에 대해 공부해보게되었다.

📌 제어 컴포넌트와 비제어 컴포넌트
제어 컴포넌트
리액트 상태에 의해 값이 제어되는 컴포넌트이다.
사용자 입력값으로 자신의 상태를 관리하고 업데이트하고, 상태가 업데이트 될 때마다 컴포넌트는 리렌더링된다.
폼 데이터가 상태에 저장되어 있어 데이터의 흐름이 명확하고, 로직 구현도 수월하지만, 빈번한 리렌더링으로 인해 불필요한 api 호출 등 자원 낭비가 발생할 수 있다.
import React, { useState } from 'react';
function ControlledComponent() {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
};
return (
<form>
<input type="text" value={inputValue} onChange={handleChange} />
</form>
);
}
비제어 컴포넌트
입력 요소 자체가 값을 제어하며 리액트와 독립적으로 동작하는 컴포넌트이다. 입력값이 DOM 요소에 저장되기 때문에 DOM에서 이루어지는 컴포넌트라고 할 수 있다.
입력값을 가져오고 싶은 경우, ref를 사용하여 입력 값에 접근하고 제어한다.
사용자 입력에 따라 상태가 변경되지 않기 때문에 이벤트마다 리렌더링은 발생하지 않으나 폼 데이터 변경 추적이 어렵다는 단점이 있다.
import React, { useRef } from 'react';
function UncontrolledComponent() {
const inputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
console.log('Submitted value:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}
❓ 각각 언제 사용되는가?
제어 컴포넌트는 데이터 흐름이 예측 가능하고 관리하기 쉬워 복잡한 폼이나 유효성이 필요한 경우에 적합하고, 비제어 컴포넌트는 간단한 입력 폼이나 초기값 설정이 중요한 경우에 성능면에서 제어 컴포넌트보다 유리할 수 있다.
하지만 제어 컴포넌트 최적화가 가능하다면?
📌 제어 컴포넌트 최적화 방법
여러 방법의 제어 컴포넌트 최적화로 성능을 개선시킬 수 있다.
1. 쓰로틀링, 디바운싱
가장 흔하게 사용되는 제어 컴포넌트 최적화 방법으로, 불필요한 연속 호출을 줄이는 데 효과적이다.
우선, 쓰로틀링은 이벤트 호출이 반복될 때 일정한 시간 간격동안 특정함수를 한번만 실행되도록 제어하는 방법이다.
function throttle(func, delay) {
let lastCall = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
return func(...args);
};
}
디바운싱은 연속적으로 발생하는 이벤트 중에서 마지막 이벤트만 처리하도록 하는 방법이다.
function debounce(func, delay) {
let debounceTimer;
return function(...args) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func(...args), delay);
};
}
2. 트라이 자료구조
문자열 검색을 위한 트리형태의 자료구조로, 문자열 탐색 및 자동 완성 기능 구현에 유용하다. 공통적인 접두사를 공유하는 문자열들을 효율적으로 저장하고 검색하여 성능을 개선시킨다.
3. useDeferedValue
일부 상태 업데이트를 지연시켜 UI 성능을 개선할 수 있는 리액트 훅이다.
우선순위가 낮은 상태 업데이트를 뒤로 미뤄 중요한 작업부터 업데이트를 진행시킬 수 있다.
import { useDeferredValue } from 'react';
function MyComponent({ value }) {
const deferredValue = useDeferredValue(value);
return (
<div>
<p>Immediate value: {value}</p>
<p>Deferred value: {deferredValue}</p>
</div>
);
}
4. useTransition : UI를 차단하지 않고 상태를 업데이트
UI를 차단하지 않으면서 비동기적으로 상태 업데이트를 시킬 수 있는 리액트 훅이다.
중요하지 않은 상태 업데이트는 중요한 작업이 끝난 후 반영되기 때문에 UI가 부드럽게 동작할 수 있다.
아래의 예시는 사용자 입력 시 호출되는 handleChange 함수 내부의 setValue가 트랜지션으로 처리되는 과정이다. 이를 통해 value는 비동기적으로 업데이트 된다.
import { useTransition, useState } from 'react';
function MyComponent() {
const [isPending, startTransition] = useTransition();
const [value, setValue] = useState('');
const handleChange = (e) => {
startTransition(() => {
setValue(e.target.value);
});
};
return (
<div>
<input type="text" onChange={handleChange} />
{isPending ? <p>Loading...</p> : <p>{value}</p>}
</div>
);
}
'Tech > ReactJS' 카테고리의 다른 글
에러 바운더리를 사용한 선언적 에러 핸들링 (0) | 2024.05.21 |
---|---|
상세 상품 prefetch로 인한 불필요한 네트워크 요청 해결 (0) | 2024.05.20 |
컴파운드 컴포넌트로 재사용성과 가독성 높이기 (0) | 2024.05.09 |
Grid 아이템 이미지 로딩 후 리플로우 이슈 해결 (0) | 2024.05.06 |
결제 후 장바구니 속 아이템이 삭제되었다가 재생성되는 이슈 (0) | 2024.04.29 |