소스 정보
- 저장소
- puk0806/gugbab-claude
- 최근 소스 활동
- 2026년 8월 26일 08:04
- 감지된 SKILL.md 언어
- 한국어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/puk0806/gugbab-claude --skill testing명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
DDD(Domain-Driven Design) 아키텍처 핵심 패턴 - 유비쿼터스 언어, 서브도메인, 바운디드 컨텍스트, Aggregate, Entity/VO, 도메인 서비스/이벤트, 레이어드 아키텍처
대규모 React/Next.js 프로젝트를 layer-first(types/·utils/·hooks/·api/·components/ 밑에 도메인이 반복되는 구조)에서 domain-first(feature/도메인 우선) 구조로 전환하는 설계 기준과 절차. Feature-Sliced Design 2.1 정본(layers 6종·slices·segments·import 규칙·@x 크로스임포트·public API), FSD를 쓰지 않는 경량 대안(features + shared 2~3계층 + ESLint import/no-restricted-paths), Next.js App Router 공존 전략(route group `()`·private folder `_`·colocation), Turborepo/Nx 모노레포에서 폴더↔패키지 승격 기준, colocation과 배럴 파일 성능 트레이드오프, 도메인 경계 역추출(import 그래프·change coupling·용어 클러스터), 전환 실패 패턴(shared 비대화·entities 남용·순환 의존·도메인=라우트 착각·조기 추상화). 도메인 개념 자체(바운디드 컨텍스트·유비쿼터스 언어)는 `architecture/ddd` 스킬을 참조한다.
소스 파일 수천 개 규모 프론트엔드 코드베이스를 멈추지 않고 점진 재구조화하는 실행 전략 - Strangler Fig / Branch by Abstraction / Parallel Change, ts-morph·jscodeshift codemod, PR 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
SKILL.md 표시 중
| name | testing |
| description | Jest/Vitest + React Testing Library 핵심 패턴, 캡슐화 기반 컴포넌트 테스트 전략 |
소스: https://testing-library.com/docs/react-testing-library/intro | https://vitest.dev/guide/ 검증일: 2026-08-26 (최초 2026-04-01 · 08-26 freshness 재검증: RTL 16부터
@testing-library/dom이 peerDependency로 분리되어 설치 커맨드에 명시 추가. Vitest 4.1 stable·RTL 16.3 기준 나머지 패턴 VERIFIED)
| Jest | Vitest | |
|---|---|---|
| 적합한 환경 | CRA, Next.js (기본 설정) | Vite, 모노레포 |
| 설정 복잡도 | Next.js에서 jest.config + babel/swc 설정 필요 | vite.config에 통합, 설정 최소 |
| 실행 속도 | 보통 | 빠름 (Vite HMR 활용) |
| API 호환성 | - | Jest API 100% 호환 (vi = jest) |
결론: Vite 기반이면 Vitest, Next.js면 Jest (또는 Next.js 공식 jest 설정 사용).
pnpm add -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom @testing-library/user-event
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({ dir: './' })
module.exports = createJestConfig({
testEnvironment: 'jest-environment-jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
})
// jest.setup.ts
import '@testing-library/jest-dom'
pnpm add -D vitest @vitest/ui jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom @testing-library/user-event
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
globals: true,
},
})
// vitest.setup.ts
import '@testing-library/jest-dom'
// ❌ 구현 테스트 — 내부 상태를 직접 확인
expect(component.state.isOpen).toBe(true)
expect(wrapper.find('.dropdown-menu')).toHaveLength(1)
// ✅ 동작 테스트 — 사용자가 보는 것을 확인
expect(screen.getByRole('listbox')).toBeVisible()
expect(screen.getByText('옵션 1')).toBeInTheDocument()
// Button.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from './Button'
describe('Button', () => {
it('텍스트를 렌더링한다', () => {
render(<Button>저장</Button>)
expect(screen.getByRole('button', { name: '저장' })).toBeInTheDocument()
})
it('클릭 시 onClick이 호출된다', async () => {
const user = userEvent.setup()
const onClick = vi.fn() // Jest: jest.fn()
render(<Button onClick={onClick}>저장</Button>)
await user.click(screen.getByRole('button'))
expect(onClick).toHaveBeenCalledTimes(1)
})
it(, () => {
user = userEvent.()
onClick = vi.()
()
user.(screen.())
(onClick)..()
})
})
// Dropdown.test.tsx — 내부 구현(isOpen state 등)은 테스트하지 않음
describe('Dropdown', () => {
it('트리거 클릭 시 옵션 목록이 열린다', async () => {
const user = userEvent.setup()
render(<Dropdown options={['사과', '배', '포도']} />)
// 초기에는 닫혀 있음
expect(screen.queryByRole('listbox')).not.toBeInTheDocument()
// 트리거 클릭
await user.click(screen.getByRole('button'))
// 옵션 목록이 보임 — 내부 상태가 아닌 DOM으로 검증
expect(screen.getByRole('listbox')).toBeVisible()
expect(screen.getByText('사과')).toBeInTheDocument()
})
it('옵션 선택 시 onChange가 선택값과 함께 호출된다', async () => {
const user = userEvent.setup()
const onChange = vi.fn()
render(<Dropdown options={['', '']} = />)
user.(screen.())
user.(screen.())
(onChange).()
})
})
// useCounter.test.ts
import { renderHook, act } from '@testing-library/react'
import { useCounter } from './useCounter'
describe('useCounter', () => {
it('초기값으로 시작한다', () => {
const { result } = renderHook(() => useCounter(10))
expect(result.current.count).toBe(10)
})
it('increment 호출 시 1 증가한다', () => {
const { result } = renderHook(() => useCounter(0))
act(() => { result.current.increment() })
expect(result.current.count).toBe(1)
})
})
// UserProfile.test.tsx
import { render, screen, waitFor } from '@testing-library/react'
// API 모킹 (Vitest)
vi.mock('../api/users', () => ({
fetchUser: vi.fn().mockResolvedValue({ name: '홍길동', email: 'hong@test.com' }),
}))
describe('UserProfile', () => {
it('사용자 정보를 불러와서 표시한다', async () => {
render(<UserProfile userId="1" />)
// 로딩 상태 확인
expect(screen.getByText('로딩 중...')).toBeInTheDocument()
// 데이터 로드 완료 대기
await waitFor(() => {
expect(screen.getByText('홍길동')).toBeInTheDocument()
})
expect(screen.getByText('hong@test.com')).toBeInTheDocument()
})
it('API 실패 시 에러 메시지를 표시한다', async () => {
const { fetchUser } = await import()
vi.(fetchUser).( ())
()
( {
(screen.()).()
})
})
})
| 우선순위 | 쿼리 | 사용 시점 |
|---|---|---|
| 1 | getByRole | 접근성 역할 (button, heading, listbox 등) |
| 2 | getByLabelText | 폼 필드 (label과 연결된 input) |
| 3 | getByPlaceholderText | placeholder가 있는 input |
| 4 | getByText | 텍스트 콘텐츠 |
| 5 | getByDisplayValue | 현재 값이 있는 input/select |
| 6 | getByAltText | img alt 텍스트 |
| 7 | getByTitle | title 속성 |
| 8 | getByTestId | data-testid (최후 수단) |
components/
└── Button/
├── Button.tsx
├── Button.module.scss
└── Button.test.tsx ← 컴포넌트 옆에 배치 (colocated)
__tests__/ 폴더 분리보다 컴포넌트 옆에 두는 것을 권장. 파일 이동 시 테스트도 함께 이동됨.