제품 생태계 소개와 온보딩 교육 자료 : 내려받아 외부에 공유
이 문서는 core의 코드 구현이 아니라 AI 하네싱 관점의 동작 원리를 다룹니다. core가 왜 존재하고, 무엇을 표준화하며, 무엇이 자동으로 굴러가는지가 주제입니다.
standarda-core는 LangChain 위에 얇게 얹은 의견 있는 표준 계층입니다.
핵심은 영리한 코드가 아니라, 모든 프로젝트가 같은 문으로 LLM·구글 API에 접근한다는 사실입니다. 그래서 프로젝트마다 제각각 다시 구현하다 어긋나는 일이 없어집니다.
core가 제공하는 능력은 여섯 가지입니다.
각 능력에서 무엇이 자동이고 무엇이 직접 켜야 하는지를 먼저 표로 못 박습니다. 이 구분이 core를 가장 헷갈리게 하는 지점입니다.
| 능력 | 자동(무설정) | 직접(opt-in) |
|---|---|---|
| LLM 접근 | 모델·온도·키 env 기본값, 키 없으면 None, 추론모델 온도 quirk 자동 처리 | 프로바이더(팩토리) 선택 |
| 관측(트레이싱) | 트레이싱 env가 있으면 LangChain이 자동 적용 | env 설정 + core 우회 금지 |
| 비용·지연(캐싱) | tool-loop 안에서는 캐싱 기본 ON | 그 밖에선 캐싱 헬퍼·비용 집계 직접 호출 |
| 문서 파싱 | (없음) | 파서 서브에이전트가 방식 결정 |
| 응답 처리 | (없음) | parse_json_from_llm 호출 |
| 구글 API | 토큰 갱신·재인증·스코프 검사 | credentials 준비 |
core의 LLM 팩토리는 어느 프로바이더든 동일한 시그니처로 준비된 모델을 돌려줍니다.
get_anthropic_llm() · get_openai_llm() · get_google_llm() 은 각각 LangChain의 ChatAnthropic·ChatOpenAI·ChatGoogleGenerativeAI를 감쌉니다.
호출부는 팩토리만 바꾸면 프로바이더를 교체할 수 있고, 돌려받는 객체는 어느 쪽이든 .invoke() 되는 LangChain 모델입니다.
기본으로 녹아 있는 것:
None을 돌려줘, 호출부가 널 체크로 처리합니다.get_openrouter_llm() 은 키 하나로 여러 회사 모델을 조합하고, 실제 청구 비용까지 응답에 실어올 수 있습니다.LangSmith 트레이싱은 core가 무언가를 설정해서가 아니라, core가 항상 LangChain 객체를 돌려주기 때문에 붙습니다.
core 안에는 트레이싱 설정 코드가 한 줄도 없습니다. 대신 (1) 팩토리가 늘 LangChain 모델을 돌려주고, (2) 프로젝트 환경에 트레이싱 env가 있으면, LangChain 런타임이 그 호출을 알아서 트레이스에 실어 보냅니다.
그래서 core를 우회하면 트레이스가 조용히 사라집니다.
직접 anthropic.Anthropic() 같은 SDK 클라이언트를 만들면 LangChain을 거치지 않으니 관측에서 빠지고, 온도 quirk·캐싱·비용 로직도 각자 다시 구현하게 됩니다.
"모든 LLM 호출은 core로"라는 규칙의 강제 이유가 바로 이 관측 일관성입니다.
같은 프리픽스를 반복 호출할 때, core의 캐싱 헬퍼로 입력 토큰 재청구와 지연을 줄입니다.
cached_system_message · with_cache_control 로 system 블록에 캐시 마커를 붙이면, 캐시 읽기는 입력가의 약 0.1배로 떨어집니다(쓰기는 약 1.25배).track_usage() 로 감싸고 record_usage() 를 부른 뒤 estimate_cost_usd() 로 추정합니다. core가 자동으로 집계하지는 않습니다.core는 형식마다 저렴한 텍스트 경로와 정확한 비전 경로를 나눠 두지만, 둘 중 무엇을 쓸지는 사람이 눈대중으로 고르지 않습니다.
방식 선택은 파서 서브에이전트(pdf-parser · excel-parser)가 샘플로 품질을 테스트해 결정합니다. 로직 흐름은 이렇습니다.
Excel에는 한 갈래가 더 있습니다. 컬럼 인덱스가 고정된 정형 데이터는 LLM 없이 openpyxl로 바로 읽습니다(가장 저렴).
어느 경로든 LLM은 함수 안에서 만들지 않고 인자로 주입해, 호출부가 프로바이더를 고릅니다.
LLM 응답에서 JSON을 꺼낼 때는 parse_json_from_llm 하나만 씁니다.
이 함수는 마크다운 코드펜스로 감싼 JSON, 산문 속에 박힌 {…}, 순수 JSON을 모두 처리합니다.
프로젝트마다 펜스를 문자열로 잘라내던 코드를 복붙하다 조금씩 어긋나던 것을, 하나의 정본으로 통일한 것입니다.
Gmail·Sheets·Drive·Docs 네 클라이언트가 하나의 인증 믹스인을 공유합니다.
DEFAULT_SCOPES 한 곳이 단일 소스입니다.Credentials 객체를 주입할 수 있고, 이때 토큰은 메모리에서만 갱신되고 디스크에 쓰지 않습니다.여섯 능력을 관통하는 두 원칙이 core의 나머지를 설명합니다.
단일 출처(single source). JSON 추출도, 구글 스코프도, 캐싱 헬퍼도 하나뿐입니다. 프로젝트들이 예전엔 각자 복붙해 두고 어긋났는데, core가 정본을 쥐면 그 드리프트가 사라집니다.
고정 git 태그로 설치(editable 금지). core는 특정 태그(git+...@vX.Y.Z)로 설치합니다. editable 설치는 한 곳을 고치면 그 core를 공유하는 모든 프로젝트에 조용히 번져 금지입니다.
업데이트는 자동이 아닙니다. 새 태그가 나와도 각 프로젝트가 자기 requirements.txt의 태그를 직접 올려 재설치할 때까지 기존 버전에 머뭅니다. 그래서 하위호환이 하드 룰입니다.
새 standarda 프로젝트는 standarda-template(cookiecutter)로 찍어내고, 그 생애는 standarda-devtools가 관리합니다. 이 문서는 코드가 아니라, 프로젝트가 태어나고(생성) → 무엇을 갖고 태어나며(하네스) → 어떻게 일하는가(개발) 의 흐름을 따라갑니다.
프로젝트의 생성·복제·삭제는 standarda-devtools가 담당합니다.
devtools는 template을 소비하는 별도의 메타 저장소입니다. 그 안에서 Claude Code를 켜면, 실행한 사용자의 홈에 프로젝트를 만들고 정리합니다.
| 단계 | 자동화하는 것 |
|---|---|
| create | 슬러그 도출 · dev+prod 포트 2개 할당(위키 포트 표) · cookiecutter 생성 · GitHub repo · 메모리 심링크. 대화형 setup.sh는 사람에게 인계 |
| clone | 기존 repo를 표준 레이아웃으로 clone · 메모리 심링크 · (Django면) venv·의존성 |
| delete | 파괴적 3단계(조회 → 사용자 확인 → 삭제): 포트 프로세스 · DB · 위키 포트 행 · Apache vhost · 인증서 · Route53 레코드 · GitHub repo · 프로젝트/정적 디렉토리 정리 |
관계는 깔끔합니다. template = 프로젝트가 무엇인가, devtools = 그 생성·복제·삭제입니다.
create의 핵심인 프로젝트 생성 순간, 사람 손 없이 자동으로 벌어지는 일들입니다.
ALLOWED_HOSTS에 주입(어느 박스에서 만들어도 하드코딩 없이 그 호스트가 들어감).git init + 원격 등록 + (인증돼 있으면) 비공개 GitHub repo 생성.core.hooksPath 설정).이어지는 대화형 setup.sh(사람이 실행)에서는 venv·의존성 설치와 함께 랜덤 시크릿 키를 gitignore된 .env에 생성하고(시크릿은 커밋 안 됨), DB 비밀번호를 실제 psql로 검증한 뒤 DB·마이그레이션·슈퍼유저를 만듭니다.
프로젝트는 만들어지는 순간 세 가지 하네스(스킬·에이전트·훅)를 물려받습니다. 셋은 서로 포함하는 계층이 아니라 역할이 다르고, 관계가 정해져 있습니다.
| 층 | 무엇 | 역할 |
|---|---|---|
| 스킬(skills) | /develop·/release·/dev 등 |
이름 붙은 작업 절차(슬래시 또는 자연어로 호출) |
| 서브에이전트(agents) | spec-verifier·debug-server-log 등 |
별도 컨텍스트 + 도구 제한으로 위임되는 검증·조사 |
| 훅(hooks) | Claude 도구 훅 + git 훅 | 규칙을 사람이 안 지켜도 자동 차단하는 강제 장치 |
관계를 정리하면 이렇습니다. 스킬이 에이전트를 호출(위임) 합니다(예: /develop이 spec-verifier·merge-master를 부른다). 에이전트는 스킬을 부르지 않습니다. 훅은 누가 부르는 게 아니라 도구 호출·git 동작 시점에 자동 발동해 위반을 막는 가드레일입니다. 즉 스킬→에이전트만 호출 관계이고, 훅은 이벤트로 동작하며, 셋은 서로 포함하지 않습니다.
프로젝트가 기본으로 갖는 스킬을 용도별로 정리하면 이렇습니다.
| 용도 | 스킬 | 역할 |
|---|---|---|
| 개발 워크플로우 | /develop |
모든 코드 변경의 기본 절차(스펙→구현→검증→문서→머지) |
/dev |
개발 서버 기동 + 첫 실행 시 포트·도메인 자동 세팅 | |
/release |
프로덕션 배포 트리거 | |
| 문서 | /feature-doc |
기능 문서 |
/structure-doc |
구조 문서 | |
/update-skill-agent-list |
스킬·에이전트 목록 재생성 | |
| 고객 산출물 | /client-usage-guide |
사용 가이드(md + Word) |
/client-test-guide |
테스트 체크리스트 | |
/manual |
스크린샷 슬라이드·PDF 매뉴얼 | |
/create-docx |
스타일 Word 생성 | |
| 템플릿 동기화 | /sync-from-template |
템플릿 → 프로젝트 반영 |
/sync-to-template |
프로젝트 공통 파일 → 템플릿 역류 |
스킬이 부르는 위임 자산들입니다. 대부분 읽기 전용이라 코드를 건드리지 않고 조사·검증만 합니다.
| 에이전트 | 역할 |
|---|---|
spec-verifier |
구현이 docs/specs/{app}.md 스펙과 맞는지 검증(어긋나면 머지 차단) |
merge-master |
스펙·문서 체크리스트·충돌 점검 후 머지·정리 |
debug-server-log |
디버깅 시 서버 로그를 먼저 읽어 traceback을 찾는 강제 선행 단계 |
lesson-finder |
팀 위키 교훈 색인에서 관련 교훈 조회(다른 프로젝트 실수 반복 방지) |
pdf-parser · excel-parser |
샘플로 추출 품질을 시험해 텍스트/비전 전략 결정 |
api-tester · output-verifier |
API 테스트 · 기록 결과를 소스와 대조 검증 |
llm-usage-doc · backlog-manager |
LLM 사용 인벤토리 · 백로그 관리 |
규칙은 사람이 잊어도 훅이 자동으로 막습니다. 두 층이 독립적으로 겹쳐 있습니다.
1층: Claude 도구 훅(Claude가 도구를 부르기 직전 PreToolUse에 발동)
| 훅 | 무엇을 막나 |
|---|---|
| 버그수정 이슈문서 | 커밋 메시지에 버그 키워드가 있는데 docs/issues/ 문서가 없으면 차단 |
| 기능 문서 | 기능 키워드 + 앱 소스 변경인데 docs/specs 또는 docs/features 없으면 차단 |
| except 로거 | except 블록에서 traceback을 버리는 logger.error/warning 신규 추가를 AST로 차단 |
| 스킬 목록·설명 | 스킬·에이전트 변경 시 목록 갱신 없으면, 또는 SKILL.md 설명이 너무 짧으면 차단 |
2층: git 훅(실제 git 동작에서 발동, Claude 훅을 우회해도 잡는 backstop)
| git 훅 | 강제 |
|---|---|
| commit-msg · pre-commit | 버그수정 커밋은 이슈 문서, 스킬 변경은 목록 갱신 포함 |
| pre-push | 지정된 원격(자기 repo) 외 push 차단 |
| reference-transaction · pre-rebase | main은 fast-forward 전용, main rebase 차단 |
git 훅은 작업 트리 바깥에 설치돼(core.hooksPath), reset --hard로도 스스로 꺼지지 않습니다.
물려받은 하네스로 실제 개발을 굴리는 것이 /develop입니다. 모든 코드 변경의 기본 절차이자, 여러 단계를 자동으로 엮는 파이프라인입니다.
worktree 격리 → 스펙 작성(docs/specs/{app}.md, 사이클의 단일 소스) → 구현 → 검증 라운드(spec-verifier 통과할 때까지 머지 차단) → 문서 생성 라운드 → merge-master.
검증은 스펙 통과까지 머지를 막고, 문서·마이그레이션은 자동으로 처리되며, push만 사람 몫으로 남습니다.
/develop으로 개발합니다. 훅은 두 층(Claude 도구 + git)으로 겹쳐 문서 누락·traceback 유실·잘못된 push·main 되감기를 자동 차단합니다.reset --hard로도 안 꺼집니다. 규칙을 "잊어도" 막아주는 안전망이지 만능은 아닙니다.이 문서는 앞의 두 문서(core·template)에서 만든 AI 하네스가 실제 클라우드 서버 위에서 어떻게 돌아가는가를 다룹니다. 보안을 위해 구체적 IP·인스턴스 식별자·계정 번호는 생략하고, 구성요소와 데이터 흐름 중심으로 봅니다.
인프라는 같은 클라우드 계정·같은 VPC 안의 prod 박스와 dev 박스 두 대로 나뉩니다.
두 박스 모두 같은 스택을 올려 둡니다.
| 구성요소 | 역할 |
|---|---|
| Apache 2.4 | 외부에서 닿는 유일한 관문. 80(→HTTPS 리다이렉트)·443(TLS 종단) + 리버스 프록시 |
| 앱 서버 | prod=Gunicorn(systemd 데몬) · dev=Django runserver(포그라운드) |
| PostgreSQL 14 | localhost:5432. prod DB=<project>_prod · dev DB=<project> |
| Redis · huey | 비동기 작업 큐(쓰는 프로젝트만, prod는 별도 systemd 서비스) |
| 정적 "CDN" 디렉토리 | Apache가 직접 서빙하는 파일 디렉토리(<slug>_static_cdn/) |
| Route53 · Let's Encrypt | DNS · 인증서 자동 발급/갱신 |
핵심 규칙 두 가지: - 앱 포트(8000번대)는 VPC 내부에서만 닿습니다. 외부 요청은 언제나 Apache 443을 거칩니다. - 프로덕션은 배포자 개인 홈에서 개인 계정으로 돌립니다(SSH·DB·git 동작이 개인 단위로 추적됨).
같은 코드베이스가 dev인지 prod인지는 환경변수 하나로 갈립니다.
<PROJ>_PRODUCTION=True 이면 production 설정 + .env.production + <project>_prod DB로 뜨고, 설정이 없으면 local 설정 + .env + <project> DB로 뜹니다.
그래서 한 코드베이스가 dev·개인 dev·prod 여러 인스턴스로 동시에 존재할 수 있고, 이들을 가르는 것은 오직 .env와 이 스위치입니다.
개발자는 자기 clone에서 runserver로 프로젝트를 띄웁니다.
포트는 8000번대에서 하나 배정받고, /dev 스킬이 그 포트에 대해 Apache vhost·Let's Encrypt 인증서·Route53 레코드를 자동 세팅해 <slug>-dev 도메인으로 HTTPS 접근을 엽니다.
DB는 로컬 PostgreSQL의 dev DB를 씁니다.
개발 요청의 데이터 경로는 이렇습니다.
정적 파일은 dev(DEBUG=True)에선 runserver가 직접 내보냅니다.
/release는 dev 박스에서 실행돼 prod 박스로 SSH하는 2-호스트 원격 배포입니다. master 병합만으로는 배포되지 않고, /release가 유일한 배포 경로입니다.
변경 델타를 분석해 필요한 단계만 조건부로 실행합니다.
git fetch, 배포 델타 계산. 새 커밋 없으면 SKIP, 트리가 더러우면 BLOCKED.git pull --ff-only(또는 태그 checkout). 코드가 GitHub에서 prod로 들어옴.pip install. core를 고정 태그로, playwright 등도 함께 설치.collectstatic이 내용 해시된 파일을 정적 CDN 디렉토리에 씀.tasks.py 변경 시 huey 소비자 서비스도 재시작.프로덕션 요청은 동기(웹) 와 비동기(작업 큐) 두 갈래로 흐릅니다.
동기 경로: 브라우저 → Route53 → prod 박스 Apache 443(TLS 종단).
여기서 갈립니다. /static·/media는 Apache가 정적 CDN 디렉토리에서 직접 내보내고, 나머지 동적 요청만 내부 포트의 Gunicorn → Django(production, DEBUG=False) → PostgreSQL prod DB로 갑니다.
비동기 경로: Django 뷰가 작업을 큐에 넣으면(enqueue) → Redis/huey 큐 → 별도 huey 소비자 프로세스가 꺼내 LLM 등 긴 작업을 실행 → 결과를 prod DB에 씁니다.
주의: 소비자가 실제로 떠 있어야 합니다. 안 그러면 "비동기" 작업이 gunicorn 워커 안에서 돌다 타임아웃으로 죽습니다.
외부로 나가는 LLM·구글 API 호출은 core를 거쳐 나가며 트레이싱됩니다(standarda-core 참조).
runserver이며, 정적은 Apache가 CDN 디렉토리에서 직접 내보냅니다. /release는 dev→prod SSH로 코드·의존성·정적·서비스 재시작을 조건부로 처리합니다.