diff --git a/June/article/Type-safe_module_mocking_in_Storybook.md b/June/article/Type-safe_module_mocking_in_Storybook.md
new file mode 100644
index 0000000..d3aec20
--- /dev/null
+++ b/June/article/Type-safe_module_mocking_in_Storybook.md
@@ -0,0 +1,273 @@
+## π [Type-safe module mocking in Storybook](https://storybook.js.org/blog/type-safe-module-mocking/?utm_source=newsletter.reactdigest.net&utm_medium=newsletter&utm_campaign=sneaky-react-memory-leaks)
+
+### ποΈ λ²μ λ μ§: 2024.06.03
+
+### π§ λ²μν ν¬λ£¨: λ¬κΈ°(λ°μ μ°)
+
+---
+
+## μ€ν 리λΆμμμ Type-safe λͺ¨λ λͺ¨νΉ
+
+UIλ₯Ό λΆλ¦¬νμ¬ κ°λ°νκ³ ν
μ€νΈνλ λ° μμ΄ μΌκ΄μ±μ λ§€μ° μ€μν©λλ€.
+
+μ΄μμ μΌλ‘, μ€ν 리λΆμ μ€ν 리λ λκ° μΈμ 보λ , λ°±μλκ° μλνκ³ μλ μλλ νμ λμΌν UIλ₯Ό λ λλ§ν΄μΌ ν©λλ€. μ€ν 리μ μ£Όμ΄μ§ μ
λ ₯μ νμ λμΌν μΆλ ₯ κ²°κ³Όλ₯Ό κ°μ ΈμΌ ν©λλ€.
+
+μ΄λ UIμ μ
λ ₯μ΄ μ»΄ν¬λνΈμ μ λ¬λλ propsλ§ μμ λλ λ¨μν©λλ€. μ»΄ν¬λνΈκ° 컨ν
μ€νΈ μ 곡μλ‘λΆν° λ°μ΄ν°λ₯Ό μμ‘΄νλ κ²½μ°, μ΄λ₯Ό λͺ¨νΉνκΈ° μν΄ μ€ν 리λ₯Ό Decoratorλ‘ κ°μΈμ λͺ¨νΉν μ μμ΅λλ€. λ€νΈμν¬μμ κ°μ Έμ¨ μ
λ ₯μ΄ μλ UIμ κ²½μ°, λ€νΈμν¬ μμ²μ κ²°μ λ‘ μ μΌλ‘ λͺ¨μνλ λ§€μ° μΈκΈ° μλ Mock Service Worker μ λμ¨μ΄ μμ΅λλ€.
+
+κ·Έλ λ€λ©΄ μ»΄ν¬λνΈκ° λΈλΌμ°μ APIμ κ°μ λ€λ₯Έ μΆμ²μ μμ‘΄νλ κ²½μ°λ μ΄λ»κ² λ κΉμ? μλ₯Ό λ€μ΄ μ¬μ©μμ ν
λ§ μ νΈλ, λ‘컬 μ€ν 리μ§μ λ°μ΄ν°, λλ μΏ ν€λ₯Ό μ½λ κ²½μ°, νΉμ μ»΄ν¬λνΈκ° νμ¬ λ μ§λ μκ°μ λ°λΌ λ€λ₯΄κ² λμνλ κ²½μ°, μλλ©΄ μ»΄ν¬λνΈκ° Next.jsμ next/routerμ κ°μ λ©ν νλ μμν¬ APIλ₯Ό μ¬μ©νλ κ²½μ°λ μ΄λ¨κΉμ?
+
+μ΄λ¬ν μ νμ μ
λ ₯μ λͺ¨νΉνλ κ²μ μμ¬μ μΌλ‘ μ€ν 리λΆμμ μ΄λ €μ μ΅λλ€. κ·Έλ¦¬κ³ λ°λ‘ μ€λ μ°λ¦¬κ° λͺ¨λ λͺ¨νΉμ ν΅ν΄ ν΄κ²°νκ³ μλ λ¬Έμ μ
λλ€! μ°λ¦¬μ μ κ·Ό λ°©μμ κ°λ¨νκ³ , νμ
μμ νλ©°, νμ€ κΈ°λ°μ
λλ€. κ·Έκ²μ λΆν¬λͺ
νκ±°λ λ
μ μ μΈ λͺ¨λ APIλ³΄λ€ λͺ
νμ±κ³Ό λλ²κΉ
μ λͺ
νμ±μ μ νΈν©λλ€.
+_κ·Έλ¦¬κ³ μ°λ¦¬λ μ’μ νμ¬μ μμ΅λλ€: Epic Stackμ μ°½μ‘°μ Kent C. Doddλ μ λμ μΈ μμ
κ³Ό React μλ² μ»΄ν¬λνΈ μν€ν
νΈ Seb MarkbΓ₯geκ° μ€ν λ¦¬λΆ λͺ¨νΉμ μ§μ μ μΈ μκ°μ μ£Όμλ€κ³ μΆμ²ν©λλ€._
+
+
+
+## λͺ¨λ λͺ¨νΉμ΄ 무μμΈκ°μ?
+
+λͺ¨λ λͺ¨νΉμ μ»΄ν¬λνΈκ° μ§μ μ μ΄κ±°λ κ°μ μ μΌλ‘ κ°μ Έμ€λ λͺ¨λμ μΌκ΄λκ³ λ
립μ μΈ λμ²΄λ¬Όλ‘ κ΅μ²΄νλ κΈ°μ μ
λλ€. μ λ ν
μ€νΈμμλ μ΄ κΈ°μ μ΄ μ½λλ₯Ό μ¬ν κ°λ₯ν μνλ‘ ν
μ€νΈνλ λ° λμμ μ€ μ μμ΅λλ€. μ€ν 리λΆμμλ μ΄λ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ₯Ό ν₯λ―Έλ‘μ΄ λ°©μμΌλ‘ κ²μνλ μ»΄ν¬λνΈλ₯Ό λ λλ§νκ³ ν
μ€νΈν μ μμ΅λλ€.
+
+μλ₯Ό λ€μ΄, μ¬μ©μκ° νμν μ 보λ₯Ό μ νν μ μκ³ κ·Έ μ€μ μ λΈλΌμ°μ μ λ‘컬 μ€ν 리μ§μ μ μ₯νλ μ¬μ©μ μ€μ κ°λ₯ν λμ보λ μ»΄ν¬λνΈλ₯Ό κ³ λ €ν΄ λ³΄μΈμ.
+
+![alt text](https://storybookblog.ghost.io/content/images/size/w1600/2024/05/Dashboard.png)
+
+μ΄κ²μ μ¬μ©μμ μ€μ μ λ‘컬 μ€ν 리μ§μ μ½κ³ μ°λ μ€μ λ°μ΄ν° μ κ·Ό κ³μΈ΅μΌλ‘ ꡬνλλ©°, UIλ₯Ό λ΄λΉνλ λμ€νλ μ΄ μ»΄ν¬λνΈμΈ λμ보λ μμμ
λλ€:
+
+```tsx
+// lib/settings.ts
+export const getDashboardLayout = () => {
+ const layout = window.localStorage.getItem("dashboard.layout");
+ return layout ? parseLayout(layout) : [];
+};
+```
+
+```tsx
+// components/Dashboard.tsx
+import { getDashboardLayout } from "../lib/settings.ts";
+
+export const Dashboard = (props) => {
+ const layout = getDashboardLayout();
+ // logic to display layout
+};
+```
+
+λμ보λ μ»΄ν¬λνΈλ₯Ό ν
μ€νΈνκΈ° μν΄, μ°λ¦¬λ ν΅μ¬ μνλ₯Ό λ€λ£¨λ λ€μν λ μ΄μμμ μμ μΈνΈλ₯Ό λ§λ€κ³ μ ν©λλ€. κ°λ¨νκ² νκΈ° μν΄μ, μΌλ°μ±μ μμ§ μλ μ μμ, μ°λ¦¬λ λ μ΄μμμ μ½λ λΆλΆμλ§ μ§μ€ν κ²μ
λλ€.
+
+μ΄ κΈμμλ λͺ¨λ λͺ¨νΉμ μ€λͺ
νκ³ , μ°λ¦¬κ° μ΄λ»κ² κ·Έκ²μ λ¬μ±νλμ§, κ·Έλ¦¬κ³ μ°λ¦¬μ μ κ·Όλ²μ΄ λ€λ₯Έ ꡬνκ³Ό λΉκ΅νμ λ μ΄λ€ μ΄μ μ΄ μλμ§λ₯Ό μ€λͺ
νλ μ€ν μμ λ‘ μ΄λ₯Ό μ¬μ©ν κ²μ
λλ€.
+
+## κΈ°μ‘΄ μ κ·Ό λ°©μ: λ
μ API
+
+Jest λ° Vitestμ κ°μ μΈκΈ° μλ μ λ ν
μ€νΈ λꡬλ€μ λͺ¨λ λͺ¨νΉμ μν μ μ°ν λ©μ»€λμ¦μ μ 곡ν©λλ€. μ΄λ€μ μΈμ ν mocks λλ ν 리μμ λͺ¨νΉ νμΌμ μλμΌλ‘ μ°Ύλ μμμ
λλ€:
+
+```tsx
+// lib/__mocks__/settings.ts
+export const getDashboardLayout = () => [
+ /* dummy data here */
+];
+```
+
+λλ, ν
μ€νΈ νμΌ λ΄μμ λͺ¨νΉλ₯Ό μ μΈνκΈ° μν΄ λͺ
λ Ήν APIλ₯Ό μ 곡ν©λλ€:
+
+```tsx
+// components/Dashboard.test.ts
+import { vi, fn } from 'vitest';
+import { getDashboardLayout } from '../lib/settings.ts';
+
+vi.mock('../lib/settings.ts', () => ({
+ getDashboardLayout: fn(() => ([ /* dummy data here */])),
+});
+```
+
+μ΄ APIλ κ°λ¨ν΄ 보μ΄μ§λ§, μ€μ λ‘λ κ°μ Έμ€κΈ°λ₯Ό λͺ¨νΉμΌλ‘ λ체νκΈ° μν΄ λ³΅μ‘νκ³ λ€μ λ§λ² κ°μ νμΌ λ³νμ μ λ°ν©λλ€. κ²°κ³Όμ μΌλ‘ μ½λμ μμ λ³κ²½μ΄ λͺ¨νΉμ νΌλμ€λ½κ² λ§λλ λ°©μμΌλ‘ κΉ¨λ¨λ¦΄ μ μμ΅λλ€. μλ₯Ό λ€μ΄, λ€μκ³Ό κ°μ λ³νμ μ€ν¨ν©λλ€:
+
+```tsx
+// components/Dashboard.test.ts
+import { vi, fn } from 'vitest';
+import { getDashboardLayout } from '../lib/settings.ts';
+
+const dummyLayout = [ /* dummy data here */];
+vi.mock('../lib/settings.ts', () => ({
+ getDashboardLayout: fn(() => dummyLayout), // FAIL!!!
+});
+```
+
+νμ§λ§ μ°λ¦¬μ λͺ©νλ μ΄ λ°μ΄λ λꡬλ€μ λΉννλ κ²μ΄ μλλλ€. μ€νλ €, μ°λ¦¬λ μλ‘κ³ νμ€ κΈ°λ°μ μ κ·Ό λ°©μμ μ¬μ©νμ¬ λ λμ λͺ¨νΉμ μ΄λ»κ² ν μ μλμ§ νꡬνκ³ μ ν©λλ€.
+
+## μ°λ¦¬μ μ κ·Ό λ°©μ: Subpath Imports
+
+μ€ν 리λΆμμμ λͺ¨λ λͺ¨νΉμ μλΈν¨μ€ μν¬νΈ νμ€μ νμ©νλ©°, μ΄λ `package.jsonμ imports` νλλ₯Ό ν΅ν΄ μ€μ κ°λ₯ν©λλ€ β λͺ¨λ JS νλ‘μ νΈμ μ¬μ₯λΆμ
λλ€ β νλ‘μ νΈ μ 체μ κ±Έμ³ λͺ¨μλ₯Ό κ°μ Έμ€λ νμ΄νλΌμΈ μν μ ν©λλ€.
+
+μ΄ μ κ·Ό λ°©μμ νλμ μνΌνμλ `package.json exports`μ λ§μ°¬κ°μ§λ‘, `package.json imports`λ 쑰건λΆλ‘ λ§λ€ μ μλ€λ κ²μ
λλ€. μ¦, λ°νμ νκ²½μ λ°λΌ κ°μ Έμ€λ κ²½λ‘λ₯Ό λ€λ₯΄κ² ν μ μμ΅λλ€. μ΄λ μ€ν 리λΆμμλ λͺ¨μλ λͺ¨λμ κ°μ Έμ€κ³ , λ€λ₯Έ κ³³μμλ μ€μ λͺ¨λμ κ°μ Έμ€λλ‘ package.jsonμ λ§μΆ€ μ€μ ν μ μλ€λ μλ―Έμ
λλ€!
+
+μλΈν¨μ€ μν¬νΈλ μ²μμ Node.jsμμ λμ
λμμ§λ§, μ΄μ λ JS μνκ³ μ λ°μμ μ§μλκ³ μμΌλ©°, TypeScript(λ²μ 5.4λΆν°), Webpack, Vite, Jest, Vitest λ±μμλ μ§μλ©λλ€.
+
+μμ μμ λ₯Ό κ³μν΄μ, λ€μμ `./lib/settings.ts`μμ λͺ¨λμ λͺ¨νΉνλ λ°©λ²μ
λλ€:
+
+```tsx
+{
+ "imports": {
+ "#lib/settings": {
+ "storybook": "./lib/settings.mock.ts",
+ "default": "./lib/settings.ts"
+ },
+ "#*": [ // fallback for non-mocked absolute imports
+ "./*",
+ "./*.ts",
+ "./*.tsx"
+ ]
+ }
+}
+```
+
+μ¬κΈ°μ μ°λ¦¬λ λͺ¨λ 리쑸λ²μκ² λͺ¨λ `#lib/settings`μμμ κ°μ Έμ€κΈ°κ° μ€ν 리λΆμμλ `../lib/settings.mock.ts`λ‘, μ ν리μΌμ΄μ
μμλ `../lib/settings.ts`λ‘ ν΄μλλλ‘ μ§μνκ³ μμ΅λλ€.
+
+μ΄λ λν Node.js μ¬μμ λ°λΌ #-κΈ°νΈλ‘ μμνλ μ λ κ²½λ‘μμ κ°μ Έμ€λλ‘ μ»΄ν¬λνΈλ₯Ό μμ ν΄μΌ νλ©°, μ΄λ κ²½λ‘λ ν¨ν€μ§ κ°μ Έμ€κΈ°μ κ΄λ ¨λ λͺ¨νΈμ±μ΄ μλλ‘ λ³΄μ₯ν©λλ€.
+
+```tsx
+// Dashboard.test.ts
+
+- import { getDashboardLayout } from '../lib/settings';
++ import { getDashboardLayout } from '#lib/settings';
+```
+
+μ΄κ²μ λ€μ λ²κ±°λ‘μ λ³΄μΌ μ μμ§λ§, λ°νμμ λ°λΌ λͺ¨λμ΄ λ¬λΌμ§ μ μλ€λ κ²μ νμΌμ μ½λ κ°λ°μλ€μκ² λͺ
ννκ² μ λ¬νλ μ₯μ μ΄ μμ΅λλ€. μ€μ λ‘, λͺ¨νΉμ μν΄ νλ₯ν λͺ¨λ μ΄μ λ€λ‘ μΈν΄, μΌλ°μ μΌλ‘ μ λ κ°μ Έμ€κΈ°μ λν΄ μ΄ νμ€μ κΆμ₯ν©λλ€(μλ μ°Έμ‘°).
+
+## μ€ν λ¦¬λ³ λͺ¨νΉ
+
+Subpath Importλ₯Ό μ¬μ©νμ¬, μ°λ¦¬λ `settings.ts` νμΌ μ 체λ₯Ό νμ€ κΈ°λ° μ κ·Ό λ°©μμ μ¬μ©νλ μ λͺ¨λλ‘ κ΅μ²΄ν μ μμ΅λλ€. κ·Έλ¬λ κ° ν
μ€νΈ(λλ μ°λ¦¬μ κ²½μ°, μ€ν λ¦¬λΆ μ€ν 리)λ§λ€ κ·Έ ꡬνμ λ¬λ¦¬νκ³ μΆλ€λ©΄ `settings.mock.ts`λ₯Ό μ΄λ»κ² ꡬ쑰νν΄μΌ ν κΉμ?
+
+λ€μμ μ΄λ€ λͺ¨λμ΄λ λͺ¨νΉνκΈ° μν 보μΌλ¬νλ μ΄νΈ ꡬ쑰μ
λλ€. μ½λλ₯Ό μμ ν μ μ΄ν μ μκΈ° λλ¬Έμ, νΉλ³ν μν©μ λ§κ² μμ ν μ μμ΅λλ€(μλ₯Ό λ€μ΄, λΈλΌμ°μ μμ μ€νλμ§ μλλ‘ λ
Έλ μ½λλ₯Ό μ κ±°νκ±°λ κ·Έ λ°λμ κ²½μ°).
+
+```tsx
+// lib/settings.mock.ts
+import { fn } from "@storybook/test";
+import * as actual from "./settings"; // π Import the actual implementation
+
+// π Re-export the actual implementation.
+// This catch-all ensures that the exports of the mock file always contains
+// all the exports of the original. It is up to the user to override
+// individual exports below as appropriate.
+export * from "./settings";
+
+// π Export a mock function whose default implementation is the actual implementation.
+// With a useful mockName, it displays nicely in Storybook's Actions addon
+// for debugging.
+export const getDashboardLayout = fn(actual.getDashboardLayout).mockName("settings::getDashboardLayout");
+```
+
+μ΄ λͺ¨μ νμΌμ μ΄μ #lib/settingsμ΄ κ°μ Έμμ§ λλ§λ€ μ€ν 리λΆμμ μ¬μ©λ©λλ€. μ€μ ꡬνμ κ°μΈλ κ² μΈμλ μμ§ λ§μ κ²μ νμ§ μμ§λ§ β κ·Έκ²μ΄ μ€μν λΆλΆμ
λλ€.
+
+μ΄μ μ€ν λ¦¬λΆ μ€ν 리μμ μ¬μ©ν΄ λ΄
μλ€:
+
+```tsx
+// components/Dashboard.stories.ts
+
+import type { Meta, StoryObj } from "@storybook/react";
+import { expect } from "@storybook/test";
+
+// π You can use subpaths as an absolute import convention even
+// for non-conditional paths
+import { Dashboard } from "#components/Dashboard";
+
+// π Import the mock file explicitly, as that will make
+// TypeScript understand that these exports are the mock functions
+import { getDashboardLayout } from "#lib/settings.mock";
+
+const meta = {
+ component: Dashboard,
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+export const Empty: Story = {
+ beforeEach: () => {
+ // π Mock return an empty layout
+ getDashboardLayout.mockReturnValue([]);
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ // π Expect the UI to prompt when the dashboard is empty
+ await expect(canvas).toHaveTextContent("Configure your dashboard");
+ // π Assert directly on the mock function that it was called as expected
+ expect(getDashboardLayout).toHaveBeenCalled();
+ },
+};
+
+export const Row: Story = {
+ beforeEach: () => {
+ // π Mock return a different, story-specific layout
+ getDashboardLayout.mockReturnValue([
+ /* hard-coded "row" layout data */
+ ]);
+ },
+};
+```
+
+μ€ν 리λΆμμ 'fn' λͺ¨μ ν¨μλ₯Ό μ¬μ©νλ κ²μ λ€μμ μλ―Έν©λλ€:
+
+1. μ€ν 리λΆμ μλ‘μ΄ beforeEach ν
μ μ¬μ©νμ¬ κ° μ€ν 리μ λν λμμ μμ ν μ μμ΅λλ€.
+2. ν¨μκ° νΈμΆλ λλ§λ€ Actions ν¨λμ΄ μ΄λ₯Ό κΈ°λ‘ν©λλ€.
+3. play ν¨μμμ νΈμΆμ νμΈν μ μμ΅λλ€.
+
+
+
+### λͺ
μμ
+
+λͺ¨νΉ νλ μμν¬μ μΌλΆ λ§λ²κ³Ό κ°μ κΈ°λ₯μ λͺ¨νΉμ΄ μ΄λ»κ² κ·Έλ¦¬κ³ μΈμ μ μ©λλμ§ μ΄ν΄νκΈ° μ΄λ ΅κ² λ§λλλ€. μλ₯Ό λ€μ΄, vi.mock νΈμΆ λ΄μμ μΈλΆ μ μ λ³μλ₯Ό μ°Έμ‘°νλ κ²μ΄ μ ν¨ν μλ°μ€ν¬λ¦½νΈμμλ λΆκ΅¬νκ³ λͺ¨νΉ μ€λ₯λ₯Ό μΌμΌν€λ κ²μ 보μμ΅λλ€.
+
+λ°λ©΄μ λͺ¨λ λͺ¨νΉμ package.jsonμμ λͺ
μμ μΌλ‘ μ μν¨μΌλ‘μ¨, μ°λ¦¬μ μ루μ
μ λ€μν νκ²½μμ λͺ¨λμ΄ μ΄λ»κ² ν΄κ²°λλμ§ μ΄ν΄νλ λͺ
ννκ³ μμΈ‘ κ°λ₯ν λ°©λ²μ μ 곡ν©λλ€. μ΄λ¬ν ν¬λͺ
μ±μ λλ²κΉ
μ λ¨μννκ³ ν
μ€νΈλ₯Ό λ μμΈ‘ κ°λ₯νκ² λ§λλλ€.
+
+### Type-Safe
+
+λͺ¨νΉ νλ μμν¬λ κ°λ°μκ° μ΅μν΄μ ΈμΌ ν κ΄λ‘, λ¬Έλ² μ€νμΌ, νΉμ APIλ₯Ό λμ
ν©λλ€. λν, μ΄λ¬ν μ루μ
λ€μ μ’
μ’
νμ
κ²μ¬λ₯Ό μ§μνμ§ μμ΅λλ€.
+
+κΈ°μ‘΄μ package.jsonμ μ¬μ©ν¨μΌλ‘μ¨ μ°λ¦¬μ μ루μ
μ μ΅μνμ μ€μ μ΄ νμν©λλ€. λν, TypeScriptκ° μ΄μ package.jsonμ μλΈν¨μ€ μν¬νΈλ₯Ό μλμμ±μΌλ‘ μ§μν¨μ λ°λΌ(2024λ
3μ TypeScript 5.4 λ²μ λΆν°) μμ°μ€λ½κ² TypeScriptμ ν΅ν©λ©λλ€.
+
+### νμ€ κΈ°λ°
+
+κ°μ₯ μ€μν κ²μ μ€ν 리λΆμ μ κ·Ό λ°©μμ΄ 100% νμ€ κΈ°λ°μ΄κΈ° λλ¬Έμ, μ΄λ€ λꡬ 체μΈμ΄λ νκ²½μμλ λͺ¨νΉμ μ¬μ©ν μ μλ€λ κ²μ
λλ€.
+
+μ΄λ νμ€μ λ°°μ°κ³ κ·Έ μ§μμ μ΄λμμλ μ¬μ¬μ©ν μ μκ² ν΄μ£Όλ―λ‘ μ μ©ν©λλ€. μλ₯Ό λ€μ΄, vi.mock μ¬μ©λ²μ Jestμ λͺ¨νΉκ³Ό λΉμ·νμ§λ§ λμΌνμ§λ μμ΅λλ€.
+
+λν μ¬λ¬ λꡬλ₯Ό ν¨κ» μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μ¬μ©μκ° μ»΄ν¬λνΈμ λν μ€ν 리λ₯Ό μμ±ν λ€μ, μ°λ¦¬μ Portable Stories κΈ°λ₯μ μ¬μ©νμ¬ κ·Έ μ€ν 리λ₯Ό λ€λ₯Έ ν
μ€νΈ λꡬμμ μ¬μ¬μ©νλ κ²μ΄ μΌλ°μ μ
λλ€.
+
+κ°μ₯ μ€μν κ²μ μ€ν 리λΆμ μ κ·Ό λ°©μμ΄ 100% νμ€ κΈ°λ°μ΄κΈ° λλ¬Έμ, μ΄λ€ λꡬ 체μΈμ΄λ νκ²½μμλ λͺ¨νΉμ μ¬μ©ν μ μλ€λ κ²μ
λλ€.
+
+μ΄λ νμ€μ λ°°μ°κ³ κ·Έ μ§μμ μ΄λμμλ μ¬μ¬μ©ν μ μκ² ν΄μ£Όλ―λ‘ μ μ©ν©λλ€. μλ₯Ό λ€μ΄, vi.mock μ¬μ©λ²μ Jestμ λͺ¨νΉκ³Ό λΉμ·νμ§λ§ λμΌνμ§λ μμ΅λλ€.
+
+λν μ¬λ¬ λꡬλ₯Ό ν¨κ» μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μ¬μ©μκ° μ»΄ν¬λνΈμ λν μ€ν 리λ₯Ό μμ±ν λ€μ, μ°λ¦¬μ Portable Stories κΈ°λ₯μ μ¬μ©νμ¬ κ·Έ μ€ν 리λ₯Ό λ€λ₯Έ ν
μ€νΈ λꡬμμ μ¬μ¬μ©νλ κ²μ΄ μΌλ°μ μ
λλ€.
+
+λν, μ¬λ¬ νκ²½μμ λͺ¨νΉμ μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μ€ν 리λΆμ λͺ¨νΉμ Node νμ€μ μΌλΆμ΄κΈ° λλ¬Έμ Nodeμμ "for free"λ‘ μλνμ§λ§, κ·Έ νμ€μ΄ Webpackκ³Ό Viteμ μν΄ κ΅¬νλμκΈ° λλ¬Έμ λΈλΌμ°μ μμλ μ μλνλ€κ³ κ°μ ν κ²½μ° μ¬μ©ν μ μμ΅λλ€.
+
+λ§μ§λ§μΌλ‘, μ°λ¦¬λ ESM νμ€μ λ§μΆ°μ Έ μκΈ° λλ¬Έμ, λ―Έλμ JS λ³κ²½κ³Ό νΈνλ μ μμ΅λλ€. μ°λ¦¬λ νλ«νΌμ λ² ν
νκ³ μμ΅λλ€. μ°λ¦¬λ μ΄κ²μ΄ λͺ¨λ λͺ¨νΉμ λ―Έλμ΄λ©° λͺ¨λ ν
μ€νΈ λκ΅¬κ° μ΄λ₯Ό μ±νν΄μΌ νλ€κ³ λ―Ώμ΅λλ€.
+
+### μ€λ λ°λ‘ μλν΄λ³΄μΈμ
+
+λͺ¨λ λͺ¨νΉμ μ€ν λ¦¬λΆ 8.1μμ μ¬μ©ν μ μμ΅λλ€. μ νλ‘μ νΈμμ μλν΄λ³΄μΈμ:
+
+```bash
+npx storybook@latest init
+```
+
+λλ κΈ°μ‘΄ νλ‘μ νΈλ₯Ό μ
κ·Έλ μ΄λνμΈμ:
+
+```bash
+npx storybook@latest upgrade
+```
+
+λͺ¨λ λͺ¨νΉμ λν΄ μμΈν μμλ³΄λ €λ©΄ μ€ν λ¦¬λΆ λ¬Έμλ₯Ό μ°Έμ‘°ν΄ μ£ΌμΈμ. μ¬κΈ°μλ λ λ§μ μμ μ μ 체 APIκ° ν¬ν¨λμ΄ μμ΅λλ€. μ°λ¦¬λ λͺ¨λ λͺ¨νΉ μ κ·Ό λ°©μμ μ¬μ©νμ¬ ν
μ€νΈν Next.js React μλ² μ»΄ν¬λνΈ(RSC) μ±μ μ 체 λ°λͺ¨λ₯Ό λ§λ€μμ΅λλ€. μ΄μ λν΄ λ μμΈν μ€λͺ
ν μμ μ΄λ©°, 곧 λΈλ‘κ·Έ ν¬μ€νΈλ₯Ό ν΅ν΄ λ¬Έμνν κ³νμ
λλ€.
+
+λ€μ λ¨κ³
+μ€ν 리λΆμ λͺ¨λ λͺ¨νΉμ κΈ°λ₯ μμ± λ¨κ³μ΄λ©° μ¬μ© μ€λΉκ° λμ΄ μμ΅λλ€. μ°λ¦¬λ λ€μκ³Ό κ°μ κ°μ μ¬νμ κ³ λ €νκ³ μμ΅λλ€:
+
+- μ£Όμ΄μ§ λͺ¨λμ λν΄ λͺ¨μ 보μΌλ¬νλ μ΄νΈλ₯Ό μλμΌλ‘ μμ±νλ CLI μ νΈλ¦¬ν°
+- UIμμ λͺ¨μ λ°μ΄ν°λ₯Ό μκ°ννκ³ νΈμ§ν μ μλ μ§μ
+
+λͺ¨λ λͺ¨νΉ μΈμλ, μ°λ¦¬λ μ¬λ¬ ν
μ€νΈ κ°μ μμ
μ μ§ν μ€μ
λλ€. μλ₯Ό λ€μ΄, λΈλΌμ°μ μμ React μλ² μ»΄ν¬λνΈλ₯Ό μ λ ν
μ€νΈν μ μλ μλ‘μ΄ λ°©λ²μ κ°λ°νμ΅λλ€. λν μ€ν 리λΆμ ν
μ€νΈλ₯Ό Jest/Vitestμ Jasmineμμ μκ°μ λ°μ ꡬ쑰μ ν¨μ¬ λ κ°κΉκ² κ°μ Έμ€λ μμ
μ μ§ννκ³ μμ΅λλ€.
diff --git a/June/article/article.md b/June/article/article.md
deleted file mode 100644
index 91ed32b..0000000
--- a/June/article/article.md
+++ /dev/null
@@ -1 +0,0 @@
-article
diff --git a/June/study/Type-safe_module_mocking_in_Storybook.md b/June/study/Type-safe_module_mocking_in_Storybook.md
new file mode 100644
index 0000000..5930e18
--- /dev/null
+++ b/June/study/Type-safe_module_mocking_in_Storybook.md
@@ -0,0 +1,49 @@
+# λ²μνλ©΄μ κ³΅λΆ ν κ²λ€
+
+## νμ
μ€ν¬λ¦½νΈμ satisfies ν€μλμ as ν€μλ
+
+νμ
μ€ν¬λ¦½νΈμμ asμ satisfies ν€μλλ νμ
μ λͺ
μνκ±°λ κ²μ¦νλ λ° μ¬μ©λμ§λ§, κ·Έ μ¬μ©λ²κ³Ό λͺ©μ μ μμ΄μ μ°¨μ΄κ° μμ΅λλ€.
+
+as ν€μλ
+as ν€μλλ νμ
λ¨μΈ(Type Assertion)μ μ¬μ©ν λ μ°μ΄λ©°, κ°λ°μκ° νΉμ κ°μ νμ
μ νμ
μ€ν¬λ¦½νΈλ³΄λ€ λ μ μκ³ μλ€κ³ λͺ
μνλ μ©λλ‘ μ¬μ©λ©λλ€. μ΄λ₯Ό ν΅ν΄ μ»΄νμΌλ¬μκ² ν΄λΉ κ°μ΄ μ§μ λ νμ
μ κ°κ³ μλ€κ³ μλ €μ£Όλ©°, μ€μ νμ
체ν¬λ₯Ό μ°νν©λλ€.
+
+μμ:
+
+```ts
+let someValue: any = "this is a string";
+let strLength: number = (someValue as string).length;
+```
+
+μ₯μ :
+
+- κ°μ νμ
λ³νμ ν΅ν΄ κ°λ°μκ° μλνλ νμ
μ λͺ
νν ν μ μμ΅λλ€.
+- any νμ
μμ ꡬ체μ μΈ νμ
μΌλ‘μ μ νμ κ°λ₯νκ² νμ¬, νμ
μ€ν¬λ¦½νΈμ νμ
μμ€ν
μ μ μ°νκ² μ¬μ©ν μ μμ΅λλ€.
+
+λ¨μ :
+
+- νμ
λ¨μΈμ μ€μ νμ
κ³Ό μΌμΉνμ§ μμ μνμ λ΄ν¬νκ³ μμ΄, λ°νμ μλ¬λ₯Ό λ°μμν¬ μ μμ΅λλ€.
+- μ»΄νμΌλ¬κ° νμ
μ κ°μ λ‘ μμ©νκΈ° λλ¬Έμ, νμ
μμ μ±μ μ ν΄ν μ μμ΅λλ€.
+
+### satisfies ν€μλ
+
+νμ
μ€ν¬λ¦½νΈ 4.9μμ λμ
λ satisfies ν€μλλ νΉμ κ°μ΄ μ£Όμ΄μ§ νμ
μ λ§μ‘±νλμ§ κ²μ¦νλ λ° μ¬μ©λ©λλ€. satisfiesλ κ°μ λ€λ₯Έ νμ
μΌλ‘ λ³ννμ§ μμΌλ©΄μ νμ
νΈνμ±μ νμΈν μ μκ² ν΄μ€λλ€.
+
+μμ:
+
+```ts
+let config = {
+ width: 800,
+ height: 600,
+ title: "My app",
+} satisfies { width: number; height: number };
+```
+
+μ₯μ :
+
+- μ€μ νμ
λ³ν μμ΄ νμ
νΈνμ±μ νμΈν μ μμ΄ μμ ν©λλ€.
+- κ°λ° κ³Όμ μμ νμ
μ€λ₯λ₯Ό 미리 λ°κ²¬νκ³ μμ ν μ μκ² λμμ€λλ€.
+
+λ¨μ :
+
+- μλ‘μ΄ ν€μλμ΄κΈ° λλ¬Έμ κΈ°μ‘΄ μ½λλ² μ΄μ€μμ νΈνμ± λ¬Έμ κ° λ°μν μ μμ΅λλ€.
+- νμ
μ€ν¬λ¦½νΈμ 볡μ‘μ±μ μ¦κ°μν¬ μ μμ΅λλ€.
diff --git a/June/study/study.md b/June/study/study.md
deleted file mode 100644
index 896aeb7..0000000
--- a/June/study/study.md
+++ /dev/null
@@ -1 +0,0 @@
-study