Communitygithub.com

ajelinek/skills-stash

Astro TypeScript 코드 작성 또는 리팩토링 시 사용합니다. Astro는 정적 페이지와 레이아웃에, SolidJS 또는 React 섬은 모든 상호작용에 사용하며, CSS Modules는 별도 파일에, 하이드레이션 지시어, 콘텐츠 컬렉션, 서버 섬, 온디맨드 렌더링, Astro Actions, 접근 가능한 시맨틱 HTML을 포함합니다. 작업에 공유 UI 구조, 스타일링, 시맨틱, 접근성, 또는 기반 컴포넌트 결정이 포함될 때는 `ui-engineering`과 함께 사용하세요. Astro 페이지, 레이아웃, 섬, 액션, 또는 서버 렌더링 기능을 구축하라는 요청 시 트리거됩니다.

skills-stash란 무엇인가요?

이 스킬은 Astro 프레임워크를 사용한 프로젝트에서 코드를 작성하거나 리팩토링할 때 적용됩니다. 핵심 원칙은 정적 구조(Astro)와 동적 상호작용(SolidJS/React)을 명확히 분리하는 것입니다. 모든 상호작용 컴포넌트는 프레임워크 섬(island)으로 구현되며, 모든 스타일은 CSS Modules를 사용하여 별도의 `styles.module.css` 파일에 정의해야 합니다. 이 스킬은 페이지, 레이아웃, 섬, 서버 렌더링 기능, 콘텐츠 컬렉션, Astro Actions 등 다양한 작업을 안내하며, 프로젝트 구조, 네이밍 컨벤션, 스타일링 규칙, 접근성 패턴을 포괄합니다. 개발자는 이 스킬을 통해 Astro의 정적 사이트 생성 및 서버 사이드 렌더링 기능을 최대한 활용하면서도, 상호작용이 필요한 부분에서는 SolidJS 또는 React의 강력한 컴포넌트 모델을 사용할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ajelinek/skills-stash/tree/HEAD/astro.js

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Astro

Astro는 정적 구조(페이지, 레이아웃, 순수 프레젠테이션 영역)를 담당합니다. SolidJS 또는 React는 모든 상호작용을 담당합니다. 사용자 입력을 처리하거나, 상태를 관리하거나, 이벤트에 응답하는 모든 컴포넌트는 .astro 파일이 아닌 프레임워크 섬(island)입니다.

CSS Modules는 모든 컴포넌트 스타일링에 사용되며, 항상 별도의 styles.module.css 파일에 위치합니다. 인라인 스타일이나 Astro <style> 블록 내부에 절대 작성하지 않습니다.

이 스킬을 사용해야 할 때

  • 새 Astro 프로젝트를 구성하거나 페이지 및 레이아웃 영역을 추가할 때
  • .astro 페이지, 레이아웃, 또는 정적 프레젠테이션 영역을 구축할 때
  • 어떤 요소가 Astro에 속하는지 아니면 SolidJS/React 섬에 속하는지 결정할 때
  • 섬에 사용할 하이드레이션 지시어를 선택할 때
  • .astro 파일이나 프레임워크 섬 내에서 CSS Modules 스타일링을 적용할 때
  • 콘텐츠 컬렉션(src/content/, src/content.config.ts)으로 작업할 때
  • 서비스 계층 데이터를 서버 렌더링된 Astro 페이지에 연결할 때
  • 개인화된 지연 렌더링 영역에 서버 섬(server:defer)을 사용할 때
  • 어댑터 및 미들웨어를 사용하여 온디맨드 렌더링(SSR)을 설정할 때
  • 타입 안전한 서버 변환을 위해 Astro Actions를 정의하고 호출할 때

Astro vs 섬 — 핵심 아키텍처 결정

이것은 모든 Astro 프로젝트에서 가장 중요한 결정입니다.

