AI Meta Clean을 아이디어 발견부터 기획, 개발, 출시, 홍보까지 전 과정을 공개하며 만들고 있습니다.

현재 진행 단계는 여기입니다.

아이디어 발견 → 프로젝트 환경 세팅 → 제품 기획
기술 스택과 아키텍처 결정
→ MVP 개발 → 배포 → 사용자 모집 → 출시와 홍보 → 실제 반응과 개선

이번에는 바로 코딩부터 시작하지 않고, 앞으로 개발할 때 기준이 될 기술 스택과 전체 아키텍처를 먼저 정했습니다.


새로운 작업은 새로운 TASK로 시작했습니다

이번 작업 역시 제가 만든 vibeops로 새로운 TASK를 열었습니다.

vibeops task add

그러면 작업 문서와 전용 브랜치가 함께 만들어집니다.

TASK-002-set-tech-stack-and-architecture.md
task/002-set-tech-stack-and-architecture

기술 스택과 아키텍처에 관한 논의, 결정 이유, 변경 내용은 모두 이 TASK와 프로젝트 문서에 남기게 됩니다.


AI가 알아서 정하게 두지는 않았습니다

이번 작업은 Codex의 Terra Medium / Standard로 진행했습니다.

첫 프롬프트는 간단했습니다.

TASK-001의 제품 기획을 참고해서
기술 스택과 아키텍처를 정하려고 하는데
나랑 티키타카하면서 정해 봅시다.

정리가 완료되면 TASK-002 문서에 반영해 주세요.

AI가 추천한 기술을 그대로 받아 적은 것은 아닙니다.

질문에 하나씩 답하면서 다음 방식으로 결정했습니다.

  • 제 생각과 맞으면 확정

  • 아닌 것 같으면 제 의견 제시

  • 다른 선택지가 궁금하면 비교 요청

  • 이해되지 않으면 쉽게 다시 설명 요청

  • MVP 이후 확장까지 고려해 구조 재검토

  • 이미 운영 중인 서버와 도구를 알려주고 다시 설계

예를 들어 진행 상태를 전달하는 방법으로 단순 Polling도 검토했지만, 최종적으로는 사용자별 작업 진행률을 실시간으로 보여주기 위해 WebSocket + Redis + Socket.IO를 선택했습니다.

MVP라고 해서 무조건 가장 단순한 구조만 고르는 것이 아니라, 나중에 다시 뜯어고칠 가능성과 현재 구현 비용을 함께 비교했습니다.


최종 기술 스택

프론트엔드

  • Next.js

  • TypeScript

  • Tailwind CSS

  • shadcn/ui

  • next-intl

한국어, 영어, 일본어, 간체 중국어를 지원하고 첫 방문 시 브라우저 언어에 따라 자동으로 이동하도록 정했습니다.

글로벌 서비스로 런칭할 계획이라 영어가 기본이면 유리하겠지만 저는 애국심이 아주 많습니다. 한국어 페이지가 루트가 되게 되도 않는 자존심좀 부려 봤습니다. ^^

한국어     /
English  /en
日本語     /ja
简体中文   /zh-CN

API와 실시간 통신

  • NestJS

  • Socket.IO

  • Redis Adapter

업로드, 작업 생성, 다운로드 권한 확인, WebSocket 연결 등을 Next.js 내부에 모두 넣지 않고 별도의 NestJS API로 분리합니다.

사용자는 자신이 요청한 작업의 진행 상태만 실시간으로 확인할 수 있습니다.

업로드 → 대기열 → 처리 → 검증 → 완료 → 다운로드

연결이 끊겼다가 다시 접속해도 PostgreSQL에 저장된 작업 상태를 기준으로 화면을 복구합니다.


비동기 작업 처리

  • Redis

  • BullMQ

  • NestJS Worker

API는 파일 검증과 작업 등록까지만 담당하고, 실제 이미지 처리는 별도의 Worker가 수행합니다.

메타데이터 탐지
C2PA 제거
EXIF/XMP/IPTC 정리
처리 결과 재검증
결과 파일 생성
파일 폐기

여러 사용자가 동시에 요청하더라도 웹 요청이 이미지 처리에 묶이지 않도록 역할을 분리했습니다.


메타데이터 처리

  • ExifTool

  • c2pa-rs 기반 Rust Helper

EXIF, XMP, IPTC는 ExifTool로 처리합니다.

C2PA는 이 서비스의 핵심 기능이기 때문에 공식 c2pa-rs 기반 Helper를 별도로 사용하기로 했습니다.

처리 후에는 실제로 메타데이터가 제거됐는지 다시 검증하고, 제거 여부와 실패 이유를 보고서에 기록합니다.


데이터베이스와 저장소

  • PostgreSQL

  • Prisma

  • Private MinIO

