서비스를 넘겨받을 때 개발자(또는 외주사)로부터 다음을 반드시 받아둔다. 없으면 받을 때까지 배포를 시작하지 않는다.
.env.example 형태) — 각 변수의 의미, 필수/선택 여부git log -p | grep -iE "api[_-]?key|secret|password|sk-ant|sk-".env로 분리, .gitignore에 .env 포함 확인.env는 서버에만 존재. 저장소·메신저·메일로 전달 금지 (필요 시 1회성 비밀 공유 도구 사용)https://<서비스도메인>/login) → AZURE_AD_CLIENT_ID, AZURE_AD_TENANT_ID, SESSION_SECRET 발급·설정public/ (정적 파일) 안에 임직원 명단, 사번, 조직도, 내부 설정 등이 들어있지 않은지 확인 — 있으면 인증 뒤로 이동npm audit (또는 해당 언어의 동등 도구) 실행 — high/critical 취약점은 패키지 교체 또는 업그레이드 후 배포~/services/<서비스명>/ # 서비스별 디렉터리 (compose.yml, .env)
포트: 내부 포트는 compose 네트워크 안에서만,
외부 노출은 리버스 프록시로만
:8001 같은 포트로 외부에 노출되지 않도록 — 리버스 프록시(nginx) 뒤에만 둔다<서비스명>.bigsox.com 형태의 서브도메인 발급 + HTTPS(Let's Encrypt) 필수. HTTP 평문 운영 금지.env.example 기준으로 운영 .env 작성, 권한 chmod 600GET /api/health)| # | P0 항목 | 확인 |
|---|---|---|
| 1 | 인증(SSO) 활성화 — 비로그인 접근 차단 | |
| 2 | API 키: 회사 명의 + 사용량 한도 설정 + 유출 이력 키 폐기 완료 | |
| 3 | 임직원 정보 등 내부 데이터가 공개 경로에 없음 | |
| 4 | 개인정보 보존 기한·삭제 절차 합의 | |
| 5 | HTTPS 적용, 직접 포트 노출 없음 | |
| 6 | high/critical 의존성 취약점 해소 | |
| 7 | 백업 동작 확인 (DB + 업로드 볼륨, 복구 시험 1회) |
P1 (배포는 가능하나 1개월 내 해소): 모니터링/알림 연결, 운영 문서 보완, 접근 권한 세분화(본인 데이터만 조회 등).
| 영역 | 기준 |
|---|---|
| 백업 | DB 덤프 + 업로드 볼륨을 일 1회 외부 저장소로. 분기 1회 복구 리허설. |
| 모니터링 | health check 주기 점검(5분 간격), 디스크·메모리 사용량 알림. |
| 키 관리 | API 사용량 월 1회 검토 (비정상 사용량 = 유출 신호). 담당자 퇴사/외주 종료 시 관련 키 전부 교체. |
| 업데이트 | 분기 1회 npm audit 재실행. 방치된 서비스는 차단이 원칙 (사용자 없는 서비스는 내림). |
| 롤백 | 직전 이미지 태그를 보존하여 docker compose로 즉시 이전 버전 복귀 가능하게 유지. DB 마이그레이션이 포함된 배포는 롤백 절차를 배포 전에 먼저 적는다. |
.env에 들어갈 모든 변수의 최종 값 (비밀값은 안전한 채널로)§2~§5의 점검에서 탈락하는 서비스는 고치는 것보다 재구축이 빠른 경우가 많다. 아래 프롬프트를 Claude Code(또는 동급 AI 코딩 도구)에 원본 소스와 함께 주면, 이 SOP가 요구하는 운영 수준을 향해 재구축을 시작할 수 있다. (성화 개인경비 정산 도구를 v1에서 현재 구조로 재구축할 때 실제로 적용한 원칙들을 프롬프트화한 것.)
사용법: 원본 소스를 ./legacy/ 폴더에 넣고, 새 빈 저장소에서 Claude Code를 열어 아래를 붙여넣는다. 한국어/영어 중 편한 버전을 사용한다 — 내용은 동일. 복사 버튼으로 전체를 복사할 수 있다.
당신은 바이브코딩으로 만들어진 웹서비스를 운영 수준으로 재구축하는 시니어 엔지니어다.
./legacy/ 의 원본 소스를 분석하고, 아래 원칙대로 처음부터 다시 구축하라.
## 0. 원본 분석 — 코드 작성 전에 먼저
- 원본이 실제로 하는 일을 끝에서 끝까지 문서화하라: 입력 → 처리 → 출력.
- 업무 규칙(도메인 로직)을 전부 추출해 목록화하라. AI 프롬프트 문자열 안에 박혀
있는 규칙(금액 한도, 시간대 분기, 예외 처리)도 빠짐없이 포함하라.
이 규칙들이 제품의 가치다 — 한 줄도 잃어버리지 마라.
- 원본의 출력물 형식(엑셀 양식, 보고서 등)은 1:1로 보존하라. 사용자의 다운스트림
시스템(회계, ERP)이 그 형식에 의존한다. 셀 단위까지 원본과 대조하라.
## 1. 아키텍처 — 읽기와 판단을 분리하라
- AI(LLM)는 비결정적 작업(OCR, 추출, 이해)에만 사용하고, 업무 규칙의 적용은
결정적 코드(rules engine)로 작성하라. 같은 입력 → 항상 같은 결과.
원본처럼 "읽기+판단"을 한 번의 AI 호출에 합치면 오류가 복합되고 디버깅이 불가능해진다.
- LLM 호출은 tool-schema 로 JSON 출력을 강제하라 (자유 텍스트 파싱 금지).
재시도/백오프를 포함하라.
- 단계별 모듈로 분리하라: 추출(extract) / 판단(classify) / 출력(export) /
인증(auth) / 저장(db). 파이프라인 오케스트레이터 하나가 단계를 묶는다.
- 규칙이 못 다루는 unknown 케이스에만 LLM fallback을 허용하되,
그 결과는 반드시 needs_review 로 표시해 사람 검토로 보내라.
## 2. 보안 — 기본값이 안전해야 한다
- 원본 코드와 git 커밋 이력 전체에서 비밀값(API 키, 비밀번호)을 스캔하고
발견 즉시 보고하라. 발견된 키는 유출로 간주한다 (교체 대상 목록에 기록).
- 모든 설정은 환경변수로. .env.example 을 제공하고, 코드에 키/URL/포트를
하드코딩하지 마라. 서버를 옮길 때 코드 수정이 없어야 한다.
- 인증은 Microsoft Entra(Teams) SSO 를 환경변수 게이트로 구현하라:
env 미설정이면 개발 편의상 열리되 기동 로그에 큰 경고를 출력하고,
env 설정 시 모든 경로(정적 파일 포함)가 인증 뒤로 들어가야 한다.
- 정적 공개 경로(public/)에 내부 데이터(임직원 명단, 사번 등)를 두지 마라.
## 3. 데이터 — 없어도 돌고, 죽어도 안 무너지게
- DB(Postgres)는 DATABASE_URL 게이트로 선택적으로 구성하라. 미설정 시
무상태 모드로 완전히 동작해야 한다. 저장은 best-effort:
DB 장애가 도구 장애가 되어서는 안 된다.
- 마이그레이션은 plain SQL 파일로, 기동 시 자동 적용, 적용 이력을 테이블로 추적.
- 어떤 개인정보를 무엇 때문에 저장하는지 README 에 명시하라
(보존 기한 결정은 IT 몫이므로 목록만 정확히).
## 4. 검증 — "좋아졌다"를 숫자로 증명하라
- 업무 규칙은 네트워크 없는 단위 테스트로 전부 커버하라.
특히 돈이 걸린 계산(한도, 캡, 인원 배수)은 케이스별로 빠짐없이.
- 원본의 동작을 그대로 재현하는 legacy 모듈을 따로 만들고, 같은 입력에 대해
신구 결과를 표로 비교하는 하니스(CLI)를 제공하라. 개선 주장은 이 표로만 한다.
## 5. 운영 준비
- Dockerfile + docker-compose (mem_limit 명시, 영속 데이터는 named volume).
- 헬스체크 엔드포인트를 만들어 키/인증/DB 상태를 노출하라
(예: GET /api/health → {ok, hasKey, authEnabled, dbConnected}).
- 재실행 가능한 배포 스크립트 (rsync 또는 동등 + compose up --build + 헬스체크).
- 의존성 취약점 점검(npm audit)을 실행하고 high/critical 은 교체하라.
## 진행 방식
- 단계마다 무엇을 발견했고 무엇을 결정했는지 보고하라.
- 원본과 동작이 달라지는 지점은 반드시 명시적으로 알려라. 조용한 동작 변경 금지.
- 의도적으로 미루는 것(인증 off, 보존정책 미구현 등)은 README 에
"deliberate gaps" 목록으로 남겨 IT 가 Go/No-Go 때 확인할 수 있게 하라.
You are a senior engineer rebuilding a vibe-coded web service to production grade.
Analyze the original source in ./legacy/ and rebuild it from scratch
following these principles.
## 0. Analyze the original — before writing any code
- Document end-to-end what the original actually does: input → processing → output.
- Extract and list every business rule (domain logic), including rules buried
inside AI prompt strings (amount limits, time-of-day branches, exception
handling). These rules are the product's value — do not lose a single line.
- Preserve the original's output formats (Excel templates, reports) 1:1.
The user's downstream systems (accounting, ERP) depend on them.
Verify against the original cell by cell.
## 1. Architecture — separate reading from judgment
- Use AI (LLMs) only for non-deterministic work (OCR, extraction, understanding).
Implement business-rule application as deterministic code (a rules engine).
Same input → same result, always. Combining "read + judge" in a single AI call,
as the original does, compounds errors and makes debugging impossible.
- Force JSON output from LLM calls via tool schemas (never parse free text).
Include retry/backoff.
- Split into stage modules: extract / classify (judge) / export / auth / db,
tied together by one pipeline orchestrator.
- Allow an LLM fallback only for unknown cases the rules cannot handle,
and always mark those results needs_review so a human checks them.
## 2. Security — safe by default
- Scan the original code and the full git history for secrets (API keys,
passwords) and report any finding immediately. Treat a found key as leaked
(record it on the rotation list).
- All configuration via environment variables. Provide .env.example.
No hardcoded keys/URLs/ports — moving servers must require zero code changes.
- Implement auth as env-gated Microsoft Entra (Teams) SSO: with env unset the
app stays open for dev convenience but prints a loud boot warning; with env
set, every route (static files included) goes behind auth.
- Never put internal data (employee rosters, IDs) in publicly served
static paths.
## 3. Data — runs without it, survives losing it
- Make the database (Postgres) optional, gated on DATABASE_URL. With it unset,
the app must work fully in stateless mode. Persistence is best-effort:
a DB outage must never become a tool outage.
- Migrations as plain SQL files, auto-applied on boot, tracked in a
history table.
- State in the README exactly what personal data is stored and why
(retention decisions belong to IT — just keep the list accurate).
## 4. Verification — prove "better" with numbers
- Cover every business rule with network-free unit tests. For money-affecting
calculations (limits, caps, per-person multipliers), cover every case.
- Build a separate legacy module that reproduces the original's behavior, and
provide a harness (CLI) that compares old vs new results on the same inputs
in a table. All claims of improvement come from this table only.
## 5. Operations readiness
- Dockerfile + docker-compose (explicit mem_limit; persistent data on
named volumes).
- A health check endpoint exposing key/auth/DB status
(e.g. GET /api/health → {ok, hasKey, authEnabled, dbConnected}).
- A re-runnable deploy script (rsync or equivalent + compose up --build +
health check).
- Run a dependency vulnerability audit (npm audit) and replace high/critical
packages.
## Working style
- At every stage, report what you found and what you decided.
- Explicitly flag every point where behavior differs from the original.
No silent behavior changes.
- Record anything deliberately deferred (auth off, no retention policy, etc.)
as a "deliberate gaps" list in the README so IT can check it at Go/No-Go.
재구축이 끝나면 결과물을 §1(접수)부터 이 SOP에 태운다 — 프롬프트가 §2~§5를 미리 충족시키도록 설계되어 있지만, 충족했는지의 판정은 사람이 한다. 점검을 건너뛰지 않는다.
이 SOP의 기준 구현체인 성화 개인경비 정산 자동화 도구의 현재 아키텍처. 새 서비스를 받을 때 이 구조를 최대한 재사용한다 — 같은 배포 패턴, 같은 인증 모듈, 같은 DB 운영 방식을 쓰면 점검도 운영도 한 가지 방법만 익히면 된다.
nginx-proxy 하나가 모든 도메인을 받고, 서비스들은 각자 독립된 compose 스택으로 나란히 산다. 개인경비 정산은 그 중 하나의 입주자일 뿐 — 새 서비스가 와도 이 그림에 칸을 하나 더 그리는 것이다.
src/
config/accounts.js 계정과목 26종 매핑 + 금액 한도 정책
extract/ STAGE 1 — 읽기 (AI 는 여기에만)
classify/ STAGE 2 — 판단 (rules.js 규칙엔진 + memory.js 학습)
export/excel.js 성화 엑셀 템플릿 채우기 (templates/)
auth/ Entra(Teams) SSO — env 게이트, Express 미들웨어
db/ Postgres — pool / migrate / store (전부 best-effort)
llm/anthropic.js Anthropic 클라이언트 (tool-schema JSON 강제 + 재시도)
pipeline.js ①→② 오케스트레이터
server.js Express 앱 + 전체 라우트
migrations/ plain SQL — 기동 시 자동 적용, _migrations 테이블로 추적
public/ 정적 UI (vanilla JS, 빌드 없음)
DATABASE_URL 게이트로 선택적 — 미설정이면 무상태 모드, 설정돼 있어도 저장 실패가 도구 장애가 되지 않는다(best-effort).
마이그레이션: migrations/*.sql 순서대로 기동 시 자동 적용. 스키마 변경 = SQL 파일 추가 후 재배포. 백업: docker exec sunghwa-expense-db pg_dump -U se sunghwa_expense > backup.sql
CREATE DATABASE <service>)를 만드는 쪽을 권장 — 메모리 절약, 백업 일원화. 현재 구성은 서비스별 컨테이너(격리 우선)이며 둘 다 유효한 선택.
코드 수정 없이 환경값만 바꾸면 되도록 설계되어 있다. 이관 체크리스트:
.env의 DOMAIN 변경.env 재작성: 성화 명의 ANTHROPIC_API_KEY(한도 설정), POSTGRES_PASSWORD 신규, Entra 3종(§2.3) 설정 → 인증 활성화 상태로 출발pg_dump → 새 DB에 restore, se-data 볼륨 내용물 rsync