<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Eugenius1st.log</title>
        <link>https://velog.io/</link>
        <description>자신만의 속도로</description>
        <lastBuildDate>Wed, 24 Jun 2026 01:34:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Eugenius1st.log</title>
            <url>https://velog.velcdn.com/images/angel_eugnen/profile/b9287067-f0b6-4bab-a461-9a8a62e31518/KakaoTalk_20220408_210325466.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Eugenius1st.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/angel_eugnen" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[OMC 빠른 참조 카드]]></title>
            <link>https://velog.io/@angel_eugnen/OMC-%EB%B9%A0%EB%A5%B8-%EC%B0%B8%EC%A1%B0-%EC%B9%B4%EB%93%9C</link>
            <guid>https://velog.io/@angel_eugnen/OMC-%EB%B9%A0%EB%A5%B8-%EC%B0%B8%EC%A1%B0-%EC%B9%B4%EB%93%9C</guid>
            <pubDate>Wed, 24 Jun 2026 01:34:26 GMT</pubDate>
            <description><![CDATA[<h1 id="🚀-omc-oh-my-claude-code-빠른-참조-카드-v460">🚀 OMC (Oh My Claude Code) 빠른 참조 카드 v4.6.0</h1>
<h2 id="📌-처음-시작">📌 처음 시작</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/deepinit</code></td>
<td>프로젝트 문서화</td>
</tr>
<tr>
<td><code>/deep-interview &#39;아이디어&#39;</code></td>
<td>요구사항 명확화 (신기능)</td>
</tr>
<tr>
<td><code>/omc-doctor</code></td>
<td>설치 진단</td>
</tr>
</tbody></table>
<hr>
<h2 id="📝-계획">📝 계획</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>plan this: [기능]</code></td>
<td>계획 수립 후 승인</td>
</tr>
<tr>
<td><code>ralplan: [기능]</code></td>
<td>전문가 합의 기반 계획</td>
</tr>
</tbody></table>
<hr>
<h2 id="⚡-실행-강도-순">⚡ 실행 (강도 순)</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>autopilot: [지시]</code></td>
<td>자율 실행 (계획 → 구현 → 검증 자동)</td>
</tr>
<tr>
<td><code>ralph: [지시]</code></td>
<td>완료까지 반복 (PRD 자동 생성)</td>
</tr>
<tr>
<td><code>ulw [지시]</code></td>
<td>최대 병렬 실행</td>
</tr>
<tr>
<td><code>team N:agent [지시]</code></td>
<td>N개 에이전트 팀 협업</td>
</tr>
<tr>
<td><code>eco: [지시]</code></td>
<td>토큰 절약 모드</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔍-분석--리뷰">🔍 분석 &amp; 리뷰</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>analyze: [문제]</code></td>
<td>디버깅 / 원인 분석</td>
</tr>
<tr>
<td><code>review code</code></td>
<td>종합 코드 리뷰</td>
</tr>
<tr>
<td><code>security review</code></td>
<td>보안 취약점 점검</td>
</tr>
<tr>
<td><code>fix build</code></td>
<td>빌드 / 타입 에러 수정</td>
</tr>
<tr>
<td><code>tdd: [기능]</code></td>
<td>테스트 주도 개발</td>
</tr>
</tbody></table>
<hr>
<h2 id="🤖-멀티-ai">🤖 멀티 AI</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/omc-teams N:ai [지시]</code></td>
<td>Codex / Gemini 병렬 활용</td>
</tr>
<tr>
<td><code>/ccg [지시]</code></td>
<td>3개 모델 동시 분석</td>
</tr>
</tbody></table>
<hr>
<h2 id="🛠️-유틸리티">🛠️ 유틸리티</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/note [내용]</code></td>
<td>메모 저장</td>
</tr>
<tr>
<td><code>&lt;remember&gt;...&lt;/remember&gt;</code></td>
<td>7일 기억</td>
</tr>
<tr>
<td><code>&lt;remember priority&gt;...&lt;/remember&gt;</code></td>
<td>영구 기억</td>
</tr>
<tr>
<td><code>/cancel</code></td>
<td>현재 모드 취소</td>
</tr>
<tr>
<td><code>/cancel --force</code></td>
<td>전체 초기화</td>
</tr>
<tr>
<td><code>/trace</code></td>
<td>실행 타임라인</td>
</tr>
<tr>
<td><code>/learner</code></td>
<td>패턴 추출 및 재사용</td>
</tr>
</tbody></table>
<hr>
<h2 id="📊-참고">📊 참고</h2>
<h3 id="실행-강도">실행 강도</h3>
<pre><code class="language-text">대화 &lt; plan &lt; autopilot &lt; ralph &lt; team ralph</code></pre>
<h3 id="비용">비용</h3>
<pre><code class="language-text">haiku &lt; sonnet &lt;&lt; opus</code></pre>
<h3 id="github">GitHub</h3>
<pre><code class="language-text">github.com/Yeachan-Heo/oh-my-claudecode</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 첫 시작]]></title>
            <link>https://velog.io/@angel_eugnen/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B2%AB-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@angel_eugnen/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B2%AB-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Wed, 24 Jun 2026 01:14:26 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-첫-시작">프로젝트 첫 시작</h2>
<p>새프로젝트에 들어가면 항상 이것부터 해라</p>
<blockquote>
<p>/deepinit</p>
</blockquote>
<p>OMC가 프로젝트 구조를 분서갛고 AGENTS.md 계층 문서를 자동 생성한다.</p>
<h2 id="모델-라우팅">모델 라우팅</h2>
<p>OMC는 작업 복잡도에 따라 자동으로 모델을 선택한다.</p>
<pre><code>haiku  (저비용, 빠름)
  → 파일 탐색, 구조 분석, 문서 작성, 간단한 검색
  → 예: explore, writer 에이전트

sonnet (중간 비용, 균형)
  → 일반 구현, 테스트, 디버깅, 리팩토링
  → 예: executor, test-engineer, debugger 에이전트

opus   (고비용, 최고 성능)
  → 아키텍처 설계, 복잡한 분석, 종합 리뷰, 비판적 검토
  → 예: architect, code-reviewer, critic 에이전트</code></pre><p>실제로 이렇게 동작한다.
사용자: &quot;autopilot: 인증 시스템 만들어줘&quot;</p>
<p>OMC 내부:</p>
<pre><code>  1. explore    (haiku)  → 현재 코드 구조 파악
  2. planner    (opus)   → 구현 계획 수립
  3. architect  (opus)   → 인터페이스 설계
  4. executor   (sonnet) × 3 → 병렬 구현
  5. test-engineer (sonnet) → 테스트 작성
  6. verifier   (sonnet) → 완료 검증</code></pre><h2 id="autopilot---완전-자율-실행">Autopilot - 완전 자율 실행</h2>
<p>복잡한 단일 기능 구현에 최적, 계획부터 검증까지 자동</p>
<p>트리거 키워드: <code>autopilot:</code>, <code>build me</code>, <code>I want a</code></p>
<pre><code>Phase 1 — 확장 (Expansion)
  &quot;할일 목록&quot;을 구체적 요구사항으로 확장
  → 할일 추가/삭제/완료 토글, 로컬 상태 관리, 반응형 UI

Phase 2 — 계획 (Planning)
  planner 에이전트가 작업 분해
  → Todo 타입 정의 → TodoItem 컴포넌트 → TodoList → 페이지 통합

Phase 3 — 실행 (Execution)
  executor 에이전트들 병렬 구현
  → src/types/todo.ts 생성
  → src/components/TodoItem.tsx 생성
  → src/components/TodoList.tsx 생성
  → src/app/todos/page.tsx 수정

Phase 4 — QA
  qa-tester 가 빌드/린트/테스트 실행
  → npm run build → 통과
  → npm run lint  → 통과

Phase 5 — 검증 (Verification)
  verifier 에이전트가 요구사항 체크리스트 확인
  → ✓ 할일 추가 기능
  → ✓ 삭제 기능
  → ✓ 완료 토글
  → &quot;완료 확인됨&quot;</code></pre><h2 id="ralph-끝까지-멈추지-않는-모드">ralph: 끝까지 멈추지 않는 모드</h2>
<p>v4.6.0부터 Ralph는 자동으로 prd.json을 생성합니다. 모든 수용 기준이 통과할 때까지 반복합니다.</p>
<p>트리거 키워드: ralph:, don&#39;t stop, must complete</p>
<pre><code>사용자: &quot;ralph: 이 앱에 JWT 인증 시스템 전체 구현해줘&quot;

자동 생성: .omc/prd.json
  {
    &quot;title&quot;: &quot;JWT 인증 시스템&quot;,
    &quot;acceptance_criteria&quot;: [
      &quot;회원가입 API (/api/auth/register) 동작&quot;,
      &quot;로그인 API (/api/auth/login) JWT 발급&quot;,
      &quot;보호된 라우트에서 토큰 검증&quot;,
      &quot;리프레시 토큰 갱신&quot;,
      &quot;모든 에러 케이스 핸들링&quot;
    ]
  }

Iteration 1: 기본 구조 + 회원가입 구현
Iteration 2: 로그인 + JWT 발급 구현
Iteration 3: 미들웨어 + 토큰 검증 구현
Iteration 4: 리프레시 토큰 구현
Iteration 5: verifier 검증 → &quot;에러 핸들링 미흡&quot;
Iteration 6: 에러 핸들링 전체 보강
Iteration 7: verifier 검증 → &quot;모든 기준 통과&quot; → 완료</code></pre><p>PRD 없이 시작하려면: &quot;ralph --no-prd: 로그인 기능 만들어줘&quot;</p>
<h2 id="ultrawork--최대-병렬-실행">Ultrawork — 최대 병렬 실행</h2>
<p>5개 이상의 에이전트를 동시에 투입. 독립적인 작업 대량 처리에 최적.</p>
<p>트리거 키워드: ultrawork, ulw</p>
<pre><code>사용자: &quot;ulw: 이 5개 API 엔드포인트 전부 구현해줘
        (users, posts, comments, likes, notifications)&quot;

동시 실행:
  executor #1 → /api/users 구현    ─┐
  executor #2 → /api/posts 구현    ─┤
  executor #3 → /api/comments 구현 ─┤ 병렬
  executor #4 → /api/likes 구현    ─┤
  executor #5 → /api/notifications ─┘

총 소요 시간 ≈ 단일 작업 시간 × 1.2 (병렬 오버헤드 포함)</code></pre><h2 id="ecomode--토큰-효율-최대화">Ecomode — 토큰 효율 최대화</h2>
<p>비용이 중요할 때. 가능한 한 저렴한 모델 우선 사용.</p>
<p>트리거 키워드: eco</p>
<pre><code>사용자: &quot;eco: 린트 에러 전부 수정해줘&quot;

→ haiku 모델로 먼저 시도
→ haiku가 해결 못하면 sonnet으로 에스컬레이션
→ opus는 절대적으로 필요한 경우에만 사용
→ 예상 비용 절감: 30~50%</code></pre><h2 id="team--네이티브-팀-협업">Team — 네이티브 팀 협업</h2>
<p>Claude Code 네이티브 팀 기능을 활용한 조율 방식.</p>
<p>트리거 키워드: team</p>
<pre><code>사용자: &quot;team 3:executor 모든 컴포넌트 테스트 코드 작성&quot;

→ executor 에이전트 3개가 팀으로 조율하며 작업
→ 실시간 메시지로 작업 상태 공유
→ 공유 태스크 리스트에서 각자 작업 픽업

파이프라인:
  team-plan → team-prd → team-exec → team-verify → team-fix</code></pre><h2 id="deep-interview--막연한-아이디어-명확화">Deep Interview — 막연한 아이디어 명확화</h2>
<p>v4.6.0 신기능. 실행 전 소크라테스식 질문으로 모호함을 제거합니다.</p>
<p>트리거 키워드: /deep-interview</p>
<pre><code>사용자: &quot;/deep-interview &#39;대시보드 만들어줘&#39;&quot;

OMC가 수학적 모호성 점수를 계산하고 질문 생성:

Q1: &quot;어떤 종류의 데이터를 보여주는 대시보드인가요?
    (a) 사용자 분석  (b) 매출 현황  (c) 시스템 모니터링  (d) 기타&quot;

Q2: &quot;실시간 업데이트가 필요한가요?&quot;

Q3: &quot;대상 사용자는 누구인가요? 기술적 지식 수준은?&quot;

→ 명확성 점수가 임계값 이상이 되면 실행 시작
→ 애매한 요구사항으로 인한 재작업 방지</code></pre><p>사용 시점: 요구사항이 추상적이거나 규모가 큰 작업 전</p>
<h2 id="plan--전략적-계획-수립">Plan — 전략적 계획 수립</h2>
<p>실행 전 계획을 먼저 세우고 승인을 받는 방식.</p>
<p>트리거 키워드: plan this:, plan the</p>
<pre><code>사용자: &quot;plan this: 결제 시스템 연동&quot;

