Selenium・Playwright로 테스트 자동화를 시작하면 “요소를 찾을 수 없다”, “로케이터 작성 방법을 모르겠다”는 벽에 부딪히는 경우가 많습니다. 그 근본적인 원인의 대부분은 HTML을 읽는 방법을 모르는 것에 있습니다. 이 글에서는 QA 엔지니어가 테스트 자동화에 필요한 HTML 기초 지식을 실례와 함께 알기 쉽게 해설합니다.
💡 용어 해설:「로케이터」란? Selenium・Playwright에서 페이지 상의 요소를 특정하기 위한 지정 방법 전체를 로케이터라고 합니다. CSS 셀렉터나 XPath도 로케이터의 일종입니다. 이 글에서는 「셀렉터」라고 쓰이는 경우도 있지만, 도구 사양상 로케이터로 통일합니다. |
📌 이 글의 대상 독자
| ✅ Selenium・Playwright를 막 시작해서 HTML을 거의 읽어본 적이 없는 분 |
| ✅ 로케이터를 작성할 때 무엇을 단서로 삼아야 할지 막막한 분 |
| ✅ DevTools에서 HTML을 봐도 어디를 봐야 할지 모르는 분 |
| ✅ HTML 경험은 없지만 QA・테스트 자동화에 도전하고 싶은 분 |
✅ 이 글을 읽으면 얻을 수 있는 것
|
👨💻 이 글의 신뢰성에 대해 본 글은 QA 엔지니어로서 15년 이상 Selenium・Playwright를 활용한 테스트 자동화에 종사해 온 필자가 작성했습니다. “HTML을 몰라서 첫 로케이터조차 쓸 수 없었다”는 실제 경험을 바탕으로, 테스트 자동화에 정말 필요한 지식만 추려 해설합니다. |
📌 이 글의 결론
|
테스트 자동화 코드를 작성할 때 반드시 등장하는 것이 「요소 특정」입니다. Selenium이라면 driver.find_element(By.ID, "email"), Playwright라면 page.get_by_test_id("login-btn")처럼 작성하는데, 이 "email"이나 "login-btn"이라는 값은 어디서 오는 걸까요?
답은 HTML 안에 있습니다. 테스트 자동화 도구는 HTML 요소에 붙은 속성을 단서로 요소를 찾습니다. 그렇기 때문에 “HTML을 읽을 수 있는지”가 테스트 자동화 습득 속도에 직결됩니다. 웹 개발 지식이 없어도 괜찮습니다. QA로서 필요한 최소한의 지식만을 실제 예시로 확인해 봅시다.
HTML의 기본 구조:태그와 속성이란?
HTML�� 웹 페이지의 골격을 만드는 언어입니다. 테스트 자동화 도구가 「버튼을 누른다」「입력란에 문자를 입력한다」는 조작을 할 때, 그 요소를 HTML 속성을 단서로 찾습니다. DevTools에서 어떤 속성을 확인하면 좋은지는 Chrome DevTools 입문도 참고하세요.
태그의 기본
HTML�� 태그(<〜>)라는 부품의 조합으로 구성됩니다. 대부분의 태그는 「여는 태그」와 「닫는 태그」가 쌍을 이룹니다.
<button>로그인</button>
<p>이 문장이 단락입니다.</p>
<input type="text">속성이란?
태그에는 속성을 추가할 수 있습니다. 속성은 「그 요소의 추가 정보」를 나타내며, 속성명="값"의 형태로 여는 태그 안에 씁니다.
<input type="email" id="user-email" name="email" placeholder="이메일 주소">
이 속성의 값이 바로 테스트 자동화에서 로케이터를 작성할 때의 단서가 됩니다.
테스트 자동화에서 중요한 속성 5가지
HTML��는 많은 속성이 있지만, 테스트 자동화에서 사용하는 속성은 한정되어 있습니다. 다음 5가지를 파악하면 대부분의 로케이터를 작성할 수 있습니다.
| 속성명 | 역할 | 로케이터에서의 사용법 | 안정성 |
|---|---|---|---|
id | 페이지 내 고유 식별자 | By.ID / #email | ⭐⭐⭐ 최우선 |
data-testid | 테스트 전용 식별자 | [data-testid='xxx'] | ⭐⭐⭐ 최우선 |
name | 폼 전송 시 키 이름 | By.NAME / [name='email'] | ⭐⭐ 비교적 안정 |
class | 스타일 그룹명 | By.CLASS_NAME / .btn-primary | ⭐ 디자인 변경으로 깨지기 쉬움 |
type | input의 종류 지정 | [type='submit'] | ⭐⭐ 용도가 명확한 경우에 유효 |
① id 속성:가장 신뢰할 수 있는 식별자
id는 페이지 내에서 1개만 사용할 수 있다는 규칙이 있어 로케이터로서 가장 안정적입니다.
<input id="login-email" type="email">
<button id="login-btn">로그인</button># Selenium
driver.find_element(By.ID, "login-email")
driver.find_element(By.CSS_SELECTOR, "#login-btn")
# Playwright
page.locator("#login-email")
page.locator("#login-btn")② data-testid 속성:테스트 전용 최강 식별자
data-testid는 테스트를 위해 개발자가 의도적으로 추가하는 속성입니다. 디자인 변경이나 기능 수정의 영향을 받기 어렵고, 테스트 자동화에서 가장 깨지기 어려운 로케이터로 권장됩니다.
<button data-testid="submit-button" class="btn btn-primary btn-lg">
제출하기
</button># Selenium(CSS 셀렉터로 취득)
driver.find_element(By.CSS_SELECTOR, "[data-testid='submit-button']")
# Playwright(전용 메서드 사용)
page.get_by_test_id("submit-button")💡 data-testid가 없을 때는 어떻게 하나요? 담당 개발자에게 “테스트용으로 실무에서는 id가 존재하지 않는 화면도 드물지 않습니다. 그럴 때는 data-testid 추가 요청이 첫 번째 선택지가 됩니다. 프로젝트 초기부터 개발자와 「테스트 속성 추가 규칙」을 정해 두면 나중이 편해집니다. |
💡 HTML 태그에는 「역할(role)」이 있다 |
③ class 속성:편리하지만 깨지기 쉽다
class는 스타일(외형)을 적용하기 위한 그룹명입니다. 여러 요소에 같은 class를 붙일 수 있어 로케이터로서는 불안정해지기 쉽다는 단점이 있습니다.
<button class="btn btn-primary btn-lg disabled">제출하기</button>
↑ 디자인 변경·상태 변화로 바뀌기 쉬움
<button class="MuiButton-root css-1abcde">제출</button> <!-- Material UI -->
<button class="chakra-button">제출</button> <!-- Chakra UI -->
<button class="Button_btn__abc123">제출</button> <!-- CSS Modules -->
↑ 빌드할 때마다 바뀔 가능성 있음⚠️ class는 최후의 수단으로 생각하자 |
테스트 자동화에서 자주 사용하는 폼 요소
웹 앱 테스트에서 가장 많이 등장하는 것이 폼 요소입니다. 로그인・회원가입・검색 등 대부분의 조작이 폼을 통해 이루어집니다.
폼 요소와 로케이터 대응표
| HTML 태그 | 화면 표시 | Selenium 코드 예시 | Playwright 권장 |
|---|---|---|---|
<input type="text"> | 텍스트 입력란 | find_element(By.ID, "...").send_keys("값") | get_by_label("라벨명") |
<input type="email"> | 이메일 입력란 | find_element(By.ID, "...").send_keys("값") | get_by_label("이메일") |
<input type="password"> | 비밀번호 입력란 (마스크) | find_element(By.ID, "...").send_keys("값") | get_by_label("비밀번호") |
<input type="checkbox"> | 체크박스 | find_element(By.ID, "...").click() | get_by_label("약관에 동의") |
<button> | 버튼 | find_element(By.ID, "...").click() | get_by_role("button", name="로그인") |
<select> | 드롭다운 | Select(element).select_by_value("값") | select_option("값") |
<textarea> | 여러 줄 텍스트 입력 | find_element(By.ID, "...").send_keys("값") | get_by_label("댓글") |
<a> | 링크 | find_element(By.LINK_TEXT, "텍스트") | get_by_role("link", name="텍스트") |
실전:로그인 폼의 HTML을 읽고 로케이터 작성하기
실제 HTML을 읽고 로케이터를 작성하는 흐름을 확인해 봅시다.
<form id="login-form">
<label for="email">이메일 주소</label>
<input type="email"
id="email"
name="email"
data-testid="login-email"
placeholder="예:user@example.com">
<label for="password">비밀번호</label>
<input type="password"
id="password"
name="password"
data-testid="login-password">
<button type="submit"
id="login-btn"
data-testid="login-submit">
로그인
</button>
</form>이 HTML에서 작성할 수 있는 로케이터 예시입니다.
# Selenium의 경우(id 우선)
email_input = driver.find_element(By.ID, "email")
password_input = driver.find_element(By.ID, "password")
login_button = driver.find_element(By.ID, "login-btn")
email_input.send_keys("test@example.com") # 샘플 URL
password_input.send_keys("password123")
login_button.click()
# Playwright의 경우① get_by_test_id(data-testid가 있는 경우)
page.get_by_test_id("login-email").fill("test@example.com") # 샘플 URL
page.get_by_test_id("login-password").fill("password123")
page.get_by_test_id("login-submit").click()
# Playwright의 경우② 시맨틱 로케이터(get_by_label / get_by_role)
page.get_by_label("이메일 주소").fill("test@example.com") # 샘플 URL
page.get_by_label("비밀번호").fill("password123")
page.get_by_role("button", name="로그인").click()로케이터를 선택할 때의 우선순위
Selenium의 경우:안정된 속성 기반 로케이터 우선
| ① id | → | ② data-testid | → | ③ name | → | ④ CSS | → | ⑤ XPath |
왼쪽일수록 안정 · 오른쪽일수록 깨지기 쉬움
Playwright의 경우:요소 종류에 따라 구분해서 사용
Playwright의 방식은 고정된 절대 순위가 아니라, 요소의 종류에 맞는 로케이터를 선택하는 것이 기본 방침입니다. 적절한 선택은 상황에 따라 달라집니다.
| 요소 종류 | 권장 로케이터 | 예시 |
|---|---|---|
| 버튼・링크 등 인터랙티브 요소 | get_by_role() | get_by_role("button", name="로그인") |
| label이 있는 입력 폼 | get_by_label() | get_by_label("이메일 주소") |
| data-testid가 있는 요소 | get_by_test_id() | get_by_test_id("login-submit") |
| 위의 방법으로 취득하기 어려운 경우 | locator() | locator("#email") |
💡 Playwright의 사고방식:사용자 시점으로 요소를 취득할 수 있는 로케이터를 우선합니다. 다만 get_by_test_id()가 더 안정적인 케이스도 많아서, 실무에서는 상황에 맞게 구분해서 사용하는 것이 현실적입니다. |
HTML을 읽을 때 자주 막히는 포인트
🚧 주의 포인트 목록 ① id가 동적으로 생성되어 매번 바뀐다 프레임워크에 따라서는 ② class가 여러 개 있어서 어떤 것을 사용해야 할지 모르겠다 ③ get_by_label()이 동작하지 않는다 ④ 같은 class명의 요소가 여러 개 있어 특정할 수 없다 ⑤ 버튼이 <button>이 아닌 <div>나 <span>으로 만들어져 있다 화면상 버튼이지만 HTML에서는 |
자주 묻는 질문 (FAQ)
브라우저의 Chrome DevTools (F12) → Elements 탭에서 확인할 수 있습니다. 확인하고 싶은 요소를 우클릭 → 「검사」를 선택하면 해당 요소의 HTML이 표시됩니다. 자세한 사용법은 Chrome DevTools 입문을 참고하세요.
테스트 자동화 목적이라면 본격적인 공부는 불필요합니다. 태그・속성・폼 요소의 기초와 DevTools에서 HTML을 읽는 감각을 익히면 충분합니다. 웹 제작자 수준의 지식은 필요 없습니다.
id는 페이지 내에서 1개만 사용할 수 있는 이름(개인 이름 같은 것), class는 여러 요소에 붙일 수 있는 그룹명(팀 이름 같은 것)입니다. 테스트 자동화에서는 id 쪽이 고유(유일)하므로 로케이터로서 신뢰할 수 있습니다.
기본적으로는 개발자(프론트엔드 엔지니어)가 추가합니다. QA 엔지니어가 “이 요소에 테스트용 data-testid를 추가해 주실 수 있나요?”라고 요청하는 형태가 일반적입니다. 테스트 품질 향상으로 이어지므로 많은 현장에서 흔쾌히 대응해 줍니다. 프로젝트 초기부터 개발자와 합의해 두면 원활합니다.
필수는 아니지만, 있으면 테스트가 크게 안정됩니다. id나 name을 사용할 수 있는 경우에는 그것을 사용하고, 없는 경우에는 개발자에게 data-testid 추가를 상담하는 것이 베스트 프랙티스입니다.
기본적인 사고방식은 같지만 작성법이 다릅니다. Selenium은 By.ID・By.CSS_SELECTOR 등 속성 기반이 중심이며, Playwright는 요소 종류에 따라 get_by_role()・get_by_label()・get_by_test_id()를 구분해서 사용하는 것이 권장됩니다. 두 도구 모두 HTML 속성을 단서로 한다는 점은 공통입니다.
Selenium에서는 CSS 셀렉터를 사용하는 경우가 많지만, Playwright에서는 get_by_role()이나 get_by_label() 같은 시맨틱 로케이터가 권장됩니다. CSS・XPath는 두 도구 모두에서 「위의 방법으로 취득하기 어려운 경우의 대체 수단」으로 사용합니다. 자세한 내용은 CSS 셀렉터・XPath 입문을 참고하세요.
정리
|
HTML�� 전부 외울 필요는 없습니다. “id・data-testid・name을 찾는다”,”폼 요소의 종류를 알고 있다”,이 2가지만으로 테스트 자동화의 첫 번째 벽을 크게 넘을 수 있습니다. HTML을 읽을 수 있게 되었다면 다음은 Chrome DevTools를 사용해 실제 페이지의 HTML을 조사해 봅시다. 셀렉터 검증까지 한 번에 할 수 있게 됩니다.
🗺️ HTML에서 테스트 자동화까지의 학습 맵
|
📚 함께 읽으면 좋은 관련 글 |
