SUMMARY
Fortie (포티) - 크라우드소싱 운세 플랫폼 풀스택 개발기
"나와 같은 사주의 사람들은 오늘 어떤 하루를 보냈을까?" - 기록할수록 정확해지는 개인화 운세 서비스입니다.
NestJS + PostgreSQL + Next.js 14 + Flutter 풀스택 구성.
사주 + 점성술 2엔진, GPT-4o-mini 파인튜닝, 관리자 대시보드.
주요 기술적 도전: 결정론적 엔진 설계, AI 파인튜닝 파이프라인, 개인 정확도 시스템, 하이브리드 WebView 아키텍처
TABLE OF CONTENTS
목차
왜 이 서비스를 만들었나? 서비스 핵심 철학 전체 아키텍처 백엔드 (NestJS) 웹 프론트엔드 (Next.js) 모바일 (Flutter) 관리자 대시보드 주요 기능 AI 파인튜닝 기술적 도전과 해결 기술 스택
WHY
왜 이 서비스를 만들었나?
기존 운세 앱들의 한계가 명확했습니다. 대부분 "오늘의 운세"를 일방적으로 보여주기만 할 뿐, 그 운세가 실제로 맞았는지 검증하지 않습니다.
Fortie는 질문 하나에서 시작했습니다: "나와 같은 사주의 사람들은 오늘 어떤 하루를 보냈을까?"
사용자의 피드백(오늘 하루 기록)을 수집하고, 같은 사주를 가진 사람들의 통계를 쌓아서, 기록할수록 정확해지는 운세를 만들고 싶었습니다.
기술적으로는 사주(동양 역학) + 점성술(Swiss Ephemeris) 2개 엔진의 수학적 계산 결과를, GPT-4o-mini 파인튜닝 모델로 해석하는 구조를 설계했습니다.
그 결과, 백엔드 + 웹 프론트 + 모바일 + 관리자 대시보드로 구성된 풀스택 운세 플랫폼이 탄생했습니다.
PHILOSOPHY
서비스 핵심 철학
주관 0%, 결정론적
같은 생년월일+시간을 입력하면 항상 같은 사주/점성술 결과를 반환합니다.
AI가 하는 일은 수학적 계산 결과를 사람이 읽기 쉬운 문장으로 번역하는 것뿐입니다.
부정도 가치다
다른 운세 앱은 긍정적인 내용만 보여줍니다. Fortie는 긍정/부정/중립 모두 있는 그대로 표현합니다.
"오늘 조심해야 할 것"을 아는 것이 "오늘 좋은 일"만 듣는 것보다 가치 있다고 봅니다.
크라우드소싱 데이터 플라이휠
사용자가 매일 피드백(하루 기록)을 남기면, 같은 사주를 가진 사람들의 실제 경험 통계가 쌓입니다.
기록이 많을수록 개인 정확도가 올라가고, 7일/30일/90일/180일 마일스톤마다 새로운 기능이 해금됩니다.
ARCHITECTURE
전체 아키텍처
3개 레포지토리 구성
fortune-api (백엔드 + 관리자 대시보드)
NestJS 11 + TypeScript + PostgreSQL 16 + TypeORM
관리자 웹: React 19 + Vite + TailwindCSS 4
AI: GPT-4o-mini 파인튜닝
다국어: 14개 언어 지원
fortune-web (웹 프론트엔드)
Next.js 14 (App Router) + TypeScript
Zustand + TanStack Query v5
커스텀 글래스모피즘 SVG + 5탭 네비게이션
14개 언어 다국어 지원
fortune-app (모바일 앱)
Flutter + Dart (하이브리드 WebView 셸)
네이티브: 소셜 로그인 + FCM + Remote Config
콘텐츠: WebView로 fortune-web 로드
iOS + Android
데이터 흐름
사용자 입력 (생년월일/시간/성별) → 2개 엔진 (사주 계산 + Swiss Ephemeris 점성술)
엔진 결과 (4주, 십성, 행성 위치, 트랜짓) → GPT-4o-mini 파인튜닝 (자연어 해석)
AI 해석 → 웹/앱 UI (글래스모피즘 카드, 커스텀 차트)
사용자 피드백 (하루 기록) → 크라우드 통계 + 개인 정확도 → 다음 날 운세에 반영
BACKEND
백엔드 (NestJS + PostgreSQL)
2개 계산 엔진
사주 엔진 (Saju) — 60갑자 기반 4주(년주/월주/일주/시주) 계산, 충/합/형, 십성, 12지지, 신살, 신강/약, 용신, 납음까지 전통 한국 역학 체계 전체 구현
점성술 엔진 (Stellar) — Swiss Ephemeris(swisseph 0.5.17) 기반 실시간 행성 위치 계산, 진트랜짓, 하우스 시스템, 달 위상, 행성 간 어스펙트 분석
AI 해석 파이프라인
GPT-4o-mini 파인튜닝 — 언어별 개별 파인튜닝 모델 운용. Claude Sonnet으로 정답지(Ground Truth) 생성 → GPT-4o-mini에 학습
프롬프트 템플릿 시스템 — 엔진 데이터 + 사용자 프로필 메타데이터를 주입하는 구조화된 프롬프트
규칙 기반 폴백 — LLM 실패 시 규칙 기반(Rule-based) 해석으로 자동 전환, 서비스 중단 없음
인증 + 인프라
소셜 로그인 — Google OAuth + Apple Sign In + 게스트 로그인(디바이스 ID), JWT + Passport 기반
PostgreSQL 16 + TypeORM — 마이그레이션 기반 스키마 관리
Cloudflare R2 — 이미지/미디어 CDN 스토리지 (cdn.myfortie.com)
Docker 멀티스테이지 빌드 — builder → production 이미지 분리 배포
모듈 구조
src/
├── admin/ 관리자 API + R2 업로드
├── auth/ 소셜 로그인 (Google/Apple/Guest) + JWT
├── fortune/ 포티 조회 (2개 엔진 + 군중 통계 + 개인화)
├── feedback/ 피드백 V2.2 (9카테고리 + 감정 기반)
├── personal/ 개인 정확도 (패턴 감지, 점수, 마일스톤)
├── counsel/ Fortie에게 물어보기 (7턴 상담, 파인튜닝)
├── daily-prediction/ 내일 예측 (LLM + 규칙 폴백)
├── fortune-flow/ 운세 흐름 (4카테고리 3개월 분석)
├── fantasy/ 판타지 전생 직업 (20개 타입, 바이럴)
├── compatibility/ 썸 판별기 v4.0 (100조합, 6등급)
├── historical/ 역사 콘텐츠 "나와 똑같은 날" (2,285건)
├── subjective/ 관점별 주관 해석 (7개 관점)
├── cms/ CMS 7종 (이벤트/공지/FAQ/법적/배너/팝업/문의)
├── engines/ 사주 + 점성술 엔진 코어
├── interpretation/ AI 해석 (LLM 파인튜닝 모델)
├── notification/ FCM 푸시 + 알림함
├── analysis/ 데이터 분석 (Chi-square, Lift)
└── common/ 공통 (가드, 유틸, 엔티티)
WEB FRONTEND
웹 프론트엔드 (Next.js 14)
홈 화면 - Fortie가 전하는 메시지 + 오늘의 조언 (DO/DON'T/TIP)
5탭 네비게이션 구조
/home (오늘) — AI 인사이트 카드, 오늘의 조언(DO/DON'T/TIP), 크라우드 통계, 역사 콘텐츠, 피드백 CTA
/analysis (분석) — 사주/스텔라 2탭, 인사이트 카드 그리드, 카드별 상세 뷰
/calendar (기록) — 월간 캘린더 히트맵, 카테고리별 통계, 피드백 타임라인
/contents (콘텐츠) — 판타지 전생 직업, 썸 판별기, Fortune Flow, AI 상담, 내일 예측
/profile (마이) — 12지신/별자리 아바타, 프로필 편집, 알림함, 언어 변경

분석 탭 - 사주(좌) / 스텔라(우) 인사이트 카드 그리드
글래스모피즘 SVG 디자인 시스템
커스텀 SVG 아이콘 — 반투명 레이어 + 그라디언트 배경의 글래스모피즘 아이콘. 히어로, 카드, 행성, 별자리, 동물, 메타포 등 전체 직접 제작
커스텀 차트 — ArcGauge, DonutChart, NatalChart(천궁도), WuxingBars(오행), EnergyMeter, CompassDiagram 등 전부 SVG로 직접 구현 (차트 라이브러리 미사용)
절대 규칙 — 이모지 사용 금지, 모든 아이콘은 커스텀 SVG, 가라데이터/폴백 금지
상태 관리 전략
Zustand — 클라이언트 상태 (인증, UI 모달, 언어 설정). localStorage persist로 새로고침에도 유지
TanStack Query v5 — 서버 상태 (운세 데이터, 피드백, 알림). 자동 캐싱, stale-while-revalidate
Axios 인터셉터 — JWT 자동 주입, snake_case ↔ camelCase 자동 변환, Accept-Language 동적 설정, 401 자동 로그아웃
MOBILE
모바일 앱 (Flutter - 하이브리드 WebView)
모바일 앱 - 홈 배너에서 '썸 판별기' 콘텐츠 노출
Fortie 모바일은 네이티브 + WebView 하이브리드 구조입니다. 로그인/알림/강제업데이트 같은 OS 종속 기능은 네이티브(Flutter)로, 콘텐츠 UI는 WebView로 fortune-web을 로드합니다.
네이티브 영역 (Flutter/Dart)
소셜 로그인 4종 — Google OAuth, Apple Sign In, 게스트(디바이스 ID), 리뷰어 계정
FCM 푸시 알림 — 알림 권한 요청, 토큰 등록, 포그라운드 로컬 알림, 백그라운드 핸들러
Remote Config — 강제 업데이트(min_version), 서버 점검 모드, 인앱 공지사항
WebView 연동 (InAppWebView)
Auth 주입 — 로그인 후 Zustand 포맷 JSON을 localStorage에 AT_DOCUMENT_START로 주입. 기존 언어 설정 덮어쓰기 방지
JS ↔ Native 브릿지 — window.KFortune.onLogout() / window.KFortune.onAuthChange(token)
URL 감지 — WebView URL이 /login 포함 시 자동으로 네이티브 LoginPage로 전환
앱 플로우
main() → Firebase 초기화 + FCM 등록 + GA
↓
SplashPage (1.2s 페이드인)
↓
Remote Config 체크
→ 강제업데이트? → 스토어 이동
→ 서버 점검? → 점검 메시지
→ 공지사항? → 공지 모달
↓
저장된 토큰 검증 (/auth/profile)
→ 유효 → WebViewPage (myfortie.com/home)
→ 무효 → LoginPage (4가지 로그인)
↓
WebViewPage
→ AT_DOCUMENT_START에 auth JSON 주입
→ JS ↔ Native 핸들러 등록
→ FCM 토큰 자동 등록
→ /login 감지 시 네이티브 전환
ADMIN
관리자 대시보드 (React 19 + Vite)
관리자 - AI 상담 테스트 (GPT-4o-mini vs Claude 정답지 비교, 사주 데이터 기반)
관리 페이지 구성
대시보드 — KPI + 감정 비율 바 + 차트. 전체 서비스 현황 한눈에 파악
AI 테스트 — 상담 테스트(7턴 시뮬레이션), 예측 테스트, 프롬프트 테스트(사주/점성술 계산 + AI 해석 비교)
콘텐츠 관리 — 텍스트 DB CRUD + JSON 내보내기/가져오기, 질문 트리 시각화, 콘텐츠 카드 관리
CMS — 이벤트, 공지사항, FAQ, 법적 문서, 배너, 팝업, 문의 (TipTap 에디터)
운영 — 사용자 관리, 피드백 브라우저, 푸시 알림 발송, 앱 버전 설정, 탈퇴 사유 통계

관리자 - 배너 관리 (홈 캐러셀 배너 CRUD)

관리자 - 콘텐츠 카드 관리 (slug/경로/태그/뱃지/활성화 등)
관리자 기술 스택
React 19 + Vite — 빠른 빌드, React Router v7 기반 라우팅
Zustand + TanStack Query — 프론트엔드와 동일한 상태 관리 패턴
Recharts — KPI 대시보드 차트/그래프 시각화
TipTap 에디터 — CMS 콘텐츠 Markdown/HTML 편집. S3 + CloudFront 배포
FEATURES
주요 기능
1. 오늘의 운세 (/fortune/today)
2개 엔진 통합 응답 — 사주 정보(4주, 십성, 충/합, 신살, 용신, 납음) + 점성술(행성 위치, 트랜짓, 하우스, 달 위상)
AI 해석 — 핵심 요약, 행동 팁(해야 할 것/하지 말 것), 메타포 카드(사주 + 점성)
군중 통계 — 같은 사주의 사람들이 경험한 카테고리별 통계. 개인 vs 전체 비교
2. 3단계 피드백 시스템
원탭 (~3초) — 이모지 mood 1개로 빠른 기록. 기본 운세 잠금해제
간단 (~30초) — mood + 카테고리 1~3개 선택
상세 (~2분) — mood + 카테고리 + impact + 시간대 + 자유 텍스트
카테고리 — 재물, 연애, 커리어, 건강, 대인관계, 감정, 창의력, 학습, 신체 등
3. 개인 정확도 + 마일스톤 시스템
FIRST_PATTERN (7일) — 첫 개인 패턴 감지 (예: "충이 있는 날 변화 경험 43%")
CROWD_COMPARE (30일) — 개인 vs 전체 통계 비교
DEEP_PROFILE (90일) — 최고/최저 패턴 분석, 엔진별 맞춤도
PERSONAL_INSIGHTS (180일) — 운세에 "나의 패턴"이 반영되기 시작
기록 탭 - 월간 캘린더 히트맵 + 피드백 타임라인
4. Fortie에게 물어보기 (AI 상담)
7턴 멀티턴 대화 — 폼 기반 질문(text/select/multiselect) → AI 응답. GPT-4o-mini 파인튜닝 모델 사용
개인화 — 사용자의 상담/피드백/운세흐름 메타데이터 자동 수집 후 프롬프트에 주입
쿼터 시스템 — 주간 7회 + 보너스 크레딧. 금지 키워드 정규식 필터 (관리자 설정)
콘텐츠 탭 - Fortune Flow (재물/연애 3개월 흐름 분석)
5. 콘텐츠 허브
Fortune Flow — 재물/연애/커리어/건강 4카테고리, 3개월 90일 전통 사주 3단계 판정(good/neutral/caution) + LLM 분석
판타지 전생 직업 — 사주 입력 → 20개 직업 타입 + 오행 5스탯 + 희귀도 뱃지. 바이럴 공유 기능
썸 판별기 v4.0 — 일간 100조합 → 6등급 판정 + 풀 사주 교차 분석 + 11점 양극 게이지
내일 예측 — 오늘 피드백 기록 시 내일 예측 해금. LLM 기반 + 규칙 폴백
6. 글로벌 14개 언어 지원
14개 언어 — 한국어, 영어, 일본어, 프랑스어, 스페인어, 포르투갈어, 태국어, 베트남어, 인도네시아어, 힌디어, 독일어, 중국어(번체), 터키어, 아랍어
언어별 AI 모델 — 주요 언어별 GPT-4o-mini 파인튜닝 모델 개별 운용
텍스트 DB — 모든 UI 텍스트를 DB로 관리, I18nModule 인메모리 캐시, Accept-Language 헤더 기반 자동 전환
AI FINE-TUNING
AI 파인튜닝 파이프라인
Fortie의 AI는 단순한 프롬프트 호출이 아닙니다. Claude Sonnet으로 정답지를 생성하고, GPT-4o-mini에 파인튜닝하는 구조입니다.
V6 파인튜닝 파이프라인
Step 1 — 사주/점성술 엔진으로 다양한 생년월일의 데이터 생성
Step 2 — Claude Sonnet에 엔진 데이터를 넣고 고품질 해석(정답지) 생성
Step 3 — 정답지를 JSONL 형식으로 변환하여 GPT-4o-mini 파인튜닝
Step 4 — 품질 검증 후 프로덕션 모델 교체
파인튜닝 모델 종류
운세 해석 — 주요 언어별 개별 파인튜닝 모델
상담 — 멀티턴 상담 전용 파인튜닝 모델
예측 — 내일 예측 전용 모델. 엔진 데이터 + 사용자 메타데이터 기반
왜 Claude가 아닌 GPT-4o-mini로 파인튜닝?
Claude Sonnet의 해석 품질은 훨씬 높지만, API 비용과 응답 속도 때문에 프로덕션에서 매 요청마다 Claude를 호출하기 어렵습니다.
해결책: Claude가 만든 고품질 정답지를 GPT-4o-mini에 학습시켜, Claude 수준의 품질을 GPT-4o-mini 가격으로 서빙합니다.
관리자 대시보드의 프롬프트 테스트 페이지에서 GPT-4o-mini 파인튜닝 vs Claude 정답지를 나란히 비교할 수 있습니다.
TROUBLESHOOTING
기술적 도전과 해결
PROBLEM 01
2개 엔진의 결정론적 일관성 보장
사주 엔진과 점성술 엔진은 완전히 다른 체계입니다. 하나는 60갑자 기반의 동양 역학이고, 다른 하나는 Swiss Ephemeris 기반의 서양 점성술입니다. 이 두 결과를 하나의 일관된 해석으로 통합해야 했습니다. 동시에, 같은 입력이면 항상 같은 결과를 반환하는 결정론적 특성을 유지해야 합니다.
SOLUTION - 엔진 분리 + 해석 레이어 통합
각 엔진은 순수 수학 계산만 수행하고, AI 해석 레이어에서 두 결과를 통합합니다. 엔진 결과는 캐싱하여 동일 입력에 항상 동일한 결과를 보장합니다.
// fortune.service.ts
async getTodayFortune(userId: number) {
// 1. 사주 엔진 (결정론적 계산)
const sajuResult = this.sajuEngine.calculate(birthDate, birthTime, gender);
// 4주, 십성, 충/합, 신살, 용신 등
// 2. 점성술 엔진 (Swiss Ephemeris)
const stellarResult = this.stellarEngine.calculate(birthDate, birthTime, location);
// 행성 위치, 트랜짓, 하우스, 어스펙트
// 3. AI 해석 (파인튜닝 모델)
const interpretation = await this.interpretationService.interpret({
saju: sajuResult,
stellar: stellarResult,
userProfile: await this.getUserProfile(userId),
});
// 4. 군중 통계 병합
const crowdStats = await this.getCrowdStats(sajuResult.dayPillar);
return { saju: sajuResult, stellar: stellarResult, interpretation, crowd: crowdStats };
}
PROBLEM 02
GPT-4o-mini 파인튜닝 시 Temperature보다 중요한 것
파인튜닝 후에도 GPT-4o-mini가 비슷한 표현을 반복하거나, 특정 패턴에 고착되는 문제가 발생했습니다. Temperature를 올려도 다양성이 크게 개선되지 않았습니다.
SOLUTION - Frequency/Presence Penalty 튜닝
Temperature보다 Frequency Penalty와 Presence Penalty가 훨씬 효과적이었습니다. 같은 토큰의 반복을 억제하고, 새로운 토픽 도입을 장려하는 방식으로 다양성을 확보했습니다.
// interpretation.service.ts - OpenAI 호출 설정
const response = await this.openai.chat.completions.create({
model: this.getModelId(language), // 언어별 파인튜닝 모델
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: JSON.stringify(engineData) },
],
temperature: 0.7,
frequency_penalty: 0.3, // 같은 토큰 반복 억제
presence_penalty: 0.2, // 새로운 토픽 도입 장려
max_tokens: 2000,
});
PROBLEM 03
Flutter WebView에서 Zustand 인증 상태 주입 타이밍
Flutter 네이티브에서 로그인 후, WebView의 Zustand 스토어에 인증 정보를 넘겨야 합니다. 하지만 WebView가 로드되기 전에 localStorage에 데이터를 써야 하고, 기존 사용자의 언어 설정은 덮어쓰면 안 됩니다.
SOLUTION - AT_DOCUMENT_START 주입 + 선택적 머지
// WebViewPage.dart
initialUserScripts: UnmodifiableListView([
UserScript(
source: '''
(function() {
// 기존 언어 설정 보존
var existing = localStorage.getItem('language-storage');
var savedLang = null;
if (existing) {
try { savedLang = JSON.parse(existing).state.language; } catch(e) {}
}
// auth 상태 주입 (Zustand persist 포맷)
localStorage.setItem('auth-storage', '$authJson');
// 언어는 기존값 우선, 없으면 네이티브 값
if (savedLang) {
localStorage.setItem('language-storage',
JSON.stringify({state:{language: savedLang}, version: 0}));
}
})();
''',
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START,
),
]),
PROBLEM 04
개인 정확도 패턴 감지의 통계적 신뢰성
"충이 있는 날 변화를 경험한 비율 43%"라는 패턴을 보여줄 때, 데이터가 5건이면 의미가 없습니다. 통계적으로 유의미한 패턴만 노출해야 했습니다.
SOLUTION - Chi-square + Lift 기반 분석 모듈
데이터 분석 모듈에서 카이제곱 검정(Chi-square)으로 통계적 유의성을 확인하고, Lift 값으로 패턴의 강도를 측정합니다. 마일스톤별로 최소 데이터 수 임계값을 두어, 충분한 데이터가 쌓이기 전에는 패턴을 노출하지 않습니다.
// analysis.service.ts
analyzePattern(feedbacks: Feedback[], engineData: EngineResult) {
// 카이제곱 검정: 관찰값 vs 기대값
const observed = countByCondition(feedbacks, condition);
const expected = feedbacks.length * baseRate;
const chiSquare = Math.pow(observed - expected, 2) / expected;
// p-value < 0.05인 경우만 패턴으로 인정
if (chiSquare < 3.841) return null;
// Lift: 조건부 발생률 / 전체 발생률
const lift = (observed / conditionCount) / baseRate;
return { pattern, confidence: chiSquare, strength: lift };
}
PROBLEM 05
다국어 텍스트의 성능과 관리 편의성
14개 언어 x 수천 개 텍스트를 매번 DB에서 조회하면 성능 병목이 됩니다. 하지만 관리자가 수시로 텍스트를 변경할 수 있어야 합니다.
SOLUTION - I18nModule 인메모리 캐시 + 관리자 수동 리프레시
서버 시작 시 전체 텍스트를 인메모리에 로드하고, 관리자 대시보드에서 캐시 리프레시 버튼을 누르면 즉시 갱신됩니다. DB 쿼리 0회로 텍스트를 서빙합니다.
// i18n.module.ts
@Global()
@Module({})
export class I18nModule implements OnModuleInit {
private cache = new Map<string, Map<string, string>>();
async onModuleInit() {
await this.loadAll(); // 서버 시작 시 전체 로드
}
getText(domain: string, key: string, lang: string): string {
return this.cache.get(`${domain}:${key}`)?.get(lang) ?? key;
}
async refresh() { // 관리자 호출: POST /admin/texts/cache-refresh
this.cache.clear();
await this.loadAll();
}
}
PROBLEM 06
커스텀 차트를 라이브러리 없이 구현
ArcGauge, DonutChart, NatalChart(천궁도), WuxingBars(오행), EnergyMeter, CompassDiagram 등 다양한 데이터 시각화 차트가 필요했습니다. Chart.js나 D3를 쓰면 디자인 일관성이 깨지고 번들 크기가 커집니다.
SOLUTION - 순수 SVG + React 컴포넌트
모든 차트를 SVG path와 circle로 직접 구현했습니다. 글래스모피즘 디자인 토큰(컬러, 그라디언트, 반경)을 그대로 적용할 수 있고, 차트 라이브러리 의존성 0으로 번들 크기를 최소화했습니다. Framer Motion과 결합하여 차트 진입 애니메이션도 자연스럽게 처리합니다.
TECH STACK
기술 스택 총정리
백엔드 (fortune-api)
Runtime — NestJS 11.0, TypeScript, Node.js
Database — PostgreSQL 16, TypeORM (마이그레이션)
Auth — JWT, Passport (Google/Apple OAuth)
AI — OpenAI GPT-4o-mini (파인튜닝), Claude Sonnet (정답지)
Engine — 사주(자체 구현), Swiss Ephemeris 0.5.17 (점성술)
Push — Firebase Admin SDK (FCM)
Storage — Cloudflare R2 (S3 호환 CDN)
i18n — 14개 언어, I18nModule 인메모리
Test — Jest (유닛 + E2E)
웹 프론트엔드 (fortune-web)
Framework — Next.js 14 (App Router), TypeScript
State — Zustand (클라이언트), TanStack Query v5 (서버)
Style — Tailwind CSS 3.4 + 커스텀 디자인 토큰
Animation — Framer Motion 12.34, Lottie React 2.4
Icons — 커스텀 글래스모피즘 SVG
Charts — 커스텀 SVG (라이브러리 미사용)
Font — Pretendard Variable (CDN)
모바일 (fortune-app)
Framework — Flutter 3, Dart 3.10.4
WebView — flutter_inappwebview 6.1.5
Auth — google_sign_in 6.2.2, sign_in_with_apple 6.1.4
Push — firebase_messaging 15.2.5, flutter_local_notifications 19.2.1
Config — firebase_remote_config 5.3.0, firebase_analytics 11.5.2
관리자 대시보드 (admin-web)
Framework — React 19 + Vite
Routing — React Router v7
State — Zustand + TanStack Query
Charts — Recharts
Style — TailwindCSS 4
Editor — TipTap (Markdown/HTML)
인프라 / 배포
Backend — Docker 멀티스테이지 빌드 + Docker Compose
Frontend — S3 + CloudFront (CDN 배포)
Storage — Cloudflare R2 (cdn.myfortie.com)
Domain — myfortie.com (Web), api.myfortie.com (API)
CONCLUSION
결론
Fortie는 "운세를 보는 앱"이 아니라 "기록하는 앱"입니다. 사주/점성술 엔진의 결정론적 계산과, GPT-4o-mini 파인튜닝 모델의 자연어 해석, 사용자 피드백의 크라우드 통계를 결합하여 기록할수록 정확해지는 경험을 제공합니다.
기술적으로 가장 도전적이었던 부분은 2개 이질적 엔진의 통합과 AI 파인튜닝 파이프라인 구축이었습니다. Claude Sonnet으로 정답지를 만들고 GPT-4o-mini에 학습시키는 구조는, 품질과 비용 사이에서 실용적인 균형점을 찾은 결과입니다.
또한, Flutter 하이브리드 WebView 구조는 네이티브의 강점(인증/알림/강제업데이트)과 웹의 유연성(빠른 콘텐츠 업데이트)을 모두 취할 수 있는 전략이었습니다.
3개 레포지토리(백엔드 + 웹 + 모바일)와 관리자 대시보드까지 포함하면 풀스택의 모든 레이어를 한 사람이 설계하고 구현한 프로젝트입니다. 부족한 부분도 많지만, 사용자의 기록이 쌓여 서비스 자체가 진화하는 구조를 만들었다는 점에서 의미가 있었습니다.
긴 글을 읽어주셔서 감사합니다!
Fortie는 사주/점성술이라는 전통적인 영역에 AI 파인튜닝과 크라우드소싱 통계를 결합한 실험적 프로젝트입니다.
백엔드(NestJS), 웹 프론트(Next.js), 모바일(Flutter), 관리자(React) 4개 프로젝트를 풀스택으로 개발하면서 얻은 경험을 공유했습니다.
서비스에 대한 의견이나 피드백은 언제든 환영합니다!
