Tips & Tricks (업데이트: 2026. 6. 2.)

Claude Code로 반응형 디자인 구현하기: CSS부터 Playwright 검증까지

Claude Code로 반응형 UI를 구현합니다. mobile-first CSS, clamp, 그리드, 이미지, 내비게이션, 표, Playwright 검증을 다룹니다.

Claude Code로 반응형 디자인 구현하기: CSS부터 Playwright 검증까지

Claude Code에 맡기기 전에 기준을 정한다

반응형 디자인은 데스크톱 화면을 휴대폰에 억지로 줄여 넣는 작업이 아닙니다. 실제로 공개할 수 있는 페이지라면 화면 폭, 터치 영역, 이미지 용량, 내비게이션 우선순위, 표의 가독성, 광고 위치, 구매나 상담 CTA까지 함께 봐야 합니다. Claude Code에 “모바일 대응해줘”라고만 말하면 특정 화면에서는 괜찮아 보여도 가로 스크롤, 고정 폭 카드, 너무 큰 히어로 이미지, 좁아진 메뉴, 아래로 밀린 CTA가 그대로 남을 수 있습니다.

그래서 먼저 작은 계약을 써 주는 것이 좋습니다. mobile-first CSS는 좁은 화면을 기본값으로 만들고 넓은 화면에서만 레이아웃을 더하는 방식입니다. clamp()는 최소값, 선호값, 최대값을 한 줄로 정하는 CSS 함수입니다. container query는 전체 브라우저 폭이 아니라 부모 컨테이너의 폭을 기준으로 컴포넌트를 바꾸는 방법입니다. 이 전제를 적어 두면 Claude Code가 만든 diff를 리뷰하기가 훨씬 쉬워집니다.

공식 기준은 MDN의 Responsive design, @container, clamp(), responsive images를 참고합니다. 검증에는 Playwright의 ScreenshotsVisual comparisons를 씁니다. Claude Code는 공식 overviewHow Claude Code works에 설명된 것처럼 코드베이스를 읽고, 파일을 수정하고, 명령을 실행하며, 결과를 확인하는 도구로 다루는 것이 안전합니다.

기초 설계는 Claude Code 디자인 시스템, 사용성 검토는 Claude Code 접근성, 브라우저 검증은 Claude Code Playwright 테스트와 함께 보면 흐름이 이어집니다.

구현 흐름

반응형 작업은 마지막에 CSS를 덧붙이는 방식으로 진행하면 쉽게 망가집니다. Claude Code에는 기존 페이지를 읽고, 각 컴포넌트가 깨지는 폭을 찾고, CSS와 이미지와 테스트를 함께 고치라고 지시합니다.

flowchart LR
  A["기존 페이지 읽기"] --> B["mobile-first 기본 CSS"]
  B --> C["fluid grid와 clamp()"]
  C --> D["container query 컴포넌트"]
  D --> E["반응형 이미지와 표"]
  E --> F["Playwright 스크린샷 검증"]

다음 프롬프트를 그대로 출발점으로 쓸 수 있습니다.

기존 /responsive-demo 페이지를 반응형으로 개선해 주세요.

요구사항:
- mobile-first CSS를 사용합니다.
- 320px, 390px, 768px, 1024px, 1440px에서 가로 스크롤이 없어야 합니다.
- 내비게이션, 카드, 가격표, 글 CTA, 푸터가 서로 겹치면 안 됩니다.
- 콘텐츠 이미지는 width/height, srcset, sizes, loading, 적절한 alt를 설정합니다.
- JavaScript 폭 판정보다 CSS Grid, clamp(), @container를 우선합니다.
- 변경 후 Playwright로 스크린샷과 overflow 검사를 실행합니다.

금지:
- 기존 URL, 전환 CTA 링크, 접근 가능한 heading 순서를 깨지 않습니다.
- 페이지 전체 가로 스크롤을 만드는 큰 고정 min-width를 추가하지 않습니다.

이 정도로 쓰면 Claude Code는 목표, 범위, 검증 방법을 동시에 이해합니다. 리뷰할 때도 “데스크톱만 예쁜 diff”를 쉽게 걸러낼 수 있습니다.

복사해서 실행할 수 있는 HTML

