모바일 앱 자동화 테스트에서 화면의 스크롤 또는 스와이프 동작은 매우 빈번하게 사용되는 기능입니다. Appium + Python 환경에서는 이러한 제스처를 scroll()과 swipe() 두 가지 주요 메서드로 구현할 수 있습니다. 각각의 목적과 사용법은 다릅니다.
scroll() 메서드: 요소 간 스크롤 이동
scroll()은 특정 UI 요소에서 다른 요소로 시각적으로 이동하는 동작을 수행합니다. 이는 실제 스크롤바 조작이 아니라, 두 요소의 위치를 기반으로 장치가 자동으로 스크롤을 실행하는 방식입니다.
def scroll_between_elements(self, start_locator, end_locator, duration_ms=None):
"""
두 웹 요소 사이를 스크롤합니다.
:param start_locator: 시작 요소의 찾기 전략 (튜플 형식, 예: (By.ID, "id"))
:param end_locator: 도착 요소의 찾기 전략
:param duration_ms: 스크롤 지속 시간 (밀리초)
"""
try:
source_element = self.wait_for_element(start_locator)
target_element = self.wait_for_element(end_locator)
self.driver.scroll(source_element, target_element, duration_ms)
except Exception as e:
print(f"스크롤 중 오류 발생: {e}")
여기서 wait_for_element()은 명시적 대기를 적용하여 요소가 나타날 때까지 기다리는 커스텀 메서드입니다. 이를 통해 요소가 로드되지 않아 인식되지 않는 문제를 방지할 수 있습니다.
swipe() 메서드: 좌표 기반 화면 제스처
swipe()은 화면 상의 절대 또는 상대 좌표를 기반으로 드래그 동작을 수행합니다. 따라서 스크롤 거리나 방향을 정밀하게 제어할 수 있습니다.
수직 스와이프 (위/아래)
def swipe_vertical(self, direction='down', duration=1000):
"""
화면을 수직 방향으로 스와이프합니다.
:param direction: 'up' 또는 'down'
:param duration: 제스처 지속 시간 (ms)
"""
screen_size = self.driver.get_window_size()
width_center = int(screen_size['width'] * 0.5)
if direction == 'down':
start_y = int(screen_size['height'] * 0.3)
end_y = int(screen_size['height'] * 0.7)
else: # up
start_y = int(screen_size['height'] * 0.7)
end_y = int(screen_size['height'] * 0.3)
self.driver.swipe(width_center, start_y, width_center, end_y, duration)
수평 스와이프 (좌/우)
def swipe_horizontal(self, direction='left', duration=1000):
"""
화면을 수평 방향으로 스와이프합니다.
:param direction: 'left' 또는 'right'
:param duration: 제스처 지속 시간 (ms)
"""
screen_size = self.driver.get_window_size()
height_center = int(screen_size['height'] * 0.5)
if direction == 'left':
start_x = int(screen_size['width'] * 0.8)
end_x = int(screen_size['width'] * 0.2)
else: # right
start_x = int(screen_size['width'] * 0.2)
end_x = int(screen_size['width'] * 0.8)
self.driver.swipe(start_x, height_center, end_x, height_center, duration)
특정 UI 요소 내에서 스와이프
전체 화면이 아닌 특정 뷰(예: 리스트뷰, 리사이클러뷰) 내부에서만 스와이프를 수행해야 할 경우도 있습니다. 아래 메서드는 요소의 경계 내에서 상대 좌표를 계산하여 제스처를 적용합니다.
def swipe_within_element(self, locator, swipe_type='down', duration=1000):
"""
특정 요소 내부에서 스와이프 동작을 수행합니다.
:param locator: 대상 요소의 위치자
:param swipe_type: 'up', 'down', 'left', 'right'
:param duration: 제스처 지속 시간
"""
element = self.wait_for_element(locator)
location = element.location
size = element.size
center_x = location['x'] + size['width'] // 2
center_y = location['y'] + size['height'] // 2
if swipe_type == 'down':
start_y = int(location['y'] + size['height'] * 0.4)
end_y = int(location['y'] + size['height'] * 0.6)
self.driver.swipe(center_x, start_y, center_x, end_y, duration)
elif swipe_type == 'up':
start_y = int(location['y'] + size['height'] * 0.6)
end_y = int(location['y'] + size['height'] * 0.4)
self.driver.swipe(center_x, start_y, center_x, end_y, duration)
scroll() vs swipe(): 핵심 차이점
- scroll(): 요소 기반. 애플리케이션의 UI 계층 구조를 이해하고 있으며, 두 요소 사이를 자연스럽게 스크롤합니다. 하지만 스크롤 거리 조절이 불가능하며, 요소가 반드시 존재해야 합니다.
- swipe(): 좌표 기반. 물리적인 제스처를 시뮬레이션하므로 더 많은 제어가 가능하지만, 해상도나 디바이스 크기에 따라 동작이 달라질 수 있어 상대 좌표 비율 사용이 권장됩니다.
따라서 일반적인 탐색에는 scroll()을, 정밀한 제어나 커스텀 뷰 제어에는 swipe()을 사용하는 것이 적절합니다.