JavaScript 없이 게임을 돌리는 극단적인 서버 사이드 렌더링 실험

웹 페이지를 동적으로 움직이려면 프론트엔드 JavaScript가 필요하다고 생각하기 쉬워요. 한 개발자가 이 전제를 뒤집는 실험을 공개했어요. HTTP 응답을 끝내지 않고 서버가 HTML과 CSS를 계속 보내면, JavaScript 한 줄 없이도 화면이 실시간으로 바뀌어요. 이 방식으로 Flappy Bird 클론까지 만들었어요. 1
핵심 요약
| 구분 | 핵심 | 왜 볼 만한가요 |
| 실험 방식 | HTTP 연결을 닫지 않고 HTML/CSS를 계속 스트리밍해 화면을 갱신 | 브라우저 렌더링의 기본 동작을 활용한 발상 전환 |
| 사용자 입력 | 숨겨진 iframe으로 폼을 제출해 페이지 새로고침 없이 전달 | JavaScript 없이 양방향 통신을 흉내 낸 방법 |
| 데모 | 물리 엔진이 서버에서 돌고 초당 60회 CSS를 보내는 Flappy Bird 클론 | 프론트엔드 JS를 꺼도 똑같이 작동해요 |
| 부작용 | 끝나지 않는 응답이 Pleroma 링크 미리보기를 무한 대기시켜 서버 장애 유발 | 스트리밍 응답을 처리하는 서비스의 보안 결함으로 이어졌어요 |
1. 발상: 브라우저는 도착한 대로 그린다
실험의 출발점은 단순해요. HTTP 서버는 응답을 글자 단위로 전송하는데, 브라우저는 도착한 HTML을 기다리지 않고 그 즉시 화면에 그려요. 저자는 netcat으로 직접 사람 속도로 응답을 천천히 입력해도 Firefox와 Chromium 둘 다에서 페이지가 실시간으로 렌더링되는 것을 확인했어요. 2
여기에 CSS도 통했어요. 서버가 나중에 보내는 CSS가 바로 적용되므로, 요소의 위치를 계속 바꾸거나 기존 요소를 숨기고 새 요소를 추가할 수 있었어요. 연결을 무기한 열어 두면 서버가 페이지를 계속 갱신할 수 있다는 뜻이에요. 저자는 이 방식을 XSSR, 극단적인 서버 사이드 렌더링이라고 불렀어요.
기존 SSR과 비교하면 차이가 명확해요. 구식 SSR은 서버가 요청마다 HTML을 만들지만 페이지를 로드된 뒤로는 정적이에요. 신식 SSR은 상호작용 시 프론트엔드 JavaScript가 서버에 요청하고 받은 HTML을 삽입하는 방식이라, 프론트엔드 로직을 유지해야 해요. XSSR은 이 둘 사이에서 프론트엔드 코드를 완전히 없애는 길을 택했어요. 2
2. 남은 문제: 입력은 어떻게 받나
화면을 서버가 밀어내는 것만으로는 부족해요. 사용자 입력도 받아야 해요. JavaScript 없이 입력을 보내는 표준 방법인 form은 제출할 때 페이지를 다시 로드하므로, 연결을 유지해야 하는 XSSR의 목적에 정면으로 반해요.
저자의 지인이 제안한 숨겨진 iframe 기법이 이 문제를 풀어요. 화면에 보이지 않는 iframe에 이름을 붙이고 form의 target을 그쪽으로 지정하면, 제출 시 메인 페이지 대신 iframe만 다시 로드돼요. 여기에 서버가 최초 로드 때 생성한 uuid를 숨겨진 필드로 함께 보내 이벤트마다 어떤 사용자의 요청인지 구분해요. 2
3. 결과: 초당 60프레임으로 도는 Flappy Bird
이 조합으로 저자는 Flappy Bird 클론을 만들었어요. 점수 표시가 바뀌고, 파이프가 움직이고, 클릭하면 새가 점프해요. 입력은 숨겨진 iframe 기법과 화면 전체를 덮는 보이지 않는 버튼으로 처리해서 화면 어디서든 클릭할 수 있고, 한 번 클릭해 버튼을 선택하면 스페이스와 엔터로도 조작돼요. 1
동작 구조가 흥미로워요. 물리 엔진은 전부 백엔드에서 실행되고, 서버가 파이프와 새의 위치, 점수를 갱신하는 CSS를 60 FPS로 계속 보내요. 응답이 끝나지 않으니 게임하는 내내 브라우저의 로딩 표시가 멈추지 않아요. 브라우저에서 JavaScript를 꺼도 똑같이 작동하고, curl로 요청하면 끝없이 흘러나오는 CSS를 직접 확인할 수 있어요. 소스 코드도 공개돼 있어요. 2
성능도 생각보다 나빠지지 않았어요. 단순한 Rust 서버 구현으로 오래된 홈서버에서 코어당 약 500개 게임을 동시에 돌릴 수 있었고, 이는 적당히 복잡한 Ruby on Rails 앱보다 낫다고 저자는 평가해요. 게임 하나가 쓰는 대역폭은 약 20 KiB/s로, 49초 정도 플레이해야 1MB짜리 JavaScript 앱을 한 번 로드하는 것과 비슷해요. 2
약점: 거리가 곧 지연
단점은 뚜렷해요. 클릭할 때마다 서버의 물리 엔진에 입력을 반영하고 새 위치를 돌려받는 왕복이 필요한데, 이 지연은 대부분 네트워크 전송 시간이 차지해요. 서버가 있는 캐나다 대서양 연안 근처에서는 잘 돌아가지만, 북미 동부 남쪽에서는 조금 일찍 클릭해 보정할 수 있는 수준, 서부 해안에서는 거슬리고, 지구 반대편에서는 플레이가 불가능할 정도예요. 저자는 멀리 있는 사람에게는 소스 코드를 내려받아 로컬에서 돌려 보거나 엣지 배치를 권해요. 2
왜 중요한가요
재미로 시작한 실험이 실제 보안 결함을 찾아냈다는 점이 이 글의 묘미예요. 저자가 데모를 Fediverse의 Pleroma 서버에 올리자 서버가 반복적으로 다운됐어요. 원인은 서버 쪽 링크 미리보기 생성이었어요. 연결을 끝내지 않고 데이터를 조금씩 보내는 사이트를 제대로 처리하지 못해 요청이 무한 대기에 빠진 거예요. 결국 DoS 공격 경로로 보안 버그가 접수됐고 빠르게 패치됐어요. 2
개발자에게 남는 포인트는 두 가지예요. 먼저 브라우저의 점진적 렌더링은 여전히 살아 있는 동작이고, 이를 활용하면 프론트엔드 코드 없이도 동적인 화면을 만들 수 있어요. 저자는 후속 아이디어로 가상 DOM 차이를 서버에서 계산해 전달하는 서버 사이드 React 포크, 그리고 헤드리스 브라우저로 페이지를 렌더링해 JavaScript를 지원하지 않는 구형 기기에 전달하는 프록시 서비스를 구상하고 있어요. 2
다른 하나는 외부 콘텐츠를 서버에서 가져오는 링크 미리보기, 스크래핑, 크롤링 같은 기능을 만들 때 응답 스트리밍에 대한 타임아웃과 크기 제한이 필수라는 교훈이에요. 이번 사례처럼 의도치 않은 서비스 장애의 원인이 되기도 하고, 악의적으로는 곧바로 서비스 거부 공격 벡터가 되니까요. 1
참고 자료
- 극단적인 서버 사이드 렌더링 (2025) — GeekNews
- EXTREME SERVER SIDE RENDERING — The Cat House (scd31.com)
- streaming-http-flappy-bird 소스 코드 — GitLab (scd31)
'IT & AI' 카테고리의 다른 글
| 2026년, 비밀번호 관리자 바꾸기가 어려웠던 시절은 끝났어요 (0) | 2026.09.09 |
|---|---|
| 28년 경력 개발자가 MIT를 버리고 EUPL을 선택한 이유 (0) | 2026.09.09 |
| 오래된 웹사이트에 남은 낯선 HTML 태그들, 무엇이었을까 (0) | 2026.09.09 |
| OpenAI, ChatGPT Images 2.5 공개 — 생성 최대 50% 빠르고 손그림 참고도 돼요 (0) | 2026.09.09 |
| 코딩 에이전트의 장황한 답변을 바꾸는 i-have-ADHD 스킬 (0) | 2026.09.09 |