아래 예제는 내비게이션, 히어로, 이미지, 카드, 사이드 패널, 비교표를 포함합니다. 이미지 경로만 프로젝트에 맞게 바꾸면 다음 CSS와 함께 바로 확인할 수 있습니다.

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Responsive Demo</title>
    <link rel="stylesheet" href="./responsive-demo.css" />
  </head>
  <body>
    <header class="site-nav">
      <a class="brand" href="/">ClaudeCodeLab</a>
      <nav class="nav-links" aria-label="주요 내비게이션">
        <a href="/ko/blog/">글</a>
        <a href="/en/products/">제품</a>
        <a href="/en/training/">상담</a>
      </nav>
    </header>

    <main class="page-shell">
      <section class="hero">
        <div>
          <p class="eyebrow">Responsive Design</p>
          <h1>작은 화면부터 설계하고 레이아웃을 키운다</h1>
          <p class="lead">
            mobile-first CSS, 유동 그리드, 반응형 이미지, Playwright 검증을 하나의 흐름으로 구현합니다.
          </p>
          <a class="primary-cta" href="/en/products/">프롬프트 템플릿 보기</a>
        </div>
        <picture class="hero-media">
          <source
            type="image/avif"
            srcset="/images/responsive-demo-640.avif 640w, /images/responsive-demo-1280.avif 1280w"
            sizes="(width < 768px) 92vw, 40vw"
          />
          <img
            src="/images/responsive-demo-1280.jpg"
            alt="휴대폰과 노트북에서 같은 반응형 페이지를 확인하는 모습"
            width="1280"
            height="900"
            loading="eager"
            decoding="async"
          />
        </picture>
      </section>

      <div class="layout-grid">
        <aside class="side-panel" aria-label="뷰포트 체크리스트">
          <h2>확인할 폭</h2>
          <ul>
            <li>320px: 작은 휴대폰</li>
            <li>390px: 일반 휴대폰</li>
            <li>768px: 태블릿</li>
            <li>1024px 이상: 데스크톱</li>
          </ul>
        </aside>

        <section class="cards" aria-label="개선 카드">
          <article class="card featured">
            <img src="/images/card-layout.jpg" alt="" width="720" height="480" loading="lazy" />
            <div class="card-body">
              <h2>카드는 컨테이너 폭에 반응한다</h2>
              <p>재사용 컴포넌트는 뷰포트뿐 아니라 놓인 영역의 폭에 따라 밀도를 바꿔야 합니다.</p>
            </div>
          </article>
          <article class="card">
            <div class="card-body">
              <h2>내비게이션은 줄바꿈을 허용한다</h2>
              <p>데스크톱 링크를 모바일 한 줄에 억지로 넣지 말고 충분한 터치 영역을 남깁니다.</p>
            </div>
          </article>
          <article class="card">
            <div class="card-body">
              <h2>표의 의미를 보존한다</h2>
              <p>좁은 화면에서는 행을 카드로 바꾸고 `data-label`로 열 이름을 보여 줍니다.</p>
            </div>
          </article>
        </section>
      </div>

      <section class="comparison">
        <h2>플랜 비교</h2>
        <table class="responsive-table">
          <thead>
            <tr>
              <th scope="col">항목</th>
              <th scope="col">개인</th>
              <th scope="col">팀</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td data-label="항목">목표</td>
              <td data-label="개인">학습과 작은 개선</td>
              <td data-label="팀">리뷰 기준 통일</td>
            </tr>
            <tr>
              <td data-label="항목">검증</td>
              <td data-label="개인">로컬 Playwright</td>
              <td data-label="팀">CI 스크린샷</td>
            </tr>
          </tbody>
        </table>
      </section>
    </main>
  </body>
</html>

mobile-first CSS, fluid grid, clamp

좁은 화면을 기본 CSS로 두고 넓은 화면에서만 컬럼을 추가합니다. 카드는 repeat(auto-fit, minmax(...))로 유동화하고, 제목과 여백은 clamp()로 갑작스러운 변화를 줄입니다. featured 카드는 부모 컨테이너가 충분히 넓을 때만 레이아웃을 바꿉니다.

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  color: #172033;
  background: #f7f8fb;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

.site-nav,
.page-shell {
  width: min(100% - 2rem, 72rem);
  margin-inline: auto;
}

.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-block: 1rem;
}