PostgreSQL에는 원본 이미지나 파일명을 저장하지 않습니다.

운영에 필요한 최소한의 정보만 남깁니다.

작업 상태
처리 시간
성공·실패 여부
실패 범주
파일 용량 구간
폐기 결과
최소 운영 통계

반대로 다음 정보는 저장하지 않습니다.

원본 이미지
결과 이미지
파일명
원본 메타데이터
세션 원문
다운로드 토큰 원문
원시 IP 로그

이미지는 외부에 공개되지 않는 Private MinIO에 임시 보관합니다.

브라우저가 MinIO에 직접 접근하지 않고, 모든 업로드와 다운로드는 NestJS API를 통해 처리합니다.


파일을 언제 삭제할지도 미리 정했습니다

AI Meta Clean의 핵심 원칙은 파일을 영구 보관하지 않는 것입니다.

하지만 단순히 “처리 후 삭제합니다”라고만 하면 기준이 모호합니다.

그래서 삭제 시점까지 구체적으로 정했습니다.

  • 원본과 중간 파일: 처리 결과 검증 후 즉시 삭제

  • 결과 파일과 보고서: 다운로드 후 즉시 삭제

  • 다운로드하지 않은 결과: 최대 30분 뒤 자동 삭제

  • Worker 삭제와 MinIO Lifecycle을 함께 적용해 이중 폐기

기능을 구현하기 전에 데이터가 언제 생성되고, 어디에 머물며, 언제 사라지는지부터 설계했습니다.


서버 아키텍처

현재 운영 중인 Oracle Linux 서버와 rootless Podman, Caddy 환경을 그대로 활용합니다.

Browser
  ↓
Host Caddy
  ├─ Next.js Web
  ├─ NestJS API
  └─ Socket.IO Gateway
          ├─ PostgreSQL
          ├─ Redis
          └─ Private MinIO
                    ↑
             BullMQ Worker
             ├─ ExifTool
             └─ c2pa-rs

외부에 공개되는 것은 Caddy뿐입니다.

PostgreSQL, Redis, MinIO는 내부 Podman 네트워크에만 두고, 이미지 처리를 담당하는 Worker는 외부 인터넷 접근도 차단할 예정입니다.


디자인도 문서로 관리합니다

Tailwind CSS와 shadcn/ui를 사용하지만, 두 도구를 디자인의 기준으로 삼지는 않습니다.

루트의 DESIGN.md를 제품 디자인의 단일 기준 문서로 사용합니다.

Typography
Spacing
Color Token
공용 컴포넌트 규칙
화면별 상태
C2PA 경고 문구
다국어 문구
접근성 기준
모바일 Safe Area

새 화면이나 공용 컴포넌트가 추가되면 코드와 함께 DESIGN.md도 업데이트합니다.


바이브코딩도 블랙박스가 아니어야 합니다

이번에 결정한 내용은 하나의 문서에만 적어두지 않았습니다.

TASK-002
아키텍처 문서
현재 상태 문서
의사결정 로그
DESIGN.md
제품 기획 문서

각 결정의 성격에 맞는 문서에 나누어 반영했습니다.

이후 구현 중 선택이 바뀌면 코드만 수정하는 것이 아니라, 결정 문서와 현재 상태도 함께 갱신합니다.

이렇게 하면 바이브코딩도 AI가 알아서 코드를 쏟아내는 블랙박스가 아니라, 기획과 의사결정, 구현과 변경 이력이 연결된 지속 가능한 개발 방식이 됩니다.


이제 구현 계획을 세우면 됩니다

무엇을 만들지 정했고, 기술 스택과 데이터 흐름, 보안 경계, 파일 폐기 원칙까지 정했습니다.

 

이제 다음 단계에서는 이 아키텍처를 기준으로 구현 계획을 만들고, 작업을 작은 단위로 나누어 하나씩 개발할 예정입니다.

여기까지가 실제로 머리를 많이 써야 하는 단계였고, 다음부터는 정한 내용을 차근차근 구현하면 됩니다.

 

그리고 작업을 마친 뒤 모델 사용량을 확인해 봤습니다.

100%에서 시작했는데 97%가 남아 있었습니다.

기술 스택과 아키텍처를 꽤 오래 티키타카했는데 3% 사용.

이 정도면 별로 안 쓴 건가요? ㅎㅎ

 

AI와 나눈 전체 의사결정 대화는 너무 길어 첨부파일로 함께 올립니다.

 

제가 AI의 답을 그대로 사용하는 것이 아니라, 어떤 식으로 질문하고 반박하고 비교하면서 바이브코딩하는지 궁금하신 분들은 가볍게 살펴보셔도 좋을 것 같습니다.

블로그에서 편하게 확인하시려면 아래 링크를 클릭하세요!

[블로그 글 확인하기]