본문으로 건너뛰기
피드

파비콘 안에 웹사이트를 숨겨 넣은 개발자, 진짜 됨

frontend 약 7분
vote
0
댓글
북마크

한 개발자가 웹사이트의 파비콘 이미지를 작은 저장소처럼 사용해 HTML을 픽셀 RGB 값 안에 넣고, 브라우저에서 다시 읽어 렌더링하는 실험을 했다. 208바이트짜리 HTML payload에 4바이트 길이 헤더를 붙여 총 212바이트를 만들었고, 이를 9x9 픽셀 PNG 안에 87% 사용률로 저장했다.

  • 1

    HTML 바이트를 PNG 픽셀의 RGB 채널에 직접 기록해 파비콘을 데이터 저장소처럼 사용했다

  • 2

    payload 208바이트와 4바이트 길이 헤더를 합쳐 212바이트를 저장했고, 9x9 픽셀 이미지면 충분했다

  • 3

    브라우저는 파비콘을 이미지로 로드하고 canvas API로 픽셀을 읽어 원래 HTML을 복원했다

  • 4

    다만 자바스크립트 부트스트랩 로더가 필요해서 파비콘 하나만으로 완전한 웹사이트가 되는 건 아니다

  • 어떤 개발자가 파비콘 안에 웹사이트 내용을 숨겨 넣는 실험을 함

    • 파비콘은 브라우저 탭에 뜨는 작은 아이콘임
    • 그런데 결국 파비콘도 이미지이고, 이미지는 픽셀이고, 픽셀은 바이트라는 식으로 접근함
    • 결론부터 말하면 됐음. 실용성은 별로 없지만 꽤 개발자스러운 장난임
  • 핵심 아이디어는 HTML을 픽셀의 RGB 값에 직접 밀어 넣는 것

    • 픽셀 하나에는 빨강, 초록, 파랑 값이 있고 각각 1바이트로 볼 수 있음
    • 그러면 픽셀 하나에 총 3바이트를 저장할 수 있음
    • 브라우저 입장에서는 그냥 색상값인데, 만든 사람 입장에서는 그게 HTML 바이트가 되는 셈임
  • 저장 과정은 생각보다 단순함

    • 먼저 작은 HTML payload를 TextEncoder로 UTF-8 바이트 배열로 바꿈
    • 그 앞에 payload 길이를 나타내는 4바이트 헤더를 붙임
    • 길이 헤더가 필요한 이유는 이미지 끝에 남는 빈 픽셀이 있을 수 있어서, 실제 데이터가 어디서 끝나는지 알아야 하기 때문임
    • 이후 바이트를 순서대로 첫 픽셀의 R, G, B 채널에 넣고 다음 픽셀로 넘어감

중요

> 실험에 쓰인 HTML payload는 208바이트였고, 4바이트 길이 헤더까지 합쳐 총 212바이트였음. 픽셀당 3바이트를 저장하니 9x9 픽셀, 즉 81픽셀짜리 이미지면 충분했음.

  • 최종 파비콘은 그냥 노이즈 이미지처럼 보임

    • 212바이트를 담기 위해 필요한 픽셀은 71개
    • 정사각형 이미지로 만들려면 최소 9x9 픽셀이 필요함
    • 9x9 이미지는 총 81픽셀이라 용량은 239바이트 수준이고, 실제 사용률은 87%였다고 함
    • 흔한 파비콘보다도 작은 이미지 안에 작은 HTML 문서가 들어간 셈임
  • 읽어오는 쪽도 브라우저 기본 기능만으로 처리함

    • 파비콘을 이미지로 로드함
    • 이미지를 캔버스에 그림
    • Canvas API로 픽셀 데이터를 읽음
    • RGB 값을 다시 바이트 배열로 재구성함
    • 앞의 4바이트에서 payload 길이를 읽고, 그 길이만큼 잘라 UTF-8 텍스트로 디코딩함
sequenceDiagram
    participant 파비콘
    participant 브라우저
    participant 캔버스
    participant 자바스크립트
    participant 문서
    브라우저->>파비콘: 이미지로 로드
    브라우저->>캔버스: 파비콘 그리기
    자바스크립트->>캔버스: 픽셀 RGB 값 읽기
    자바스크립트->>자바스크립트: 바이트 배열과 길이 헤더 복원
    자바스크립트->>문서: HTML로 디코딩해 렌더링
  • 중요한 단서는 ‘파비콘만으로 웹사이트가 완성되는 건 아니다’라는 점임

    • 파비콘에는 웹사이트의 내용, 즉 HTML payload가 들어 있음
    • 하지만 그걸 읽어서 복원할 자바스크립트 부트스트랩 로더는 별도로 필요함
    • 데모에서는 Render Website 버튼을 누르면 파비콘을 읽고, HTML을 디코딩한 뒤, 현재 페이지를 복원된 내용으로 바꾸는 방식임
  • 대안도 몇 가지 있음

    • SVG 파비콘 안에 마크업을 직접 넣고 페이지 로드시 읽는 방식
    • PNG의 tEXt, zTXt, iTXt 같은 comment chunk를 쓰는 방식
    • 여러 해상도의 아이콘을 담을 수 있는 ICO 포맷을 활용하는 방식
    • 다만 이 글의 재미는 그런 정석 저장 공간이 아니라 RGB 픽셀을 억지 저장소로 쓴 데 있음
  • 실용성은 거의 없음

    • 저장 가능한 데이터가 너무 작음
    • 어차피 복원용 자바스크립트가 필요함
    • 작은 HTML 문서를 배포하는 더 좋은 방법은 이미 널림
    • 그래도 웹 플랫폼이 어디까지 말이 되는지 찔러보는 실험으로는 꽤 맛있음

