파이썬 Selenium에서 unexpected alert open 오류가 발생하면, 먼저 WebDriver 동작을 막고 있는 JavaScript alert·confirm·prompt를 확인해야 합니다. 해결의 핵심은 alert가 나타나는 흐름에서는 alert_is_present()로 기다린 뒤 텍스트를 확인하고 accept() 또는 dismiss()를 실행하는 것입니다. 알림이 선택적으로 나타나는 흐름이라면 같은 대기를 무조건 성공한다고 가정하지 말고, 알림이 없을 때의 분기를 함께 두어야 합니다.
unexpected alert open은 왜 다음 동작을 막을까
JavaScript 알림은 일반 HTML 요소처럼 DOM에서 찾아 클릭하는 대상이 아닙니다. Selenium 공식 문서의 alert·confirm·prompt는 WebDriver가 별도 알림 객체로 다루며, 열려 있는 동안 추가 WebDriver 명령을 막을 수 있습니다. 그래서 화면 캡처에는 알림이 잘 드러나지 않거나 headless 실행에서 보이지 않더라도, 그 다음 click(), 페이지 이동, 캡처 같은 명령에서 UnexpectedAlertPresentException이 나타날 수 있습니다.
이 오류를 단순히 예외가 발생한 줄의 클릭 문제로 보면 처리 순서가 꼬입니다. 알림을 먼저 처리해야 하는 상태인지, 애초에 알림이 발생하지 않는 선택적 흐름인지부터 구분하는 편이 안전합니다.
먼저 alert 텍스트를 읽고 처리하기
알림이 반드시 발생하는 동작이라면 고정된 time.sleep()보다 알림 상태를 조건으로 기다리는 편이 낫습니다.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def handle_required_alert(driver, timeout=5):
alert = WebDriverWait(driver, timeout).until(EC.alert_is_present())
message = alert.text
if "삭제" in message:
alert.dismiss()
else:
alert.accept()
return message
EC.alert_is_present()는 alert가 나타날 때까지 확인하고 해당 Alert로 전환하는 조건입니다. 전환한 뒤 text로 내용을 읽고, 확인 흐름이면 accept(), 취소나 거부 흐름이면 dismiss()를 선택합니다. confirm은 확인·취소 선택을 제공하고 prompt는 필요할 때 send_keys()로 입력값을 넣은 뒤 처리할 수 있습니다.
알림 문구를 확인하지 않고 언제나 accept()부터 호출하면 삭제나 취소처럼 업무 결과가 달라지는 동작을 잘못 진행할 수 있습니다. 텍스트가 예상 범위와 다르면 자동으로 진행하지 않고 상위 흐름에 판단을 넘기는 방식도 고려할 수 있습니다.
alert가 항상 나타나지 않을 때의 분기
페이지 상태나 입력 조건에 따라 알림이 생길 수도, 생기지 않을 수도 있다면 대기 타임아웃을 정상적인 분기로 다뤄야 합니다.
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def handle_optional_alert(driver, timeout=3):
try:
alert = WebDriverWait(driver, timeout).until(EC.alert_is_present())
except TimeoutException:
return None
message = alert.text
alert.accept()
return message
알림이 없을 때 driver.switch_to.alert를 바로 실행하면 NoAlertPresentException이 발생할 수 있습니다. 선택적 알림이라면 알림 없음으로 보고 다음 단계로 갈지, 반드시 떠야 하는 알림이라면 타임아웃을 원인 조사 대상으로 남길지 업무 흐름에 맞춰 정해야 합니다.
처리 후에는 페이지 요소를 다시 기다리기
알림을 닫았다고 해서 직전 페이지 상태가 곧바로 준비되었다고 볼 수는 없습니다. 알림 처리 뒤 페이지가 갱신되거나 다음 화면으로 넘어가는 흐름이라면 필요한 요소를 다시 명시적으로 기다린 후 다음 WebDriver 명령을 실행합니다.
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
handle_required_alert(driver)
next_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "next-button"))
)
next_button.click()
next-button은 실제 페이지의 식별자로 바꿔야 합니다. 알림 처리 후 기다릴 대상은 사이트별 DOM과 동작에 따라 달라지므로 같은 요소나 대기 조건을 복사해서는 안 됩니다. 화면이 다시 그려지는 흐름에서는 이전 요소 참조를 그대로 쓰기보다 필요한 요소를 다시 찾는 편이 안전합니다.
고정 sleep과 강제 제거를 피해야 하는 이유
time.sleep()은 알림이 열렸는지를 확인하지 못합니다. 너무 짧으면 여전히 알림이 열려 있어 같은 예외가 반복되고, 너무 길면 알림이 없는 정상 흐름까지 늦어집니다. WebDriver가 제공하는 조건 대기는 실제 상태를 기준으로 다음 동작을 진행하게 해 줍니다.
JavaScript로 알림을 무력화하거나 예외를 무조건 삼키는 방법도 근본적인 해결이 아닙니다. 그런 우회는 사용자가 확인하거나 취소해야 하는 업무 흐름을 잃게 만들고, 오류 원인과 실제 결과를 로그에서 구분하기 어렵게 할 수 있습니다. 먼저 알림의 발생 조건과 텍스트를 확인한 뒤, 자동 처리가 허용되는 문구만 명시적으로 처리하는 것이 운영에 맞는 순서입니다.
적용 전 확인할 체크 순서
- 예외가 발생한 WebDriver 명령 직전에 alert·confirm·prompt를 여는 동작이 있는지 확인합니다.
- 반드시 나타나는 알림인지 선택적으로 나타나는 알림인지 구분합니다.
- 반드시 나타난다면
alert_is_present()로 기다린 뒤text를 기록합니다. - 결과에 따라
accept()와dismiss()를 선택하고, prompt 입력이 필요하면send_keys()를 먼저 사용합니다. - 선택적 알림이면 알림 없음 분기를 두고
NoAlertPresentException이 정상 흐름에 섞이지 않게 합니다. - 처리 후 다음 요소나 페이지 상태를 다시 기다린 다음 클릭·이동·캡처를 진행합니다.
이 순서를 적용하면 unexpected alert open을 단순히 숨기는 대신, 알림이 WebDriver 동작을 막은 것인지와 실제 사용자 선택이 무엇이어야 하는지를 나누어 판단할 수 있습니다. headless 환경에서 알림이 캡처에 보이지 않는 경우에도 화면의 모양보다 WebDriver의 alert 상태와 텍스트를 진단 기준으로 삼는 것이 적절합니다.