๐Ÿš€ Gatsby CI/CD ํ™˜๊ฒฝ์—์„œ Playwright ์˜ค๋ฅ˜ ํ•ด๊ฒฐ: browserType.launch, Chromium ๋ฒ„์ „ ์ถฉ๋Œ ๋ฌธ์ œ ํ•ด๊ฒฐ

@leekh8 ยท September 13, 2024 ยท 17 min read

Code N Solve ๐Ÿ“˜: Playwright์™€ Gatsby CI/CD ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•œ browserType.launch ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ์ •๋ฆฌ

์ด์ „์— ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋˜ Playwright1 ๊ด€๋ จ ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ–ˆ๋‹ค.

Gatsby build ๊ณผ์ •์—์„œ ์ง€์†์ ์œผ๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.


๋ฌธ์ œ: Playwright Chromium ๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „ ์ฐธ์กฐ ์˜ค๋ฅ˜2

  • Gatsby ๋ธ”๋กœ๊ทธ์˜ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ GitHub Actions์—์„œ Playwright ์„ค์น˜ ์‹œ, ์˜ค๋ž˜๋œ Chromium ๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „(์˜ˆ: chromium-1129)์„ ๊ณ„์† ์ฐธ์กฐํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
  • ์บ์‹œ๋ฅผ ๋น„์›Œ๋„ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๊ฑฐ๋‚˜, ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „(chromium-1134)์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.
  • browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium-1129/chrome-linux/chrome

๋ฌธ์ œ ๋ถ„์„

  • ์ด์ „์— ์บ์‹œ๋œ Playwright ๋ธŒ๋ผ์šฐ์ € ๊ฒฝ๋กœ๊ฐ€ GitHub Actions์—์„œ ์ฐธ์กฐ๋˜๋ฉด์„œ, ์ตœ์‹  ๋ฒ„์ „ ์„ค์น˜ ํ›„์—๋„ ์ž˜๋ชป๋œ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•จ.
  • Playwright ๊ฒฝ๋กœ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์ •ํ•˜์ง€ ์•Š์•„ Gatsby ๋นŒ๋“œ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•จ.

Playwright ์•„ํ‚คํ…์ฒ˜ ์ดํ•ด

๋ฌธ์ œ๋ฅผ ์ œ๋Œ€๋กœ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด, ๋จผ์ € Playwright๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

๋ธŒ๋ผ์šฐ์ € ์—”์ง„ ์ข…๋ฅ˜

Playwright๋Š” ์„ธ ๊ฐ€์ง€ ๋ธŒ๋ผ์šฐ์ € ์—”์ง„์„ ์ง€์›ํ•œ๋‹ค.

์—”์ง„ ๊ธฐ๋ฐ˜ ํŠน์ง•
Chromium Google Chromium Chrome, Edge์™€ ๋™์ผ ์—”์ง„. ๊ฐ€์žฅ ๋„๋ฆฌ ์“ฐ์ž„
Firefox Mozilla Gecko Firefox ์‹ค์ œ ์—”์ง„. ์›น ํ‘œ์ค€ ์ค€์ˆ˜ ํ…Œ์ŠคํŠธ์— ์ ํ•ฉ
WebKit Apple WebKit Safari์™€ ๋™์ผ ์—”์ง„. iOS/macOS ๋™์ž‘ ํ™•์ธ์— ํ•„์ˆ˜

๊ฐ ๋ธŒ๋ผ์šฐ์ € ์—”์ง„์€ Playwright ํŒจํ‚ค์ง€์™€ ๋ณ„๋„๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•ํƒœ๋กœ ์„ค์น˜๋œ๋‹ค. ์ฆ‰, npm install playwright๋ฅผ ํ•ด๋„ ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ ์ž์ฒด๋Š” ๋”ฐ๋กœ ๋‚ด๋ ค๋ฐ›์•„์•ผ ํ•œ๋‹ค. ์ด ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ~/.cache/ms-playwright/ ์•„๋ž˜์— ๋ฒ„์ „๋ณ„๋กœ ์ €์žฅ๋œ๋‹ค.

~/.cache/ms-playwright/
โ”œโ”€โ”€ chromium-1134/
โ”‚   โ””โ”€โ”€ chrome-linux/
โ”‚       โ””โ”€โ”€ chrome          โ† ์‹คํ–‰ ํŒŒ์ผ
โ”œโ”€โ”€ firefox-1458/
โ”‚   โ””โ”€โ”€ firefox/
โ”‚       โ””โ”€โ”€ firefox
โ””โ”€โ”€ webkit-2067/
    โ””โ”€โ”€ ...

Playwright๋Š” ์„ค์น˜ ์‹œ์ ์˜ ๋ฒ„์ „ ๋ฒˆํ˜ธ์™€ ์‹ค์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ๋งคํ•‘ํ•ด ๊ด€๋ฆฌํ•œ๋‹ค. ์ด ๋งคํ•‘์ด ์–ด๊ธ‹๋‚˜๋ฉด Executable doesn't exist ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์™œ CI ํ™˜๊ฒฝ์—์„œ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€

๋กœ์ปฌ์—์„œ ๋ฉ€์ฉกํžˆ ๋Œ์•„๊ฐ€๋˜ Playwright๊ฐ€ CI์—์„œ ํ„ฐ์ง€๋Š” ์ด์œ ๋Š” ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋‹ค.

1. ์ƒŒ๋“œ๋ฐ•์Šค(Sandbox) ๋ฌธ์ œ

Chromium์€ ๊ธฐ๋ณธ์ ์œผ๋กœ OS ์ˆ˜์ค€์˜ ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค. ๋กœ์ปฌ ๋จธ์‹ ์—์„œ๋Š” ์ด ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜์ง€๋งŒ, GitHub Actions์˜ Ubuntu ๋Ÿฌ๋„ˆ์ฒ˜๋Ÿผ ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ƒŒ๋“œ๋ฐ•์Šค์— ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์—†์–ด ์‹คํ–‰ ์ž์ฒด๊ฐ€ ์‹คํŒจํ•œ๋‹ค.

# ์ด๋Ÿฐ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์˜ค๋ฉด ์ƒŒ๋“œ๋ฐ•์Šค ๋ฌธ์ œ
[0913/120000.123:FATAL:zygote_host_impl_linux.cc(128)] No usable sandbox!

์ด ๊ฒฝ์šฐ --no-sandbox ์˜ต์…˜์„ ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค.

