의사결정 로그
✍️ 주요 의사결정 로그 (Architecture Decision Record) 최종 업데이트: 2025년 6월 29일
이 문서는 SurfAI 프로젝트를 진행하며 내렸던 주요 기술적 결정의 배경, 고려했던 대안, 그리고 최종 선택의 이유를 기록합니다.
1. 인증 시스템: JWT
+ HttpOnly
쿠키 방식 채택
- 날짜: 2025-06-21
- 논의:
- 초기에는 전통적인 세션-쿠키 방식을 고려했습니다.
- 이후, 확장성과 클라이언트(모바일 등) 호환성을 위해
JWT
토큰을LocalStorage
에 저장하는 방식을 논의했습니다. - 최종적으로, 보안과 편의성을 모두 고려하여
JWT
를HttpOnly
쿠키에 담아 전달하는 방식으로 결정했습니다.
- 결정:
JWT
(Access/Refresh Token)를 생성하고, 이를HttpOnly
Secure
쿠키를 통해 클라이언트와 통신한다. - 이유:
- 보안 강화 (XSS 방어):
HttpOnly
속성을 통해, 브라우저의 JavaScript 코드가 토큰에 직접 접근하는 것을 막아 XSS(Cross-Site Scripting) 공격으로 인한 토큰 탈취 위험을 크게 줄일 수 있습니다. - 서버 Stateless 유지:
JWT
를 사용함으로써 서버는 사용자 세션 상태를 저장할 필요가 없어, 향후 서버를 수평적으로 확장(Scale-out)하기에 매우 유리한 구조를 가집니다. - 자동 인증: 프론트엔드는
apiClient
에서credentials: 'include'
옵션만 설정하면, 브라우저가 모든 요청에 자동으로 인증 쿠키를 포함시켜주므로 코드 관리가 단순해집니다.
- 보안 강화 (XSS 방어):
2. 배포 플랫폼: Docker
+ Google Cloud Run
채택
- 날짜: 2025-06-22
- 논의:
- 프론트엔드 배포를 위해 간편한
Cloudflare Pages
/Vercel
사용을 고려했습니다. - 백엔드 배포를 위해 GCP
App Engine
(PaaS)과Compute Engine
(IaaS)을 검토했습니다. - 최종적으로 프론트엔드와 백엔드 모두
Docker
컨테이너화하여Google Cloud Run
에 배포하는 것으로 결정했습니다.
- 프론트엔드 배포를 위해 간편한
- 결정: 프론트엔드(
Next.js
)와 백엔드(NestJS
)를 모두 각각의Docker
컨테이너로 빌드하고,Google Cloud Run
에 배포한다. - 이유:
- 환경의 일관성:
Docker
를 사용하여 로컬 개발 환경과 클라우드 운영 환경을 100% 동일하게 유지할 수 있습니다. "내 컴퓨터에선 됐는데..."와 같은 문제를 원천적으로 방지합니다. - 배포 유연성:
Docker
컨테이너는 "웹 배포의 표준 규격"과 같습니다. 나중에AWS
,Azure
등 다른 클라우드 플랫폼이나 자체 서버로 이전해야 할 경우, 최소한의 변경으로 마이그레이션이 가능하여 특정 플랫폼에 종속되지 않습니다. - 통합된 CI/CD: 두 프로젝트 모두 "Docker 이미지 빌드 -> Cloud Run 배포"라는 동일한 형태의 CI/CD 파이프라인을
GitHub Actions
에서 구성할 수 있어 관리가 용이합니다.
- 환경의 일관성:
3. 도메인 아키텍처: 서브도메인 분리 방식 채택
- 날짜: 2025-06-20
- 논의:
- 경로 기반 라우팅 (
surfai.org/
vssurfai.org/api/
): 단일 도메인을 사용하여CORS
및 쿠키 문제를 근본적으로 해결할 수 있는 장점이 있었습니다. 하지만 앞단에 트래픽을 분배할 로드밸런서 설정이 추가로 필요했습니다. - 서브도메인 분리 (
app.surfai.org
vsapi.surfai.org
): 각 서비스의 역할을 명확히 분리하고, 독립적으로 관리하기에 용이한 표준적인 방식입니다.
- 경로 기반 라우팅 (
- 결정: 프론트엔드는
surfai.org
, 백엔드는api.surfai.org
와 같이 서브도메인을 분리하여 운영한다. - 이유:
- 초기 설정이 로드밸런서 구성보다 직관적이고 간단합니다. (
Cloud Run
의 커스텀 도메인 매핑 기능 활용) - 프론트엔드와 백엔드의 역할이 도메인 수준에서 명확하게 분리되어, 각 서비스에 대한 독립적인 보안 정책(방화벽, 캐싱 등)을 적용하기 용이합니다.
- 복잡했던 크로스 도메인 쿠키 문제는, 백엔드에서
sameSite: 'none'
,domain: 'surfai.org'
와 같이 올바른 쿠키 옵션을 설정함으로써 해결했습니다.
- 초기 설정이 로드밸런서 구성보다 직관적이고 간단합니다. (
4. 데이터 보존 정책: 파일 자동 삭제, 메타데이터 영구 보존
- 날짜: 2025-06-23
- 논의:
- 스토리지 비용 절감을 위해 2일이 지난 모든 데이터(파일+DB 레코드)를 삭제하는 방안을 고려했습니다.
- 하지만 이 경우 사용자가 자신의 과거 작업 기록(프롬프트, 파라미터 등)을 영원히 잃게 되는 부정적인 사용자 경험이 우려되었습니다.
- 결정: 실제 이미지/비디오 파일은 2일 후
Cloudflare R2
에서 자동으로 삭제하되, 생성 기록 메타데이터(DB 레코드)는 사용자가 직접 삭제하기 전까지 영구적으로 보존한다. - 이유:
- 가장 큰 용량을 차지하는 파일만 삭제하여 스토리지 비용을 효과적으로 절감할 수 있습니다.
- 사용자는 자신의 "히스토리" 페이지에서 과거 작업의 상세 정보(어떤 프롬프트를 썼는지 등)를 계속 확인할 수 있어 사용자 경험을 보존할 수 있습니다.
- 프론트엔드에서는 만료된 파일에 대해 "기간 만료" UI를 표시하여 사용자에게 명확한 피드백을 제공합니다.
5. 문서화 시스템: Docusaurus
채택
- 날짜: 2025-07-07
- 논의:
- 초기에는
GitHub Wiki
나 단순한README.md
파일을 사용하여 문서를 관리하는 방안을 고려했습니다. - 더 체계적인 관리를 위해
GitBook
과 같은 외부 SaaS 솔루션 사용도 검토했습니다. - 최종적으로, 코드와 문서를 동일한 저장소에서 관리하고, React 기반의 커스터마이징이 용이한
Docusaurus
를 채택하기로 결정했습니다.
- 초기에는
- 결정: 프로젝트의 공식 문서화 도구로
Docusaurus
를 사용하고,surfai-docs
라는 별도의 모듈로 관리한다. - 이유:
- 살아있는 문서 (Living Documentation): 모든 문서가 Markdown(
.md
,.mdx
) 파일로 관리되므로, 코드와 함께 Git으로 버전 관리가 가능합니다. 이는 코드 변경과 문서 업데이트의 동기화를 용이하게 하여 항상 최신 상태를 유지하는 데 도움을 줍니다. - 개발자 친화성: 개발자들은 익숙한 Markdown 문법으로 문서를 작성하고, 필요시 React 컴포넌트를 문서 내에 직접 삽입(
MDX
)하여 인터랙티브한 문서를 만들 수 있습니다. - 쉬운 접근성: 빌드된 결과물은 정적 웹사이트이므로, 비개발 직군을 포함한 모든 팀원이 별도의 도구 없이 웹 브라우저만으로 항상 최신화된 문서에 쉽게 접근하고 검색할 수 있습니다.
- 강력한 확장성: 다국어 지원, 문서 버전 관리, 검색 기능(Algolia) 등 복잡한 요구사항을 플러그인과 커스터마이징을 통해 손쉽게 해결할 수 있습니다.
- 살아있는 문서 (Living Documentation): 모든 문서가 Markdown(