← posts

로그인이 필요한 E2E 테스트는 매번 로그인해야 할까?

2026-09-04 · 회고

9주차 과제에서 인증을 붙이니 E2E 앞에도 로그인 단계가 생겼다. 로그인 흐름 자체를 보는 테스트라면 당연한 일이지만, 주문 완료를 확인하려고 매번 이메일과 비밀번호부터 입력하는 게 맞나 싶었다.

결론부터 말하면 두 경우를 나눴다. 로그인 과정이 중요한 테스트는 빈 브라우저에서 시작했다. 로그인이 준비 과정일 뿐인 테스트에서는 워커마다 한 번만 로그인하고 그 결과를 storageState로 저장해 다시 썼다.

주문 완료를 보려는데 왜 매번 로그인해야 하지

이번에 만든 E2E 시나리오는 네 가지였다.

  • 미로그인 상태로 보호 경로에 접근한 뒤 로그인하면 원래 경로로 돌아온다.
  • 잘못된 자격 증명으로 로그인하면 오류를 보여주고 세션을 만들지 않는다.
  • 로그인된 세션이 만료되면 안내와 함께 로그인 화면으로 이동한다.
  • 상품을 장바구니에 담고 주문서를 제출하면 주문 내역에 새 주문이 나타난다.

앞의 두 시나리오에서는 로그인 화면을 띄우고 자격 증명을 제출한 뒤, 세션 쿠키를 받아 원래 경로로 돌아가는 과정까지 확인해야 한다. 세션 만료와 주문 완료 테스트에서는 로그인이 시작 조건일 뿐이다.

처음에는 모든 테스트에서 UI 로그인을 하면 된다고 생각했다. 가장 단순하고 각 테스트가 필요한 상태를 직접 만드니 이해하기도 쉽다. 대신 테스트가 늘면 확인하려는 내용과 상관없는 로그인을 계속 반복하게 된다.

그다음 떠올린 건 로그인 상태 하나를 모든 테스트가 같이 쓰는 방법이었다. 로그인 횟수는 가장 적지만 주문 테스트 때문에 마음에 걸렸다. 브라우저를 따로 띄워도 같은 계정으로 주문하면 서버의 주문 내역은 공유되기 때문이다.

선택지를 표로 놓고 비교해봤다.

방법장점걸리는 부분
테스트마다 UI 로그인각자 필요한 상태를 직접 만든다테스트 수만큼 로그인이 반복된다
인증 상태 하나를 전체 테스트가 공유로그인은 한 번만 하면 된다같은 계정의 서버 데이터가 섞일 수 있다
워커마다 다른 계정으로 한 번 로그인로그인 횟수를 줄이고 병렬 실행도 계정별로 나눌 수 있다같은 워커의 테스트끼리는 계정을 공유한다

서버 데이터를 바꾸는 E2E는 주문 테스트 하나뿐이었다. 테스트마다 계정을 따로 만들 필요까지는 없었고 병렬로 도는 워커끼리만 주문 내역이 섞이지 않으면 됐다. 그래서 워커마다 계정과 인증 파일을 하나씩 두기로 했다.

Playwright 워커는 테스트 파일을 나누어 실행한다

Playwright의 워커는 테스트를 실행하는 별도 프로세스다. --workers=4로 실행하면 테스트 파일을 최대 네 프로세스에 나눠 돌린다. 워커마다 브라우저와 fixture도 따로 생긴다.

worker-scoped fixture를 쓰면 준비 코드를 테스트마다 반복하지 않고 워커가 시작할 때 한 번만 실행할 수 있다. 이 범위에 UI 로그인을 넣었다.

workerInfo.parallelIndex로 워커 번호를 구한 뒤 0번 워커에는 looper1, 1번 워커에는 looper2를 배정했다. 로그인이 끝나면 브라우저 상태를 worker-0.json, worker-1.json처럼 워커별 파일에 저장했다.

워커마다 별도 계정으로 한 번 로그인해 storageState를 만들고, 각 테스트는 새로운 브라우저 컨텍스트에서 인증 상태를 불러오는 흐름

워커를 네 개로 설정했다고 실행 시간이 딱 4분의 1이 되는 건 아니다. 테스트 파일 수와 각각의 실행 시간, 브라우저를 띄우는 비용에 따라 달라진다. 이번에 추가한 네 시나리오도 --workers=4로 실행했을 때 실제로는 세 워커에 나뉘었다.

병렬 실행과 별개로 로그인 횟수도 줄어든다. 인증 fixture를 쓰는 테스트가 N개라면 원래는 N번 로그인해야 한다. 워커 범위에서 로그인 결과를 돌려 쓰면 정상 실행에서는 실제로 사용된 워커 수만큼만 로그인한다.

storageState는 브라우저를 공유하는 기능이 아니었다

Playwright의 storageState를 사용하면 브라우저 컨텍스트의 쿠키와 localStorage를 파일로 저장할 수 있다. 워커에서 로그인을 마친 뒤 JSON으로 남겨두고 같은 워커가 맡은 테스트를 시작할 때 불러왔다.

