{"id":359,"date":"2026-05-17T06:46:48","date_gmt":"2026-05-16T22:46:48","guid":{"rendered":"https:\/\/junai.ai\/blog\/js-test-jest-26\/"},"modified":"2026-05-17T07:11:54","modified_gmt":"2026-05-16T23:11:54","slug":"js-test-jest-26","status":"publish","type":"post","link":"https:\/\/junai.ai\/blog\/js-test-jest-26\/","title":{"rendered":"\ud14c\uc2a4\ud2b8 \uc791\uc131 \u2014 vitest\/jest \ub85c \uccab \ub2e8\uc704 \ud14c\uc2a4\ud2b8 (\uc878\uc5c5 26\ud3b8)"},"content":{"rendered":"\n<!-- WordPress REST API \ubc1c\ud589\uc6a9 HTML (\uc790\ub3d9 \uc0dd\uc131) -->\n<!-- WP-FEATURED-MEDIA-ID: 552 -->\n<div style=\"max-width:800px;margin:0 auto;\">\n<style>\n\/* js-textbook \u2014 PostgreSQL \uad50\uc7ac \ub3d9\uc77c \ud314\ub808\ud2b8 *\/\n:root {\n  --color-primary: #336791;\n  --color-accent: #60a5fa;\n  --color-bg: #fafafa;\n  --color-bg-card: #ffffff;\n  --color-text: #1e293b;\n  --color-text-muted: #64748b;\n  --hero-start: #0f172a;\n  --hero-end: #336791;\n  --font-body: -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif;\n  --size-body: 17px;\n  --line-height: 1.75;\n}\n* { box-sizing: border-box; }\n.container { max-width: 760px; margin: 0 auto; padding: 0 22px 80px; }\n.hero { background: linear-gradient(135deg, var(--hero-start) 0%, var(--hero-end) 100%); color: #fff; padding: 72px 22px 56px; text-align: center; }\n.hero .badge { display: inline-block; background: rgba(96,165,250,0.18); color: var(--color-accent); padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 600; letter-spacing: 0.5px; margin-bottom: 18px; }\n.hero h1 { margin: 0 0 18px; font-size: 36px; line-height: 1.3; letter-spacing: -0.3px; }\n.hero p.sub { margin: 0 auto; max-width: 580px; font-size: 17px; color: #bfdbfe; }\n.hero img { width: 100%; max-width: 720px; height: auto; margin: 36px auto 0; display: block; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); }\n.meta { display: flex; gap: 14px; justify-content: center; margin-top: 20px; font-size: 13px; color: #93c5fd; flex-wrap: wrap; }\n.meta span::before { content: \"\u00b7\"; margin-right: 14px; color: #1e3a8a; }\n.meta span:first-child::before { content: \"\"; margin: 0; }\narticle { background: var(--color-bg-card); margin-top: -36px; padding: 44px 28px; border-radius: 14px; box-shadow: 0 2px 18px rgba(0,0,0,0.06); }\narticle p { margin: 0 0 18px; }\nh2 { font-size: 28px; line-height: 1.35; letter-spacing: -0.3px; margin: 48px 0 18px; padding-bottom: 10px; border-bottom: 2px solid var(--color-primary); }\nh2:first-of-type { margin-top: 8px; }\nh3 { font-size: 21px; line-height: 1.4; margin: 30px 0 12px; color: var(--color-primary); }\ncode { background: #f1f5f9; color: #0f172a; padding: 2px 6px; border-radius: 4px; font-family: 'SFMono-Regular', Menlo, Consolas, monospace; font-size: 0.92em; }\npre { background: #0f172a; color: #e2e8f0; padding: 18px 20px; border-radius: 10px; overflow-x: auto; font-size: 14.5px; line-height: 1.65; margin: 18px 0; }\npre code { background: transparent; color: inherit; padding: 0; }\nul, ol { margin: 0 0 18px; padding-left: 24px; }\nli { margin-bottom: 8px; }\nstrong { color: #0f172a; }\n.databox { background: #eff6ff; border-left: 4px solid var(--color-primary); padding: 18px 20px; border-radius: 6px; margin: 22px 0; }\n.databox p { margin: 0 0 8px; }\n.databox p:last-child { margin: 0; }\n.databox strong { color: var(--color-primary); }\n.warnbox { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border-left: 4px solid #d97706; padding: 18px 20px; border-radius: 6px; margin: 22px 0; }\n.warnbox strong { color: #92400e; }\n.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 22px 0; }\ntable { width: 100%; border-collapse: collapse; font-size: 15px; background: var(--color-bg-card); }\nth, td { padding: 11px 12px; text-align: left; border-bottom: 1px solid #e2e8f0; vertical-align: top; }\nth { background: #f1f5f9; font-weight: 700; color: #0f172a; }\ntd:first-child, th:first-child { font-weight: 700; }\n.cta { background: linear-gradient(135deg, #336791 0%, #60a5fa 100%); color: #fff; padding: 30px 24px; border-radius: 12px; margin-top: 44px; text-align: center; }\n.cta h3 { color: #fff; margin: 0 0 10px; }\n.cta p { margin: 0; color: #dbeafe; }\n.series-nav { background: #eff6ff; padding: 18px 22px; border-radius: 10px; margin-top: 24px; font-size: 14.5px; color: var(--color-text-muted); }\n.series-nav strong { color: var(--color-primary); }\n@media (max-width: 480px) {\n  .hero { padding: 52px 18px 44px; }\n  .hero h1 { font-size: 26px; }\n  .hero p.sub { font-size: 15px; }\n  article { padding: 28px 18px; border-radius: 10px; }\n  h2 { font-size: 22px; }\n  h3 { font-size: 18px; }\npre { font-size: 13px; padding: 14px 16px; }\n}\n@media (max-width: 560px) {\n  .tablewrap table, .tablewrap thead, .tablewrap tbody, .tablewrap tr, .tablewrap th, .tablewrap td { display: block; width: auto; }\n  .tablewrap thead { display: none; }\n  .tablewrap tr { margin: 0 0 14px; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; }\n  .tablewrap td { border: none; border-bottom: 1px solid #f1f5f9; padding: 9px 14px; }\n  .tablewrap td:first-child { background: #eff6ff; font-weight: 800; font-size: 15.5px; }\n  .tablewrap td:last-child { border-bottom: none; }\n  .tablewrap td[data-label]::before { content: attr(data-label) \" \u2014 \"; font-weight: 700; color: var(--color-primary); }\n}\n<\/style>\n<header class=\"hero\">\n  <span class=\"badge\">\uc790\ubc14\uc2a4\ud06c\ub9bd\ud2b8 \uad50\uc7ac \u00b7 26\ud3b8 \/ 26\ud3b8 \u2605\uc644\uacb0<\/span>\n  <h1>\ud14c\uc2a4\ud2b8 \uc791\uc131 \u2014 \uccab \ub2e8\uc704 \ud14c\uc2a4\ud2b8<\/h1>\n  <p class=\"sub\">\uc2dc\ub9ac\uc988 \ub9c8\uc9c0\ub9c9. \ucf54\ub4dc\uac00 \uc548\uc804\ud558\uac8c \uc790\ub77c\ub294 \ubc29\ubc95.<\/p>\n  <div class=\"meta\"><span>\uace0\uae09<\/span><span>\uc77d\ub294 \uc2dc\uac04 8\ubd84<\/span><span>2026-05-17<\/span><\/div>\n  <img decoding=\"async\" src=\"https:\/\/junai.ai\/blog\/wp-content\/uploads\/2026\/05\/hero-197.jpg\" alt=\"JS \ud14c\uc2a4\ud2b8\uac00 \ucf54\ub4dc\uc758 \uc548\uc804\ub9dd \uc5ed\ud560\uc744 \ud558\ub294 \uc77c\ub7ec\uc2a4\ud2b8\">\n<\/header>\n\n<div class=\"container\">\n<article>\n\n<p>26\ud3b8\uc758 \ub9c8\uc9c0\ub9c9. \ud14c\uc2a4\ud2b8\ub294 &#8220;\ucf54\ub4dc\uac00 \uc758\ub3c4\ub300\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \uc790\ub3d9\uc73c\ub85c \ud655\uc778\ud558\ub294 \ucf54\ub4dc&#8221;. \ucc98\uc74c\uc5d0\ub294 \uadc0\ucc2e\uc9c0\ub9cc \ucf54\ub4dc\uac00 \ucee4\uc9c0\ub294 \uc21c\uac04\ubd80\ud130 \ud14c\uc2a4\ud2b8 \uc5c6\ub294 \uac8c \ub354 \uadc0\ucc2e\uc544\uc9d1\ub2c8\ub2e4. 26\ud3b8\uc740 vitest\/jest \uc758 \uccab \ub2e8\uc704 \ud14c\uc2a4\ud2b8\uc640 mock\u00b7CI \uc5f0\uacb0\uae4c\uc9c0.<\/p>\n\n<h2>\uc65c \ud14c\uc2a4\ud2b8 \u2014 5\uac00\uc9c0 \uc774\uc720<\/h2>\n\n<ul>\n  <li><strong>\ud68c\uadc0 \ubc29\uc9c0<\/strong> \u2014 \ud55c \ubc88 \uc7a1\uc740 \ubc84\uadf8\uac00 \ub2e4\uc2dc \uc548 \ub098\ub294 \uc57d\uc18d.<\/li>\n  <li><strong>\ub9ac\ud329\ud130\ub9c1 \uc548\uc804\ub9dd<\/strong> \u2014 \ucf54\ub4dc \uad6c\uc870 \ubc14\uafd4\ub3c4 \ub3d9\uc791 \ub3d9\uc77c.<\/li>\n  <li><strong>\ubb38\uc11c<\/strong> \u2014 &#8220;\uc774 \ud568\uc218\uac00 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub098&#8221; \uc758 \uc2e4\ud589 \uac00\ub2a5\ud55c \uc124\uba85.<\/li>\n  <li><strong>\uc124\uacc4 \uc555\ubc15<\/strong> \u2014 \ud14c\uc2a4\ud2b8\ud558\uae30 \uc88b\uc740 \ucf54\ub4dc = \ubcf4\ud1b5 \uc88b\uc740 \uc124\uacc4.<\/li>\n  <li><strong>\ub9c8\uc74c \ud3c9\ud654<\/strong> \u2014 \ubc30\ud3ec \ud6c4 \uc7a0 \uc790\uae30.<\/li>\n<\/ul>\n\n<h2>vitest vs jest \u2014 2026 \uc120\ud0dd\uc9c0<\/h2>\n\n<div class=\"tablewrap\">\n<table>\n  <thead>\n    <tr><th><\/th><th>vitest<\/th><th>jest<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td data-label=\"\">\uc18d\ub3c4<\/td><td data-label=\"\">\ub9e4\uc6b0 \ube60\ub984 (esbuild \uae30\ubc18)<\/td><td data-label=\"\">\ubcf4\ud1b5<\/td><\/tr>\n    <tr><td data-label=\"\">ESM<\/td><td data-label=\"\">\uae30\ubcf8 \uc9c0\uc6d0<\/td><td data-label=\"\">\uc124\uc815 \ud544\uc694<\/td><\/tr>\n    <tr><td data-label=\"\">TS<\/td><td data-label=\"\">\uae30\ubcf8 \uc9c0\uc6d0<\/td><td data-label=\"\">ts-jest\u00b7@swc\/jest \ud544\uc694<\/td><\/tr>\n    <tr><td data-label=\"\">API<\/td><td data-label=\"\">jest \ud638\ud658<\/td><td data-label=\"\">\uc6d0\uc870<\/td><\/tr>\n    <tr><td data-label=\"\">\uc0dd\ud0dc\uacc4<\/td><td data-label=\"\">\uc2e0\uc0dd (\uc131\uc7a5 \uc911)<\/td><td data-label=\"\">\uad11\ubc94\uc704<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<div class=\"databox\">\n  <p><strong>2026 \ucd94\ucc9c.<\/strong> <strong>\uc0c8 \ud504\ub85c\uc81d\ud2b8<\/strong>\ub294 vitest \u2014 \ube60\ub974\uace0 ESM\/TS \uae30\ubcf8. <strong>\uae30\uc874 jest \ud504\ub85c\uc81d\ud2b8<\/strong>\ub294 \uadf8\ub300\ub85c \uc720\uc9c0. API \uac00 \uac70\uc758 \ud638\ud658\uc774\ub77c \ud559\uc2b5\uc740 \ud55c \ubc88\uc774\uba74 \ub458 \ub2e4.<\/p>\n<\/div>\n\n<h2>\uc124\uce58 \u2014 vitest<\/h2>\n\n<pre><code>npm install -D vitest\n\n# package.json\n{\n  \"scripts\": {\n    \"test\": \"vitest\",\n    \"test:run\": \"vitest run\",\n    \"test:cov\": \"vitest run --coverage\"\n  }\n}<\/code><\/pre>\n\n<h2>\uccab \ud14c\uc2a4\ud2b8 \u2014 describe \/ it \/ expect<\/h2>\n\n<pre><code>\/\/ src\/math.js\nexport function add(a, b) {\n  return a + b;\n}\n\n\/\/ src\/math.test.js\nimport { describe, it, expect } from \"vitest\";\nimport { add } from \".\/math.js\";\n\ndescribe(\"add()\", () =&gt; {\n  it(\"\ub450 \uc591\uc218\uc758 \ud569\", () =&gt; {\n    expect(add(1, 2)).toBe(3);\n  });\n\n  it(\"\uc74c\uc218 \ud3ec\ud568\", () =&gt; {\n    expect(add(-5, 3)).toBe(-2);\n  });\n\n  it(\"0 \ub354\ud558\uae30\", () =&gt; {\n    expect(add(0, 7)).toBe(7);\n  });\n});<\/code><\/pre>\n\n<pre><code>npx vitest\n\n# \ucd9c\ub825\n# \u2713 add() \ub450 \uc591\uc218\uc758 \ud569\n# \u2713 add() \uc74c\uc218 \ud3ec\ud568\n# \u2713 add() 0 \ub354\ud558\uae30\n# 3 passed<\/code><\/pre>\n\n<h2>\uc790\uc8fc \uc4f0\ub294 \ub9e4\ucc98(matcher)<\/h2>\n\n<pre><code>\/\/ \ub3d9\ub4f1\uc131\nexpect(x).toBe(y);              \/\/ === (primitive)\nexpect(obj).toEqual({ a: 1 });   \/\/ \uae4a\uc740 \ube44\uad50 (\uac1d\uccb4\u00b7\ubc30\uc5f4)\nexpect(obj).toStrictEqual({ a: 1 });   \/\/ undefined \ucc28\uc774\ub3c4 \uac80\uc0ac\n\n\/\/ \uc9c4\uc2e4\uc131\nexpect(x).toBeTruthy();\nexpect(x).toBeFalsy();\nexpect(x).toBeNull();\nexpect(x).toBeUndefined();\nexpect(x).toBeDefined();\n\n\/\/ \uc22b\uc790\nexpect(x).toBeGreaterThan(5);\nexpect(x).toBeLessThanOrEqual(10);\nexpect(0.1 + 0.2).toBeCloseTo(0.3);   \/\/ \ubd80\ub3d9\uc18c\uc218\uc810 \ube44\uad50\n\n\/\/ \ubb38\uc790\uc5f4\u00b7\ubc30\uc5f4\nexpect(\"hello\").toContain(\"ll\");\nexpect([1, 2, 3]).toContain(2);\nexpect(\"hello\").toMatch(\/^h\/);\n\n\/\/ \ud568\uc218 \ud638\ucd9c \uacb0\uacfc\nexpect(() =&gt; doStuff()).toThrow();\nexpect(() =&gt; divide(1, 0)).toThrow(\"0 \uc73c\ub85c\");\n\n\/\/ \ube44\ub3d9\uae30\nawait expect(fetchUser(1)).resolves.toMatchObject({ id: 1 });\nawait expect(fetchUser(999)).rejects.toThrow(\"not found\");<\/code><\/pre>\n\n<h2>setup\/teardown \u2014 \uac01 \ud14c\uc2a4\ud2b8 \uc804\ud6c4<\/h2>\n\n<pre><code>import { beforeEach, afterEach } from \"vitest\";\n\nlet db;\n\nbeforeEach(() =&gt; {\n  db = new Database();\n  db.seed();\n});\n\nafterEach(() =&gt; {\n  db.close();\n});\n\nit(\"...\", () =&gt; { ... });<\/code><\/pre>\n\n<h2>mock \u2014 \uc678\ubd80 \uc758\uc874 \uaca9\ub9ac<\/h2>\n\n<pre><code>import { vi, it, expect } from \"vitest\";\n\n\/\/ \ud568\uc218 mock\nconst fn = vi.fn();\nfn(\"a\", 1);\nexpect(fn).toHaveBeenCalledWith(\"a\", 1);\nexpect(fn).toHaveBeenCalledTimes(1);\n\n\/\/ \ubc18\ud658\uac12 \uc124\uc815\nfn.mockReturnValue(42);\nfn();   \/\/ 42\n\n\/\/ \ubaa8\ub4c8 mock\nvi.mock(\".\/api.js\", () =&gt; ({\n  fetchUser: vi.fn(async (id) =&gt; ({ id, name: \"\ud14c\uc2a4\ud2b8\" })),\n}));\n\n\/\/ \ubd80\ubd84 mock (\uc2e4\uc81c \ud568\uc218 \uc77c\ubd80\ub9cc)\nvi.mock(\".\/api.js\", async () =&gt; {\n  const actual = await vi.importActual(\".\/api.js\");\n  return {\n    ...actual,\n    fetchUser: vi.fn(),\n  };\n});<\/code><\/pre>\n\n<h2>\ube44\ub3d9\uae30 \ud14c\uc2a4\ud2b8<\/h2>\n\n<pre><code>it(\"loadUser \u2014 \uc131\uacf5\", async () =&gt; {\n  const user = await loadUser(1);\n  expect(user.name).toBe(\"\uc900\uc131\");\n});\n\nit(\"loadUser \u2014 404\", async () =&gt; {\n  await expect(loadUser(999)).rejects.toThrow(\"not found\");\n});\n\n\/\/ \ud0c0\uc774\uba38 mock\nimport { vi } from \"vitest\";\n\nit(\"debounce\", () =&gt; {\n  vi.useFakeTimers();\n  const fn = vi.fn();\n  const debounced = debounce(fn, 500);\n\n  debounced();\n  debounced();\n  vi.advanceTimersByTime(600);\n\n  expect(fn).toHaveBeenCalledTimes(1);\n});<\/code><\/pre>\n\n<h2>\ucee4\ubc84\ub9ac\uc9c0<\/h2>\n\n<pre><code>npm install -D @vitest\/coverage-v8\n\n# vitest.config.js\nexport default {\n  test: {\n    coverage: {\n      reporter: [\"text\", \"html\"],\n      exclude: [\"node_modules\", \"dist\", \"**\/*.test.js\"],\n      thresholds: { lines: 80, functions: 80 },\n    }\n  }\n};\n\nnpx vitest run --coverage\n# \uacb0\uacfc \u2014 \ub77c\uc778\u00b7\ube0c\ub79c\uce58\u00b7\ud568\uc218\ubcc4 % \uc640 \ubbf8\ucee4\ubc84 \uc601\uc5ed<\/code><\/pre>\n\n<div class=\"warnbox\">\n  <p><strong>\ucee4\ubc84\ub9ac\uc9c0 \ud568\uc815.<\/strong> 100% \uac00 \ud56d\uc0c1 \uc88b\uc740 \uac74 \uc544\ub2d8. \ubb34\uc758\ubbf8\ud55c \ud14c\uc2a4\ud2b8\ub85c \ub77c\uc778\ub9cc \ucc44\uc6b8 \uc218 \uc788\uc74c. <strong>\ucee4\ubc84\ub9ac\uc9c0\ubcf4\ub2e4 \uc2dc\ub098\ub9ac\uc624 \ub2e4\uc591\uc131<\/strong>(\uc5e3\uc9c0 \ucf00\uc774\uc2a4\u00b7\uc2e4\ud328 \uacbd\ub85c) \uc774 \uc911\uc694. 70-80% \uac00 \ubcf4\ud1b5 \uc88b\uc740 \uade0\ud615.<\/p>\n<\/div>\n\n<h2>CI \uc5f0\uacb0 \u2014 GitHub Actions<\/h2>\n\n<pre><code># .github\/workflows\/test.yml\nname: Test\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - uses: actions\/setup-node@v4\n        with:\n          node-version: 22\n          cache: npm\n      - run: npm ci\n      - run: npm run test:run\n      - run: npm run typecheck   # TS \ub77c\uba74 (19\ud3b8)<\/code><\/pre>\n\n<h2>\ud14c\uc2a4\ud2b8 \uc885\ub958 \u2014 \ud55c \ud45c<\/h2>\n\n<div class=\"tablewrap\">\n<table>\n  <thead>\n    <tr><th>\ub808\ubca8<\/th><th>\ub300\uc0c1<\/th><th>\ube44\uc728<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td data-label=\"\">unit<\/td><td data-label=\"\">\ud568\uc218\u00b7\ud074\ub798\uc2a4 \ud558\ub098<\/td><td data-label=\"\">\uac00\uc7a5 \ub9ce\uc774 (70%)<\/td><\/tr>\n    <tr><td data-label=\"\">integration<\/td><td data-label=\"\">\uc5ec\ub7ec \ubaa8\ub4c8 \uac19\uc774 (DB\u00b7API)<\/td><td data-label=\"\">\uc911\uac04 (20%)<\/td><\/tr>\n    <tr><td data-label=\"\">e2e<\/td><td data-label=\"\">\uc2e4\uc81c \ube0c\ub77c\uc6b0\uc800\u00b7\uc804\uccb4 \ud750\ub984<\/td><td data-label=\"\">\uc801\uc74c (10%)<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<p>&#8220;\ud14c\uc2a4\ud2b8 \ud53c\ub77c\ubbf8\ub4dc&#8221; \u2014 \ube60\ub978 unit \uc774 \ub9ce\uace0, \ub290\ub9ac\uace0 \ube44\uc2fc e2e \ub294 \uc801\uac8c.<\/p>\n\n<h2>\ud83d\udcda 26\ud3b8 \uc2dc\ub9ac\uc988 \ud68c\uace0 \u2014 \ubb34\uc5c7\uc744 \uc775\ud614\ub098<\/h2>\n\n<div class=\"tablewrap\">\n<table>\n  <thead>\n    <tr><th>\ud30c\ud2b8<\/th><th>\ud575\uc2ec<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td data-label=\"\">Part 1 \uc785\ubb38(1-5)<\/td><td data-label=\"\">\uc124\uce58\u00b7console.log\u00b7\ubcc0\uc218\u00b7\uc790\ub8cc\ud615\u00b7\uc5f0\uc0b0\uc790<\/td><\/tr>\n    <tr><td data-label=\"\">Part 2 \uae30\ucd08(6-13)<\/td><td data-label=\"\">\uc870\uac74\u00b7\ubc18\ubcf5\u00b7\ud568\uc218\u00b7\ubc30\uc5f4\u00b7\uac1d\uccb4\u00b7\ubb38\uc790\uc5f4\u00b7\uc2a4\ucf54\ud504\u00b7\uc5d0\ub7ec<\/td><\/tr>\n    <tr><td data-label=\"\">Part 3 \uc911\uae09(14-20)<\/td><td data-label=\"\">DOM\u00b7\ube44\ub3d9\uae30\u00b7Promise\u00b7fetch\u00b7class\u00b7\ubaa8\ub4c8\u00b7\uc815\uaddc\uc2dd<\/td><\/tr>\n    <tr><td data-label=\"\">Part 4 \uace0\uae09(21-26)<\/td><td data-label=\"\">\ud074\ub85c\uc800\u00b7this\u00b7\ud504\ub85c\ud1a0\ud0c0\uc785\u00b7\uc774\ud130\ub808\uc774\ud130\u00b7\ud328\ud134\u00b7\ud14c\uc2a4\ud2b8<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<div class=\"databox\">\n  <p><strong>\ub2e4\uc74c\uc740 \uc5b4\ub514\ub85c?<\/strong> \uc2e4\uc81c \ud504\ub85c\uc81d\ud2b8. \u2460 \uc791\uc740 \ud1a0\uc774 \u2014 TODO\u00b7\uba54\ubaa8\u00b7\ube14\ub85c\uadf8 \ub9cc\ub4e4\uba74\uc11c 1~13\ud3b8 \ubcf5\uc2b5. \u2461 \ud504\ub808\uc784\uc6cc\ud06c \u2014 React\/Svelte\/Vue \ub85c 14~20\ud3b8 \uc2e4\uc804. \u2462 \ubcf8\uaca9 \u2014 TS(20\ud3b8 \uc2dc\ub9ac\uc988 \ubcc4\ub3c4)\u00b7\ud14c\uc2a4\ud2b8\u00b7CI \uae4c\uc9c0 \uc644\ube44\ub41c \ucf54\ub4dc\ubca0\uc774\uc2a4. JS \ub294 \uac70\uae30\uc11c \ub05d\uc5c6\uc774 \uae4a\uc5b4\uc9d1\ub2c8\ub2e4 \u2014 \ud3c9\uc0dd \ud559\uc2b5.<\/p>\n<\/div>\n\n<h2>\ub9c8\uc9c0\ub9c9 \ud55c \uc904<\/h2>\n\n<p><strong>&#8220;\ucf54\ub4dc\ub294 \uae00\uc774\ub2e4.&#8221;<\/strong> \ucc98\uc74c \uc775\ud790 \ub54c\ub294 \ubb38\ubc95\uc774 \uc804\ubd80\uc9c0\ub9cc, \uc9c4\uc9dc \uc2e4\ub825\uc740 &#8220;\ub2e4\uc74c \uc0ac\ub78c\uc774 6\uac1c\uc6d4 \ub4a4 \uc77d\uae30 \uc88b\uac8c \uc4f0\ub294 \uac83&#8221;. 26\ud3b8\uc758 \ubaa8\ub4e0 \ub3c4\uad6c\ub294 \uacb0\uad6d \uadf8 \ubaa9\uc801\uc744 \uc704\ud55c \uc218\ub2e8\uc785\ub2c8\ub2e4.<\/p>\n\n<div class=\"cta\">\n  <h3>\ud83c\udf93 \uc790\ubc14\uc2a4\ud06c\ub9bd\ud2b8 26\ud3b8 \uc2dc\ub9ac\uc988 \uc644\uacb0<\/h3>\n  <p>console.log \ud55c \uc904\ub85c \uc2dc\uc791\ud574 \ud14c\uc2a4\ud2b8\u00b7CI \uae4c\uc9c0. \ub2e4\uc74c\uc740 \uc9c4\uc9dc \ud504\ub85c\uc81d\ud2b8\ub85c \uc190\uc5d0 \uc775\ud788\uae30. 6\uac1c\uc6d4 \ub4a4 1\ud3b8\uc744 \ub2e4\uc2dc \ubcf4\uba74 \u2014 \uadf8\ub54c \uc774\ud574 \ubabb\ud55c \ubaa8\ub4e0 \uac8c \uba85\ud655\ud560 \uac70\uc608\uc694.<\/p>\n<\/div>\n\n<div class=\"series-nav\">\n  <strong>\ud83d\udcda \uc27d\uac8c \ubc30\uc6b0\ub294 \uc790\ubc14\uc2a4\ud06c\ub9bd\ud2b8 \u2014 \uc644\uacb0<\/strong><br>\n  \uc774\uc804: 25\ud3b8 \ub514\uc790\uc778 \ud328\ud134 \u00b7 \ud604\uc7ac: <strong>26\ud3b8 (\uc878\uc5c5\uc791 \u2605)<\/strong> \u00b7 \uc9c4\ud589: <strong>26\/26 \u2705<\/strong>\n<\/div>\n\n<\/article>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>vitest\u00b7jest\u00b7describe\/it\/expect\u00b7mock\u00b7CI. \uc2dc\ub9ac\uc988 \ub9c8\uc9c0\ub9c9 26\ud3b8.<\/p>\n","protected":false},"author":1,"featured_media":552,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[],"class_list":["post-359","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-javascript-basic"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/359","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/comments?post=359"}],"version-history":[{"count":2,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/359\/revisions"}],"predecessor-version":[{"id":578,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/359\/revisions\/578"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media\/552"}],"wp:attachment":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media?parent=359"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/categories?post=359"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/tags?post=359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}