// playwright.config.ts
use: {
  launchOptions: {
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  },
},

๋‹จ, --no-sandbox๋Š” ๋ณด์•ˆ์„ ์•ฝํ™”์‹œํ‚ค๋Š” ์˜ต์…˜์ด๋ฏ€๋กœ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. CI ํ™˜๊ฒฝ์—์„œ๋งŒ ์กฐ๊ฑด๋ถ€๋กœ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜, ๊ณต์‹ Playwright Docker ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ํ—ค๋“œ๋ฆฌ์Šค(Headless) ๋ชจ๋“œ

CI ํ™˜๊ฒฝ์—๋Š” ๋””์Šคํ”Œ๋ ˆ์ด ์„œ๋ฒ„(X11)๊ฐ€ ์—†๋‹ค. Playwright๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ headless: true๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋ถ€๋ถ„ ๋ฌธ์ œ์—†์ง€๋งŒ, ์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ์„ค์ •์ด GUI๋ฅผ ์š”๊ตฌํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค.

// ๋ช…์‹œ์ ์œผ๋กœ headless ์„ค์ •
use: {
  headless: true,
},

3. ์‹œ์Šคํ…œ ์˜์กด์„ฑ ๋ˆ„๋ฝ

Chromium์€ ๋‹จ๋… ์‹คํ–‰ ํŒŒ์ผ์ด ์•„๋‹ˆ๋‹ค. ๋ฆฌ๋ˆ…์Šค์—์„œ ๋™์ž‘ํ•˜๋ ค๋ฉด ์ˆ˜์‹ญ ๊ฐœ์˜ ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค (libglib, libnss, libfontconfig ๋“ฑ). GitHub Actions์˜ ๊ธฐ๋ณธ Ubuntu ์ด๋ฏธ์ง€์—๋Š” ์ด๋Ÿฐ ์˜์กด์„ฑ์ด ๋ชจ๋‘ ๊ฐ–์ถฐ์ ธ ์žˆ์ง€ ์•Š๋‹ค.

npx playwright install --with-deps ๋ช…๋ น์˜ --with-deps ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋ฐ”๋กœ ์ด ์˜์กด์„ฑ์„ OS ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋กœ ์ž๋™ ์„ค์น˜ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.


CI ํ™˜๊ฒฝ์—์„œ ์ž์ฃผ ๋งŒ๋‚˜๋Š” Playwright ์˜ค๋ฅ˜๋“ค

์˜ค๋ฅ˜ 1: Executable doesn't exist

browserType.launch: Executable doesn't exist at
/home/runner/.cache/ms-playwright/chromium-1129/chrome-linux/chrome

์›์ธ: Playwright ํŒจํ‚ค์ง€ ๋ฒ„์ „๊ณผ ์„ค์น˜๋œ ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฒ„์ „์ด ๋ถˆ์ผ์น˜ํ•œ๋‹ค. ์ฃผ๋กœ ๋‹ค์Œ ์ƒํ™ฉ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค.

  • ์ด์ „ ์บ์‹œ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋‚จ์•„์žˆ๋Š”๋ฐ ํŒจํ‚ค์ง€๋Š” ์—…๋ฐ์ดํŠธ๋œ ๊ฒฝ์šฐ
  • playwright install์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  playwright ํŒจํ‚ค์ง€๋งŒ ์„ค์น˜ํ•œ ๊ฒฝ์šฐ
  • ์บ์‹œ ํ‚ค๊ฐ€ ์ž˜๋ชป ์„ค์ •๋˜์–ด ๋‚ก์€ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

ํ•ด๊ฒฐ:

- name: Install Playwright Browsers
  run: npx playwright install --with-deps chromium

๋˜๋Š” ์บ์‹œ๊ฐ€ ๋ฌธ์ œ๋ผ๋ฉด ๊ฐ•์ œ ์‚ญ์ œ ํ›„ ์žฌ์„ค์น˜:

- name: Clear Playwright Cache and Reinstall
  run: |
    rm -rf ~/.cache/ms-playwright
    npx playwright install --with-deps chromium

์˜ค๋ฅ˜ 2: Host system is missing dependencies

browserType.launch: Host system is missing dependencies!

  Missing libraries:
    libgbm.so.1
    libglib-2.0.so.0
    libnss3.so
    ...

์›์ธ: Chromium ์‹คํ–‰์— ํ•„์š”ํ•œ ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†๋‹ค. --with-deps ์—†์ด playwright install๋งŒ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, ์ตœ์†Œ Docker ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

ํ•ด๊ฒฐ:

# --with-deps ํ”Œ๋ž˜๊ทธ๋กœ ์˜์กด์„ฑ ์ž๋™ ์„ค์น˜
- name: Install Playwright with Dependencies
  run: npx playwright install --with-deps

๋˜๋Š” ์ˆ˜๋™์œผ๋กœ ์˜์กด์„ฑ ์„ค์น˜:

# Ubuntu/Debian ๊ธฐ์ค€
sudo apt-get install -y \
  libgbm-dev \
  libnss3 \
  libatk-bridge2.0-0 \
  libdrm2 \
  libxkbcommon0 \
  libxcomposite1 \
  libxdamage1 \
  libxfixes3 \
  libxrandr2 \
  libglib2.0-0

์˜ค๋ฅ˜ 3: Error: spawn ENOENT

Error: spawn /home/runner/.cache/ms-playwright/chromium-1134/chrome-linux/chrome ENOENT

์›์ธ: ์‹คํ–‰ ํŒŒ์ผ ์ž์ฒด๊ฐ€ ์—†๊ฑฐ๋‚˜ ์‹คํ–‰ ๊ถŒํ•œ์ด ์—†๋‹ค. ENOENT๋Š” "Error NO ENTry"์˜ ์•ฝ์ž๋กœ ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

ํ•ด๊ฒฐ:

# ์„ค์น˜ ํ›„ ํŒŒ์ผ ์กด์žฌ ํ™•์ธ
ls -la ~/.cache/ms-playwright/chromium-*/chrome-linux/chrome

# ์‹คํ–‰ ๊ถŒํ•œ ํ™•์ธ ๋ฐ ๋ถ€์—ฌ
chmod +x ~/.cache/ms-playwright/chromium-*/chrome-linux/chrome

์˜ค๋ฅ˜ 4: Timeout 30000ms exceeded

browserType.launch: Timeout 30000ms exceeded.

