원클릭으로
agent-feature
Agent 페이지 구현 스킬. SSE 토큰 스트리밍, 사이드바, 마크다운 렌더링, 메시지 액션을 포함한 AI 챗봇 인터페이스 구현 시 사용. React 19 + TypeScript + TailwindCSS 기반.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Agent 페이지 구현 스킬. SSE 토큰 스트리밍, 사이드바, 마크다운 렌더링, 메시지 액션을 포함한 AI 챗봇 인터페이스 구현 시 사용. React 19 + TypeScript + TailwindCSS 기반.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
코드 품질 관리 스킬. Prettier/ESLint/TypeScript 컨벤션, Agent Skills 활용 패턴, 코드 리뷰 체크리스트.
Guide for the Map page feature (Kakao Maps integration, location search, store category filtering). Use when working on map components, search bar, bottom sheet, detail sheet, store/pickup filters, or Kakao Map API integration. Triggers on "map", "kakao map", "location search", "store category", "bottom sheet", "map marker", "nearby", "autocomplete", "suggest".
Agent 채팅 데이터 무결성 관리 스킬. IndexedDB v3 스키마, Optimistic Updates, Eventual Consistency 패턴 구현 시 사용.
Agent 기능 트러블슈팅 스킬. 빌드 에러, 이미지 업로드 실패, SSE 연결 문제 등 실전 문제 해결 가이드.
Vercel 공식 React 성능 최적화 스킬. AI 에이전트를 위한 40+ 규칙 (waterfalls 제거, 번들 크기 최적화 등).
| name | agent-feature |
| description | Agent 페이지 구현 스킬. SSE 토큰 스트리밍, 사이드바, 마크다운 렌더링, 메시지 액션을 포함한 AI 챗봇 인터페이스 구현 시 사용. React 19 + TypeScript + TailwindCSS 기반. |
폐기물 분리배출 AI 코칭 서비스 "이코에코"의 Agent 페이지 구현 가이드
Agent 페이지는 기존 Chat과 분리된 새로운 AI 대화 인터페이스입니다. 주요 특징:
| 컴포넌트 | 설명 |
|---|---|
AgentContainer | 메인 레이아웃 |
AgentSidebar | 대화 목록 사이드바 |
AgentMessageList | 메시지 영역 |
AgentInputBar | 입력창 + 전송 |
useAgentSSE | SSE 토큰 스트리밍 훅 |
AgentMarkdownRenderer | MD 렌더링 |
AgentCodeBlock | 코드 블록 + 복사 |
AgentMessageActions | 복사/재생성/피드백 |
AgentStopButton | 생성 중단 |
| 컴포넌트 | 설명 |
|---|---|
AgentImageUpload | 이미지 첨부 |
AgentScrollToBottom | 하단 이동 FAB |
AgentImage | MD 이미지 렌더링 |
| 컴포넌트 | 설명 |
|---|---|
AgentSidebarItem | 제목 수정 기능 확장 |
Agent 기능 구현 시작
│
├─ SSE 스트리밍 구현?
│ └─ references/api-spec.md 참조
│
├─ 컴포넌트 설계 필요?
│ └─ references/component-design.md 참조
│
├─ 프론트엔드 컨벤션 확인?
│ └─ references/frontend-stack.md 참조
│
├─ 이미지 업로드 구현?
│ └─ references/existing-code-reference.md §1 참조
│ └─ 기존 image.service.ts 재사용
│
├─ 위치 정보 포함?
│ └─ references/existing-code-reference.md §2 참조
│ └─ 기존 useGeolocation.tsx 재사용
│ └─ ⚠️ { lat, lng } → { latitude, longitude } 변환 필수
│
├─ 기존 Chat 코드 참조?
│ └─ references/existing-code-reference.md §3 참조
│ └─ Chat 페이지는 레퍼런스로 보존 (스키마 불일치 주의)
│
└─ 마크다운 렌더링?
└─ react-markdown + remark-gfm + rehype-highlight 사용
// hooks/agent/useAgentSSE.ts
const useAgentSSE = (jobId: string | null) => {
const [streamingText, setStreamingText] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const eventSourceRef = useRef<EventSource | null>(null);
useEffect(() => {
if (!jobId) return;
const url = `${import.meta.env.VITE_API_URL}/api/v1/chat/${jobId}/events`;
const es = new EventSource(url, { withCredentials: true });
eventSourceRef.current = es;
es.addEventListener('token', (e) => {
const data = JSON.parse(e.data);
setStreamingText((prev) => prev + data.content);
});
es.addEventListener('done', () => {
es.close();
setIsStreaming(false);
});
setIsStreaming(true);
return () => es.close();
}, [jobId]);
const stopGeneration = useCallback(() => {
eventSourceRef.current?.close();
setIsStreaming(false);
}, []);
return { streamingText, isStreaming, stopGeneration };
};
// components/agent/AgentMarkdownRenderer.tsx
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
const AgentMarkdownRenderer = ({ content }: { content: string }) => (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
components={{
code: ({ inline, className, children }) => {
if (inline) return <code className="...">{children}</code>;
const lang = className?.replace('language-', '') || '';
return <AgentCodeBlock code={String(children)} language={lang} />;
},
img: ({ src, alt }) => <AgentImage src={src} alt={alt} />,
}}
>
{content}
</ReactMarkdown>
);
// 클립보드 복사
const handleCopy = async (text: string) => {
await navigator.clipboard.writeText(text);
};
// 재생성
const handleRegenerate = async (messageId: string) => {
// 이전 assistant 메시지 삭제 후 동일 질문 재전송
};
src/
├── pages/
│ └── Agent/
│ ├── Agent.tsx # 메인 페이지
│ └── index.ts
│
├── components/
│ └── agent/
│ ├── index.ts
│ ├── AgentContainer.tsx
│ ├── AgentHeader.tsx
│ ├── AgentMessageList.tsx
│ ├── AgentMessage.tsx
│ ├── AgentMessageActions.tsx
│ ├── AgentStopButton.tsx
│ ├── AgentMarkdownRenderer.tsx
│ ├── AgentCodeBlock.tsx
│ ├── AgentImage.tsx
│ ├── AgentImageModal.tsx
│ ├── AgentScrollToBottom.tsx
│ ├── AgentImageUpload.tsx
│ ├── AgentInputBar.tsx
│ ├── AgentThinkingUI.tsx
│ ├── ModelSelector.tsx
│ └── sidebar/
│ ├── AgentSidebar.tsx
│ ├── AgentSidebarHeader.tsx
│ ├── AgentSidebarList.tsx
│ ├── AgentSidebarItem.tsx
│ └── AgentSidebarEmpty.tsx
│
├── hooks/
│ └── agent/
│ ├── useAgentSSE.ts
│ ├── useAgentChat.ts
│ ├── useAgentSidebar.ts
│ ├── useImageUpload.ts
│ └── useScrollToBottom.ts
│
├── api/
│ └── services/
│ └── agent/
│ ├── agent.service.ts
│ ├── agent.type.ts
│ ├── agent.queries.ts
│ └── agent.mutation.ts
│
└── types/
└── AgentTypes.ts
# 필수
yarn add react-markdown remark-gfm rehype-highlight
# highlight.js 테마 (global.css에서 import)
@import 'highlight.js/styles/github-dark.css';
상세 정보는 references/ 디렉토리 참조:
| 파일 | 내용 |
|---|---|
frontend-stack.md | 프론트엔드 기술 스택, 컨벤션, Git 규칙, 배포 |
api-spec.md | Agent API 엔드포인트, SSE 이벤트 형식 |
component-design.md | 컴포넌트 상세 설계 (Props, 스타일) |
existing-code-reference.md | Camera/Location/Chat 기존 코드 참조 |
| 파일 | 용도 |
|---|---|
api/services/image/image.service.ts | 이미지 업로드 (Presigned URL) |
hooks/useGeolocation.tsx | 위치 정보 (Geolocation API) |
types/MapTypes.ts | Position 타입 정의 |
| 파일 | 참조 용도 |
|---|---|
pages/Chat/Chat.tsx | 메시지 상태 관리 |
components/chat/* | 메시지 UI 패턴 |
hooks/useScanSSE.ts | SSE + 폴링 패턴 |
구현 시 아래 순서대로 진행:
[ ] 1. API 서비스 레이어 (agent.service.ts, agent.type.ts)
[ ] 2. SSE 훅 (useAgentSSE.ts)
[ ] 3. 위치 정보 훅 (useGeolocation 재사용 + 변환 함수)
[ ] 4. 이미지 업로드 훅 (image.service.ts 재사용)
[ ] 5. 사이드바 컴포넌트
[ ] 6. 메시지 리스트 + 마크다운 렌더링
[ ] 7. 입력창 + 전송 (이미지/위치 통합)
[ ] 8. 메시지 액션 (복사, 재생성)
[ ] 9. 생성 중단 버튼
[ ] 10. 이미지 첨부 UI (P1)
[ ] 11. 스크롤 하단 FAB (P1)
[ ] 12. 제목 수정 (P2)
EventSource.close() 호출token_recovery 이벤트로 누락 토큰 복구TextDecoder 사용 권장withCredentials: true 필수 (s_access 쿠키){ lat, lng } → Backend { latitude, longitude }