The process of thinking of anxiety as a question, question as measurement, and measurement as confidence while implementing the filter.

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.
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.
useEffect, which will be executed on each state changeuseEffect dependency arrays, it may be inefficient.// 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.”
Instead of just guessing, we decided to measure. We checked actual performance with React Profiler and console.log.
import { Profiler, ProfilerOnRenderCallback } from 'react';
const onRenderCallback: ProfilerOnRenderCallback = (
id,
phase,
actualDuration,
) => {
console.log('Profiler:', {
id,
phase,
actualDuration: `${actualDuration.toFixed(2)}ms`,
});
};
export default function App() {
return (
<Profiler id="UrlSection" onRender={onRenderCallback}>
<UrlSection />
</Profiler>
);
}
export const UrlSection = () => {
console.log('Rendering UrlSection');
useEffect(() => {
console.log('execute useEffect:', { startDate, endDate, search });
const before = performance.now();
window.history.pushState({}, '', `?${params}`);
const after = performance.now();
console.log(`pushState time: ${(after - before).toFixed(3)}ms`);
}, [startDate, endDate, search]);
// ...
};
Measurement results
Performance recognition criteria include:
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.
We checked how other services implemented it.
Carrot Market
?in=Mulgeum-eup-3662&price=100__100000&search=Laptop
Coupang
?filterType=&rating=0&minPrice=680000&maxPrice=1360000
&brand=257&q=laptop
?q=From ₩300,000 to +₩800,000+Laptop
All three services managed filters by URL. But I saw a pattern.
?in=물금읍-3662&price=100__100000&search=노트북Differences in UI layout
Why was it separated like this? I tried an experiment.
Experiment at Coupang
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.
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
replaceState test
/logsWhich is better? I thought about the user's "intent".
When the user presses back,
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.
These are the five principles we learned in this course.
“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.
It was just a guess until I checked 1.8ms with React Profiler. When measured, vague anxiety becomes concrete numbers.
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.
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.
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.
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.