You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: pages/posts/React/react-toastify.md
+37-37Lines changed: 37 additions & 37 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,11 +7,11 @@ description: 리액트에서 상태 관리와 렌더링은 보통 컴포넌트
7
7
8
8
# react-toastify 는 어떻게 리액트 렌더링사이클 밖에서도 토스트를 띄울수 있을까?
9
9
10
-
리액트에서 상태 관리와 렌더링은 보통 컴포넌트 내부의 state, props를 통해 이루어진다
10
+
리액트에서 상태 관리와 렌더링은 보통 컴포넌트 내부의 state, props를 통해 이루어집니다
11
11
12
-
그런데 `react-toastify` 와 같은 라이브러리를 보면, "리액트 컴포넌트 바깥"에서, 즉 비동기 로직이나 이벤트 핸들러, API 응답 등 어디서든 토스트(알림)를 띄우는 기능이 존재한다. 어떻게 그게 가능할까?
12
+
그런데 `react-toastify` 와 같은 라이브러리를 보면, "리액트 컴포넌트 바깥"에서, 즉 비동기 로직이나 이벤트 핸들러, API 응답 등 어디서든 토스트(알림)를 띄우는 기능이 존재합니다. 어떻게 그게 가능할까요?
13
13
14
-
예시를 보자
14
+
예시를 한번 보겠습니다
15
15
16
16
```js
17
17
toast("로그인 성공!");
@@ -20,49 +20,49 @@ setTimeout(() => toast("잠깐 후에 다시 알려줌!"), 5000);
20
20
21
21
이런 코드가 정말 동작한다
22
22
23
-
어떻게 리액트 컴포넌트 바깥에서 toast()를 호출해도, 화면에는 토스트가 "짠!" 하고 나타나는 걸까?
23
+
어떻게 리액트 컴포넌트 바깥에서 toast()를 호출해도, 화면에는 토스트가 "짠!" 하고 나타나는 걸까요?
24
24
25
25
> 결론부터 말하자면, 내부적으로 <br/>
26
26
> `useSyncExternalStore` 와 `Observer 패턴`을 사용한다
27
27
28
28
## 🤔 리액트의 일반적인 상태 흐름
29
29
30
-
먼저, 리액트의 일반적인 렌더링을 생각해보자.
30
+
먼저, 리액트의 일반적인 렌더링은 다음과 같이 동작합니다
31
31
32
32
> 1. 컴포넌트 내부의 상태(state)가 바뀌면
33
33
> 2. 컴포넌트가 리렌더링되고, 새로운 가상 DOM (Virtual DOM) 이 생성된다
34
34
> 3. 새로만든 가상 DOM과 이전 가상 DOM 을 비교한다 (Reconciliation)
35
35
> 4. 변경된 사항을 DOM 에 반영한다
36
36
37
-
즉, `상태 변화 → 렌더링 → UI 반영` 이라는 단방향 흐름이 기본적으로 리액트가 동작하는 방식이다.
37
+
즉, `상태 변화 → 렌더링 → UI 반영` 이라는 단방향 흐름이 기본적으로 리액트가 동작하는 방식입니다
38
38
39
-
하지만 `toast()` 는 리액트 컴포넌트가 아닌 일반 함수다.
40
-
이 함수가 상태를 바꾼다고 해서, 리액트가 "알아서" 리렌더링해주지는 않는다.
39
+
하지만 `toast()` 는 리액트 컴포넌트가 아닌 일반 함수입니다.
40
+
이 함수가 상태를 바꾼다고 해서, 리액트가 "알아서" 리렌더링해주지는 않습니다
41
41
42
42
## ✅ Observer 패턴!
43
43
44
44
### Observer 패턴이란?
45
45
46
46
> "상태가 바뀌면, 그걸 구독(관찰)하고 있는 객체들에게 자동으로 알림을 보내고, 각 객체가 알아서 처리하게 한다" ([Refactoring Guru - 옵저버 패턴](https://refactoring.guru/ko/design-patterns/observer))
47
47
48
-
쉽게 말해, "내가 상태가 바뀌면, 구독자들에게 알림을 쏴준다!" 는 개념이다.
49
-
`react-toastify` 는 이 디자인패턴을 사용해 `<ToastContainer/>` 를 구독자로 만들고, `toast()` 함수 호출을 통해 알림을 전달한다.
48
+
쉽게 말해, "내가 상태가 바뀌면, 구독자들에게 알림을 쏴준다!" 는 개념입니다
49
+
`react-toastify` 는 이 디자인패턴을 사용해 `<ToastContainer/>` 를 구독자로 만들고, `toast()` 함수 호출을 통해 알림을 전달합니다
50
50
51
51
## `react-toastify` 의 전체 흐름을 따라가보자
52
52
53
-
`react-toastify` 는 아래와 같은 흐름으로 동작한다 <br/>
53
+
`react-toastify` 는 아래와 같은 흐름으로 동작합니다 <br/>
54
54
55
55

56
56
57
-
옆에 `react-toastify` 코드를 함께 보고 흐름을따라가면 이해하기가 쉽다! (https://github.com/fkhadra/react-toastify)
57
+
옆에 `react-toastify` 코드를 함께 보고 흐름을따라가면 이해하기가 쉽습니다! (https://github.com/fkhadra/react-toastify)
58
58
59
59
<br/>
60
60
61
61
### 1. `<ToastContainer/>` 의 마운트 및 subscribe
62
62
63
63

64
64
65
-
`react-toastify` 를 사용하기 위해서는 가장 먼저 `<ToastContainer/>` 를 리액트 앱에 마운트 해야 한다
65
+
`react-toastify` 를 사용하기 위해서는 가장 먼저 `<ToastContainer/>` 를 리액트 앱에 마운트 해야 합니다.
66
66
67
67
```tsx
68
68
exportdefaultfunction App() {
@@ -75,7 +75,7 @@ export default function App() {
75
75
}
76
76
```
77
77
78
-
[`<ToastContainer/>`](https://github.com/fkhadra/react-toastify/blob/main/src/components/ToastContainer.tsx#L28) 의 내부를 보면, `getToastToRender` 를 호출하고, 해당 콜백함수에서 `toastList` 를 받아와 `<Toast/>` 컴포넌트를 렌더링하는것을 볼 수 있다
78
+
[`<ToastContainer/>`](https://github.com/fkhadra/react-toastify/blob/main/src/components/ToastContainer.tsx#L28) 의 내부를 보면, `getToastToRender` 를 호출하고, 해당 콜백함수에서 `toastList` 를 받아와 `<Toast/>` 컴포넌트를 렌더링하는것을 볼 수 있습니다
`react-toastify` 의 store 는 `createContainerObserver` 라는 팩토리 함수로 만들어진다
125
+
`react-toastify` 의 store 는 `createContainerObserver` 라는 팩토리 함수로 만들어집니다
126
126
127
127
여기서 토스트 상태(`toasts`), 스냅샷(`snapshot`), 그리고 구독자(`listeners`) 등이 클로저로 관리되고,
128
-
`observe()` 와 `notify()` 로 구독자 등록 및 알림전송을 한다
128
+
`observe()` 와 `notify()` 로 구독자 등록 및 알림전송을 합니다
129
129
130
130
```ts
131
131
exportfunction createContainerObserver(
@@ -167,7 +167,7 @@ export function createContainerObserver(
167
167
168
168
### 3. `useSyncExternalStore()` 로 외부 상태 구독
169
169
170
-
`getToastToRender` 는 [`useToastContainer`](https://github.com/fkhadra/react-toastify/blob/e1fa4760cea8adf28d5cf93cd14067a852b1f5c8/src/hooks/useToastContainer.ts#L8) 에서 가져오는데, [`registerContainer()`](https://github.com/fkhadra/react-toastify/blob/main/src/core/store.ts#L113) 에서 `subscribe`, `getSnapshot` 을 가져와 [`useSyncExternalStore`](https://github.com/fkhadra/react-toastify/blob/e1fa4760cea8adf28d5cf93cd14067a852b1f5c8/src/hooks/useToastContainer.ts#L8) 로 전달한다.
170
+
`getToastToRender` 는 [`useToastContainer`](https://github.com/fkhadra/react-toastify/blob/e1fa4760cea8adf28d5cf93cd14067a852b1f5c8/src/hooks/useToastContainer.ts#L8) 에서 가져오는데, [`registerContainer()`](https://github.com/fkhadra/react-toastify/blob/main/src/core/store.ts#L113) 에서 `subscribe`, `getSnapshot` 을 가져와 [`useSyncExternalStore`](https://github.com/fkhadra/react-toastify/blob/e1fa4760cea8adf28d5cf93cd14067a852b1f5c8/src/hooks/useToastContainer.ts#L8) 로 전달합니다
[`dispatchToast()`](https://github.com/fkhadra/react-toastify/blob/main/src/core/toast.ts#L27) 는 `pushToast()` 를 호출하고 `pushToast()` 는 다시 `buildToast()` 를 호출한다.
226
+
[`dispatchToast()`](https://github.com/fkhadra/react-toastify/blob/main/src/core/toast.ts#L27) 는 `pushToast()` 를 호출하고 `pushToast()` 는 다시 `buildToast()` 를 호출합니다
227
227
228
228
```ts
229
229
function dispatchToast<TData>(content:ToastContent<TData>, options:NotValidatedToastProps):Id {
@@ -243,23 +243,23 @@ export function pushToast<TData>(content: ToastContent<TData>, options: NotValid
243
243
}
244
244
```
245
245
246
-
`buildToast()` 는 토스트 객체를 생성하고, Observer 패턴의 핵심인 `notify()` 를 호출한다
246
+
`buildToast()` 는 토스트 객체를 생성하고, Observer 패턴의 핵심인 `notify()` 를 호출합니다
247
247
248
-
`notify()` 가 호출되면 알림 컨테이너에 등록된 모든 구독자에게 상태가 바뀌었다고 신호를 보낸다.
249
-
`useSyncExternalStore` 에 전달된 subscribe 콜백이 실행되고, `<ToastContainer/>` 가 재렌더링된다.
248
+
`notify()` 가 호출되면 알림 컨테이너에 등록된 모든 구독자에게 상태가 바뀌었다고 신호를 보냅니다.
249
+
`useSyncExternalStore` 에 전달된 subscribe 콜백이 실행되고, `<ToastContainer/>` 가 재렌더링됩니다
250
250
251
251
<br/>
252
252
253
253
## ✍️ 정리하면...
254
254
255
-
`react-toastify` 가 리액트 렌더링 사이클 밖에서 작동하는 비밀은 `useSyncExternalStore` 훅과 `옵저버 패턴`의 조합에 있다
255
+
`react-toastify` 가 리액트 렌더링 사이클 밖에서 작동하는 비밀은 `useSyncExternalStore` 훅과 `옵저버 패턴`의 조합에 있습니다
256
256
257
257
### 1. 옵저버 패턴
258
258
259
-
`toast()` 함수가 호출되면, 리액트 컴포넌트 외부에 있는 `toasts` 라는 Map에 새로운 알림이 추가된다.
260
-
이 `toasts` Map이 바로 외부 상태고, 이 상태가 변경될 때마다 `notify()` 함수가 실행되어 `<ToastContainer/>`에게 상태가 바뀌었다는 신호를 보낸다
259
+
`toast()` 함수가 호출되면, 리액트 컴포넌트 외부에 있는 `toasts` 라는 Map에 새로운 알림이 추가됩니다
260
+
이 `toasts` Map이 바로 외부 상태고, 이 상태가 변경될 때마다 `notify()` 함수가 실행되어 `<ToastContainer/>`에게 상태가 바뀌었다는 신호를 보냅니다
261
261
262
262
### 2. useSyncExternalStore
263
263
264
-
`<ToastContainer/>` 는 useSyncExternalStore를 통해 toasts의 변화를 구독한다.
265
-
이 훅은 `notify()` 신호를 받으면 `getSnapshot` 함수를 호출하여 toasts의 현재 상태를 복사한 snapshot을 가져온다
264
+
`<ToastContainer/>` 는 useSyncExternalStore를 통해 toasts의 변화를 구독합니다
265
+
이 훅은 `notify()` 신호를 받으면 `getSnapshot` 함수를 호출하여 toasts의 현재 상태를 복사한 snapshot을 가져와 변경이된경우 재렌더링을 트리거합니다
0 commit comments