→ 현재 코드 분석 (explore)
→ 구현 계획 상세 작성
→ 계획을 사용자에게 제시 → 승인 요청

출력 예시:
  구현 계획: 결제 시스템 연동

  Step 1: 환경 변수 설정 (5분)
    - .env에 STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY 추가

  Step 2: Stripe SDK 설치 및 클라이언트 설정 (10분)
    - npm install @stripe/stripe-js stripe
    - src/lib/stripe.ts 생성

  Step 3: 결제 API 라우트 구현 (20분)
    - POST /api/checkout → Stripe 세션 생성
    - GET /api/checkout/success → 결제 확인

  Step 4: 프론트엔드 결제 버튼 구현 (15분)
    - src/components/CheckoutButton.tsx 생성

  Step 5: 웹훅 처리 (15분)
    - POST /api/webhooks/stripe → 결제 완료 처리

  이 계획으로 진행할까요?</code></pre><h2 id="ralplan--합의-기반-계획">Ralplan — 합의 기반 계획</h2>
<p>Planner, Architect, Critic 세 에이전트가 합의에 이를 때까지 반복 검토.</p>
<p>트리거 키워드: ralplan, consensus plan</p>
<pre><code>사용자: &quot;ralplan: 멀티테넌트 SaaS 아키텍처 설계해줘&quot;

Round 1:
  planner   → 초안 작성 (마이크로서비스 제안)
  architect → 검토: &quot;데이터 격리 방식 명시 필요&quot;
  critic    → 비판: &quot;배포 복잡성 과소평가됨&quot;

Round 2:
  planner   → 수정안 (모놀리스 + 모듈식)
  architect → 수정: &quot;테넌트별 스키마 격리 추가&quot;
  critic    → &quot;허용 가능, 스케일링 계획 보강 필요&quot;

Round 3:
  합의 도달 → 최종 아키텍처 문서 생성

옵션: &quot;ralplan --deliberate: ...&quot; 
  → 사전 위험 분석 + 확장된 테스트 계획 포함</code></pre><h2 id="claude-code-모드--에이전트-완전-정리">Claude Code 모드 &amp; 에이전트 완전 정리</h2>
<p>Claude Code는 단순히 코드 생성만 하는 것이 아니라, 작업 규모와 목적에 따라 다양한 실행 모드와 전문 에이전트를 활용할 수 있다.</p>
<hr>
<h3 id="1-작업-규모별-추천-모드">1. 작업 규모별 추천 모드</h3>
<table>
<thead>
<tr>
<th>작업 유형</th>
<th>추천 모드</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>버그 1개 수정</td>
<td>일반 대화</td>
<td>단순하고 빠르게 해결 가능</td>
</tr>
<tr>
<td>컴포넌트 1개 추가</td>
<td><code>plan → 직접 구현</code></td>
<td>구현 전에 구조를 먼저 정리</td>
</tr>
<tr>
<td>기능 1개 개발 (여러 파일 수정)</td>
<td><code>autopilot</code></td>
<td>계획 → 구현 → 검증까지 자동 수행</td>
</tr>
<tr>
<td>대규모 기능 개발</td>
<td><code>ralph</code></td>
<td>완료될 때까지 반복적으로 작업</td>
</tr>
<tr>
<td>풀스택 프로젝트 구축</td>
<td><code>team + ralph</code></td>
<td>여러 에이전트 협업 + 지속 실행</td>
</tr>
<tr>
<td>여러 파일 리팩토링</td>
<td><code>ulw</code></td>
<td>병렬 처리에 강함</td>
</tr>
<tr>
<td>아키텍처 설계</td>
<td><code>ralplan</code></td>
<td>전문가 관점으로 설계 합의</td>
</tr>
<tr>
<td>막연한 아이디어 정리</td>
<td><code>/deep-interview</code></td>
<td>요구사항을 먼저 명확화</td>
</tr>
<tr>
<td>비용 절감 우선</td>
<td><code>eco</code></td>
<td>저비용 모델 중심으로 실행</td>
</tr>
</tbody></table>
<hr>
<h3 id="2-빌드--분석-에이전트">2. 빌드 &amp; 분석 에이전트</h3>
<p>프로젝트를 이해하고 구현하기 위한 핵심 에이전트들이다.</p>
<table>
<thead>
<tr>
<th>에이전트</th>
<th>모델</th>
<th>역할</th>
<th>활용 예시</th>
</tr>
</thead>
<tbody><tr>
<td>explore</td>
<td>Haiku</td>
<td>코드 탐색, 파일 구조 분석</td>
<td>&quot;인증 관련 파일 찾아줘&quot;</td>
</tr>
<tr>
<td>analyst</td>
<td>Opus</td>
<td>요구사항 분석, 엣지케이스 정리</td>
<td>&quot;이 기능의 예외 상황 정리해줘&quot;</td>
</tr>
<tr>
<td>planner</td>
<td>Opus</td>
<td>작업 순서 계획 수립</td>
<td>&quot;마이그레이션 순서 알려줘&quot;</td>
</tr>
<tr>
<td>architect</td>
<td>Opus</td>
<td>시스템 설계 및 인터페이스 정의</td>
<td>&quot;확장 가능한 API 구조 설계해줘&quot;</td>
</tr>
<tr>
<td>debugger</td>
<td>Sonnet</td>
<td>버그 원인 분석</td>
<td>&quot;이 에러가 왜 발생하는지 찾아줘&quot;</td>
</tr>
<tr>
<td>executor</td>
<td>Sonnet</td>
<td>구현 및 리팩토링</td>
<td>&quot;이 코드를 TypeScript로 변환해줘&quot;</td>
</tr>
<tr>
<td>deep-executor</td>
<td>Opus</td>
<td>복잡한 다중 파일 구현</td>
<td>대규모 기능 개발</td>
</tr>
<tr>
<td>verifier</td>
<td>Sonnet</td>
<td>구현 결과 검증</td>
<td>&quot;정말 모든 기능이 동작하는지 확인해줘&quot;</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-리뷰-에이전트">3. 리뷰 에이전트</h3>
<p>코드 품질과 안정성을 검토하는 역할을 담당한다.</p>
<table>
<thead>
<tr>
<th>에이전트</th>
<th>모델</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>quality-reviewer</td>
<td>Sonnet</td>
<td>로직 결함, 성능 문제, 안티패턴 점검</td>
</tr>
<tr>
<td>security-reviewer</td>
<td>Sonnet</td>
<td>보안 취약점 분석</td>
</tr>
<tr>
<td>code-reviewer</td>
<td>Opus</td>
<td>종합 코드 리뷰 및 API 계약 검토</td>
</tr>
</tbody></table>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-bash">review code</code></pre>
<p>→ code-reviewer 실행</p>
<pre><code class="language-bash">security review</code></pre>
<p>→ security-reviewer 실행</p>
<hr>
<h3 id="4-도메인-전문가-에이전트">4. 도메인 전문가 에이전트</h3>
<p>특정 분야에 특화된 전문가 역할을 수행한다.</p>
<table>
<thead>
<tr>
<th>에이전트</th>
<th>모델</th>
<th>역할</th>
<th>활용 예시</th>
</tr>
</thead>
<tbody><tr>
<td>test-engineer</td>
<td>Sonnet</td>
<td>테스트 전략 및 TDD</td>
<td>&quot;단위 테스트 작성해줘&quot;</td>
</tr>
<tr>
<td>build-fixer</td>
<td>Sonnet</td>
<td>빌드 및 타입 에러 해결</td>
<td>&quot;타입 에러 전부 수정해줘&quot;</td>
</tr>
<tr>
<td>designer</td>
<td>Sonnet</td>
<td>UI/UX 설계</td>
<td>&quot;폼 UX 개선해줘&quot;</td>
</tr>
<tr>
<td>writer</td>
<td>Haiku</td>
<td>문서 작성</td>
<td>README, API 문서 작성</td>
</tr>
<tr>
<td>qa-tester</td>
<td>Sonnet</td>
<td>E2E 및 런타임 검증</td>
<td>&quot;실제로 테스트해봐&quot;</td>
</tr>
<tr>
<td>scientist</td>
<td>Sonnet</td>
<td>데이터 분석</td>
<td>로그 분석, 통계 분석</td>
</tr>
<tr>
<td>document-specialist</td>
<td>Sonnet</td>
<td>공식 문서 조사</td>
<td>Next.js, React 문서 확인</td>
</tr>
<tr>
<td>git-master</td>
<td>Sonnet</td>
<td>Git 관리</td>
<td>커밋 정리, PR 작성</td>
</tr>
<tr>
<td>code-simplifier</td>
<td>Opus</td>
<td>복잡한 코드 단순화</td>
<td>중복 제거, 리팩토링</td>
</tr>
</tbody></table>
<hr>
<h3 id="5-조율-에이전트">5. 조율 에이전트</h3>
<p>다른 에이전트가 만든 결과를 비판적으로 검토한다.</p>
<table>
<thead>
<tr>
<th>에이전트</th>
<th>모델</th>
<th>역할</th>
<th>활용 예시</th>
</tr>
</thead>
<tbody><tr>
<td>critic</td>
<td>Opus</td>
<td>설계 및 계획 검토</td>
<td>&quot;이 아키텍처의 문제점을 찾아줘&quot;</td>
</tr>
</tbody></table>
<hr>
<h3 id="추천-활용-패턴">추천 활용 패턴</h3>
<ul>
<li>기능 개발</li>
</ul>
<pre><code class="language-text">analyst
 → planner
 → architect
 → executor
 → verifier</code></pre>
<p>요구사항 분석 → 설계 → 구현 → 검증</p>
<hr>
<ul>
<li>버그 수정</li>
</ul>
<pre><code class="language-text">explore
 → debugger
 → executor
 → verifier</code></pre>
<p>원인 파악 → 수정 → 검증</p>
<hr>
<ul>
<li>코드 리뷰</li>
</ul>
<pre><code class="language-text">quality-reviewer
 → security-reviewer
 → code-reviewer</code></pre>
<p>품질 → 보안 → 최종 리뷰</p>
<hr>
<ul>
<li>대규모 프로젝트</li>
</ul>
<pre><code class="language-text">team
 + ralph
 + architect
 + deep-executor
 + verifier</code></pre>
