맥북 로컬에서 매일 정보를 자동 수집해 **localhost 대시보드(탭형 게시판)**로 보여주는 서비스.
가격 대시보드 탭은 2026-08-01 제거됐다. 네이버 개발자센터의 검색 쇼핑 API 가 2026-07-31 부로 영구 종료(유예 기간 없음, 기존 키도 차단, 대체 API 미제공)되어 판매처별 가격 리스팅을 얻을 정식 경로가 사라졌다. 남은 수단은 robots·이용약관이 금지하는 크롤링뿐이라 기능을 접었다. 과거 가격 이력은 DB(
price_points/listings/reviews)에 그대로 보존돼 있다.
탭 구성:
0. Overview — 지난번 본 뒤 뉴스·유튜브·팝업·전시에 새로 들어온 것과 증시 한 줄을 추린 첫 화면(주소에 탭이 없으면 여기로 연다). 탭을 열고 3초 머물면 그 탭의 새 소식은 읽은 것으로 정리되고(읽음 기록은 이 브라우저 localStorage), 오늘 수집이 실패한 탭에는 탭 줄에 경고 점이 붙는다. 활성 탭은 주소(#news)에 남아 새로고침해도 유지된다.
- 팝업·전시 — 팝업스토어 + 서울/경기 전시·박람회(코엑스·세텍·킨텍스·수원컨벤션센터). 기본 매일 10:00 갱신, 이력 저장 없이 최신 스냅샷만 캐시.
- 뉴스 — 카테고리별 데일리 다이제스트. 기본 하루 2회(08:00·17:00).
- 유튜브 소식 — 채널·주제별 신규 영상 큐레이션.
- 증시 — 한국장(08:00 개장 전 프리뷰)·미국장(18:00) 브리핑. 지수·뉴스·등록 종목 분석 + 내 포지션 점검.
- 보유 주식 — 실제 보유 포지션(수량·평단가·목표가·손절가·투자기간·메모). 등록하면 관심 종목에 자동 포함되고, **시간당 실시간 취합(펄스)**의 영향도 판정 대상이 된다.
- 로또 — 매주 1회(기본 일요일 10:00) 자동으로 새 추첨 결과를 받아 지난주에 미리 확정해 둔 추천 번호를 채점하고, 그 결과로 전략을 갱신한 뒤 다음 회차 번호를 뽑는다. 화면은 다음 회차 예상 번호 + 지난 10회차 비교 결과뿐이다.
새 게시판은 web/src/App.tsx의 TABS 배열에 항목을 추가해 확장한다.
위 시각은 전부 기본값이다. 탭별 자동 실행 시각·요일·주기(매일/매주·격주·N주마다/매월·말일)· 켜기/끄기는 대시보드에서 바꿀 수 있다 → 아래 자동 실행 스케줄.
매주 1회(기본 일요일 10:00) cron 하나가 전부 한다(src/lotto/cycle.ts). 화면에는 조작 버튼이 없다.
요일·시각은 스케줄에서 바꿀 수 있지만 매주 1번만 되고, 토요일 20:00~24:00 은 금지다(아래 '실행' 행).
① 새 추첨 결과 수집 → ② 지난주에 못박아 둔 추천 번호 채점
→ ③ 결과를 에이전트에 전달해 전략 갱신
→ ④ 다음 회차 번호를 뽑아 추첨 전에 저장(다음 주 ②의 대상)
| 항목 | 내용 |
|---|---|
| 실행 | 스케줄 시각(기본 일요일 10:00) cron + catch-up(기동 직후·30분마다). 맥이 자고 있었으면 깨어난 뒤 보충한다. 토 20:00 |
| 데이터 | 동행복권 lt645/selectPstLt645InfoNew.do. 구 getLottoNumber API 는 WAF 로 차단되어 못 쓴다. srchDir=center 는 요청 회차를 중심으로 위아래 10건을 준다(실측) |
| 대조 | 당첨번호 = 본번호 6 + 보너스 1 = 7개 |
| 번호 확정 | 추첨 전에 lotto_upcoming 에 저장하고 절대 덮어쓰지 않는다. 이것이 없으면 화면을 열 때마다 번호가 달라져 '추천했던 번호와 비교'가 성립하지 않는다(전환 전의 실제 버그) |
| 목표 | 가중 회차 점수 = 1×[3+ 있음] + 2×[4+ 있음] + 4×[5+ 있음] + 8×[6개 있음] (회차당 0~15) |
| 대조군 | 회차 점수(적중 m개=+m, 0개=-6, 10세트 합)와 '3+ 세트 수'. 전략이 못 움직인다는 것이 1234회차로 확인됐다 |
| 전략 | 선언형 사양. 모델이 코드를 쓰지 않으므로 eval 이 없다 |
| 갱신 | 새로 채점한 회차가 있을 때만 에이전트를 부른다(주 1회). 프롬프트가 "표본 1로 사양을 흔들지 마라 · 유지가 정상" 을 명시한다 — 사용자가 반복 학습을 중단한 이유가 과잉 정합성이었다 |
| 모델 | Fable 5.1 고정 — 대시보드 공용 모델 설정을 따르지 않는다(통제 변인). v1~v174 는 Opus 5, v175 부터 Fable 5.1 — 세대 비교 시 이 경계를 확인할 것 |
| 토큰 한도 | 전략 갱신만 미루고 번호 추출은 직전 세대로 계속한다. 4시간 뒤 재시도(data/lotto-state.json) |
| 누락 회차 | 미리 못박아 두지 않은 회차는 채점하지 않는다 — 결과를 보고 번호를 고르면 예측이 아니다(로그만 남긴다) |
반복 학습(전 회차를 여러 바퀴 다시 도는 것)을 과잉 정합성 때문에 중단했다. 함께 사라진 것:
[실험 시작]/[일시정지]/[회차 동기화]/[초기화]/[실험 반복] 버튼, 상태 머신(idle/running/paused/done),
세대 이력·바퀴별 성적표, 회차 점수·누적 점수·가중 회차 점수 추이 차트.
백테스트로 쌓인 8바퀴 × 1234회차 기록은 지우지 않았고(run 스코프로 남아 있다), 라이브 예측은
마지막 바퀴를 이어서 쌓는다.
처음 목표는 '예측 정확도'(점수)였다. 실데이터 1234회차로 검정한 결과 그 축에는 올릴 것이 없다는 것이 확인됐다:
| 검정 | 결과 |
|---|---|
| 번호별 출현 빈도 균등성 | χ²=33.41 (df=44), p=0.88 — 기계·볼 편향 없음 |
| 전반 617회차 vs 후반 동질성 | p=0.23 |
| 직전 회차 번호의 재출현 | 16.09% vs 15.46% (기대 15.56%), z=1.50 — 회차 간 의존성 없음 |
| 엔진 특징 6종의 예측 AUC | hot 0.4983 / cold 0.5017 / overall 0.4961 / gap 0.5002 / decay 0.4988 / pair 0.5011 — 전부 0.50 |
| 합·홀짝·연속번호 분포 | 전부 이론값과 일치 |
점수의 기대값은 어떤 전략을 써도 -11.003 으로 고정이다. 고정된 6개 집합이 맞힐 개수의 분포는 그 집합이 무엇이든 같은 초기하분포이고, 기대값은 선형이기 때문이다.
3+ 를 달성한 세트의 총 개수도 같은 이유로 고정이다(회차당 0.394개). 하지만 그 적중이 몇 개의 서로 다른 회차에 흩어지는가는 디자인이 정한다 — 그래서 이 비율만이 최적화 가능하다.
| 구조 | 3+ 적중률 |
|---|---|
| 무작위 10세트 (기준선) | 33.36% |
| 45개 전부 커버 | 35.8% |
| 45개 전부 커버 + 세트간 중복 ≤1 | 36.1% |
| 국소탐색 최적해 (천장) | 36.43% |
| 풀을 좁힘(휠링 12~21개) | 12~27% — 오히려 나쁨 |
| 극단 집중 | 5.6% |
최적 구조는 45개를 전부 덮으면서 어떤 두 세트도 번호를 1개까지만 공유하는 것이다(그때 10세트가 덮는 서로 다른 3조합이 이론 최대인 200개가 된다). 이 값들은 40만 회 홀드아웃 표본으로 측정했다 — 탐색에 쓴 표본으로 평가하면 39.3% 같은 과적합된 숫자가 나온다(실측). 엔진이 표본을 두 벌 쓰는 이유가 이것이다.
탭마다 켜기/끄기 + 반복(어느 날) + 시각(그날 몇 시) 를 정한다. 의미 규칙의 정본은
docs/customization-contract.md §1, 계산은 shared/schedule.ts(서버·화면 공용)다.
| 잡 | 반복 | 시각 | 기본값 |
|---|---|---|---|
| 팝업·전시 / 뉴스 / 유튜브 / 증시(한국·미국) | 매일 · 매주(요일, 1~4주마다) · 매월(날짜, 말일) | 최대 24개 | .env 시각, 매일 |
| 증시 펄스 | 같음 | 최대 48개 | STOCK_PULSE_TIMES, 매일 |
| 스킬 랭킹 | 매주(요일 1개, 1~4주마다) · 매월(날짜 1개) | 1개 | 매주 월요일 09:00 |
| 로또 | 매주(요일 1개) | 1개 | 매주 일요일 10:00 (토 20:00~24:00 금지) |
- 저장:
data/schedule.json— 바꾼 잡만 기록하고 나머지는.env를 따른다. 예전 형식(잡 → 시각 배열)도 그대로 읽는다(매일로 해석). 기본값과 같은 값을 저장하면 그 잡은 다시.env를 따른다. - 반영: 저장 즉시 cron 재등록(재시작 불필요). 꺼진 잡은 정시 실행·누락 보충·가동 중단 누락 집계가 모두 멈추고, '지금 갱신'(수동)은 그대로 된다.
- 누락 보충: 30분마다 '마지막 예정 시각'이 정상 결과로 커버됐는지 본다. 그 시각이 이전 날이면 오늘이 실행일이 아닐 때만 보충한다(매일 잡은 어제 슬롯을 보충하지 않는 기존 동작 그대로). 증시 브리핑은 날을 넘겨 보충하지 않고, 펄스는 보충이 없다.
curl -s localhost:7777/api/schedule | jq '.schedule.news, .next' # 현재값·다음 실행
curl -s -X PUT localhost:7777/api/schedule -H 'content-type: application/json' \
-d '{"news":{"enabled":true,"repeat":{"kind":"weekly","days":[1,2,3,4,5],"intervalWeeks":1},"times":["08:00","17:00"]}}'
# 검증 실패는 400 {"error":"<한국어 사유>"} — 아무것도 저장하지 않는다- events/ news/ youtube/ stock/ lotto/ 탭별 수집·큐레이션
- scheduler/ in-process 정시 수집(스케줄 v2: 매일 cron + 실행일 게이트) + 잠자기/재시작 누락 catch-up + 보존 정책(매일 04:30)
- api/ 탭별 조회·갱신 엔드포인트 · 프론트 정적 서빙
- web/ React+TS+Vite+shadcn+Tailwind+recharts 대시보드
- service/ launchd 등록/제거 스크립트
기술 스택: 단일 TypeScript 프로젝트, 저장소는 Node 내장 node:sqlite(네이티브 빌드 불필요).
- macOS (launchd 서비스 등록 기준) / Node.js 18 이상 (
node -v로 확인, 권장 20+) - (선택) 네이버 개발자센터 검색 API 키 — 팝업·전시 탭의 webkr/blog 보강용. https://developers.naver.com/
- (선택) Anthropic Console API 키 — Opus 5.5 큐레이션용
@anthropic-ai/claude-agent-sdk0.3.280 이상 —Opus 5.5는 번들 Claude Code 2.1.280+ 를 요구한다 (0.1.x 는 HTTP 400claude_code_version_too_old).npm install로 package.json 의 범위를 따라간다- Codex CLI 0.159.1 이상 + ChatGPT 로그인 — 기본 모델(Codex 6.1 Sol) 이 정액제 크레딧을 쓴다.
구버전 CLI 는
gpt-6.1-sol에 HTTP 400(not supported when using Codex with a ChatGPT account)을 돌려주고, 이 실패는 한도/로그인 신호가 아니라 임시 대체가 걸리지 않는다 →npm install -g @openai/codex@latest - (선택) Python 3 — 팝업/전시 날짜 검증(insane-engine)용.
bash tools/insane-engine/setup.sh1회 실행 - 별도 DB 설치 불필요 (Node 내장 SQLite 사용)
npm install && npm install --prefix web # 1) 의존성 설치
cp .env.example .env # 2) .env 에 키 입력
bash tools/insane-engine/setup.sh # 2-1) (선택) 날짜 검증 엔진 venv 준비
npm run web:build # 3) 프론트 빌드
npm start # 4) http://localhost:7777 접속| 스크립트 | 설명 |
|---|---|
npm install |
백엔드 의존성 설치 |
npm install --prefix web |
프론트(web) 의존성 설치 |
npm run web:build |
프론트 빌드 → web/dist 생성 (서버가 이걸 서빙) |
npm run build |
web:build 별칭 |
npm start |
백엔드+스케줄러 기동 (프론트 dist 정적 서빙) |
npm run dev |
백엔드 watch 모드 |
npm run web:dev |
프론트 Vite dev 서버(핫리로드) |
npm run typecheck |
백엔드 타입체크 |
npm install # 백엔드 의존성
npm install --prefix web # 프론트 의존성 (install.sh가 자동 빌드도 함)
cp .env.example .env # 키 입력 (이미 .env 가 있다면 생략)| 키 | 필수 | 설명 |
|---|---|---|
NAVER_CLIENT_ID / NAVER_CLIENT_SECRET |
팝업·전시에 필수 | 네이버 검색 API(webkr/blog). 팝업·전시 원본 코퍼스 = LLM 씨앗 + 링크 진실원 + 폴백. 없으면 그 탭이 빈 화면이 된다(다른 탭은 무관) |
ANTHROPIC_API_KEY |
선택 | Opus 5.5 큐레이션용. Codex 선택 시에는 불필요 |
EVENTS_VERIFY_DATES / INSANE_* |
선택 | 팝업/전시 날짜 검증(insane-engine). 차단된 실제 페이지를 우회 fetch 해 종료된 행사를 제외. 끄려면 EVENTS_VERIFY_DATES=false(대시보드 설정에서도 변경 가능, 대시보드 값 우선). 자세한 항목은 .env.example/tools/insane-engine/README.md |
PORT |
기본 7777 | 대시보드/API 포트 |
EVENTS_COLLECT_TIME · NEWS_COLLECT_TIMES · YOUTUBE_COLLECT_TIMES · STOCK_KR/US_COLLECT_TIMES · STOCK_PULSE_TIMES |
기본 10:00 · 08:00,17:00 · 08:30 · 08:00 / 18:00 · 장중 매시 | 탭별 자동 수집 시각(로컬). 대시보드 설정에서도 변경 가능(대시보드 값 우선) — 화면에서는 요일·격주·매월까지 |
YOUTUBE_FRESH_DAYS / YOUTUBE_WATCHED_EXCLUDE_DAYS |
기본 7(권장 3) / 7 | 유튜브 신선도(최근 N일 게시만) / '본영상' 제외 일수. 대시보드 설정에서도 변경 가능(대시보드 값 우선) |
EVENTS_NEW_GAP_DAYS / EVENTS_NEW_SHOW_DAYS |
기본 7 / 3 | 팝업·전시 '신규' 판정 공백 / 배지 유지 일수. 대시보드 설정에서도 변경 가능(대시보드 값 우선) |
STOCK_PULSE_THRESHOLD · _PER_SYMBOL_CAP · _DAILY_CAP · _MACRO_CAP · _DEDUP_HOURS · _QUIET_START · _QUIET_END |
기본 important · 3 · 10 · 2 · 24 · 23 · 7 | 펄스 문자 문턱·하루 상한·중복 억제·조용 시간(가동 중단 알림도 같은 조용 시간). 대시보드 설정에서도 변경 가능(대시보드 값 우선) |
HISTORY_RETENTION_DAYS |
기본 90 | 증시 시계열·펄스 원장 보존 일수. 대시보드 설정에서도 변경 가능(대시보드 값 우선) |
NEWS_TIMEOUT_MS · YOUTUBE_TIMEOUT_MS · EVENTS_TIMEOUT_MS · STOCK_BRIEF_TIMEOUT_MS · STOCK_PULSE_TIMEOUT_MS · SKILLS_SUMMARY_TIMEOUT_MS · SKILLS_SUMMARY_BUDGET_MS |
기본 20 · 20 · 20 · 15 · 10 · 5 · 20분 | 탭별 LLM 1회 최대 대기(ms). 대시보드 설정에서도 변경 가능(대시보드 값 우선, 분 단위, 다음 실행부터) |
NOTIFY_EMAIL / GMAIL_ADDRESS / GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
선택 | 이메일 리포트 알림 전역 스위치·발신 계정·OAuth 클라이언트(.env 에서만) |
NOTIFY_IMESSAGE / IMESSAGE_TO |
선택 | iMessage 알림 전역 스위치·수신자(.env 에서만) |
알림 자격증명이 없으면 이메일 알림만 경고 후 건너뛰고, 수집/대시보드는 정상 동작한다.
메일 미리보기:
npm run mail:preview는 발송 없이 가상 데이터로 메일 5종(+증시 휴장)을 렌더해data/mail-preview/index.html을 연다(데스크톱 720px·아이폰 390px 나란히). 지금 서비스에 있는 실데이터로 보려면npm run mail:preview -- --live(읽기 전용 GET 만 쓴다), 브라우저를 열지 않으려면-- --no-open.대시보드 값 우선: 위 표에서 '대시보드 설정에서도 변경 가능' 항목은 탭별 '자동 수집·설정' 버튼 또는 헤더 '설정'에서 바꿀 수 있고, 저장한 값(
data/tab-settings.json, 스케줄은data/schedule.json)이.env보다 우선한다. 저장 즉시 반영된다(재시작 불필요, 타임아웃은 다음 실행부터). 바꾼 항목만 저장되고 나머지는 계속.env를 따른다 — '기본값으로 되돌리기'는 그 항목을 다시.env로 돌린다. 탭별 알림 스위치는 전역 스위치(NOTIFY_EMAIL/NOTIFY_IMESSAGE+IMESSAGE_TO)와 AND 다: 전역이 꺼져 있으면 탭에서 켜도 나가지 않는다. 비밀값·경로·포트·수신자는.env에서만 바꾼다.AI 에이전트 선택: 대시보드 상단에서
Codex 6.1 Sol(기본) ·Opus 5.5중 고른다. Codex는codex login status가Logged in using ChatGPT일 때만 실행하며,OPENAI_API_KEY/CODEX_API_KEY를 전달하거나 API 종량 과금으로 자동 전환하지 않는다.실행마다 Codex 확인 → 임시 대체: Codex를 고른 상태에서 수집을 실행할 때마다 Codex를 먼저 확인한다. 로그인이 초기화됐거나(
codex login status가 ChatGPT 로그인이 아님), CLI가 없거나, 정액제 한도가 소진됐으면(You've hit your usage limit,Quota exceeded,429등) 그 실행만 남은 시간 안에서Opus 5.5로 즉시 재시도해 결과를 살린다. 대시보드에 저장된 선택은 그대로 유지되고, 대체 중이라는 사실만 모델 버튼에 배지로 남는다. 복귀에 정해진 시각은 없다 — 다음 실행에서 Codex가 다시 응답하는 순간 배지가 풀리고 그대로 Codex로 돌아간다. 프롬프트 오류·네트워크 오류 같은 '진짜 실패'는 대체하지 않고 그대로 실패한다 (대체로 넘기면 Codex 경로의 버그가 Opus 실행에 묻힌다).카카오 알림 안내: 카카오 "나에게 보내기"(PlayMCP MemoChat)는 OAuth 인증이 필요한 MCP라, 무인으로 도는 launchd 백그라운드 서비스에서는 직접 호출할 수 없다. 카카오 알림이 필요하면 PlayMCP가 연결된 claude.ai/Claude Code 측 스케줄 에이전트가 대시보드(
/api/runs/today)를 읽어 메모를 보내는 방식으로 구성해야 한다. (대시보드가 1차 출력, 알림은 부가)
npm run web:build # 프론트 빌드 → web/dist 생성
npm start # http://localhost:7777npm run web:build는 내부적으로tsc --noEmit(타입체크) →vite build를 수행한다.npm start는web/dist가 있으면 그대로 서빙하고, 없으면 빌드 안내 페이지를 띄운다. (빌드를 깜빡해도 API는/api/*에서 정상 동작한다.)
npm run dev # 터미널1: 백엔드 + 스케줄러 (파일 변경 시 자동 재시작)
npm run web:dev # 터미널2: 프론트 dev 서버(5173) — /api 는 7777로 프록시→ 브라우저에서 http://localhost:5173 접속.
.env에서 변경 후 재시작:
PORT=8080로그인 시 자동 기동 + 상시 유지 + 탭별 정시 자동 수집:
./service/install.sh # plist 생성·로드 (프론트 미빌드 시 자동 빌드)
./service/uninstall.sh # 제거 (DB/로그는 보존)로그: logs/stdout.log, logs/stderr.log
curl -X POST localhost:7777/api/events/refresh # 팝업/전시 지금 갱신
curl -X POST localhost:7777/api/news/refresh # 뉴스 지금 갱신
curl -X POST localhost:7777/api/youtube/refresh # 유튜브 지금 갱신
npx tsx src/cli.ts config # 현재 설정·경고 확인| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/events |
최신 팝업/전시 스냅샷 |
| POST | /api/events/refresh |
팝업/전시 지금 갱신 |
| GET | /api/stock/:market |
최신 증시 브리핑 스냅샷 (kr|us) |
| GET | /api/stock/holdings |
보유 종목 + 현재가·손익 (시세 조회로 느림) |
| GET | /api/stock/holdings/raw |
보유 종목 원장만 (빠름) |
| POST | /api/stock/holdings |
보유 종목 추가 (등록 종목에 자동 포함) |
| PATCH | /api/stock/holdings/:id |
보유 종목 수정 (null=해제, 키 생략=유지) |
| DELETE | /api/stock/holdings/:id?confirm=<심볼> |
보유 종목 삭제 (등록 종목은 보존) |
| GET | /api/stock/pulse/status |
펄스 상태 + 최근 24시간 판정 목록 |
| POST | /api/stock/pulse/run |
펄스 지금 실행 (백오프 무시) |
| GET | /api/lotto |
다음 회차 예상 번호 + 지난 10회차 비교 결과 |
| POST | /api/lotto/cycle |
주간 사이클 수동 실행 (운영·검증용, 화면에 버튼 없음. 응답 즉시) |
| GET | /api/settings |
탭별 운영 설정 TabSettingsResponse — 현재값(대시보드 값 우선)·.env 기본값·숫자 범위·탭별 알림 채널·전역 채널 상태 |
| PUT | /api/settings |
운영 설정 부분 수정(DeepPartial<TabSettings>). 바꾼 필드만 저장하고 기본값과 같으면 다시 .env 를 따른다. 범위·타입 오류는 400 {error}. 조용 시간을 바꾸면 펄스 보류분 발송 cron 을 즉시 재등록 |
- catch-up: 기동 직후 + 30분마다 점검, 예정 시각이 지났고 오늘 성공 수집이 없으면 1회 보충.
- 보존 정책: 매일 04:30 에
HISTORY_RETENTION_DAYS초과 증시 시계열·펄스 원장을 정리한다. (예전에는 가격 수집이 끝날 때 돌았고, 가격 탭 제거 후 독립 cron 으로 옮겼다. 보존 아카이브가 된 가격 표는 정리 대상에서 뺐다.) - 날짜: 모든 "오늘" 판정은 로컬(KST) 기준.
- DoD: 백엔드/프론트 타입체크 통과 · 로컬 동작 · 최소 테스트.
| 증상 | 원인 / 해결 |
|---|---|
| 대시보드에 "프론트가 아직 빌드되지 않았습니다" | npm run web:build 실행 후 새로고침 |
| 모델 버튼에 "→ Opus 5.5 임시 대체" 배지 | Codex를 지금 못 쓰는 상태(로그인 초기화 / 정액제 한도 소진). 배지 안에 사유·시작 시각이 있다. 선택은 Codex 그대로이고, 다음 수집에서 Codex가 응답하면 저절로 풀린다. 로그인이 원인이면 codex login 후 다음 수집을 기다리거나 "지금 갱신"을 누르면 된다 |
| Codex 수집이 로그인 오류로 실패 | codex login 실행 후 codex login status가 Logged in using ChatGPT인지 확인. API 키 로그인은 의도적으로 거부. 로그인이 풀린 동안에도 수집은 Opus 5.5로 대체 실행되므로 결과가 비지는 않는다 |
ExperimentalWarning: SQLite ... |
Node 내장 SQLite 경고(무해). 서비스는 NODE_NO_WARNINGS=1로 숨김 |
| 포트 충돌 | .env의 PORT 변경 후 재시작 |
| launchd 서비스 상태 확인 | launchctl print gui/$(id -u)/com.daegun.dailyprice | head / 로그: logs/stderr.log |