Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/pages/hooks/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@
"useTranslation": "useTranslation",
"useUnmountEffect": "useUnmountEffect",
"useOutsideClick": "useOutsideClick",
"useKeyDown": "useKeyDown"
"useKeyDown": "useKeyDown",
"useWorker": "useWorker",
"useLocalStorage": "useLocalStorage",
"_template": {
"display": "hidden"
}
}
57 changes: 57 additions & 0 deletions docs/pages/hooks/useLocalStorage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# useLocalStorage

## Introduce

로컬 스토리지와 동기화된 상태를 관리하는 훅입니다.

브라우저의 로컬 스토리지 API를 간편하게 사용할 수 있도록 도와주며,
타입 일관성을 유지하기 위해 잘못된 값이 저장되지 않도록 차단합니다.

```ts
interface UseLocalStorageHookProps<T> {
key: string;
initialValue: T;
}

interface UseLocalStorageHookReturns<T> extends Readonly<[T, (value: ValueResolver<T>) => void]> {}

const useLocalStorage = <T>(
key: string,
initialValue: T
): UseLocalStorageHookReturns<T>
```

import { Callout } from 'nextra/components';

<Callout type="warning">
값의 타입이 일치하지 않을 경우 초기값으로 재설정합니다. 또한, 브라우저
환경에서만 동작하므로 서버 환경에서는 사용할 수 없습니다.
</Callout>

### Props

- `key` : 로컬 스토리지에 저장할 값의 키를 지정합니다.
- `initialValue` : 저장할 초기 값을 설정합니다. 만약 로컬 스토리지에 기존 값이 존재하지 않거나 타입이 다른 경우, 해당 초기값이 사용됩니다.

### Returns

- `[storedValue, setValue]` : 첫 번째 요소는 저장된 값이며, 두 번째 요소는 해당 값을 업데이트하는 함수입니다.

## Examples

```tsx copy filename="TestComponent.tsx"
const TestComponent = () => {
const [value, setValue] = useLocalStorage<number>('count', 0);

const handleIncrement = () => setValue((prev) => prev + 1);
const handleReset = () => setValue(0);

return (
<div>
<p>Stored Value: {value}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleReset}>Reset</button>
</div>
);
};
```
134 changes: 134 additions & 0 deletions docs/pages/hooks/useWorker.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# useWorker

## Introduce

[웹 워커](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API)를 사용하여 비동기 작업을 처리하는 훅입니다.

비동기 작업을 백그라운드에서 실행하여 UI 스레드의 성능을 향상시킵니다.

```ts
interface UseWorkerProps<Arg, Return, Closure = never> {
script: WorkerScript<Arg, Return, Closure>;
}

interface UseWorkerReturns<Return> {
result: Return | undefined;
start: (args: Arg, closure?: Closure) => void;
cancel: () => void;
}

const useWorker = <Arg, Return, Closure = never>(props: UseWorkerProps<Arg, Return, Closure>): UseWorkerReturns<Return>
```

- 메인 스레드(주 스레드)와는 별도의 스레드에서 자바스크립트 코드를 실행하여 작업을 수행하고,
메인 스레드가 UI 렌더링과 같은 다른 작업을 차질 없이 진행할 수 있도록 도와줍니다.

- 기본적인 작업 외에 네트워크 요청도 워커 스레드 내부에서 동작이 가능하도록 구현하였습니다.

- 컴포넌트가 언마운트될 때, 사용 중이던 웹 워커를 자동으로 정리합니다.

### Props

- `script` - 웹 워커에서 실행할 함수를 전달합니다.

### Returns

- `result` : 작업의 결과를 저장하는 상태 값입니다. 작업이 완료되면 값이 동적으로 업데이트됩니다.
- `start` : 작업을 시작하는 함수입니다. 이 함수를 호출하면 웹 워커가 생성되고 작업이 시작됩니다.
- `cancel` : 현재 진행 중인 작업을 취소하는 함수입니다. 호출 시 활성화된 웹 워커가 종료됩니다.

import { Callout } from 'nextra/components';

<Callout type="error">useWorker 훅은 브라우저 환경에서만 동작합니다.</Callout>

### Type

- `Arg` : 작업에 필요한 인수 타입입니다.
- `Return` : 작업 결과의 반환 타입입니다.
- `Closure` :클로저(closure)로 전달될 타입입니다. 전달된 값은 작업 스레드의 추가 인자로 전달됩니다. (선택 사항)

## Examples

```tsx copy filename="TestComponent.tsx"
const App = () => {
const [count, setCount] = useState(0);

// <SynchronousComponent /> 컴포넌트는 매우 복잡한 계산이 수행될 때,
// 버튼 클릭과 같은 상호작용 이벤트가 처리되지 않는 블로킹 현상이 발생합니다.
// 반면, <WorkerComponent /> 컴포넌트는 웹 워커를 사용해 무거운 작업을 별도의 스레드에서 처리하므로,
// 메인 스레드에서는 버튼 클릭과 같은 이벤트를 원활하게 처리할 수 있습니다.

return (
<div>
<h1>O(n^2) 100억 연산 블로킹 테스트</h1>
<h1>메인 스레드 비블로킹 작업 (Web Worker 사용)</h1>
<WorkerComponent />
<h1>메인 스레드 블로킹 작업 (Web Worker 미사용)</h1>
<SynchronousComponent />
<button
onClick={() =>
setCount((prev) => prev + 1)
}>{`나를 클릭해주세요!: ${count}`}</button>
</div>
);
};

const size = 100_000;
// 테스트용 O(n^2) 작업
const workerScript = (num: number): number => {
let sum = 0;

for (let i = 1; i <= num; i++) {
for (let j = 1; j <= num; j++) {
sum += i * j;
}
}

return sum;
};

const WorkerComponent = () => {
const [input, setInput] = useState(size);
const { result, start, cancel } = useWorker(workerScript);

const handleStart = () => {
start(input);
};

return (
<div>
<input
type="number"
value={input}
disabled
onChange={(e) => setInput(parseInt(e.target.value, 10))}
/>
<button onClick={handleStart}>Start Worker</button>
<button onClick={cancel}>Cancel Worker</button>
<pre>{result ? JSON.stringify(result, null, 2) : 'No result'}</pre>
</div>
);
};

const SynchronousComponent = () => {
const [input, setInput] = useState(size);
const [result, setResult] = useState(0);

const handleStart = () => {
setResult(workerScript(input));
};

return (
<div>
<input
type="number"
value={input}
disabled
onChange={(e) => setInput(parseInt(e.target.value, 10))}
/>
<button onClick={handleStart}>Start Synchronous</button>
<div>Result: {result}</div>
</div>
);
};
```
Loading