.brand,
.nav-links a,
.primary-cta {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

.nav-links {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.page-shell {
  padding-block: clamp(1rem, 4vw, 3rem);
}

.hero {
  display: grid;
  gap: clamp(1rem, 4vw, 2.5rem);
  align-items: center;
}

.hero h1 {
  max-width: 11ch;
  margin: 0;
  font-size: clamp(2.25rem, 10vw, 5rem);
  line-height: 1.02;
}

.lead {
  max-width: 62ch;
  font-size: clamp(1rem, 2vw, 1.25rem);
  line-height: 1.8;
}

.primary-cta {
  width: fit-content;
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  background: #172033;
  color: white;
  text-decoration: none;
  font-weight: 700;
}

.layout-grid {
  display: grid;
  gap: clamp(1rem, 3vw, 2rem);
  margin-block-start: 2rem;
}

.cards {
  container: cards / inline-size;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1rem;
}

.side-panel,
.card,
.comparison {
  background: white;
  border: 1px solid #dbe3ef;
  border-radius: 0.75rem;
}

.card {
  overflow: hidden;
}

.card-body,
.side-panel,
.comparison {
  padding: 1rem;
}

@container cards (width >= 42rem) {
  .card.featured {
    grid-column: span 2;
    display: grid;
    grid-template-columns: minmax(14rem, 0.8fr) 1fr;
  }

  .card.featured img {
    height: 100%;
    object-fit: cover;
  }
}

.comparison {
  margin-block-start: 2rem;
  overflow-x: auto;
}

.responsive-table {
  width: 100%;
  border-collapse: collapse;
}

.responsive-table th,
.responsive-table td {
  padding: 0.875rem;
  border-block-end: 1px solid #dbe3ef;
  text-align: left;
}

@media (width < 48rem) {
  .responsive-table thead {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .responsive-table,
  .responsive-table tbody,
  .responsive-table tr,
  .responsive-table td {
    display: block;
    width: 100%;
  }

  .responsive-table tr {
    border: 1px solid #dbe3ef;
    border-radius: 0.5rem;
    margin-block: 0.75rem;
    overflow: hidden;
  }

  .responsive-table td {
    display: grid;
    grid-template-columns: minmax(7rem, 40%) 1fr;
    gap: 1rem;
  }

  .responsive-table td::before {
    content: attr(data-label);
    font-weight: 700;
    color: #526071;
  }
}

@media (width >= 64rem) {
  .hero {
    grid-template-columns: minmax(0, 1.1fr) minmax(18rem, 0.9fr);
  }

  .layout-grid {
    grid-template-columns: 16rem minmax(0, 1fr);
  }
}

핵심은 큰 고정 폭을 없애는 것입니다. width: min(100% - 2rem, 72rem)은 작은 화면에서 여백을 남기고, 큰 화면에서는 읽기 폭을 제한합니다. minmax(min(100%, 18rem), 1fr)는 카드의 최소 폭이 좁은 컨테이너를 뚫고 나가지 않게 합니다.

컴포넌트별 체크 기준

Claude Code에는 다음 기준을 함께 넘기면 좋습니다.

컴포넌트흔한 실패요청할 수정
내비게이션데스크톱 링크를 모바일 한 줄에 압축줄바꿈, 터치 영역, aria-label 유지
카드width: 320px 또는 큰 min-width로 overflow 발생auto-fit, minmax(), container query 사용
이미지휴대폰에도 같은 대형 이미지 제공srcset, sizes, width, height, loading, alt 추가
여러 열이 눌려 읽기 어려움가로 스크롤 또는 행 카드 선택, data-label 유지
CTA구매나 상담 링크가 아래로 묻힘모바일 첫 스크롤과 글 하단 모두 확인

링크가 세 개뿐이라면 햄버거 메뉴보다 줄바꿈이 더 빠를 수 있습니다. 가격 비교표는 가로 비교가 필요할 수 있지만, 티켓 목록은 행 카드가 더 읽기 쉽습니다. Claude Code에는 데스크톱 모양을 보존하라고 하기보다 사용자의 작업을 보존하라고 지시해야 합니다.

세 가지 이상 테스트할 유스케이스

첫 번째는 SaaS 대시보드입니다. 사이드바, 필터, KPI 카드, 차트, 데이터 테이블이 함께 나옵니다. 모바일에서는 핵심 KPI를 먼저 보여 주고, 보조 필터는 접고, 상세 행은 카드로 바꾸는 편이 좋습니다.

두 번째는 블로그나 미디어 사이트입니다. 본문 폭, 목차, 광고, 관련 글, 무료 PDF CTA가 공간을 경쟁합니다. 코드 블록 overflow, 이미지 로딩, CTA 노출 위치를 반드시 확인해야 합니다.

세 번째는 전자상거래나 강의 판매 페이지입니다. 상품 카드, 가격 비교, 구매 버튼, FAQ가 매출에 직접 연결됩니다. 가격과 구매 버튼이 모바일에서 너무 아래로 밀리면 전환이 떨어집니다.

네 번째는 내부 관리 화면입니다. 매일 쓰는 사용자는 화려함보다 검색, 필터, 키보드 조작, 표의 가독성을 원합니다. 반응형 개선도 기존 업무 순서를 망가뜨리지 않는 방향이어야 합니다.

Playwright로 스크린샷과 overflow를 확인한다

브라우저에서 한 번 보는 것으로 끝내지 마세요. Playwright는 대표적인 폭에서 주요 요소가 보이는지, 가로 스크롤이 생겼는지, 스크린샷 차이가 의도한 것인지 확인할 수 있습니다.

import { expect, test } from "@playwright/test";

const baseUrl = process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000";

const viewports = [
  { name: "mobile-320", width: 320, height: 740 },
  { name: "mobile-390", width: 390, height: 844 },
  { name: "tablet-768", width: 768, height: 1024 },
  { name: "desktop-1440", width: 1440, height: 1000 },
];

for (const viewport of viewports) {
  test(`responsive demo has no horizontal overflow at ${viewport.name}`, async ({ page }) => {
    await page.setViewportSize({ width: viewport.width, height: viewport.height });
    await page.goto(`${baseUrl}/responsive-demo`);

    await expect(page.getByRole("navigation", { name: "주요 내비게이션" })).toBeVisible();
    await expect(page.getByRole("link", { name: "프롬프트 템플릿 보기" })).toBeVisible();

    const hasHorizontalOverflow = await page.evaluate(() => {
      return document.documentElement.scrollWidth > document.documentElement.clientWidth;
    });

    expect(hasHorizontalOverflow).toBe(false);
    await expect(page).toHaveScreenshot(`responsive-${viewport.name}.png`, {
      fullPage: true,
      maxDiffPixels: 300,
    });
  });
}

처음 실행하면 기준 이미지가 만들어집니다. 디자인 변경이 의도된 경우에만 npx playwright test --update-snapshots로 갱신합니다. 스크린샷은 OS, 폰트 렌더링, GPU, headless 설정에 영향을 받으므로 팀에서는 같은 CI 환경에서 비교하는 것이 좋습니다.

자주 생기는 함정

가장 흔한 함정은 데스크톱 우선 CSS 위에 모바일 override를 계속 쌓는 것입니다. 시간이 지나면 어떤 폭에서 어떤 규칙이 적용되는지 알기 어렵습니다. Claude Code에는 좁은 화면을 기본 규칙으로 되돌리고 넓은 화면만 추가하라고 요청합니다.

두 번째는 <meta name="viewport"> 누락입니다. 이 태그가 없으면 모바일 브라우저가 가상의 데스크톱 폭으로 렌더링할 수 있어 실기기와 CSS 검증이 어긋납니다.

세 번째는 카드, 표, 이미지, 임베드 안에 숨어 있는 고정 폭입니다. min-width: 960px는 데스크톱에서는 티가 나지 않지만 휴대폰에서는 페이지 전체 overflow를 만듭니다.

네 번째는 srcset만 추가하고 sizes를 빼는 것입니다. 브라우저가 렌더링 폭을 알아야 적절한 이미지를 고를 수 있습니다. srcset, sizes, width, height, alt를 한 세트로 봐야 합니다.

다섯 번째는 스크린샷 테스트만 믿는 것입니다. 광고, 날짜, 애니메이션, 외부 위젯은 스크린샷을 흔들 수 있습니다. overflow, CTA 표시, navigation landmark 같은 DOM assertion을 함께 둡니다.

수익 경로도 함께 본다

반응형 디자인은 비즈니스 목표를 지켜야 합니다. 이 사이트라면 모바일 독자도 무료 PDF, Gumroad 제품, 상담 링크를 쉽게 찾을 수 있어야 합니다. 반복 가능한 워크플로가 필요하면 Claude Code products를, 실제 팀 화면을 기준으로 리뷰 규칙과 Playwright 검증을 정리하려면 Claude Code training을 확인하세요.

Claude Code에 brief를 줄 때 전환 경로를 완료 조건에 넣으세요. 보기에는 깨끗해도 구매 버튼이나 문의 링크를 숨기는 레이아웃은 완료가 아닙니다.

정리

Claude Code로 반응형 디자인을 할 때는 mobile-first CSS, fluid grid, clamp(), container query, 반응형 이미지, 내비게이션, 카드, 표, Playwright 검증을 하나의 흐름으로 묶어야 합니다. “모바일 대응”이 아니라 “어떤 폭에서 어떤 컴포넌트를 어떻게 검증할지”를 전달하는 것이 핵심입니다.

이 글의 데모 패턴을 320px, 390px, 768px, 1440px에서 확인했을 때 내비게이션은 overflow 없이 줄바꿈되었고, 카드는 휴대폰에서 한 열로 내려갔으며, 표는 행 카드로 읽을 수 있었습니다. Playwright의 가로 스크롤 assertion도 통과했습니다. Masa의 실감은 명확합니다. Claude Code를 구현자이자 리뷰어로 쓰고, 고정 폭, 이미지 힌트, CTA 위치, 표 동작, 스크린샷 차이를 의심하게 만들면 공개 전 수정이 줄어듭니다.

#Claude Code #responsive #CSS #mobile #Playwright
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.