ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 카카오클라우드 - 클라우드 개발자 교육 22주차 후기 (4/20 - 4/24)
    bootcamp 2026. 4. 24. 20:22

     

    발표는 끝났고, 운영이 시작됐다 — 배포 자동화·보안 핫픽스·추천 신호 다층 흡수

     

     

    이번 주 한 눈에

    | 영역 | 핵심 산출물 |
    | 인프라 / DevOps | 5개 레포 `cd-prod.yml` 운영 자동 배포 구축 · `scripts/deploy-prod.sh` · 운영서버 4-VM 재구축 매뉴얼 신설 · 운영 Nginx OAuth2 404 핫픽스 |
    | 보안 | OCR SSRF 방어 · 10MB 스트리밍 제한 · debug EP 운영 가드 · PaddleOCR 싱글턴 race fix · `/ocr-events/analyze` 인증 필수화 · 결제 웹훅 서명 검증 재설계 |
    | Chat Agent 품질 | CBF 약한 신호 다층 흡수 (reviews ∪ watch_history UNION + likes 0.7× + wishlist 0.4× + clicked_rec 0.5×) · 추천 편수 사용자 요청 존중 · info 의도 첫 턴 폴백 · 이미지 단독 첨부 422 hotfix · image-aware 재질문 |
    | 결제 | Toss 콘솔 직접 취소 동기화 — `refundOrder` 선조회 구조 개편 · 관리자 "PG 재조회" 버튼 신설 · 웹훅 핸들러 `syncFromPg` 위임 일원화 |
    | 관리자 AI 에이전트 v3 | v2 Write tool·HITL·risk_gate 전면 제거 → Read 54 + Draft 10 + Navigate 12 = **76 tool** ReAct 루프 재설계 · SSE `form_prefill`/`navigation` 2 이벤트 추가 |
    | 고객센터 챗봇 v3.3 | vLLM 1.2B 분류 → ES Nori BM25 top-5 + 점수 임계값 분기 + Solar 경계 분류로 재구성. 정상 경로 LLM 호출 횟수 절반 이하 |
    | 팀 협업 | 민수 OCR 영수증 인증 + 한나 좋아요 토글/리뷰 삭제 머지 반영 → 5개 레포 PR 생성 |

     

     

    게스트 쿼터 검증 + 팀원 PR 전수 리뷰 (4/21~22)

     

     

    8주차 발표가 끝난 직후라 월요일은 "안전하게 운영으로 가져갈 수 있는 상태"를 만드는 날로 잡았어요. 

    두 가지를 정리:

     비로그인 체험 1회 무료 실측
    - `mongle_guest` HMAC HttpOnly 쿠키 + IP 이중 방어 (`chat:guest_used:{guestId}` + `chat:guest_used_ip:{ip}` TTL 365일)
    - 쿠키 삭제 후 재접속·IP 고정 시나리오·다른 브라우저 동일 IP 3가지로 우회 시도 → 모두 차단 확인
    - 소진 후 SSE `error: GUEST_QUOTA_EXCEEDED` → Client `LoginRequiredModal` 자동 노출

     팀원 조직레포 PR/머지 전수 리뷰
    팀원 4명 × 5개 레포의 열린 PR/최근 머지 전부 읽어봤어요. 정한나의 recommend v2 `review_repository.py` 에서 SQL 문자열 동적 치환 경로가 발견돼 화이트리스트(`_ALLOWED_TABLES`) + 런타임 컬럼 존재 검사로 보강하도록 피드백 남김. 민수 OCR 코드는 다음날 머지 반영에서 본격 점검.

     

     

    운영 CI/CD 자동 배포 구축 (4/23)

    18주차 Q&A 때 "운영 배포는 어떻게 해요?" 에 "로컬 빌드 → rsync → VM2 `docker load` → compose up. 자동화는 다음 스프린트 과제" 라고 답변한 그 다음 스프린트가 바로 이번 주였어요.

     

    핵심 결정 — GHCR 이미지 빌드 → VM pull

    | 옵션 | 장점 | 단점 | 선택 |
    | **A) GHCR 빌드 → VM pull** | VM 에서 빌드 X, SHA 태그 롤백 쉬움 | GHCR 권한/로그인 세팅 필요 | ✅ |
    | B) rsync + VM 빌드 | 스테이징과 동일 패턴 | **VM2 디스크 94% / Agent 1.1GB 로드 실패 전례(4/16)** | ✗ |

    4/16 운영 배포 때 VM2 디스크가 94% 까지 차서 Agent 이미지 로드가 실패한 사건이 결정적. VM 에서 빌드하지 않는 쪽으로 확정.

     5개 레포 `cd-prod.yml` 구조


    GitHub Actions (push: main)
      ├─ test (gradle/pytest/lint)
      ├─ build & GHCR push (backend/agent/recommend) → tag: {sha} + latest
      └─ deploy (ssh + ProxyJump)
          VM1 bastion (210.109.15.187, 공인)
            └─ VM2 app (10.20.0.11) — docker pull + compose up -d + 헬스체크


    - Client/Admin 은 VM1 Nginx 에 정적 서빙이라 dist artifact → VM1 rsync → `nginx -s reload`
    - Backend/Agent/Recommend 는 VM2 사설망이라 **VM1 bastion ProxyJump 필수**
    - Concurrency key `cd-prod-<svc>` — 동일 서비스 배포 직렬화
    - 이미지 태그 **2종 동시 push**: `{sha}` (롤백용) + `latest` (compose 참조)

     자동 롤백 트릭 — `:latest` 포인터

    deploy job 이 배포 직전 컨테이너의 **이미지 ID** 를 `docker inspect` 로 기억 → 헬스체크 실패하면 `docker tag <prev_id> ...:latest` 1줄로 이전 이미지 복귀. compose 는 항상 `:latest` 만 참조하니까 SHA 를 `:latest` 로 덮어쓰면 `docker compose up -d --no-deps --force-recreate` 만으로 교체됨.

    대안 중에 "compose image 에 SHA 직접 박기" 도 있었지만 배포마다 compose 수정 + commit 필요해서 기각. 불변 SHA + 가변 포인터 조합이 **compose 수정 없는 교체 + SHA 추적** 을 양립시키는 유일한 방법.

     수동 배포/롤백 CLI — `scripts/deploy-prod.sh`


    ./scripts/deploy-prod.sh all                    # 전체
    ./scripts/deploy-prod.sh backend                # 단일 서비스 :latest 재풀
    ./scripts/deploy-prod.sh backend abc1234        # SHA 롤백

    첫 구현 때 `declare -A SERVICES` 의 메타를 `:` 구분자로 파싱하다가 health URL(`http://localhost:8080/health`) 의 `:` 때문에 split 실패. 구분자를 **`|`** 로 바꿔서 해결. 외부 입력이 아닌 상수라도 구분자 충돌 가능성은 반드시 체크해야 한다는 교훈.

     


    관리자 AI 에이전트 v3 재설계

    "AI 가 직접 쓰게 하면 안 되겠다"

    3종 Tool 분류 (Read / Draft / Navigate)

    | 분류 | Backend 호출 | 반환 payload | Client 동작 |
    | Read | GET 직접 | `AdminApiResult.data` | narrator 가 자연어 서술 |
    | Draft | 없음 | `{target_path, draft_fields, action_label, summary}` | SSE `form_prefill` → "해당 화면으로 열기" 버튼 → `navigate(path, {state: {draft}})` |
    | Navigate | GET (검색) | `{target_path\|None, label, context_summary, candidates?}` | SSE `navigation` → "이동" 버튼. 단건/다건 분기 |

     

     AI 가 실수로 데이터를 바꿀 수 없고, HITL/confirm_keyword 복잡도가 사라지며, UX 는 "AI 가 초안·링크를 주고 관리자가 최종 저장/실행" 으로 명확해짐.

     

     

    결제 사고 두 건 근본 해결 

     

    유저(관리자)가 Toss 개발자센터 콘솔에서 결제 1건을 직접 취소 → 몽글픽 관리자 페이지 확인 → 상태가 여전히 `COMPLETED`. 관리자가 "환불" 버튼 클릭 → 500 에러.

    교훈

    공식 문서를 한 번 더 확인했으면 피할 수 있었던 유령 구현. 외부 시스템 통합은 "있을 것 같은" API 를 구현하지 말고 실제 스펙을 읽어야 함. 단일 진실 원본 원칙 — 관리자 "PG 재조회" 버튼과 웹훅 핸들러가 정확히 같은 로직 수행 → 유지보수 1개 경로로 단순화.

     

     

     

     

     

     

    —————————————————————————— 
    본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 3기(B-log) 리뷰로 작성 되었습니다.

Designed by Tistory.