๐ŸŽญ Playwright์˜ Auto-Waiting์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ?

LIMHALIMยท2025๋…„ 4์›” 17์ผ
1

๋จผ์ €, Playright๋Š” ๋ฌด์—‡์ผ๊นŒ?

Playwright๋Š” Microsoft์—์„œ ๋งŒ๋“  ์˜คํ”ˆ์†Œ์Šค E2E(End-to-End) ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์„ ๊ทธ๋Œ€๋กœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋Š” ๋ฐ ์ดˆ์ ์„ ๋งž์ถ”๊ณ  ์žˆ๋‹ค. Chromium, Firefox, WebKit ๋“ฑ ์ฃผ์š” ๋ Œ๋”๋ง ์—”์ง„์„ ๋ชจ๋‘ ์ง€์›ํ•˜๋ฉฐ, ๋ฉ€ํ‹ฐ ๋ธŒ๋ผ์šฐ์ € ํ…Œ์ŠคํŠธ๋ฅผ ๋‹จ์ผ ์ฝ”๋“œ๋ฒ ์ด์Šค๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

๐ŸŽฏ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ณด๋‹ค๋„ Playwright๊ฐ€ ๋›ฐ์–ด๋‚œ ์ด์œ ?

๋น„๊ต ํ•ญ๋ชฉPlaywrightCypressSelenium
๋ฉ€ํ‹ฐ ๋ธŒ๋ผ์šฐ์ € ์ง€์›โœ… Chromium, Firefox, WebKit๐Ÿšซ Chrome ์ค‘์‹ฌโœ… ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €
๋ฉ€ํ‹ฐ ํƒญ/์ฐฝ ์ œ์–ดโœ… ๊ฐ€๋Šฅ๐Ÿšซ ๋ถˆ๊ฐ€๋Šฅโœ… ๊ฐ€๋Šฅ
๋„คํŠธ์›Œํฌ ์š”์ฒญ ๊ฐ€๋กœ์ฑ„๊ธฐโœ… ์‰ฌ์›€๐Ÿ”ธ ์ œํ•œ์ ๐Ÿ”ธ ๋ฒˆ๊ฑฐ๋กœ์›€
์ž๋™ ๋Œ€๊ธฐ(Auto-Wait)โœ… ๊ธฐ๋ณธ ๋‚ด์žฅ๐Ÿšซ ์ง์ ‘ ์ฒ˜๋ฆฌ๐Ÿšซ ์ง์ ‘ ์ฒ˜๋ฆฌ
์†๋„โšก ๋น ๋ฆ„โšก ๋น ๋ฆ„๐Ÿข ์ƒ๋Œ€์ ์œผ๋กœ ๋А๋ฆผ
์„ค์น˜/์…‹์—…โšก ๊ฐ„ํŽธโšก ๊ฐ„ํŽธ๐Ÿ˜ต ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ

๋ฉ€ํ‹ฐ ๋ธŒ๋ผ์šฐ์ € ์ง€์›์—๋„ ๊ฐ•๋ ฅํ•œ ๊ฐ•์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ์œ„์—์„œ ์ฒ˜๋Ÿผ Playwright๋งŒ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Auto-wait ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ณ ์ž ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ๋„์ค‘ ํ”ํžˆ ๊ฒช๋Š” ํƒ€์ด๋ฐ ๋ฌธ์ œ๋ฅผ ์ค„์—ฌ์ฃผ๋ฉฐ, ์•ˆ์ •์ ์ด๊ณ  ์‹ ๋ขฐ๋„ ๋†’์€ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

โณ Auto-Waiting

Playwright์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ Auto-Waiting์€ ์‚ฌ์šฉ์ž์˜ ๋ช…๋ น (click, fill, goto ๋“ฑ)์„ ์‹คํ–‰ํ•  ๋•Œ, DOM ์ƒํƒœ๊ฐ€ ์•ˆ์ „ํ•˜๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ์ž๋™์œผ๋กœ ๊ธฐ๋‹ค๋ ค์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ„๋„๋กœ waitFor ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„, Playwright๋Š” ์ ์ ˆํ•œ ํƒ€์ด๋ฐ์„ ์Šค์Šค๋กœ ํŒ๋‹จํ•ด์ค€๋‹ค.

๐Ÿง  โ€œActionabilityโ€ ๋ชจ๋ธ

Playwright๊ฐ€ ์–ด๋–ค element๋ฅผ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ ์ž…๋ ฅํ•˜๊ธฐ ์ „์— ํ™•์ธํ•˜๋Š” ์กฐ๊ฑด๋“ค์ด ์žˆ๋‹ค. ๋ฐ”๋กœ Actionability checks๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๊ฒƒ๋“ค์ด๋‹ค. ์ด๊ฒŒ ๋ฐ”๋กœ Auto-Waiting์˜ ํ•ต์‹ฌ์ด๋‹ค.

