⚠️ 주의! 이 글은 테스트용으로 AI로 작성되었습니다.

블로그를 정적 사이트로 운영하다 보면 편한 점이 많습니다. 페이지는 빠르고, 배포 구조는 단순하고, 글은 Git으로 관리하면 됩니다. 대신 가끔은 “정말 서버가 살아 있나?” 같은 아주 현실적인 확인을 하고 싶을 때가 있습니다. 단순히 /api/ping을 호출하는 것도 가능하지만, 블로그 소개 페이지에 그런 상태 확인을 그대로 드러내는 건 조금 재미가 없습니다.

그래서 이번에는 About 페이지의 서버 사진에 작은 이스터에그를 하나 넣었습니다. 사진 속 컴퓨터 전원 버튼 위치에 보이지 않는 버튼을 두고, 그 버튼을 누르면 백엔드에서 픽셀아트를 받아와 오른쪽 패널에 그려주는 방식입니다.

픽셀아트 이스터에그 플레이스홀더

시작은 전원 버튼 위치 잡기

About 페이지에는 개인 서버 사진이 하나 있습니다. 처음에는 이 사진이 왼쪽에 붙어 있었는데, 페이지를 열자마자 보이는 이미지라서 먼저 가운데 정렬부터 했습니다. 그리고 사진 위에 실제로는 보이지 않는 버튼을 하나 올렸습니다.

이 버튼은 디자인 요소라기보다는 히트박스에 가깝습니다. 사용자는 버튼을 보지는 못하지만, 컴퓨터의 전원 버튼처럼 보이는 부분을 눌렀을 때 반응합니다. 키보드 접근성을 완전히 버리지는 않기 위해 focus-visible 상태에서는 약한 outline이 보이도록 해뒀습니다.

<div class="about-server-photo-wrap">
  <img src="/assets/images/server_new.jpg" alt="서버 이미지" class="about-server-photo">
  <button type="button" class="about-power-hotspot" aria-label="서버 전원 확인"></button>
</div>

처음에는 전원 버튼을 누르면 곧바로 픽셀아트 패널이 튀어나오게 만들었는데, 생각보다 동작이 너무 갑작스러웠습니다. 그래서 시퀀스를 나눴습니다.

  1. 컴퓨터 이미지가 왼쪽으로 이동
  2. 오른쪽 픽셀아트 패널이 나타남
  3. booting... 진행바가 표시됨
  4. 백엔드에서 픽셀아트 데이터를 가져옴
  5. 픽셀아트가 한 칸씩 렌더링됨

끄는 동작도 반대로 구성했습니다. 전원 버튼을 다시 누르면 shutdown... 진행바가 거꾸로 움직이고, 패널이 닫힌 뒤 이미지가 다시 가운데로 돌아옵니다. 이런 순서를 조금만 넣어도 단순한 API 호출이 훨씬 장치처럼 느껴집니다.

픽셀아트는 백엔드에서 온다

픽셀아트 자체는 프론트에서 하드코딩하지 않고 백엔드 API에서 받아오도록 했습니다.

GET /api/about/pixel-art?index=0

응답은 대략 이런 구조입니다.

{
  "success": true,
  "id": "blog-terminal",
  "name": "Dandevlog Terminal",
  "width": 64,
  "height": 48,
  "effect": "scanline boot",
  "next_index": 1,
  "pixels": ["0x00000000", "0x1e73d1ff"],
  "render_order": [0, 1, 2, 3]
}

여기서 핵심은 pixelsrender_order입니다. pixels0xRRGGBBAA 형식의 색상 배열이고, render_order는 프론트엔드가 픽셀을 그릴 순서입니다. 그냥 왼쪽 위부터 오른쪽 아래까지 한 번에 그리면 너무 밋밋해서, 백엔드에서 렌더링 순서도 같이 내려주도록 했습니다.

덕분에 프론트에서는 같은 픽셀아트라도 scanline처럼 위에서 아래로 그릴 수도 있고, 대각선으로 패킷이 떨어지는 것처럼 그릴 수도 있습니다. 지금은 가벼운 장난감에 가깝지만, 응답 데이터에 effect 같은 필드를 둔 것도 나중에 효과를 늘리기 쉽게 하기 위해서입니다.

C++ 쪽 구현

백엔드에는 AboutPage 클래스를 새로 만들었습니다. 기존 검색 API처럼 main.cpp에서 라우트를 등록하고, 실제 응답 생성은 page/about_page.cpp로 뺐습니다.

