파이썬 Selenium 중급 가이드 Page Object Model 설계와 명시적 대기 활용법
🚀 실무에서 통하는 POM 패턴으로 안정적이고 유지보수 쉬운 자동화 프레임워크 만들기
웹 자동화 테스트를 처음 시작할 때는 간단한 스크립트 작성만으로도 충분히 동작하는 것처럼 보입니다.
하지만 프로젝트 규모가 커지면 유지보수성과 확장성이 중요한 과제로 떠오르게 되죠.
특히 Selenium을 사용하다 보면 코드 중복이나 불필요한 대기 문제 때문에 성능 저하를 경험하기도 합니다.
이런 상황에서 해결책이 될 수 있는 방법이 바로 Page Object Model(POM) 설계 패턴입니다.
POM은 웹 페이지를 객체 단위로 추상화하여 테스트 코드와 UI 요소를 분리하는 방식입니다.
이 덕분에 테스트 스크립트가 보다 간결해지고, 변경에 대한 대응력이 크게 향상됩니다.
특히 명시적 대기를 클래스 내부에 내재화하면 불필요한 `sleep()` 함수 사용을 피하면서 안정적인 동작을 보장할 수 있습니다.
또한 단일 책임 원칙을 적용하면 클래스와 메서드가 각각 한 가지 역할에만 집중할 수 있어, 팀 단위 협업과 장기적인 유지보수에도 큰 장점을 줍니다.
이 글에서는 Python Selenium을 활용한 Page Object Model 설계를 중점적으로 다루며, 명시적 대기(Explicit Wait) 적용과 단일 책임 원칙을 어떻게 실무에 반영할 수 있는지 구체적으로 살펴봅니다.
또한 예제 코드와 함께 실제 프로젝트에서 어떻게 활용할 수 있는지도 설명하니, Selenium 자동화를 한 단계 발전시키고 싶은 분들에게 도움이 될 것입니다.
📋 목차
🔗 Page Object Model이란?
Page Object Model(POM)은 웹 자동화 테스트를 설계할 때 널리 활용되는 디자인 패턴으로, 테스트 코드와 UI 요소를 분리하여 유지보수성과 재사용성을 높이는 방법입니다.
쉽게 말해, 하나의 웹 페이지를 객체 단위로 정의해 각 페이지별로 클래스(Class)를 만들고, 그 안에 해당 페이지의 요소와 동작을 캡슐화하는 구조라고 할 수 있습니다.
예를 들어 로그인 페이지가 있다고 가정해 보겠습니다.
테스트 스크립트 안에 직접 `driver.find_element`를 반복해서 작성하는 대신, 로그인 페이지를 나타내는 클래스를 만들고 그 안에 `아이디 입력`, `비밀번호 입력`, `로그인 버튼 클릭`과 같은 메서드를 정의합니다.
이렇게 하면 테스트 코드에서는 LoginPage.login(“id”, “password”) 와 같이 직관적이고 간결하게 호출할 수 있게 됩니다.
- 📂UI 요소와 테스트 로직 분리
- ♻️중복 코드 제거 및 재사용성 향상
- 🛠️변경 사항 발생 시 유지보수 용이
POM의 핵심 장점은 테스트 코드가 단순해지고, 페이지 구조가 바뀌더라도 해당 페이지 클래스만 수정하면 된다는 점입니다.
따라서 대규모 프로젝트에서 UI가 자주 바뀌는 환경일수록 POM의 효과가 더욱 커집니다.
또한 협업 환경에서도 역할 분리가 명확해져, QA 엔지니어와 개발자가 효율적으로 협력할 수 있는 기반을 마련해 줍니다.
즉, Page Object Model은 테스트 코드의 가독성과 유지보수성을 동시에 확보할 수 있는 핵심적인 설계 패턴이며, Selenium 자동화를 본격적으로 활용하려는 단계에서 반드시 이해하고 적용해야 할 개념입니다.
🛠️ Python Selenium에서 POM 적용하기
Python 환경에서 Selenium을 사용할 때 Page Object Model(POM)을 적용하는 기본 방법은 각 웹 페이지를 클래스로 정의하는 것입니다.
각 클래스에는 해당 페이지의 요소를 찾는 locator와 그 요소들을 다루는 동작 메서드를 함께 정의합니다.
이 방식은 테스트 코드가 더 이상 UI 세부 사항에 의존하지 않고, 클래스 메서드를 호출하는 방식으로 동작을 수행할 수 있도록 도와줍니다.
예를 들어 로그인 페이지를 설계할 경우, 다음과 같은 구조로 구현할 수 있습니다.
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_input = (By.ID, "username")
self.password_input = (By.ID, "password")
self.login_button = (By.ID, "loginBtn")
def enter_username(self, username):
self.driver.find_element(*self.username_input).send_keys(username)
def enter_password(self, password):
self.driver.find_element(*self.password_input).send_keys(password)
def click_login(self):
self.driver.find_element(*self.login_button).click()
이제 테스트 코드는 다음처럼 간결하게 작성됩니다.
def test_login(driver):
login_page = LoginPage(driver)
login_page.enter_username("test_user")
login_page.enter_password("secure123")
login_page.click_login()
위 구조를 통해 알 수 있듯이 테스트 함수는 더 이상 UI 세부 사항에 직접 접근하지 않고, LoginPage 클래스가 제공하는 메서드만 호출합니다.
이로써 코드의 재사용성이 크게 높아지고, 페이지 구조가 바뀌더라도 수정 범위는 LoginPage 클래스 내부에 국한됩니다.
💡 TIP: POM 설계 시 각 페이지마다 전용 클래스를 두는 것이 이상적이며, 테스트 시나리오는 이 클래스를 조합해 작성하는 것이 가장 효율적입니다.
⚙️ 명시적 대기 내부화로 안정성 높이기
Selenium 자동화 과정에서 가장 흔히 발생하는 오류 중 하나는 요소를 찾지 못하는 문제입니다.
이는 웹 페이지 로딩 속도나 JavaScript 렌더링 시간 차이 때문에 발생하는데, 단순히 time.sleep()을 사용하는 방식은 비효율적일 뿐만 아니라 테스트 실행 속도까지 늦춰버립니다.
이 문제를 해결하기 위해서는 명시적 대기(Explicit Wait)를 사용하는 것이 필수적입니다.
명시적 대기는 특정 요소가 조건을 만족할 때까지 기다리도록 설정할 수 있어, 불필요한 지연을 줄이면서 안정적인 테스트 실행을 보장합니다.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def click_login(self):
login_button = self.wait.until(
EC.element_to_be_clickable(("id", "loginBtn"))
)
login_button.click()
위 코드처럼 POM 클래스 내부에 명시적 대기를 내재화하면, 테스트 코드에서는 단순히 login_page.click_login()만 호출해도 안정적으로 실행됩니다.
즉, 테스트 작성자는 대기 로직을 신경 쓸 필요가 없고, 페이지 객체가 모든 복잡성을 관리해주는 셈이죠.
💬 명시적 대기를 클래스 내부에 두면 코드의 일관성과 안정성이 높아지고, 예기치 못한 오류 발생을 크게 줄일 수 있습니다.
이 방식은 특히 AJAX 요청이나 동적 요소가 많은 현대 웹 애플리케이션 테스트에 효과적입니다.
각 페이지 클래스마다 공통적으로 사용할 수 있는 대기 유틸리티를 만들어두면 코드 재사용성과 가독성이 한층 더 강화됩니다.
🔌 단일 책임 원칙 적용 사례
Page Object Model(POM)을 올바르게 설계하려면 단일 책임 원칙(Single Responsibility Principle, SRP)을 반드시 고려해야 합니다.
단일 책임 원칙은 클래스가 단 하나의 역할만 담당해야 한다는 객체지향 프로그래밍의 기본 원칙으로, POM 설계 시 특히 중요한 요소입니다.
예를 들어 로그인 페이지 클래스라면 “로그인 동작”만 책임져야 하며, 회원가입이나 비밀번호 재설정 기능까지 모두 담아서는 안 됩니다.
이 경우 로그인 관련 변경 사항만 해당 클래스에서 수정하면 되고, 다른 기능에 영향을 주지 않으므로 유지보수가 훨씬 용이해집니다.
class LoginPage:
def __init__(self, driver):
self.driver = driver
def enter_username(self, username):
# 아이디 입력만 책임
pass
def enter_password(self, password):
# 비밀번호 입력만 책임
pass
def click_login(self):
# 로그인 버튼 클릭만 책임
pass
위 코드에서 각 메서드는 자기 역할에만 집중합니다.
아이디 입력, 비밀번호 입력, 로그인 버튼 클릭이 서로 독립적이므로, 한 부분의 수정이 다른 동작에 영향을 주지 않습니다.
이처럼 SRP를 적용하면 테스트의 견고성과 유지보수성이 동시에 향상됩니다.
⚠️ 주의: 단일 책임 원칙을 무시하고 한 클래스에 여러 기능을 몰아넣으면 코드가 복잡해지고, 작은 수정에도 예기치 못한 오류가 발생할 위험이 큽니다.
또한 SRP는 협업 환경에서도 큰 장점을 가집니다.
여러 명의 개발자나 QA 엔지니어가 동시에 작업할 때 충돌 가능성을 줄이고, 각자가 담당하는 클래스의 책임 범위가 명확해집니다.
이는 곧 효율적인 테스트 프레임워크 운영으로 이어지게 됩니다.
💡 유지보수성과 확장성을 높이는 전략
Page Object Model(POM)을 적용했다고 해서 자동으로 완벽한 설계가 보장되는 것은 아닙니다.
장기적으로 효율적인 프레임워크를 만들기 위해서는 유지보수성과 확장성을 고려한 추가 전략이 필요합니다.
특히 Selenium 기반 프로젝트에서는 테스트 케이스가 점점 많아지고, 페이지 요소가 바뀌는 일이 잦기 때문에 구조적인 관리 방법이 필수적입니다.
첫 번째 전략은 공통 컴포넌트 추출입니다.
예를 들어 여러 페이지에서 공통으로 사용되는 헤더, 푸터, 네비게이션 바 등을 별도의 클래스로 분리하면 중복 코드를 줄이고 수정 시 일관성을 유지할 수 있습니다.
- 🧩공통 UI 컴포넌트는 별도의 클래스/모듈로 분리
- 🛠️Utility 클래스 활용해 대기, 스크린샷, 로깅 등 공통 기능 관리
- 📈테스트 데이터 관리 방식을 표준화하여 유지보수 용이성 확보
두 번째 전략은 유틸리티 계층 도입입니다.
명시적 대기, 스크린샷 저장, 로깅 기능 등 자주 반복되는 기능들을 전용 유틸리티 모듈로 만들어 두면, 각 페이지 클래스에서 불필요한 중복을 피할 수 있습니다.
세 번째 전략은 테스트 데이터 관리의 체계화입니다.
하드코딩된 데이터 대신 JSON, YAML, 혹은 데이터베이스를 활용하면 변경이 발생했을 때 코드 수정 없이 데이터만 교체할 수 있어 확장성과 유연성이 크게 향상됩니다.
💎 핵심 포인트:
POM 설계는 단순히 페이지를 클래스화하는 것을 넘어, 공통 모듈화·유틸리티화·데이터 관리 최적화까지 고려해야 장기적으로 강력한 프레임워크가 완성됩니다.
이와 같은 전략을 꾸준히 적용하면 Selenium 기반 자동화 프로젝트의 수명이 길어지고, 새로운 기능 추가나 수정 시에도 최소한의 변경만으로 안정적으로 운영할 수 있습니다.
이는 곧 비용 절감과 품질 향상으로 이어지며, 팀 전체의 생산성 향상에도 크게 기여합니다.
❓ 자주 묻는 질문 (FAQ)
Page Object Model을 꼭 사용해야 하나요?
명시적 대기와 암묵적 대기의 차이는 무엇인가요?
단일 책임 원칙을 적용하지 않으면 어떤 문제가 생기나요?
POM에서 공통 요소는 어떻게 관리하나요?
테스트 데이터는 코드 안에 직접 작성해도 되나요?
Selenium 외에도 POM을 적용할 수 있나요?
명시적 대기를 어디에 적용하는 것이 좋을까요?
POM을 적용하면 테스트 속도가 느려지지 않나요?
📌 파이썬 Selenium POM 설계 핵심 정리
Python Selenium에서 Page Object Model(POM) 설계는 단순히 테스트 코드를 구조화하는 수준을 넘어, 실무 환경에서 유지보수성과 확장성을 확보하는 핵심 전략입니다.
특히 명시적 대기를 클래스 내부에 내재화하면 안정성이 크게 향상되고, 단일 책임 원칙을 적용하면 코드의 가독성과 협업 효율성이 함께 올라갑니다.
또한 공통 컴포넌트 분리, 유틸리티 계층 도입, 데이터 관리 체계화 같은 전략을 병행하면 장기적인 운영 비용 절감과 품질 향상까지 이룰 수 있습니다.
즉, Selenium 자동화 프로젝트를 한 단계 더 성숙하게 만들고 싶다면 POM은 반드시 익히고 적용해야 할 설계 패턴입니다.
🏷️ 관련 태그 : Selenium, 파이썬자동화, PageObjectModel, 테스트자동화, 명시적대기, 단일책임원칙, QA자동화, 웹테스트, 자동화프레임워크, 소프트웨어테스트