๐ŸŽฏ ์˜ˆ๋ฅผ ๋“ค์–ด page.click(selector) ํ˜ธ์ถœ ์‹œ, Playwright๋Š” ๋‹ค์Œ์„ ํ™•์ธํ•œ๋‹ค.

  • Element๊ฐ€ DOM์— ์กด์žฌํ•˜๋Š”๊ฐ€? (attached)
  • ๋ณด์ด๋Š”๊ฐ€? (visible)
  • ์“ธ ์ˆ˜ ์žˆ๋Š” (ํ™œ์„ฑํ™”) ์ƒํƒœ์ธ๊ฐ€? (enabled)
  • ์Šคํฌ๋กค์ด ํ•„์š”ํ•œ๊ฐ€? (in viewport)
  • ๋‹ค๋ฅธ ์š”์†Œ์— ์•ˆ ๊ฐ€๋ ค์กŒ๋Š”๊ฐ€? (not obscured)

์ด ๋ชจ๋“  ์กฐ๊ฑด์„ ๋งŒ์กฑํ•  ๋•Œ๊นŒ์ง€, Playwright๋Š” ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งŒ์กฑ๋˜๋Š” ์ˆœ๊ฐ„, click ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ธ๋‹ค.

์ด๊ฑธ Actionability model์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๐Ÿ•ต๏ธโ€โ™€๏ธ ๋‚ด๋ถ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜, ์–ด๋–ป๊ฒŒ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ์„๊นŒ?

Playwright๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ WebSocket์„ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ €์™€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—ฐ๊ฒฐ๋œ ์ƒํƒœ์—์„œ ๋ช…๋ น์„ ์ฃผ๊ณ ๋ฐ›๋Š”๋‹ค. ์ด ์—ฐ๊ฒฐ์„ ํ†ตํ•ด DOM ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ๋ณ€ํ™”๊ฐ€ ๊ฐ์ง€๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.

page.click() ๊ฐ™์€ ๋ช…๋ น์€ ๋‚ด๋ถ€์ ์œผ๋กœ JavaScript Promise๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฉฐ, actionability ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๊ธฐ ์ „๊นŒ์ง€ resolve๋˜์ง€ ์•Š๋Š”๋‹ค.

์กฐ๊ฑด์„ ์ฒดํฌํ•˜๊ธฐ ์œ„ํ•ด ๋ธŒ๋ผ์šฐ์ € ๋‚ด์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด DOM ๋ณ€ํ™” ๊ฐ์ง€๋ฅผ ํ•˜์—ฌ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋Š”์ง€ ํŒ๋‹จํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

  • MutationObserver: DOM ๊ตฌ์กฐ ๋ณ€ํ™” ๊ฐ์ง€
  • IntersectionObserver: ์š”์†Œ์˜ ํ™”๋ฉด ๋‚ด ๋…ธ์ถœ ์—ฌ๋ถ€ ํ™•์ธ
  • ์ฃผ๊ธฐ์  polling: ์กฐ๊ฑด ์ถฉ์กฑ ์—ฌ๋ถ€ ์ ๊ฒ€

์˜ˆ๋ฅผ ๋“ค์–ด, page.click('#submit')๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ๋ฆ„์„ ๋”ฐ๋ฅธ๋‹ค.

await page.waitForSelector('#submit'); // ์กด์žฌ ํ™•์ธ
await ensureActionable('#submit');     // visibility, enabled ๋“ฑ ์ฒดํฌ
await scrollIntoView('#submit');       // ์Šคํฌ๋กค ์กฐ์ •
await page.click('#submit');           // ์‹ค์ œ ํด๋ฆญ ์‹คํ–‰
}

Playwright๋Š” ์ด๋Ÿฐ ์‹์œผ๋กœ, wait โ†’ check โ†’ ์‹คํ–‰ ํŒจํ„ด์„ ์ž๋™ํ™”ํ•ด๋†“์•˜๋‹ค.
๋‹จ์ˆœํžˆ click()์„ ํ˜ธ์ถœํ•ฌ์„ ๋ฟ์ธ๋ฐ, ์ด ๋ชจ๋“  ๊ณผ์ •์„ ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ Playwright๊ฐ€ ์ž๋™์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ํŒ๋‹จํ•œ๋‹ค.

๐Ÿงช ๊ทธ๋Ÿผ ์ด๊ฒŒ ์™œ ๋Œ€๋‹จํ•œ๊ฐ€?

1. Flaky Test ๊ฐ์†Œ

E2E ํ…Œ์ŠคํŠธ์˜ ๊ฐ€์žฅ ํฐ ์ ์€ Flaky Test, ์ฆ‰ ๊ฐ„ํ—์  ์‹คํŒจ๋ผ๊ณ  ํ•œ๋‹ค. ์ด๋Š” ๋Œ€๋ถ€๋ถ„ DOM์ด ์•„์ง ์ค€๋น„๋˜์ง€ ์•Š์•˜๋Š”๋ฐ ๋™์ž‘์„ ์‹œ๋„ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•œ๋‹ค.
Playwright๋Š” ์ด๋Ÿฐ ํƒ€์ด๋ฐ ์ด์Šˆ๋ฅผ ๋ฐฉ์ง€ํ•ด ํ…Œ์ŠคํŠธ์˜ ์‹ ๋ขฐ๋„์™€ ์•ˆ์ •์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค.

2. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ฐ„๊ฒฐํ™”

๊ธฐ์กด์—๋Š” waitForSelector, waitForTimeout ๋“ฑ์„ ์ˆ˜๋™์œผ๋กœ ์ž‘์„ฑํ•ด์•ผ ํ–ˆ์ง€๋งŒ, Playwright๋Š” ์ด ๊ณผ์ •์„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
โ†’ ๋” ์ ์€ ์ฝ”๋“œ๋กœ ๋” ๋งŽ์€ ์•ˆ์ •์„ฑ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ๋‹ค.

โš ๏ธ ๊ทธ๋Ÿฐ๋ฐ ๋ฌด์กฐ๊ฑด ๋‹ค ๊ธฐ๋‹ค๋ ค์ฃผ์ง„ ์•Š๋Š”๋‹ค.

Playwright์˜ Auto-Waiting์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ์ ์ธ ์ƒํ˜ธ์ž‘์šฉ ๋™์ž‘์— ๋Œ€ํ•ด์„œ๋งŒ ๋™์ž‘ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

  • click, dblclick
  • fill, type, press
  • check, uncheck
  • hover
  • selectOption
  • setInputFiles
  • goto, waitForNavigation, reload

โŒ ํ•˜์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์ˆ˜๋™ ๋Œ€๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์ƒํ™ฉ๋Œ€๊ธฐ ํ•จ์ˆ˜ ์˜ˆ์‹œ
๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์™„๋ฃŒ ํ™•์ธpage.waitForResponse()
ํŠน์ • ํ…์ŠคํŠธ ๋ Œ๋”๋ง ๋Œ€๊ธฐpage.waitForSelector('text=์™„๋ฃŒ')
์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฃŒ ํ™•์ธpage.waitForFunction(() => !isAnimating)
API ์‘๋‹ต ์™„๋ฃŒpage.waitForResponse(res => res.url().includes('/api') && res.status() === 200)

์ด๋Ÿด ๋• ์—ฌ์ „ํžˆ page.waitForFunction(), page.waitForResponse(), page.waitForSelector() ๊ฐ™์€ ์ˆ˜๋™ ๋Œ€๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋ฉด Auto-waiting์ด ์ง€์›๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์ผ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๊ผผ๊ผผํžˆ ํ™•์ธํ•˜์ž.


โœจ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

Playwright์˜ Auto-Waiting์€ ๋‹จ์ˆœํžˆ โ€œ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ธฐ๋Šฅโ€์„ ๋„˜์–ด์„œ, ์‚ฌ์šฉ์ž์˜ ์‹ค์ œ ํ–‰๋™ ํ๋ฆ„์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์‹œ์ ์„ ํŒ๋‹จํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๋˜‘๋˜‘ํ•œ ๊ธฐ๋Šฅ์ธ ๊ฒƒ ๊ฐ™๋‹ค.

๋‚˜๋Š” ํ‰์†Œ์— ๋‹จ์œ„ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ Jest๋งŒ ์•Œ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, (์‚ฌ์šฉํ•ด๋ณธ ์ ์€ ์—†๊ณ ..) Playwright๋ฅผ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ € ์ƒ์—์„œ ์ „์ฒด ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ๋ฅผ ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์‹ ๊ธฐํ•˜๊ณ  ๋งค๋ ฅ์ ์ด์—ˆ๋‹ค.

๊ธฐ์กด์—๋Š” ๋งค๋ฒˆ ์ž…๋ ฅ ๊ฐ’์„ ์ง์ ‘ ๋„ฃ์–ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋˜๋Š” ๋“ฑ๋ก์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ–ˆ์—ˆ๋Š”๋ฐ, Playwright๋ฅผ ํ†ตํ•ด ์ž๋™ํ™”ํ•ด ๋†“์œผ๋‹ˆ ์ •๋ง ํŽธ๋ฆฌํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ์‹œ๊ฐ„์ด ์€๊ทผ ๋งŽ์ด ์†Œ์š”๋˜๊ณ  ๊ท€์ฐฎ์€ ์ผ์ด์˜€๋Š”๋ฐ, ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—๋„ ๋„์ž…ํ•˜๋ฉด ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜์ด ๋งค์šฐ ํ–ฅ์ƒ๋  ๊ฒƒ ๊ฐ™๋‹ค :)


์ฐธ๊ณ  ๋ฌธํ—Œ

https://playwright.dev/

profile
๋ชจ๋“  ์ต์ˆ™ํ•จ์— ๋ฌผ์Œํ‘œ ๋”ํ•˜๊ธฐ

0๊ฐœ์˜ ๋Œ“๊ธ€