JAVA 웹개발 과정 국비 50일차 정리

50일차

※ 배우는 과정이므로 정확하지 않을 수 있습니다.
잘못된 점은 알려주시면 수정하도록 하겠습니다!


웹 자동화 도구인 Selenium에 대해 배웠다.

1. UI 테스트

1) UI(User Interface) 테스트란?

Frontend 기술로 만들어진 페이지를 테스트 하는 기술

  • 사전 필요 지식
    • 자바 객체지향 + 라이브러리 활용
    • HTML / CSS / JS
  • 테스트의 종류
    • 수동 테스트 : 사람이 페이지의 요소를 하나하나 테스트 하는 방식
    • 자동 테스트 : 개발자가 만들어낸 UI 테스트 어플리케이션으로 테스트 하는 방석
  • 테스트 도구
    • 네이버 기타 : 사진이미지를 이용 (진입장벽이 낮다. 장난감같은 느낌 - 추천하지 않음)
    • Selenium : 전세계적으로 유명한 UI 테스트 라이브러리

2) Selenium을 사용해보자!

① 환경설정

셀레니움을 이용하려면 라이브러리를 다운받아야 할 뿐만 아니라 브라우저 드라이버도 다운받아야한다.

  1. 구글에 selenium 검색
  2. 공식 사이트에 들어가서 Downloads 클릭
  3. 자바 그림 아래 4.0.0버전(21.11월기준)을 다운받는다.
  4. 라이브러리 폴더에 압축을 풀어서 넣어준다.
  5. 원하는 프로젝트에 라이브러리를 추가해준다.(selenium jar파일 뿐만 아니라 lib폴더에 들어있는 의존성 라이브러리 파일들도 모두 추가해줘야한다.)
  6. 구글에 selenium chromedriver 검색
  7. 다운로드 사이트 들어가기
  8. 내 브라우저 버전 찾기(우측 상단 … > 도움말 > Chrome 정보)
  9. 맞는 버전의 드라이버를 다운받는다.
  10. 압축을 풀어서 exe 파일을 프로젝트에 드래그앤드랍으로 넣어준다.(라이브러리 아래 exe 파일이 추가되면 성공)

② 사용 방법

ⓐ 셀레니움 인스턴스 생성

처음에 셀레니움 인스턴스를 생성해주는 것으로 시작한다.

WebDriver driver = new ChromeDriver(); 
ⓑ 셀레니움 이동시키기

그리고 만든 셀레니움 인스턴스를 get메소드로 이동시켜준다.
아래와 같이 get 메소드 안에 원하는 페이지 url을 넣으면 셀레니움은 그 페이지를 연다.

driver.get("원하는 페이지 url");
ⓒ 원하는 요소의 주소 찾기

findElement 또는 findElements 를 사용하여 원하는 요소의 주소를 변수에 담아준다.
findElements는 배열을 리턴하므로 주의한다.
By뒤에는 여러가지 메소드가 올 수 있는데, id나 class로 찾을 수도 있고, cssSelector를 이용하여 css선택자로 찾을 수도 있고, linkText로 text내용을 찾아서 요소를 가져올 수도 있다.

// id로 찾는 방법
WebElement inputSearch = driver.findElement(By.id("query"));
// class 이름으로 찾는 방법 (배열)
List<WebElement> list = driver.findElements(By.className("btn_import"));
WebElement writeToMe = list.get(0);
// css선택자로 찾는 방법
WebElement mailToMe = driver.findElement(By.cssSelector(".btn_workset>a:nth-child(2)"));
// text 내용으로 찾는 방법
WebElement mailToMe = driver.findElement(By.linkText("내게쓰기"));

이런 저런 방법을 써도 대상을 찾기 힘들 경우에 쓰는 최후의 방법으로는 xpath메소드가 있다.
원하는 요소의 코드에 우클릭 > 복사 > XPath 복사 > 붙여넣기 를 하면 대상의 경로를 가져올 수 있다.
편리해보이지만 추천하지 않는 이유는 검색속도가 가장 느리기 때문이다.