기능사용하는 곳
데이터로부터 정적 HTML 렌더링 — 사용자 상호작용 없음.astro
페이지 셸, 레이아웃 래퍼, 네비게이션 구조.astro
콘텐츠 섹션, 블로그 게시물, 마케팅 영역.astro
onClick, onChange, 폼 상태, 반응형 데이터가 있는 모든 컴포넌트SolidJS 또는 React 섬
기본 UI 컴포넌트(Button, Input, Modal, Alert 등)SolidJS 또는 React 섬
스토어나 서비스 훅을 읽거나 쓰는 모든 컴포넌트SolidJS 또는 React 섬

간단히 말해:

  • Astro는 구조와 콘텐츠를 렌더링합니다. 사용자 상호작용을 처리하지 않습니다.
  • SolidJS 또는 React는 모든 상호작용, 모든 폼 상태, 모든 기본 상호작용 UI 프리미티브(이벤트를 발생시키는 단순한 버튼도 포함)를 처리합니다.
  • .astro 파일에서는 상호작용 UI를 구축하지 마십시오. Astro에는 반응성이 없습니다. .astro 파일에 상호작용을 추가하려는 시도는 취약하고 테스트 불가능한 코드를 생성합니다.

어떤 프레임워크 — SolidJS 또는 React?

프로젝트는 하나의 프레임워크를 일관되게 사용합니다. astro.config.mjs에서 구성된 통합(@astrojs/solid-js 또는 @astrojs/react)을 확인하고 프로젝트의 모든 섬에 대해 해당 프레임워크를 사용하십시오. 동일한 프로젝트에서 두 프레임워크를 혼합하지 마십시오.

전체 결정 가이드 및 하이드레이션 지시어 참조는 ./references/islands-and-hydration.md를 읽으십시오.

스타일링 규칙 — 별도 파일의 CSS Modules

이 스킬에서 가장 중요한 규칙입니다.

모든 Astro 컴포넌트, 페이지 및 레이아웃은 스코프된 스타일링을 위해 CSS Modules를 사용합니다. CSS는 별도 파일에 위치하며, Astro <style> 태그 내부에 절대 작성하지 않습니다.

ComponentName/
├── index.astro         ← 컴포넌트 로직 및 마크업
└── styles.module.css   ← 이 컴포넌트의 모든 스타일, 항상 별도 파일
  • 항상 index.astro와 함께 styles.module.css를 생성합니다.
  • 절대 Astro의 <style> 블록 안에 스타일을 넣지 않습니다.
  • 절대 인라인 style 속성을 사용하지 않습니다.
  • 프론트매터 펜스 상단에서 모듈을 임포트합니다: import styles from './styles.module.css'
  • class:list 지시어 또는 직접 클래스 바인딩을 사용하여 클래스를 적용합니다:
    • <div class={styles.container}>
    • <div class:list={[styles.container, isActive && styles.active]}>
  • 시맨틱 클래스 이름을 사용하십시오. 유틸리티 클래스 스태킹을 하지 마십시오.
  • 모든 디자인 토큰(색상, 간격, 타이포그래피)에 CSS 사용자 정의 속성을 사용하십시오.
  • !important는 없습니다. 깊은 선택자는 없습니다. 스타일링을 위한 ID 선택자는 없습니다.

토큰 전략, 테마 변수, 반응형 규칙 및 애니메이션 가이드는 ./references/styling-and-css-modules.md를 읽으십시오. 별도의 CSS Module 파일이 포함된 완전한 Button 예제는 ./examples/button-component/를 참조하십시오.

프로젝트 구조

