Use Cases (업데이트: 2026. 6. 2.)

Claude Code와 Astro로 포트폴리오 사이트 만들기: 구현과 공개 전 점검

Claude Code와 Astro로 포트폴리오 사이트를 만드는 실전 가이드. 코드, SEO, 사례, 실수와 체크리스트 포함.

Claude Code와 Astro로 포트폴리오 사이트 만들기: 구현과 공개 전 점검

포트폴리오가 대신해야 할 일을 먼저 정한다

포트폴리오 사이트는 작품 모음이 아니라 의사결정 페이지입니다. 채용 담당자, 프리랜스 의뢰자, 발표 제안자, 협업 후보가 짧은 시간 안에 “이 사람에게 무엇을 맡길 수 있는가”를 판단해야 합니다. 그래서 첫 버전의 목표는 화려한 효과가 아니라 역할, 강점, 실제 프로젝트, 연락 방법을 명확히 보여주는 것입니다.

이 글에서는 Claude Code 공식 문서의 설명처럼 Claude Code를 코드베이스를 읽고, 파일을 수정하고, 명령을 실행하는 개발 파트너로 사용합니다. 구현은 Astro를 선택합니다. 개인 포트폴리오는 정적 HTML로 배포해도 충분한 경우가 많고, Astro의 프로젝트 구조는 초보자와 Claude Code 모두가 이해하기 쉽습니다.

Claude Code가 처음이라면 먼저 Claude Code 입문 가이드를 읽어보세요. Astro 작업 흐름은 Claude Code로 Astro 개발하기와 같이 보면 더 자연스럽습니다.

만들 사이트의 범위

이번 예제는 Hero, About, Projects, Services, Contact로 구성된 단일 페이지입니다. 첫날부터 CMS, 인증, 복잡한 문의 폼을 넣으면 검토할 파일이 많아지고 Claude Code의 작업 범위도 커집니다. 먼저 공개 가능한 작은 버전을 만들고, 이후에 블로그나 폼 API를 추가하는 편이 안전합니다.

flowchart TD
  A["포트폴리오 요구사항"] --> B["src/data/profile.ts"]
  B --> C["src/pages/index.astro"]
  C --> D["src/styles/global.css"]
  D --> E["npm run build"]
  E --> F["공개 전 리뷰"]

데이터는 src/data/profile.ts에 모으고, index.astro는 렌더링에 집중시킵니다. 이렇게 나누면 나중에 “프로젝트 문구만 고쳐줘”, “카드 레이아웃만 바꿔줘”라고 작은 단위로 요청할 수 있습니다.

my-portfolio/
  src/
    data/profile.ts
    pages/index.astro
    styles/global.css
  public/
    ogp.png
    projects/booking-site.webp

이미지는 public/ 아래에 두면 /projects/booking-site.webp처럼 사용할 수 있습니다. MDN의 img 요소 문서를 보면 이미지에는 의미 있는 alt가 필요합니다. 아래쪽 프로젝트 이미지는 Lazy loadingloading="lazy"를 쓸 수 있지만, 첫 화면의 핵심 이미지는 지연 로딩하지 않는 편이 좋습니다.

Claude Code에 줄 프롬프트

“멋진 포트폴리오 만들어줘”는 범위가 넓습니다. 목적, 독자, 파일, 제약, 완료 조건을 넣으면 훨씬 안정적입니다.

Astro로 개인 포트폴리오 사이트를 만듭니다.
대상 독자는 채용 담당자와 소규모 클라이언트이며, 3분 안에 제 경험과 연락 방법을 이해해야 합니다.

요구사항:
- Hero, About, Projects, Services, Contact가 있는 단일 페이지
- 데이터는 src/data/profile.ts에 모으기
- src/pages/index.astro와 src/styles/global.css 구현
- React나 무거운 UI 라이브러리 추가 금지
- 모든 이미지에 alt 추가
- 모바일에서 CTA 버튼이 읽기 좋게 줄바꿈되도록 하기
- 마지막에 npm run build 실행

수정 전에 구현 계획과 변경할 파일을 먼저 요약하세요.

