최근에 참여했던 프로젝트에서 출장 여비정산 리포트를 개발 과업 중에
“주행거리 증빙 화면(지도 포함)”을 이미지로 캡쳐해 ClipReport에 삽입해야 하는 과정이 있었다.
요구사항은 단순해 보였다.
- 지도 화면이 반드시 포함된 상태로 캡쳐되어야 하고
- 매번 다른 주행 경로를 동적으로 캡쳐해야 하며
- 최종 결과는 리포트 안에서 이미지로 출력되어야 했다
하지만 실제 구현은 예상보다 훨씬 복잡했다.
이번 이슈는 아래와 같은 환경에서 발생했다.
- Report: ClipReport
- Frontend: React
- Backend: Spring (Java)
- Screenshot: Playwright (Headless Chromium)
- Map SDK: Kakao Map SDK
그리고 문제는 단순히 “캡쳐가 안 된다” 수준이 아니었다.
이슈는 크게 세 가지 층으로 나뉘었다.
- 브라우저 보안(CORS)
- 서버 OS ↔ Playwright(Chromium) ↔ Java 패키지 호환
- 지도 SDK 도메인(URL) 매핑 문제
그리고 이 복잡한 문제들을 하나로 관통한 키워드는 로깅(Logging) 이었다.
1. 문제 배경: “지도 포함 주행거리 증빙 캡쳐” 요구사항
처음 목표는 아래처럼 정리할 수 있었다.
- 특정 URL(증빙 페이지)을 로드한다
- 페이지가 렌더링 완료되면 스크린샷을 찍는다
- 생성된 이미지 파일을 ClipReport에서 출력한다
말만 보면 쉬운데, “지도(카카오맵/타일)”이 끼어들면서 난이도가 급상승했다.
2. 1차 시도: 프론트(html2canvas) 캡쳐 — 브라우저 보안(CORS)에 의해 실패
처음에는 React 화면에서 html2canvas 기반으로 캡쳐해서 증빙 이미지를 만들려고 했다.
2.1 시도한 방식
- html2canvas(contentRef.current) 로 화면을 캡쳐
- 캡쳐된 이미지를 다운로드 또는 서버 업로드
- 리포트에서 해당 이미지 출력
2.2 문제: 지도 타일/외부 이미지 CORS 차단
지도는 외부 타일 이미지(daumcdn 등)를 끌어온다.
이 과정에서 브라우저 보안 정책이 바로 발목을 잡았다.
- 타일 이미지 로딩 자체는 브라우저에서 되지만,
- 캔버스에 그리는 순간 CORS 정책 때문에 canvas 오염(tainted) 이 되고
- 결과적으로 캡쳐 이미지에서 지도 영역이 누락/빈칸 처리되었다.