์›์ธ: CI ํ™˜๊ฒฝ์˜ ํ•˜๋“œ์›จ์–ด ์„ฑ๋Šฅ์ด ๋‚ฎ์•„ ๋ธŒ๋ผ์šฐ์ € ์‹œ์ž‘์— 30์ดˆ ์ด์ƒ ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ ์ง€์—ฐ์œผ๋กœ ๋ฆฌ์†Œ์Šค ๋กœ๋“œ๊ฐ€ ๋А๋ฆฐ ๊ฒฝ์šฐ๋‹ค.

ํ•ด๊ฒฐ:

// playwright.config.ts
export default defineConfig({
  timeout: 60000,          // ํ…Œ์ŠคํŠธ ์ „์ฒด ํƒ€์ž„์•„์›ƒ (60์ดˆ)
  expect: {
    timeout: 10000,        // expect() ํƒ€์ž„์•„์›ƒ (10์ดˆ)
  },
  use: {
    actionTimeout: 15000,  // ๊ฐ ์•ก์…˜ ํƒ€์ž„์•„์›ƒ (15์ดˆ)
    navigationTimeout: 30000, // ํŽ˜์ด์ง€ ์ด๋™ ํƒ€์ž„์•„์›ƒ (30์ดˆ)
  },
});

GitHub Actions ๋ ˆ๋ฒจ์—์„œ๋„ ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•ด๋‘๋ฉด ๋ฌดํ•œ ๋Œ€๊ธฐ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค:

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest

Playwright + Docker์—์„œ์˜ ์„ค์ •

๋กœ์ปฌ ๊ฐœ๋ฐœ๊ณผ CI ํ™˜๊ฒฝ์˜ ์ฐจ์ด๋ฅผ ์™„์ „ํžˆ ์—†์• ๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์€ Docker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ณต์‹ Playwright Docker ์ด๋ฏธ์ง€ ์‚ฌ์šฉ

Playwright ํŒ€์€ ๋ชจ๋“  ์˜์กด์„ฑ์ด ์‚ฌ์ „ ์„ค์น˜๋œ ๊ณต์‹ Docker ์ด๋ฏธ์ง€๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

# Playwright ๊ณต์‹ ์ด๋ฏธ์ง€ โ€” ๋ฒ„์ „์€ playwright ํŒจํ‚ค์ง€ ๋ฒ„์ „๊ณผ ๋งž์ถฐ์•ผ ํ•œ๋‹ค
FROM mcr.microsoft.com/playwright:v1.47.0-jammy

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

# ํ…Œ์ŠคํŠธ ์‹คํ–‰
CMD ["npx", "playwright", "test"]

GitHub Actions์—์„œ ์ด ์ด๋ฏธ์ง€๋ฅผ ์ง์ ‘ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค:

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.47.0-jammy
    steps:
      - uses: actions/checkout@v4
      - name: Install dependencies
        run: npm ci
      - name: Run Playwright tests
        run: npx playwright test

๊ณต์‹ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด --with-deps๋‚˜ --no-sandbox ๊ฐ™์€ ์ถ”๊ฐ€ ์„ค์ • ์—†์ด๋„ ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

์ปค์Šคํ…€ Dockerfile์—์„œ ์˜์กด์„ฑ ์„ค์น˜

๊ธฐ์กด Node.js ์ด๋ฏธ์ง€ ๊ธฐ๋ฐ˜์—์„œ Playwright๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์˜์กด์„ฑ์„ ์ง์ ‘ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

FROM node:20-bookworm-slim

# Playwright ์˜์กด์„ฑ ์„ค์น˜
RUN apt-get update && apt-get install -y \
    libnss3 \
    libnspr4 \
    libdbus-1-3 \
    libatk1.0-0 \
    libatk-bridge2.0-0 \
    libcups2 \
    libdrm2 \
    libxkbcommon0 \
    libxcomposite1 \
    libxdamage1 \
    libxfixes3 \
    libxrandr2 \
    libgbm1 \
    libasound2 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY package*.json ./
RUN npm ci

# ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ ์„ค์น˜
RUN npx playwright install chromium

COPY . .

GitHub Actions ์บ์‹ฑ ์ตœ์ ํ™”

CI๋ฅผ ๋งค๋ฒˆ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค node_modules์™€ ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ƒˆ๋กœ ๋‚ด๋ ค๋ฐ›์œผ๋ฉด ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค. ์บ์‹ฑ์„ ์ž˜ ์„ค์ •ํ•˜๋ฉด ์‹คํ–‰ ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

node_modules ์บ์‹ฑ

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

- name: Install dependencies
  run: npm ci

hashFiles('**/package-lock.json')์„ ์บ์‹œ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋ฉด package-lock.json์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์บ์‹œ๊ฐ€ ๋ฌดํšจํ™”๋œ๋‹ค. yarn์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด yarn.lock์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ๋‹ค.

# yarn ์‚ฌ์šฉ ์‹œ
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}

Playwright ๋ธŒ๋ผ์šฐ์ € ์บ์‹ฑ (๊ณต์‹ ๊ถŒ์žฅ ๋ฐฉ๋ฒ•)

Playwright ํŒ€์ด ๊ณต์‹์ ์œผ๋กœ ๊ถŒ์žฅํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ ์บ์‹ฑ ๋ฐฉ๋ฒ•์ด๋‹ค.6

- name: Cache Playwright Browsers
  uses: actions/cache@v4
  id: playwright-cache
  with:
    path: ~/.cache/ms-playwright
    key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}

- name: Install Playwright Browsers
  if: steps.playwright-cache.outputs.cache-hit != 'true'
  run: npx playwright install --with-deps

- name: Install Playwright System Dependencies
  if: steps.playwright-cache.outputs.cache-hit == 'true'
  run: npx playwright install-deps

์บ์‹œ ํžˆํŠธ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.

  • ์บ์‹œ ๋ฏธ์Šค: --with-deps๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ + ์‹œ์Šคํ…œ ์˜์กด์„ฑ ๋ชจ๋‘ ์„ค์น˜
  • ์บ์‹œ ํžˆํŠธ: ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ์บ์‹œ์—์„œ ๋ณต์›๋˜๋ฏ€๋กœ ์‹œ์Šคํ…œ ์˜์กด์„ฑ(install-deps)๋งŒ ์„ค์น˜

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์บ์‹œ ํžˆํŠธ ์‹œ ์ˆ˜์‹ญ~์ˆ˜๋ฐฑ MB์˜ ๋‹ค์šด๋กœ๋“œ๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋‹ค.

