용어집 · UI 자동화
StaleElementReferenceException
다른 이름: stale element, stale element reference, 스테일 엘리먼트, 스테일 요소 예외
정의
찾아 둔 WebElement의 DOM 노드가 재렌더링 등으로 제거·교체된 뒤 테스트가 그 요소를 사용할 때 Selenium이 던지는 예외입니다.
StaleElementReferenceException은 코드가 붙잡고 있는 요소가 더 이상 페이지에 존재하지 않는다는 뜻입니다. Selenium의 WebElement는 특정 DOM 노드 하나에 대한 참조입니다. 프레임워크가 목록을 다시 렌더링했거나, 페이지가 이동했거나, AJAX 업데이트로 영역이 교체되는 등의 이유로 애플리케이션이 그 노드를 제거하면 참조는 아무것도 가리키지 않게 되고, 그 참조로 무엇을 호출하든 이 예외가 발생합니다.
예시
WebElement firstRow = driver.findElement(By.cssSelector("table tbody tr"));
driver.findElement(By.id("sort-by-date")).click(); // table body is re-rendered
firstRow.getText(); // StaleElementReferenceException
화면에서는 똑같아 보이지만 이 요소는 새로 만들어진 노드입니다. 기존 참조는 이미 죽은 상태입니다.
해결 방법
예외를 잡아서 넘기지 말고, 요소가 필요한 시점에 다시 찾고 새 상태가 될 때까지 기다립니다.
By firstRow = By.cssSelector("table tbody tr");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement before = driver.findElement(firstRow);
driver.findElement(By.id("sort-by-date")).click();
// Wait until the old node is gone, then look up the new one
wait.until(ExpectedConditions.stalenessOf(before));
String text = driver.findElement(firstRow).getText();
그 밖에 도움이 되는 패턴은 다음과 같습니다.
- 페이지 객체에는
WebElement필드가 아니라By로케이터를 저장합니다. 각 메서드 안에서 요소를 찾습니다. - 값을 읽기 전에 업데이트가 끝날 때까지 기다립니다. 스피너가 사라지거나 행 수가 바뀌는 것을 기준으로 삼습니다.
- 오래 유지되는 목록을 피합니다. 재렌더링을 일으키는 항목을 클릭하면서
List<WebElement>를 순회하는 것은 이 오류의 대표적인 원인입니다. 반복할 때마다 인덱스나 고유 속성으로 다시 조회합니다.
Playwright에서 이 문제가 거의 없는 이유
Playwright의 Locator는 노드 참조가 아니라 쿼리입니다. 모든 액션과 어설션(assertion)마다 요소를 다시 찾으므로 단계 사이에 재렌더링이 일어나도 깨지지 않습니다. 다만 Playwright의 저수준 API인 ElementHandle은 여전히 분리된(detached) 노드를 가리킬 수 있으며, 이것이 공식 문서가 로케이터 사용을 권장하는 이유 중 하나입니다.
모든 호출을 stale 발생 시 재시도하는 루프로 감싸면 오류는 사라지지만 진짜 문제를 숨기게 됩니다. 진짜 문제는 테스트가 페이지가 안정될 때까지 기다리지 않는다는 점입니다.