TIL/Playwright

로그인 인증 상태 저장 및 재사용 방법 w/ Playwright

아람2 2025. 8. 12. 18:35
반응형

로그인의 흐름은 자주 바뀔 수 있다 

캡차, OTP, SSO 같은 요소가 섞이면 자동화가 불안정해진다 

그래서 사람 손으로 한 번 로그인을 하고, 이후에는 인증 상태를 저장하고 재사용하는 방법을 사용했다 

전체 흐름 

🧑 사람이 한번 GUI 브라우저로 수동 로그인을 하고 세션을 JSON 으로 저장한다 
🤖 실행 시 저장된 JSON 을 Context 에 로드해서 로그인 상태로 시작한다 

 

폴더 구조 

project/
├── scripts/
│   └── save_login_session.py      👈 사람 손으로 로그인해서 세션 저장
├── keywords/
│   ├── login_keywords.py          👈 저장된 세션을 불러오는 Python 키워드
│   └── main_keywords.py           👈 페이지 접속 등 기능 실행
├── resource/
│   └── common.resource            👈 Robot 에서 사용할 Keyword 래퍼
├── tests/
│   └── mail.robot                 👈 진짜 테스트 케이스
├── logged_in.json        	   👈 저장된 로그인 세션 (자동 로그인에 사용)

 

save_login_session.py 로 로그인 과정을 수행하고, 로그인 상태를 logged_in.json 파일에 저장한다 

 

수동 로그인 & 세션 저장 코드 

GUI 로 Browser 를 띄워 사람이 직접 로그인하고 Storage State 를 저장한다 

# scripts/save_login_session.py

import asyncio
from playwright.async_api import async_playwright

SESSION_PATH = "logged_in.json"


async def save_login_session():
    async with async_playwright() as p:
        # GUI 열림 (headless=False)
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()

        print("🌐 웹페이지 접속 중...")
        await page.goto("https://www.webpage.com")

        print("🔐 로그인 버튼 클릭")
        await page.get_by_role("button", name="로그인").click()

        print("\n👉 수동으로 로그인하세요. 로그인 완료 후 터미널에 엔터를 눌러주세요.")
        input()

        # 로그인 세션 저장
        await context.storage_state(path=SESSION_PATH)
        print(f"\n✅ 로그인 세션 저장 완료: {SESSION_PATH}")

        await browser.close()

 

아래 명령어를 실행하면 logged_in.json 파일이 자동으로 생성되고, 파일 안에 세션이 저장된다 

python scripts/save_login_session.py

 

logged_in.json 파일의 구조는 아래와 같다 

{
	"cookies": [...],
	"origins": [...]
}

 

cookies 에는 로그인 유지 (사용자 세션 유지) 에 필요한 쿠키 목록이 들어있고, 

origins 에는 도메인 별로 저장된 localStorage 스냅샷 목록이 들어간다 

저장된 세션 불러오기  

저장된 Storage State 를 불러와 로그인된 컨텍스트를 생성한다 

이 함수는 코루틴 객체를 반환하므로, 호출 측에서 await 로 받아 사용할 수 있다 

# keywords/login_keywords.py
from playwright.async_api import async_playwright

SESSION_PATH = "logged_in.json"


def load_logged_in_context():
    async def run():
        async with async_playwright() as p:
            browser = await p.chromium.launch(headless=True)
            context = await browser.new_context(storage_state=SESSION_PATH)
            return browser, context

    return run()  # 코루틴 객체를 반환해서 호출측에서 await로 받는다

 

저장된 세션으로 메일 화면 진입 

한 번 호출로, 열고 이동하고 확인하고 닫기까지 끝낼 수 있도록 안쪽에서 코루틴을 실행까지 보장한다 

# keywords/mail_keywords.py
from login_keywords import load_logged_in_context


def open_mail_page():
    async def run():
        browser, context = await load_logged_in_context()
        page = await context.new_page()
        await page.goto("https://www.naver.com")

        await page.get_by_role("link", name="메일").click()
        await page.wait_for_url("https://mail.naver.com/*")

        assert "메일" in await page.title()
        await browser.close()

    import asyncio

    try:
        # 현재 스레드에 이벤트 루프가 없으면
        # 여기서 새 루프를 만들어 run()을 끝날 때까지 실행
        return asyncio.run(run())
    except RuntimeError:
        # 이미 이벤트 루프가 돌고 있는 환경이면
        # 여기서 실행하면 충돌하니 코루틴만 넘기고 호출자가 await 하게 함
        return run()

 

비로그인으로 열고 싶을 때 

Storage State 를 지정하지 않으면 비로그인 상태로 동작한다 

context = browser.new_context()   # storage_state 없음

 

Resource 에서 Keyword 래핑하기 

Resource 에서 Keyword 를 한 번 더 감싸면, 내부 Playwright API 나 구현이 바뀌어도 유지보수가 쉬워진다 

*** Settings ***
Library    ../keywords/login_keywords.py
Library    ../keywords/mail_keywords.py
Library    ../src/main_page_test_case.py

*** Keywords ***
...
Access to Mail w Login
    [Documentation]      로그인 한 상태로 메일 화면 진입 
    [Arguments]    ${url}
    Access Mail w Login   ${url}

Robot Framework 로 실행하기 

Test Suite 는 Resource 의 Wrapping Keyword 를 그대로 호출한다 

Test Setup/ Teardown 으로 공통 초기화/ 정리를 자동화하면 케이스마다 보일러플레이트를 없앨 수 있다 

*** Settings ***
Resource    ../resource/web_access.resource
Resource    ../resource/main_page.resource
Test Setup    Initialize Environment
Test Teardown    Close Environment

*** Test Cases ***
...
Check the Mail w Login
    Access Mail w login   https://www.webpage.com

실행 결과

 

 


 

주의할 점 

logged_in.json 은 민감한 정보가 들어갈 수 있으니 반드시 .gitignore 에 추가한다  

저장된 세션은 사이트 정책에 따라 만료될 수 있으니 실패 시 저장 스크립트를 다시 실행한다 
저장과 사용 환경의 브라우저 버전, User-Agent, 타임존이 크게 다르면 세션이 거부될 수 있으니 가능하면 동일하게 맞춘다 

 

반응형