로컬 환경 실행
백엔드 (Go)
cd backend
APP_ENV=local air # hot reload 권장
APP_ENV=local go run ./cmd/server # 직접 실행- 포트:
8000 - 로컬 DB:
postgres://dartbrief:dartbrief@localhost:5432/local - env:
.env.local자동 로드 (godotenv). prod 는.env.prod - local 에선 공시 모니터 / AI 스케줄러 OFF
DISCLOSURE_MONITOR_AUTO_START=falseANALYSIS_SCHEDULER_AUTO_START=false
- FCM 푸시:
FIREBASE_SERVICE_ACCOUNT_JSON_B64(base64 인코딩된 서비스 계정 JSON). 빈 값이면 푸시 비활성화 (개발 시 FCM 없이도 빌드/실행 OK)- 키 발급: Firebase Console → 프로젝트 설정 → 서비스 계정 → 새 비공개 키 생성 → 다운로드된 .json 을
base64 -i <file>로 인코딩
- 키 발급: Firebase Console → 프로젝트 설정 → 서비스 계정 → 새 비공개 키 생성 → 다운로드된 .json 을
모바일 (Flutter)
별도 언급 없으면 make run-dev 사용 — dart-define 키 자동 로드, Tailscale IP 자동 감지.
cd mobile
make run-dev # Tailscale IP 자동 감지, Android 또는 iPhone
make run-dev IP=100.x.x.x # 수동 IP 지정 (Tailscale IP)
make run-all # iOS + Android 동시
make run-prod # prod 빌드
make build-ios-dev # IPA 빌드- 디바이스 ID:
- Android:
RFCWA07VPZA(SM-A346N) - iPhone:
00008101-001161440290001E
- Android:
- API_URL 자동 갱신 (
_sync-dev-iprule →dart-defines.dev.json수정) - Flutter 버전:
fvm사용 (mobile/.fvmrc= stable)
API_URL — Tailscale 메시 VPN
실기기 ↔ Mac 연결은 항상 Tailscale 사용. 같은 Wi-Fi 불필요 — 어느 네트워크에서든 동작.
# Mac Tailscale IP 확인
tailscale ip -4
# Android 무선 디버깅 (Tailscale IP + ADB 포트)
adb connect <android-tailscale-ip>:<port>
# Tailscale 미실행 시
tailscale upmake run-dev 가 tailscale ip -4 로 Mac IP 를 자동 감지해 dart-defines.dev.json 에 주입. Tailscale 이 꺼져있으면 빌드 실패 메시지 출력.
프론트엔드 (Next.js)
cd frontend
npm run dev # localhost:3000
npm run build && npm run start # prod 빌드 (Node)
npm run lint # ESLint
npm test # vitest
# Cloudflare Workers + Static Assets 배포 (OpenNext for Cloudflare 어댑터)
npm run build:cf # .open-next/ (worker.js + assets/) 산출
npm run preview:cf # 로컬 wrangler 프리뷰
npm run deploy:cf # CF Workers 업로드 (wrangler deploy)환경 변수:
API_BASE_URL— server-side fetch (ISR) 의 백엔드 base. 미설정 시http://localhost:8000으로 fallback (backendPORT기본값과 일치). prod:https://api.dartbrief.com. CF Workers dashboard 환경변수로 주입.NEXT_PUBLIC_SITE_PASSWORD— SiteGate 비공개 게이트 비밀번호 (런치 전까지). 미설정 시 게이트 비활성.NEXT_PUBLIC_APPLE_APP_ID— Smart App Banner 의 App Store ID. 등록 전 placeholder.
자세한 CF Workers 운영은 docs/operations/cloudflare-deploy.md 참조.
디자인 시스템 (docs/design/)
docs/design/ 가 디자인 SSOT site — tokens/*.json → Style Dictionary → CSS + Dart 생성 + HTML 카탈로그 (components/) + screen/flow spec 통합 페이지 (screens/, flows/) 한 site.
cd docs/design
npm install # 최초 1회
npm run build:tokens # tokens/*.json → CSS + Dart 양쪽 재생성
npm run dev # Vite site (localhost:5174)
npm run build # dist/ 빌드 (CF Pages 배포 대상)구조:
tokens/{color,spacing,radius,typography}.json— SSOT, semantic 토큰은 scale primitive (teal.500등) 를 alias 로 참조style-dictionary.config.js— 커스텀 transform 2개 (CSS px 단위, FlutterColor(0xAARRGGBB)변환), 커스텀 Dart 포맷 (DesignTokens클래스)- 출력 위치 (3 platform):
- CSS →
docs/design/build/css/tokens.css(Vite site) - Dart →
mobile/lib/shared/theme/generated/design_tokens.dart(Flutterpackage:import) - Web →
frontend/src/styles/generated/tokens.css(--ds-*prefix, Tailwind 4@theme가var(--ds-color-brand-*)alias).frontend/src/styles/globals.css가 import
- CSS →
- 모두 커밋 대상. 직접 수정 금지, 항상
tokens/*.json에서 변경 후npm run build:tokens재실행 + 3개 생성물 같이 커밋.
HTML 카탈로그 vs spec 통합 페이지:
components/*.html— 재사용 부품 (button, modal 등). 시각 박제만.screens/*.html+flows/*.html— 화면/시나리오. 같은 페이지에 컴포넌트 iframe + spec 마크다운 inline 렌더 (marked.js).- spec 마크다운 source =
docs/spec/screen-*.md/docs/spec/flow-*.md. Vite 가?rawimport 로 빌드 시 텍스트 inline. GitHub UI 에선 마크다운 직접, design site 에선 시각 + 마크다운 합쳐 보임. drift 0 (단일 source). - Vite
server.fs.allow: ['..']로 sibling 폴더 (docs/spec/) 접근 허용.
DB 접속
# 로컬
PGPASSWORD=dartbrief psql -h localhost -U dartbrief -d local
# prod (Railway)
source ~/.config/cloud-tokens/railway.env && psql "$RAILWAY_POSTGRESQL_URL"- schema:
dartbrief(모든 테이블CREATE TABLE dartbrief.X형식) - 마이그레이션: goose, 백엔드 시작 시 자동
publicschema 테이블 생성 금지
로컬 PostgreSQL 구조
~/claudeProjects/local-infra/ 에서 docker-compose 로 관리.
- 컨테이너:
local-postgres(포트 5432) - Database:
local(모든 프로젝트 공유 단일 DB) - 각 프로젝트는 자기 이름의 schema (dartbrief / uijeongbogi / daeva)
- Role 은 자기 schema OWNER,
localDB 에는 CONNECT 만 search_path는 ROLE 단위 자동 ({schema}, public)
규칙:
- ❌ public schema 에 테이블 만들지 말 것 (prod 와 달라짐)
- ❌
dartbrief라는 database 이름 X (구방식) - ❌ 앱이 superuser(postgres) 로 실행되면 안 됨
- ✅ 모든 SQL
dartbrief.{table}schema-qualified
일부 테이블 ownership 문제 (로컬 only)
dartbrief.analysis_cache, disclosures, stocks 가 postgres superuser 소유로 생성된 케이스 — 마이그레이션 시 must be owner of table 에러 (SQLSTATE 42501).
해결:
PGPASSWORD=postgres psql -h localhost -U postgres -d local -c "
ALTER TABLE dartbrief.analysis_cache OWNER TO dartbrief;
ALTER TABLE dartbrief.disclosures OWNER TO dartbrief;
ALTER TABLE dartbrief.stocks OWNER TO dartbrief;
"prod (Railway) 는 단일 유저 운영이라 이 문제 없음. 로컬 환경 only.
DB 스키마 문서 재생성
마이그레이션 추가 후 docs/spec/db-schema/ (tbls 자동 생성) 갱신:
# 1. 로컬 DB에 마이그레이션 적용 (백엔드 한 번 띄우면 Goose가 자동, skip 가능)
cd backend && APP_ENV=local air
# 2. 레포 루트에서 재생성 스크립트 실행
cd ..
./scripts/regen-db-schema.sh스크립트가 하는 일:
tbls doc --rm-dist로 문서 자동 생성- README 의 cross-schema Enums 섹션 제거 (tbls 한계로 daeva 등 타 스키마 enum 이 섞여 들어오는 문제 우회)
설정: .tbls.yml — 로컬 DB 기준, dartbrief 스키마, goose_db_version 제외, disableOutputSchema: true. 설치: brew install k1low/tap/tbls
자세한 내용은 docs/spec/db-schema.md.
빌드 / 리체크 (커밋 전 필수)
# 백엔드
cd backend && gofmt -l . && go vet ./...
# 모바일
cd mobile && flutter analyze
# 영향 받은 패키지만 테스트 (전체 안 돌려도 됨)
cd backend && go test ./internal/ai/...
cd mobile && flutter analyze lib/features/quota/백엔드 핵심 파일 경로
| 역할 | 경로 |
|---|---|
| 서버 진입점 | backend/cmd/server/main.go |
| 마이그레이션 | backend/cmd/server/migrations/ |
| Watchlist 핸들러 | backend/internal/watchlist/handler.go |
| AI 분석 핸들러 | backend/internal/ai/handler.go |
| AI 레포지토리 | backend/internal/ai/repository.go |
| Quota / Reveal 레포 | backend/internal/ai/quota_repository.go, reveal_repository.go |
| Dashboard 핸들러 | backend/internal/dashboard/handler.go |
| 스케줄러 | backend/internal/scheduler/ |
| Admin API | backend/internal/admin/handler.go |
모바일 (Flutter) 핵심 파일 경로
| 역할 | 경로 |
|---|---|
| 대시보드 (메인) | mobile/lib/features/watchlist/presentation/pages/dashboard_page.dart |
| 공시 상세 + AI 분석 | mobile/lib/features/disclosure/presentation/pages/disclosure_detail_page.dart |
| AI 분석 카드 | mobile/lib/features/disclosure/presentation/widgets/analysis_card.dart |
| Quota 인디케이터 | mobile/lib/features/quota/presentation/quota_indicator.dart |
| Watchlist 컨트롤러 | mobile/lib/features/watchlist/application/watchlist_controller.dart |
| 종목 검색/추가 | mobile/lib/features/watchlist/presentation/widgets/stock_search.dart |
| 공통 API 클라이언트 | mobile/lib/shared/api/api_client.dart |
기술 스택
| 영역 | 기술 |
|---|---|
| 모바일 | Flutter, Riverpod |
| 백엔드 | Go 1.26, pgx/v5, goose 마이그레이션 |
| DB | PostgreSQL (Railway prod / 로컬 postgres) |
| 인증 | 카카오 / 구글 소셜 로그인 |
| 알림 | FCM 푸시 |
| AI | vLLM 직접 호출 (DGX Spark 192.168.0.7, prod 는 CF Access 터널) |
| 배포 | Railway (백엔드 + DB), Vercel (프론트) |