<p>설계부터 구현, 검증까지 자동화된 워크플로우 구성 가능</p>
<hr>
<h3 id="한-줄-요약">한 줄 요약</h3>
<ul>
<li>작은 작업 → 일반 대화</li>
<li>기능 개발 → autopilot</li>
<li>대규모 개발 → ralph</li>
<li>설계 → architect / ralplan</li>
<li>리뷰 → code-reviewer</li>
<li>보안 → security-reviewer</li>
<li>테스트 → test-engineer</li>
<li>Git 작업 → git-master</li>
</ul>
<p>작업 규모가 커질수록 &quot;계획 → 구현 → 검증&quot;을 자동으로 반복하는 모드가 효율적이다.</p>
<h2 id="6-매직-키워드--슬래시-명령어-레퍼런스">6. 매직 키워드 &amp; 슬래시 명령어 레퍼런스</h2>
<p>Claude Code는 특정 키워드나 슬래시 명령어를 입력하면 자동으로 적절한 실행 모드와 에이전트를 활성화할 수 있다.</p>
<hr>
<h3 id="매직-키워드magic-keywords">매직 키워드(Magic Keywords)</h3>
<p>프롬프트에 특정 키워드를 포함하면 자동으로 해당 모드가 활성화된다.</p>
<h3 id="자율-실행-계열">자율 실행 계열</h3>
<table>
<thead>
<tr>
<th>키워드</th>
<th>실행 모드</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>autopilot:</code></td>
<td>완전 자율 실행</td>
<td><code>autopilot: 검색 기능 추가해줘</code></td>
</tr>
<tr>
<td><code>build me</code></td>
<td>완전 자율 실행</td>
<td><code>build me a REST API</code></td>
</tr>
<tr>
<td><code>ralph:</code></td>
<td>완료까지 반복 실행</td>
<td><code>ralph: 인증 시스템 전체 구현</code></td>
</tr>
<tr>
<td><code>don&#39;t stop</code></td>
<td>완료 시까지 반복</td>
<td><code>don&#39;t stop until all tests pass</code></td>
</tr>
</tbody></table>
<h3 id="언제-사용할까">언제 사용할까?</h3>
<ul>
<li>여러 파일 수정이 필요한 기능 개발</li>
<li>구현 → 테스트 → 수정 과정을 자동으로 돌리고 싶을 때</li>
<li>중간 승인 없이 끝까지 진행시키고 싶을 때</li>
</ul>
<hr>
<h3 id="병렬-작업-계열">병렬 작업 계열</h3>
<table>
<thead>
<tr>
<th>키워드</th>
<th>실행 모드</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>ulw</code></td>
<td>최대 병렬 처리</td>
<td><code>ulw 이 10개 컴포넌트 전부 리팩토링</code></td>
</tr>
<tr>
<td><code>ultrawork</code></td>
<td>최대 병렬 처리</td>
<td><code>ultrawork: API 5개 동시 구현</code></td>
</tr>
</tbody></table>
<h3 id="언제-사용할까-1">언제 사용할까?</h3>
<ul>
<li>많은 파일을 동시에 수정해야 할 때</li>
<li>독립적인 작업을 병렬로 처리할 수 있을 때</li>
<li>대규모 리팩토링 작업</li>
</ul>
<hr>
<h3 id="계획-수립-계열">계획 수립 계열</h3>
<table>
<thead>
<tr>
<th>키워드</th>
<th>실행 모드</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>plan this:</code></td>
<td>작업 계획 수립</td>
<td><code>plan this: 결제 시스템 연동</code></td>
</tr>
<tr>
<td><code>ralplan</code></td>
<td>전문가 합의 기반 설계</td>
<td><code>ralplan: DB 스키마 설계</code></td>
</tr>
</tbody></table>
<h3 id="언제-사용할까-2">언제 사용할까?</h3>
<ul>
<li>구현 전에 설계를 검토하고 싶을 때</li>
<li>시스템 구조를 먼저 정의해야 할 때</li>
<li>대규모 기능 개발 전</li>
</ul>
<hr>
<h2 id="분석-및-디버깅-계열">분석 및 디버깅 계열</h2>
<table>
<thead>
<tr>
<th>키워드</th>
<th>실행 모드</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>analyze</code></td>
<td>분석 및 원인 추적</td>
<td><code>analyze: 메모리 누수 원인 찾아줘</code></td>
</tr>
<tr>
<td><code>debug</code></td>
<td>디버깅 모드</td>
<td><code>debug: API 500 에러 원인 분석</code></td>
</tr>
</tbody></table>
<h3 id="언제-사용할까-3">언제 사용할까?</h3>
<ul>
<li>에러의 근본 원인을 찾고 싶을 때</li>
<li>성능 저하나 메모리 문제 분석</li>
<li>복잡한 버그 조사</li>
</ul>
<hr>
<h3 id="리뷰-및-품질-관리-계열">리뷰 및 품질 관리 계열</h3>
<table>
<thead>
<tr>
<th>키워드</th>
<th>실행 모드</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>review code</code></td>
<td>종합 코드 리뷰</td>
<td><code>review code</code></td>
</tr>
<tr>
<td><code>security review</code></td>
<td>보안 리뷰</td>
<td><code>security review</code></td>
</tr>
<tr>
<td><code>fix build</code></td>
<td>빌드 오류 수정</td>
<td><code>fix build</code></td>
</tr>
<tr>
<td><code>type errors</code></td>
<td>타입 오류 수정</td>
<td><code>fix all type errors</code></td>
</tr>
<tr>
<td><code>tdd</code></td>
<td>테스트 주도 개발</td>
<td><code>tdd: 로그인 기능 구현</code></td>
</tr>
</tbody></table>
<h3 id="언제-사용할까-4">언제 사용할까?</h3>
<ul>
<li>PR 생성 전</li>
<li>배포 직전 점검</li>
<li>TypeScript 마이그레이션</li>
<li>테스트 코드 작성</li>
</ul>
<hr>
<h2 id="슬래시-명령어slash-commands">슬래시 명령어(Slash Commands)</h2>
<p>직접 입력하여 사용할 수 있는 명령어들이다.</p>
<hr>
<h3 id="핵심-워크플로우">핵심 워크플로우</h3>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/deepinit</code></td>
<td>프로젝트용 AGENTS.md 계층 구조 생성</td>
</tr>
<tr>
<td><code>/deep-interview</code></td>
<td>요구사항 명확화 인터뷰</td>
</tr>
<tr>
<td><code>/autopilot</code></td>
<td>자율 실행 모드</td>
</tr>
<tr>
<td><code>/ralph</code></td>
<td>완료 시까지 반복 실행</td>
</tr>
<tr>
<td><code>/ultrawork</code></td>
<td>최대 병렬 작업</td>
</tr>
<tr>
<td><code>/team</code></td>
<td>여러 에이전트 협업</td>
</tr>
<tr>
<td><code>/plan</code></td>
<td>작업 계획 수립</td>
</tr>
<tr>
<td><code>/ralplan</code></td>
<td>합의 기반 설계</td>
</tr>
<tr>
<td><code>/analyze</code></td>
<td>분석 및 디버깅</td>
</tr>
</tbody></table>
<hr>
<h3 id="코드-품질-관리">코드 품질 관리</h3>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/code-review</code></td>
<td>종합 코드 리뷰</td>
</tr>
<tr>
<td><code>/security-review</code></td>
<td>보안 취약점 점검</td>
</tr>
<tr>
<td><code>/build-fix</code></td>
<td>빌드 오류 수정</td>
</tr>
<tr>
<td><code>/tdd</code></td>
<td>테스트 주도 개발</td>
</tr>
<tr>
<td><code>/sciomc</code></td>
<td>데이터 분석 및 통계 작업</td>
</tr>
</tbody></table>
<hr>
<h3 id="멀티-ai-협업">멀티 AI 협업</h3>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/omc-teams</code></td>
<td>Codex, Gemini 등 외부 AI 협업</td>
</tr>
<tr>
<td><code>/ccg</code></td>
<td>Claude + Codex + Gemini 동시 활용</td>
</tr>
</tbody></table>
<hr>
<h3 id="유틸리티">유틸리티</h3>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/note</code></td>
<td>메모 저장</td>
</tr>
<tr>
<td><code>/cancel</code></td>
<td>현재 작업 취소</td>
</tr>
<tr>
<td><code>/cancel --force</code></td>
<td>모든 상태 초기화</td>
</tr>
<tr>
<td><code>/trace</code></td>
<td>에이전트 실행 로그 확인</td>
</tr>
<tr>
<td><code>/hud</code></td>
<td>HUD 상태바 설정</td>
</tr>
<tr>
<td><code>/learner</code></td>
<td>재사용 가능한 패턴 추출</td>
</tr>
<tr>
<td><code>/skill</code></td>
<td>커스텀 스킬 관리</td>
</tr>
<tr>
<td><code>/configure-notifications</code></td>
<td>알림 설정</td>
</tr>
<tr>
<td><code>/omc-setup</code></td>
<td>OMC 재설정</td>
</tr>
<tr>
<td><code>/omc-doctor</code></td>
<td>설치 진단</td>
</tr>
<tr>
<td><code>/omc-help</code></td>
<td>도움말</td>
</tr>
</tbody></table>
<hr>
<h2 id="추천-사용-흐름">추천 사용 흐름</h2>
<h3 id="기능-개발">기능 개발</h3>
<pre><code class="language-text">/deep-interview
→ /plan
→ /autopilot
→ /code-review</code></pre>
<hr>
<h3 id="버그-수정">버그 수정</h3>
<pre><code class="language-text">/analyze
→ debug
→ fix build
→ review code</code></pre>
<hr>
<h3 id="대규모-프로젝트">대규모 프로젝트</h3>
<pre><code class="language-text">/team
→ /ralplan
→ /ralph
→ /code-review</code></pre>
<hr>
<h3 id="배포-전-점검">배포 전 점검</h3>
<pre><code class="language-text">/security-review
→ review code
→ verifier</code></pre>
<hr>
<h2 id="한-줄-요약-1">한 줄 요약</h2>
<p>Claude Code를 제대로 활용하려면 단순히 &quot;코드 작성&quot;을 요청하는 것이 아니라,</p>
<ul>
<li>설계는 <code>plan</code>, <code>ralplan</code></li>
<li>구현은 <code>autopilot</code>, <code>ralph</code></li>
<li>병렬 작업은 <code>ultrawork</code></li>
<li>리뷰는 <code>review code</code></li>
<li>보안은 <code>security review</code></li>
</ul>
<p>처럼 작업 목적에 맞는 모드와 에이전트를 선택하는 것이 핵심이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OMC 멀티 에이전트의 오케스트레이션]]></title>
            <link>https://velog.io/@angel_eugnen/OMC-%EB%A9%80%ED%8B%B0-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%9D%98-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@angel_eugnen/OMC-%EB%A9%80%ED%8B%B0-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%9D%98-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</guid>
            <pubDate>Wed, 24 Jun 2026 00:35:13 GMT</pubDate>
            <description><![CDATA[<h1 id="omc-멀티-에이전트의-오케스트레이션">OMC 멀티 에이전트의 오케스트레이션</h1>
<h2 id="가성비">가성비</h2>
<p>강력한 AI 를 단순 작업에 쓰는 것은 낭비
저렴한 AI를 복잡한 AI 에 쓰는 것은 품질 저하</p>
<h2 id="omc-에이전트-소개">OMC 에이전트 소개</h2>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/d4080806-1c57-4d9f-a511-429c5430dddd/image.png" alt=""></p>
<h2 id="작업에-맞는-올바른-도구">작업에 맞는 올바른 도구</h2>
<ul>
<li>Haiku: 저렴하고 빠름, </li>
<li>Sonnet : 균형</li>
<li>Opus: 성능이 좋은대신, 느리고 비쌈</li>
</ul>
<p>Haiku * 60 = Opus
비용 차이는 60배 이다.</p>
<p>OMC에서 단순 반복 작업은 Haiku, 깊은 사고가 필요하면 Opus를 투입하는 것이다</p>
<h2 id="작업은-어떻게-할당되는가">작업은 어떻게 할당되는가</h2>
<p>그것을 하는것이 &quot;멀티 에이전트 오케스트레이션&quot;이다.</p>
<p>PM처럼 딱 맞는 담당자에게 일을 맡겨주는 것을 의미한다.
<img src="https://velog.velcdn.com/images/angel_eugnen/post/c6558398-f7e9-443f-86ac-17ad03b2ca0a/image.png" alt=""></p>
<h2 id="실제-작동하는-ai-팀">실제 작동하는 AI 팀</h2>
<p>예를 들어 결제 시스템 환불 기능 추가에선</p>
<ol>
<li>planner가 요청 분석</li>
<li>explore 가 코드를 찾고 architect가 통합 전략 구현</li>
<li>excutor 가 환불, api 데이터 모델 등 새 코드를 작성</li>
<li>build-fixer 가 오류를 해결하고 code-reviewer가 품질 검사</li>
<li>writer가 새 기능을 반영하여 API 문서 업데이트</li>
</ol>
<h2 id="ai팀-리딩하기">AI팀 리딩하기</h2>
<ol>
<li>명확한 개요 작성: 모호한 요청 대신 구체적으로 지시하라</li>
<li>적절한 톤 사용: 복잡한 문제에는 &quot;신중하게 검토해줘&quot; 라는 힌트를 추가</li>
<li>구축 전 계획: 대규모 작업은 planner를 먼저 사용하여 낭비 줄이기</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[OMC 슬래시 완벽 가이드]]></title>
            <link>https://velog.io/@angel_eugnen/OMC-%EC%8A%AC%EB%9E%98%EC%8B%9C-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@angel_eugnen/OMC-%EC%8A%AC%EB%9E%98%EC%8B%9C-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Wed, 24 Jun 2026 00:24:09 GMT</pubDate>
            <description><![CDATA[<h2 id="슬래시스킬">슬래시스킬(/)</h2>
<p>테스트 주도 개발(TDD) 같은 특정 워크 플로우전체를 직접 호출하여 실행하는 강력한 명령어</p>
<h2 id="autopilot">/autopilot</h2>
<p>: 뼈대완성
아무것도 없는 빈화면에서 실행 화면으로 단숨에 끌어올릴 수 있다.
PM이 일하는 방법과 같다.
우리의 목표를 이해하려고 하는 질문들을 한 후에 코드를 한다. 이후 스스로 수정한다. 이후 완벽히 돌아가는지 확인한다.</p>
<h2 id="code-review">/code-review</h2>
<p>: 가상팀. 단순한 문법 오류를 잡아주는 팀이 아니다. 시니어 개발자가 코드 품질, 가독성, 잠재적 버그를 검토해주며 피드백을 준다.
팀 구성</p>
<ol>
<li>코드리뷰어</li>
<li>보안 전문가</li>
<li>오류검출 테스터</li>
</ol>
<h2 id="security-review">/security-review</h2>
<p>: 품질, 보안검사해주는 프로젝트 경호원</p>
<ol>
<li>보안의 허점을 미리 잡아준다.</li>
</ol>
<h2 id="tdd">/tdd</h2>
<p>: 테트스 주도 개발, 테스트 만듦 -&gt; 테스트 통과 코드 -&gt; 코드 다듬기 이걸 ai가 대신 해주어 튼튼한 코드 작성 가능</p>
<h2 id="온디맨드-ai-팀">온디맨드 AI 팀</h2>
<p>: 디지털 군단으로 확장하기
작업이 너무 커서 ai 하나로 안되면 어떡하지?
=&gt; /swarm</p>
<h2 id="swarm">/swarm</h2>
<p>: 프로젝트 매니저가 에이전트에게 분할하여 독립적으로 처리하고 협력하게 함으로써 큰 프로젝트를 더 빨리 완료</p>
<h2 id="순서">순서</h2>
<ol>
<li>/autopilot 으로 큰 그림을 그린다</li>
<li>/code-review 로 멘토의 조언을 듣고</li>
<li>/tdd 로 코드를 보장하고</li>
<li>문제가 생기면 /swarm으로 해결한다</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[OMC 매직키워드]]></title>
            <link>https://velog.io/@angel_eugnen/OMC-%EB%A7%A4%EC%A7%81%ED%82%A4%EC%9B%8C%EB%93%9C</link>
            <guid>https://velog.io/@angel_eugnen/OMC-%EB%A7%A4%EC%A7%81%ED%82%A4%EC%9B%8C%EB%93%9C</guid>
            <pubDate>Tue, 23 Jun 2026 05:57:01 GMT</pubDate>
            <description><![CDATA[<h1 id="omc-매직-키워드">OMC 매직 키워드</h1>
<h2 id="ralph">ralph</h2>
<p>: 완료 될때까지 자동 반복 실행
    - 분석 &gt; 수정 &gt; 테스트 &gt; 반복</p>
<blockquote>
<p>ralph: 회원가입/로그인/프로필 수정 기능 전부 만들어줘</p>
</blockquote>
<h2 id="uwl">uwl</h2>
<p>: 멀티태스킹 용도. 최대 병렬 처리로 속도 향상, ex) api 를 한번에 5개를 리팩토링 해야 할때</p>
<h2 id="plan">plan</h2>
<p>: 코딩 전 기획 인터뷰 모드
    - 애초에 실수 막고 계획하도록 함</p>
<h2 id="ralpan">ralpan</h2>
<p>: 멀티에이전트와 반복 기획
    - 기획 아이디어가 계속 제련됨</p>
<h2 id="정리">정리</h2>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/d42bb6fb-ba57-447d-b747-97326e221a41/image.png" alt=""></p>
<h2 id="조합">조합</h2>
<p>조합을 하면 더 잘 쓸 수 있다.
최강조합 uwl + ralph 각각의 테스트들을 통과할때까지 계속 반복된다.</p>
<h2 id="실전팁">실전팁</h2>
<ol>
<li>키워드를 너무 남발하지 말아라.</li>
<li>애매하면 plan으로 시작하라</li>
<li>ulw 을 쓸땐 서로 의존성 없는지 확인하고 작업하라</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[OMC 시작하기]]></title>
            <link>https://velog.io/@angel_eugnen/OMC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@angel_eugnen/OMC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 23 Jun 2026 03:58:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/d888e0bb-dec3-4f22-8a32-8a4b0a02a234/image.png" alt=""></p>
<h1 id="omc-이용해서-개발해보기-전-왜-써야-하는가">OMC 이용해서 개발해보기 전 왜 써야 하는가?</h1>
<h2 id="claude-code의-한계">Claude Code의 한계</h2>
<ol>
<li><p>단일 agent라는 점
: 설계, 테스트, 리뷰까지 혼자하는건 벅차다</p>
</li>
<li><p>콘텍스트 손실</p>
</li>
<li><p>작업 실패시 반복적으로 다시 시도하게 명령하는 것</p>
</li>
</ol>
<h2 id="omc가-해결한-것">OMC가 해결한 것</h2>
<ol>
<li><p>여러 전문 agent가 함께 일하는 multi agent Orchestration 을 적용</p>
</li>
<li><p>영구적인 메모리로 해결로 대화의 길을 잃을 일이 없다</p>
</li>
<li><p>자동 반복 실행 기능으로, 알아서 여러번 재시도</p>
</li>
</ol>
<h2 id="omc-3계층-구조">OMC 3계층 구조</h2>
<ol>
<li><p>매직 키워드
: ralph, plan 같은 키워드로 특별 실행모드 활성화(계획을 짬)</p>
</li>
<li><p>슬래시 스킬
: /oh-my-claudecode 처럼 직접 호출하는 전문 기능</p>
</li>
<li><p>플러그인 에이전트
: 굳이 시키지 않아도, 전문 ai(리서치 전문가, 아키텍처 설계 전문가)를 데려와서 작업한다</p>
</li>
</ol>
<h2 id="설치방법">설치방법</h2>
<p>Claude Code에서 다음 명령어를 실행합니다:</p>
<pre><code>/plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode
/plugin install oh-my-claudecode</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Remix 걸음마]]></title>
            <link>https://velog.io/@angel_eugnen/Remix-%EA%B1%B8%EC%9D%8C%EB%A7%88</link>
            <guid>https://velog.io/@angel_eugnen/Remix-%EA%B1%B8%EC%9D%8C%EB%A7%88</guid>
            <pubDate>Mon, 15 Dec 2025 07:36:06 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/9b518cad-8ef6-44c7-bb36-48c66213aadb/image.png" alt=""></p>
