Skip to content

로컬 환경 실행

백엔드 (Go)

bash
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=false
    • ANALYSIS_SCHEDULER_AUTO_START=false
  • FCM 푸시: FIREBASE_SERVICE_ACCOUNT_JSON_B64 (base64 인코딩된 서비스 계정 JSON). 빈 값이면 푸시 비활성화 (개발 시 FCM 없이도 빌드/실행 OK)
    • 키 발급: Firebase Console → 프로젝트 설정 → 서비스 계정 → 새 비공개 키 생성 → 다운로드된 .json 을 base64 -i <file> 로 인코딩

모바일 (Flutter)

별도 언급 없으면 make run-dev 사용 — dart-define 키 자동 로드, Tailscale IP 자동 감지.

bash
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
  • API_URL 자동 갱신 (_sync-dev-ip rule → dart-defines.dev.json 수정)
  • Flutter 버전: fvm 사용 (mobile/.fvmrc = stable)

API_URL — Tailscale 메시 VPN

실기기 ↔ Mac 연결은 항상 Tailscale 사용. 같은 Wi-Fi 불필요 — 어느 네트워크에서든 동작.

bash
# Mac Tailscale IP 확인
tailscale ip -4

# Android 무선 디버깅 (Tailscale IP + ADB 포트)
adb connect <android-tailscale-ip>:<port>

# Tailscale 미실행 시
tailscale up

make run-devtailscale ip -4 로 Mac IP 를 자동 감지해 dart-defines.dev.json 에 주입. Tailscale 이 꺼져있으면 빌드 실패 메시지 출력.

프론트엔드 (Next.js)

bash
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 (backend PORT 기본값과 일치). 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.

bash
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 단위, Flutter Color(0xAARRGGBB) 변환), 커스텀 Dart 포맷 (DesignTokens 클래스)
  • 출력 위치 (3 platform):
    • CSS → docs/design/build/css/tokens.css (Vite site)
    • Dart → mobile/lib/shared/theme/generated/design_tokens.dart (Flutter package: import)
    • Web → frontend/src/styles/generated/tokens.css (--ds-* prefix, Tailwind 4 @themevar(--ds-color-brand-*) alias). frontend/src/styles/globals.css 가 import
  • 모두 커밋 대상. 직접 수정 금지, 항상 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 가 ?raw import 로 빌드 시 텍스트 inline. GitHub UI 에선 마크다운 직접, design site 에선 시각 + 마크다운 합쳐 보임. drift 0 (단일 source).
  • Vite server.fs.allow: ['..'] 로 sibling 폴더 (docs/spec/) 접근 허용.

DB 접속

bash
# 로컬
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, 백엔드 시작 시 자동
  • public schema 테이블 생성 금지

로컬 PostgreSQL 구조

~/claudeProjects/local-infra/ 에서 docker-compose 로 관리.

  • 컨테이너: local-postgres (포트 5432)
  • Database: local (모든 프로젝트 공유 단일 DB)
  • 각 프로젝트는 자기 이름의 schema (dartbrief / uijeongbogi / daeva)
  • Role 은 자기 schema OWNER, local DB 에는 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).

해결:

bash
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 자동 생성) 갱신:

bash
# 1. 로컬 DB에 마이그레이션 적용 (백엔드 한 번 띄우면 Goose가 자동, skip 가능)
cd backend && APP_ENV=local air

# 2. 레포 루트에서 재생성 스크립트 실행
cd ..
./scripts/regen-db-schema.sh

스크립트가 하는 일:

  1. tbls doc --rm-dist 로 문서 자동 생성
  2. 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.

빌드 / 리체크 (커밋 전 필수)

bash
# 백엔드
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 APIbackend/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 마이그레이션
DBPostgreSQL (Railway prod / 로컬 postgres)
인증카카오 / 구글 소셜 로그인
알림FCM 푸시
AIvLLM 직접 호출 (DGX Spark 192.168.0.7, prod 는 CF Access 터널)
배포Railway (백엔드 + DB), Vercel (프론트)