์บ์‹œ ํ‚ค ์ „๋žต ์š”์•ฝ

์บ์‹œ ๋Œ€์ƒ ๊ถŒ์žฅ ํ‚ค ํŒจํ„ด ๋ฌดํšจํ™” ์‹œ์ 
node_modules OS-node-hash(package-lock.json) ํŒจํ‚ค์ง€ ์˜์กด์„ฑ ๋ณ€๊ฒฝ ์‹œ
Playwright ๋ฐ”์ด๋„ˆ๋ฆฌ OS-playwright-hash(package-lock.json) playwright ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๋ณ€๊ฒฝ ์‹œ
Gatsby .cache OS-gatsby-hash(gatsby-config.js) ์„ค์ • ๋ณ€๊ฒฝ ์‹œ

E2E ํ…Œ์ŠคํŠธ ์„ค์ •

playwright.config.ts ๊ธฐ๋ณธ ์„ค์ •

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  // ํ…Œ์ŠคํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ
  testDir: './e2e',

  // ์ „์ฒด ํ…Œ์ŠคํŠธ ํƒ€์ž„์•„์›ƒ (ms)
  timeout: 30000,

  // expect() ํƒ€์ž„์•„์›ƒ
  expect: {
    timeout: 5000,
  },

  // ํ…Œ์ŠคํŠธ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ํšŸ์ˆ˜ (CI์—์„œ๋Š” 2ํšŒ, ๋กœ์ปฌ์—์„œ๋Š” 0ํšŒ)
  retries: process.env.CI ? 2 : 0,

  // ๋ณ‘๋ ฌ ์‹คํ–‰ ์›Œ์ปค ์ˆ˜
  // CI ํ™˜๊ฒฝ์€ ๋ฆฌ์†Œ์Šค ์ œํ•œ์ด ์žˆ์œผ๋ฏ€๋กœ ์ œํ•œ
  workers: process.env.CI ? 1 : undefined,

  // ๋ฆฌํฌํŠธ ์„ค์ •
  reporter: [
    ['html', { outputFolder: 'playwright-report', open: 'never' }],
    ['list'],
  ],

  // ๋ชจ๋“  ํ…Œ์ŠคํŠธ์— ๊ณตํ†ต ์ ์šฉ๋˜๋Š” ์„ค์ •
  use: {
    // ํ…Œ์ŠคํŠธ ๋Œ€์ƒ URL
    baseURL: 'http://localhost:8000',

    // ์‹คํŒจ ์‹œ ํŠธ๋ ˆ์ด์Šค ์ˆ˜์ง‘
    trace: 'on-first-retry',

    // ์‹คํŒจ ์‹œ ์Šคํฌ๋ฆฐ์ƒท
    screenshot: 'only-on-failure',

    // ์‹คํŒจ ์‹œ ๋น„๋””์˜ค ๋…นํ™”
    video: 'on-first-retry',

    // headless ๋ชจ๋“œ (CI์—์„œ๋Š” ํ•ญ์ƒ true)
    headless: true,
  },

  // ํ…Œ์ŠคํŠธํ•  ๋ธŒ๋ผ์šฐ์ €/๋””๋ฐ”์ด์Šค ๋ชฉ๋ก
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
  ],

  // ํ…Œ์ŠคํŠธ ์ „ ๋กœ์ปฌ ์„œ๋ฒ„ ์ž๋™ ์‹œ์ž‘
  webServer: {
    command: 'yarn serve',
    url: 'http://localhost:9000',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },
});

ํ…Œ์ŠคํŠธ ๋ณ‘๋ ฌ ์‹คํ–‰

Playwright๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์›Œ์ปค๋ฅผ ๋„์›Œ ํ…Œ์ŠคํŠธ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•œ๋‹ค. ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” CPU ์ฝ”์–ด ์ˆ˜์— ๋งž๊ฒŒ ์ž๋™์œผ๋กœ ์„ค์ •๋˜์ง€๋งŒ, CI์—์„œ๋Š” ์กฐ์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

// playwright.config.ts

// ๋ฐฉ๋ฒ• 1: ์›Œ์ปค ์ˆ˜ ๊ณ ์ •
workers: process.env.CI ? 2 : 4,

// ๋ฐฉ๋ฒ• 2: ํŒŒ์ผ ๋‹จ์œ„ ๋ณ‘๋ ฌ ์‹คํ–‰๋งŒ ํ—ˆ์šฉ (ํŒŒ์ผ ๋‚ด์—์„œ๋Š” ์ˆœ์ฐจ)
fullyParallel: false,

// ๋ฐฉ๋ฒ• 3: ํŠน์ • ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—์„œ๋งŒ ์ˆœ์ฐจ ์‹คํ–‰ ๊ฐ•์ œ
// test.describe.serial('...', () => { ... });

GitHub Actions์˜ ๋งคํŠธ๋ฆญ์Šค ์ „๋žต์œผ๋กœ ์ƒค๋”ฉ(sharding)์„ ํ™œ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋Ÿฌ๋„ˆ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ๋‚˜๋ˆ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - name: Run Playwright tests (shard)
        run: npx playwright test --shard=${{ matrix.shard }}/4

๋ฆฌํฌํŠธ ์„ค์ • (HTML ๋ฆฌํฌํŠธ)

- name: Run Playwright tests
  run: npx playwright test

- name: Upload HTML Report
  uses: actions/upload-artifact@v4
  if: always()  # ํ…Œ์ŠคํŠธ ์‹คํŒจํ•ด๋„ ๋ฆฌํฌํŠธ ์—…๋กœ๋“œ
  with:
    name: playwright-report
    path: playwright-report/
    retention-days: 30

retention-days: 30์œผ๋กœ 30์ผ๊ฐ„ ๋ณด๊ด€๋œ๋‹ค. ์•„ํ‹ฐํŒฉํŠธ ํŽ˜์ด์ง€์—์„œ ์ง์ ‘ HTML ๋ฆฌํฌํŠธ๋ฅผ ๋‹ค์šด๋ฐ›์•„ ์—ด์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.


Playwright ํ…Œ์ŠคํŠธ ๋””๋ฒ„๊น…

--headed ๋ชจ๋“œ๋กœ ๋กœ์ปฌ ๋””๋ฒ„๊น…

CI์—์„œ ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋กœ์ปฌ์—์„œ ์žฌํ˜„ํ•  ๋•Œ, --headed ์˜ต์…˜์œผ๋กœ ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์„ ๋„์›Œ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