<p>GPT생겨서 공부하기 참 쉬워졌쥬..</p>
<h2 id="1️⃣-공식문서-초기-세팅-이건-꼭-해라">1️⃣ 공식문서 초기 세팅 (이건 꼭 해라)</h2>
<p>✔️ 프로젝트 생성
<code>npx create-react-router@latest</code></p>
<p>Remix v2는 이제 React Router에 흡수됐어.
👉 Remix = React Router + 서버 기능 이라고 생각해.</p>
<h2 id="2️⃣-생성-후-구조부터-이해-중요-⭐⭐⭐">2️⃣ 생성 후 구조부터 이해 (중요 ⭐⭐⭐)</h2>
<p>처음 보면 이게 제일 멘붕이야.</p>
<pre><code>app/
 ├─ routes/
 │   ├─ _index.tsx
 │   ├─ login.tsx
 │   └─ posts.$id.tsx
 ├─ root.tsx
 └─ entry.server.tsx</code></pre><p>핵심만 딱 정리해줄게</p>
<ul>
<li>routes/ = 페이지</li>
<li>파일 이름 = URL</li>
</ul>
<p>이거 하나만 기억해도 반은 이해함</p>
<ul>
<li>routes/login.tsx   →  /login</li>
<li>routes/posts.$id.tsx → /posts/123</li>
</ul>
<p>👉 React Router의 <Route /> 안 써도 됨
👉 파일이 라우터다</p>
<h2 id="3️⃣-remix에서-제일-중요한-개념-top-4">3️⃣ Remix에서 제일 중요한 개념 TOP 4</h2>
<h3 id="🧠-①-loader-서버에서-데이터-가져오기">🧠 ① loader (서버에서 데이터 가져오기)</h3>
<pre><code class="language-js">export const loader = async () =&gt; {
  return { message: &#39;hello&#39; };
};</code></pre>
<p>✔️ 이 함수는 브라우저에서 실행 안 됨
✔️ 서버에서만 실행</p>
<pre><code class="language-js">const data = useLoaderData&lt;typeof loader&gt;();</code></pre>
<p>👉 API 호출 + 상태관리 + 로딩을 한 번에 해결</p>
<h3 id="🧠-②-action-폼-submit-처리">🧠 ② action (폼 submit 처리)</h3>
<pre><code class="language-js">export const action = async ({ request }) =&gt; {
  const formData = await request.formData();
  return null;
};</code></pre>
<p>✔️ POST / PUT / DELETE 전용
✔️ 서버에서 실행됨</p>
<p>👉 axios.post() + onSubmit 안 써도 됨</p>
<h3 id="🧠-③-form-remix-전용">🧠 ③ Form (Remix 전용)</h3>
<pre><code class="language-js">&lt;Form method=&quot;post&quot;&gt;
  &lt;input name=&quot;email&quot; /&gt;
  &lt;button type=&quot;submit&quot; /&gt;
&lt;/Form&gt;</code></pre>
<p>👉 submit 하면 자동으로 action() 실행
👉 새로고침 ❌
👉 상태관리 ❌</p>
<h3 id="🧠-④-usenavigation-로딩-상태">🧠 ④ useNavigation (로딩 상태)</h3>
<pre><code class="language-js">const navigation = useNavigation();

navigation.state === &#39;loading&#39;
navigation.state === &#39;submitting&#39;</code></pre>
<p>👉 버튼 로딩 처리, 스피너 여기서 함</p>
<h3 id="4️⃣-아-그래서-뭐가-좋은데-리액트랑-비교">4️⃣ “아 그래서 뭐가 좋은데?” (리액트랑 비교)</h3>
<table>
<thead>
<tr>
<th>리액트</th>
<th>Remix</th>
</tr>
</thead>
<tbody><tr>
<td>useEffect로 API 호출</td>
<td>loader에서 서버 호출</td>
</tr>
<tr>
<td>axios + 상태관리</td>
<td>기본 제공</td>
</tr>
<tr>
<td>폼 submit 직접 처리</td>
<td>action 자동</td>
</tr>
<tr>
<td>로딩 상태 직접 관리</td>
<td>navigation</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS]]></title>
            <link>https://velog.io/@angel_eugnen/NestJS</link>
            <guid>https://velog.io/@angel_eugnen/NestJS</guid>
            <pubDate>Fri, 12 Dec 2025 15:54:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/916c1116-6fe6-44d2-89f3-f2083d53ad91/image.png" alt=""></p>
<h2 id="시작-전-환경-세팅">시작 전 환경 세팅</h2>
<h3 id="1️⃣-필수-도구">1️⃣ 필수 도구</h3>
<p>Insomnia
→ API 테스트용 (Postman 대체)</p>
<h3 id="2️⃣-nestjs-cli-설치-및-프로젝트-생성">2️⃣ NestJS CLI 설치 및 프로젝트 생성</h3>
<p><code>npm i -g @nestjs/cli
nest new project-name</code></p>
<p>nest new 명령어를 사용하면
기본적인 폴더 구조와 설정이 자동으로 구성된다.</p>
<hr>
<h2 id="single-responsibility-principle-srp">Single Responsibility Principle (SRP)</h2>
<p>NestJS는 단일 책임 원칙(SRP) 을 굉장히 강하게 따른다.</p>
<p>하나의 module / class / function 은 하나의 책임만 가져야 한다</p>
<h3 id="기본-구조-예시">기본 구조 예시</h3>
<blockquote>
</blockquote>
<p>AppModule
 ├── AppController
 └── AppService</p>
<h3 id="역할-분리">역할 분리</h3>
<ol>
<li>Controller</li>
</ol>
<ul>
<li>HTTP 요청/응답 처리</li>
<li>URL, Method 정의</li>
</ul>
<ol start="2">
<li>Service</li>
</ol>
<ul>
<li>실제 비즈니스 로직 담당</li>
</ul>
<ol start="3">
<li>Module</li>
</ol>
<ul>
<li>관련 Controller와 Provider를 묶는 단위</li>
</ul>
<p>👉
Controller에서 로직을 처리하지 않고
Service로 위임하는 구조가 기본이다.</p>
<hr>
<h2 id="validationpipe-요청-데이터-검증">ValidationPipe (요청 데이터 검증)</h2>
<p>NestJS는 class 기반 유효성 검사를 공식적으로 지원한다.</p>
<p>1️⃣ 패키지 설치
<code>npm i class-validator class-transformer</code></p>
<p><code>class-validator</code> : 유효성 검사</p>
<p><code>class-transformer</code> : plain object → class 변환</p>
<p>2️⃣ DTO를 위한 패키지
<code>npm i @nestjs/mapped-types</code></p>
<p>DTO 상속 및 변환을 도와주는 유틸 패키지</p>
<p>3️⃣ DTO 예시</p>
<pre><code class="language-js">export class CreateMovieDto {
  @IsString()
  title: string;

  @IsNumber()
  year: number;
}</code></pre>
<p>👉
ValidationPipe를 사용하면</p>
<p>컨트롤러 진입 전에 자동으로 유효성 검증이 이루어진다.</p>
<hr>
<h2 id="dependency-injection-di">Dependency Injection (DI)</h2>
<p>NestJS의 가장 핵심 개념 중 하나.</p>
<h3 id="nestjs의-di-기본-구조--핵심-3요소">NestJS의 DI 기본 구조 – 핵심 3요소</h3>
<p>역할 - 의미
Provider    - 주입 가능한 클래스
Consumer    - 주입받는 쪽
Module    - Provider를 등록하는 장소</p>
<p>예시</p>
<pre><code class="language-js">@Injectable()
export class MoviesService {}

@Module({
  providers: [MoviesService],
})
export class MoviesModule {}