결론: “프론트에서 보이는 화면을 캡쳐” 방식은
지도/외부 리소스가 포함되면 구조적으로 실패할 가능성이 높다.
따라서 방향을 변경했다.
3. 방향 전환: 서버에서 페이지를 직접 요청/렌더링해서 캡쳐하자
브라우저 보안에 막혔으니, 서버가 Headless Chromium을 실행해서 캡쳐하도록 구조를 전환했다.
즉,
- 서버가 Playwright로 브라우저를 띄우고
- 해당 URL에 접속해 렌더링한 뒤
- 렌더링 결과를 스크린샷으로 저장한다
이 방식은 “보이는 그대로”를 캡쳐하기에 적합하다.
4. 2차 장애: 서버 OS ↔ Playwright 버전/Chromium 호환 문제
서버에서 Playwright를 올리고 실행하는 순간, 다음과 같은 문제가 발생했다.
- Chromium이 정상 구동되지 않거나
- OS 지원 경고가 뜨거나
- fallback build 다운로드로 인해 불안정하게 동작했다
여기서 포인트는 “코드가 잘못됐다”가 아니라:
서버 OS 환경과 Playwright/Chromium 빌드가 맞지 않으면
브라우저 자체가 실행되지 않는다.
4.1 해결: Playwright 버전 다운그레이드
결국 이 문제는 Playwright 버전을 낮추는 방향으로 해결했다.
- 최신 버전은 서버 OS(또는 glibc 등)와 호환이 애매했고
- 낮춘 버전에서는 Chromium이 정상 실행되었다
✅ 결과:
- 페이지 접속 성공
- 스크린샷 생성 성공
여기까지 오면 끝일 줄 알았지만, 진짜 문제는 다음 단계였다.
5. 3차 장애: “캡쳐는 되는데 지도 SDK가 안 뜬다”
Chromium이 뜨고 캡쳐도 된다.
그런데 결과 이미지에 지도만 안 나온다.
- 페이지는 정상
- 텍스트/레이아웃도 정상
- 그런데 지도 영역만 비어 있음
이 시점부터는 코드 문제가 아니라 환경/네트워크/보안/SDK 정책 영역으로 넘어간다.
6. 이때부터 결정적으로 중요해진 것: 로깅(Logging)
여기서부터는 감으로 때려맞추면 시간이 10배 걸린다.
내가 했던 핵심은:
6.1 서버 네트워크 레벨 검증
- 서버에서 curl 로 해당 URL 접근 가능한지 확인
- 지도 SDK 스크립트 주소 접근 가능한지 확인
- 외부망 라우팅/DNS/방화벽 이슈 가능성 제거
6.2 Playwright 브라우저 로그 출력
브라우저 단에서만 보이는 에러가 있다.
따라서 로그를 찍었다.
- console log
- requestfailed
- response status
- network trace
이걸 통해 결론적으로 파악한 핵심은:
지도 SDK는 “등록된 URL(도메인)”에서만 정상 동작한다.
7. 근본 원인: 지도 SDK는 허용된 URL/도메인이 일치해야 로드된다
내 서버 캡쳐 환경에서는 다음과 같은 차이가 있었다.
- 원래 SDK 호출 기준 URL이 내부망 기반(172.x.x.x)으로 잡혀 있었고
- 로컬/외부에서 접근할 때는 외부망 IP로 호출되었다
지도 SDK는 도메인 정책이 강해서:
- 등록된 URL과 다르면
- SDK가 로드되지 않거나 일부 기능이 차단된다
8. 해결: 지도 SDK가 인식하는 URL을 “등록된 주소로 통일” (URL 매핑)
따라서 해결책은 단순했다.
- 서버에서 지도 SDK를 호출할 때도
- SDK에 등록된 URL과 동일한 형태로 인식되도록
- 내부망 IP → 외부망 IP 기반 호출로 변경
- 로컬 호출 환경과 동일한 IP/도메인 기준으로 통일
✅ 결과:
- 서버 Playwright 환경에서도 지도 SDK 정상 로드
- 지도까지 포함된 증빙 화면 캡쳐 성공

9. 최종 결론: 이번 이슈의 핵심 정리
이번 문제는 “기술”이 여러 층으로 겹쳐져 있었다.
(1) 브라우저 보안(CORS)은 피할 수 없는 장벽
- 지도/외부 리소스 포함 캡쳐는 프론트 방식에 한계가 있다
- 서버 캡쳐 방식이 정답에 가깝다
(2) 서버 캡쳐는 OS 호환성이 승패를 가른다
- Playwright 최신이 항상 정답이 아니다
- 서버 OS / glibc / 패키지 호환성이 맞아야 Chromium이 뜬다
(3) 지도 SDK는 “URL 정책” 때문에 예상치 못하게 실패한다
- SDK는 허용 도메인 기반으로 동작한다
- 서버에서 띄우면 host/origin이 달라져서 SDK가 막힐 수 있다
- 해결은 “등록된 URL로 통일(매핑)”이다
(4) 결정타는 로깅이었다
- curl로 네트워크 확인
- Playwright에서 console/network 로깅
- 이게 없었으면 “지도 안 뜨는 이유”는 끝까지 감으로만 맞췄을 것이다
'Record > Trubble Shooting' 카테고리의 다른 글
| IIS API 요청 실패 및 DB 연결 문제 해결 (0) | 2025.01.26 |
|---|---|
| [Python] pip 명령어 문제 해결: Fatal error in launcher: Unable to create process using '"' (2) | 2023.10.07 |
| C# ASP.NET에서 JavaScriptSerializer maxJsonLength 초과 에러 발생 해결 (0) | 2023.06.26 |
| [Postgresql14] postgres service가 에러로인해 시작되지 않던 문제 해결 (0) | 2023.02.01 |
| [NPM] 사용했던 Package가 업데이트되면서 발생되던 버그 해결 (0) | 2022.12.08 |