티스토리 뷰
imprun.dev Console 개발 가이드
새로운 세션에서도 일관된 개발을 위한 프론트엔드 아키텍처 및 코딩 규칙
DESIGN_GUIDELINES.md0.01MB
목차
- 기술 스택
- 아키텍처 패턴
- 프로젝트 구조
- 페이지 작성 규칙
- 컴포넌트 작성 규칙
- Hooks 패턴
- 타입 시스템
- 스타일링 & 디자인 시스템 ⭐
- API 서비스 패턴
- 새 기능 추가 가이드
- 코드 리뷰 체크리스트
기술 스택
Core
- React 19: UI 라이브러리
- react-router-dom v6: 클라이언트 사이드 라우팅
- TypeScript:
strictNullChecks: false,noImplicitAny: false - Vite: 빌드 도구
상태 관리
- Zustand: 전역 상태 (auth, ui)
- TanStack Query v5: 서버 상태 관리
UI
- Tailwind CSS v4: 유틸리티 기반 스타일링
- shadcn/ui: UI 컴포넌트 라이브러리
- lucide-react: 아이콘
기타
- Monaco Editor: 코드 에디터
- React Hook Form + Zod: 폼 관리 및 검증
- Recharts: 차트
- Sonner: 토스트 알림
- axios: HTTP 클라이언트
배포
- nginx: 정적 파일 서빙
- Docker: 컨테이너화
- Kubernetes: 오케스트레이션
아키텍처 패턴
핵심 철학
"프레임워크는 UI 레이어에만 영향을 줘야 한다. 비즈니스 로직과 데이터 레이어는 독립적이어야 한다."
이 프로젝트는 프레임워크에 독립적인 아키텍처를 채택하여:
- ✅ 프레임워크 마이그레이션 용이성 확보
- ✅ 테스트 가능성 향상
- ✅ 재사용성 극대화
- ✅ 유지보수성 개선
1. Container/Presentational Pattern
데이터 로직과 UI 렌더링을 완전히 분리합니다.
// ✅ Container Component (데이터 + 로직)
export function ApplicationListContainer() {
// 데이터 페칭 (Hook Layer)
const { data: applications, isLoading } = useApplications()
// 비즈니스 로직
const activeApps = applications?.filter(app => app.phase !== 'Deleted')
// Presentational Component에 데이터 전달
return <ApplicationList applications={activeApps} isLoading={isLoading} />
}
// ✅ Presentational Component (렌더링만)
interface ApplicationListProps {
applications: TApplicationDetail[]
isLoading: boolean
}
export function ApplicationList({ applications, isLoading }: ApplicationListProps) {
if (isLoading) return <Spinner />
return (
<div className="grid grid-cols-3 gap-4">
{applications.map(app => (
<ApplicationCard key={app.appid} app={app} />
))}
</div>
)
}
장점:
- 테스트 용이: Presentational 컴포넌트는 props만 검증
- 재사용성: 동일한 UI를 다른 데이터 소스에 연결 가능
- 프레임워크 독립성: 렌더링 로직은 라우터 변경에 영향 없음
2. Layered Architecture (4계층)
┌──────────────────────────────────────┐
│ Component Layer │ ← UI 렌더링
│ - React 컴포넌트 │
│ - 사용자 인터랙션 │
│ - UI 상태 (useState) │
├──────────────────────────────────────┤
│ Hook Layer │ ← 비즈니스 로직
│ - TanStack Query (use-*.ts) │
│ - 로직 Hook (use-*-control.ts) │
│ - 서버 상태 관리 │
├──────────────────────────────────────┤
│ Service Layer │ ← API 인터페이스
│ - *.service.ts │
│ - 도메인별 API 그룹화 │
│ - 요청/응답 변환 │
├──────────────────────────────────────┤
│ HTTP Client Layer │ ← 네트워크 통신
│ - axios (httpClient.ts) │
│ - 인증 토큰 주입 │
│ - 공통 에러 처리 │
└──────────────────────────────────────┘
3. 실제 데이터 흐름
// 1️⃣ Component Layer
export function ApplicationCard({ app }: { app: TApplicationDetail }) {
const { actions, state } = useApplicationControl(app) // Hook 호출
return (
<Card>
<Button onClick={actions.start} disabled={!state.canStart}>
시작
</Button>
</Card>
)
}
// 2️⃣ Hook Layer (use-application-control.ts)
export function useApplicationControl(app: TApplicationDetail) {
const queryClient = useQueryClient()
const start = async () => {
await applicationService.start(app.appid) // Service 호출
queryClient.invalidateQueries({ queryKey: ['applications'] })
toast.success("시작했습니다")
}
return {
actions: { start },
state: { canStart: app.phase === 'Stopped' }
}
}
// 3️⃣ Service Layer (application.service.ts)
export const applicationService = {
async start(appid: string): Promise<void> {
await httpClient.post(`/v1/applications/${appid}/start`) // HTTP 호출
}
}
// 4️⃣ HTTP Client Layer (httpclient.ts)
const httpClient = axios.create({
baseURL: env.API_URL,
timeout: 10000,
})
httpClient.interceptors.request.use((config) => {
const token = useAuthStore.getState().token
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
4. 프레임워크 독립성
프레임워크 변경 시 영향 범위:
- 변경 필요 (5%): 라우팅, 페이지 구조
- 변경 불필요 (95%): Hooks, Services, Components, Store
프로젝트 구조
frontend/
└── src/
├── pages/ # 페이지 컴포넌트
│ ├── Home.tsx
│ ├── Applications.tsx
│ ├── ApplicationDetail.tsx
│ └── FunctionEditor.tsx
│
├── routes/ # 라우트 정의
│ └── index.tsx # react-router-dom 설정
│
├── components/
│ ├── layout/ # 레이아웃 컴포넌트
│ │ ├── AppBar.tsx
│ │ ├── MainNavBar.tsx
│ │ └── AppNavBar.tsx
│ ├── applications/ # 도메인별 컴포넌트
│ ├── functions/
│ ├── database/
│ ├── triggers/
│ ├── modals/ # 모달 컴포넌트
│ └── ui/ # shadcn/ui 컴포넌트
│
├── hooks/ # TanStack Query + 로직 훅
│ ├── use-applications.ts
│ ├── use-functions.ts
│ └── use-application-control.ts
│
├── services/ # API 서비스
│ ├── application.service.ts
│ ├── function.service.ts
│ └── database.service.ts
│
├── store/ # Zustand 스토어
│ ├── auth.ts
│ └── ui.ts
│
├── types/ # 도메인별 타입
│ ├── application.ts
│ ├── function.ts
│ ├── common.ts
│ └── index.ts # ⚠️ 필수 re-export
│
└── lib/ # 유틸리티
├── httpclient.ts
└── env.ts
레이아웃 계층 구조
┌──────────────────────────────────────────┐
│ AppBar (전역) │ ← 모든 페이지
│ Logo + Breadcrumb [User Menu] │
├──────────────────────────────────────────┤
│ MainNavBar (메인 영역) │ ← /projects, /billing
│ 프로젝트 | 빌링 | 설정 │
├──────────────────────────────────────────┤
│ AppNavBar (Application 영역) │ ← /applications/:appid/*
│ 개요 | Functions | Database | ... │
├──────────────────────────────────────────┤
│ Page Content │
└──────────────────────────────────────────┘
페이지 작성 규칙
React Router DOM 기반 페이지
// src/routes/index.tsx
import { createBrowserRouter } from 'react-router-dom'
export const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <HomePage /> },
{
path: 'applications',
children: [
{ index: true, element: <ApplicationList /> },
{
path: ':appid',
element: <ApplicationLayout />,
children: [
{ index: true, element: <ApplicationOverview /> },
{ path: 'functions', element: <FunctionList /> },
{ path: 'functions/:name/edit', element: <FunctionEditor /> },
],
},
],
},
],
},
])
페이지 컴포넌트 패턴
// src/pages/ApplicationList.tsx
import { useApplications } from '@/hooks/use-applications'
import { ApplicationCard } from '@/components/applications/ApplicationCard'
import { CreateApplicationModal } from '@/components/modals/CreateApplicationModal'
export function ApplicationList() {
// 1. 데이터 페칭 (Hook Layer)
const { data: applications, isLoading } = useApplications()
// 2. 로딩 처리
if (isLoading) return <Spinner />
// 3. 페이지 구조 정의
return (
<div className="p-6 h-full overflow-auto">
<div className="max-w-7xl mx-auto flex flex-col gap-6">
{/* 헤더 */}
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">Applications</h1>
<CreateApplicationModal />
</div>
{/* 메인 콘텐츠 */}
{applications.length === 0 ? (
<EmptyState />
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{applications.map(app => (
<ApplicationCard key={app.appid} app={app} />
))}
</div>
)}
</div>
</div>
)
}
페이지 작성 원칙
✅ 해야 할 것:
- Hook으로 데이터 페칭
- 컴포넌트 조합으로 UI 구성
- 페이지 레벨 레이아웃 정의
❌ 하지 말아야 할 것:
- 페이지에 비즈니스 로직 직접 작성 (Hook으로 분리)
- 인라인 API 호출 (Service Layer 사용)
- 복잡한 상태 관리 (Hook으로 추상화)
컴포넌트 작성 규칙
컴포넌트 분리 기준
다음 조건을 만족하면 별도 컴포넌트로 분리:
- 재사용 가능: 2곳 이상에서 사용
- 복잡도: 100줄 이상
- 독립 로직: 자체 상태 관리 필요
- Client 인터랙션: 이벤트 핸들러 필요
컴포넌트 배치 규칙
src/components/
├── layout/ # 레이아웃 (AppBar, NavBar)
├── applications/ # Application 도메인
├── functions/ # Function 도메인
├── database/ # Database 도메인
├── triggers/ # Trigger 도메인
├── modals/ # 모든 모달
└── ui/ # shadcn/ui 컴포넌트
컴포넌트 작성 패턴
// src/components/applications/ApplicationCard.tsx
import { Card, CardHeader, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { useApplicationControl } from "@/hooks/use-application-control"
import type { TApplicationDetail } from "@/types"
interface ApplicationCardProps {
app: TApplicationDetail
}
export function ApplicationCard({ app }: ApplicationCardProps) {
// Hook으로 로직 추상화
const { actions, state } = useApplicationControl(app)
return (
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">{app.name}</h3>
<StatusBadge phase={app.phase} />
</CardHeader>
<CardContent>
<div className="flex gap-2">
<Button
onClick={actions.start}
disabled={!state.canStart || state.isActioning}
size="sm"
>
시작
</Button>
<Button
onClick={actions.stop}
disabled={!state.canStop || state.isActioning}
size="sm"
variant="secondary"
>
정지
</Button>
</div>
</CardContent>
</Card>
)
}
Hooks 패턴
1. Data Hooks (TanStack Query)
// src/hooks/use-applications.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { applicationService } from "@/services/application.service"
import type { TApplicationDetail, TCreateApplicationDto } from "@/types"
// 조회
export function useApplications() {
return useQuery({
queryKey: ["applications"],
queryFn: () => applicationService.getApplications(),
})
}
export function useApplication(appid: string) {
return useQuery({
queryKey: ["applications", appid],
queryFn: () => applicationService.getApplication(appid),
enabled: !!appid,
})
}
// 생성
export function useCreateApplication() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: TCreateApplicationDto) =>
applicationService.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["applications"] })
toast.success("생성되었습니다")
},
onError: (error: any) => {
toast.error(error.response?.data?.message || "생성 실패")
},
})
}
2. Logic Hooks (비즈니스 로직)
// src/hooks/use-application-control.ts
import { useState } from "react"
import { useQueryClient } from "@tanstack/react-query"
import { applicationService } from "@/services/application.service"
import { ApplicationPhase } from "@/types"
import { toast } from "sonner"
export function useApplicationControl(app: TApplicationDetail | null) {
const [isActioning, setIsActioning] = useState(false)
const queryClient = useQueryClient()
// 상태 계산
const canStart = app?.phase === ApplicationPhase.Stopped
const canStop = app?.phase === ApplicationPhase.Started
// 액션 정의
const start = async () => {
if (!app) return
setIsActioning(true)
try {
await applicationService.start(app.appid)
queryClient.invalidateQueries({ queryKey: ["applications"] })
toast.success("시작했습니다")
} catch (error: any) {
toast.error(error.response?.data?.message || "시작 실패")
} finally {
setIsActioning(false)
}
}
const stop = async () => {
// 동일한 패턴
}
return {
actions: { start, stop },
state: { canStart, canStop, isActioning },
}
}
Hooks 작성 원칙
- Data Hooks: TanStack Query 사용, 캐시 관리
- Logic Hooks: 재사용 가능한 비즈니스 로직
- 명명 규칙:
use-도메인명.ts(Data),use-도메인명-동사.ts(Logic)
타입 시스템
1. 도메인별 타입 분리
src/types/
├── application.ts # Application 도메인
├── function.ts # Function 도메인
├── database.ts # Database 도메인
├── trigger.ts # Trigger 도메인
├── common.ts # 공통 타입
└── index.ts # ⚠️ 필수 re-export
2. 타입 정의 예시
// src/types/application.ts
export enum ApplicationPhase {
Starting = "Starting",
Started = "Started",
Stopping = "Stopping",
Stopped = "Stopped",
}
export type TApplicationDetail = {
_id: string
appid: string
name: string
phase: string // ApplicationPhase
state: string
region: string
createdAt: string
updatedAt: string
}
export type TCreateApplicationDto = {
name: string
region: string
}
3. 중앙 re-export (필수!)
// src/types/index.ts
export * from "./application"
export * from "./function"
export * from "./database"
export * from "./trigger"
export * from "./common"
4. 타입 Import 규칙
// ✅ 중앙 re-export 사용
import { TApplicationDetail, ApplicationPhase } from "@/types"
// ❌ 개별 파일 직접 import 금지
import { TApplicationDetail } from "@/types/application"
5. State vs Phase
- State: 사용자가 설정한 목표 상태 (Running, Stopped)
- Phase: 실제 시스템 상태 (Starting, Started, Stopping, Stopped)
// ✅ Phase 기반 UI 제어
const canStart = app.phase === ApplicationPhase.Stopped
const canStop = app.phase === ApplicationPhase.Started
const isTransitioning = ["Starting", "Stopping"].includes(app.phase)
6. 주의사항
.ts사용 (.d.ts아님): enum은 런타임 코드- TypeScript 설정:
strictNullChecks: false→ null/undefined 명시적 체크 필수
스타일링 & 디자인 시스템
⭐ 중요: 상세한 UI/UX 가이드라인은 DESIGN_GUIDELINES.md 참조
디자인 시스템에는 다음이 포함되어 있습니다:
- ✨ 일관된 타이포그래피 (그라디언트 헤더, semantic colors)
- 🎨 세련된 카드 디자인 (border-2, hover 효과, 그라디언트 아이콘)
- 🎯 인터랙티브 애니메이션 (duration-300, -translate-y-1)
- 📱 반응형 레이아웃 (grid, gap-8, md:, lg:)
- 🌙 다크모드 자동 지원 (semantic colors 사용)
아래는 기본 스타일링 규칙입니다. 새 페이지/컴포넌트 작성 시 반드시 DESIGN_GUIDELINES.md를 함께 참조하세요.
1. Tailwind 기본 패턴
// ✅ flex + gap 사용
<div className="flex flex-col gap-6">
<div className="flex items-center gap-2">
...
</div>
</div>
// ❌ margin 사용 금지
<div className="space-y-4"> // X
<div className="mb-4"> // X
2. 페이지 레이아웃 패턴
<div className="p-6 h-full overflow-auto">
<div className="max-w-7xl mx-auto flex flex-col gap-6">
{/* 헤더 */}
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">Title</h1>
<Button>Action</Button>
</div>
{/* 콘텐츠 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{items.map(item => <Card key={item.id} />)}
</div>
</div>
</div>
3. 색상 시스템 (Semantic Colors)
// ✅ Tailwind semantic colors 사용
text-foreground
bg-background
border-border
bg-primary text-primary-foreground
bg-muted text-muted-foreground
// ❌ 하드코딩된 색상 금지
text-gray-900
bg-white
border-gray-200
이유: 다크모드 자동 지원
4. 아이콘 사용
import { Settings, Info, AlertCircle } from "lucide-react"
// ✅ size prop 사용
<Settings size={16} /> // 기본 (대부분)
<Info size={14} /> // 버튼 내부
<Settings size={20} /> // 헤더
// ❌ className 크기 금지
<Settings className="h-4 w-4" />
5. 반응형 디자인
// ✅ Tailwind breakpoints 사용
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* 모바일: 1열, 태블릿: 2열, 데스크톱: 3열 */}
</div>
API 서비스 패턴
Service Layer 구조
// src/services/application.service.ts
import { httpClient } from "@/lib/httpclient"
import type {
TApplicationDetail,
TCreateApplicationDto,
TUpdateApplicationDto,
} from "@/types"
export const applicationService = {
// 조회
async getApplications(): Promise<TApplicationDetail[]> {
return await httpClient.get("/v1/applications")
},
async getApplication(appid: string): Promise<TApplicationDetail> {
return await httpClient.get(`/v1/applications/${appid}`)
},
// 생성
async create(dto: TCreateApplicationDto): Promise<TApplicationDetail> {
return await httpClient.post("/v1/applications", dto)
},
// 수정
async update(
appid: string,
dto: TUpdateApplicationDto
): Promise<TApplicationDetail> {
return await httpClient.patch(`/v1/applications/${appid}`, dto)
},
// 삭제
async delete(appid: string): Promise<void> {
await httpClient.delete(`/v1/applications/${appid}`)
},
// 액션
async start(appid: string): Promise<void> {
await httpClient.post(`/v1/applications/${appid}/start`)
},
async stop(appid: string): Promise<void> {
await httpClient.post(`/v1/applications/${appid}/stop`)
},
}
HTTP Client 설정
// src/lib/httpclient.ts
import axios from "axios"
import { useAuthStore } from "@/store/auth"
import { env } from "./env"
const httpClient = axios.create({
baseURL: env.API_URL,
timeout: 10000,
})
// 요청 인터셉터: 토큰 주입
httpClient.interceptors.request.use((config) => {
const token = useAuthStore.getState().token
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 응답 인터셉터: 공통 에러 처리
httpClient.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
useAuthStore.getState().logout()
window.location.href = "/login"
}
return Promise.reject(error)
}
)
export default httpClient
Service 작성 원칙
- 도메인별 분리:
application.service.ts,function.service.ts - 명명 규칙:
도메인명.service.ts - 타입 명시: 요청/응답 타입 명확히
- 에러 처리: HTTP Client에서 일괄 처리
새 기능 추가 가이드
1. 라우트 추가
// src/routes/index.tsx
{
path: ':appid',
element: <ApplicationLayout />,
children: [
// ... 기존 라우트
{ path: '새기능', element: <새기능Page /> },
],
}
2. NavBar 업데이트
// src/components/layout/AppNavBar.tsx
const navItems = [
// ... 기존 항목
{ label: "새기능", href: "새기능", icon: YourIcon },
]
3. 타입 정의
// src/types/새기능.ts
export type T새기능 = {
id: string
name: string
// ...
}
export type TCreate새기능Dto = {
name: string
// ...
}
// src/types/index.ts에 추가
export * from "./새기능"
4. Service 작성
// src/services/새기능.service.ts
import { httpClient } from "@/lib/httpclient"
import type { T새기능, TCreate새기능Dto } from "@/types"
export const 새기능Service = {
async getList(appid: string): Promise<T새기능[]> {
return await httpClient.get(`/v1/apps/${appid}/새기능`)
},
async create(appid: string, dto: TCreate새기능Dto): Promise<T새기능> {
return await httpClient.post(`/v1/apps/${appid}/새기능`, dto)
},
}
5. Hook 작성
// src/hooks/use-새기능.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { 새기능Service } from "@/services/새기능.service"
export function use새기능s(appid: string) {
return useQuery({
queryKey: ["새기능", appid],
queryFn: () => 새기능Service.getList(appid),
enabled: !!appid,
})
}
export function useCreate새기능(appid: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: TCreate새기능Dto) =>
새기능Service.create(appid, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["새기능", appid] })
},
})
}
6. 컴포넌트 작성
// src/components/새기능/새기능List.tsx
import type { T새기능 } from "@/types"
interface 새기능ListProps {
items: T새기능[]
appid: string
}
export function 새기능List({ items, appid }: 새기능ListProps) {
return (
<div className="grid grid-cols-3 gap-4">
{items.map(item => (
<새기능Card key={item.id} item={item} appid={appid} />
))}
</div>
)
}
7. 페이지 작성
// src/pages/새기능Page.tsx
import { useParams } from "react-router-dom"
import { use새기능s } from "@/hooks/use-새기능"
import { 새기능List } from "@/components/새기능/새기능List"
import { Create새기능Modal } from "@/components/modals/Create새기능Modal"
export function 새기능Page() {
const { appid } = useParams<{ appid: string }>()
const { data: items, isLoading } = use새기능s(appid!)
if (isLoading) return <Spinner />
return (
<div className="p-6 h-full overflow-auto">
<div className="max-w-7xl mx-auto flex flex-col gap-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">새기능</h1>
<Create새기능Modal appid={appid!} />
</div>
<새기능List items={items} appid={appid!} />
</div>
</div>
)
}
코드 리뷰 체크리스트
아키텍처
- Container/Presentational 패턴 준수?
- 4계층 구조 유지? (Component → Hook → Service → HTTP Client)
- 비즈니스 로직이 Hook Layer에 있는가?
- 페이지 컴포넌트가 단순 래퍼가 아닌가?
컴포넌트
- 컴포넌트가 도메인별 폴더에 위치?
- Presentational 컴포넌트가 props만 받는가?
- 100줄 이상이면 분리 검토했는가?
Hooks
- Data Hook은 TanStack Query 사용?
- Logic Hook은 재사용 가능한가?
- Mutation 성공 시 Query Invalidation?
타입
- 타입이 도메인별 파일에 정의?
-
types/index.ts에서 re-export? - 중앙 re-export로 import?
스타일링
-
flex + gap사용,margin회피? - Semantic colors 사용?
- 아이콘에
sizeprop 사용? - 반응형 디자인 적용?
API
- Service Layer 사용?
- 에러 처리가 적절한가?
- Toast 알림 제공?
일반
- TypeScript 타입 명시?
- null/undefined 명시적 체크? (
strictNullChecks: false) - Phase 기반 UI 제어?
참고 자료
코드 예시
- 페이지: src/pages/Applications.tsx
- 컴포넌트: src/components/applications/ApplicationCard.tsx
- Hook: src/hooks/use-application-control.ts
- Service: src/services/application.service.ts
- 타입: src/types/application.ts
- 디자인: DESIGN_GUIDELINES.md - ⭐ 필수 참조
마지막 원칙
"일관성이 완벽함보다 중요하다. 기존 코드 패턴을 따르라."
새로운 세션에서 작업 시:
- 기존 유사 기능 찾기
- 패턴 파악 후 동일하게 적용
- 의문점은 이 가이드 참조
- 가이드에 없으면 기존 코드 우선
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
| Kubernetes에서 특권 포트 피하기: NodePort + iptables 포워딩 패턴 (0) | 2025.10.27 |
|---|---|
| Kubernetes Gateway API 실전 가이드: Kong Ingress에서 표준 API로 전환하기 (0) | 2025.10.27 |
| Claude AI와 함께하는 프론트엔드 개발: imprun.dev의 CLAUDE.md 가이드 공개 (0) | 2025.10.27 |
| Next.js를 버리고 순수 React로 돌아온 이유: 실무 관점의 프레임워크 선택 여정 (0) | 2025.10.27 |
| Sequential Thinking MCP: AI의 구조화된 사고 프로세스 (0) | 2025.10.27 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- AI agent
- Gemini 3.0
- claude code
- Claude Opus 4.5
- imprun.dev
- Next.js
- NestJS
- architecture
- Go
- AI
- api gateway
- authorization
- ai coding
- authentication
- troubleshooting
- react
- Claude
- feature-sliced design
- frontend
- GPT-5.1
- EnvironmentAgnostic
- Tailwind CSS
- zustand
- backend
- Kubernetes
- Development Tools
- CLAUDE.md
- AGENTS.md
- Developer Tools
- security
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
글 보관함
