[SpringBoot] Selenium으로 Velog 로그인하기

울상냥·2023년 5월 6일
0

SpringBoot

목록 보기
7/11
post-thumbnail
post-custom-banner

지난 글에서 구상한 바에 이어

로그인 버튼 클릭 -> 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에 맞게 의존성 추가해주었다.


init

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 propertyDriver 설정을 해주었다.

PageLoadStrategy.EAGER

ChromeOption에 PageLoadStrategy.EAGER 옵션을 주었는데 이 옵션을 주면 selenium은 로딩이 완료되기를 기다리지 않고 다음 작업을 수행한다.
Velog 메인페이지 로딩이 꽤나 걸리기 때문에 로그인 화면을 띄우기에 로딩 완료를 기다릴 필요가 없었다. 해당 옵션을 주고 로그인 화면 띄우기까지 약 3초정도 더 빨라졌다😊

(다만 위 옵션을 주면 사용하고자하는 element가 로드 되었는지 확인을 해주어야 하는데 로그인버튼에 해당하는 element는 확인하지않아도 바로 로딩이 되는지 문제가 없었다..!)


process

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분간 로그인이 완료되기를 대기한다.
로그인 완료의 기준은 몇가지로 둘 수 있겠지만

그냥 대강 이 프로필 이미지 element가 뜨는 것을 기준으로 했다. `WebDriverWailt`의 `ExpectedConditions`의 옵션도 아주많아서 `visibilityOfElementLocated`로 element가 보이는지를 확인

로그인이 완료되면 currentuseraccesstoken정보를 가져와 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로 넘겨주었다. (맘에 안드는데 더 좋은 방법을 아직은 모르겠다)


getCurrentUser

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 해주었다.

getAccessToken

private String getAccessToken(){

        return driver.manage().getCookieNamed("access_token").getValue();
    }

쿠키도 가져올 수 있다.


code

@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을 사용했었는데 이번에 조금 더 다양한 옵션과 기능들을 사용해 본 것 같다.
사용자 인증 정보를 가져오는게 고민이었는데 잘 해결되어 좋다 (❁´◡`❁)

profile
안되면 되게하라
post-custom-banner

0개의 댓글