/collect.js를 API Worker와 다른 서브도메인으로 분리한 구조가 많이 보이는데, 왜 분리하는지 의문이 들었다.
한 줄 요약
collect.js 는 모든 사용자에게 반복해서 전달되는 정적 파일이고, /collect 는 데이터 수집을 처리하는 동적 API 이다. 이 둘은 트래픽 성격과 운영 요구가 다르기 때문에 보통 다음처럼 나눈다.
사용자가 등록한 사이트
│
├─ GET https://cdn.foundly.cc/collect.js
│ 정적 JavaScript 다운로드
│
└─ POST https://api.foundly.cc/collect
설치·분석 데이터 전송
용어 구분
collect.js
브라우저가 다운로드해서 실행하는 자바스크립트 파일이다. 특징은 다음과 같다:
- 여러 사이트들이 동일한 파일을 사용한다.
- 공개되어도 되는 코드이다.
- 내용이 자주 바뀌지 않는다.
- 캐시할 수 있다.
즉, 이미지·폰트·CSS와 비슷한 정적 리소스이다.
/collect
collect.js가 실행된 뒤 API로 보내는 요청이다. 특징은 다음과 같다:
- 사이트별
id와 설치 이벤트, 분석 데이터 등의 전송을 담당한다. - 요청마다 내용이 다르다.
- 서버 검증이 필요하다.
- 데이터베이스를 변경한다.
- Origin 검증, Rate Limit, 개인정보 보호 정책이 적용될 수 있다.
즉, 일반적인 동적 API 엔드포인트이다.
왜 같은 도메인에 두지 않을까?
https://api.foundly.cc/collect.js
https://api.foundly.cc/collect
위와 같이 구성해도 기술적으로 문제없이 동작한다. 다만, collect.js 를 분리하면 다음과 같은 장점이 생긴다.
1. 캐싱 정책을 독립적으로 관리
collect.js는 같은 파일을 수많은 사이트가 요청한다. 따라서 CDN은 다음처럼 처리할 수 있다.
GET /collect.js
Cache-Control: public, max-age=3600
반면 /collect는 사이트마다 다른 데이터를 받으므로 캐시하면 안 된다.
POST /collect
Cache-Control: no-store
같은 도메인에 두더라도 경로별 설정은 가능하지만, 별도 도메인을 사용하면 정적 파일과 데이터 API의 운영 의도가 더 분명해진다.
2. 배포 주기를 분리
분리하지 않으면 collect.js 를 수정할 때 API Worker 배포가 불가피하다. 분리하면 다음이 가능하다:
collect.js 수정
→ CDN Worker만 배포
/collect 처리 로직 수정
→ API Worker만 배포
즉, 브라우저용 스크립트와 데이터 처리 서버는 변경 이유와 배포 주기가 다르기 때문에 독립적으로 관리하는 편이 편할 수 있다.
3. 장애와 트래픽 영향을 분리
분석 API에 갑자기 요청이 몰려도 정적 스크립트 파일 제공에는 영향을 덜 줄 수 있다. 반대로 API에 장애가 발생하더라도 사용자가 등록한 사이트가 collect.js 를 다운로드하는 것 자체는 계속 가능할 수 있다. 다만 이것은 완전한 장애 격리를 보장한다는 뜻은 아니다.
CDN 스크립트가 API에 의존하기 때문에 분석 데이터 전송은 별도로 실패할 수 있다.
4. 보안·WAF·Rate Limit 정책을 다르게 적용
정적 파일과 API는 보호 방식이 다르다.
| 대상 | 일반적인 정책 |
|---|---|
cdn.foundly.cc | 캐싱, 정적 파일 보안 헤더, 배포 버전 관리 |
api.foundly.cc | 인증·Origin 검증, Rate Limit, 요청 본문 검증, 로그·감사 |
API에는 요청량 제한과 데이터 검증이 중요하고, CDN에는 빠른 파일 전달과 캐시 무효화가 중요하다.
5. 도메인 이름이 책임을 설명
cdn.foundly.cc → 파일을 받는 곳
api.foundly.cc → 데이터를 처리하는 곳
요청 경로를 보았을 때 의도를 빠르게 이해할 수 있다.
두 방식 비교
| 기준 | API 도메인에서 함께 제공 | CDN 서브도메인으로 분리 |
|---|---|---|
| 예시 | api.foundly.cc/collect.js | cdn.foundly.cc/collect.js |
| 구현 난이도 | 낮음 | DNS·배포 설정이 추가됨 |
| 배포 | API와 함께 배포 | CDN과 API를 독립 배포 |
| 캐시 관리 | API 경로와 함께 관리 | 정적 파일에 최적화하기 쉬움 |
| 장애 격리 | 낮음 | 상대적으로 높음 |
| 운영 정책 | 하나의 도메인에 여러 성격 혼재 | 역할별 정책 분리 |
| 로컬 개발 | 단순함 | 로컬 CDN 포트와 API 포트가 필요할 수 있음 |
| 계약 관리 | 단순하지만 결합도가 높음 | 독립적이지만 호환성 관리 필요 |
| 작은 서비스 | 충분히 적합 | 다소 과할 수 있음 |
| 여러 고객·사이트 운영 | 점점 불편해질 수 있음 | 장기적으로 관리하기 쉬움 |
실제 환경에서의 흐름
1. 서비스가 설치 코드 생성
2. 사용자가 본인 서비스 코드에 script 태그 추가
3. 사용자 사이트가 CDN worker에서 collect.js 다운로드
4. collect.js가 실행됨
5. collect.js가 API Worker로 설치, 분석 이벤트 전송
6. API Worker가 이벤트를 수신하고 별도의 처리 과정을 거친 뒤 데이터를 DB에 저장
분리했을 때 주의할 점
캐시된 이전 버전
CDN에 캐시된 이전 collect.js가 일정 시간 동안 계속 제공될 수 있다. 대응 방법은 보통:
/collect.js?v=2
/collect-v2.js
/assets/collect.abc123.js
단, 설치 스니펫이 고정 URL을 사용한다면 배포 후 캐시 무효화 정책을 명확히 설정해서 관리해야 한다.
스크립트와 API의 호환성
CDN 스크립트가 먼저 배포되고 API가 나중에 배포되거나, 그 반대가 될 수 있다.
따라서, 새 API는 기존 스크립트 요청도 일정 기간 처리할 수 있어야 한다.
결론
따라서, /collect.js 를 API Worker와 다른 Worker로 관리하는 이유는 단순히 성능 때문이 아니라:
- 정적 파일 전달과 동적 데이터 처리를 분리
- 캐시·배포·장애 정책을 분리
- 각 Worker의 보안·운영 책임을 분리
- 도메인만 보아도 시스템 역할을 이해할 수 있게 구성
다만, 필수 규칙은 아니며 절대적인 정답 또한 아니다. 작은 규모라면 API Worker로 처리해도 충분하며, 규모가 커진다면 분리하는 것의 구조적 이점이 커지는 것 뿐이다.
*참고로, 여기서 언급한 CDN Worker는 cdn.domain.com 같이 그냥 기존 도메인의 서브 도메인을 말한 것이다. test.domain.com로 설정할 수도 있다. 중요한 것은 API Worker와 분리한다는 것이다.