발표(2026-09-25)까지 세 주다. 이번 세션의 주제는 하나 — 아무도 안 보고 있는 사이에 뭔가 조용히 죽지 않게 만든다. 만든 게 아니라 지킨다. 새 기능은 하나만(진위 판별 서비스 배포), 나머지는 전부 관측·자동화·복구·문서다.
오늘 한 것 (TL;DR)
- 매일 도장 자동 게시를 다시 살렸다 — 12시간 만료 토큰으로 죽던 것, 무료 가스 공급이 다 막힌 체인에서 다른 체인(Ethereum Sepolia)으로 갈아탄 것, 실패 시 이슈로 알리게 한 것
- 매일 04시 DB 백업 → 전용 S3 버킷(수명주기 30일). 쓰기 권한만 있는 IAM 이라 이 키로 되뽑을 수 없다
- CloudWatch 지표 + SNS 이메일 경보 — 디스크·백업 나이·API 헬스·메모리·인스턴스 검사 5종. 무료 구간 안
- 밖·안 이중 감시 — 서버 안 지표에 더해 GitHub Actions 가 15분마다 밖에서 공개 주소를 찌른다. 실패 시 자동으로 이슈를 열고, 복구되면 자동으로 닫는다
- 서버 크기 확장 t3.small → t3.medium — 진위 판별 서비스가 분석 1건에 574MiB 를 써서 기존 크기로는 API·워커와 동거 불가
- 표정·음성 진위 판별 서비스 배포 (박소연 PR #42, ADR-0029) — 리버스 프록시 /ai/* 로. CPU 모델이라 GPU 서버 불필요
- 메일 파이프라인 새 계정에서 실증 — 지원자 이메일 수신까지 끝, SES 프로덕션 액세스 신청 완료(승인 대기)
- 결정(ADR-0030): 알림·메일을 워크플로 도구(n8n)로 분리해 공급자를 노드 하나로 바꿀 수 있게. 1단계 인프라 컨테이너 붙임
이번 세션 AWS 비용: t3.medium 승격분(월 +$17)이 유일한 새 지출. 나머지(백업·경보·외부 헬스체크)는 무료 구간 안. 총 예산 $400 · 2026-10-27 까지 안에서 진행.
1. 매일 도장이 조용히 죽어 있었다
우리 서비스는 매일 아침 지원자 제출물의 요약값(해시)을 공개 블록체인에 한 번 게시한다. 이걸로 “이 시각 이후 제출물이 바뀌지 않았다” 는 근거를 남긴다. 결정 문서는 ADR-0028.
문제: 이 자동 게시가 이틀 동안 실패한 채 아무도 몰랐다. 원인 두 가지가 겹쳤다.
첫째, 로그인 토큰이 12시간 만에 만료. 워크플로가 이틀 전 발급한 토큰을 계속 쓰다 401 로 죽었다. 매 실행마다 새로 로그인해서 짧은 수명 토큰을 새로 받도록 수정. 실패해도 알아채도록, 실패 시 GitHub 이슈를 자동으로 하나 열어 담당자에게 알리는 워크플로도 함께 붙였다(백엔드 이우정 PR #21·#25).
둘째, 쓰던 테스트넷의 무료 가스 공급이 전부 막혔다. 블록체인에 뭘 올리려면 아주 소액의 수수료(가스)가 필요한데, 우리가 쓰던 Polygon Amoy 테스트넷의 무료 수도꼭지(faucet) 여섯 곳을 다 돌아도 하나도 안 주더라. 두 곳은 서비스 종료, 한 곳은 유료 전환, 세 곳은 “메인넷에 자산이 있어야 준다”는 진입 장벽. 이런 시점의 표준 대안은 Ethereum Sepolia — Coinbase 개발자 포털이 별 조건 없이 소액을 나눠 준다. chain.py 두 줄 교체와 결정 문서 개정으로 전환(PR #29). 실전송 1건으로 체인 반영 확인.
부수 안전장치 하나 더 붙였다(이우정 PR #31): 설정에 적어 둔 이름표(예: ethereum-sepolia)와 실제 접속한 RPC 노드가 다른 체인을 가리키고 있으면 전송 전에 멈춘다. 이름표만 믿고 잘못된 체인에 올리는 사고 방지.
2. “죽었는지 어떻게 아나” — 계기판과 경보를 걸었다
지금까지는 사람이 눈으로 훑어야 알았다. 다섯 개 경보를 걸었다.
| 지표 | 뜻 | 규칙 |
|---|---|---|
| 디스크 사용률 | 서버 EBS 볼륨 % | ≥ 85% (2회 연속) |
| 백업 파일 나이 | 마지막 백업 파일이 몇 시간 전인지 | ≥ 30시간 (1회) |
| API 헬스 | /health 응답 | 20분 연속 실패 |
| 메모리 사용률 | (total − available) / total | ≥ 90% (2회 연속) |
| 인스턴스 시스템 검사 | AWS 자체 하드웨어 점검 | 실패 시 인스턴스 자동 복구 + 메일 |
지표는 서버가 10분마다 CloudWatch(AWS 관측 서비스)로 밀어 넣는다(PR #37). 경보 정의는 CloudFormation 템플릿 파일 하나로 만들어, 콘솔 30번 클릭 대신 파일 한 번 업로드로 만들고 지운다(PR #39). 알림은 SNS(알림 서비스) 를 통해 이메일로. 관측 에이전트는 안 깔았다 — 기존 aws-cli 하나로 충분하고, 상시 무료 구간(지표 10개·알람 10개·API 100만 건/월) 안이라 비용 0.
API 헬스는 “누락 데이터도 경보로 취급” 하도록 뒀다. 그래야 인스턴스가 통째로 죽어 아무 숫자도 안 올 때도 울린다. 나머지는 누락 시 무시(정상 취급) — 시스템 검사가 통째 죽음을 이미 잡는다.
밖에서 찌르는 감시도 하나(PR #55). GitHub Actions 가 15분마다 공개 주소(api.seuk.suvisdev.cloud/health 와 첫 화면)를 밖에서 찌른다. 30초 간격 2회 실패해야 실제 알림 — 반짝 실패 오탐 방지. 실패 시 이슈 “🔴 외부 헬스체크 실패” 를 자동으로 열고, 복구되면 자동으로 닫는다. 조합 규칙: 둘 다 울리면 서버, 밖만 울리면 DNS·TLS·리버스 프록시·보안그룹.
주 1회 사람이 훑는 계기판 도 하나 짰다(PR #36): 디스크·컨테이너 상태·자동 배포 최근 결과·백업·헬스 응답을 한 화면에 보여 주는 읽기 전용 스크립트. 자동 경보와 별개로 사람이 “괜찮아 보이네” 를 확인하는 용도.
3. 백업이 없으면 이 다 소용없다
경보만 걸어도 소용없다 — 데이터가 사라진 뒤에 알아 봐야 못 되돌린다.
매일 04:00 KST 서버가 DB 를 통째로 뜨서 압축(gzip)·검증 후 전용 S3 버킷 arda-db-backups-seuk 에 올린다(PR #32). 쓰기 권한은 최소한(PutObject 만) — 이 키로 데이터를 되뽑을 수 없다. 버킷 자체는 퍼블릭 차단, 수명주기 30일 자동 만료로 스토리지 비용도 상한.
부수 발견: Ubuntu 24.04 에 apt 로 aws-cli 가 없다 — snap 으로 설치해야 한다는 것을 문서에 반영(PR #34). 새 서버 세팅 시 30분 낭비를 예방.
컨테이너 로그 상한 도 함께 걸었다(PR #35). 20MB × 3, 서비스당. 그동안 로그 하나가 무한히 커지다 29GB 디스크를 다 먹은 적이 있었다. 이제 사고 방지.
4. 서버가 진위 판별 서비스랑 같이 못 살았다 — t3.small → t3.medium
박소연 PR #42 로 표정·음성 진위 판별 서비스(ADR-0029)가 같은 EC2 에 붙는다. 분석 1건이 574MiB 를 쓴다. 기존 t3.small(RAM 2GB)로는 API·워커·DB 와 동거가 불가능 — 승격했다. 월 +$17. 4GB RAM 이 되고 나서 여유 2.8Gi 확보, 진위 판별을 CPU 모델로 붙여도 흔들리지 않는다.
함정 하나 배웠다: 인스턴스 유형 변경은 완전히 중지된 상태에서만 적용된다. “중지” 표시가 뜨자마자 “시작” 해 버리면 옛 유형으로 그대로 뜬다. 판별법: free -h 총량과 컨테이너의 실행 시간(재부팅했다면 전부 몇 분 이내).
5. 메일이 새 계정에서 나가는지 끝까지 실증
AWS 계정 이전 후 메일(SES) 파이프라인이 실제로 지원자에게 도착하는지가 미검증이었다. MAIL_DRY_RUN=0(실제 발송 모드)로 바꾸고, 검증된 지메일 주소로 공고 → 지원 → 서류심사 → 면접 → 최종 합격까지 다섯 단계를 통과시켜 실제 도착을 확인했다.
발견 두 가지:
- 로그가 배포 시 재생성으로 사라진다. 컨테이너가 재생성되면 그때까지의 워커 로그가 통째로 유실 — 다음 안정화 항목으로 CloudWatch Logs 에 실어 보내는 것을 후보에 올렸다
- SES 프로덕션 액세스가 신청되어 있지 않았다. 새 계정은 처음엔 샌드박스(검증된 주소로만 발송 가능)라 신청이 필요. 이번 세션에서 첫 신청(승인 대기). 승인 전까지는 검증된 주소로만 실발송
이우정이 발송 결과 식별자(SES MessageId)를 email_logs 행에 남기는 것(PR #58)까지 붙였다. “보냈다는데 안 왔다” 를 조사할 근거가 이제 있다.
6. AWS 를 떠나도 돌게 — n8n 워크플로 분리 (ADR-0030)
강사 제안: “AWS 가 내려가는 것도 생각해서 n8n 으로 돌리는 게 어떠냐.” “AWS 가 내려간다” 를 장애가 아니라 “AWS 를 떠나는 상황”(예산 $400 · 10/27 이후) 으로 읽고 결정 문서 ADR-0030 을 썼다.
결정: 알림·메일 자동화 계층을 워크플로 도구(n8n, 오픈소스 자동화 엔진)로 분리한다. 트리거는 우리 API 가 쏘는 웹훅(HTTP 신호), 발송은 노드 하나(SES ↔ SMTP)만 바꾸면 공급자가 바뀐다. AWS 를 떠날 때 n8n 컨테이너와 워크플로 JSON 을 그대로 다른 곳으로 옮긴다.
한계는 숨기지 않는다 — n8n 이 같은 EC2 에 있는 한 AWS 장애에는 같이 죽는다. 이 결정이 푸는 것은 이식성이지 장애 대응이 아니다. 장애 대응은 위의 백업·경보·자동 복구가 맡는다.
1단계 인프라 몫 완료(PR #63): compose 에 n8n 서비스(포트는 안쪽에만, 밖에서는 리버스 프록시 /n8n/* + Basic Auth 로만), 백업에 n8n 볼륨 추가, 지표에 n8n 헬스 추가, 경보 6번째(20분 실패)까지. 워크플로 JSON 은 저장소가 진실. 백엔드 몫(내부 API 2개, 발송 경로 스위치)은 별도.
팀 접근: 편집 화면 로그인은 팀 전원 공유 — “공동 프로젝트로 추후 누군가가 빠지더라도 모두가 진행할 수 있게.” 권한이 한 사람에게 묶여 치른 대가(ADR-0025)를 반복하지 않는 원칙의 재확인.
7. 문서 톤 정리 — “잠금” 이 아니라 “현재 구현 + 확장 후보”
이번 세션에서 반복된 상황: 설계 문서가 “이렇게 하지 않는다·안 한다”로 못 박혀 있어 필요한 확장을 못 얹는 것처럼 읽혔다. 대표적으로 면접 설계 §7 이 원래 오디오·답변 단위·재시도 허용이었는데, 실시간 스트리밍·재시도 없음을 얹고 싶을 때 “잠금” 이 걸림돌이었다.
결정 문서 개정 원칙을 명문화(PR #49):
오너가 개정 결정 문서를 쓰면 확정 상태가 바뀐다. 팀장 확정 대기 게이트 없음. 기능은 계속 추가되고 이전 결정은 오너가 자유롭게 개정한다.
확정 결정 문서 23개 맨 위에 “오너가 개정 ADR 을 쓰면 바뀐다” 표준 한 줄(PR #61), 지시서 36개 + 로드맵 5개 맨 위에 “작성 시점의 설계·계획, 확장·개정은 오너” 한 줄(PR #62). 문서 톤은 앞으로 “현재 구현 + 확장 후보” 로 쓴다 — “안 한다·불가” 단정 금지.
2026-09-07 운영 변경(서버 확장·백업·경보·체인 전환·예산 $400)은 상세 문서 10개에 한 번에 반영(PR #51).
8. 이 세션이 지운 위험 목록
- 매일 도장이 조용히 죽어 있어도 며칠 뒤에나 알아채는 것
- 실수로 유료 AI 를 부르는 것 (하네스 옵트인 가드, PR #8)
- 새 개발자가 clone 하고 pytest 를 돌려도 실패하는 것 (이우정 PR #24)
- 이름표만 믿고 잘못된 체인에 앵커를 올리는 것 (이우정 PR #31)
- DB 가 인스턴스 사고 한 번으로 사라지는 것
- 서버가 조용히 죽어도 다음 사람이 접속할 때까지 모르는 것
- 로그 하나가 디스크를 다 먹는 것
- 진위 판별 서비스와 API 워커가 메모리를 다투는 것
- 메일이 새 계정에서 실제로 나가는지 미확인
- 예산 종료 후 AWS 를 떠나야 할 때 파이프라인을 다시 짜야 하는 것
발표(2026-09-25) 까지 남은 것: GPU 서버 준비(qwen 로컬 실행 여부 결정용, 켜고 끄기 전제·예산 안), n8n 백엔드 몫과 시연 경로 확정, 로컬 배포 판(개인정보 때문에 로컬 우선) 실증.