# headed ๋ชจ๋“œ๋กœ ์‹คํ–‰ (๋ธŒ๋ผ์šฐ์ € ์ฐฝ์ด ๋œฌ๋‹ค)
npx playwright test --headed

# ํŠน์ • ํ…Œ์ŠคํŠธ๋งŒ headed๋กœ ์‹คํ–‰
npx playwright test e2e/home.spec.ts --headed

# ๋А๋ฆฌ๊ฒŒ ์‹คํ–‰ (๊ฐ ์•ก์…˜ ์‚ฌ์ด์— 500ms ์ง€์—ฐ)
npx playwright test --headed --slow-mo=500

PWDEBUG=1 ํ™˜๊ฒฝ๋ณ€์ˆ˜

PWDEBUG=1์„ ์„ค์ •ํ•˜๋ฉด Playwright Inspector๊ฐ€ ์—ด๋ฆฐ๋‹ค. ๋‹จ๊ณ„๋ณ„๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ์„ ํƒ์ž๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๋””๋ฒ„๊น… ๋„๊ตฌ๋‹ค.

# Playwright Inspector ์—ด๊ธฐ
PWDEBUG=1 npx playwright test

# Windows PowerShell
$env:PWDEBUG=1; npx playwright test

Inspector์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค:

  • ํ…Œ์ŠคํŠธ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์‹คํ–‰/์ผ์‹œ์ •์ง€
  • DOM ์š”์†Œ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ ค ์„ ํƒ์ž ์ž๋™ ์ œ์•ˆ
  • ํ˜„์žฌ ํŽ˜์ด์ง€ ์ƒํƒœ ์Šค๋ƒ…์ƒท ํ™•์ธ
  • ์•ก์…˜ ๋กœ๊ทธ ์‹ค์‹œ๊ฐ„ ํ™•์ธ

์Šคํฌ๋ฆฐ์ƒท/๋น„๋””์˜ค ๋…นํ™”

ํ…Œ์ŠคํŠธ ์‹คํŒจ ์›์ธ์„ ํŒŒ์•…ํ•˜๋Š” ๊ฐ€์žฅ ์ง๊ด€์ ์ธ ๋ฐฉ๋ฒ•์€ ์Šคํฌ๋ฆฐ์ƒท๊ณผ ๋น„๋””์˜ค๋‹ค.

// playwright.config.ts โ€” ์ „์—ญ ์„ค์ •
use: {
  // 'off' | 'on' | 'only-on-failure' | 'retain-on-failure'
  screenshot: 'only-on-failure',

  // 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'
  video: 'on-first-retry',

  // 'off' | 'on' | 'retain-on-failure' | 'on-all-retries' | 'on-first-retry'
  trace: 'on-first-retry',
},

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์•ˆ์—์„œ ์ˆ˜๋™์œผ๋กœ ์ฐ์„ ์ˆ˜๋„ ์žˆ๋‹ค:

test('๋ธ”๋กœ๊ทธ ํ™ˆ ๋ Œ๋”๋ง ํ™•์ธ', async ({ page }) => {
  await page.goto('/');

  // ํŠน์ • ์‹œ์ ์— ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ
  await page.screenshot({ path: 'screenshots/home.png', fullPage: true });

  // ํŠน์ • ์š”์†Œ๋งŒ ์บก์ฒ˜
  const header = page.locator('header');
  await header.screenshot({ path: 'screenshots/header.png' });

  await expect(page).toHaveTitle(/Gatsby Blog/);
});

ํŠธ๋ ˆ์ด์Šค(Trace) ๋ทฐ์–ด

Playwright์˜ ํŠธ๋ ˆ์ด์Šค๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ๋ชจ๋“  ์•ก์…˜, ๋„คํŠธ์›Œํฌ ์š”์ฒญ, ์ฝ˜์†” ๋กœ๊ทธ, ์Šคํฌ๋ฆฐ์ƒท์„ ํƒ€์ž„๋ผ์ธ ํ˜•ํƒœ๋กœ ๊ธฐ๋กํ•œ๋‹ค.

# ํŠธ๋ ˆ์ด์Šค ํŒŒ์ผ ์—ด๊ธฐ
npx playwright show-trace trace.zip

CI์—์„œ ์ˆ˜์ง‘ํ•œ ํŠธ๋ ˆ์ด์Šค๋ฅผ ์•„ํ‹ฐํŒฉํŠธ๋กœ ๋‚ด๋ ค๋ฐ›์•„ ๋กœ์ปฌ์—์„œ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋‹ค:

- name: Upload traces on failure
  uses: actions/upload-artifact@v4
  if: failure()
  with:
    name: playwright-traces
    path: test-results/

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• (๊ธฐ์กด)

์ฒ˜์Œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•ด ์‹œ๋„ํ–ˆ๋˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•๋“ค์„ ์ •๋ฆฌํ•œ๋‹ค.

์‹œ๋„ 1: Playwright ์บ์‹œ ๊ฐ•์ œ ์‚ญ์ œ

  • ์บ์‹œ๋œ Playwright ๋ธŒ๋ผ์šฐ์ € ํŒŒ์ผ๋“ค์„ ๊ฐ•์ œ๋กœ ์‚ญ์ œํ•œ ํ›„, ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ๋‹ค์‹œ ์„ค์น˜ํ•˜์˜€๋‹ค.
  • - name: Remove Playwright Cache
      run: |
        rm -rf ~/.cache/ms-playwright
        rm -rf ~/work/<your-repo-name>/<your-repo-name>/.cache/ms-playwright
  • ์†Œ์šฉ์€ ์—†์—ˆ๋‹ค.

์‹œ๋„ 2: Chromium ๊ฒฝ๋กœ ๋ช…ํ™•ํžˆ ์ง€์ •

  • Playwright ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์„ค์น˜ํ•œ ํ›„, ์ตœ์‹  Chromium ๊ฒฝ๋กœ๋ฅผ ํ™•์ธํ•˜์—ฌ ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•œ๋‹ค.
  • ์ด๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์—ฌ GitHub Actions์™€ Gatsby ๋นŒ๋“œ ์‹œ ๋ช…์‹œ์ ์œผ๋กœ ํ•ด๋‹น ๊ฒฝ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒํ•œ๋‹ค.
  • - name: Install Playwright and Set Browser Path
      run: |
        npx playwright install --with-deps chromium
        CHROMIUM_DIR=$(ls -d $HOME/.cache/ms-playwright/chromium-*/ | sort -V | tail -n 1)
        echo "CHROMIUM_DIR=$CHROMIUM_DIR" >> $GITHUB_ENV
        ls -al $CHROMIUM_DIR
  • ์†Œ์šฉ์€ ์—†์—ˆ๋‹ค.

