JOURNAL ENTRY Engineering

AI에게 디자인을 계속 설명하는 대신 실제 React 화면 76개를 HTML로 뽑았다

반복되는 AI 디자인 피드백 대신 실제 React 앱의 38개 UI 상태를 desktop/mobile 각각 standalone HTML로 추출했다. 새 mock을 만드는 대신 현재 제품을 editable projection으로 바꿔 human visual review 비용을 낮춘 과정을 정리한다.

AI와 화면 디자인을 수정할 때 답답했던 지점이 하나 있었다.

코드 수정 자체는 빠르다.

문제는 내가 실제 화면을 보고 느끼는 이런 판단을 매번 텍스트로 다시 설명해야 한다는 것이었다.

여기가 너무 답답하다
간격이 이상하다
정보 우선순위가 뒤집혔다
모바일에서 이 부분이 무겁다

스크린샷을 보내고 수정하고, 다시 앱을 띄우고, 또 캡처하고, 다시 설명하는 루프가 반복됐다.

어느 순간에는 AI에게 디자인을 더 잘 설명하는 방법을 찾는 것보다 내가 직접 실제 화면을 편집할 수 있는 surface를 만드는 편이 더 빠르겠다는 생각이 들었다.

그래서 PhotoGram의 현재 React UI를 static HTML로 뽑았다.

새 mock을 만든 것이 아니다

가장 먼저 정한 원칙은 새 디자인을 만들지 않는 것이었다.

NOT a new mock
NOT a new design system
NOT a rewritten static site

원하는 것은 현재 실행 중인 React 앱의 projection이었다.

ACTUAL REACT UI
→ browser-rendered DOM
→ applied CSS
→ standalone HTML

source를 다른 형태로 다시 구현하는 것이 아니라 브라우저가 실제로 보여준 결과를 편집 가능한 형태로 고정했다.

이 차이가 중요했다.

새 mock을 만들면 실제 제품과 다시 synchronization 문제가 생긴다.

React source
→ product UI

separate mock
→ another UI source

two sources
→ drift

반면 rendered snapshot은 authority가 아니다.

React product
= source

static HTML
= editable projection

원본을 대체하지 않기 때문에 디자인 작업 surface로만 사용할 수 있다.

한 페이지 캡처가 아니라 UI state를 먼저 셌다

처음에는 주요 화면 몇 개만 추출하면 될 것 같았다.

그런데 실제 앱은 route 하나가 곧 화면 하나가 아니었다.

예를 들어 Photo Detail만 해도 상태가 달랐다.

guest
other user
owner
owner + minimal caption
owner + long caption
owner + editing
owner + delete confirm

Create도:

step 1 empty
step 1 selected
step 2
step 3

가 다르다.

Feed 역시 guest/authenticated/selection pane 상태가 있다.

따라서 페이지 수보다 사용자가 실제로 보는 UI state를 기준으로 inventory를 만들었다.

최종적으로 38개 상태가 나왔다.

Desktop과 Mobile을 따로 뽑으니 76개가 됐다

같은 DOM이라도 responsive layout은 완전히 다른 디자인 문제를 만든다.

그래서 각 state를 두 viewport에서 추출했다.

desktop: 1280px
mobile:   390px

38 states × 2
= 76 standalone HTML files

대상에는 feed, search, photo detail, photo editor, create wizard, story, saved/following, notifications, profile, account, settings, login/register 등이 포함됐다.

단순 happy path만 뽑지 않았다.

empty state
guest state
owner/non-owner
long content
confirmation state
editing state

도 같이 남겼다.

디자인 문제는 정상 상태보다 이런 variation에서 더 잘 보이기 때문이다.

Screenshot이 아니라 DOM을 뽑은 이유

스크린샷도 visual review에는 좋다.

하지만 직접 수정할 수는 없다.

내가 원한 작업은:

눈으로 확인
→ CSS/markup 직접 조정
→ 바로 다시 확인

이었다.

그래서 image capture보다 rendered DOM + CSS export가 더 적합했다.

각 HTML에는 실제 적용된 CSS를 standalone 형태로 넣고, 가능한 asset URL도 원래 dev server를 가리키도록 정리했다.

결과적으로 browser에서 파일 하나만 열어도 현재 UI와 거의 같은 상태를 볼 수 있다.

그리고 개발 toolchain을 다시 띄우지 않고도 HTML/CSS를 직접 만질 수 있다.

모든 runtime state를 static file로 옮길 수 있는 것은 아니다

static export에도 한계는 있었다.

대표적인 것이 upload preview였다.

React 앱에서는 사용자가 고른 이미지를 browser blob URL로 preview하고 있었다.

이 URL은 해당 tab/session에 묶여 있다.

