본문으로 건너뛰기
중급약 22분

Selenium WebDriver로 첫 자동화 테스트 작성하기

웹 자동화의 원조 Selenium을 모듈 13의 Playwright와 나란히 놓고 읽는다. WebDriver 프로토콜이라는 구조가 왜 압도적인 언어·브라우저 지원을 만들었는지, 그리고 명시적 대기를 왜 직접 써야 하는지 이해한다.

  • #Selenium
  • #WebDriver
  • #웹자동화
  • #명시적대기

개념

2004년에 나온 도구를 지금 배우는 이유

Selenium은 2004년에 등장한 웹 자동화의 원조다. 모듈 13(레슨 2)의 Playwright가 2020년생인데도 오늘날 가장 많은 기업의 자동화 자산은 여전히 Selenium으로 쓰여 있다. 이 레슨의 목표는 문법 암기가 아니라 Playwright에서 익힌 개념이 Selenium에서 어떤 모양으로 나타나는지 알아보는 것 이다.

구조 차이 — 표준 프로토콜을 사이에 둔다

모듈 13(레슨 9)에서 Playwright는 브라우저 "밖", Cypress는 브라우저 "안"에서 실행된다고 배웠다. Selenium은 제3의 구조다.

Playwright: 테스트 코드 → (전용 프로토콜) → 브라우저 엔진 직접 제어
Cypress:    테스트 코드가 브라우저 안에서 함께 실행
Selenium:   테스트 코드 → WebDriver(W3C 표준 프로토콜)
            → 브라우저 드라이버(ChromeDriver 등) → 브라우저

핵심은 가운데의 WebDriver 다. 특정 회사의 구현이 아니라 W3C 웹 표준 규격이다. "요소를 찾아라", "클릭해라" 같은 명령이 HTTP 요청 형태로 규격화돼 있고, 각 브라우저 벤더가 그 규격을 지키는 드라이버를 직접 만들어 배포한다. 이 표준 계층 한 겹이 Selenium의 특징 대부분을 설명한다.

SeleniumPlaywright
브라우저와의 연결W3C WebDriver 표준 프로토콜자체 프로토콜로 엔진 직접 제어
지원 언어Java·Python·C#·JS·Ruby·Kotlin 등JS/TS·Python·Java·.NET
브라우저 지원드라이버를 만드는 모든 브라우저Chromium·Firefox·WebKit
자동 대기제한적 (직접 대기를 써야 할 때가 많음)기본 제공

언어 지원이 넓은 것은 프로토콜이 HTTP 규격이라 요청만 보낼 수 있으면 어떤 언어로든 클라이언트를 만들 수 있어서고, 브라우저 지원이 넓은 것은 브라우저를 만드는 회사가 자기 드라이버를 직접 책임지기 때문이다.

같은 시나리오, 다른 문법

모듈 13(레슨 2)의 로그인 예시를 Selenium(Python)으로 옮기면 이렇다.

def test_로그인에_성공하면_대시보드로_이동한다():
    driver = webdriver.Chrome()
 
    # Arrange — 페이지 접속
    driver.get("https://example.com/login")
 
    # Act — 로그인 정보 입력 후 제출
    driver.find_element(By.ID, "email").send_keys("user@example.com")
    driver.find_element(By.ID, "password").send_keys("password123")
    driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
 
    # Assert — 대시보드로 이동했는지 확인
    WebDriverWait(driver, 10).until(EC.url_contains("dashboard"))
    assert "환영합니다" in driver.page_source
 
    driver.quit()

page.goto ↔ driver.get, getByLabel(...).fill ↔ find_element(...).send_keys. 모듈 10(레슨 1)의 AAA 패턴도 그대로다. 개념은 같고 문법만 다르다 는 모듈 13(레슨 9)의 결론이 반복된다.

다만 두 가지가 눈에 띄게 다르다. 브라우저를 직접 열고(webdriver.Chrome()) 직접 닫아야(driver.quit()) 한다는 것, 그리고 검증 앞에 붙은 WebDriverWait 다.

명시적 대기 — Selenium 플래키의 최대 원인

Playwright는 요소가 나타나고 클릭 가능해질 때까지 알아서 기다린다(모듈 13 레슨 2, 레슨 5). Selenium의 암묵적 대기(implicit wait)는 "DOM에서 찾을 때까지"만 기다릴 뿐 화면에 보이는지·클릭 가능한지는 보장하지 않는다. 그래서 조건을 직접 명시한다.

time.sleep(3)                                    # 안티패턴 (모듈 13 레슨 5)
WebDriverWait(driver, 10).until(                 # 조건을 명시해서 기다린다
    EC.element_to_be_clickable((By.ID, "submit"))
).click()

WebDriverWait(driver, 10)은 "최대 10초 동안 조건이 참이 될 때까지 짧은 간격으로 반복 확인한다"는 뜻이다. 1초 만에 충족되면 1초만 쓴다.

이 한 줄을 빼먹는 것이 Selenium 테스트가 플래키해지는 가장 흔한 원인 이다. 로컬에서는 화면이 빨리 그려져 통과하고, 부하가 걸린 CI에서는 렌더링이 0.5초 늦어져 실패한다. 모듈 13(레슨 6)의 "환경에 따라 결과가 달라지는 불안정한 테스트"의 교과서적 사례다.

왜 아직도 Selenium인가

