1. 개요

DoEatFit 서비스에 사용자의 참여를 극대화하기 위해 웹 브라우저/모바일 PWA 환경에서도 OS 네이티브 수준의 푸시 알림을 보낼 수 있는 FCM(Firebase Cloud Messaging) 기술을 성공적으로 도입한 과정과 그 이면의 아키텍처 및 보안 연동에 대한 회고록입니다.

FCM은 구글이 제공하는 무료 푸시 메시지 중계 서버입니다. 우리가 모든 사용자의 각기 다른 브라우저와 기기(아이폰, 안드로이드, 윈도우 등)의 통신 방식을 맞춰가며 직접 메시지를 쏴주는 것은 현실적으로 불가능합니다. 따라서 **“우리가 편지(알림)를 써서 구글 우체국(FCM)에 던져주면, 구글 우체부가 알아서 전 세계의 기기들을 찾아가 편지를 배달해 주는 마법”**과 같은 기술입니다.


2. 기술 설명 및 서비스 구현 방법

FCM 시스템은 크게 편지를 보내는 쪽(백엔드)과 편지를 받는 쪽(프론트엔드)으로 나뉘어 구현됩니다.

2-1. 프론트엔드 (편지 수신자 세팅)

프론트엔드(Next.js)에서는 우편함을 튼튼하게 세우고, 내가 누구인지 구글 우체국에 등록해 고유 식별 번호(Token)를 받아야 합니다.

  • Service Worker (sw.ts): 브라우저 탭이 꺼져있거나 백그라운드에 있을 때도 우체부가 편지를 몰래 우편함에 넣고 가면, 이 서비스 워커가 “알림 왔어요!” 하고 OS에 팝업을 띄워주는 24시간 경비원 역할을 수행합니다.
  • VAPID Key 인증: 무분별한 스팸 우편을 막기 위해, 구글 우체국에 내 웹앱이 정상적인 수신처임을 인증하는 공개키 쌍입니다. 보안을 위해 소스 코드에 하드코딩하지 않고 환경 변수로 은닉했습니다.
    # .env (보안 처리됨)
    NEXT_PUBLIC_FIREBASE_API_KEY={YOUR_API_KEY}
    NEXT_PUBLIC_FIREBASE_VAPID_KEY={YOUR_VAPID_KEY}

2-2. 백엔드 (편지 발송자 세팅)

백엔드(Spring Boot)는 구글 우체국에 “이 편지를 이 사람들에게 배달해!” 라고 명령을 내리는 주체이므로 막강한 권한이 필요합니다.

  • 비공개 서비스 계정 키 (Service Account Key): 이것은 구글 우체국에 명령을 내릴 수 있는 **마스터 키(Master Key)**입니다.
  • FcmService 비동기 발송: 푸시 알림 발송은 외부 API를 호출하는 네트워크 I/O 작업이므로, 기존 사용자 서비스(DB 저장 등)가 느려지는 것을 방지하기 위해 Spring의 @Async를 부여해 별도의 스레드에서 조용히 백그라운드로 발송되도록 최적화했습니다.

3. 어떻게 호출해서 사용하는가? (단일 진입점 아키텍처)

가장 심혈을 기울인 아키텍처 포인트는 “기존 코드를 건드리지 않고 어떻게 시스템 전체에 푸시 알림을 끼워 넣을 것인가?” 였습니다. DoEatFit 서비스 내에는 친구 요청, 조건 달성, 로그 작성 등 무수히 많은 알림 발생 위치가 존재합니다. 이를 일일이 찾아가 FCM 발송 코드를 넣는 것은 비효율의 극치입니다.

  • Single Entry Point (단일 진입점) 기존 서비스의 모든 비즈니스 로직은 단 하나의 공통 메서드인 NotificationService.createNotification()을 거쳐 DB에 알림을 저장하고 있었습니다.
  • 해결책: 바로 이 createNotification() 메서드 내부 가장 마지막 줄에 단 한 줄, fcmService.sendPushToUser(...)를 삽입했습니다.
  • 결과: 기존의 수많은 비즈니스 로직들은 자기가 FCM을 호출하는지도 모른 채 단순히 알림을 DB에 저장했을 뿐인데, 내부적으로 자동으로 구글 우체국에 편지가 위탁되는 마법 같은 결합도를 달성했습니다. 앞으로 새로운 알림 로직이 추가되어도 FCM 연동을 잊어버릴 일이 없습니다.

4. 보안과 CI/CD 자동화 구축 (GitHub Actions)

백엔드가 구글 우체국과 통신하기 위한 마스터 키(serviceAccountKey.json)는 깃허브(GitHub)에 절대 올라가서는 안 되는 치명적인 보안 파일입니다. 이를 안전하게 다루기 위해 **“GitHub Actions Secrets를 활용한 동적 파일 주입 기법”**을 채택했습니다.

  1. 마스터 키의 은닉: JSON 키 파일 내용을 깃허브 리포지토리의 보안 창고(Settings -> Secrets)에 {FIREBASE_SERVICE_ACCOUNT}라는 이름으로 봉인해 두었습니다. 로컬 코드 저장소에는 파일이 아예 존재하지 않습니다.
  2. 동적 주입 파이프라인: 빌드 자동화 도구인 GitHub Actions 워크플로우에 다음 단계를 추가하여, CI/CD 배포가 도는 순간에만 허공에서 파일을 만들어내어 백엔드 품에 쏙 넣어주고 빌드를 진행하게 설계했습니다.
    # .github/workflows/deploy.yml 중 일부
    - name: Create Firebase Service Account Key JSON
      run: |
        echo '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}' > src/main/resources/serviceAccountKey.json

결과적으로, 인프라의 서버(리눅스/도커) 환경에 수동으로 마스터 키 파일을 들고 다니며 관리해야 하는 고통을 완전히 제거했고, 소스코드의 완벽한 무결성과 보안을 동시에 이루어낸 우수 사례(Best Practice)로 자리 잡았습니다.