[6주자] 1차 릴리즈를 향한 질주

코헤·2026년 2월 26일

cohiChat

목록 보기
9/10

🔗 Milestone #6
📅 2026년 2월 3주차

🏃 1차 릴리즈를 향한 질주, 그리고 팀의 성장

이번 주는 '1차 릴리즈 완성'이라는 하나의 목표만을 바라보고 전력 질주했습니다. 그 과정에서 단순히 기능을 완성하는 것을 넘어, 팀원 모두가 '우리가 진짜 팀으로 움직이고 있구나'를 느낀 소중한 시간이었는데요. 구체적으로 어떤 변화가 있었는지 기록해 봅니다.

✅ 릴리즈의 마지막 퍼즐을 맞추다

가장 시급했던 필수 이슈들(#24, #87, #184, #187, #189)을 모두 해결했습니다. 이제 사용자들에게 우리 서비스를 선보일 준비가 거의 끝났습니다!

🙌 우리가 정말 '잘했다'고 생각하는 점들

  • 물 흐르듯 자연스러워진 Git 워크플로우
    처음엔 브랜치 전략이나 PR 규칙이 낯설어 삐걱대기도 했지만, 이제는 약속이라도 한 듯 자연스럽게 프로젝트가 운영되고 있습니다. 협업의 규칙이 '짐'이 아니라 '가이드'가 된 순간이죠.
  • 날카롭고 따뜻한 코드 리뷰
    서로의 코드를 보는 눈이 몰라보게 높아졌습니다. 이제는 지엽적인 문법보다는 아키텍처의 핵심을 짚어내는 리뷰가 오가고 있고, 덕분에 리뷰의 질과 속도라는 두 마리 토끼를 잡을 수 있었습니다.
  • 체감되는 개발 속도의 가속
    불과 얼마 전까지만 해도 2주가 꼬박 걸리던 기능들을 이제는 단 이틀 만에 뚝딱 완성해내고 있습니다. 서로의 스타일을 이해하고 재사용 가능한 구조를 잘 쌓아둔 덕분이라 생각합니다.

💬 솔직담백한 개인 피드백

팀 내부적으로 진행한 피드백 시간은 서로를 더 신뢰하게 만드는 계기가 되었습니다.

"전체적으로 프로젝트가 안정된 느낌이에요. 특히 이번 디자인이 정말 맘에 듭니다!"
동료의 칭찬 한마디가 가장 큰 동기부여가 됐습니다.

물론 아쉬운 점도 있었습니다. "나도 디자인 과정에 더 깊게 참여하고 싶다"는 의견이 있었는데요. 이는 팀원이 프로젝트에 그만큼 애정이 깊다는 증거이기도 하죠. 그래서 다음 릴리즈부터는 기획 단계부터 디자인 협업 기회를 더 넓혀보기로 약속했습니다!

이번에는 기술적 의사결정 파트를 블로그 독자들이 흥미진진하게 읽을 수 있도록 바꿔볼게요. 단순히 "이걸 썼다"가 아니라, "어떤 고민 끝에 이런 설계를 했는지" 개발자의 깊은 속사정이 드러나게 구성하는 것이 포인트입니다.


3. 기술적 고민의 흔적들: 더 나은 설계란 무엇일까

3-1. "회원이 탈퇴했는데 예약이 남아있다면?" (분산 시스템의 일관성)

회원이 탈퇴할 때 연결된 Google 캘린더 예약도 함께 정리되어야 합니다. 하지만 우리 DB와 외부 API는 한 몸이 아니죠. 이 '동기화' 문제를 해결하기 위해 세 가지 선택지를 두고 고민했습니다.

대안고민의 결과판단
Soft Delete + 스케줄러30일 뒤에 지우면 안전하겠지만, 그동안 쌓일 유령 데이터가 걱정됐습니다.
예약 있으면 탈퇴 차단구현은 쉽지만, "예약 취소하고 오세요"라는 UX는 이탈을 부르는 지름길이라 판단했습니다.
즉시 취소 + 이벤트 기반확장성이 좋지만, 이벤트가 중간에 유실될 리스크를 어떻게 잡을지가 관건이었습니다.✅ 채택

💡 해결책: Transactional Outbox 패턴의 실용적 접근

완전한 Message Queue를 도입하기엔 오버엔지니어링이라 판단하여, 스프링의 @TransactionalEventListener(AFTER_COMMIT)을 활용했습니다.

  • 원자성 보장: 우리 DB의 탈퇴 처리가 '완벽히 성공(Commit)'했을 때만 캘린더 삭제 로직을 실행합니다.
  • 장애 격리 (Bulkhead 패턴): 여러 예약 건 중 하나가 Google API 연동에 실패하더라도, 나머지 예약 취소에 영향을 주지 않도록 로직을 격리했습니다.
// 한 건이 실패해도 다른 예약의 취소는 멈추지 않습니다.
for (Booking booking : bookings) {
    try {
        deleteGoogleCalendarEvent(booking);
    } catch (Exception e) {
        log.error("Google Calendar 삭제 실패 - bookingId: {}", booking.getId(), e);
        // 실패 건은 로그를 남겨 추후 재처리할 수 있는 여지를 둡니다.
    }
}

📈 어떤 결과가 있었나요?

이 과정을 통해 Google API 장애 상황에서도 서비스의 핵심 로직인 '탈퇴'는 100% 정상 작동하도록 설계했습니다. 덤으로 캘린더 조회 쿼리 성능을 O(n)O(n)에서 O(1)O(1)로 끌어올리며 N+1 문제까지 깔끔하게 해결하는 성과를 얻었습니다.

3-2. "컨테이너를 껐더니 데이터가 사라졌다?" (Docker 영속성 확보)

운영 배포를 앞두고 진행한 컨테이너 재시작 테스트 도중 아찔한 상황을 마주했습니다. 공들여 쌓은 데이터가 컨테이너와 함께 소멸해버린 것이죠. 배포 후에 발견했다면 대형 사고였겠지만, 다행히 선제적 테스트를 통해 리스크를 잡아냈습니다.

🧐 우리가 고민한 세 가지 선택지

단순히 "고치자"를 넘어, 현재 우리 팀의 규모와 비용을 고려해 가장 효율적인 선택지를 비교했습니다.

  • Named Volume (현재 적용): Docker가 관리하는 영역에 데이터를 저장합니다. 단일 호스트 환경인 지금 우리에게 가장 깔끔하고 충분한 대안이었습니다.
  • Bind Mount: 호스트 시스템의 특정 경로에 직접 연결하는 방식입니다. 하지만 호스트 경로에 의존성이 생겨 이식성이 떨어질 것 같아 배제했습니다.
  • 외부 DB (RDS): 가장 안정적이지만, 아직 서비스 초기 단계인 만큼 비용 대비 효율을 고려해 '나중'을 기약하기로 했습니다.

🎯 "언제 DB를 바꿀 것인가?" 명확한 기준 설정

저희는 '막연하게 나중에'라고 하지 않습니다. 팀원들과 논의 끝에 SQLite에서 PostgreSQL로 넘어갈 명확한 전환 지표를 정의했습니다.

  • 전환 트리거: 동시 접속자 100명 또는 MAU(월간 활성 사용자) 1,000명 돌파 시
  • 이유: SQLite의 강점인 가벼움이 'Write Lock(쓰기 잠금)' 병목 현상으로 인해 서비스 품질을 저하시키기 시작하는 시점이기 때문입니다.

3-3. "DevTools가 왜 거기서 나와?" (프로덕션 번들 최적화)

편리한 개발을 돕는 TanStack Router DevTools, 하지만 유저가 사용하는 프로덕션 환경에서는 짐일 뿐입니다. 단순히 눈에 안 보이게 가리는 것을 넘어, 번들 파일에서 아예 삭제해버리는 최적화 과정을 거쳤습니다.

🛠️ 단순 해결 vs 근본 해결

처음에는 단순히 조건부 렌더링을 생각했습니다. 하지만 이 방식은 코드 실행만 안 될 뿐, 브라우저가 내려받는 JS 파일에는 여전히 DevTools 코드가 포함되어 용량을 차지합니다.

저희는 빌드 타임 분기를 통해 'Dead Code Elimination'을 유도했습니다.

// ✅ 빌드 시점에 코드를 완전히 제거하는 방식
const TanStackRouterDevtools = import.meta.env.DEV
  ? lazy(() => import('@tanstack/router-devtools'))
  : () => null;
  • 작동 원리: Vite(esbuild)가 빌드할 때 import.meta.env.DEVfalse라는 상수로 치환합니다. 그러면 번들러는 "이 조건문은 평생 실행될 일이 없네?"라고 판단하고 해당 import 문과 코드를 번들에서 통째로 들어냅니다.
  • 검증: 빌드 후 dist/ 폴더 내에서 grep 명령어로 해당 패키지명이 남아있는지 철저히 확인하며 성능을 챙겼습니다.

3-4. "오버엔지니어링은 사치다" (의도적인 기술 부채 관리)

모든 좋은 기술을 다 쓰고 싶지만, 지금 우리 팀에게 가장 중요한 건 '속도''생존'입니다. 저희는 성장에 맞춰 인프라를 확장하기 위해 '의도적인 기술 부채' 목록을 만들고, 언제든 꺼내 볼 수 있도록 전환 트리거를 정의했습니다.

항목현재 상태전환 트리거 (언제 바꿀까?)판단 근거
DB 구조SQLite + Volume동시접속 100+쓰기 잠금(Write Lock) 병목 예상 시점
서킷 브레이커미적용외부 API 3개 이상현재는 구글 캘린더 하나라 관리가능
아키텍처Layered 아키텍처Entity 10개 이상구조적 복잡도가 오버헤드보다 커질 때
검색 엔진미도입로그 100만 건 이상현재는 DB 쿼리만으로 충분

"기술을 몰라서 안 쓰는 것이 아니라, 지금 필요하지 않아서 안 쓰는 것"이 저희 팀의 원칙입니다. 덕분에 핵심 비즈니스 로직에 더 집중할 수 있었습니다.

3-5. "왜 이메일 실존 여부는 정규식으로만 체크할까?" (업계 표준 조사)

처음엔 욕심이 생겼습니다. 단순히 이메일 형식이 맞는지를 넘어, 실제 존재하는 계정인지 SMTP 서버에 직접 물어보는 방식을 검토했죠. 하지만 조사 결과, 왜 오늘날 서비스들이 정규식 검증에 머무르는지 흥미로운 이유를 발견했습니다.

✉️ 한때는 가능했던 '실존 확인' (SMTP RCPT TO)

2000년대 초반까지만 해도 메일 서버에 "이 메일 주소 있어?"라고 물어보면 친절하게 답해주던 시절이 있었습니다.

MAIL FROM:<verify@service.com>
RCPT TO:<target@gmail.com>
→ 서버: "250 OK (응, 있어!)" 또는 "550 Not Found (없는데?)"

🛡️ 하지만 지금은 '철벽 보안' 시대

조사 결과, 현재 주요 메일 서비스들은 보안과 스팸 방지를 위해 이 방식을 철저히 차단하고 있었습니다.

서버현재 정책이유
Gmail항상 250 OK 응답없는 주소라고 알려주면 스팸 발송자가 이메일 목록을 수집하기 때문
Microsoft요청 IP 블랙리스트 등록짧은 시간에 실존 여부를 계속 묻는 IP는 공격자로 간주
Yahoo항상 250 OK 응답스팸 방지 및 개인정보 보호

💡 결론: 클래식은 영원하다

RFC 5321 표준에는 여전히 명시되어 있지만, 실제 현대의 메일 서버 생태계에서는 더 이상 유효하지 않은 방식이 된 것이죠. 결국 저희는 탄탄한 정규식 검증 + 이메일 인증 링크(OTP)라는 업계 표준 방식을 채택하기로 했습니다. "왜?"라는 질문 덕분에 기술의 역사와 현재의 트렌드를 깊이 이해할 수 있었던 의미 있는 시간이었습니다.

4. 주요 이슈 해결 및 리팩토링

릴리즈 직전까지 저희를 괴롭혔던 문제들도 깔끔하게 매듭지었습니다. 특히 Bulletproof React 패턴을 지향하며 진행한 캘린더 리팩토링은 코드의 지속 가능성을 한 단계 높여주었습니다.

  • P1 이슈 해결: 프로덕션 환경에서 불필요한 개발자 도구가 노출되던 문제를 완벽히 차단했습니다.
  • 사용자 경험(UX) 개선: 회원가입과 로그인 폼의 동선을 다듬고, 호스트 등록 과정에서 발생할 수 있는 에러 상태를 꼼꼼하게 관리하여 유저가 길을 잃지 않게 했습니다.
  • 코드 품질 향상: 코드 리뷰 피드백을 적극 반영해 죽은 코드(Dead Code)를 걷어내고, 흩어져 있던 API URL 상수를 중앙화하는 등 '읽기 좋은 코드'를 만드는 데 집중했습니다.

5. Next Step: 2차 릴리즈

1차 릴리즈가 튼튼한 뼈대를 세우는 과정이었다면, 2차 릴리즈부터는 서비스의 '개성'을 더할 고난도 기술들이 기다리고 있습니다.

🚀 2차 릴리즈: 실시간성과 연결성

  • WebRTC 기반 1:N 화상 회의: 중앙 미디어 서버를 도입해 안정적인 다자간 미팅 환경을 구축합니다.
  • 동시 편집 문서 (CRDT): Yjs와 Milkdown을 활용해 유저들이 실시간으로 문서를 함께 편집하는 경험을 선사합니다.
  • 이벤트 드리븐 채팅: Kafka를 도입해 확장성 있는 채팅 시스템으로 전환할 예정입니다.

✨ 3차 릴리즈: 똑똑한 AI 비서

미팅 내용을 요약해주고, 맞춤법을 교정해주며, 글을 자동으로 생성해주는 AI 기능을 통해 유저의 생산성을 극대화할 계획입니다.


🤝 팀의 성장과 새로운 동료

기술만큼이나 중요한 건 팀의 운영 방식이었습니다. 이번 주에는 팀장의 역할에 대해 다시 정의해보고, 연차가 높은 팀원과 어떻게 더 시너지를 낼 수 있을지 깊이 있는 논의를 나눴습니다.

또한, 저희 팀에 신선한 활력을 불어넣어 줄 새로운 FE 팀원 영입도 준비 중입니다. 현재 저희 프로젝트에 소중한 컨트리뷰션을 해주고 계신 펭귄님과 설 연휴 이후 미팅을 가질 예정인데요. 좋은 인연이 되길 기대하고 있습니다.


📚 기록은 기억을 지배한다 (Wiki & Infrastructure)

우리의 고민이 휘발되지 않도록 아키텍처, ERD, API 설계, CI/CD 파이프라인 등 모든 과정은 팀 위키(Wiki)에 기록하고 있습니다. 또한 AWS 비용 관리와 보안(Secrets Manager) 설정을 마쳐, 서비스가 안정적으로 운영될 수 있는 기반 시설도 튼튼히 다져두었습니다.


💡 마무리하며

1차 릴리즈는 끝이 아니라 시작입니다. 기술적 호기심과 실용성 사이에서 균형을 잡으며 성장하는 cohi-chat 팀의 다음 이야기도 기대해 주세요!

profile
하이하이

0개의 댓글