Joyuno's Dev Blog

2026-08-21 (금) 개발일지

dev-blog

오늘은 여러 프로젝트를 동시에 진행하면서 각각 다른 성질의 문제들을 마주쳤다. 사주 앱의 로컬 모델 호환성 검증부터 시작해서, UI/UX 기획안 재작성, 그리고 LG-CNS 레거시 코드 분석까지 — 각 이슈마다 처음 가설과 최종 결론이 달랐던 하루였다.

사주 앱 로컬 모델 실행 가능성 — M1 Pro 32GB는 “VRAM 없음”이 아니라 “통합 메모리”였다

M1 Pro 맥북에서 사주 앱 백엔드를 로컬 모델로 돌릴 수 있을지 검토해야 했다. 처음 질문은 단순했다: “램이 32GB인데 GPU VRAM은 없는 거 아닌가? 로컬 모델 돌릴 수 있나?” 직관적으로는 VRAM이 없으면 GPU 추론이 불가능할 거라고 생각했다.

하지만 실제로 확인해보니 Apple Silicon은 CPU와 GPU가 같은 메모리 공간을 공유하는 통합 메모리(UMA) 아키텍처였다. 별도 VRAM 칩이 없는 대신, 32GB 중 대략 24GB를 GPU가 자유롭게 쓸 수 있다는 뜻이었다. 오히려 일반 PC(VRAM 8GB + RAM 16GB)보다 유리한 상황이었다.

구성 GPU 가용 메모리
M1 Pro (32GB 통합) ~24GB (CPU와 공유)
일반 PC (RTX 4060) 8GB (VRAM만)
일반 PC (RTX 4090) 24GB (VRAM만)

그제서야 모델 선택이 명확해졌다. 8B급 모델(약 5GB 메모리 필요)이면 충분했고, 실제로 시스템에 0.5GB 정도 여유가 있었으니 EXAONE 4.0 32B(한국어 벤치마크 KMMLU-Pro 67.7점)나 Qwen3-Coder-30B(추론 속도 42.8 tok/s)를 후보로 삼을 수 있었다.

검증: 한국 LLM 벤치마크 데이터를 56개 소스에서 수집해 비교표를 만들었고, 최종 권장은 EXAONE 4.0 32B(한국어 성능 + 추론 능력) 또는 Qwen3-Coder-30B(속도 중심)로 정리됐다. 맥북 스펙이 충분하다는 확인이 나왔으니 이제 실제 모델 통합은 local_model_compat.py에서 진행하면 된다.

남은 과제: 모델 다운로드 후 백엔드 API(/health 엔드포인트는 이미 127.0.0.1:8010에서 정상 응답 중)와 통합하는 단계.


사주 UI/UX 기획안 재작성 — 텍스트만 있는 문서에서 실제 스크린샷 2단 레이아웃으로

기존 uiux_overview.html 기획안이 있었는데, 검토 결과 스펙 텍스트만 있고 실제 앱 화면 스크린샷이 없었다. 원본 기획안을 보니 좌측에 폰 목업 이미지, 우측에 스펙 텍스트(초록 번호 배지 + 한글명 + 영문명 + GOAL/COMPONENTS/INTERACTION/STATE 정보)가 2단으로 정렬돼 있었다.

처음에는 “텍스트 박스로 충분하겠지” 라고 생각했다가, 원본과 비교하면서 시각적 품질 격차가 명확했다. 단순 텍스트는 문서처럼 보이지만, 실제 앱 화면과 함께 있어야 “이게 진짜 앱 기획서다” 라는 신뢰가 생긴다는 걸 깨달았다.

시도 과정:

  1. 첫 번째: 텍스트 박스만 배치 → 원본과 비교해 부족함 인식
  2. 두 번째: 스크린샷 경로 매핑 시도 → 업로드 한도 초과로 실패 (9,681px 세로 이미지)
  3. 세 번째: 1600px 고정 폭 + 2단 레이아웃으로 재설계 → HTML/CSS 단일 파일로 완성

