로그인 상태 재사용은 만료 규칙까지 같이 설계해야 비용을 회수합니다

3줄 요약

  • 테스트마다 로그인을 다시 하면 그 시간이 실행할 때마다 통째로 되풀이됩니다. 로그인 11초에 테스트 46개, 워커 4개면 매 실행에서 약 2분이 로그인에만 들어갑니다.
  • 아끼는 양은 로그인 시간 × (테스트 수 ÷ 워커 수 − 1)로 계산됩니다. 워커당 테스트가 5개를 넘지 않으면 절감이 작아서 손이 아깝습니다.
  • 워커당 5개를 넘으신다면 Playwright setup 프로젝트로 한 번만 로그인해 storageState를 재사용하시면 됩니다. 단, 세션 만료를 감지해 다시 만드는 규칙을 같은 커밋에 넣으십시오.

지난달에 팀 막내가 물어봤습니다. "테스트 46개 도는데 왜 로그인 화면을 46번 보고 있어요?" 화면 공유로 CI 트레이스를 같이 열어봤더니 정말 그랬습니다. 아이디를 치고, 비밀번호를 치고, 버튼을 누르고, 리다이렉트 두 번을 기다리는 그 11초짜리 장면이 46번 반복되고 있었습니다. 테스트가 검증하는 건 주문서 화면인데, 실행 시간의 절반 가까이는 로그인 폼 앞에서 흘러가고 있었습니다.

여기서 갈 수 있는 길은 크게 셋입니다. 지금처럼 매 테스트마다 UI로 로그인하거나, 한 번만 로그인해서 그 상태를 파일로 저장했다가 모든 테스트가 그걸 물고 시작하거나, 병렬 워커마다 계정을 하나씩 쥐여주고 워커 단위로 한 번씩 로그인하거나. Playwright 공식 문서는 두 번째와 세 번째를 각각 "권장"이라고 적어두고, 어떤 조건에서 어느 쪽인지를 나눠 설명합니다.

결론부터 적겠습니다. 저는 두 번째, 그러니까 setup 프로젝트로 한 번 로그인해서 storageState를 재사용하는 방식을 권합니다. 다만 그건 세션 만료를 어떻게 감지하고 어떻게 다시 만들지를 같이 정한 팀에 한해서입니다. 이 규칙 없이 설정 세 줄만 넣으면, 아낀 2분보다 "어제까지 되던 게 오늘 아침에 전부 빨간불"을 파헤치는 시간이 더 큽니다. 저희 팀도 그 순서를 거꾸로 밟았다가 한 주를 날렸습니다.

1. 로그인 11초가 46개 테스트에서 어떻게 불어나는가

시간이 쌓이는 자리

Playwright는 테스트마다 새 브라우저 컨텍스트를 만듭니다. 컨텍스트가 새로 생긴다는 건 쿠키도, 로컬 스토리지도, IndexedDB도 전부 빈 상태로 시작한다는 뜻입니다. 이 격리 덕분에 테스트가 서로를 오염시키지 않지만, 대가로 인증 상태도 매번 사라집니다. 로그인 헬퍼를 beforeEach에 넣어둔 저장소가 흔한 이유가 이겁니다.

문제는 이 비용이 곱셈이라는 점입니다. 로그인 한 번에 11초가 든다면, 테스트가 46개일 때 총 506초입니다. 워커를 4개 돌리면 벽시계 기준으로는 약 126초로 줄지만, 그 126초는 매 실행마다 정확히 다시 발생합니다. 기능을 하나도 안 고쳐도, 커밋을 하나만 밀어도, 그대로 다시 냅니다.

손익분기 계산식

계산은 단순합니다. 매 테스트 로그인 방식과 한 번만 로그인하는 방식의 차이는 이렇게 나옵니다.

# 기호
L = 로그인 한 번에 걸리는 시간(초)
N = 로그인이 필요한 테스트 개수
W = 병렬 워커 수

# 매 테스트 로그인 — 벽시계 시간
T_before = L × N / W

# setup 프로젝트에서 한 번만 로그인 — setup은 직렬로 먼저 돈다
T_after  = L

# 실행 1회당 절감
saving   = L × (N / W - 1)

핵심은 N / W입니다. 워커 수보다 테스트 개수가 압도적으로 많을수록 이득이 커지고, 테스트가 워커 수와 비슷하면 이득이 거의 없습니다. 즉 테스트가 적은 팀은 이 최적화를 서두를 필요가 없습니다. 아래는 로그인 10초를 기준으로 계산한 실행 1회당 절감입니다.

