MeetBack 2026.08.19 6 min read

MeetBack, 약속을 정하는 순간부터 운영까지 직접 만들어 가는 팀 프로젝트

MeetBack에서 Google 로그인과 Redis 실시간 공통 모듈, Swarm 기반 인프라를 담당했다. 프로젝트 진행 중 구현한 범위와 남은 연동 작업을 기록한다.

프로젝트 기간: 2026-08-18 ~ 2026-09-02
작성 기준: 2026-08-29, 프로젝트 진행 중

여러 사람의 가능한 시간을 모아 약속을 정하는 MeetBack을 팀 프로젝트로 만들고 있다. 가능한 시간을 공유하고 모임을 만든 뒤 실시간으로 의견을 나누는 서비스다.

나는 2026년 8월 18일부터 9월 2일까지 진행되는 이 프로젝트에서 백엔드 기능과 인프라를 함께 담당하고 있다. 구현한 기능을 여러 서버에서 실행하고 장애가 났을 때의 동작까지 확인해 보고 싶었다. Google 로그인과 Redis Pub/Sub 실시간 공통 모듈을 구현했고, Docker Swarm 실행 환경과 데이터베이스·스토리지·모니터링·외부 진입 경로도 설계하고 구축했다.

내가 맡은 개발 영역

첫 번째는 Google Identity Services 로그인이다.

브라우저가 전달하는 이메일이나 이름을 그대로 신뢰하지 않고, Google이 서명한 OpenID Connect ID Token을 백엔드에서 공식 검증기로 확인하도록 구현했다. 이때 이메일이 아니라 Google 계정의 안정적인 식별자인 sub를 서비스의 providerId로 사용했다. 같은 이메일의 일반 계정이 존재하더라도 자동으로 계정을 합치지 않도록 막아, 이메일 문자열 일치만으로 계정 소유권을 넘길 수 없게 했다.

처음 구현한 버전은 신규 Google 사용자를 즉시 가입시키는 흐름이었다. 팀의 인증 기능과 통합하면서 내가 만든 Token 검증과 계정 매핑에 10분짜리 SOCIAL_SIGNUP JWT를 사용하는 2단계 가입 흐름이 연결됐다. 현재는 Google 인증 뒤 사용자가 닉네임과 필수 약관을 선택해야 가입된다. 내가 담당한 부분은 Google 검증과 계정 식별 기반이며, 후속 가입 흐름은 팀 통합 과정에서 확장한 결과다.

두 번째는 Docker Swarm의 여러 애플리케이션 replica에서 실시간 이벤트를 공유하기 위한 Redis Pub/Sub 공통 모듈이다.

Spring의 SimpleBroker는 JVM 내부에만 상태가 존재한다. 따라서 사용자가 서로 다른 replica에 WebSocket으로 연결되면, 한 replica가 보낸 채팅·투표 같은 이벤트를 다른 replica의 사용자가 받지 못할 수 있다. Swarm VIP는 연결을 분산해 줄 뿐 JVM 메모리까지 공유하지 않는다.

기존 SimpleBroker와 클라이언트 메시지 계약을 유지하면서 replica 사이의 이벤트를 Redis Pub/Sub으로 전달하는 공통 계층을 만들었다. 업무 이벤트와 Redis 전송용 envelope를 분리하고, 요청을 받은 replica에서 먼저 로컬 전달을 시도한 뒤 Redis로 전파하도록 했다. Redis에 문제가 생겨도 현재 replica의 로컬 전달은 시도할 수 있게 한 local-first 구조다.

공통 모듈은 main에 병합했지만 채팅·투표·강퇴 기능과의 연결은 남아 있다. 기존 직접 방송 코드를 RealtimeEventPublisher 호출로 바꾸고, WebSocket 세션 종료와 Redis 기반 전역 Presence도 구현해야 한다. Redis Pub/Sub에는 메시지 영속성이나 exactly-once 보장이 없고, 계획한 단일 Redis도 장애점으로 남는다.

내가 구축한 인프라 영역

실행 환경은 아래처럼 역할을 나눠 구성했다.

  • Docker Swarm Manager 3대와 Worker 2대
  • MySQL Primary 1대와 Read Only Replica 1대
  • 서비스 파일용 NFS와 백업·로그용 NFS
  • Prometheus, Grafana, Alertmanager, Loki, Alloy를 운영하는 모니터링 서버
  • 서비스망과 DB 전용망을 연결하는 VyOS 라우터

Swarm에서는 overlay network와 Service DNS/VIP를 확인했고, 데이터베이스는 MySQL 8의 GTID와 TLS 기반 비동기 복제로 구성했다. Replica에는 read_only와 super_read_only를 적용해 평상시 쓰기를 차단했다. 자동 승격보다 데이터 정합성과 원인 확인을 우선해 수동 failover 절차를 선택했다.

스토리지는 하드웨어 RAID5 위에 LVM과 ext4를 구성하고 NFS 역할을 분리했다. MySQL 데이터 디렉터리를 NFS에 올리지는 않았다. DB 데이터는 로컬 디스크에 두고, mysqldump 결과를 압축하고 SHA-256 체크섬과 함께 별도 NFS에 보관하는 방식으로 만들었다. 백업 파일이 만들어지는 것에 그치지 않고 별도 시험 DB에 복원해 보는 과정까지 확인했다.

운영 가시성을 위해 전체 노드와 컨테이너, MySQL, NFS 상태를 Prometheus로 수집하고 Grafana 대시보드를 구성했다. Alertmanager는 Telegram으로 알림을 전달하도록 연결했다. 외부 진입의 기본 경로는 Cloudflare Tunnel로 두고, 각 Swarm 노드의 cloudflared connector가 애플리케이션 Service DNS/VIP를 바라보도록 설계했다. NGINX Proxy Manager와 DNS-01 wildcard 인증서도 준비했지만, 공인 IP와 NAT를 사용할 수 없는 현재 환경에서는 예비 외부 경로의 E2E 검증을 완료했다고 말하지 않는다.

지금 어디까지 왔는가

현재는 인프라와 운영 기반을 구축하고 실제 애플리케이션 stack 연결을 기다리고 있다. 애플리케이션 개발이 끝나면 NFS 권한, Docker Secret, placement와 rolling update 정책을 실제 서비스에 맞춰 확정해야 한다. Redis 공통 모듈 역시 실제 기능 호출부와 두 replica 환경의 WebSocket E2E 검증이 남아 있다.

CI/CD는 이번 프로젝트 범위에서 제외했다. 기간 안에 네트워크·데이터·스토리지·관측 환경을 직접 구성하고 검증할 수 있는 범위로 작업을 정했다.

이후 글에서는 Google 로그인, Redis 실시간 모듈과 인프라 작업을 나눠 기록하려 한다. 구현과 검증을 마친 부분, 팀과 통합하면서 바뀐 부분, 아직 남은 작업을 구분해 남길 예정이다.

다음 글에서는 Google ID Token을 검증하는 로그인 흐름과 이메일 대신 sub로 계정을 식별한 이유를 정리한다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동