์‹œ๋„ 3: ๋นŒ๋“œ ์‹œ Playwright ๊ฒฝ๋กœ ํ™•์ธ

  • ๋นŒ๋“œ ๋„์ค‘ Playwright ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์น˜๋˜์—ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ •ํ™•ํ•œ ๊ฒฝ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋””๋ฒ„๊น…์— ํ™œ์šฉํ–ˆ๋‹ค.
  • - name: Verify Playwright Installation and Path
      run: |
        npx playwright --version
        echo "Using Chromium from: $CHROMIUM_DIR"
        ls -al $CHROMIUM_DIR

์‹œ๋„ 4: ๋นŒ๋“œ ์ „ gatsby ์บ์‹œ ์‚ญ์ œ

  • ๋นŒ๋“œ ์‹คํ–‰ ์ „์— package.json์— ์„ค์ •ํ•ด๋‘” cache clean ๋ช…๋ น์–ด๋ฅผ ๋ฏธ๋ฆฌ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ์‹œ ์ด์ „ cache๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋นŒ๋“œํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ฐพ๋„๋ก ํ•˜์˜€๋‹ค.
  • - name: Build with Gatsby
      env:
        PREFIX_PATHS: "true"
        CHROMIUM_DIR: ${{ env.CHROMIUM_DIR }}
      run: |
        yarn clean
        yarn build

์ตœ์ข… CI/CD ์›Œํฌํ”Œ๋กœ

CI/CD ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ํ๋ฆ„

Pull Request
Push to main
Miss
Hit
Pass
Fail
Success
Failure
์ฝ”๋“œ ๋ณ€๊ฒฝ
์ด๋ฒคํŠธ ์ข…๋ฅ˜?
CI: check_on_pull_request
CI: build_on_push
Checkout
Setup Node.js
Cache ๋ณต์›: node_modules
Cache ๋ณต์›: Playwright ๋ธŒ๋ผ์šฐ์ €
์บ์‹œ ํžˆํŠธ?
playwright install --with-deps
playwright install-deps only
yarn install
Lint ๊ฒ€์‚ฌ
Prettier ๊ฒ€์‚ฌ
๊ฒ€์‚ฌ ํ†ต๊ณผ?
PR ์ฒดํฌ ์™„๋ฃŒ โœ“
PR ์ฒดํฌ ์‹คํŒจ โœ—
Checkout
Setup Node.js
Cache ๋ณต์›
์˜์กด์„ฑ ์„ค์น˜
yarn clean + yarn build
๋นŒ๋“œ ์„ฑ๊ณต?
CI ์™„๋ฃŒ โœ“
์Šคํฌ๋ฆฐ์ƒท/๋น„๋””์˜ค ์•„ํ‹ฐํŒฉํŠธ ์—…๋กœ๋“œ
CI ์‹คํŒจ โœ—
CD: workflow_run trigger
Checkout
Setup Pages configure-pages@v5
Setup Node.js
์˜์กด์„ฑ + Playwright ์„ค์น˜
yarn clean + yarn build
Upload artifact
Deploy to GitHub Pages
๋ฐฐํฌ ์™„๋ฃŒ ๐Ÿš€

CI ํŒŒ์ดํ”„๋ผ์ธ (์บ์‹ฑ + ์•„ํ‹ฐํŒฉํŠธ ์ถ”๊ฐ€)

  • ์ตœ์‹  Playwright ๋ธŒ๋ผ์šฐ์ €๋ฅผ $HOME/.cache/ms-playwright ๊ฒฝ๋กœ์— ์„ค์น˜ํ•˜๊ณ , ์ด ๋ฒ„์ „์˜ ๊ฒฝ๋กœ๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•˜์—ฌ ๋นŒ๋“œ ๊ณผ์ •์—์„œ ํ™œ์šฉํ•˜์˜€๋‹ค.
  • ๋˜, pull request์™€ push ์ƒํ™ฉ์„ ๋‚˜๋ˆ„์–ด ๊ด€๋ฆฌํ•˜์˜€๋‹ค.
  • node_modules์™€ Playwright ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์บ์‹ฑํ•˜์—ฌ ์‹คํ–‰ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•˜์˜€๋‹ค.
  • name: CI
    
    on:
      pull_request:
        branches:
          - main
      push:
        branches:
          - main
    
    jobs:
      check_on_pull_request:
        if: github.event_name == 'pull_request'
        runs-on: ubuntu-latest
        timeout-minutes: 20
        steps:
          - uses: actions/checkout@v4
    
          - uses: actions/setup-node@v4
            with:
              node-version: "20"
    
          - name: Cache node_modules
            uses: actions/cache@v4
            with:
              path: node_modules
              key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
              restore-keys: |
                ${{ runner.os }}-yarn-
    
          - name: Cache Playwright Browsers
            uses: actions/cache@v4
            id: playwright-cache
            with:
              path: ~/.cache/ms-playwright
              key: ${{ runner.os }}-playwright-${{ hashFiles('**/yarn.lock') }}
    
          - name: Install node packages
            run: yarn
    
          - name: Install Playwright Browsers
            if: steps.playwright-cache.outputs.cache-hit != 'true'
            run: npx playwright install --with-deps chromium
    
          - name: Install Playwright System Dependencies
            if: steps.playwright-cache.outputs.cache-hit == 'true'
            run: npx playwright install-deps chromium
    
          - name: Check lint
            run: yarn check:lint
    
          - name: Check prettier
            run: yarn check:prettier
    
      build_on_push:
        if: github.event_name == 'push'
        runs-on: ubuntu-latest
        timeout-minutes: 30
        steps:
          - uses: actions/checkout@v4
    
          - uses: actions/setup-node@v4
            with:
              node-version: "20"
    
          - name: Cache node_modules
            uses: actions/cache@v4
            with:
              path: node_modules
              key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
              restore-keys: |
                ${{ runner.os }}-yarn-
    
          - name: Cache Playwright Browsers
            uses: actions/cache@v4
            id: playwright-cache
            with:
              path: ~/.cache/ms-playwright
              key: ${{ runner.os }}-playwright-${{ hashFiles('**/yarn.lock') }}
    
          - name: Install node packages
            run: yarn
    
          - name: Install Playwright Browsers
            if: steps.playwright-cache.outputs.cache-hit != 'true'
            run: npx playwright install --with-deps chromium
    
          - name: Install Playwright System Dependencies
            if: steps.playwright-cache.outputs.cache-hit == 'true'
            run: npx playwright install-deps chromium
    
          - name: Build
            run: yarn build
    
          - name: Upload test failure artifacts
            uses: actions/upload-artifact@v4
            if: failure()
            with:
              name: playwright-failure-artifacts
              path: |
                test-results/
                playwright-report/
              retention-days: 7
    
          - name: Verify Playwright Installation
            run: npx playwright --version

