Skip to content

Commit 4e0e10b

Browse files
docs(blog): 9차 배치 팁 포스트 5개 추가 (3개 언어)
1 parent 80856b0 commit 4e0e10b

15 files changed

Lines changed: 1159 additions & 0 deletions
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
layout: post
3+
title: "React useDeferredValue: Optimize Search Without Debounce"
4+
date: 2026-02-06 09:00:00 +0900
5+
categories: [Development, Tips]
6+
tags: [React, useDeferredValue, performance, rendering]
7+
author: "Kevin Park"
8+
lang: en
9+
excerpt: "Keep input responsive while deferring expensive list rendering — no debounce needed"
10+
---
11+
12+
## Problem
13+
14+
A search filter over a large list causes janky typing because every keystroke triggers a full re-render. Debounce helps, but introduces a fixed delay and hides the input value while typing.
15+
16+
```jsx
17+
function SearchList({ items }) {
18+
const [query, setQuery] = useState('');
19+
20+
// With 10,000 items, filtering runs on every keystroke
21+
const filtered = items.filter(item =>
22+
item.name.toLowerCase().includes(query.toLowerCase())
23+
);
24+
25+
return (
26+
<>
27+
<input value={query} onChange={e => setQuery(e.target.value)} />
28+
<List items={filtered} />
29+
</>
30+
);
31+
}
32+
```
33+
34+
## Solution
35+
36+
`useDeferredValue` keeps the input instantly responsive while deferring the expensive rendering.
37+
38+
```jsx
39+
import { useState, useDeferredValue, memo } from 'react';
40+
41+
function SearchList({ items }) {
42+
const [query, setQuery] = useState('');
43+
const deferredQuery = useDeferredValue(query);
44+
45+
// Input reflects query immediately, list uses deferredQuery
46+
const filtered = items.filter(item =>
47+
item.name.toLowerCase().includes(deferredQuery.toLowerCase())
48+
);
49+
50+
const isStale = query !== deferredQuery;
51+
52+
return (
53+
<>
54+
<input value={query} onChange={e => setQuery(e.target.value)} />
55+
<div style={{ opacity: isStale ? 0.7 : 1 }}>
56+
<SlowList items={filtered} />
57+
</div>
58+
</>
59+
);
60+
}
61+
62+
// Must wrap with memo for the deferral to actually work
63+
const SlowList = memo(function SlowList({ items }) {
64+
return (
65+
<ul>
66+
{items.map(item => (
67+
<li key={item.id}>{item.name}</li>
68+
))}
69+
</ul>
70+
);
71+
});
72+
```
73+
74+
The difference from debounce:
75+
76+
```
77+
debounce: typing stops → wait 300ms → start filtering
78+
useDeferredValue: input updates instantly → filtering updates when browser is idle
79+
```
80+
81+
User input is never delayed. React automatically defers only the expensive re-render.
82+
83+
## Key Points
84+
85+
- `useDeferredValue` separates urgent updates (input) from non-urgent updates (list rendering)
86+
- Must be used with `memo` — without it, the component re-renders every time regardless
87+
- Unlike debounce, there's no fixed delay — fast devices see near-instant updates, slow devices get graceful degradation
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
layout: post
3+
title: "React useDeferredValueでdebounceなしに検索を最適化する"
4+
date: 2026-02-06 09:00:00 +0900
5+
categories: [Development, Tips]
6+
tags: [React, useDeferredValue, パフォーマンス, レンダリング]
7+
author: "Kevin Park"
8+
lang: ja
9+
excerpt: "入力はすぐに反映しつつ、重いリストのレンダリングだけ遅延させます"
10+
---
11+
12+
## 問題
13+
14+
検索フィルター付きのリストを作りましたが、データが多いとキーストロークのたびに再レンダリングが走り、入力がカクつきます。`debounce`で対応はできますが、入力中に値が見えない固定の遅延が気になります。
15+
16+
```jsx
17+
function SearchList({ items }) {
18+
const [query, setQuery] = useState('');
19+
20+
// itemsが10000件あると、毎回のタイピングで全件フィルタリング
21+
const filtered = items.filter(item =>
22+
item.name.toLowerCase().includes(query.toLowerCase())
23+
);
24+
25+
return (
26+
<>
27+
<input value={query} onChange={e => setQuery(e.target.value)} />
28+
<List items={filtered} />
29+
</>
30+
);
31+
}
32+
```
33+
34+
## 解決方法
35+
36+
`useDeferredValue`を使えば、入力は即座に反映しつつ、重いレンダリングは後回しにできます。
37+
38+
```jsx
39+
import { useState, useDeferredValue, memo } from 'react';
40+
41+
function SearchList({ items }) {
42+
const [query, setQuery] = useState('');
43+
const deferredQuery = useDeferredValue(query);
44+
45+
// 入力はqueryで即時反映、リストはdeferredQueryで遅延レンダリング
46+
const filtered = items.filter(item =>
47+
item.name.toLowerCase().includes(deferredQuery.toLowerCase())
48+
);
49+
50+
const isStale = query !== deferredQuery;
51+
52+
return (
53+
<>
54+
<input value={query} onChange={e => setQuery(e.target.value)} />
55+
<div style={{ opacity: isStale ? 0.7 : 1 }}>
56+
<SlowList items={filtered} />
57+
</div>
58+
</>
59+
);
60+
}
61+
62+
// memoで囲まないと遅延効果が正しく動作しません
63+
const SlowList = memo(function SlowList({ items }) {
64+
return (
65+
<ul>
66+
{items.map(item => (
67+
<li key={item.id}>{item.name}</li>
68+
))}
69+
</ul>
70+
);
71+
});
72+
```
73+
74+
`debounce`との違いはこうです:
75+
76+
```
77+
debounce: 入力停止 → 300ms待機 → フィルタリング開始
78+
useDeferredValue: 入力は即時反映 → ブラウザの空き時間にフィルタリング更新
79+
```
80+
81+
ユーザーの入力は一切遅延せず、重い再レンダリングだけをReactが自動的に後回しにします。
82+
83+
## ポイント
84+
85+
- `useDeferredValue`は緊急な更新(入力)と非緊急な更新(リスト描画)を分離します
86+
- `memo`と一緒に使う必要があります — 使わないと毎回再レンダリングされてしまいます
87+
- `debounce`と違い固定の遅延がないので、高速なデバイスではほぼ即時に反映され、低速なデバイスでのみ遅延が発生します
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
layout: post
3+
title: "React useDeferredValue로 debounce 없이 검색 최적화하기"
4+
date: 2026-02-06 09:00:00 +0900
5+
categories: [Development, Tips]
6+
tags: [React, useDeferredValue, 성능 최적화, 렌더링]
7+
author: "Kevin Park"
8+
lang: ko
9+
excerpt: "검색 입력할 때마다 버벅대는 리스트, debounce 대신 React 네이티브 방식으로 해결한다"
10+
---
11+
12+
## 문제
13+
14+
검색 필터가 있는 리스트를 만들었는데, 데이터가 많으니까 입력할 때마다 리렌더링이 걸려서 타이핑이 버벅거렸다. `debounce`를 걸면 되긴 하는데, 타이핑 중에 입력값이 안 보이는 게 거슬렸다.
15+
16+
```jsx
17+
function SearchList({ items }) {
18+
const [query, setQuery] = useState('');
19+
20+
// items가 10000개면 매 타이핑마다 전부 필터링
21+
const filtered = items.filter(item =>
22+
item.name.toLowerCase().includes(query.toLowerCase())
23+
);
24+
25+
return (
26+
<>
27+
<input value={query} onChange={e => setQuery(e.target.value)} />
28+
<List items={filtered} />
29+
</>
30+
);
31+
}
32+
```
33+
34+
## 해결
35+
36+
`useDeferredValue`를 쓰면 입력은 즉시 반영하고, 무거운 렌더링은 뒤로 미룰 수 있다.
37+
38+
```jsx
39+
import { useState, useDeferredValue, memo } from 'react';
40+
41+
function SearchList({ items }) {
42+
const [query, setQuery] = useState('');
43+
const deferredQuery = useDeferredValue(query);
44+
45+
// 입력은 query로 즉시 반영, 리스트는 deferredQuery로 지연 렌더링
46+
const filtered = items.filter(item =>
47+
item.name.toLowerCase().includes(deferredQuery.toLowerCase())
48+
);
49+
50+
const isStale = query !== deferredQuery;
51+
52+
return (
53+
<>
54+
<input value={query} onChange={e => setQuery(e.target.value)} />
55+
<div style={{ opacity: isStale ? 0.7 : 1 }}>
56+
<SlowList items={filtered} />
57+
</div>
58+
</>
59+
);
60+
}
61+
62+
// memo로 감싸야 지연 효과가 제대로 동작한다
63+
const SlowList = memo(function SlowList({ items }) {
64+
return (
65+
<ul>
66+
{items.map(item => (
67+
<li key={item.id}>{item.name}</li>
68+
))}
69+
</ul>
70+
);
71+
});
72+
```
73+
74+
`debounce`와의 차이는 이렇다:
75+
76+
```
77+
debounce: 입력 멈춤 → 300ms 대기 → 필터링 시작
78+
useDeferredValue: 입력 즉시 반영 → 브라우저 여유 시 필터링 업데이트
79+
```
80+
81+
사용자 입력은 절대 지연되지 않고, 무거운 리렌더링만 React가 알아서 뒤로 미뤄준다.
82+
83+
## 핵심 포인트
84+
85+
- `useDeferredValue`는 긴급한 업데이트(입력)와 느린 업데이트(리스트)를 분리해준다
86+
- `memo`와 같이 써야 효과가 있다 — 안 쓰면 어차피 매번 리렌더링된다
87+
- `debounce`와 달리 고정 딜레이가 없어서, 빠른 기기에서는 거의 즉시 반영되고 느린 기기에서만 지연된다
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
layout: post
3+
title: "Generate UUIDs in Node.js Without the uuid Package"
4+
date: 2026-03-09 09:00:00 +0900
5+
categories: [Development, Tips]
6+
tags: [Node.js, UUID, crypto, dependencies]
7+
author: "Kevin Park"
8+
lang: en
9+
excerpt: "crypto.randomUUID() is built-in — no need for the uuid package anymore"
10+
---
11+
12+
## Problem
13+
14+
Every project that needs UUIDs pulls in the `uuid` package. Adding an external dependency just for UUID v4 generation feels unnecessary.
15+
16+
```bash
17+
npm install uuid
18+
```
19+
20+
```javascript
21+
const { v4: uuidv4 } = require('uuid');
22+
const id = uuidv4();
23+
```
24+
25+
## Solution
26+
27+
Since Node.js 19 (and LTS 20+), `crypto.randomUUID()` is available globally. No package install needed.
28+
29+
```javascript
30+
// Node.js 19+ / all modern browsers
31+
const id = crypto.randomUUID();
32+
// 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
33+
```
34+
35+
No `require` needed — `crypto` is on the global object.
36+
37+
For older versions (Node 14.17–18), you need the require:
38+
39+
```javascript
40+
// Node 14.17 – 18
41+
const { randomUUID } = require('crypto');
42+
const id = randomUUID();
43+
```
44+
45+
Works in browsers too:
46+
47+
```javascript
48+
// All modern browsers
49+
const id = self.crypto.randomUUID();
50+
```
51+
52+
A compatibility helper if you need one:
53+
54+
```javascript
55+
function generateUUID() {
56+
// Global crypto (Node 19+ / browsers)
57+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
58+
return crypto.randomUUID();
59+
}
60+
// Node 14.17+
61+
return require('crypto').randomUUID();
62+
}
63+
```
64+
65+
## Key Points
66+
67+
- `crypto.randomUUID()` generates RFC 4122 compliant UUID v4 — identical output to the `uuid` package
68+
- Available globally on Node.js 20 LTS and above — zero external dependencies
69+
- Supported in all modern browsers, so you can use the same approach across your full stack

0 commit comments

Comments
 (0)