@Controller(&#39;movies&#39;)
export class MoviesController {
  constructor(private readonly moviesService: MoviesService) {}
}</code></pre>
<p>👉
객체를 직접 생성하지 않고
NestJS가 DI 컨테이너를 통해 자동으로 주입해준다.</p>
<hr>
<h2 id="fastify-vs-express">Fastify vs Express</h2>
<p>NestJS는 기본적으로 Express 위에서 동작한다</p>
<p>하지만 Fastify를 사용하면
Express 대비 약 2배 빠른 성능을 낼 수 있다.</p>
<p>Req / Res 직접 접근
getAll(@Req() req, @Res() res): GetMovieDto[] {
  return this.movies;
}</p>
<p>NestJS에서는 @Req(), @Res() 데코레이터를 통해
플랫폼(Express / Fastify)의 객체에 직접 접근할 수 있다.</p>
<h3 id="❗-하지만-권장되지-않는-이유">❗ 하지만 권장되지 않는 이유</h3>
<p>NestJS의 추상화 레이어를 깨게 됨</p>
<p>플랫폼 종속 코드가 됨</p>
<p>테스트 및 유지보수 어려움 증가</p>
<p>👉
가능하면 NestJS 방식(@Body, @Param, @Query 등)을 사용하는 것이 좋다.</p>
<hr>
<h3 id="정리하며-느낀-점">정리하며 느낀 점</h3>
<p>NestJS는 구조와 규칙을 강하게 강제하는 프레임워크</p>
<p>처음엔 번거롭지만 규모가 커질수록 장점이 커진다</p>
<p>특히 DI + Module 구조는 테스트, 유지보수, 협업에 굉장히 유리하다</p>
<p>React 개발자 입장에서는
“왜 이렇게 나눠?” 싶지만
커질수록 이 구조가 왜 필요한지 체감하게 되겠지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TinyMCE webpack 최적화 구현 계획 (하이브리드 방식)]]></title>
            <link>https://velog.io/@angel_eugnen/TinyMCE-webpack-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B5%AC%ED%98%84-%EA%B3%84%ED%9A%8D-%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@angel_eugnen/TinyMCE-webpack-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B5%AC%ED%98%84-%EA%B3%84%ED%9A%8D-%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Tue, 09 Dec 2025 02:29:10 GMT</pubDate>
            <description><![CDATA[<p>기존 코드에서 <code>tinymce/tinymce-react</code> 를 <code>CDN</code>방식으로 이용하고 있었다.</p>
<p>이유는, </p>
<blockquote>
<ul>
<li>프로젝트에 파일을 포함하지 않아도 됨</li>
</ul>
</blockquote>
<ul>
<li>캐싱 활용 가능 (다른 사이트에서도 같은 CDN 사용 시)</li>
<li>초기 설정이 간단
... 가장 큰 이유는 회사에서 빨리 HTML &lt;-&gt; 에디터 읽기쓰기 호환 기능을 가진 에디터를 도입해달라고 했기 때문.</li>
</ul>
<p>하지만 React에서 CDN으로 불러오면 아래와 같은 단점이 있어서 npm방식으로 바꾸려 한다.</p>
<blockquote>
<ul>
<li>CDN 서버 장애 시 사용 불가</li>
</ul>
</blockquote>
<ul>
<li>버전 관리가 명확하지 않음 (URL에 버전이 있어도 관리가 어려움)</li>
<li>보안 이슈 가능성 (외부 스크립트 실행)</li>
<li>번들러 최적화 불가</li>
</ul>
<p>아지만 이렇게 하다보니
예전에 써서 문제가 없던 Quill에디터는 
모노리식 번들(단일번들) 이라 css만 import하면 동작하고, 모든것을 webpack이 처리해서 문제가 없었다고 한다. 
TinyMCE 라는 라이브러리는 모듈러 아키텍처라 필요한 리소스를 런타임에 로드하기 때문에 React 에서 사용시 문제가 생겼다. 이 라이브러리의 유연성과 확장성 때문에 리소스 경로설정 이라는 것이 필요했다.</p>
<p>추가로 번들 크기가 증가할것이다 라는 문제점이 또 보였고..</p>
<p>결국 CRACO/webpack 설정을 하기로 했다.</p>
<hr>
<h1 id="tinymce-webpack-최적화-구현-계획-하이브리드-방식-개요">TinyMCE webpack 최적화 구현 계획 (하이브리드 방식) 개요</h1>
<h2 id="목표">목표</h2>
<ul>
<li>TinyMCE를 npm 패키지로 사용 (CDN 제거)</li>
<li>공통 webpack 설정 + 앱별 오버라이드 구조</li>
<li>webpack으로 리소스 최적화 및 번들 크기 감소</li>
<li>seller-admin에 먼저 적용 (테스트 후 system-admin 확장)
구현 단계</li>
</ul>
<h3 id="1-craco-설치-및-공통-설정">1. CRACO 설치 및 공통 설정</h3>
<ul>
<li>apps/seller-admin/package.json에 @craco/craco 의존성 추가</li>
<li>루트에 공통 설정 생성: craco.base.js</li>
<li>TinyMCE 리소스 복사 설정 (CopyWebpackPlugin)</li>
<li>공통 번들 최적화 설정 (압축, 트리쉐이킹, 코드 스플리팅)</li>
<li>apps/seller-admin/craco.config.js 생성</li>
<li>공통 설정 import 및 seller-admin 특화 오버라이드 (필요시)</li>
<li>apps/seller-admin/package.json의 scripts를 react-scripts → craco로 변경<h3 id="2-nodejs-버전-고정">2. Node.js 버전 고정</h3>
</li>
<li>루트에 .nvmrc 파일 생성 (Node.js 24.6.0)</li>
<li>Jenkins 배포 시 버전 일관성 보장<h3 id="3-tinymce-컴포넌트-수정">3. TinyMCE 컴포넌트 수정</h3>
</li>
<li>packages/ui-components/src/organisms/TinyMCEEditor.tsx 수정</li>
<li>base_url 설정 제거 (webpack이 처리)</li>
<li>tinymceScriptSrc prop 제거 (npm 패키지 사용)</li>
<li>필요한 플러그인만 import하여 트리쉐이킹 최적화<h3 id="4-빌드-스크립트-검증">4. 빌드 스크립트 검증</h3>
</li>
<li>pnpm install 실행</li>
<li>pnpm --filter seller-admin start로 개발 서버 테스트</li>
<li>pnpm --filter seller-admin build로 프로덕션 빌드 테스트</li>
</ul>
<h2 id="주요-파일-변경">주요 파일 변경</h2>
<h3 id="1-appsseller-adminpackagejson">1. apps/seller-admin/package.json</h3>
<ul>
<li>@craco/craco 의존성 추가</li>
<li>scripts 수정: react-scripts → craco<h3 id="2-cracobasejs-신규-루트">2. craco.base.js (신규, 루트)</h3>
</li>
<li>공통 webpack 설정</li>
<li>TinyMCE 리소스 복사 (CopyWebpackPlugin)</li>
<li>공통 번들 최적화 설정<h3 id="3-appsseller-admincracoconfigjs-신규">3. apps/seller-admin/craco.config.js (신규)</h3>
</li>
<li>공통 설정 import</li>
<li>seller-admin 특화 설정 오버라이드 (필요시)<h3 id="4-nvmrc-신규">4. .nvmrc (신규)</h3>
</li>
<li>Node.js 24.6.0 고정<h3 id="5-packagesui-componentssrcorganismstinymceeditortsx">5. packages/ui-components/src/organisms/TinyMCEEditor.tsx</h3>
</li>
<li>base_url 제거</li>
<li>tinymceScriptSrc 제거</li>
</ul>
<h2 id="아키텍처">아키텍처</h2>
<pre><code>루트/
├── craco.base.js          # 공통 webpack 설정
├── .nvmrc                  # Node.js 버전 고정
└── apps/
    └── seller-admin/
        └── craco.config.js # 공통 설정 import + 오버라이드</code></pre><h2 id="예상-효과">예상 효과</h2>
<ul>
<li>번들 크기: 약 20-30% 감소 (트리쉐이킹 및 최적화)</li>
<li>로딩 속도: 개선 (로컬 파일, 번들 최적화)</li>
<li>안정성: 향상 (외부 CDN 의존성 제거)</li>
<li>유지보수성: 향상 (공통 설정 중앙 관리)<h2 id="주의사항">주의사항</h2>
Jenkins 배포 시 Node.js 24.6.0 이상 필요
빌드 메모리 사용량 증가 가능 (webpack 최적화)
seller-admin 테스트 완료 후 system-admin에도 동일 적용</li>
</ul>
<hr>
<h2 id="코드">코드</h2>
<h3 id="1-appssystem-adminpackagejson-수정">1. apps/system-admin/package.json 수정</h3>
<p>script 에서
&#39;start&#39;:&#39;react-script start&#39; 와 같은 것들을 아래처럼 바꾸고</p>
<ul>
<li>&quot;prestart&quot;: &quot;node ../../scripts/copy-tinymce.js seller-admin&quot;,</li>
<li>&quot;start&quot;: &quot;craco start&quot;,</li>
<li>&quot;build&quot;: &quot;craco build&quot;,</li>
<li>&quot;test&quot;: &quot;craco test&quot;,</li>
</ul>
<p>devDependencies에서 아래와 같은 것들을 설치</p>
<ul>
<li>&quot;@craco/craco&quot;: &quot;^7.1.0&quot;,</li>
<li>&quot;copy-webpack-plugin&quot;: &quot;^12.0.2&quot;,</li>
</ul>
<h3 id="2-appssystem-admincracoconfigjs-생성">2. apps/system-admin/craco.config.js 생성</h3>
<pre><code class="language-js">const baseConfig = require(&#39;../../craco.base.js&#39;);

module.exports = {
  ...baseConfig,
  // system-admin 특화 설정이 필요한 경우 여기에 추가
  // 예: 특정 플러그인, 로더 설정 등
};
</code></pre>
<h3 id="3-루트-스크립트에-copy-tinymcejs-생성">3. 루트 스크립트에 copy-tinymce.js 생성</h3>
<pre><code class="language-js">const fs = require(&#39;fs&#39;);
const path = require(&#39;path&#39;);

// 루트 디렉토리 찾기 (craco.base.js와 동일한 기준)
const rootDir = path.resolve(__dirname, &#39;../../..&#39;);

// 여러 가능한 경로 시도
const possibleSources = [
  // 1. 루트의 node_modules (craco.base.js와 동일한 방식)
  path.resolve(rootDir, &#39;node_modules/tinymce&#39;),
  // 2. pnpm의 .pnpm 디렉토리에서 찾기
  (() =&gt; {
    try {
      const pnpmStore = path.resolve(rootDir, &#39;node_modules/.pnpm&#39;);
      if (fs.existsSync(pnpmStore)) {
        // tinymce@로 시작하는 디렉토리 찾기
        const entries = fs.readdirSync(pnpmStore);
        for (const entry of entries) {
          if (entry.startsWith(&#39;tinymce@&#39;)) {
            const entryPath = path.join(pnpmStore, entry);
            if (fs.statSync(entryPath).isDirectory()) {
              const tinymcePath = path.join(entryPath, &#39;node_modules/tinymce&#39;);
              if (fs.existsSync(tinymcePath)) {
                return tinymcePath;
              }
            }
          }
        }
      }
      return null;
    } catch (e) {
      return null;
    }
  })(),
  // 3. ui-components의 node_modules
  path.resolve(rootDir, &#39;packages/ui-components/node_modules/tinymce&#39;),
  // 4. system-admin의 node_modules
  path.resolve(__dirname, &#39;../node_modules/tinymce&#39;),
];

// 존재하는 소스 경로 찾기
let source = null;
for (const possibleSource of possibleSources) {
  if (possibleSource &amp;&amp; fs.existsSync(possibleSource)) {
    source = possibleSource;
    break;
  }
}

if (!source) {
  console.error(&#39;❌ TinyMCE not found in node_modules&#39;);
  console.error(&#39;   Tried paths:&#39;);
  possibleSources.forEach((p, i) =&gt; {
    if (p) {
      const exists = fs.existsSync(p) ? &#39;✓&#39; : &#39;✗&#39;;
      console.error(`   ${i + 1}. ${exists} ${p}`);
    } else {
      console.error(`   ${i + 1}. (skipped)`);
    }
  });
  console.error(&#39;\n   Please ensure TinyMCE is installed:&#39;);
  console.error(&#39;   pnpm install&#39;);
  console.error(&#39;\n   Or check if TinyMCE is in packages/ui-components:&#39;);
  console.error(&#39;   ls packages/ui-components/node_modules/tinymce&#39;);
  process.exit(1);
}

const dest = path.resolve(__dirname, &#39;../public/tinymce&#39;);
const checkFile = path.resolve(dest, &#39;plugins/help/js/i18n/keynav/en.js&#39;);

// 파일이 없을 때만 복사 (빠름)
if (!fs.existsSync(checkFile)) {
  console.log(&#39;📦 Copying TinyMCE files (first time only)...&#39;);
  console.log(`   From: ${source}`);
  console.log(`   To: ${dest}`);

  const { execSync } = require(&#39;child_process&#39;);

  if (fs.existsSync(dest)) {
    fs.rmSync(dest, { recursive: true, force: true });
  }

  const isWindows = process.platform === &#39;win32&#39;;
  if (isWindows) {
    execSync(`xcopy /E /I /Y &quot;${source}&quot; &quot;${dest}&quot;`, { stdio: &#39;inherit&#39; });
  } else {
    execSync(`cp -R &quot;${source}&quot; &quot;${dest}&quot;`, { stdio: &#39;inherit&#39; });
  }
  console.log(&#39;✅ Done&#39;);
} else {
  console.log(&#39;⚡ TinyMCE files already exist&#39;);
}
</code></pre>
<h2 id="파일별-용도-설명">파일별 용도 설명</h2>
<h3 id="1-cracobasejs---webpack-설정-파일">1. craco.base.js - Webpack 설정 파일</h3>
<ul>
<li>용도: CRACO를 통한 webpack 설정</li>
<li>역할:
개발/프로덕션 모드에서 webpack이 실행될 때 동작
CopyWebpackPlugin으로 TinyMCE 리소스를 빌드 출력에 복사
프로덕션 빌드 최적화 (코드 스플리팅, 트리쉐이킹, 압축)</li>
<li>작동 시점:</li>
<li>craco start 실행 시 (개발 모드)</li>
<li>craco build 실행 시 (프로덕션 빌드)</li>
<li>🔴 문제점:
개발 모드에서 CopyWebpackPlugin이 초기 복사를 보장하지 않을 수 있음
개발 서버 시작 시 파일이 없으면 에러 발생 가능!!!<h3 id="2-scriptscopy-tinymcejs---파일-복사-스크립트">2. scripts/copy-tinymce.js - 파일 복사 스크립트</h3>
</li>
<li>용도: 개발 서버/빌드 시작 전에 TinyMCE 파일을 수동 복사</li>
<li>역할:
prestart, prebuild 훅에서 실행
TinyMCE 파일이 없을 때만 복사 (조건부)
개발 모드에서 리소스 누락 방지</li>
<li>작동 시점:
pnpm start 실행 전 (prestart 훅)
pnpm build 실행 전 (prebuild 훅)</li>
<li>장점:
개발 서버 시작 전에 파일 존재 보장
파일이 있으면 스킵하여 빠른 시작
에러 메시지로 문제 진단 용이</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Deployment-Vercel]]></title>
            <link>https://velog.io/@angel_eugnen/Deployment-Vercel</link>
            <guid>https://velog.io/@angel_eugnen/Deployment-Vercel</guid>
            <pubDate>Sun, 13 Jul 2025 08:42:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/e1c0ff5f-ccf1-43ea-8656-87013a00da10/image.png" alt=""></p>
<h2 id="deployment">Deployment</h2>
<p>git에 push 를 마쳤다면,
Vercel이 호출할 수 있도록 package.json 에 명령어를 수정한다</p>
<pre><code class="language-bash">  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;next dev&quot;,
    &quot;build&quot;: &quot;next build&quot;,
    &quot;start&quot;:&quot;next start&quot;
  },</code></pre>
<p>vercel 은 그냥 deploy 버튼만 누르고 기다리면 된다.
그리고 나타나는 오류들을 해결하면 dashboard에 보인다.</p>
<hr>
<p>추가)
<code>&lt;Link prefetch href={</code>/movies/${id}<code>}&gt;{title}&lt;/Link&gt;</code></p>
<p>배포 시에 prefetch 라는 props 통해  통해 사용자가 클릭하기 전부터 fetch를 진행한다.
스크롤을 내리고 아래쪽에 있는 링크가 보여지면, NextJS는 자동적으로 요청을 보내게 된다.
movie페이지를 자동으로 fetch 하고 있는 것이다. 클릭하지 않았지만 마치 페이지에 들어간 것처럼 요청하는 것이다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS Modules]]></title>
            <link>https://velog.io/@angel_eugnen/CSS-Modules</link>
            <guid>https://velog.io/@angel_eugnen/CSS-Modules</guid>
            <pubDate>Sun, 13 Jul 2025 08:06:41 GMT</pubDate>
            <description><![CDATA[<h2 id="css-module">Css Module</h2>
<ol>
<li><p>application 전체에 적용할 global style 을 만든다.
전체 웹사디트의 bg color 나 폰트를 적용하도록 한다. </p>
</li>
<li><p>app/layout 에서 import global css를 한다.</p>
</li>
<li><p>공통 디자인을 적용해준다.</p>
<pre><code class="language-css">html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: &quot;&quot;;
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
</code></pre>
</li>
</ol>
<p>body {
  font-family: Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;
  background-color: black;
  color: white;
  font-size: 18px;
}</p>
<p>a {
  color: inhefit;</p>
<p>  text-decoration: none;
}</p>
<p>a:hover {
  text-decoration: underline;
}</p>
<pre><code>
4. 특정 디자인에 적용하고 싶다면 이름에 `.module.css`를 넣도록 한다.
![](https://velog.velcdn.com/images/angel_eugnen/post/0bbb9e61-c53d-4b38-860e-eb0e403d3359/image.png)

폴더 위치는 /styles 폴더에 있어도 무관하다.

그리고 오로지 className만 작성할 것이다.

5. 그리고 사용하고 싶은 tsx 파일에서 css 파일을 마치 자바스크립트 처럼 import 해온다.
`import styles from &#39;../style/navigation.module.css&#39;`
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Error Handling]]></title>
            <link>https://velog.io/@angel_eugnen/Error-Handling</link>
            <guid>https://velog.io/@angel_eugnen/Error-Handling</guid>
            <pubDate>Sun, 13 Jul 2025 06:52:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/e66901f8-92eb-4358-aa21-0e93848deae1/image.png" alt=""></p>
<h2 id="error-handling">Error Handling</h2>
<ol>
<li>API 요청이 안되거나</li>
<li>네트워크가 끊기는 등
에러가 생긴 경우의 핸들링은
error.tsx 파일을 만들면 된다.</li>
</ol>
<p>프레임워크 특성상 파일 위치는 페이지 대상이 되는 폴더 안에 넣어야 한다.</p>
<pre><code class="language-js">&quot;use client&quot;;
export default function ErrorOMG() {
  return &lt;h1&gt;Error OMG&lt;/h1&gt;;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Parallel Request]]></title>
            <link>https://velog.io/@angel_eugnen/Parallel-Request</link>
            <guid>https://velog.io/@angel_eugnen/Parallel-Request</guid>
            <pubDate>Sun, 13 Jul 2025 06:37:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/c6c583dd-0fe0-4df7-ba78-c49f6c0169a3/image.png" alt=""></p>
<h2 id="parallel-request">Parallel Request</h2>
<pre><code class="language-js">import { API_URL } from &quot;../../../(home)/page&quot;;
async function getMovie(id: string) {
  console.log(`getMovie: ${Date.now()}`);
  await new Promise((resolve) =&gt; setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}`);
  const json = await response.json();
  return json;
}
async function getVideo(id: string) {
  console.log(`getVideo: ${Date.now()}`);
  await new Promise((resolve) =&gt; setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}/videos`);
  const json = await response.json();
  return json;
}
export default async function Text({ params }) {
  const { id } = await params;
  console.log(&quot;startfetching&quot;);
  const movie = await getMovie(id); // 이 함수 응답이 오래걸리면
  const video = await getVideo(id); // 첫줄 함수를 기다리다가 응답이 늦는다. 병렬로 실행해야 한다.
  console.log(&quot;endfetching&quot;);

  return (
    &lt;div&gt;
      testestest
      &lt;h1&gt;{movie.title}&lt;/h1&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>이렇게 하면 
start fetching 이 출력되고 end fetching이 순차적으로 출력되며
총 10초 넘게 걸리게 된다. </p>
<pre><code class="language-bash">startfetching
getMovie: 1752386606417
getVideo: 1752386607056
endfetching</code></pre>
<p>병렬 요청으로 바꿔줘야 한다.</p>
<blockquote>
<p><code>promise.all</code>을 사용하면 결과값이 동시에 나올 것이다.</p>
</blockquote>
<pre><code class="language-js">const [movie, videos]=  await Promise.all([getMovie(id), getVideos(id)])</code></pre>
<p>이렇게 요청을 보내면</p>
<pre><code class="language-bash">startfetching
getMovie: 1752386815316
getVideo: 1752386815316 
endfetching</code></pre>
<p>동시에 시작하게 된다.
순차적인 작업이 아니기 때문이다. 이것이 병렬 fetch 이다.</p>
<p>하지만 지금은 둘이 끝나야 보이므로, 둘을 분리하는 것이 좋다.
둘중 어느 한쪽도 기다릴 필요가 없게! 그것이 suspense이다.</p>
<h2 id="suspense">Suspense</h2>
<p>getMovie와 getVideos 만 렌더링 하는 컴포넌트를 만든다.
/components/movie-video.tsx 
/components/movie-info.tsx </p>
<pre><code class="language-js">// 1. components.movie-video.tsx
import { API_URL } from &quot;../app/(home)/page&quot;;
async function getVideos(id: string) {
  console.log(`getVideo: ${Date.now()}`);
  await new Promise((resolve) =&gt; setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}/videos`);
  const json = await response.json();
  return json;
}

export default async function MovieVideos({ id }: { id: string }) {
  const videos = await getVideos(id);
  return &lt;h6&gt;{JSON.stringify(videos)}&lt;/h6&gt;;
}

//2. components/movie-video.tsx
import { API_URL } from &quot;../app/(home)/page&quot;;
async function getMovie(id: string) {
  console.log(`getMovie: ${Date.now()}`);
  await new Promise((resolve) =&gt; setTimeout(resolve, 3000));
  const response = await fetch(`${API_URL}/${id}`);
  const json = await response.json();
  return json;
}

export default async function MovieInfo({ id }: { id: string }) {
  const movie = await getMovie(id);
  return &lt;h6&gt;{JSON.stringify(movie)}&lt;/h6&gt;;
}
</code></pre>
<p>를 만들고 개별적으로 기다리게 한다.
그리고 page.tsx에서 MovieInfo 와 MovieVideos를 렌더링한다.</p>
<pre><code class="language-js">import { Suspense } from &quot;react&quot;;
import MovieDetail from &quot;../../../../components/movie-info&quot;;
import MovieVideos from &quot;../../../../components/movie-video&quot;;
export default async function Text({ params }) {
  const { id } = await params;



  return (
    &lt;div&gt;
      &lt;Suspense fallback={&lt;h1&gt;Loading Movie Info&lt;/h1&gt;}&gt;
        &lt;MovieDetail id={id} /&gt;
      &lt;/Suspense&gt;
      &lt;Suspense fallback={&lt;h1&gt;Loading Movie Video&lt;/h1&gt;}&gt;
        &lt;MovieVideos id={id} /&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>그럼 순차적으로 데이터가 준비되는 순간 사용자는 렌더링된 데이터를 볼 수 있다.
그리고 page.tsx 와 같은 위치에 있는 loading.tsx 는 최적화 한 덕분에 await 하는게 없으므로 전체 로딩하는 일이 없게 된다.</p>
<blockquote>
<p>page 단위 로딩: <code>loading.tsx</code>
서버컴포넌트 단위 로딩: <code>Suspense</code></p>
</blockquote>
<hr>
<p>Next15) 15버전 부터는 기본 캐싱이 안되기 때문에 캐싱을 확인하려면 fetch의 두번째 인자 cache옵션을 <code>force-cache</code> 로 바꿔줘야 한다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Data Fetching]]></title>
            <link>https://velog.io/@angel_eugnen/Data-Fetching</link>
            <guid>https://velog.io/@angel_eugnen/Data-Fetching</guid>
            <pubDate>Sun, 13 Jul 2025 05:49:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/eb7cdb7a-b6bb-43c1-902c-9479c0262562/image.png" alt=""></p>
<h1 id="data-fetching">Data Fetching</h1>
<p>fetch, streaming, suspense, boundary 등을 배울것이다.
쉽고 멋지다고 한다.</p>
<p>NextJS 의 fetching을 위해 영화 상세보기 API를 이용하자.</p>
<blockquote>
<p>[출처-노마드코더] :  <a href="https://nomad-movies.nomadcoders.workers.dev/">https://nomad-movies.nomadcoders.workers.dev/</a></p>
</blockquote>
<p>/: This page
/movies: List popular movies
/movies/:id: Get movie by :id
/movies/:id/credits: Get credits for a movie by :id
/movies/:id/videos: Get videos for a movie by :id
/movies/:id/providers: Get providers for a movie by :id
/movies/:id/similar: Get similar movies for a movie by :id</p>
<p>url 종류는 위와 같다.</p>
<hr>
<h2 id="react-client-fetching">React Client Fetching</h2>
<pre><code class="language-js">&quot;use client&quot;;
// 과거에 리액트에서 하던 방식의 클라이언트 fetch는 아래와 같다.
import { useEffect, useState } from &quot;react&quot;;

export default function HOME() {
  const [isLoading, setIsLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovies = async () =&gt; {
    const response = await fetch(
      &quot;https://nomad-movies.nomadcoders.workers.dev/movies&quot;
    );
    const json = await response.json();
    setMovies(json);
    setIsLoading(false);
  };
  useEffect(() =&gt; {
    getMovies();
  }, []);
  return (
    &lt;div&gt;
      &lt;h1&gt;HOME&lt;/h1&gt;
      &lt;p&gt;{isLoading ? &quot;...isLoading&quot; : JSON.stringify(movies)}&lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>기존방식: React API &lt;===&gt; API &lt;===&gt; DB
이렇게 하면 네트워크 탭을 열면 누구나 api를 보고 쿼리를 통해 정보를 찾을 수 있어서 보안적으로 안전하지 않은 경우가 있다.</p>
<p>모든것을 client 에서 fetching을 하면 API에서 보안을 위해 DB요청하는 중간의 API이 필요했다. 하지만 NextJS 는 중간의 백엔드 API 없이 DB에서 바로 데이터를 가져올 수 있다.</p>
<p>또한 React앱에서는 로딩 상태를 항상 신경써서 직접 만들어야 하고, useState를 사용해서 데이터를 관리해야 한다. </p>
<p>하지만 server component 에서 fetching을 하면, useEffect와 useState, Loding을 사용하지 않아도 된다.</p>
<p>그리고 프론트엔드 개발자는 백엔드가 만든 API가 필요하지 않게 된다.</p>
<pre><code class="language-js">//next.js
// 브라우저에서는 아무것도 fetch하지 않는다.
export const metadata = {
  title: &quot;Home&quot;,
};
const MoviesURL = &quot;https://nomad-movies.nomadcoders.workers.dev/movies&quot;;

async function getMovies() {
  const response = await fetch(MoviesURL);
  const json = await response.json();
  return json;
}
// 한번만 서버에서 fetch 하면 캐시된다. server component 에서 Next JS 가 fetch한 것을 기억하고 있기 때문이다.
// 새로고침해도 이미 응답이 캐시되었기 때문에 로딩이 필요 없다
// 하지만 만약 서버 끄고 재 실행된다면 로딩이 필요하다. 즉 첫번째 fetch한 데이터가 존재하면 API에 요청하지 않고 캐싱된 데이터를 보여준다.
export default async function HomePage() {
  const movies = await getMovies();
  return &lt;div&gt;{JSON.stringify(movies)}&lt;/div&gt;;
}
</code></pre>
<blockquote>
<p>최신 데이터가 필요할때는 캐싱이나 revalidation을 해야한다.</p>
</blockquote>
<hr>
<h2 id="loding">Loding</h2>
<p>백엔드의 응답까지 첫 초기 렌더링이 UI 가 표기되지 않는 멈춤상태로 있는 설정이 싫고 최소한의 정적 UI를(네비게이션) 보고있게 하고 싶다면 loading파일을 하나 만들어서 해결할 수 있다. loading.tsx 파일을 만들어주자</p>
<pre><code class="language-js">export default function Loading() {
  return &lt;h2&gt;Loading...&lt;/h2&gt;;
}
</code></pre>
<p>loading 파일만 제공 해줘도 된다. 
이것은 백엔드가 페이지를 streaming 하기 때문에 가능하다.
백엔드에서 fetch함수가 완료되면 결과값을 브라우저에 보낸다. 그래서 layout과 navigation을 먼저 보내고 loading이 끝난 결과값을 보여주면 된다.</p>
<p>네트워크 탭을 열면 로딩중이라면 localhost가 로딩중임을 확인 할 수 있다. 이때 NextJS는 브라우저의 일부를 보여주며 기다려 달라고 하고, loading컴포넌트가 교체되는 것이다. 그리고 HomePage가 async인 이유는 준비된 Html 부분을 브라우저에 전달하기 위함이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dynamic Routes]]></title>
            <link>https://velog.io/@angel_eugnen/Dynamic-Routes</link>
            <guid>https://velog.io/@angel_eugnen/Dynamic-Routes</guid>
            <pubDate>Thu, 10 Jul 2025 02:48:11 GMT</pubDate>
            <description><![CDATA[<h2 id="dynamic-routes">Dynamic Routes</h2>
<p>변수가 라우터에 들어갈 때 사용한다.
파일시스템으로 동작한다.</p>
<p><code>[id]</code> 이런 식으로 동작한다.
<img src="https://velog.velcdn.com/images/angel_eugnen/post/e988d2f7-dfdb-4000-930a-3507bdfab11f/image.png" alt=""></p>
<pre><code class="language-js">export default function MovieDetail(props){
    console.log(props)
    return &lt;div&gt;Movie&lt;/div&gt;
}</code></pre>
<p>콘솔로 찍어보면
<img src="https://velog.velcdn.com/images/angel_eugnen/post/2bcdd554-1d66-45f3-9c25-fd3d178352a0/image.png" alt=""></p>
<p>이렇게 나온다.</p>
<p>id, sesarchParams을 얻게 되는데, searchParams는 url 뒤에 <code>?</code> 데 대한 내용이다..</p>
<p>15 버전 이상 부터는 params 와 searchParams가 비동기로 작동해서, async await를  사용해야 한다.</p>
<pre><code class="language-js">const Page = async ({
  params,
  searchParams,
}: {
  params: Promise&lt;{ id: string }&gt;
  searchParams: Promise&lt;{ [key: string]: string }&gt;
}) =&gt; {
  const { id } = await params
  const search = await searchParams

  console.log(id, search)

  return &lt;h1&gt;My Post: {id}&lt;/h1&gt;
}

export default Page
</code></pre>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/c1d4986f-942a-41ff-9ce9-a862265cf1f1/image.png" alt=""></p>
<p>그러면 이렇게 출력된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Metadata]]></title>
            <link>https://velog.io/@angel_eugnen/Metadata</link>
            <guid>https://velog.io/@angel_eugnen/Metadata</guid>
            <pubDate>Thu, 10 Jul 2025 02:23:17 GMT</pubDate>
            <description><![CDATA[<h2 id="route-groups">route groups</h2>
<blockquote>
<p>layout, not-found 는 꼭 루트 폴더에 있어야 한다.</p>
</blockquote>
<p>그룹을 만들고 싶으면 <code>(home)</code> 처럼 폴더 이름을 괄호로 묶어줘야 한다.
폴더 이름을 지정해주면 url이 바뀌지 않고, 프레임 워크에서만 보인다.</p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/c072afe3-38a5-41d2-a775-c2aa589b5e20/image.png" alt="">
<img src="https://velog.velcdn.com/images/angel_eugnen/post/9fa0e3e9-e65c-4ffd-a007-c8fe7bb3db00/image.png" alt=""></p>
<p>그래서 루트에 있던 홈 컴포넌트를 (home) 폴더로 옮길 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/559cc9db-7f85-46f0-b349-e8f66d792bd3/image.png" alt=""></p>
<h2 id="metadata">Metadata</h2>
<p>꼭 내보내야 하는 object 이고, 이것을 Metadata라고 부른다. 그리고 이것은 헤더에 표시된다. </p>
<pre><code class="language-bash">export const metadata = {
  title: &#39;Home&#39;,
  description: &#39;집에 보내줘 빨리, 바이 짜이쩬~&#39;,
}
export default function HOME(){
   return  &lt;div&gt; HOME&lt;/div&gt;
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/54d28e9b-5101-4eb0-aa7c-122a4c177265/image.png" alt=""></p>
<p>metadata는 병합된다.
title을 상위에 적고 하위에 description을 적으면
title은 상위에 있는 내용으로, description은 하위에 있는 내용으로 메타데이터가 병합된다. 또한 metadata는 서버 컴포넌트에만 있을 수 있다.</p>
<p>메타데이터에 대한 템플릿 또한 만들 수 있다.</p>
<hr>
<p>루트에서 템플릿 메타데이터를 만들었다.</p>
<pre><code class="language-bash">import { Metadata } from &quot;next&quot;

export const metadata :Metadata = {
  title: {template:&quot;%s | Next Movies&quot;,
  default: &quot;Loading...&quot;,
  },
  description: &#39;집에 보내줘 빨리, 바이 짜이쩬~&#39;,
}

export default function Layout({childeren}:{childeren:React.ReactNode}){
   return ( &lt;div&gt;
    {childeren}
&amp;copy; Next JS is great!    &lt;/div&gt;)
}

</code></pre>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/ab182a7e-3b46-489d-961f-aa66c2358985/image.png" alt=""></p>
<p>헤더에 반영되고, head 태그에 반영된 것을 알 수 있다.</p>
<hr>
<p>추가) 동적 메타데이터 정보가 api에 있을때? generateMetadata에서 호출해도 된다</p>
<pre><code class="language-js">
export async function generateMetadata({ params: { id } }: IParamas) {
  const movie = await getMovie(id); // 영화 정보를 불러오기 위해 API 를 부르면 안좋은가? -&gt; 최신버전은 fetch한번하면 캐싱된 응답을 받아서 괜찮다 ㅎㅎ
  return {
    title: movie.title,
  };
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Navigation]]></title>
            <link>https://velog.io/@angel_eugnen/Navigation</link>
            <guid>https://velog.io/@angel_eugnen/Navigation</guid>
            <pubDate>Thu, 10 Jul 2025 02:00:49 GMT</pubDate>
            <description><![CDATA[<h2 id="navigation">Navigation</h2>
<p>layout.tsx 라는 파일을 만들면
그 하위에 적용된다.</p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/15f36479-8bb6-4ab6-b9e6-259b33ee19e2/image.png" alt=""></p>
<p>이렇게 있는 경우 루트의 layout 에서 컴포넌트를 임포트 하여 그것이 제일 상단으로, 그리고 아채 children이 추가된다.
<img src="https://velog.velcdn.com/images/angel_eugnen/post/0548bb81-84d7-4581-b4a8-fccd08ad2940/image.png" alt=""></p>
<p>그리고 about-us라는 루트 하위의 폴더에서 layout을 만들고 하위 url로 접속하면
<img src="https://velog.velcdn.com/images/angel_eugnen/post/a6a5c617-c17e-4f6c-a595-47caf7cb84b2/image.png" alt="">
<img src="https://velog.velcdn.com/images/angel_eugnen/post/b1e9d37d-f4f9-462e-afbc-314eadf8d94f/image.png" alt=""></p>
<p>app/about-us/jobs/sales 라는 폴더에서 레이아웃이 렌더링 되는 것을 알 수 있다.</p>
<p>레이아웃은 서로 상쇄되는 것이 아닌 중첩되고 있다.</p>
<h2 id="next-js-동작-방식">Next JS 동작 방식</h2>
<p><code>&lt;RootLayout/&gt;</code> 확인 -&gt; <code>/about-us</code> 경로 확인 -&gt; <code>&lt;Layout/&gt;</code> 확인 -&gt; 하위의 children인 <code>&lt;Sales/&gt;</code> 렌더링</p>
<p>즉 2개의 layout을 중첩하여 렌더링 하고 있다.</p>
<p>프레임 워크는 page의 가장 가까운 layout을 찾으려고 한다. 그래서 가까운 layout을 찾으면, 그 위 상위 항목을 확인하고 또 layout을 찾는다.</p>
<p>0</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hydration]]></title>
            <link>https://velog.io/@angel_eugnen/Hydration</link>
            <guid>https://velog.io/@angel_eugnen/Hydration</guid>
            <pubDate>Wed, 09 Jul 2025 06:21:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/5127ab46-da6b-4b48-9a53-70bc971ec296/image.png" alt=""></p>
<h1 id="hydration">Hydration</h1>
<p>단순 HTML을 React applaction으로 초기화 하는 작업이다.(HTML 위에서 React Application을 실행한다는 의미)</p>
<p>예를 들어 React 를 Initialize하여 onClick를 부착해 여기서 작성 가능 한 기능을 수행하도록 하는 것이다.</p>
<p>/aout-us 접근 =&gt; <code>&lt;button&gt;0&lt;/button&gt;</code> =&gt; 사용자 확인 이후 =&gt; <code>&lt;button onClick={()=&gt;이벤트수행}&gt;&lt;/button&gt;</code></p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/43634903-7291-4552-89ac-f2c67871387a/image.png" alt="">
마치 위의 상태로 있는 것이다.
그리고 먼저 렌더링 한 후에 뒷단에서 next.js를 로딩하고 framework 를 초기화 하고 React application을 생성하는 것이다.</p>
<p>그러면 버튼이 interactive해지고 eventListener가 생기는 것이다.</p>
<hr>
<h3 id="🤔-render">🤔 render()</h3>
<p><code>ReactDOM.render(element, container, [callback])</code></p>
<ul>
<li>element: 화면에 그려진 React element (집어넣어 줄 요소)</li>
<li>container: React element를 해당 container DOM에 렌더링 (구체적인 위치)</li>
<li>callback: 렌더링 후 반환되는 값을 돌려주는 콜백 함수
CRA하게 되면 index.js에 다음 코드를 쉽게 볼 수 있습니다.</li>
</ul>
<pre><code class="language-js">import App from &#39;./App&#39;;

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;));</code></pre>
<p>즉, <code>&lt;App/&gt;</code> 컴포넌트를 root라는 id를 가지고 있는 엘리먼트 내부로 넣어주어 페이지를 렌더링 해주고 있습니다.</p>
<h3 id="🤔-hyrate">🤔 hyrate()</h3>
<p><code>ReactDOM.hydrate(element, container, [callback])</code>
기본적으로 render()와 동일하지만, ReactDOMServer로 렌더링된 HTML에 이벤트 리스터(자바스크립트 코드)를 연결해주기 위해 사용됩니다.</p>
<p>서버 사이드를 통해 이미 HTML에는 엘리먼트들이 채워져 있죠? 따라서 다시 render 해줄 필요 없이 hydrate를 통해 기존 마크업에 이벤트 리스너를 붙여주는 과정입니다.</p>
<p>위 과정들을 정리해봅시다.</p>
<p>Next.js는 서버에서 HTML을 문자열로 가져온 후에, 클라이언트에서 서버로부터 보내준 HTML을 render(), hydrate()하여 브라우저에 렌더링 했습니다. 이 일련의 과정을 Hydration이라고 합니다!</p>
<blockquote>
<p>Hydration 사전적 정의
[명사] 수분 공급</p>
</blockquote>
<p>서버의 데이터가 클라이언트의 DOM과 결합하는 과정을 빗대어 hydrate라는 단어로 정의된 것 같습니다.</p>
<p>React는 클라이언트 렌더링만 있어, 유저에게 보여줄 HTML, CSS 그리고 자바스크립트 모두 render() 함수를 이용해 생성하여, 모든 리소스를 한번에 렌더링합니다.</p>
<p>반면, Next.js는 서버에서 보여줄 HTML 컨텐츠를 미리 렌더링(내용을 채워서)하여 가져오기 때문에 render() 함수로 HTML 뼈대만 렌더하고, hydrate()를 통해 서버에서 받아온 HTML에 유저가 상호작용할 수 있는 이벤트 리스너(JS파일)을 연결하는 것입니다.</p>
<p>HTML에 JS파일(수분💦)을 주입한다고 해서 hydrate라고 이해할 수 있겠습니다!</p>
<hr>
<h2 id="client">client</h2>
<p>client 에서 hydrdate 되어 interactive 하게 만들어질 components는 오직 <code>use client</code> 지시어를 최 상단에 갖고있는 컴포넌트만 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/6a493a09-db15-439f-b278-87df46a8ba11/image.png" alt="">
<img src="blob:https://velog.io/b17bf315-df6f-4083-ab5b-f3738c17bab0" alt="업로드중.."></p>
<p>따라서 Navigation 만 hydration 되는 것이다.</p>
<blockquote>
<p>백엔드에서 render되고 프론트엔드에서 hydrate된다는 것이다.</p>
</blockquote>
<p>이는 data fetch 되는 방식에서 중요한 개념이다.
useQuery, useEffect ... 등등은 이제 안 쓸것이다.</p>
<h2 id="hydrate장점">hydrate장점</h2>
<p>: Javascript를 선택적으로 작은파일만 다운로드 하게 할 수 있다. 그래서 백엔드에서 HTML을 먼저 preRender할 수 있다. 그리고 use client를 작성하면 그 child는 모두 client component가 된다. 그리고 server component 에서 fetch를 진행하면, 보안을 신경쓰지 않아도 된다. client에서 render 되기 때문! 그래서 API키를 넣거나 DB에 접근해도 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Routing]]></title>
            <link>https://velog.io/@angel_eugnen/Routing</link>
            <guid>https://velog.io/@angel_eugnen/Routing</guid>
            <pubDate>Wed, 09 Jul 2025 05:57:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/c0d1a82e-fdd3-482d-b02e-86ef6de00102/image.png" alt=""></p>
<h2 id="recap">Recap</h2>
<p>Routing, Navigation, Layout, client, server components 등에 대해 배울 예정이다.</p>
<p>client, server 는 NextJS에서 나온 개념이라고 한다.</p>
<hr>
<p>NextJS는 프레임워크라서, 올바른 폴더에 정확한 이름의 파일이 들어가 있으면 NextJS는 우리 코드를 이용해서 만들어 준다.</p>
<p>app 폴더 안에 page 파일을 찾는것 처럼.
그리고 꼭 export를 해야한다.
그리고 동시에 layout을 찾는다. 그래서 자동 생성된다. meta data라는 객체와 RootLauout는 html 바디 안에 body 태그를 갖는다.</p>
<h2 id="routing">Routing</h2>
<ul>
<li><p>react 라우팅 방식</p>
<pre><code>/ -&gt; &lt;Home /&gt;
/about-us -&gt; &lt;AboutUs /&gt;</code></pre></li>
<li><p>NextJS 방식</p>
<pre><code>app 폴더 -&gt; / (roote segment)
app폴더/about-us 폴더 -&gt; /about-us</code></pre><p><img src="https://velog.velcdn.com/images/angel_eugnen/post/6b45f2db-2fb5-4252-a0a3-a3e4ac55de80/image.png" alt=""></p>
</li>
<li><p>주의
<img src="https://velog.velcdn.com/images/angel_eugnen/post/6f8d5606-a876-48c0-87c6-ca45bc9c6a44/image.png" alt=""></p>
</li>
</ul>
<p>이렇게 된 경우 url 에서 /about-us/company에 접근하면 404 와 같다. page.tsx파일이 할당되지 않았기 때문이다.</p>
<h2 id="navigation-bar">Navigation Bar</h2>
<pre><code class="language-js">&quot;use client&quot;
import Link from &quot;next/link&quot;
import { usePathname } from &quot;next/navigation&quot;

export default function Navigation(){
    const path = usePathname();
    return &lt;nav&gt;
        &lt;ul&gt;
            &lt;li&gt;
                &lt;Link href=&#39;/&#39;&gt;HOME&lt;/Link&gt;
                {path === &#39;/&#39; ? &#39;💕&#39;:&#39;&#39;}
            &lt;/li&gt;
            &lt;li&gt;
                &lt;Link href=&#39;/about-us&#39;&gt;AboutUS&lt;/Link&gt;
                {path === &#39;/about-us&#39; ? &#39;💕&#39;:&#39;&#39;}

            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/nav&gt;
}</code></pre>
<p>Link 라는 태그를 이용해 네비게이션을 구현한다.
현재 경로를 알기 위해선 usePathname을 이용한다. 이때 최상단에 <code>use client</code> 를 작성하지 않으면 에러가 난다.</p>
<p>왜그럴까? client component가 뭘까</p>
<p>평범한 react 가 렌더링 되는 방식은 client side application이다. 유저가 페이지에 도착한 시점에는 javascript를 모두 실행한 후 페이지를 렌더링 한다. html에 UI가 없는 시점에서 Javascript를 기다릴 때 빈화면이 보이게 된다. App을 실행하려면 JavaScript가 실행되어야만 한다.</p>
<p>client side rendering 단점</p>
<ul>
<li>만약 사용자가 데이터 연결 상태가 안좋은 상태에서 이 페이지에 접근하게 된다면, 아무 UI가 없는 빈 화면을 더 오래보게 될 것이다.</li>
<li>Google에서도 빈 HTML을 보게 된다.</li>
</ul>
<p>server side rendering 장점</p>
<ul>
<li>페이지의 내용들이 (html) 이미 브라우저에 있다. html 을 보여주는데 Javascript가 필요하지 않기 때문이다. 그래서 빈 화면을 보지 않게 된다.</li>
<li>가장 먼저 back end 에서 렌더링 되는 것이다.=&gt; 렌더링이란, JavaScript function을 가져와서 브라우저가 이해할 수 있는 html로 변환하는 작업니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Intro - NextJS]]></title>
            <link>https://velog.io/@angel_eugnen/Intro-NextJS</link>
            <guid>https://velog.io/@angel_eugnen/Intro-NextJS</guid>
            <pubDate>Wed, 09 Jul 2025 05:17:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/7cc77e9f-9896-44e9-88e2-6ec805732121/image.png" alt=""></p>
<h2 id="1-introduction">1. Introduction</h2>
<p>이 Framework 는 풀스택 웹앱을 개발하기 위한 최선의 Framework 라고 한다. 이 코스가 끝날 때에는 모든 프로그램을 NextJS로 만들고 싶어질거라 한다.</p>
<p>React Framework 중에 1등이라 하는 NextJS
최신 업데이트로 많이 발전했다고 한다.(14기준)
자동화로 개발자 경험 1위 라는데.. 기대가 된다.</p>
<h2 id="2-requirements">2. Requirements</h2>
<blockquote>
<p>React.js 다룰줄 알아야 한다. (state, props, fetch, routing 등을 알아야 하니..)</p>
</blockquote>
<h2 id="3-libraryreact-vs-frameworknextjs">3. Library(React) vs Framework(NextJS)</h2>
<ul>
<li><p>Library: 라이브러리는 코드 내에서 사용하는 것이다. 원하는 아키텍를 원하는 방식으로 짠다. 함수 지향, 객체지향 프로그래밍이 가능하다. 코드 내에서 사용하고 사용의 주체는, 사용자이다. 파일이름, 폴더 구조 등. 라이브러리는 우리가 사용할 때 쓸 수 있다는 것이다.라이브러리의 도움이 필요할 때만 가져와서 사용할 수 있다. 다운받아서 사용하는 것. React는 UI 인터페이스를 build하는데 사용하는 라이브러리 이다.반응형 인터페이스를 구축하는데 도움이 된다. CSS 에 Styled Components 를 사용하거나 tailwind를 사용하거나, expo 를 사용하거나 routing을 사용하거나.. 등등의 모든 자유를 개발자가 가지는 것이다.</p>
</li>
<li><p>Framework: 개발자에게 주도권은 없다. 프레임워크가 주도하고 담당한다. 프레임워크가 대신 자동화 하고 결정을 담당한다. 그래서 NextJS는 많은 Feature을 가지고 있다. 그래서 규칙을 따라야 한다. 이것이 프레임 워크다. 개발자가 규칙을 지켜야 한다는 것이다. 적당한 폴더에 파일을 만들어야 한다는 것. 그것이 지켜지지 않는다면 동작하지 않는다. import의 개념을 잊어라. 올바른 변수의 모양으로 넣어야 한다는 것이다. 올바른 위치에 넣으면 그것을 실행해 줄것이다.</p>
</li>
</ul>
<blockquote>
<p>라이브러리는 개발자가 사용하고, 프레임워크는 코드를 사용한다.</p>
</blockquote>
<h2 id="4-old-vs-new-version">4. Old vs New Version</h2>
<ul>
<li>Page Router : app 폴더에서 찾는다</li>
<li>App Router : pages폴더에서 찾는다.</li>
</ul>
<p>app에서 라우팅 하는 방식과 data fetching하는 법은 많이 바뀌었다.
getStaticProps같은 것은 잊어라. page Router를 사용했다면, 최신 버전을 사용하기 위해선 migration 해야 한다.</p>
<h2 id="5-installation">5. Installation</h2>
<p><code>npm init -y</code></p>
<pre><code class="language-js">// 이후  package.json 생김. author 수정
{
  &quot;name&quot;: &quot;next-practice&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },
  &quot;repository&quot;: {
    &quot;type&quot;: &quot;git&quot;,
    &quot;url&quot;: &quot;git+https://github.com/Eugenius1st/next-practice.git&quot;
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;MIT&quot;, // 여기 수정
  &quot;bugs&quot;: {
    &quot;url&quot;: &quot;https://github.com/Eugenius1st/next-practice/issues&quot;
  },
  &quot;homepage&quot;: &quot;https://github.com/Eugenius1st/next-practice#readme&quot;,
  &quot;description&quot;: &quot;&quot;
}
</code></pre>
<p><code>npm install react@latest next@latest react-dom@latest</code></p>
<ul>
<li>react: UI 와 여타 많은 것들을 구성하는 부분</li>
<li>reactdom: 브라우저에서 렌더링 하는 도구</li>
</ul>
<pre><code class="language-js">// 이후 스크립트 수정
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;next dev&quot;

  },</code></pre>
<p>실행하면 NextJS는 pages라는 폴더를 찾으로 할 것이다. 그리고 그 폴더는 app 폴더 안에 있을 것이다.</p>
<p>다음
app/page.tsx 혹은 .jsx를 만들어라.
<img src="https://velog.velcdn.com/images/angel_eugnen/post/9a959907-fdee-4585-b985-d4b3b761f1cc/image.png" alt=""></p>
<p>그리고 
<code>npm run dev</code>로 실행</p>
<p><img src="https://velog.velcdn.com/images/angel_eugnen/post/ffe51d41-792f-4d26-a3a3-fc662ea4d80b/image.png" alt=""></p>
<p>그리고 실행하면 local:3000 에서 실행중임을 알 수 있다.
그리고 자연히 layout.tsx 가 생겨남을 알 수 있다.</p>
<pre><code class="language-js">export const metadata = {
  title: &#39;Next.js&#39;,
  description: &#39;Generated by Next.js&#39;,
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  )
}
</code></pre>
]]></description>
        </item>
    </channel>
</rss>