CD ํŒŒ์ดํ”„๋ผ์ธ (์บ์‹ฑ + ์ตœ์ ํ™”)

  • CI๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ์ž๋™์œผ๋กœ ์ง„ํ–‰๋˜๋ฉด์„œ build ์ „์— ์บ์‹œ ์‚ญ์ œ ํ›„ build๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ ์ด์ „์— ์‚ฌ์šฉํ•˜๋˜ ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹Œ ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ playwright๊ฐ€ ์„ค์น˜๋œ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์˜€๋‹ค.
  • name: CD
    
    on:
      workflow_run:
        workflows: ["CI"]
        types:
          - completed
    
    permissions:
      contents: read
      pages: write
      id-token: write
    
    jobs:
      build:
        runs-on: ubuntu-latest
        timeout-minutes: 30
        steps:
          - name: Checkout Repository
            uses: actions/checkout@v4
    
          - name: Setup Pages
            id: pages
            uses: actions/configure-pages@v5
            with:
              static_site_generator: gatsby
    
          - name: Setup Node.js
            uses: actions/setup-node@v4
            with:
              node-version: "20"
    
          - name: Cache node_modules
            uses: actions/cache@v4
            with:
              path: node_modules
              key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
              restore-keys: |
                ${{ runner.os }}-yarn-
    
          - name: Cache Playwright Browsers
            uses: actions/cache@v4
            id: playwright-cache
            with:
              path: ~/.cache/ms-playwright
              key: ${{ runner.os }}-playwright-${{ hashFiles('**/yarn.lock') }}
    
          - name: Install Project Dependencies
            run: yarn install
    
          - name: Install Playwright Browsers
            if: steps.playwright-cache.outputs.cache-hit != 'true'
            run: npx playwright install --with-deps chromium
    
          - name: Install Playwright System Dependencies
            if: steps.playwright-cache.outputs.cache-hit == 'true'
            run: npx playwright install-deps chromium
    
          - name: Build with Gatsby
            env:
              PREFIX_PATHS: "true"
            run: |
              yarn clean
              yarn build
    
          - name: Upload artifact
            uses: actions/upload-pages-artifact@v3
            with:
              path: ./public
    
      deploy:
        environment:
          name: github-pages
          url: ${{ steps.deployment.outputs.page_url }}
        runs-on: ubuntu-latest
        needs: build
        steps:
          - name: Deploy to GitHub Pages
            id: deployment
            uses: actions/deploy-pages@v4

์ถ”๊ฐ€: Gatsby ํŽ˜์ด์ง€ ์„ค์ • ๋ฐ 404 ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

  • Node.js 20 ๋ฒ„์ „ ์„ค์น˜ ํ›„ actions/configure-pages@v5์„ ์ถ”๊ฐ€ํ•˜์—ฌ Gatsby ํŽ˜์ด์ง€๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋นŒ๋“œ๋œ ๋ธ”๋กœ๊ทธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ฐฐํฌ๋˜๋„๋ก ํ•œ๋‹ค.
  • - name: Setup Pages
      id: pages
      uses: actions/configure-pages@v5
      with:
        static_site_generator: gatsby

Github Pages์˜ ๋™์ž‘ ๋ฐฉ์‹

  • Github Pages๋Š” ์ •์  ์›น์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค๋กœ, HTML, CSS, JavaScript์™€ ๊ฐ™์€ ์ •์  ํŒŒ์ผ์„ ์ œ๊ณตํ•œ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • URL์— ์ ‘์†ํ•˜๋ฉด, Github Pages๋Š” ํ•ด๋‹น URL์— ๋งž๋Š” HTML ํŒŒ์ผ์„ ์ฐพ์•„์„œ ์ œ๊ณตํ•œ๋‹ค.
  • ๋งŒ์•ฝ ํ•ด๋‹น URL์— ๋งž๋Š” HTML ํŒŒ์ผ์ด ์—†์œผ๋ฉด, 404 ์˜ค๋ฅ˜ ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.

Gatsby์˜ ํŠน์ง•

  • Gatsby๋Š” React ๊ธฐ๋ฐ˜์˜ ์ •์  ์›น์‚ฌ์ดํŠธ ์ƒ์„ฑ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋นŒ๋“œ ๊ณผ์ •์—์„œ ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜์—ฌ ์ •์  HTML ํŒŒ์ผ๋กœ ์ €์žฅํ•œ๋‹ค.
  • ํ•˜์ง€๋งŒ Gatsby๋Š” ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ผ์šฐํŒ…์„ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€ ์ „ํ™˜์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.3
  • ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ์›น์‚ฌ์ดํŠธ ๋‚ด์—์„œ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด, ์‹ค์ œ๋กœ ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ JavaScript๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•œ๋‹ค.