src/
├── components/
│   ├── foundation/         # 상호작용 UI 프리미티브 — SolidJS 또는 React, .astro 아님
│   │   ├── Button/
│   │   │   ├── index.tsx           ← SolidJS (.tsx) 또는 React (.tsx)
│   │   │   └── styles.module.css
│   │   └── Input/
│   │       ├── index.tsx
│   │       └── styles.module.css
│   ├── layout/             # 정적 레이아웃 구조 — .astro
│   │   ├── Header/
│   │   │   ├── index.astro
│   │   │   └── styles.module.css
│   │   └── Footer/
│   │       ├── index.astro
│   │       └── styles.module.css
│   └── features/           # 상호작용 기능 영역 — SolidJS 또는 React
│       └── SomeFeature/
│           ├── index.tsx           ← 섬 컴포넌트
│           └── styles.module.css
├── layouts/
│   └── BaseLayout/
│       ├── index.astro             ← 레이아웃 셸은 항상 .astro
│       └── styles.module.css
├── pages/
│   ├── index/
│   │   ├── index.astro             ← 페이지는 항상 .astro
│   │   └── styles.module.css
│   └── [dynamic]/
│       ├── index.astro
│       └── styles.module.css
├── content/
│   ├── blog/
│   └── [collection-name]/
├── content.config.ts   ← Astro 5 컬렉션 스키마 및 로더
├── store/
│   ├── repository/
│   ├── service/
│   └── config.ts
├── styles/
│   ├── tokens/
│   │   ├── _colors.css
│   │   ├── _spacing.css
│   │   └── _typography.css
│   ├── global.css
│   └── themes/
│       ├── light.css
│       └── dark.css
└── utils/

네이밍 규칙:

  • .astro 페이지 및 레이아웃: PascalCase/ 폴더에 index.astro
  • 프레임워크 섬 컴포넌트: PascalCase/ 폴더에 index.tsx
  • 페이지: kebab-case/ 폴더에 index.astro 또는 [dynamic]/ 폴더에 index.astro
  • CSS Module 파일: 항상 styles.module.css — 컴포넌트 디렉토리당 하나
  • 유틸리티: kebab-case.ts

디렉토리 책임 및 네이밍 규칙에 대한 전체 분석은 ./references/project-structure.md를 읽으십시오.

Astro 컴포넌트 형태

.astro 파일은 정적 구조 전용입니다. props와 데이터로부터 HTML을 렌더링하며, 이벤트를 처리하거나 상태를 관리하지 않습니다.

  • 프론트매터 펜스에서 interface Props를 통해 타입이 지정된 props를 사용하십시오.
  • Astro.props로 props에 접근합니다.
  • 컴포넌트를 작고 단일 목적으로 유지하십시오.
  • prop 드릴링보다는 컴포지션을 선호하십시오.
  • 표현식을 간단하게 유지하고, 복잡한 로직은 프론트매터 변수로 이동하십시오.
  • 의미상 의미가 있는 경우 일반 <div> 래퍼 대신 시맨틱 HTML 요소(<section>, <article>, <nav>, <main>, <header>)를 사용하십시오.
---
import styles from './styles.module.css'

interface Props {
  title: string
  subtitle?: string
}

const { title, subtitle } = Astro.props
---

<section class={styles.hero} aria-label={title}>
  <h1 class={styles.title}>{title}</h1>
  {subtitle && <p class={styles.subtitle}>{subtitle}</p>}
  <slot />
</section>

프론트매터 규칙, 슬롯 사용법 및 Astro API 참조는 ./references/component-patterns.md를 읽으십시오.

기본 컴포넌트 — SolidJS 또는 React, Astro 아님

기본 UI 프리미티브(Button, Input, Alert, Modal, Select, Toggle 등)는 항상 SolidJS 또는 React 컴포넌트로 구축됩니다. 절대 .astro 파일이 아닙니다.

이유: 기본 컴포넌트는 사용자 이벤트를 처리하고, 포커스를 관리하며, onClick 및 onChange 콜백을 노출하고, 때로는 내부 상태를 유지합니다. 이 중 어느 것도 .astro 파일에서는 불가능합니다. .astro로 "버튼"을 구축하면 클릭에 타입 안전하고 재사용 가능한 방식으로 응답할 수 없는 정적 HTML이 생성됩니다.

