Flutter + Gemini AI 앱을 만들어 내 대학 출석을 '해킹'한 방법 (오픈소스)

발행: (2026년 1월 4일 오전 02:59 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

만약 당신이 (저처럼) 모라투와 대학교에 다니는 공학도라면 80 % 규칙이 얼마나 끔찍한지 잘 알 것입니다.

간단한 수학: 수업의 80 %에 참석하거나 시험에서 추방당한다.

현실은 악몽과 같습니다.

  • “오늘 강의를 빼먹으면 내 출석 비율이 79.9 %로 떨어질까?”
  • “강사가 의료 휴가를 인정해 줄까?”
  • “내일은 늦게 일어나도 괜찮을까?”

나는 새벽 2시에도 아침 강의를 빼먹을지 말지를 결정하기 위해 복잡한 대수를 풀곤 했습니다. 단순히 습관을 추적하는 것이 아니라 전략을 세워주는 도구가 필요했습니다.

그래서 **The 80 Percent**를 만들었습니다.

아래는 Flutter, Google Gemini AI, 그리고 무료 티어를 유지하면서도 고도로 최적화된 Firebase 백엔드로 구현한 풀‑스택 솔루션의 전체 과정을 소개합니다.

1. 전략 엔진: “안전하게 건너뛸 수 있음” 로직

대부분의 습관 추적기는 “현재 75 %입니다.” 라고만 말합니다. 이는 쓸모 없는 정보입니다. 나는 실행 가능한 조언이 필요했습니다.

“버퍼” 공식

목표를 초과했을 때, 위험 구역에 도달하기 전에 몇 번의 수업을 결석할 수 있나요?

// The Magic Formula
int calculateSafeSkips(int present, int total, double target) {
  // Formula: floor((present / target) - total)
  int skips = ((present / target) - total).floor();
  return skips < 0 ? 0 : skips;
}

시나리오: 18/20 수업에 참석했습니다. 목표 = 80 %.
결과: 안전하게 2번 더 결석할 수 있습니다.

왜 중요한가: 학생들이 필요 없는 수업에 불안하게 참석하는 것을 막아 번아웃을 예방합니다.

“회복” 공식

만약 실패 상태라면, 다시 안전해지기 위해 연속해서 몇 번의 수업에 참석해야 할까요?

// The Recovery Formula
int calculateRecovery(int present, int total, double target) {
  // Formula: ceil((target * total - present) / (1 - target))
  return ((target * total - present) / (1 - target)).ceil();
}

이는 중요합니다. 왜냐하면 70 %인 학생은 언제 다시 안전해질지 정확히 알아야 하기 때문입니다.

2. AI 도전 과제: Gemini 2.5로 시간표 파싱

학생용 앱에서 가장 큰 마찰은 설정입니다. 15개의 모듈과 시간대를 일일이 입력하고 싶어 하는 사람은 없습니다.

왜 Gemini 2.5 Flash인가?

ReasonBenefit
Speed & Cost무료 학생 앱에 이상적 – 저지연, 저비용 추론.
Multimodal IntelligencePDF/이미지 입력을 직접 받아들임.
Zero‑Shot Capability긴 few‑shot 예제가 필요 없으며, 하나의 잘 구조화된 시스템 지시만 있으면 충분합니다.

구현 (Zero‑Shot Prompt)

final model = GenerativeModel(
  model: 'gemini-2.5-flash',
  apiKey: apiKey,
);

final prompt = """
You are an intelligent assistant that extracts university timetable data.
**GOAL**: Extract timetable metadata, modules, and classes into structured JSON.
**SCHEMA**:
{
  "modules": [{"code": String, "name": String, ...}],
  "classes": [{"day": String, "time": String, "location": String, ...}]
}
""";

final response = await model.generateContent([
  Content.text(prompt),
  Content.data('image/png', timetableImageBytes),
]);

앱은 JSON 응답을 파싱하고 클래스를 로컬 데이터베이스에 배치‑쓰기합니다. 이 “Zero‑Shot” 접근 방식은 요청 페이로드를 작고 빠르게 유지합니다.

3. 아키텍처: Firebase 무료 티어에서 살아남기

학생인 저는 $0 예산을 가지고 있습니다. Firebase는 훌륭하지만 Firestore 제한(하루 50 000 읽기)은 함정입니다. 500명의 사용자가 하루에 10번씩 앱을 열고 각각 10개의 레코드를 가져간다면 첫날에 바로 할당량을 초과하게 됩니다.

나의 “오프라인‑우선” 솔루션

ComponentWhat it does
Local Caching앱 실행 시 기기에서 데이터를 즉시 로드합니다.
Lazy Syncing사용자가 명시적으로 무언가를 변경할 때만 Firestore에 기록합니다 (예: 출석 표시).
No Images in DB유료 “Blaze” 플랜이나 부피가 큰 Base64 해킹을 피하기 위해 프로필 사진 동기화를 생략했습니다.

리포지토리 패턴을 사용하면 앱을 수천 명의 사용자까지 비용을 전혀 들이지 않고 확장할 수 있습니다.

4. 결과: 프로덕션‑레디 베타

“80 %”는 단순한 계산기가 아니라 전체 스택 학술 도구입니다.

  • 기술 스택: Flutter, Firebase Auth / Firestore, Provider, Gemini API.
  • 상태: 오픈‑소스 및 베타 라이브.

이 프로젝트를 구축하면서 엔지니어링은 트레이드‑오프에 관한 것이라는 것을 배웠습니다 – 안정성(무료 티어 제한)을 위해 화려한 기능(클라우드 이미지)을 포기했고, 앱을 실제로 유용하게 만들기 위해 단순한 로직을 정밀한 수학으로 대체했습니다.

Screenshot

80 퍼센트 스크린샷

시도해 보기

저는 동료 개발자와 학생들의 피드백을 찾고 있습니다.

  • 👉 Notion Documentation (원본에서 링크가 잘렸습니다; 사용 가능한 경우 올바른 URL로 교체하세요)

레포를 복제하고, 이슈를 열거나, 개선 사항을 제안해 주세요!

[The 80% Attendance Strategy for Undergrads](https://ill-grenadilla-8a4.notion.site/The-80-Attendance-Strategy-for-Undergrads-2d4d8852a123808c900cd5c78f7de104?pvs=74)

👉 [Demo code and APK file](https://github.com/inusha-thathsara/attendance-tracker-demo/tree/main)

*(Star the repo if you think the project is cool! ⭐)*
Back to Blog

관련 글

더 보기 »

RGB LED 사이드퀘스트 💡

markdown !Jennifer Davis https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%...

Mendex: 내가 만드는 이유

소개 안녕하세요 여러분. 오늘은 제가 누구인지, 무엇을 만들고 있는지, 그리고 그 이유를 공유하고 싶습니다. 초기 경력과 번아웃 저는 개발자로서 17년 동안 경력을 시작했습니다.