따라서 standalone HTML로 직렬화해도 나중에 같은 image를 읽을 수 없다.

그래서 create-step1-selected의 thumbnail은 깨졌다.

이 문제를 해결하기 위해 exporter를 크게 다시 만들지는 않았다.

왜냐하면 목적은 완벽한 offline clone이 아니었기 때문이다.

GOAL
= visual editing surface

NOT
= fully functional static reproduction

layout과 DOM/CSS가 살아 있으면 이번 목적에는 충분했다.

projection은 source가 가진 모든 capability를 복제할 필요가 없다.

이 방식이 유용했던 이유는 human judgment가 병목이었기 때문이다

AI가 잘하는 일과 내가 직접 해야 하는 일이 달랐다.

AI는 component 수정, CSS 반복 적용, route/state 수집, mechanical reflection을 빠르게 할 수 있다.

반대로 최종 visual judgment는 내가 보는 편이 빨랐다.

이 간격이 더 낫다
이 버튼이 너무 튄다
이 정보는 여기 없어야 한다
desktop에서 이 패널은 너무 넓다

이런 판단을 매번 자연어 instruction으로 직렬화하는 것은 비효율적이었다.

그래서 workflow를 뒤집었다.

기존:
Human sees UI
→ describes problem to AI
→ AI edits React
→ Human reruns/reviews
→ repeat

변경:
React UI
→ static editable projection
→ Human edits visual surface directly
→ accepted visual delta
→ mechanically reflect into React

AI에게 더 많은 visual authority를 주는 대신, human review가 쉬운 representation을 만든 것이다.

Design artifact를 source of truth로 승격하지 않는다

여기서 중요한 boundary가 하나 있다.

static HTML에서 디자인을 수정한다고 해서 그 파일이 최종 제품 source가 되는 것은 아니다.

STATIC EXPORT
!= PRODUCT SOURCE

이 파일은 review/editing artifact다.

최종 반영은 다시 React component/CSS에 들어가야 한다.

그래야 routing, state, interaction, accessibility, responsive logic, tests 같은 실제 product behavior와 함께 유지된다.

즉 flow는:

React source
→ rendered projection
→ human visual change
→ accepted delta
→ React source reflection

이다.

중간 projection을 canonical source로 바꾸지 않는다.

이 방식은 디자인 도구를 하나 더 만든 것도 아니다

이 문제를 만나면 자동화 tool이나 별도 visual editor를 만들고 싶어질 수 있다.

이번에는 그러지 않았다.

필요한 것은 반복 가능한 거대한 시스템이 아니라 당장 디자인을 직접 만질 수 있는 representation이었다.

그래서 output도 단순했다.

desktop/*.html
mobile/*.html
inventory.md

필요할 때 다시 export하면 된다.

이 방식이 반복적으로 핵심 workflow가 된다면 나중에 자동화를 더 붙일 수 있다.

하지만 첫 사용부터 별도 product로 확대하지 않았다.

Physical source와 useful view는 다를 수 있다

이 작업을 하면서 History retrieval에서 썼던 원칙과 비슷한 패턴을 다시 봤다.

History에서는:

physical files
!= useful retrieval view

였고, 그래서 source를 옮기지 않고 catalog/query projection을 추가했다.

디자인에서는:

React component tree
!= useful visual editing view

였다.

그래서 React를 버리지 않고 static rendered projection을 추가했다.

둘 다 같은 방향이다.

SOURCE HAS VALUE
+
CURRENT REPRESENTATION IS EXPENSIVE FOR THE TASK
→ ADD A PURPOSE-SPECIFIC PROJECTION

source를 새 구조로 다시 만들기 전에 필요한 view를 하나 더 만드는 방식이다.

AI-assisted workflow에서 representation 선택이 생각보다 중요했다

AI를 많이 쓰면 모델 성능이나 prompt 품질에 먼저 관심이 간다.

하지만 실제 작업에서는 같은 정보를 어떤 형태로 놓느냐가 더 큰 차이를 만들 때가 있다.

React source는 구현에는 좋다.

Screenshot은 관찰에는 좋다.

Static rendered HTML은 visual editing에는 좋다.

SAME PRODUCT
DIFFERENT TASK
→ DIFFERENT USEFUL REPRESENTATION

이번에는 더 좋은 디자인 AI가 필요한 게 아니었다.

내가 직접 판단하기 쉬운 surface가 필요했다.

38개 상태, 2개 viewport, 총 76개의 HTML은 그 문제를 꽤 단순하게 해결했다.

그리고 그 결과 디자인 수정의 병목을 AI가 내 말을 얼마나 잘 이해하느냐에서 내가 실제 화면을 보고 무엇을 바꿀 것인가로 다시 옮길 수 있었다.