최종 해법: uiux_overview.html을 6개 섹션(표지 / 유저 플로우 / 화면 스펙 / 결제 모델 / 신뢰·안전 / 디자인 시스템)으로 재구성했다. 화면 스펙 섹션에는 31개 스크린샷을 모두 통합했고, 결제 지점(₩1,900)만 주홍색으로 강조해 “이번 업데이트의 핵심”을 시각적으로 드러냈다.

디자인 방향: 고급 한지·편집 디자인 톤. 표지는 먹빛 풀블리드에 주홍 緣 인장, 본문은 한지 크림 배경 + 명조 제목/Pretendard 본문의 절제된 2단 위계. 외부 리소스 없이 순수 HTML/CSS만 써서 어디서나 한 번에 열린다.

검증: 1600px 고정 폭에서 모든 텍스트가 overflow 없이 정렬되고, 결제 강조 노드(09)가 명확히 읽히는 걸 확인했다. Chrome headless로 세로 긴 PNG 렌더링도 준비 중.


LG-CNS 레거시 코드 분석 — GitLab 익명 접근 불가에서 로컬 AST 추출로 전환

terasys-rd/lgchem-app 저장소를 분석해야 했는데, 내부 GitLab 서버(http://<INTERNAL_IP>)에 익명 접근이 전부 막혀 있었다. /api/v4/version을 쳤더니 401 Unauthorized, 프로젝트 목록 API도 []만 반환했다. 로컬 자격증명(~/.config/glab-cli)도 없었다.

처음엔 “GitLab 접근이 막혀 있으면 분석 불가” 라고 생각했다. 하지만 저장소가 이미 로컬에 클론돼 있다는 걸 깨달았고, 읽기 전용으로 파일 시스템만 탐색해도 충분하다는 결론에 도달했다.

실행한 작업:

  1. AST 추출: 8개 문서 파일(TypeScript, JSON, Markdown)을 파싱해 256개 노드 / 526개 엣지 그래프 생성
  2. 의미 추출: 서브에이전트 2개를 병렬로 투입해 각 파일의 개념 계층 추출
  3. 학습 가이드 생성: LEARNING_GUIDE.md (23KB, Mermaid 다이어그램 6개 포함) — 이 프로젝트를 처음 보는 사람도 A부터 Z까지 따라갈 수 있도록 작성

발견한 버그 3건:

  • 🔴 usePagingQuery.ts:84 — 서브쿼리 폴링 setInterval ID를 지역변수에 담아서, 언마운트 정리(resetInterval)가 못 잡음. 필터를 쓰다가 메뉴를 옮기면 폴링이 계속 돌면서 메모리 누수 발생
  • 🟡 라우팅 이중 관리 — react-router를 안 쓰고 수동 상태 관리로 20개 화면을 조작 (의존성 최소화는 좋지만 유지보수 리스크)
  • 🟡 Logpresso 디자인 시스템 규약 이중화 — 구 규약(DESIGN.md 765줄) vs 신 규약(design.logpresso.com) 혼재

검증: git status가 빈 줄 — 저장소는 클론 직후 그대로 유지됐다. 읽기 전용으로만 작업했으니 커밋·푸시 없이 분석 산출물만 graphify-out/ 디렉토리에 남겼다.

산출물:

  • LEARNING_GUIDE.md — 23KB, 완전 학습 문서
  • MEMORY.md — 핵심 개념 메모
  • .graphify_chunk_01.json — 133개 노드, 183개 엣지, 3개 하이퍼엣지 그래프
  • GRAPH_REPORT.md — 감사 리포트

남은 과제: 버그 3건의 우선순위 정리 및 수정 계획 수립. 특히 폴링 누수는 성능 영향이 직접적이라 우선 처리 권장.


오늘의 한줄

가설이 틀렸을 때 재빠르게 방향을 바꾸는 게 중요했다 — M1의 “VRAM 없음”은 “통합 메모리”였고, GitLab 접근 불가는 로컬 파일 시스템 분석으로 우회했고, 기획안 텍스트만으로는 부족했을 때 스크린샷 2단 레이아웃으로 전환했다. 각 이슈마다 처음 직관과 최종 해법이 달랐다는 게 오늘의 패턴이었다.