Record/Trubble Shooting

[Trouble Shooting] 지도 포함 리포트 이미지 삽입 문제 해결

범데이 2026. 2. 18. 01:26

최근에 참여했던 프로젝트에서 출장 여비정산 리포트를 개발 과업 중에

“주행거리 증빙 화면(지도 포함)”을 이미지로 캡쳐해 ClipReport에 삽입해야 하는 과정이 있었다.

 

요구사항은 단순해 보였다.

  • 지도 화면이 반드시 포함된 상태로 캡쳐되어야 하고
  • 매번 다른 주행 경로를 동적으로 캡쳐해야 하며
  • 최종 결과는 리포트 안에서 이미지로 출력되어야 했다

하지만 실제 구현은 예상보다 훨씬 복잡했다.

 

이번 이슈는 아래와 같은 환경에서 발생했다.

  • Report: ClipReport
  • Frontend: React
  • Backend: Spring (Java)
  • Screenshot: Playwright (Headless Chromium)
  • Map SDK: Kakao Map SDK

그리고 문제는 단순히 “캡쳐가 안 된다” 수준이 아니었다.

 

이슈는 크게 세 가지 층으로 나뉘었다.

  1. 브라우저 보안(CORS)
  2. 서버 OS ↔ Playwright(Chromium) ↔ Java 패키지 호환
  3. 지도 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 로깅
  • 이게 없었으면 “지도 안 뜨는 이유”는 끝까지 감으로만 맞췄을 것이다
728x90
반응형