WebElement mailToMe = driver.findElement(By.xpath(//*[@id="NM_FAVORITE"]/div[1]/ul[1]/li[4]/a))
ⓓ 값 넣기

sendKeys메소드로 원하는 값을 파라미터에 넣으면 요소에 값을 쉽게 넣을 수 있다.
특정 사이트에서는 이 메소드로 값을 넣게되면 bot으로 인식하는 경우도 있으니 주의해야한다.

// 검색창 input 태그를 요소로 잡아 Selenium 검색
inputSearch.sendKeys("Selenium");

다른 방법으로는 자바스크립트를 이용하여 넣는 방법이 있다.
JavascriptExecutor는 셀레니움이 자바스크립트를 실행할 수 있게 지원하는 클래스이다.
executeScript메소드에 js로 요소의 value값을 선택한다.
arguments[index]는 쉽게 말하면 JDBC의 preparedStatement의 쿼리문의 ? 역할을 해준다고 생각하면 된다. 뒤쪽에 파라미터를 이어 붙이면 0번, 1번, 2번… 이렇게 값을 넣어줄 수 있다.
이 방식은 sendkeys처럼 특정 키를 눌러 입력하는 것으로 인식하지 않고, 복사-붙여넣기로 인식하기 때문에 bot으로 인식하지 못한다.

JavascriptExecutor script = (JavascriptExecutor)driver;
script.executeScript("document.getElementById('id').value=arguments[0]", "Test");
ⓔ 클릭하기

클릭은 click메소드를 이용하면 간단하다.

WebElement btnSearch = driver.findElement(By.id("search_btn"));
btnSearch.click();
ⓕ iframe

웹페이지를 개발자도구로 뜯어보다보면 iframe 태그로 감싸져있는 부분이 존재하는 경우가 있다.
iframe 태그는 내 페이지에 다른 페이지를 가져오게 해준다.
태그 자체가 보안적 위협을 많이 가지고 있어 다른 사이트를 가져오면 대부분 블락을 해놓은 경우가 많다.
따라서 보통은 다른 사람의 페이지보다는 내 페이지를 가져오는 식으로 사용한다.
이 태그는 html, body 태그가 또 들어있는 구조이기 때문에 셀레니움이 바깥쪽 페이지를 보고 있을 때 iframe 안쪽의 요소를 선택하면 id를 찾지 못하는 오류가 발생한다.
이때, switchTo().frame 메소드를 사용하여 셀레니움이 안쪽(iframe)을 보도록 만들어주면 된다.
그러나 마찬가지로 바깥쪽에 있는 요소는 건드릴 수 없게 된다.

driver.switchTo().frame("loginIframe");
ⓖ 셀레니움 대기하게 하기

컴퓨터의 성능에 따라 동작 후 로딩 시간이 걸리게 되는데, 로딩이 완료된 후에 셀레니움을 동작하게 하지 않으면 에러가 나기가 쉽다.
Thread.sleep(밀리초)로 코드의 실행 속도를 늦출 수는 있지만, 시간을 얼마나 줘야 로딩이 끝나는지, 에러인지 아닌지 판단하기는 쉽지 않다.
WebDriverWait 클래스는 시간을 적어주는 것이 아니라 어떤 상황이 될 때까지 셀레니움을 대기시킨다.
Duration.ofSeconds의 파라미터인 10은 대기가 10초를 넘기면 에러를 발생시키라는 뜻이다.
ExpectedConditions 뒤에는 무수히 많은 상황을 담은 메소드가 존재하니 필요한 메소드를 찾아서 적재적소에 활용하자!

WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(10));
wait.until(ExpectedConditions.persenceOfElementLocated(By.id("subject"));)

3) 자주 보는 오류

① NoSuchElementException

말 그대로 요소를 찾지 못해서 뜨는 에러이다.
원인은 크게 세가지로 볼 수 있다.

  1. 정말로 그 요소가 존재하지 않을 때, 또는 오타일 때
  2. iframe 안에 있는 요소를 검색했으나 셀레니움은 바깥의 페이지를 보고 있을 때
  3. 요소가 로딩 되기 전에 셀레니움이 요소를 먼저 찾으려고 시도했을 때

② ElementNotInteractableException

요소에 어떤 동작(클릭 등)을 적용하려고 했으나 그 요소가 숨어있거나 기타 다른 상황으로 인하여 동작을 진행할 수 없을 때 발생하는 에러이다.

③ StaleElementReferenceException

셀레니움을 쓰면서 많이 겪는 에러라고 한다. 잘 알아두어야겠다.
stale은 오래된, 낡은, 퀴퀴한 이라는 뜻으로, 직역하면 오래된 요소를 참조하려고 했음!!
즉, 지나간 페이지의 요소를 참조하려고 해서 생긴 에러이다.
예를 들면, 네이버 메인의 a 태그의 링크를 배열로 모은 리스트를 for문을 돌려가며 클릭하는 코드가 있을 때, 셀레니움은 첫번째 a 태그를 클릭하여 그 태그의 페이지로 이동할 것이다. 이때 원래 있었던 for문의 요소들은 오래된 요소가 되버리면서 이 에러가 발생하는 것이다.

4) 네이버 자동로그인 후 내게 메일 쓰기 - 예제

네이버 로그인 창에 아이디, 비밀번호를 쓰고 로그인을 한 뒤, 메일 페이지에 들어가서 내게 쓰기 버튼을 클릭한 후 제목에 “웹 자동화 연습 중 입니다…“를 써보는 실습을 했다.

import java.time.Duration;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class quiz02 {
	public static void main(String[] args) throws Exception {
		// 셀레니움 인스턴스 생성
		WebDriver driver = new ChromeDriver();
		// 셀레니움 대기시키는 인스턴스 생성
		WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(10));
		// 자바스크립트 실행 가능하게 하기
		JavascriptExecutor script = (JavascriptExecutor)driver;
		// 셀레니움 네이버 로그인 페이지로 이동
		driver.get("https://nid.naver.com/nidlogin.login?mode=form&url=https%3A%2F%2Fwww.naver.com");
		// 아이디, 비밀번호 입력 (js이용)
		script.executeScript("document.getElementById('id').value=arguments[0]", "id");
		script.executeScript("document.getElementById('pw').value=arguments[0]", "pw");
		// 로그인 버튼 클릭
		WebElement loginBtn = driver.findElement(By.id("log.login"));
		loginBtn.click();
		
		// '내게쓰기' 버튼을 찾을 때 까지 대기
		wait.until(ExpectedConditions.presenceOfElementLocated(By.id("subject")));
		// 제목에 "웹 자동화 연습 중 입니다..." 입력
		WebElement subject = driver.findElement(By.id("subject"));
		subject.sendKeys("웹 자동화 연습 중 입니다...");
		// 지연 시간 주기
		Thread.sleep(5000);
		// 셀레니움 종료
		driver.close();
	}
}

댓글남기기