이 부분을 처음에는 잘못 이해했다. 같은 storageState를 사용하면 테스트가 브라우저 하나를 함께 쓰는 줄 알았다. 실제로 공유하는 건 브라우저가 아니라 새 컨텍스트를 만들 때 넣어줄 초기값이었다.

흐름을 풀어보면 이렇다.

  1. 워커가 UI로 한 번 로그인한다.
  2. 쿠키와 localStorage를 JSON 파일에 저장한다.
  3. 테스트를 시작할 때마다 새 브라우저 컨텍스트를 만든다.
  4. 저장해둔 storageState를 새 컨텍스트에 불러온다.

한 테스트가 쿠키나 localStorage를 바꾸더라도 다른 테스트의 컨텍스트나 원본 JSON은 그대로다. 로그인 상태는 가져오되 테스트 중에 생긴 변경까지 옆 테스트로 넘어가지는 않는다.

다만 새 브라우저 컨텍스트가 나눠주는 건 브라우저 안의 상태뿐이다. 같은 워커의 테스트는 같은 계정을 쓰므로 서버에 저장된 주문이나 계정 설정은 여전히 공유한다. 지금은 서버 데이터를 바꾸는 테스트가 하나라 괜찮지만 이런 테스트가 늘면 테스트별 계정을 만들거나 실행 후 데이터를 지워야 한다.

로그인 테스트에는 일부러 빈 storageState를 넣었다

세션 만료와 주문 완료 테스트는 워커용 fixture에서 확장한 test를 썼다. 반대로 로그인 자체를 확인하는 auth.spec.ts에서는 Playwright의 기본 test를 가져오고 파일 전체에 빈 상태를 넣었다.

import { expect, test } from '@playwright/test';
 
test.use({ storageState: { cookies: [], origins: [] } });

cookiesorigins를 빈 배열로 두면 저장된 쿠키와 origin별 localStorage 없이 테스트가 시작된다.

지금은 전역 설정에도 인증된 storageState가 없어서 이 코드가 없어도 결과는 같다. 그래도 일부러 적어뒀다. 나중에 전역 설정이 바뀌더라도 auth.spec.ts만큼은 항상 로그아웃 상태에서 시작한다는 뜻이 코드에 바로 보이기 때문이다.

같은 로그인 기능을 다루더라도 테스트의 목적에 따라 시작점을 달리 잡았다.

  • 로그인 과정을 확인하는 테스트는 빈 상태에서 UI로 로그인한다.
  • 로그인이 다른 시나리오의 전제 조건이면 저장된 인증 상태를 사용한다.

워커 수를 바꾸고 반복해도 결과는 같았다

적용을 마친 뒤 워커 수를 바꾸고 같은 테스트도 반복해봤다.

  • --workers=4: 신규 시나리오 4개 통과
  • --workers=1: 신규 시나리오 4개 통과
  • --workers=1 --repeat-each=3: 총 12회 통과

워커가 하나일 때와 넷일 때 결과가 같았고 세 번씩 반복해도 쿠키나 스토리지가 섞이지 않았다.

테스트가 많지 않아 전후 실행 시간은 따로 재지 않았다. 그래서 얼마나 빨라졌다고 말할 수는 없다. 이번에 확인하고 싶었던 건 로그인을 워커마다 한 번으로 줄여도 테스트가 서로 영향을 주지 않는지였다. 지금의 네 시나리오에서는 문제가 없었다.

재사용할 상태의 범위를 먼저 정해야 했다

처음에는 storageState를 다시 쓰면 테스트 독립성을 포기하는 줄 알았다. 직접 적용해 보니 로그인 정보를 재사용하는 것과 브라우저 컨텍스트를 공유하는 것은 별개였다.

이번에는 로그인 결과만 워커 범위에서 돌려 썼다. 쿠키와 localStorage는 테스트마다 새 컨텍스트를 만들어 나눴고 서버 데이터가 워커 사이에서 겹치지 않도록 계정도 따로 뒀다.

물론 이 구성이 언제나 맞지는 않는다. 조회만 하는 테스트라면 인증 상태 하나로 충분할 수도 있다. 같은 워커 안에서 주문이나 계정 설정을 바꾸는 테스트가 많아진다면 지금보다 더 잘게 계정을 나눠야 한다.

발제를 듣기 전 실무에서 E2E를 적용해보려 했을 때, 가장 까다롭게 느껴진 부분도 로그인 처리였다. 로그인해야 하는 테스트마다 로그인 과정을 반복해야 하는지, 인증 상태를 공유하면 테스트끼리 영향을 주지는 않을지 감이 잘 오지 않았다.

이번 과제를 하면서 Playwright의 워커가 테스트를 어떻게 나눠 실행하는지 알게 됐고, storageState로 로그인 정보를 재사용하면서도 각 테스트를 새 브라우저 컨텍스트에서 시작할 수 있다는 걸 배웠다. 막막하게만 느껴졌던 부분을 직접 구현하고 확인해볼 수 있어서 특히 기억에 남는다.