결정 규칙:

  1. 프리미티브가 존재하면 — 그것을 사용합니다.
  2. 프리미티브가 거의 맞지만 prop이 누락된 경우 — 기존 프리미티브를 확장합니다.
  3. 프리미티브가 존재하지 않으면 — 사용하기 전에 components/foundation/에 SolidJS 또는 React 컴포넌트로 생성합니다. 페이지 또는 기능 컴포넌트에 일회성 상호작용 요소를 인라인으로 절대 작성하지 마십시오.

모든 기본 컴포넌트는 일관되고, 접근 가능하며, 강력한 타입이 지정되어야 합니다. 섬 컴포넌트 내에 적용되는 구현 규칙은 solid.js 또는 react 스킬을 참조하십시오.

섬 및 하이드레이션

모든 상호작용 컴포넌트는 섬입니다. .astro 페이지 및 레이아웃에서 client:* 지시어를 사용하여 섬을 마운트합니다. 상호작용 요구 사항을 충족하는 최소 지시어를 선택하십시오.

지시어사용 시기
client:load페이지 로드 시 즉시 상호작용해야 함
client:idle브라우저가 유휴 상태가 될 때까지 기다릴 수 있음
client:visible뷰포트에 들어올 때만 하이드레이션 필요
client:media="(query)"특정 뷰포트 크기에서만 상호작용
client:only="solid-js" 또는 client:only="react"렌더링 시 브라우저 API 필요, SSR 건너뜀

기본값은 client:visible 또는 client:idle입니다. client:load는 사용자가 즉시 상호작용할 UI(기본 양식, 네비게이션 드롭다운, 인증 페이지)에만 사용하십시오.

섬 컴포넌트는 항상 자체 styles.module.css를 가집니다:

components/features/ContactForm/
├── index.tsx          ← SolidJS 또는 React 섬
└── styles.module.css  ← 별도 CSS Module, 다른 곳과 동일한 규칙

Astro 페이지에서 섬 마운트:

---
import styles from './styles.module.css'
import ContactForm from '../../components/features/ContactForm/index.tsx'

const { prefillEmail } = Astro.props
---

<section class={styles.section}>
  <h2 class={styles.heading}>Contact Us</h2>
  <ContactForm client:visible prefillEmail={prefillEmail} />
</section>

전체 결정 가이드, 프레임워크별 참고 사항 및 데이터 전달 규칙은 ./references/islands-and-hydration.md를 읽으십시오.

콘텐츠 컬렉션

콘텐츠 컬렉션은 Markdown, MDX 및 구조화된 데이터 파일을 완전한 TypeScript 타입 안전성으로 관리하는 표준 방법입니다.

Astro 5 주요 변경 사항:

  • 설정 파일은 src/content/config.ts가 아닌 src/content.config.ts에 있습니다.
  • 컬렉션은 astro/loaders에서 glob() 또는 file()인 loader를 사용합니다.
  • render(entry)는 astro:content에서 가져오는 함수입니다(entry.render() 아님).
  • z를 zod에서 직접 가져오지 말고 astro:content에서 가져옵니다.
// src/content.config.ts
import { defineCollection, z, reference } from 'astro:content'
import { glob } from 'astro/loaders'

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    draft: z.boolean().default(false),
  }),
})

export const collections = { blog }

페이지에서 렌더링:

---
import { getEntry, render } from 'astro:content'
const post = await getEntry('blog', Astro.params.slug)
const { Content } = await render(post)
---
<Content />
---

전체 API(로더, 교차 컬렉션 참조, 사용자 정의 로더, 런타임 컬렉션, 동적 라우트 패턴)는 ./references/content-collections.md를 읽으십시오.

서버 섬, SSR 및 Actions

서버 섬 (server:defer)

페이지 셸을 즉시 제공하면서 개인화되거나 동적인 .astro 컴포넌트를 지연시킵니다. 어댑터가 필요합니다.

<UserCart server:defer>
  <span slot="fallback">···</span>