수정 전 계획을 요구하면 범위가 커지는 순간을 잡을 수 있습니다. Claude Code가 애니메이션 라이브러리, CMS, API route를 추가하려고 하면 첫 버전의 목적과 맞는지 다시 판단하세요. SEO 리뷰는 구현 뒤에 별도 턴으로 Claude Code SEO 최적화를 참고해 요청하는 것이 좋습니다.

복사해서 시작하는 코드

새 프로젝트라면 Astro 앱을 만들고 dev 서버를 켭니다. 기존 저장소라면 이 명령을 실행하지 말고 현재 구조를 먼저 읽게 하세요.

npm create astro@latest my-portfolio
cd my-portfolio
npm run dev

프로필 데이터는 구체적으로 씁니다. “React 가능”보다 “예약 흐름을 개선했다”가 더 설득력 있습니다.

// src/data/profile.ts
export const profile = {
  name: "Masa Tanaka",
  role: "프론트엔드 엔지니어 / Claude Code 도입 파트너",
  location: "Tokyo, Japan",
  summary:
    "Astro, React, TypeScript로 빠르고 유지보수하기 쉬운 웹사이트를 만들고, 작은 개선을 측정 가능한 단위로 반복합니다.",
  skills: ["Astro", "TypeScript", "React", "CSS", "SEO", "Content Ops"],
  links: {
    email: "masa@example.com",
    github: "https://github.com/example",
  },
} as const;

export const projects = [
  {
    title: "예약 사이트 전환 흐름 개선",
    description:
      "모바일 메뉴, 서비스 설명, 예약 CTA를 정리해 방문자가 다음 행동을 더 빨리 선택하도록 만들었습니다.",
    image: "/projects/booking-site.webp",
    alt: "서비스 카드와 예약 버튼이 보이는 예약 사이트 스크린샷",
    tags: ["Astro", "SEO", "Responsive"],
    url: "https://example.com/booking",
  },
  {
    title: "SaaS 작업 대시보드",
    description:
      "작업 카드, 상태 필터, 빈 상태를 재설계해 첫 사용자도 흐름을 이해하기 쉽게 했습니다.",
    image: "/projects/task-dashboard.webp",
    alt: "작업 카드와 상태 필터가 있는 SaaS 대시보드 스크린샷",
    tags: ["TypeScript", "UI Design", "Dashboard"],
    url: "https://example.com/dashboard",
  },
] as const;

페이지는 다음처럼 시작할 수 있습니다. 첫 버전은 메일 링크로 충분합니다. 문의 폼, 스팸 방지, 분석 이벤트는 실제 필요가 생겼을 때 별도 작업으로 추가하세요.

---
import { profile, projects } from "../data/profile";
import "../styles/global.css";
---

<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{profile.name} | Portfolio</title>
    <meta name="description" content={`${profile.role}의 프로젝트, 서비스, 연락처를 정리한 포트폴리오입니다.`} />
    <meta property="og:title" content={`${profile.name} | Portfolio`} />
    <meta property="og:description" content={profile.summary} />
    <meta property="og:image" content="/ogp.png" />
  </head>
  <body>
    <main>
      <section class="hero" aria-labelledby="hero-title">
        <p class="eyebrow">{profile.location}</p>
        <h1 id="hero-title">{profile.name}</h1>
        <p class="lead">{profile.summary}</p>
        <div class="actions">
          <a class="button primary" href="#projects">프로젝트 보기</a>
          <a class="button secondary" href={`mailto:${profile.links.email}`}>상담하기</a>
        </div>
      </section>

      <section id="projects" class="section" aria-labelledby="projects-title">
        <h2 id="projects-title">Projects</h2>
        <div class="project-grid">
          {projects.map((project) => (
            <article class="project-card">
              <img src={project.image} alt={project.alt} width="960" height="540" loading="lazy" />
              <div class="project-body">
                <h3>{project.title}</h3>
                <p>{project.description}</p>
                <ul class="tag-list">
                  {project.tags.map((tag) => <li>{tag}</li>)}
                </ul>
                <a href={project.url} target="_blank" rel="noreferrer">자세히 보기</a>
              </div>
            </article>
          ))}
        </div>
      </section>
    </main>
  </body>
</html>

CSS는 읽기 쉬운 여백과 반응형 카드에 집중합니다.

