start-server-and-test로 안전한 E2E 테스트 환경 구축하기 (feat. Sync API)
웹 애플리케이션의 규모가 커질수록 E2E(End-to-End) 테스트의 중요성은 나날이 높아집니다. 하지만 CI/CD 파이프라인에서 테스트를 수행할 때 가장 빈번하게 마주치는 문제는 “서버가 완전히 뜰 때까지 어떻게 기다릴 것인가?”입니다. 단순히 npm run dev && npm run test를 실행하면 서버가 준비되기도 전에 테스트가 시작되어 실패하기 때문이죠.
오늘은 이러한 문제를 우아하게 해결해주는 start-server-and-test 라이브러리를 소개하고, 개발 서버에서만 동작하는 Sync API 환경을 예로 들어 실무 적용법을 살펴보겠습니다.
목차
- start-server-and-test란 무엇인가?
- 왜 이 도구가 필요한가 (Wait-on의 한계 극복)
- 실무 예제: 개발 서버 전용 Sync API 테스트 환경 구축
- 핵심 설정 가이드 및 트러블슈팅
- 자주 묻는 질문(FAQ)
1. start-server-and-test란 무엇인가?
start-server-and-test는 특정 URL이 응답을 보낼 때까지 기다렸다가 테스트 스크립트를 실행하고, 테스트가 종료되면 서버 프로세스까지 깔끔하게 종료해주는 CLI 도구입니다.
Cypress의 공동 창립자인 Gleb Bahmutov가 만든 이 도구는 특히 GitHub Actions나 Jenkins 같은 CI 환경에서 빛을 발합니다. 개발자가 수동으로 서버를 켜고 테스트를 돌리는 과정을 자동화된 하나의 파이프라인으로 연결해 줍니다.
2. 왜 이 도구가 필요한가?
일반적인 쉘 커맨드 체이닝(&)은 서버를 백그라운드에서 실행하지만, 서버가 리스닝 상태가 되었는지 확인하지 않습니다. 반면 &&는 앞선 프로세스가 종료되어야 다음 프로세스로 넘어갑니다. 즉, 꺼지지 않는 개발 서버 뒤에 테스트를 붙일 수가 없는 구조죠.
이 도구는 다음과 같은 프로세스를 보장합니다:
- 서버 실행: 지정된 스크립트(예:
npm run dev)를 실행합니다. - 상태 폴링(Polling): 특정 포트(예:
http://localhost:3000)에 주기적으로 요청을 보내 응답이 오는지 확인합니다. - 테스트 실행: 응답이 확인되면 테스트 스크립트(예:
npm run test:e2e)를 실행합니다. - 프로세스 정리: 테스트가 성공하든 실패하든, 처음에 띄웠던 서버를 안전하게 종료합니다.
3. 실무 예제: 개발 서버 전용 Sync API 테스트
사용자가 요청한 상황을 가정해 봅시다. 보안이나 비용 문제로 데이터 동기화(Sync) 기능을 로컬 개발 서버(development 환경)에서만 활성화해 두었다면, 테스트 환경에서도 이 서버가 정상적으로 동작하는지 확인해야 합니다.
package.json 설정 예시
{
"scripts": {
"dev": "next dev",
"test:e2e": "playwright test",
"ci:sync-test": "start-server-and-test dev http://localhost:3000 test:e2e"
}
}
테스트 코드 (TypeScript)
개발 서버에서만 활성화된 /api/sync 엔드포인트를 테스트하는 예제입니다.
import { test, expect } from '@playwright/test';
/**
* 개발 서버 전용 Test API 검증 테스트
* 이 테스트는 오직 로컬/개발 환경에서만 유효한 시나리오입니다.
*/
test('로컬 개발 서버에서 Test API가 정상적으로 데이터를 동기화하는지 확인', async ({ request }) => {
// 1. Sync API 호출 (개발 서버에만 존재하는 엔드포인트)
const response = await request.post('http://localhost:3000/api/test', {
data: { lastSyncedAt: new Date().toISOString() }
});
// 2. 응답 상태 코드 확인
expect(response.status()).toBe(200);
const body = await response.json();
// 3. 비즈니스 로직 검증
expect(body.success).toBe(true);
expect(body.syncedCount).toBeGreaterThan(0);
});
이 설정에서 npm run ci:test를 실행하면, 도구는 dev 스크립트를 먼저 실행하고 localhost:3000이 응답할 때까지 기다린 뒤, 비로소 Sync API 테스트를 수행합니다.
4. 핵심 설정 가이드 및 트러블슈팅
개발자의 팁: 타임아웃 설정
서버 규모가 커서 빌드 시간이 오래 걸린다면 기본 타임아웃 때문에 테스트가 시작되기도 전에 종료될 수 있습니다. 이때는 --timeout 옵션을 사용하세요.
start-server-and-test dev 3000 'test:e2e' --timeout 300000 (5분)
주의사항: 만약 서버가 HTTPS를 사용한다면 URL에 반드시
https://를 명시해야 합니다. 또한,localhost대신127.0.0.1을 사용해야 하는 환경(Node.js 17+ 의 IPv6 관련 이슈 등)이 있으니 연결이 안 될 경우 IP 주소로 변경해 보세요.
5. 자주 묻는 질문(FAQ)
Q: wait-on 라이브러리와의 차이점은 무엇인가요?
wait-on은 단순히 특정 포트가 열릴 때까지 기다리는 기능만 제공합니다. 반면 start-server-and-test는 서버 실행, 대기, 테스트 실행, 서버 종료라는 전체 라이프사이클을 하나의 명령어로 관리해 주므로 CI 설정이 훨씬 간결해집니다.
Q: 여러 개의 서버를 띄워야 하는 경우는 어떻게 하나요?
이 도구는 다중 서버를 지원합니다. start-server-and-test 'start:api' 8080 'start:front' 3000 'test:e2e' 형식으로 인자를 전달하면 8080과 3000 포트가 모두 활성화될 때까지 기다린 후 테스트를 시작합니다.
Q: CI 환경에서 포트 충돌이 발생하면 어떡하죠?
CI 환경에서는 매번 새로운 컨테이너나 가상 머신에서 실행되므로 보통 충돌하지 않습니다. 하지만 로컬에서 실행할 때는 이전 프로세스가 죽지 않았는지 확인하거나, PORT 환경 변수를 동적으로 할당하는 방식을 권장합니다.
start-server-and-test는 특히 개발 환경에서만 동작하는 특수한 기능을 검증할 때 매우 강력한 도구입니다. 여러분의 프로젝트에도 도입하여 더 견고한 파이프라인을 구축해 보시길 바랍니다!