LLM 이 만든 UI 가 "AI스러워" 보이는 이유와, 단계를 쪼개는 전략

카드 남발·과한 라운드·보라 그라데이션은 우연이 아니다. 원인 패턴을 정리하고 ground-up 과 redesign 두 상황에서 각각 다른 워크플로를 쓴 기록.

Claude Code 같은 도구로 UI 를 짜면 결과물에 특유의 인상이 남는다. 잘 돌아가는데 어딘가 양산형처럼 보인다.

“AI스럽다”는 감상을 구체적인 패턴으로 분해해보니 매번 같은 것들이 나왔다. 그리고 그 패턴들은 프롬프트와 워크플로를 바꾸면 상당 부분 사라졌다.

1. 반복되는 다섯 가지 신호

  • 카드 남발 — 정보 단위마다 box-shadow 박스로 감싼다
  • 과한 라운드값 — 전부 rounded-xl 이상
  • 영혼 없는 그라데이션 — 보라·블루 계열 linear-gradient 가 기본값처럼 등장
  • 균질한 SaaS 레이아웃 — 완벽 대칭 그리드, 가로 3분할 카드
  • 마이크로 인터랙션 부재 — 정적 뼈대만 있고 hover·focus·로딩 전환이 없다

마지막 항목이 특히 크다. 스크린샷으로는 멀쩡한데 실제로 만져보면 죽어 있는 느낌이 든다.

이유는 짐작이 간다. 이 패턴들은 학습 데이터에서 압도적으로 흔한 “안전한 기본값”이다. 명시하지 않으면 평균으로 수렴한다.

2. 의도적으로 깨는 처방

컴포넌트 중심에서 타이포그래피 중심으로

border 와 배경색을 지우고, 폰트 두께·자간·여백만으로 정보 위계를 만든다. 박스 대신 여백이 구분자가 되게 한다.

이 하나만 바꿔도 인상이 크게 달라진다. 카드가 사라지면 나머지 네 신호 중 셋이 같이 사라진다.

절제된 border

무거운 box-shadow 대신 1px muted color border 를 쓴다. 그림자는 정말 떠 있어야 하는 것에만 남긴다.

의도적 비대칭

균질 그리드 대신 특정 아이템만 시각적으로 강조한다.

다만 이건 조심해야 한다. “그리드를 비틀어라”를 기준 없이 적용하면 오히려 망가진다. LLM 에게 “비대칭적으로” 라고만 하면 무작위로 어긋난 결과가 나온다. 강조할 항목 하나를 명시적으로 지정하는 식이 낫다.

마이크로 인터랙션 주입

hover 시 미세한 스프링, focus ring, 스켈레톤 UI 의 자연스러운 전환. 정적 뼈대에 움직임 디테일을 직접 얹으면 양산형 느낌이 크게 줄어든다.

이 부분은 LLM 에게 맡기기보다 직접 가다듬는 게 결과가 좋았다. 움직임은 “얼마나”가 전부인데, 그 정도를 글로 지시하기가 어렵다.

재사용 프롬프트

프로젝트 설정 파일이나 시스템 프롬프트에 주입해두면 매번 안 써도 된다.

Do not use standard SaaS card-based layouts. Avoid generic linear gradients.
Emphasize raw typography with strict baseline grids. Use subtle borders
(1px, muted colors) instead of heavy box-shadows. Focus on micro-interactions
for interactive elements.

금지형으로 쓴 이유가 있다. “예쁘게”는 평균으로 수렴하지만 “이건 하지 마”는 탐색 공간을 실제로 좁힌다.

3. 상황에 따라 워크플로가 달라야 한다

같은 “디자인 잘 뽑기”라도, 빈 상태에서 쌓는 것과 기존 구조를 유지하며 갈아엎는 것은 접근이 완전히 다르다.

A. Ground-up — 점진적 구체화

완성형 UI 를 한 번에 요청하면 익숙한 스타일의 스파게티가 나온다. 단계를 쪼개서 올린다.

도메인 데이터 모델링
  → 컴포넌트 구조 설계 (트리)
  → 기능적 와이어프레임 (인라인 스타일 제외, 구조와 동작만)
  → 디자인 시스템 테마 입히기 (마지막 레이어)

핵심은 디자인을 가장 마지막 레이어로 격리하는 것이다. 상태관리와 데이터페칭의 구조적 뼈대를 먼저 잡는다.

로직과 스타일이 한 단계에 섞이면 둘 다 망가진다. 스타일을 고치려다 로직이 바뀌고, 로직을 고치려다 레이아웃이 무너진다. 순서를 지키면 각 단계에서 볼 것이 하나로 줄어든다.

B. Redesign — 계약 기반 리팩토링

“파일 통째로 던지고 예쁘게 바꿔줘”가 가장 위험하다. 로직이 부서지거나 뜬금없는 라이브러리가 import 된다.

Props / State / Hook 을 불변의 계약으로 선언하고, 스타일 클래스명과 스타일 담당 자식 컴포넌트만 교체하도록 스코프를 좁힌다.

파일 상단에 가드 주석을 둔다.

// LOGIC GUARD: Do not change any state, hooks, or props handling.

효과가 확실하지는 않지만 비용이 0 에 가깝다. 넣어서 손해 볼 게 없다.

Tailwind 환경이라면 구조와 스타일을 분리하는 리팩토링을 먼저 하는 게 안전하다. headless 컴포넌트 라이브러리로 동작·접근성과 스타일을 분리해두면, 디자인 교체가 로직을 건드릴 수 없는 구조가 된다.

여기서 이름을 정확히 구분해야 한다. Radix UI, Headless UI, Mantine 은 서로 독립된 프로젝트다. 특히 Mantine 은 headless 가 아니라 스타일이 포함된 컴포넌트 라이브러리다. 한 계열로 묶어 부르면 선택 자체가 어긋난다.

4. 도구 이야기 — 검증한 것만

이 주제를 조사하다 보면 출처가 불분명한 플러그인 이름이 많이 나온다. 실제로 확인한 것만 적는다.

  • frontend-design 스킬 — 실재한다. 프론트엔드 생성 시 디자인 시스템과 접근성 가이드를 프롬프트·체크리스트 수준에서 유도한다. 다만 “특정 라이브러리나 ARIA 준수를 자동 강제”한다는 설명은 과장이다. 강제 집행 도구가 아니라 가이드다.
  • lazyweb 플러그인 — 실재한다. 디자인 inspiration 검색과 리서치용이다. UI 레퍼런스를 모으고 개선 아이디어를 뽑는 데 쓴다.
  • MCP 로 디자인 시스템 문서 연결 — 개념적으로 타당하다. 디자인 시스템 문서를 MCP resource 로 노출하면 LLM 이 컴포넌트 스펙을 실시간 참조할 수 있다. 다만 이건 직접 구성하는 패턴이지 기성 제품이 있는 게 아니다.

원안에 있던 몇몇 마켓플레이스·스킬 이름은 공개된 근거를 찾지 못해 뺐다. 검증 안 된 고유명사를 인용하면 그 문서 전체의 신뢰도가 떨어진다.

정리

“AI스러운 디자인”은 모델의 한계라기보다 지시의 공백에 가까웠다. 명시하지 않은 자리는 전부 평균으로 채워지고, 평균이 곧 그 인상이다.

그래서 대응도 두 방향이다. 금지 목록으로 평균을 밀어내고, 단계를 쪼개서 한 번에 하나만 결정하게 한다. 움직임의 정도처럼 글로 지시하기 어려운 것은 직접 손보는 몫으로 남긴다.