/* src/styles/global.css */
:root {
  --bg: #f7f3ec;
  --panel: #ffffff;
  --text: #1f2933;
  --muted: #5f6c7b;
  --accent: #0f766e;
  --line: #d8dee4;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Inter, system-ui, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.75;
}

.hero,
.section {
  width: min(1120px, calc(100% - 32px));
  margin: 0 auto;
}

.hero {
  min-height: 82vh;
  display: grid;
  align-content: center;
}

.actions,
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: 0;
  list-style: none;
}

.button {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0 18px;
  border: 1px solid var(--accent);
  border-radius: 6px;
  text-decoration: none;
  font-weight: 700;
}

.button.primary {
  background: var(--accent);
  color: white;
}

.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
}

.project-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.project-card img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

@media (max-width: 640px) {
  .button {
    width: 100%;
  }
}

실제 사용 사례

포트폴리오는 누구에게 보여줄지에 따라 내용이 달라져야 합니다.

사용 사례독자가 확인하는 것넣어야 할 내용
이직 또는 부업 지원기술 범위, 담당 역할, 증거기술 스택, 담당 범위, 데모, GitHub
프리랜스 수주문제 해결 가능성, 연락 쉬움서비스 목록, 자주 받는 상담, 이메일 CTA
발표와 집필 제안전문 주제와 신뢰도발표 주제, 글 링크, 짧은 소개
학습 기록성장 과정과 재현 가능한 결과학습 로그, 실패 메모, 데모, 개선 계획

채용 목적이라면 프로젝트마다 본인이 맡은 역할을 분명히 쓰세요. 수주 목적이라면 “기존 사이트 속도 개선”, “랜딩 페이지 CTA 개선”, “Claude Code 도입 리뷰”처럼 상담 가능한 입구를 만들어야 합니다. ClaudeCodeLab의 트레이닝 및 상담처럼 무료 콘텐츠, 체크리스트, 유료 상담이 자연스럽게 이어지는 CTA도 참고할 수 있습니다.

공개 전 리뷰와 함정

SEO는 검색어를 억지로 반복하는 일이 아닙니다. 독자가 실제로 찾을 말, 예를 들어 “프론트엔드 포트폴리오”, “Astro 개발자”, “Claude Code 도입 지원”을 title, description, h1, 프로젝트 설명에 자연스럽게 넣는 일입니다. 내부 링크와 외부 링크도 최소 하나씩 배치하세요.

이 Astro 포트폴리오를 SEO와 접근성 관점에서 리뷰하세요.
title, description, heading 구조, 이미지 alt, 내부 링크, 외부 링크, CTA, 모바일 레이아웃을 확인하고
문제, 이유, 최소 수정안만 제시하세요.

자주 생기는 문제는 네 가지입니다. 첫째, 첫 화면에서 누구인지 알 수 없을 정도로 장식이 과한 경우입니다. 둘째, 프로젝트 설명이 “React 사용”처럼 추상적인 경우입니다. 셋째, OGP 이미지, 링크, 모바일 버튼을 확인하지 않고 배포하는 경우입니다. 넷째, 고객명, 매출, 비공개 URL, .env 값을 AI에게 그대로 주는 경우입니다. 실제 자료는 반드시 익명화하거나 더미 데이터로 바꾸세요.

실제로 적용해 본 결과

이 흐름에서 가장 안정적인 결정은 src/data/profile.ts로 내용과 화면을 분리한 것입니다. 문구 수정과 레이아웃 수정을 나누어 요청할 수 있어 Claude Code의 diff가 짧아집니다. 공개 전에는 npm run build를 실행하고, 모바일 폭에서 Hero, Projects, Contact가 겹치지 않는지 확인하세요.

포트폴리오는 공개 후에도 계속 고치는 자산입니다. 프로젝트가 늘면 설명과 스크린샷을 바꾸고, 문의가 없으면 CTA와 첫 문장을 수정합니다. Claude Code는 한 번에 완성시키는 도구보다, 이런 작은 개선을 반복하는 도구로 쓸 때 더 강합니다.

#Claude Code #portfolio #Astro #CSS #SEO
무료

무료 PDF: Claude Code 치트시트

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

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

Masa

작성자 소개

Masa

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