Python Selenium을 이용한 iframe 내 UI 요소 조작 및 기본 페이지 복귀

웹 애플리케이션 자동화 시나리오에서 특정 작업을 수행하려면 여러 단계를 거쳐야 하는 경우가 많습니다. 예를 들어, 메인 페이지(A)에서 특정 항목을 선택하고 '서명' 버튼을 클릭하면, 서명 관련 내용을 담은 팝업 창(B)이 나타날 수 있습니다. 이 팝업 창은 종종 iframe으로 구현되어 있어, 메인 페이지의 DOM과는 별도로 관리됩니다.

이러한 iframe 내부에 위치한 요소(예: '동의' 버튼)를 직접 조작하려고 하면, Selenium은 해당 요소를 찾지 못해 오류를 발생시킵니다. 따라서 iframe 내부의 요소를 제어하기 위해서는 먼저 해당 iframe으로 전환(switch)하는 과정이 필수적입니다. iframe으로 전환한 후에야 내부 요소에 대한 접근 및 조작이 가능해집니다.

iframe으로 전환하는 코드는 다음과 같습니다:


from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def transition_to_iframe(driver, iframe_locator, module_name):
    """
    중첩된 iframe 내의 요소를 조작하기 위해 해당 iframe으로 전환합니다.

    :param driver: Selenium WebDriver 인스턴스
    :param iframe_locator: 전환할 iframe을 식별하는 locator (예: By.ID, By.NAME, By.XPATH)
    :param module_name: 현재 작업 중인 모듈명 (로깅용)
    :return: None
    """
    print(f"'{module_name}' 모듈의 '{iframe_locator}' iframe으로 전환 중...")
    WebDriverWait(driver, timeout=10).until(
        EC.frame_to_be_available_and_switch_to_it(iframe_locator)
    )

다음은 iframe 전환 및 기본 페이지 복귀를 포함한 전체 UI 자동화 작업 흐름입니다:


import time
# logger, sf (selector definitions), and other necessary imports assumed

class WebDriverActions:
    def __init__(self, driver):
        self.driver = driver
        # ... other initializations

    def _wait_for_element_visibility(self, loc, timeout=8, poll_frequency=0.2):
        """지정된 locator로 요소가 보일 때까지 대기합니다."""
        try:
            return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.visibility_of_element_located(loc)
            )
        except Exception:
            return None

    def _scroll_and_click(self, loc, model=""):
        """요소를 화면으로 스크롤한 후 클릭합니다."""
        element = self._wait_for_element_visibility(loc)
        if element:
            # JavaScript를 사용하여 스크롤 및 클릭
            self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
            time.sleep(0.5) # 스크롤 후 잠시 대기
            element.click()
            print(f"'{model}' 작업 완료.")
        else:
            print(f"'{model}' 요소를 찾을 수 없습니다.")


    def _transition_to_iframe(self, iframe_locator, module_name):
        """iframe으로 전환하는 내부 메서드."""
        print(f"'{module_name}' 모듈의 '{iframe_locator}' iframe으로 전환 중...")
        WebDriverWait(self.driver, timeout=10).until(
            EC.frame_to_be_available_and_switch_to_it(iframe_locator)
        )

    def _switch_to_default_content(self):
        """iframe에서 빠져나와 기본 HTML 페이지로 전환합니다."""
        print("기본 HTML 페이지로 전환합니다.")
        self.driver.switch_to.default_content()

    def perform_signing_operation(self, shop_name):
        '''
        [서명 작업 수행] 통신사 계약서 팝업 출현 여부 확인, 출현 시 서명 절차 진행.
        서명 작업은 iframe 내부에 존재하므로 iframe으로 전환 후 작업 수행.
        한 매장 계약 완료 후, 팝업을 닫고 기본 페이지로 복귀하여 다음 계약을 준비.
        '''
        # 통신사 계약 동의 절차 관련 요소 대기
        agreement_button_loc = (By.XPATH, "//button[text()='동의하기']") # 예시 locator
        agreement_popup_loc = (By.ID, "agreement-iframe") # 예시 iframe locator
        agree_checkbox_loc = (By.CSS_SELECTOR, "input[type='checkbox']") # 예시 locator
        close_popup_loc = (By.CLASS_NAME, "close-button") # 예시 locator

        if self._wait_for_element_visibility(agreement_button_loc, timeout=8):
            # 계약 동의 iframe으로 전환
            self._transition_to_iframe(iframe_locator=agreement_popup_loc, module_name="계약 동의 iframe 전환")
            try:
                # iframe 내부의 동의 체크박스 대기 및 클릭
                checkbox_element = self._wait_for_element_visibility(agree_checkbox_loc, timeout=8)
                if checkbox_element:
                    checkbox_element.click()
                    print(f"'{shop_name}' - 계약 동의 체크박스 클릭 완료.")
                else:
                     print(f"'{shop_name}' - 계약 동의 체크박스를 찾을 수 없습니다.")

                # 실제 동의 버튼 클릭 (필요시)
                # self._scroll_and_click(loc=agreement_button_loc, model="계약 동의 버튼 클릭")

            except Exception as e:
                # 이미 계약 기록이 있는 매장의 경우 예외 처리
                error_message_loc = (By.CLASS_NAME, "error-message") # 예시 locator
                error_text = self.driver.find_element(*error_message_loc).text if self._wait_for_element_visibility(error_message_loc) else "알 수 없는 오류"
                print(f"'{shop_name}' - 계약 실패: {error_text}. 오류 상세: {e}")
            
            time.sleep(1)
            # iframe에서 빠져나와 기본 페이지로 복귀
            self._switch_to_default_content()
            # 필요 시, 메인 페이지 상단으로 스크롤 및 팝업 닫기
            # self._scroll_and_click(loc=agreement_button_loc, model="계약 페이지 상단으로 이동") # 예시
            self._scroll_and_click(loc=close_popup_loc, model="통신사 팝업 닫기")
        else:
            print("계약 관련 팝업이 나타나지 않았습니다.")

iframe 조작 후, 기본 HTML 페이지 컨텍스트로 다시 돌아오는 코드는 다음과 같습니다:


from selenium.webdriver.remote.webdriver import WebDriver

def return_to_default_content(driver: WebDriver):
    """
    iframe에서의 모든 조작이 완료된 후, 기본 HTML 페이지 컨텍스트로 복귀합니다.
    현재 활성화된 iframe이 어떤 레벨에 있든 상관없이, 이 메서드를 한 번 호출하면
    최상위 기본 HTML 페이지로 돌아갑니다.

    :param driver: Selenium WebDriver 인스턴스
    :return: None
    """
    print("기본 HTML 페이지로 전환합니다.")
    driver.switch_to.default_content()

태그: selenium python webdriver iframe ui automation

9월 14일 01:33에 게시됨