Measure, Compare, Decide - Filter Implementation Thought Process
The process of thinking of anxiety as a question, question as measurement, and measurement as confidence while implementing the filter.
5 min readreactlearn
Published
Oct 27, 2025
Reading time
5 min
Sections
12
On this page12+-
Start: Vague anxiety
When creating a log inquiry system in practice, I thought about how to implement filters. We had to decide whether to manage date ranges and search terms by URL, or by local storage or client state.
At first, I thought the URL method was right, but I had vague concerns.
“If you manage it by URL, won’t a lot of re-rendering happen every time you change the filter?”
“Wouldn’t there be a performance issue if the URL changes every time you type a search term?”
I couldn't explain specifically what the problem was, but I was worried about something.
Step 1: Identify the identity of your anxiety
We started by asking, “Why are we worried about re-rendering?”
This is what I was actually worried about, rather than the re-rendering itself being the problem.
Treat multiple filters as one useEffect, which will be executed on each state change
If there are multiple useEffect dependency arrays, it may be inefficient.
I think there will be a problem if the number of server calls increases every time you type a search term.
// I was worried about this code
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
const [search, setSearch] = useState('');
useEffect(() => {
const params = new URLSearchParams();
if (startDate) params.set('start', startDate);
if (endDate) params.set('end', endDate);
if (search) params.set('search', search);
window.history.pushState({}, '', `?${params}`);
}, [startDate, endDate, search]);
In summary, the essence of my anxiety came from the stereotype that “re-rendering = failure to optimize performance.”
Step 2: Actually Measure
Instead of just guessing, we decided to measure. We checked actual performance with React Profiler and console.log.
My worries were unfounded. window.history.pushState() only adds an object to the browser history stack; it is not a network request or DOM manipulation.
What we learned here: “Re-rendering = bad” is not always true. Optimization is something you do when you have a measured problem.
All three services managed filters by URL. But I saw a pattern.
실제 서비스 분석
주요 서비스들의 필터 구현 패턴 비교
URL 구조
?in=물금읍-3662&price=100__100000&search=노트북
검색
위치네비게이션 바
동작Enter 또는 검색 버튼
필터
위치사이드바
동작즉시 적용
공통 패턴
모든 서비스가 URL로 필터 상태를 관리합니다
검색과 필터의 UI 위치를 명확히 분리합니다
검색은 명시적 완료가 필요하고, 필터는 즉시 적용됩니다
검색 시 필터가 초기화됩니다 (검색이 더 큰 범위)
Differences in UI layout
Search box: Navigation bar (top, prominent location)
Filters: Sidebar (secondary location)
Why was it separated like this? I tried an experiment.
Experiment at Coupang
Set price filter (KRW 500,000 - KRW 1 million)
Enter “laptop” in the search box and search.
Result: Price filter initialized
Search has greater scope. A search is an action that changes the entire context, while a filter is a fine-tuning within the search results.
What we learned here: Same category as “filter”, but different roles require different handling.
Step 4: Switch to the user perspective
I chose the URL method, but another problem arose. Should I use pushState or replaceState?
// pushState: Add to history stack
window.history.pushState({}, '', `?${params}`);
// replaceState: Replace current item
window.history.replaceState({}, '', `?${params}`);
I tested the difference.
pushState test
Type “Gold Item Bug” in the search box (8 characters)
Click the back button
Result: Must press 8 times to return to previous page
replaceState test
Type “Gold Item Bug” in the search box (8 characters)
Click the back button
Result: Click once to return to the previous page
pushState vs replaceState 비교
검색어 입력 시 브라우저 히스토리 동작 차이
검색창pushState
브라우저 히스토리
초기 상태
/logs
현재
pushState 특징
입력한 글자 수만큼 히스토리가 쌓입니다
"골드아이템" 입력 시 뒤로가기를 5번 눌러야 합니다
타이핑 과정을 모두 기록합니다
Which is better? I thought about the user's "intent".
When the user presses back,
Would you like to go back to the typing process one by one?
Or do we want to bring back the very intention of “search”?
Entering “Gold Item Bug” is the means, and “I will search for Gold Item Bug” is the intent. History must be built on an intent-by-intent basis.
So the search term had to either apply debounce or explicitly mark completion via the Enter key/search button. I used pushState immediately because the date filter is done the moment it is selected.
Reflection: Judgment principles learned
These are the five principles we learned in this course.
1. Turn vague anxiety into a concrete scenario
“I’m worried about re-rendering” is vague. "I'm concerned that if useEffect is executed multiple times, it increases server calls" is specific. If you specify it, you can verify it.
2. Don’t guess, measure
It was just a guess until I checked 1.8ms with React Profiler. When measured, vague anxiety becomes concrete numbers.
3. Don’t worry alone, do research
There are patterns that Carrot, Coupang, and Google have already verified. There's no need to reinvent the wheel. However, it is important to infer why it was made that way.
4. Optimization for a reason
It's not "It's right to do it if you can," it's "Let's think about why it's needed now and apply it." If there is no measured problem, optimization is overoptimization.
5. User perspective rather than technology
The choice of pushState vs. replaceState was based more on “what the user expects when they hit back” than on technical differences. You need to think about your users before writing code.
Finish
I learned a lot from implementing one filter. Rather than simply “how to manage filters with URLs,” we practiced the thought process of turning anxiety into questions, questions into measurements, and measurements into confidence.
The next time you find yourself on the fence about choosing another technology, remember these five principles.