🔗 Milestone #6
📅 2026년 2월 3주차
이번 주는 '1차 릴리즈 완성'이라는 하나의 목표만을 바라보고 전력 질주했습니다. 그 과정에서 단순히 기능을 완성하는 것을 넘어, 팀원 모두가 '우리가 진짜 팀으로 움직이고 있구나'를 느낀 소중한 시간이었는데요. 구체적으로 어떤 변화가 있었는지 기록해 봅니다.
가장 시급했던 필수 이슈들(#24, #87, #184, #187, #189)을 모두 해결했습니다. 이제 사용자들에게 우리 서비스를 선보일 준비가 거의 끝났습니다!
팀 내부적으로 진행한 피드백 시간은 서로를 더 신뢰하게 만드는 계기가 되었습니다.
"전체적으로 프로젝트가 안정된 느낌이에요. 특히 이번 디자인이 정말 맘에 듭니다!"
— 동료의 칭찬 한마디가 가장 큰 동기부여가 됐습니다.
물론 아쉬운 점도 있었습니다. "나도 디자인 과정에 더 깊게 참여하고 싶다"는 의견이 있었는데요. 이는 팀원이 프로젝트에 그만큼 애정이 깊다는 증거이기도 하죠. 그래서 다음 릴리즈부터는 기획 단계부터 디자인 협업 기회를 더 넓혀보기로 약속했습니다!
이번에는 기술적 의사결정 파트를 블로그 독자들이 흥미진진하게 읽을 수 있도록 바꿔볼게요. 단순히 "이걸 썼다"가 아니라, "어떤 고민 끝에 이런 설계를 했는지" 개발자의 깊은 속사정이 드러나게 구성하는 것이 포인트입니다.
회원이 탈퇴할 때 연결된 Google 캘린더 예약도 함께 정리되어야 합니다. 하지만 우리 DB와 외부 API는 한 몸이 아니죠. 이 '동기화' 문제를 해결하기 위해 세 가지 선택지를 두고 고민했습니다.
| 대안 | 고민의 결과 | 판단 |
|---|---|---|
| Soft Delete + 스케줄러 | 30일 뒤에 지우면 안전하겠지만, 그동안 쌓일 유령 데이터가 걱정됐습니다. | ❌ |
| 예약 있으면 탈퇴 차단 | 구현은 쉽지만, "예약 취소하고 오세요"라는 UX는 이탈을 부르는 지름길이라 판단했습니다. | ❌ |
| 즉시 취소 + 이벤트 기반 | 확장성이 좋지만, 이벤트가 중간에 유실될 리스크를 어떻게 잡을지가 관건이었습니다. | ✅ 채택 |
완전한 Message Queue를 도입하기엔 오버엔지니어링이라 판단하여, 스프링의 @TransactionalEventListener(AFTER_COMMIT)을 활용했습니다.
// 한 건이 실패해도 다른 예약의 취소는 멈추지 않습니다.
for (Booking booking : bookings) {
try {
deleteGoogleCalendarEvent(booking);
} catch (Exception e) {
log.error("Google Calendar 삭제 실패 - bookingId: {}", booking.getId(), e);
// 실패 건은 로그를 남겨 추후 재처리할 수 있는 여지를 둡니다.
}
}
이 과정을 통해 Google API 장애 상황에서도 서비스의 핵심 로직인 '탈퇴'는 100% 정상 작동하도록 설계했습니다. 덤으로 캘린더 조회 쿼리 성능을 에서 로 끌어올리며 N+1 문제까지 깔끔하게 해결하는 성과를 얻었습니다.
운영 배포를 앞두고 진행한 컨테이너 재시작 테스트 도중 아찔한 상황을 마주했습니다. 공들여 쌓은 데이터가 컨테이너와 함께 소멸해버린 것이죠. 배포 후에 발견했다면 대형 사고였겠지만, 다행히 선제적 테스트를 통해 리스크를 잡아냈습니다.
단순히 "고치자"를 넘어, 현재 우리 팀의 규모와 비용을 고려해 가장 효율적인 선택지를 비교했습니다.
저희는 '막연하게 나중에'라고 하지 않습니다. 팀원들과 논의 끝에 SQLite에서 PostgreSQL로 넘어갈 명확한 전환 지표를 정의했습니다.
편리한 개발을 돕는 TanStack Router DevTools, 하지만 유저가 사용하는 프로덕션 환경에서는 짐일 뿐입니다. 단순히 눈에 안 보이게 가리는 것을 넘어, 번들 파일에서 아예 삭제해버리는 최적화 과정을 거쳤습니다.
처음에는 단순히 조건부 렌더링을 생각했습니다. 하지만 이 방식은 코드 실행만 안 될 뿐, 브라우저가 내려받는 JS 파일에는 여전히 DevTools 코드가 포함되어 용량을 차지합니다.
저희는 빌드 타임 분기를 통해 'Dead Code Elimination'을 유도했습니다.
// ✅ 빌드 시점에 코드를 완전히 제거하는 방식
const TanStackRouterDevtools = import.meta.env.DEV
? lazy(() => import('@tanstack/router-devtools'))
: () => null;
import.meta.env.DEV를 false라는 상수로 치환합니다. 그러면 번들러는 "이 조건문은 평생 실행될 일이 없네?"라고 판단하고 해당 import 문과 코드를 번들에서 통째로 들어냅니다.dist/ 폴더 내에서 grep 명령어로 해당 패키지명이 남아있는지 철저히 확인하며 성능을 챙겼습니다.모든 좋은 기술을 다 쓰고 싶지만, 지금 우리 팀에게 가장 중요한 건 '속도'와 '생존'입니다. 저희는 성장에 맞춰 인프라를 확장하기 위해 '의도적인 기술 부채' 목록을 만들고, 언제든 꺼내 볼 수 있도록 전환 트리거를 정의했습니다.
| 항목 | 현재 상태 | 전환 트리거 (언제 바꿀까?) | 판단 근거 |
|---|---|---|---|
| DB 구조 | SQLite + Volume | 동시접속 100+ | 쓰기 잠금(Write Lock) 병목 예상 시점 |
| 서킷 브레이커 | 미적용 | 외부 API 3개 이상 | 현재는 구글 캘린더 하나라 관리가능 |
| 아키텍처 | Layered 아키텍처 | Entity 10개 이상 | 구조적 복잡도가 오버헤드보다 커질 때 |
| 검색 엔진 | 미도입 | 로그 100만 건 이상 | 현재는 DB 쿼리만으로 충분 |
"기술을 몰라서 안 쓰는 것이 아니라, 지금 필요하지 않아서 안 쓰는 것"이 저희 팀의 원칙입니다. 덕분에 핵심 비즈니스 로직에 더 집중할 수 있었습니다.
처음엔 욕심이 생겼습니다. 단순히 이메일 형식이 맞는지를 넘어, 실제 존재하는 계정인지 SMTP 서버에 직접 물어보는 방식을 검토했죠. 하지만 조사 결과, 왜 오늘날 서비스들이 정규식 검증에 머무르는지 흥미로운 이유를 발견했습니다.
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)라는 업계 표준 방식을 채택하기로 했습니다. "왜?"라는 질문 덕분에 기술의 역사와 현재의 트렌드를 깊이 이해할 수 있었던 의미 있는 시간이었습니다.
릴리즈 직전까지 저희를 괴롭혔던 문제들도 깔끔하게 매듭지었습니다. 특히 Bulletproof React 패턴을 지향하며 진행한 캘린더 리팩토링은 코드의 지속 가능성을 한 단계 높여주었습니다.
1차 릴리즈가 튼튼한 뼈대를 세우는 과정이었다면, 2차 릴리즈부터는 서비스의 '개성'을 더할 고난도 기술들이 기다리고 있습니다.
미팅 내용을 요약해주고, 맞춤법을 교정해주며, 글을 자동으로 생성해주는 AI 기능을 통해 유저의 생산성을 극대화할 계획입니다.
기술만큼이나 중요한 건 팀의 운영 방식이었습니다. 이번 주에는 팀장의 역할에 대해 다시 정의해보고, 연차가 높은 팀원과 어떻게 더 시너지를 낼 수 있을지 깊이 있는 논의를 나눴습니다.
또한, 저희 팀에 신선한 활력을 불어넣어 줄 새로운 FE 팀원 영입도 준비 중입니다. 현재 저희 프로젝트에 소중한 컨트리뷰션을 해주고 계신 펭귄님과 설 연휴 이후 미팅을 가질 예정인데요. 좋은 인연이 되길 기대하고 있습니다.
우리의 고민이 휘발되지 않도록 아키텍처, ERD, API 설계, CI/CD 파이프라인 등 모든 과정은 팀 위키(Wiki)에 기록하고 있습니다. 또한 AWS 비용 관리와 보안(Secrets Manager) 설정을 마쳐, 서비스가 안정적으로 운영될 수 있는 기반 시설도 튼튼히 다져두었습니다.
1차 릴리즈는 끝이 아니라 시작입니다. 기술적 호기심과 실용성 사이에서 균형을 잡으며 성장하는 cohi-chat 팀의 다음 이야기도 기대해 주세요!