</UserCart>

지연된 컴포넌트는 서버 측에서 실행되고 스트리밍됩니다. 정적 페이지 전달을 차단할 사용자별 UI(장바구니 카운트, 알림, 개인화된 인사말)에 사용하십시오.

온디맨드 렌더링 (SSR)

export const prerender = false(하이브리드 모드)로 페이지를 서버 렌더링에 옵트인하거나 전역적으로 output: 'server'를 설정합니다.

SSR 페이지는 Astro.cookies, Astro.request, Astro.redirect() 및 Astro.locals(미들웨어에 의해 채워짐)에 접근할 수 있습니다.

미들웨어

요청 차단 로직을 src/middleware.ts에 정의하십시오:

import { defineMiddleware } from 'astro:middleware'

export const onRequest = defineMiddleware(async (context, next) => {
  context.locals.user = await getUser(context.cookies.get('session')?.value)
  return next()
})

Astro Actions

src/actions/index.ts에 정의된 타입 안전한 서버 함수로, 섬 또는 HTML 양식에서 호출 가능:

// src/actions/index.ts
import { defineAction, ActionError } from 'astro:actions'
import { z } from 'astro:content'

export const server = {
  newsletter: {
    subscribe: defineAction({
      input: z.object({ email: z.string().email() }),
      handler: async ({ email }) => {
        await addSubscriber(email)
        return { success: true }
      },
    }),
  },
}

섬에서 호출:

import { actions } from 'astro:actions'
const { data, error } = await actions.newsletter.subscribe({ email })

isInputError(error)를 사용하여 Zod 유효성 검사 오류와 핸들러 오류를 구별하십시오.

전체 내용(렌더링 모드, 어댑터 구성, 미들웨어, 서버 섬, 온디맨드 API, 양식이 있는 액션, 오류 코드, 타입이 지정된 env vars(astro:env), 뷰 전환)은 ./references/server-rendering.md를 읽으십시오.

Astro API 참조

  • Astro.props — 타입이 지정된 컴포넌트 props
  • Astro.params — 동적 라우트 매개변수 ([slug], [...path])
  • Astro.request — 서버 측 요청 객체 (헤더, 쿠키, URL)
  • Astro.url — 현재 페이지 URL
  • getStaticPaths() — 정적 사이트 생성을 위한 경로 열거
  • getEntry() / getCollection() — 콘텐츠 컬렉션 쿼리

서비스 계층 데이터 흐름

Astro 페이지와 레이아웃은 프론트매터 펜스에서 서비스 또는 저장소 함수를 호출하기에 적합한 장소입니다. 컴포넌트는 props로 데이터를 수신해야 합니다.

  • 페이지 프론트매터에서 데이터를 가져오고, 컴포넌트 내부에서는 가져오지 마십시오.
  • 데이터를 타입이 지정된 props로 전달하십시오.
  • 컴포넌트는 저장소나 외부 API를 직접 호출해서는 안 됩니다.
  • 유효성 검사 및 비즈니스 로직은 서비스 계층에 유지하십시오.

접근성

  • 요소에 의미가 있는 경우 시맨틱 HTML 요소를 사용하십시오.
  • 실제 시맨틱 정보를 추가하는 곳에 ARIA 속성을 추가하십시오.
  • 모든 상호작용 요소에 접근 가능한 이름이 있는지 확인하십시오.
  • 가시적인 포커스 상태를 유지하십시오.
  • 상호작용 요소에 대해 최소 44px 터치 대상 크기를 유지하십시오.
  • prefers-reduced-motion을 존중하십시오.
  • 페이지 콘텐츠 내에서 제목을 논리적으로 구성하십시오(h1 → h2 → h3).

ARIA 패턴, 양식 레이블 및 제목 계층 구조 가이드는 ./references/accessibility-patterns.md를 읽으십시오.

지원 파일

  • ./references/styling-and-css-modules.md CSS Modules 규칙, ...

관련 스킬