๋ฌธ์ œ ๋ฐœ์ƒ ์›์ธ

  • actions/configure-pages@v54 ์•ก์…˜์ด ์—†์œผ๋ฉด, Github Pages๋Š” Gatsby์˜ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ผ์šฐํŒ…์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๋‹ค.5
  • ์‚ฌ์šฉ์ž๊ฐ€ Gatsby ๋ธ”๋กœ๊ทธ ๋‚ด์—์„œ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜์—ฌ ํŽ˜์ด์ง€๋ฅผ ์ „ํ™˜ํ•˜๋ฉด, ์‹ค์ œ๋กœ ์ƒˆ๋กœ์šด URL์— ์ ‘์†ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ Github Pages๋Š” ํ•ด๋‹น URL์— ๋งž๋Š” HTML ํŒŒ์ผ์„ ์ฐพ์ง€ ๋ชปํ•ด 404 ์˜ค๋ฅ˜ ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • actions/configure-pages@v5 ์•ก์…˜ ์‚ฌ์šฉ์„ ํ†ตํ•ด, Gatsby ๋ธ”๋กœ๊ทธ์˜ ํŠน์ง•์„ Github Pages์— ์•Œ๋ ค์ค€๋‹ค.
  • ์ด ์•ก์…˜์€ Gatsby ๋ธ”๋กœ๊ทธ์˜ ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฅผ ๋ถ„์„ํ•˜์—ฌ, ๊ฐ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ Github Pages์— ์ œ๊ณตํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž๊ฐ€ Gatsby ๋ธ”๋กœ๊ทธ ๋‚ด์—์„œ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜์—ฌ ํŽ˜์ด์ง€๋ฅผ ์ „ํ™˜ํ•˜๋”๋ผ๋„, Github Pages๋Š” ํ•ด๋‹น URL์— ๋งž๋Š” HTML ํŒŒ์ผ์„ ์ฐพ์•„์„œ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

์ž์ฃผ ์“ฐ๋Š” Playwright CLI ๋ช…๋ น์–ด ์ •๋ฆฌ

์‹ค์ œ๋กœ ์ž‘์—…ํ•˜๋ฉด์„œ ์ž์ฃผ ์“ฐ๊ฒŒ ๋˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์ •๋ฆฌํ•ด๋‘”๋‹ค.

# ๋ธŒ๋ผ์šฐ์ € ์„ค์น˜
npx playwright install                        # ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ € ์„ค์น˜
npx playwright install chromium               # Chromium๋งŒ ์„ค์น˜
npx playwright install --with-deps chromium   # ์‹œ์Šคํ…œ ์˜์กด์„ฑ ํฌํ•จ ์„ค์น˜
npx playwright install-deps                   # ์‹œ์Šคํ…œ ์˜์กด์„ฑ๋งŒ ์„ค์น˜

# ํ…Œ์ŠคํŠธ ์‹คํ–‰
npx playwright test                           # ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์‹คํ–‰
npx playwright test home.spec.ts              # ํŠน์ • ํŒŒ์ผ๋งŒ ์‹คํ–‰
npx playwright test --headed                  # ๋ธŒ๋ผ์šฐ์ € ์ฐฝ ๋„์›Œ์„œ ์‹คํ–‰
npx playwright test --debug                   # Inspector ์—ด์–ด์„œ ๋‹จ๊ณ„ ์‹คํ–‰

# ๋ฆฌํฌํŠธ
npx playwright show-report                    # HTML ๋ฆฌํฌํŠธ ์—ด๊ธฐ
npx playwright show-trace trace.zip           # ํŠธ๋ ˆ์ด์Šค ๋ทฐ์–ด ์—ด๊ธฐ

# ์ฝ”๋“œ ์ƒ์„ฑ (์•ก์…˜์„ ๊ธฐ๋กํ•ด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž๋™ ์ƒ์„ฑ)
npx playwright codegen https://example.com

# ๋ฒ„์ „ ํ™•์ธ
npx playwright --version

๊ฒฐ๋ก 

Playwright ๊ฒฝ๋กœ ๋ฌธ์ œ

์ตœ์‹  playwright ๋ฒ„์ „์„ ํ™•์ธํ•˜๊ณ , ํ•ด๋‹น ๋ฒ„์ „์˜ ์ตœ์‹  playwright๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก, ์ด์ „ ์บ์‹œ๋ฅผ ์‚ญ์ œํ•จ์œผ๋กœ์จ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ตœ์‹  playwright๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์ •ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๊ทผ๋ณธ์ ์œผ๋กœ๋Š” Playwright ํŒจํ‚ค์ง€ ๋ฒ„์ „๊ณผ ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฒ„์ „์˜ ๋งคํ•‘์ด ์–ด๊ธ‹๋‚˜๋Š” ๊ฒƒ์ด ์›์ธ์ด๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด:

  1. yarn.lock / package-lock.json ๊ธฐ์ค€์œผ๋กœ ์บ์‹œ ํ‚ค๋ฅผ ์„ค์ •ํ•œ๋‹ค
  2. npx playwright install --with-deps๋กœ ํŒจํ‚ค์ง€ ๋ฒ„์ „์— ๋งž๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ํ•ญ์ƒ ์ƒˆ๋กœ ๋‚ด๋ ค๋ฐ›๋Š”๋‹ค
  3. ์บ์‹œ ํžˆํŠธ/๋ฏธ์Šค์— ๋”ฐ๋ผ install-deps์™€ install --with-deps๋ฅผ ๋ถ„๊ธฐํ•œ๋‹ค

Gatsby ํŽ˜์ด์ง€ ์„ค์ • ๋ฐ 404 ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

actions/configure-pages@v5 ์•ก์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ Gatsby ํŽ˜์ด์ง€๋ฅผ ์„ค์ •ํ•˜๊ณ  404 ํŽ˜์ด์ง€ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„ ์›์น™

์ด๋ฒˆ ๊ฒฝํ—˜์„ ํ†ตํ•ด ์ •๋ฆฌํ•œ ์•ˆ์ •์ ์ธ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„ ์›์น™์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์›์น™ ์ ์šฉ ๋ฐฉ๋ฒ•
์บ์‹œ ํ‚ค๋Š” lock ํŒŒ์ผ ๊ธฐ๋ฐ˜์œผ๋กœ hashFiles('**/yarn.lock') ์‚ฌ์šฉ
์บ์‹œ ํžˆํŠธ/๋ฏธ์Šค ๋ถ„๊ธฐ if: steps.cache.outputs.cache-hit != 'true'
ํƒ€์ž„์•„์›ƒ ๋ช…์‹œ timeout-minutes ์„ค์ •์œผ๋กœ ๋ฌดํ•œ ๋Œ€๊ธฐ ๋ฐฉ์ง€
์‹คํŒจ ์‹œ ์•„ํ‹ฐํŒฉํŠธ ์ˆ˜์ง‘ if: failure() + upload-artifact
PR๊ณผ Push ๋ถ„๋ฆฌ ์ฝ”๋“œ ํ’ˆ์งˆ ๊ฒ€์‚ฌ(PR)์™€ ๋นŒ๋“œ ๊ฒ€์ฆ(Push) ์—ญํ•  ๊ตฌ๋ถ„
@leekh8
๋ณด์•ˆ, ์›น ๊ฐœ๋ฐœ, Python์„ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