- 🌟 프로젝트 소개
- 📊 프로젝트 성과
- 🛠️ 기술 스택
- 🖼️ 앱 스크린샷
- 🏗️ 시스템 아키텍처
- 🚀 기술적 도전 과제 & 해결 방법
- 💻 코드 스니펫 (Cloud Functions)
- 👤 역할
- 📚 배운 점
- ✨ 한 줄 정리
반디(Bandi) 는 AI 기반 감정 분석과 회고 기능을 제공하는 일상 감정 관리 플랫폼입니다.
- 감정 일기 작성 및 자동 키워드 분석 기능을 Flutter와 Firebase 기반으로 구현하며, 데이터 흐름과 비동기 처리 경험 축적
- ChatGPT 기반 맞춤형 회고 챗봇 구현, API 연동과 상태관리 최적화 학습
- Firebase Cloud Messaging 활용 푸시 알림 설계, 앱-서버리스 연동 경험
- DeepL API를 통한 다국어 지원 구현, Flutter Localization 적용 및 글로벌 사용자 확장 경험
✅ iOS AppStore & Android PlayStore 동시 출시, 실사용자를 대상으로 기술 구현 및 운영 경험 확보
| 🏆 K-Startup 예비창업패키지 지원 선정 | 🏆 제 13 회 창업경진대회 RPM 대상 |
|---|---|
![]() |
![]() |
| 2024-04-29 | 2024-12-24 |
| 관련 기사 1 관련 기사 2 | 관련 기사 1 관련 기사 2 |
- 🏆 캡스톤디자인 경진대회 우수상 (2024-06-03)
- 🏆 POSTECH Mini-I-Corps 우수상 (2024-02-08)
- 🏆 제 12 회 창업경진대회 RPM 장려상 (2023-11-30)
- 🏆 SW Festival 문제해결 아이디어 공모전 장려상 (2023-11-17)
- 🏆 SW 창업 경진대회 대상 (2023-10-27)
| 홈 화면 | 감정 일기 작성 | 감정 분석 결과 |
|---|---|---|
| 챗봇 회고 | 알림 기능 | 다국어 지원 |
|---|---|---|
flowchart TD
User((사용자)) -->|소셜 로그인| Auth[Google/Apple OAuth] --> Provider["상태관리\nProvider"] --> DB[(Firestore Database)]
User -->|일기 작성| Write[일기 작성]
Write --> Local[로컬 저장소]
Write --> DB
User -->|기록 공유| Share[공유된 기록]
Share -->|공감| Functions[Firebase Cloud Functions] -->|공감 / 알림 Trigger| FCM[Firebase Cloud Messaging] -->|알림| App[상대방앱]
Share -->|번역| DeepL[DeepL API]
Functions -->|AI 호출| OpenAI[ChatGPT API]
-
다국어화 및 번역 토글 구현
- PR: #133 Feature 다국어화(localization) 설정하기
- 내용: Flutter UI 전반과 Firebase Function에 다국어(Localization) 적용, 사용자 입력 일기 공유 시 DeepL API 기반 번역 토글 기능 구현
- 의미: 글로벌 사용자 대상 확장 및 플랫폼 내 언어 선택 유연성 강화, Flutter와 서버리스 연동 실무 경험 확보
-
기록 공유의 신고 및 차단 기능 추가
- PR: #144 Release 공유 알고리즘 개선 등
- 내용: 공유된 일기에서 부적절한 콘텐츠에 대한 신고 및 차단 기능 도입, 커뮤니티 안전성 확보
- 의미: 사용자 경험 중심의 책임 있는 서비스 운영
-
소셜 로그인 자동 무한 루프 오류 해결
- PR: #136 Release 로그인 오류 수정
- 내용: 자동 로그인 시 발생하던 무한 재시도 루프 버그 수정, 안정적인 로그인 흐름 확보
- 의미: 원활한 사용자 흐름 보장 및 UX 개선
-
계정 탈퇴 기능 안정화 및 코드 개선
- PR: #141 Fix 계정 탈퇴 버그 및 코드 개선
- 내용: 계정 탈퇴 시 무한 로딩 문제 수정, Firebase DB 및 Auth 계정 삭제 로직 정상화, 재인증 기능 보완
- 의미: 사용자 신뢰 확보 및 데이터 정합성 유지
-
인터넷 미연결 시 에러 핸들링 추가
- PR: #123 Bug 인터넷 연결 시 에러 핸들링
- 내용: 로그인 전 네트워크 연결 여부 확인 및 적절한 에러 메시지 처리 로직 추가
- 의미: 네트워크 불안정 상황에서도 앱의 안정성 및 사용자 안내 강화
Flutter와 Firebase를 단순히 사용하는 수준을 넘어, 실제 서비스에서 발생하는 이벤트 기반 기능을 직접 구현하며 클라이언트-서버리스 연동, 데이터 흐름, 비동기 처리를 깊이 탐구.
아래 코드는 이러한 경험을 통해 작성된, 공유된 일기 콘텐츠에 '좋아요'가 발생했을 때 작성자에게 실시간 알림을 전송하는 Cloud Function 예제.
// functions/index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
// 좋아요 발생 시 작성자에게 알림 전송
exports.sendLikedDiaryNotification = functions.region("asia-northeast3").https.onCall(async (data, context) => {
// [보안 1] 인증 확인: 로그인한 사용자만 호출 가능
if (!context.auth) {
throw new functions.https.HttpsError("unauthenticated", "로그인이 필요한 서비스입니다.");
}
// data.fcmToken은 보안상 신뢰할 수 없으므로 제거하고, DB에서 직접 조회합니다.
const { likedDiaryId, userId } = data; // userId는 알림을 받을 대상(일기 작성자)
// [보안 2] 필수 데이터 검증
if (!likedDiaryId || !userId) {
throw new functions.https.HttpsError("invalid-argument", "필요한 정보(likedDiaryId, userId)가 누락되었습니다.");
}
try {
// [성능/보안] 알림 받을 유저 정보를 DB에서 한 번만 조회 (언어 설정 + FCM 토큰)
const userDocRef = db.collection("users").doc(userId);
const userDoc = await userDocRef.get();
if (!userDoc.exists) {
console.log(`[Error] Target user ${userId} not found.`);
return { success: false, reason: "user_not_found" };
}
const userData = userDoc.data();
const langCode = userData.language || "ko";
// [보안 3] 클라이언트가 준 토큰이 아니라, DB에 저장된 신뢰할 수 있는 토큰 사용
const targetFcmToken = userData.fcmToken;
let notificationTitle = "";
let notificationBody = "";
const notificationType = "likedDiary";
if (langCode === "ko") {
notificationTitle = `누군가 나의 기록에 공감했어요!`;
notificationBody = `나의 기록을 확인해보세요.`;
} else {
notificationTitle = `Someone reacted to your journal.`;
notificationBody = `Take a look at your journal.`;
}
// 1. FCM 푸시 알림 전송
if (targetFcmToken) {
const message = {
notification: {
title: notificationTitle,
body: notificationBody,
},
data: {
screen: "liked_diary_detail",
likedDiaryId: likedDiaryId,
},
token: targetFcmToken,
};
try {
await admin.messaging().send(message);
console.log(`[Success] Notification sent to user ${userId}`);
} catch (fcmError) {
// 토큰이 만료되었거나 삭제된 경우 등 에러 처리
console.error(`[Warning] Failed to send FCM to user ${userId}: ${fcmError.message}`);
// FCM 전송 실패가 DB 저장을 막으면 안 되므로 에러를 throw 하지 않음
}
} else {
console.log(`[Info] User ${userId} has no FCM token. Skipping push notification.`);
}
// 2. 알림 내역 DB 저장 (이전에 개선한 함수 호출)
await addNotification(userId, notificationTitle, notificationType, likedDiaryId);
return { success: true };
} catch (error) {
console.error(`[Error] sendLikedDiaryNotification failed:`, error);
throw new functions.https.HttpsError("internal", "알림 전송 중 오류가 발생했습니다.");
}
});| 이름 | 역할 |
|---|---|
김형진 |
Flutter 프론트엔드 & Firebase 백엔드 개발, Cloud Functions 및 서버리스 아키텍처 구현, ChatGPT API 연동 및 최적화, 배포 및 스토어 심사 대응 |
김경록 |
일기 작성 및 공유 기능 중심 개발, 대표 문서 체계 도입, DeepL API 기반 다국어화 적용 |
권세한 |
팀 내 역할 (개발/운영 보조) |
박창휘 |
앱 디자인 총괄(UI/UX), 리서치 기반 앱 컨셉 및 브랜딩 전략 수립, UI 디자인 및 디자인 시스템 구축, 디자인 검수 |
- 앱 심사와 서비스 운영 과정에서 발생하는 실제 문제 해결 경험 축적, Flutter/Firebase/Cloud Functions 적용 및 심화 이해
- 소셜 로그인, 다국어, 네트워크 등 실무 난이도 높은 문제 해결 능력 확보
- 실제 사용자 피드백 기반으로 지속적인 개선 사이클 운영
“실서비스를 개발·출시·운영하며, 복잡한 기술적 문제를 해결할 수 있는 풀스택 모바일 개발 경험”