테스트 수(N) 워커(W) N/W 매번 로그인 한 번만 로그인 절감
8 4 2 20초 10초 10초
20 4 5 50초 10초 40초
46 4 11.5 115초 10초 105초
100 4 25 250초 10초 240초
200 8 25 250초 10초 240초

표를 보면 세 번째 줄과 네 번째 줄, 그리고 네 번째와 다섯 번째 줄이 각각 눈에 걸립니다. 테스트를 100개에서 200개로 두 배 늘려도 워커를 함께 두 배로 늘리면 절감은 그대로입니다. 반대로 워커를 늘리지 않고 테스트만 쌓으면 절감이 계속 커집니다. 다시 말해 이 최적화는 테스트 개수가 아니라 워커당 테스트 개수를 보고 판단해야 합니다. CI 러너를 증설해서 병렬도를 올린 팀이라면 기대만큼 이득이 안 나올 수 있다는 뜻이기도 합니다.

여기에 실행 빈도를 곱하면 월 단위 숫자가 나옵니다. 저희는 PR마다 도는 파이프라인이 하루 평균 12회였고, 절감이 실행당 약 105초였으니 하루 21분, 영업일 20일이면 7시간입니다. 도입에 반나절(4시간)을 썼다고 치면 회수까지 열흘 남짓입니다.

계산 전에 자기 저장소 숫자부터 재십시오

L은 팀마다 다릅니다. SSO 리다이렉트가 끼면 15초를 넘기도 하고, 단순 폼이면 3초로 끝납니다. 추정하지 말고 재보십시오.

# 로그인 스텝만 잘라 5회 실행하고 중앙값을 봅니다
# --repeat-each 로 같은 테스트를 반복 실행합니다
npx playwright test tests/auth.setup.ts --repeat-each=5 --workers=1 --reporter=list

# 전체 실행에서 로그인이 차지하는 비중은 트레이스로 확인합니다
npx playwright test --trace=on
npx playwright show-trace test-results/<폴더>/trace.zip

L이 3초 이하라면 이 글의 나머지는 급하지 않습니다. 다른 병목을 먼저 찾으십시오.

2. 선택지 세 가지와 각각의 조건

안1 — 매 테스트마다 UI 로그인

가장 흔하고, 가장 정직하고, 가장 느립니다. 장점이 없지는 않습니다. 로그인 플로우 자체가 매 실행마다 검증되고, 상태 파일이라는 새로운 관리 대상이 생기지 않으며, 신규 입사자가 코드를 읽을 때 설명이 필요 없습니다.

테스트가 열 개 안팎이고 자동화를 막 시작한 팀이라면 저는 여기 그대로 두라고 말합니다. 위 표에서 봤듯 절감이 10초입니다. 10초를 얻자고 상태 파일 만료 처리라는 새 걱정거리를 들이는 건 손해입니다.

안2 — setup 프로젝트 + 공용 계정

Playwright가 인증 문서에서 "서버 상태를 바꾸지 않는 테스트"에 권장하는 방식입니다. 로그인 전용 프로젝트를 하나 만들고, 나머지 프로젝트가 그것을 dependencies로 걸어둡니다. 의존 프로젝트는 setup이 전부 통과한 뒤에야 시작합니다.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    // 로그인 전용 프로젝트. *.setup.ts 파일만 잡습니다
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        // setup이 저장해 둔 인증 상태를 물고 시작합니다
        storageState: 'playwright/.auth/user.json',
      },
      // setup이 통과해야만 이 프로젝트가 실행됩니다
      dependencies: ['setup'],
    },
  ],
});
// tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
import path from 'path';

const authFile = path.join(__dirname, '../playwright/.auth/user.json');

setup('authenticate', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('아이디').fill(process.env.E2E_USER!);
  await page.getByLabel('비밀번호').fill(process.env.E2E_PASS!);
  await page.getByRole('button', { name: '로그인' }).click();

  // 리다이렉트가 여러 번이면 쿠키가 다 심기기 전에 넘어갈 수 있습니다.
  // 최종 URL과 로그인 후에만 보이는 요소를 둘 다 기다립니다
  await page.waitForURL('https://example.com/');
  await expect(page.getByRole('button', { name: '내 정보' })).toBeVisible();

  // 쿠키 + localStorage + IndexedDB가 이 파일 하나에 담깁니다
  await page.context().storageState({ path: authFile });
});