app.registerHandler("/api/about/pixel-art", [&aboutPage](const drogon::HttpRequestPtr& req,
                                                         std::function<void(const drogon::HttpResponsePtr&)>&& callback) {
    uint32_t index = 0;
    const std::string indexStr = req->getParameter("index");
    if (!indexStr.empty()) {
        try {
            index = static_cast<uint32_t>(std::stoul(indexStr));
        } catch (...) {
            index = 0;
        }
    }

    callback(drogon::HttpResponse::newHttpJsonResponse(aboutPage.getPixelArt(index)));
}, {drogon::Get});

픽셀아트 샘플은 page/pixel_art.h에 넣었습니다. 별도 이미지 파일을 읽는 대신 C++ 배열로 직접 만들었습니다. 블로그 아이콘, C++ 로고 느낌의 그림, Drogon을 떠올리게 하는 작은 드래곤, 홈 서버 상태 패널 같은 샘플을 넣어뒀습니다.

픽셀 색상은 다음처럼 0xRRGGBBAA로 표현합니다.

inline constexpr uint32_t blue = 0x1e73d1ff;
inline constexpr uint32_t white = 0xf8fafcff;
inline constexpr uint32_t transparent = 0x00000000;

처음에는 모든 픽셀 배열을 손으로 하나하나 쓰는 방식을 생각했지만, 64x48만 되어도 3072칸입니다. 그래서 rect, line, put 같은 작은 유틸리티 함수를 만들어 대략적인 형태를 그리는 쪽으로 방향을 바꿨습니다. 진짜 픽셀아트 도구처럼 정교하지는 않지만, 블로그 이스터에그로는 충분했습니다.

프론트엔드 렌더링

프론트에서는 <canvas>를 64x48 해상도로 두고 CSS에서 크게 확대합니다.

.about-pixel-canvas {
  image-rendering: pixelated;
}

이 한 줄이 중요합니다. 브라우저가 캔버스를 부드럽게 보간하지 않고 픽셀 느낌을 유지해서 확대해줍니다.

렌더링 중에는 클릭을 막았습니다. 픽셀아트 패널을 누르면 다음 그림을 요청하는데, 애니메이션 도중에 계속 클릭하면 서버 요청이 쌓이거나 상태가 꼬일 수 있습니다. 그래서 rendering 상태를 두고, 진행 중에는 전원 버튼과 캔버스 클릭을 무시하도록 했습니다.

if (state.rendering) {
  return;
}

이 정도만 해도 과한 방어 코드는 아니면서, 사용자가 장난으로 연타해도 서버가 피곤해지는 상황은 피할 수 있습니다.

로딩 화면은 클라이언트에서

처음에는 booting...shutdown... 텍스트도 캔버스에 직접 그렸습니다. 그런데 64x48 해상도에 작은 픽셀 폰트로 글자를 그리니 많이 뭉개졌습니다. 픽셀아트에는 잘 어울리지만, 로딩 상태를 읽어야 하는 UI에는 맞지 않았습니다.

그래서 로딩 텍스트와 프로그레스 바는 HTML 오버레이로 분리했습니다. 캔버스에는 배경과 픽셀아트만 맡기고, 사람이 읽어야 하는 문자는 DOM으로 렌더링합니다. 결과적으로 booting... 글자는 선명하고, 픽셀아트 영역의 분위기는 유지됩니다.

헬스체크와 장난 사이

이 장치는 사실 엄청나게 실용적인 기능은 아닙니다. 서버가 살아 있는지 확인하려면 /api/ping 하나면 충분합니다. 하지만 About 페이지에서 서버 사진의 전원 버튼을 누르면 실제 백엔드가 응답하고, 픽셀아트가 켜지는 흐름은 이 블로그의 성격과 꽤 잘 맞습니다.

정적 사이트처럼 보이지만 뒤에는 C++ 백엔드가 있고, 그 백엔드가 작게나마 페이지와 상호작용합니다. 기능보다는 감각에 가까운 작업이지만, 이런 작은 장치가 사이트를 조금 더 내 것처럼 만들어줍니다.

나중에는 픽셀아트를 더 추가하거나, 실제 서버 상태를 조금 섞어도 재미있을 것 같습니다. 예를 들어 디스크 사용량이나 uptime을 픽셀 패널 아래에 작은 상태 값으로 표시한다면, 이스터에그와 관리 도구의 중간쯤 되는 기능이 될 수도 있습니다.