지난 글에서 구상한 바에 이어
로그인 버튼 클릭
-> velog 로그인 창 열림
-> 사용자 로그인
-> 로그인 완료
-> 사용자 인증정보 가져옴
-> 3분 내 로그인 안됨
-> 창닫음
의 과정으로 Velog 로그인을 위한 Selenium Service
를 작성했다.
implementation 'org.seleniumhq.selenium:selenium-java:4.5.0'
implementation 'org.seleniumhq.selenium:selenium-http-jdk-client:4.5.0'
implementation 'org.seleniumhq.selenium:selenium-devtools-v112:4.9.0'
자바 11
사용중이기에 selenium은 4.5.0
이상의 버전을 사용해 주어야 한다.
devtools는 크롬 드라이버 버전 v112
에 맞게 의존성 추가해주었다.
public User process() throws JsonProcessingException {
System.setProperty("webdriver.http.factory", "jdk-http-client");
System.setProperty("webdriver.chrome.driver", "C:\\Users\\USER\\Downloads\\chromedriver_win32\\chromedriver.exe");
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER);
driver = new ChromeDriver(chromeOptions);
driver.get(URL);
driver.findElement(By.className(LOGIN_BUTTON_CLASS_NAME)).click();
...
}
System property
와 Driver
설정을 해주었다.
PageLoadStrategy.EAGER
ChromeOption에
PageLoadStrategy.EAGER
옵션을 주었는데 이 옵션을 주면 selenium은 로딩이 완료되기를 기다리지 않고 다음 작업을 수행한다.
Velog 메인페이지 로딩이 꽤나 걸리기 때문에 로그인 화면을 띄우기에 로딩 완료를 기다릴 필요가 없었다. 해당 옵션을 주고 로그인 화면 띄우기까지 약 3초정도 더 빨라졌다😊(다만 위 옵션을 주면 사용하고자하는 element가 로드 되었는지 확인을 해주어야 하는데 로그인버튼에 해당하는 element는 확인하지않아도 바로 로딩이 되는지 문제가 없었다..!)
public User process() throws JsonProcessingException {
...
driver = new ChromeDriver(chromeOptions);
driver.get(URL);
driver.findElement(By.className(LOGIN_BUTTON_CLASS_NAME)).click();
WebDriverWait webDriverWait = new WebDriverWait(driver, Duration.ofMinutes(3));
try
{
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.className(USER_PROFILE_CLASS_NAME)));
} catch (TimeoutException e) {
driver.quit();
throw new TimeoutException();
}
driver.quit();
return new User(getCurrentUser(), getAccessToken());
}
Velog 메인페이지로 브라우저를 띄우고 로그인버튼을 click()
한다.
여기서부터는 사용자가 직접 로그인을 진행하고 WebDriverWait
를 사용하여 최대 3분간 로그인이 완료되기를 대기한다.
로그인 완료의 기준은 몇가지로 둘 수 있겠지만
로그인이 완료되면 currentuser
과 accesstoken
정보를 가져와 User
객체를 반환한다.
findElement 속도
참고로 처음에 로그인화면을 띄우기까지 오래걸리는 부분이 메인페이지 로딩의 문제라고 생각못하고
findElement
의 속도 때문인가 싶어 알아봤었는데
By
의 여러 locator중에className
을 사용했는데 보통id
name
css
가 가장 빠르고xpath
가 가장느리다고 한다..!
명시적 대기 vs 암시적 대기
기존에는
while
문을 사용하여Thread.sleep(1000)
를 반복하며 localstorage에서CURRENT_USER
가 있는지를 확인하는 명시적 대기 방법을 사용했는데
중간에 창을 닫아버리면 발생하는NoSuchWindowException
이라던가 여러 예외처리를 해주어야했다.selenium 에서 제공하는
WebDriverWait
을 사용하니 간단하게 로그인 완료 확인과 3분의 제한시간을 줄 수 있었다. 예외 처리도TimeoutException
만 해주게 되어 편했다,,
가독성도 좋고 역시 라이브러리에 어떤기능을 제공하는지 살펴봐야..
예외처리
try catch
를 사용하고 싶지 않아서@ControllerAdvice
와@ExceptionHandler
로 전역예외 처리를 해주었는데TimeoutException
발생시에 창은 닫아주고 싶어서..
결국try catch
를 사용하여driver.quit()
하고 다시@ControllerAdvice
로 넘겨주었다. (맘에 안드는데 더 좋은 방법을 아직은 모르겠다)
private CurrentUser getCurrentUser() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
WebStorage storage = (WebStorage)driver;
LocalStorage localStorage = storage.getLocalStorage();
String currentUser = localStorage.getItem("CURRENT_USER");
return objectMapper.readValue(currentUser, CurrentUser.class);
}
CURRENT_USER
에 저장된내용을 deserialize 해주었다.
private String getAccessToken(){
return driver.manage().getCookieNamed("access_token").getValue();
}
쿠키도 가져올 수 있다.
@Service
public class SeleniumService{
private WebDriver driver;
private static final String URL = "https://velog.io/";
private static final String LOGIN_BUTTON_CLASS_NAME = "sc-bqiRlB";
private static final String USER_PROFILE_CLASS_NAME = "sc-fotOHu";
public User process() throws JsonProcessingException {
System.setProperty("webdriver.http.factory", "jdk-http-client");
System.setProperty("webdriver.chrome.driver", "C:\\Users\\USER\\Downloads\\chromedriver_win32\\chromedriver.exe");
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER);
driver = new ChromeDriver(chromeOptions);
driver.get(URL);
driver.findElement(By.className(LOGIN_BUTTON_CLASS_NAME)).click();
WebDriverWait webDriverWait = new WebDriverWait(driver, Duration.ofMinutes(3));
try
{
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.className(USER_PROFILE_CLASS_NAME)));
} catch (TimeoutException e) {
driver.quit();
throw new TimeoutException();
}
CurrentUser currentUser = getCurrentUser();
String accessToken = getAccessToken();
driver.quit();
return new User(currentUser, accessToken);
}
private String getAccessToken(){
return driver.manage().getCookieNamed("access_token").getValue();
}
private CurrentUser getCurrentUser() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
WebStorage storage = (WebStorage)driver;
LocalStorage localStorage = storage.getLocalStorage();
String currentUser = localStorage.getItem("CURRENT_USER");
return objectMapper.readValue(currentUser, CurrentUser.class);
}
}
졸업 프로젝트할때 데이터 수집이 필요해서 파이썬으로 selenium을 사용했었는데 이번에 조금 더 다양한 옵션과 기능들을 사용해 본 것 같다.
사용자 인증 정보를 가져오는게 고민이었는데 잘 해결되어 좋다 (❁´◡`❁)