안3 — 워커별 계정 fixture

테스트가 서버 상태를 건드리면 공용 계정이 무너집니다. 한 테스트가 알림 설정을 끄는 동안 다른 테스트가 알림 배지를 확인하고 있으면, 둘 다 자기 코드는 멀쩡한데 결과만 흔들립니다. 이때는 워커마다 계정을 하나씩 쥐여주는 방식으로 갑니다. Playwright는 testInfo.parallelIndex를 워커 식별자로 제공하고, 이 값은 워커가 실패로 재시작돼도 유지됩니다.

// playwright/fixtures.ts
import { test as baseTest, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';

export * from '@playwright/test';

export const test = baseTest.extend<{}, { workerStorageState: string }>({
  storageState: ({ workerStorageState }, use) => use(workerStorageState),

  workerStorageState: [async ({ browser }, use) => {
    // parallelIndex는 0..workers-1 범위이고 워커 재시작 후에도 같은 값입니다
    const id = test.info().parallelIndex;
    const fileName = path.resolve(test.info().project.outputDir, `.auth/${id}.json`);

    if (fs.existsSync(fileName)) {
      await use(fileName);
      return;
    }

    // 중요: storageState를 undefined로 두어야 깨끗한 상태에서 로그인합니다
    const page = await browser.newPage({ storageState: undefined });
    const account = await acquireAccount(id);   // 워커 전용 계정을 가져옵니다
    // ... 로그인 스텝 ...
    await page.context().storageState({ path: fileName });
    await page.close();
    await use(fileName);
  }, { scope: 'worker' }],
});

도입 난이도 · 유지보수 비용 · 학습곡선

항목 안1 매번 로그인 안2 setup + 공용 계정 안3 워커별 계정
도입 난이도 없음 낮음 — 설정 파일과 setup 파일 하나 중간 — fixture 이해 + 계정 풀 준비
유지보수 비용 낮음 중간 — 만료·비밀 관리 높음 — 계정 수를 워커 수에 맞춰 유지
팀 학습곡선 없음 하루 — 프로젝트 의존성 개념 사나흘 — fixture scope, parallelIndex
서버 상태 변경 테스트 안전 위험 안전
CI 시간 절감 0
실패 시 파급 테스트 1개 전체 중단 해당 워커만

QA 네다섯 명 규모에 자동화를 이제 막 굴리기 시작한 팀이라면 안2로 시작하는 게 맞습니다. 안3은 계정 풀을 만들고 관리하는 일이 따라붙는데, 이건 자동화 코드가 아니라 테스트 데이터 관리 문제라 별도 예산이 필요합니다. 안2로 굴리다가 "서버 상태를 바꾸는 테스트"가 다섯 개를 넘어가는 시점에 안3을 검토하십시오.

3. 붙여보니 설정 세 줄로 끝나지 않았습니다

첫날은 오히려 느려졌습니다

설정을 넣고 처음 돌렸을 때 CI 시간이 줄기는커녕 20초쯤 늘었습니다. 원인은 단순했습니다. setup 프로젝트가 직렬로 먼저 돌기 때문입니다. 기존에는 46개 로그인이 4개 워커에 흩어져 병렬로 처리됐는데, 이제는 로그인 하나가 혼자 다 끝날 때까지 나머지가 대기합니다. 그 대신 테스트 46개는 로그인 없이 시작하죠. 앞의 계산식 그대로입니다.

늘어난 20초는 setup 프로젝트가 브라우저를 새로 띄우는 오버헤드였습니다. 실제 로그인은 11초인데 워커 부팅과 브라우저 기동까지 합치니 30초 가까이 됐던 겁니다. 총량으로는 여전히 이득이었지만, "체감상 더 느려진 것 같다"는 말이 나오기 전에 이 구조를 미리 설명해 두는 편이 낫습니다.

한 가지 더 있습니다. setup 프로젝트가 실패하면 의존하는 프로젝트는 아예 실행되지 않습니다. 문서에도 명시된 동작이고, 생각해보면 당연합니다. 인증이 안 됐는데 나머지를 돌려봐야 전부 같은 이유로 실패하니까요. 다만 리포트에서 보이는 그림은 달라집니다. 전에는 "46개 중 3개 실패"였던 것이 이제는 "1개 실패, 46개 미실행"으로 나옵니다. 대시보드에 통과율을 걸어둔 팀이라면 이 숫자가 갑자기 0%로 떨어지는 날이 옵니다. 지표를 세는 방식을 같이 손봐야 한다는 뜻입니다.

API 로그인으로 바꾸니 setup이 3초가 됐습니다

가장 크게 효과를 본 변경은 setup 안에서 UI를 쓰지 않는 것이었습니다. 공식 문서에도 APIRequestContext로 인증하는 방법이 따로 나와 있습니다. 저희 서비스는 로그인 API가 세션 쿠키를 그대로 내려주기 때문에 폼을 채울 이유가 없었습니다.

// tests/auth.setup.ts — API로 로그인하고 상태만 저장합니다
import { test as setup } from '@playwright/test';

const authFile = 'playwright/.auth/user.json';

setup('authenticate via api', async ({ request }) => {
  // 폼 전송과 같은 요청을 그대로 보냅니다. 브라우저를 띄우지 않습니다
  await request.post('https://example.com/api/login', {
    form: { user: process.env.E2E_USER!, password: process.env.E2E_PASS! },
  });
  // request 컨텍스트에서도 storageState를 뽑을 수 있습니다
  await request.storageState({ path: authFile });
});

여기서 주의할 점이 하나 있습니다. API 로그인으로 바꾸면 로그인 화면 자체는 더 이상 회귀 테스트를 받지 않습니다. 그래서 저희는 로그인 UI만 검증하는 테스트를 한 개 따로 남겼습니다. 그 파일에서는 인증 상태를 비워야 하는데, 이건 문서에 방법이 있습니다.

// tests/login-ui.spec.ts
import { test } from '@playwright/test';

// 이 파일만 인증되지 않은 상태로 시작합니다
test.use({ storageState: { cookies: [], origins: [] } });

test('로그인 화면에서 잘못된 비밀번호는 오류 문구를 보여준다', async ({ page }) => {
  // ...
});

4. 네 개의 함정과 대응

붙여놓고 한 달을 굴리면서 실제로 부딪힌 것들입니다. 공통점은 전부 "실패 메시지가 원인을 가리키지 않는다"는 데 있습니다. 먼저 증상과 원인을 한 표로 묶어두고, 하나씩 풀겠습니다.

증상 실제 원인 대응
아침 첫 실행만 전부 빨간불, 재실행하면 통과 상태 파일 세션 만료 setup에 나이 검사 + 헬스체크
테스트 계정으로 스테이징에 낯선 로그인 기록 상태 파일을 저장소에 커밋 .gitignoreplaywright/.auth
"로컬 UI 모드에서만 인증이 안 된다" UI 모드는 setup을 기본 실행하지 않음 필터에서 setup 수동 실행
열 번에 한 번 실패, 원인 재현 불가 공용 계정으로 쓰기 테스트 병렬 실행 쓰기 테스트를 별도 프로젝트로 분리
로그인은 통과하는데 첫 화면부터 로그아웃 인증 정보가 sessionStorage에 있음 API 로그인 검토 또는 addInitScript

첫 번째 함정 — 만료된 상태 파일

playwright/.auth/user.json은 한 번 만들면 계속 남습니다. 안에 든 토큰이 만료되면 모든 테스트가 로그인 화면으로 튕기고, 실패 메시지는 "요소를 찾을 수 없다"로만 나옵니다. 원인이 인증이라는 걸 알아채는 데 시간이 걸리는 게 진짜 비용입니다.

문서에는 "만료되면 저장된 상태를 지워야 한다"고만 적혀 있고, 그 판단을 언제 누가 하는지는 우리 몫입니다. 저희가 정한 규칙은 이렇습니다. setup 안에서 저장 직전에 인증이 실제로 먹었는지를 한 번 더 확인하고, 상태 파일에 만든 시각을 같이 기록해 일정 시간이 지나면 무조건 다시 만듭니다.

// tests/auth.setup.ts — 만료 처리를 붙인 버전
import { test as setup, expect } from '@playwright/test';
import fs from 'fs';

const authFile = 'playwright/.auth/user.json';
const MAX_AGE_MS = 6 * 60 * 60 * 1000;   // 세션 수명보다 짧게 잡습니다

setup('authenticate', async ({ page, request }) => {
  // 파일이 충분히 최신이면 그대로 씁니다
  if (fs.existsSync(authFile)) {
    const age = Date.now() - fs.statSync(authFile).mtimeMs;
    if (age < MAX_AGE_MS) {
      // 살아 있는 세션인지 가벼운 API로 확인합니다
      const res = await request.get('https://example.com/api/me');
      if (res.ok()) return;
    }
  }

  await page.goto('https://example.com/login');
  // ... 로그인 스텝 ...
  await expect(page.getByRole('button', { name: '내 정보' })).toBeVisible();
  await page.context().storageState({ path: authFile });
});

MAX_AGE_MS는 서비스의 세션 수명에서 넉넉히 뺀 값으로 잡으십시오. 저희는 세션이 24시간이라 6시간으로 뒀습니다. 더 간단한 대안도 있습니다. 상태 파일을 testProject.outputDir 아래에 쓰면 매 실행 전에 자동으로 지워집니다. 매 실행 로그인 한 번을 그냥 감수하겠다는 선택이고, 저는 CI에서는 이쪽을 권합니다. 만료라는 변수 자체가 사라집니다.

두 번째 함정 — 상태 파일을 커밋하는 사고

storageState 파일에는 세션 쿠키와 토큰이 평문으로 들어 있습니다. 공식 문서도 이 지점에 danger 박스를 달아 저장소에 넣지 말라고 명시합니다. 테스트 계정이라도 스테이징 데이터 전체에 접근할 수 있다면 사고입니다.

# 디렉터리를 만들고 곧바로 무시 목록에 넣습니다. 순서를 바꾸지 마십시오
mkdir -p playwright/.auth
echo $'\nplaywright/.auth' >> .gitignore

계정 정보 자체도 코드에 박지 말고 CI 시크릿에서 환경변수로 받으십시오. 위 예제에서 process.env.E2E_USER를 쓴 게 그 이유입니다.

세 번째 함정 — UI 모드는 setup을 건너뜁니다

로컬에서 UI 모드로 테스트를 보다가 "왜 나만 인증이 안 되지"라는 질문이 나옵니다. Playwright는 속도를 위해 UI 모드에서 setup 프로젝트를 기본적으로 실행하지 않습니다. 문서가 권하는 해법은 필터에서 setup 프로젝트를 켜고 auth.setup.ts 옆의 삼각형 버튼을 눌러 수동 실행한 뒤 다시 끄는 것입니다.

반대 방향의 함정도 있습니다. --grep이나 파일 경로로 테스트 하나만 골라 돌려도 의존 프로젝트는 전부 함께 실행됩니다. 테스트 하나 확인하려는데 로그인이 먼저 도는 셈입니다. 이럴 때 쓰라고 만들어진 옵션이 --no-deps입니다.

# 상태 파일이 이미 살아 있을 때, setup을 건너뛰고 한 개만 돌립니다
npx playwright test tests/order.spec.ts --no-deps

# 인증이 꼬였다 싶으면 setup만 다시 돌립니다
npx playwright test --project=setup

이 두 줄을 팀 README 맨 위에 적어두는 것만으로 문의가 눈에 띄게 줄었습니다.

가장 비싼 함정 — 서버 상태를 바꾸는 테스트

지금까지 중 복구 비용이 가장 큰 건 이겁니다. 공용 계정 하나로 병렬 실행을 하면, 설정을 바꾸는 테스트와 그 설정을 읽는 테스트가 같은 시각에 돌 수 있습니다. 문제는 이게 항상 실패하지 않는다는 데 있습니다. 열 번에 한 번 실패하니 재실행하면 통과하고, 그래서 원인을 찾지 않고 넘어가게 됩니다. 그렇게 넘어간 케이스가 쌓이면 팀은 빨간불을 신뢰하지 않게 됩니다.

제 해석으로는, 여기서 진짜로 결정해야 하는 건 도구 설정이 아니라 어떤 테스트가 공유 상태를 건드리는지 목록을 갖고 있느냐입니다. 저희는 스프레드시트에 테스트별로 "읽기만 함 / 쓰기 있음" 한 칸을 두고, 쓰기 있는 테스트는 공용 계정 프로젝트에서 빼서 별도 프로젝트로 분리했습니다. 그 프로젝트만 안3 방식이거나 workers: 1입니다. 전부를 안3으로 옮기는 것보다 훨씬 쌌습니다.

덤 — sessionStorage는 저장되지 않습니다

storageState가 담는 건 쿠키, 로컬 스토리지, IndexedDB, 그리고 패스키(WebAuthn) 기반 인증입니다. sessionStorage는 포함되지 않습니다. 인증 정보를 세션 스토리지에 두는 서비스라면 이 방식이 그냥 동작하지 않습니다. 문서에 addInitScript로 밀어 넣는 우회 코드가 있지만, 저는 그 정도까지 갔다면 API 로그인 쪽을 먼저 검토하겠습니다.

5. 권할 만한 경우와 권하지 않는 경우

상황 판단 이유
테스트 30개 이상, 로그인 8초 이상 권합니다 실행당 절감이 1분을 넘어 회수가 확실합니다
로그인 API가 따로 있다 강하게 권합니다 setup이 브라우저 없이 3초에 끝납니다
테스트 10개 안팎 권하지 않습니다 절감 10초를 얻자고 만료 관리를 들이는 셈입니다
인증을 sessionStorage에 둔다 권하지 않습니다 저장 대상이 아니라 우회가 필요합니다
쓰기 테스트가 절반 이상 조건부 안3으로 가거나, 쓰기 테스트만 분리하십시오
브라우저마다 인증이 다르다 권하지 않습니다 공용 상태 파일을 공유할 수 없습니다
자동화 도입 3개월 미만 나중에 먼저 플레이키를 잡고, 테스트가 쌓인 뒤에 오십시오

도입 전 체크리스트

붙이기 전에 아래 일곱 개에 답할 수 있는지 확인하십시오. 답이 막히는 항목이 있으면 그게 이번 스프린트에 먼저 해결할 일입니다.

□ L(로그인 1회 소요)을 실제로 측정했는가 — 추정치가 아니라 측정치
□ N/W가 5를 넘는가 — 넘지 않으면 절감이 작다
□ 로그인 API가 있는가 — 있으면 UI 대신 그걸 쓴다
□ 세션 수명이 몇 시간인가 — MAX_AGE는 그보다 짧아야 한다
□ playwright/.auth 를 .gitignore에 넣었는가
□ 계정 정보를 CI 시크릿에서 받는가 — 코드에 박혀 있지 않은가
□ 서버 상태를 바꾸는 테스트 목록을 갖고 있는가

마지막으로, 인증이 살아 있는지 확인하는 30초짜리 스크립트를 하나 남깁니다. 아침에 파이프라인이 통째로 빨간불일 때 원인이 인증인지 아닌지부터 가릅니다.

#!/usr/bin/env bash
# check-auth.sh — 저장된 인증 상태가 아직 유효한지 확인합니다
set -euo pipefail
AUTH="playwright/.auth/user.json"

if [ ! -f "$AUTH" ]; then
  echo "상태 파일 없음 → npx playwright test --project=setup 실행하십시오"; exit 1
fi

# 파일 나이를 시간 단위로 계산합니다 (macOS는 stat -f %m)
MTIME=$(stat -c %Y "$AUTH" 2>/dev/null || stat -f %m "$AUTH")
AGE_H=$(( ( $(date +%s) - MTIME ) / 3600 ))
echo "상태 파일 나이: ${AGE_H}시간"

# 쿠키 만료 시각을 읽어 이미 지난 것이 있는지 봅니다
python3 - "$AUTH" <<'PY'
import json, sys, time
st = json.load(open(sys.argv[1]))
now = time.time()
dead = [c["name"] for c in st.get("cookies", [])
        if 0 < c.get("expires", -1) < now]
print("만료된 쿠키:", dead if dead else "없음")
sys.exit(1 if dead else 0)
PY

정리하면 이렇습니다. storageState 재사용은 어려운 기술이 아닙니다. 설정 파일 몇 줄이면 붙습니다. 어려운 건 그 상태가 언제 썩는지를 정하고, 썩었을 때 팀이 3분 안에 알아채도록 만들어 두는 일입니다. 그 규칙까지 같이 설계했을 때만 아낀 시간이 실제로 손에 남습니다.

댓글

이 블로그의 인기 게시물

옵시디언 3기기 동기화, 유료 결제 없이 구글 드라이브로 끝내기

커리큘럼을 여섯 개 병행해도 인터리빙 효과는 적용되지 않습니다

반값 모두의카드가 끝나도 환급 감소는 수도권 기준 월 3만 2천원이 최대입니다