"오래됐다"는 단점이 아니라 오히려 이유다.

  • 관성 — 수천 개의 테스트가 이미 Selenium으로 쓰인 회사가 많다. 전부 옮기는 비용은 모듈 13(레슨 1)의 ROI 관점에서 대개 정당화되지 않는다
  • 자산과 언어 — 20년치 커뮤니티 답변이 쌓여 있고, 팀 주력 언어가 Java나 C#이면 선택지가 사실상 Selenium이다
  • Selenium Grid — 여러 머신에 브라우저를 띄워 분산 실행하는 공식 도구다. 500개 테스트를 10대에 나눠 돌리거나 Chrome·Firefox·Edge에 동시에 태울 수 있다 — 모듈 13(레슨 8) 크로스 브라우저 전략의 실행 수단이다

실무에서 왜 필요한가

이직해 들어간 팀의 자동화 자산이 Selenium인 경우는 매우 흔하다. 그때 필요한 능력은 이미 아는 Playwright 개념을 Selenium 코드 위에 올려 읽는 것 이다. find_element를 보면 "로케이터구나(모듈 13 레슨 3)", WebDriverWait을 보면 "대기 전략을 직접 쓴 거구나(레슨 5)" 하고 알아볼 수 있으면 첫 주부터 코드 리뷰에 참여할 수 있다.

또 CI에서 간헐적으로 깨지는 Selenium 테스트를 만났을 때 sleep이 흩뿌려져 있는지, WebDriverWait이 빠졌는지 먼저 의심하는 것만으로 원인을 크게 좁힐 수 있다. QA가 자동화 팀에 넘기지 않고 스스로 1차 분석을 하는 지점이다.

실습 과제

과제 1 — 코드 대응표 만들기 (10분)

모듈 13(레슨 2)의 Playwright 로그인 예시와 이 레슨의 Selenium 예시를 한 줄씩 짝지어 표로 정리하고, Selenium에만 있는 줄이 왜 Playwright에는 없는지 한 문장씩 적는다.

과제 2 — 플래키 테스트 진단하기 (10분)

다음 Selenium 코드는 CI에서 열 번 중 두 번 정도 실패한다. 무엇이 문제이고 어떻게 고쳐야 하는지 적는다.

driver.get("https://example.com/products")
driver.find_element(By.CLASS_NAME, "add-to-cart").click()
assert driver.find_element(By.ID, "cart-badge").text == "1"
해설 보기

문제는 대기가 전혀 없다는 것 이다. 버튼이 아직 렌더링되지 않았는데 클릭할 수 있고, 배지는 비동기 응답 후 갱신되는데 코드는 클릭 직후 값을 읽어 갱신 전 값(0 또는 빈 문자열)을 볼 수 있다. 두 지점 모두에 조건 기반 대기를 넣는다.

wait = WebDriverWait(driver, 10)
wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "add-to-cart"))).click()
wait.until(EC.text_to_be_present_in_element((By.ID, "cart-badge"), "1"))

time.sleep(2)로 때우면 느린 날에는 여전히 실패하고 빠른 날에는 2초를 낭비한다 — 모듈 13(레슨 5)에서 배운 그대로다.

과제 3 — 도구 선택 판단하기 (5분)

Java 백엔드 개발자 5명인 팀이 사내 관리자 페이지의 회귀 테스트를 자동화하려 한다. Selenium과 Playwright 중 어느 쪽을 권할지 근거를 두 가지 이상 적는다.

자가 체크리스트

  • WebDriver가 W3C 표준 프로토콜이라는 점과, 그것이 넓은 언어·브라우저 지원으로 이어지는 이유를 설명할 수 있다
  • Selenium·Playwright·Cypress가 브라우저와 연결되는 구조를 각각 구분할 수 있다
  • Playwright 코드의 각 줄을 Selenium의 대응하는 줄로 옮겨 읽을 수 있다
  • 암묵적 대기와 명시적 대기(WebDriverWait)의 차이를 설명할 수 있다
  • Selenium 테스트가 플래키해지는 흔한 원인을 코드에서 지목할 수 있다

흔한 실수

  • 오래된 도구라서 배울 가치가 없다고 단정한다. 실무에서 만날 확률로 따지면 Selenium이 가장 높다. 도구의 나이는 우열의 기준이 아니다.
  • 대기가 필요한 자리에 time.sleep()을 쓴다. 느린 환경에서는 여전히 실패하고 빠른 환경에서는 시간을 버린다. WebDriverWait으로 바꾼다.
  • 암묵적 대기를 걸어놨으니 안전하다고 믿는다. 요소가 DOM에 있는지까지만 볼 뿐이고, 명시적 대기와 섞어 쓰면 예측하기 어려운 대기가 생긴다.
  • driver.quit()을 빼먹는다. CI 실행기에 브라우저 프로세스가 쌓여 나중 테스트까지 느려진다. teardown(모듈 10 레슨 1)에 넣는다.

참고 자료

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

HTTP — 하이퍼텍스트 전송 규약
웹에서 요청과 응답을 주고받는 약속. "무엇을 달라"고 보내면 "여기 있다" 또는 "그런 건 없다"가 돌아온다.
JS — 자바스크립트
웹 화면을 움직이게 만드는 프로그래밍 언어. 버튼을 눌렀을 때 일어나는 일을 적는다.
AAA — 준비-실행-확인
자동화 시험 코드를 세 덩어리로 나눠 쓰는 관습 — 상황을 만들고, 한 번 실행하고, 결과를 확인한다.
DOM — 문서 객체 모델
웹 화면을 프로그램이 다룰 수 있게 나무 구조로 바꿔 놓은 것. 화면의 현재 상태를 담고 있다.
CI — 지속적 통합
코드를 올릴 때마다 자동으로 합쳐서 빌드하고 시험까지 돌리는 방식.
ROI — 투자 수익률
들인 돈과 시간에 비해 얼마를 돌려받았는지.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.

용어집 전체 보기

이 레슨의 실습

실습은 별도 저장소에서 진행합니다. 새 탭으로 열립니다.