0. Boini 소개
Boini는 실시간 프레젠테이션 상호작용 플랫폼이다. 발표자와 청중이 발표 중 실시간으로 소통하고(이모지/질문), 발표 종료 후에는 AI 기반 리포트로 슬라이드 반응과 질문을 분석할 수 있다.
서비스 특성상 발표 자료가 바로 떠야 실시간 상호작용이 가능하므로, 발표 자료 업로드 속도는 UX의 핵심이다.
1. MVP에서 구현한 PDF 업로드 흐름
MVP에서는 발표자가 PDF를 올리면 프론트에서 PDF를 이미지로 변환한 뒤 S3에 저장하고, 서버는 이미지 URL만 DB에 저장하는 구조로 구현했다.
MVP 흐름
- 발표자: PDF 업로드
- 프론트: PDF → 이미지(N장) 변환
- 브라우저: 변환된 이미지 N장을 S3 업로드
- 서버: 업로드된 이미지 URL을 DB에 저장
- 청중: 클라이언트가 요청하면 서버가 DB의 이미지 URL 리스트만 응답
이 방식은 구현이 단순하고 슬라이드 이미지만 내려주면 되니까 청중 뷰도 만들기 수월했다.
하지만 런칭을 하기에는 문제가 있는 방식이다.
2. 현재 방식의 문제점: 페이지 수 × 이미지 용량만큼 느려진다
지금 구조는 업로드 시간이 다음과 같이 늘어난다.
- PDF 페이지가 많아질수록 변환 시간이 증가
- 이미지가 고해상도일수록, PNG일수록 업로드 시간이 증가
- 페이지 수가 50장이면 변환 50번 + 업로드 50번이 발생
즉, 총 지연시간 ≈ (변환 비용 + 업로드 비용) × 페이지 수로 커지는 구조다.
병목의 정체
이 문제는 보통 두 가지가 겹쳐서 터진다.
- 브라우저 부하
- PDF 렌더링/캔버스 변환은 CPU를 많이 먹는다
- 변환 중 UI가 버벅이거나, 업로드 진행이 “멈춘 것처럼” 보인다
- 네트워크 요청 비효율
- 페이지마다 파일 업로드 요청이 발생 (N번)
- 서버 저장도 페이지 단위로 하면 API/DB write가 계속 발생 (N번)
결론적으로 사용자는 업로드가 너무 느리다고 느끼게 됨
3. 해결 방법
MVP 이후 런칭 준비 단계에서는 체감 속도를 끌어올리는 게 중요한다.
그래서 해결책을 크게 두개로 나눴다.
- A. 현재 구조 유지하되, 프론트엔드에서 적용 가능한 최적화를 한다. (Quick Wins)
- B. 구조 자체를 바꿔서 백엔드 비동기 처리로 근본적인 원인을 제거한다.
4. A안: 지금 구조를 유지하면서 인프라를 크게 안 늘리고 개선할 수 있는 방법 : Quick Wins
인프라를 크게 늘리지 않고도 바로 체감되도록 할 수 있는 개선들이 있다.
4-1) 이미지 용량 줄이기: WebP/JPEG + 해상도 낮추기
업로드 시간은 대부분 파일 크기에 비례한다.
- PNG → WebP/JPEG로 변환
- 슬라이드 이미지 적정 해상도로 낮추기
효과: 전송량이 줄어 업로드가 즉시 빨라짐
4-2) 업로드 병렬화 (동시성 3~6)
페이지를 1장씩 순차 업로드하면 페이지 수가 늘수록 최악이 된다.
- 업로드를 3~6개 정도 병렬로 처리
- p-limit 같은 방식으로 동시성 제한
효과: 페이지 수가 많을수록 체감이 크게 좋아짐
4-3) Progressive UX: 1~2페이지 먼저 보여주기
사용자는 “전체 완료”보다 첫 화면이 빨리 뜨는 것에 훨씬 민감하다 -> 이탈률 증가 .
- 전체 업로드가 끝날 때까지 기다리게 하는 대신, 1~2페이지가 준비되면 즉시 발표 시작 화면으로 갈 수 있도록 만들기
- 뒷부분은 백그라운드에서 계속 업로드
효과: 총 시간은 같아도 “엄청 빨라진 느낌”을 만든다
4-4) Presigned URL로 S3 직접 업로드
이미지를 서버를 거쳐서 올리면 서버가 병목이 될 수 있다.
- 서버는 Presigned URL만 발급
- 프론트는 S3에 직접 PUT/POST 업로드
- 서버는 메타데이터만 저장
효과: Presigned URL 이미지를 API 서버를 거쳐 S3로 보내지 않고, 프론트엔드에서 S3로 직접 업로드하여 서버 병목을 제거
B안을 선택할 시에도
A안 중 포맷/해상도 정책, presigned 업로드 패턴, progressive UX는 B안에서도 그대로 재사용한다.
5. B안: PDF 1회 업로드 + 백엔드 비동기 변환
A안은 급한 상황에 대처하는 방법이라면, B 는 대용량 자료나 사양 낮은 기기에서 발표자료를 업로드 했을때에 업로드 속도가 느려지는 이슈를 근본적으로 해결할 수 있는 방안이다.
핵심 아이디어
이미지 N장을 업로드하는 구조를 없애고, PDF 1개만 업로드한다.
그리고 변환은 서버에서 비동기로 처리한다.
흐름
- 발표자 클라이언트 → Presigned URL로 PDF를 S3에 직접 업로드
- S3 업로드 이벤트 → Lambda/Webhook
- 변환 작업을 SQS에 enqueue
- 워커가 페이지 단위 병렬 변환(WebP/적정 해상도)
- 변환된 결과 URL을 DB에 저장
- API는 업로드 직후 jobId + processing 반환
- 클라이언트는 폴링/웹소켓으로 상태 구독
- 썸네일 먼저 노출 → 전체 슬라이드 순차 로드
- CloudFront 캐시 + 워커 재시도/에러 핸들링으로 안정성 확보
6. 왜 백엔드 비동기 변환을 선택했나?
다른 선택지도 있었지만(예: 청중이 pdf.js로 직접 렌더링), Boini의 서비스 성격에서는 비동기 변환이 가장 적절하다고 생각했다.
6-1) UX 우선: 업로드 즉시 응답 + 진행률 + 썸네일
업로드 후 바로 jobId를 받고 처리중 상태를 보여주면 사용자는 멈춘 느낌이 없다.
또 썸네일/앞부분부터 보여주면 발표자는 기다리지 않고 바로 진행할 수 있다.
6-2) 성능 및 확장성
변환은 무겁고 예측하기 어려운 작업이다.
이걸 API 요청 경로(동기 처리)로 두면 서버가 쉽게 밀린다.
워커로 분리하면:
- 트래픽이 늘어도 API는 안정적
- 워커 개수로 처리량을 조절할 수 있어 비용/성능 밸런스가 좋다
6-3) 안정성
큐 기반 구조는 실패 복구가 체계적이다.
- 재시도 정책
- 데드레터 큐(DLQ)
- 타임아웃/부분 실패 처리
같은 운영 요소를 붙이기 쉽다.
6-4) 포맷/해상도 정책을 서버에서 일관되게 제공
클라이언트 변환은 브라우저와 기기마다 차이가 크다.
→ 서버에서 변환하면:
- 해상도/품질/포맷(WebP 등)을 일관되게 적용
- 캐시 전략까지 통제 가능
7. 정리
Boini의 MVP 구조는 기능 구현에는 빠르지만, 런칭 환경에서는 페이지 수 × 이미지 용량만큼 지연이 커진다.
따라서 런칭을 위해서는
- 전송량을 줄이고(WebP/다운스케일)
- 네트워크 요청을 효율화하며(병렬/배치/presigned)
- 사용자가 기다리지 않게(progressive UX) 만들고,
- PDF 1회 업로드 + 비동기 변환 으로 구조를 개선하는 것이 가장 안정적이다.
특히, 아래의 경우가 런칭했을 때에는 빈번할 것이라고 생각하여, 이 방법이 적절하다고 판단하였다.
- 발표 자료가 30~100페이지처럼 커질 가능성이 높다
- 다양한 기기( 예를 들어 저사양 노트북/모바일)에서도 일정한 UX가 필요하다
- 운영 안정성(재시도, 실패 복구, 모니터링)이 중요하다
- 트래픽이 늘어날 수 있다.
추가) 화면 캡쳐 방지
보안에 관련하여 팀원들과 가장 많이 논의했던 주제 중 하나는 캡처 방지였다.
금융 앱들처럼 캡처를 시도하면 "보안 정책에 따라 캡처할 수 없습니다"라는 토스트 메시지를 띄우고 검은색 화면만 나오게 하고 싶었다.
하지만 웹 환경의 보안 모델을 깊게 공부하면서, 웹에서 캡처 차단은 기술적으로 불가능하다는 결론에 도달했다.
그 이유와 차선책을 정리해보았다.
7-1. 왜 웹에서는 캡처를 막을 수 없을까? (Browser Sandbox)
Android나 iOS는 OS에게 보안 앱이니까 화면을 건들지말라고 요청할 수 있는 권한(FLAG_SECURE등)이 있다. 하지만 웹은 다르다.
- 샌드박스의 한계: 브라우저는 사용자의 안전을 위해 OS의 핵심 기능에 접근하지 못하도록 되어 있다.JavaScript는 브라우저가 렌더링한 DOM 내부만 제어할 수 있고, OS 차원에서 실행되는 스크린샷·화면 녹화 기능에는 접근하거나 제어할 권한이 없다.
- 렌더링 주체의 차이: 화면을 그리는 건 브라우저지만, 그 화면을 보고 있는 건 운영체제의의 윈도우 매니저다. JavaScript에서 어떤 이벤트를 막더라도, OS 레벨에서 발생하는 캡처 명령은 브라우저를 거치지 않고 실행된다.
- OTT의 캡처 방지에 대한 오해 : 넷플릭스 같은 OTT 서비스가 캡처를 막는 것처럼 보이는 이유는EME(Encrypted Media Extensions) 기반 DRM으로 비디오 스트림 자체만 암호화하기 때문이다.이는 영상 재생에 특화된 방식이며, HTML 텍스트나 차트처럼 DOM으로 렌더링되는 일반 웹 UI에는 적용할 수 없다.
결국 웹에서 캡쳐를 막는것은 기술적으로 불가능하다😭
7-2. OTT의 경우
우리가 시청하는 넷플릭스, 티빙과 같은 OTT 서비스에서는 화면 캡처나 화면 녹화가 제한되어 있다.
그래서 처음에는 “발표자료도 이런 캡처 방지를 적용할 수 있지 않을까?” 라는 생각이 자연스럽게 들었다.
하지만 결론부터 말하자면, OTT에서 사용하는 캡처 방지 기술과 웹 기반 프레젠테이션 서비스에서의 콘텐츠 노출은 전제가 완전히 다르다.
이 차이를 이해하기 위해서는 먼저 OTT 서비스에서 사용되는 DRM 기술을 이해할 필요가 있다.
DRM이란 무엇인가?
DRM(Digital Rights Management, 디지털 저작권 관리)은
디지털 콘텐츠가 허가된 사용자에게만 재생되도록 제어하는 기술이다.
OTT 서비스에서 DRM은 다음과 같은 역할을 한다.
- 콘텐츠를 암호화하여 저장 및 전송
- 합법적인 사용자에게만 복호화 키(라이선스) 발급
- 재생 환경(OS, 브라우저)에 따라 맞는 DRM 적용
- 재생 중 녹화·캡처 시도를 운영체제 레벨에서 차단
즉, DRM은 단순히 파일을 숨기는 기술이 아니라,
운영체제와 브라우저, 전용 미디어 플레이어가 함께 동작하는 보안 체계라고 볼 수 있다.
넷플릭스에서 영상이 캡처되지 않는 이유도,
영상이 일반적인 이미지나 파일 형태로 노출되는 것이 아니라
OS 레벨의 미디어 파이프라인 안에서만 재생되기 때문이다.
그렇다면 발표자료에도 DRM을 적용할 수 있을까?
결론적으로 OTT에서 사용하는 DRM을 발표자료 서비스에 그대로 적용하는 것은 어렵다.
그 이유는 콘텐츠의 재생 방식과 노출 구조 자체가 다르기 때문이다.
| 구분 | OTT 서비스 | Boini 발표 서비스 |
| 콘텐츠 형태 | 영상 스트리밍(HLS/DASH) | PDF → 이미지 슬라이드 |
| 재생 방식 | OS 미디어 플레이어 | 웹 브라우저 렌더링 |
| DRM 적용 지점 | OS/브라우저 레벨 | 적용 불가 |
| 캡처 차단 | 가능 | 구조적으로 불가능 |
웹에서 슬라이드가 화면에 보인다는 것은, 결국 브라우저가 이미지를 렌더링해 픽셀로 출력했다는 뜻이다.
이 시점부터는 운영체제의 스크린샷, 화면 녹화, 외부 촬영까지 어떤 방식으로도 완전한 차단이 불가능하다.
즉, 웹 기반 서비스에서 보여지는 순간의 캡처를 기술적으로 100% 막는 것은 불가능하다.
이는 구현의 문제가 아니라 웹 플랫폼의 구조적 한계다.
7-3. 대책
1. 워터마크
캡처를 못 하게 하는 게 아니라, 하면 청중의 정보가 뜨도록
- 청중별 동적 워터마크
- 사용자 이름 / 이메일 / 세션 ID / 시간
- 슬라이드 위에 반투명 반복 배치
- 페이지마다 위치/각도 랜덤화
효과)
- 유출 시 누가 캡처했는지 추적 가능
- 현업에서 가장 많이 쓰는 방법
2. 실시간 세션 기반 접근 제어
- 발표 중에만 슬라이드 접근 가능
- 세션 종료 시:
- URL 만료
- presigned URL TTL 짧게 설정
- 새로고침/뒤로가기 시 재인증
효과)
- 나중에 몰래 다시 열어보기 차단
- 라이브 발표 전용 자료라는 인식 강화
3. 해상도 제한 + 확대 방지
- 청중 화면은
- 표시용 해상도만 제공
- 고해상도 원본은 서버에만 존재
- CSS/JS로:
- 우클릭 비활성화
- 텍스트 선택 방지
- 확대/드래그 제한
한계)
- 캡처 자체는 막지 못하지만 쓸모 없는 캡처로 만들 수 있음
4. 캡처 시도 감지
완전한 방지는 아니지만 이런 시그널은 감지 가능:
- visibilitychange
- blur/focus 이벤트
- 모바일에서 스크린 레코딩 감지
- 발표 중 이탈 패턴 로그
활용)
- 캡처 의심 이벤트 로그
- 워터마크 강화
- 발표자에게 알림 (선택)
5. UX로 억제
기술만으로는 부족해서 명시적 고지가 중요함.
예:
- 본 발표 자료는 캡처 및 외부 공유를 금지합니다
- 캡처 시 사용자 식별 정보가 포함됩니다
- 입장 시 동의 체크
실제로 이 문구 하나만으로도 유출률이 크게 줄어듭니다.
현실적인 보안 대책
1️⃣ 청중별 동적 워터마크
2️⃣ 세션 종료 시 접근 차단 + presigned URL 만료
3️⃣ 청중용 저해상도 렌더링
4️⃣ 캡처 금지 고지 + 로그 수집
8. 마치며
프론트엔드 개발자로서 100% 완벽한 보안을 장담할 수는 없다. 하지만 적어도 브라우저에 흔적을 남기지 않고, 보안 사고가 났을 때 로그를 통해 유출되지 않도록 막고, 실패했을 때도 안전하게 실패하도록 하는것이 현재 상황에서 최선의 방법이라고 생각한다.

'FE' 카테고리의 다른 글
| React Suspense + Error Boundary로 안정적인 로딩/에러 핸들링 (1) | 2026.01.03 |
|---|---|
| 건강검진 결과 시각화 : 차트 라이브러리 선정 및 구현 전략 (0) | 2026.01.01 |
| TanStack Query 낙관적 업데이트: onMutate + Rollback을 찜 해제에 적용 (0) | 2025.12.19 |
| [멋사] 2025 4호선톤 회고 (0) | 2025.11.17 |
| 전역 상태 라이브러리 (1) | 2025.11.17 |