기술 맥락

  • 이 실험의 선택은 “파비콘을 저장소로 쓴다”는 거예요. 파비콘은 원래 아이콘이지만, 브라우저가 결국 이미지 파일로 로드하니까 픽셀 RGB 값을 데이터 슬롯처럼 다룰 수 있거든요.

  • 왜 굳이 RGB 채널이냐면 구현이 제일 직관적이기 때문이에요. HTML 문자열을 UTF-8 바이트로 바꾸고, 그 바이트를 R, G, B 순서로 넣으면 별도 포맷 설계 없이도 데이터를 이미지 안에 심을 수 있어요.

  • 길이 헤더 4바이트를 앞에 붙인 것도 꽤 중요한 결정이에요. 9x9 이미지는 81픽셀이라 총 239바이트를 담을 수 있지만 실제 payload는 208바이트라 빈 공간이 남거든요. 어디까지가 진짜 HTML인지 모르면 디코딩 결과가 깨질 수 있어요.

  • 복원 단계에서 Canvas API를 쓴 이유는 브라우저 안에서 이미지 픽셀을 직접 읽을 수 있는 표준적인 방법이기 때문이에요. 파비콘을 캔버스에 그리고 RGB 값을 다시 바이트 배열로 만들면, 서버나 네이티브 코드 없이도 원래 HTML을 되살릴 수 있어요.

  • 다만 이건 배포 기술이라기보다 경계 실험에 가까워요. 파비콘에 내용은 들어가지만 실행을 시작할 로더는 별도로 필요해서, ‘웹사이트 전체가 파비콘 하나에 들어갔다’기보다는 ‘웹사이트 콘텐츠를 파비콘 픽셀에 숨겼다’가 더 정확해요.

쓸모는 거의 없지만 웹 플랫폼의 경계가 어디까지 열려 있는지 보여주는 꽤 재밌는 실험이다. 특히 ‘이미지는 픽셀이고 픽셀은 바이트’라는 단순한 관찰이 브라우저 API와 만나면 이런 장난도 실제 구현이 된다.

댓글

댓글

댓글을 불러오는 중...

frontend

스크린이 절대 못 보여주는 색은 어디에 있을까

이 글은 우리가 화면에서 보는 색이 인간이 볼 수 있는 색 전체가 아니라, sRGB와 Display-P3 같은 색역 안에 갇힌 일부라는 점을 파고든다. 특히 숲, 바닷속, 새와 나비의 구조색, 생물발광, 교통신호 LED 같은 실제 세계에는 모니터와 카메라가 제대로 담지 못하는 청록색과 녹색 계열이 꽤 많다는 얘기다. 디스플레이, 카메라, 조명, 렌더링을 다루는 개발자라면 “색상값 하나”가 생각보다 물리와 표준의 타협이라는 걸 체감하게 된다.

frontend

크롬, 매니페스트 버전 2 우회로까지 닫는다

구글 크롬이 매니페스트 버전 2 확장 지원을 사실상 최종 종료 단계로 밀어넣고 있다. 기존에는 플래그나 레지스트리 설정으로 유블록 오리진 같은 확장을 살리는 우회가 있었지만, 크로미움 150과 151을 거치며 그 우회 코드까지 제거되는 흐름이다.

frontend

HTML 우선 사이트로 전환했더니 신청 완료자가 하룻밤 사이 2배가 된 이야기

한 공공성 강한 유틸리티 회사가 React 기반 신청 폼 실패 뒤, Astro와 HTML 우선 구조로 다시 만들었더니 폼 완료자가 출시 직후 2배로 늘어남. 핵심은 자바스크립트 없이도 동작하는 페이지별 폼, 서버 저장 세션, 접근성, 점진적 향상이었음.

frontend

Linear가 빠른 이유? 브라우저 안에 DB를 넣고 서버를 ‘동기화 대상’으로 밀어낸 설계

Linear의 속도는 특정 프레임워크나 마법 같은 최적화가 아니라, 브라우저 로컬 DB, 낙관적 업데이트, 세밀한 MobX 반응성, 공격적인 코드 스플리팅, 서비스 워커 캐싱, 키보드 중심 UX가 쌓인 결과다. 전통적인 CRUD 앱이 이슈 업데이트에 약 300ms를 쓰는 동안 Linear는 사용자가 체감하기 전에 로컬 상태를 먼저 바꾸고 서버와는 나중에 맞춘다.

frontend

Vite 만든 VoidZero가 Cloudflare로 합류, 핵심은 ‘Vite는 계속 벤더 중립’이라는 약속

Vite, Vitest, Rolldown, Oxc, Vite+를 만드는 VoidZero 팀이 Cloudflare에 합류한다. Cloudflare는 Vite 생태계가 계속 오픈소스, 벤더 중립, 커뮤니티 주도로 유지된다고 강조하면서 100만 달러 규모의 생태계 펀드도 약속했다.