타이미 프로젝트 소개: 집중 시간을 게임처럼 만드는 타이머
목차
타이미(Tymee)는 집중한 시간을 기록하고 그 시간만큼 보상과 성장 요소를 주는 게이미피케이션 타이머입니다. 기획부터 디자인, 백엔드, 모바일 앱까지 혼자 만들고 있습니다. 공부나 작업에 집중하는 시간을 게임처럼 눈에 보이게 만들어서 동기를 오래 유지하는 것이 목표입니다.

한눈에 보기
| 항목 | 내용 |
|---|---|
| 한 줄 소개 | 집중한 시간을 기록하고 보상과 성장 요소로 몰입 습관을 돕는 게이미피케이션 타이머 |
| 기간 | 2025.11 ~ 2026.02 (저장소 커밋은 2026.01.16까지) |
| 형태 | 1인 개인 프로젝트, 기획, 디자인, 백엔드, 앱을 모두 혼자 담당 |
| 기술 | Spring Boot 4, Java 25, MySQL, Redis, RabbitMQ, Cloudflare R2, React Native, GitHub Actions, Linear |
| 서비스 | 아직 출시 전, 앱스토어나 플레이스토어에 공개 배포한 링크는 없음 |
| 코드 | github.com/dj258255/tymee |
왜 만들었나
친구가 공부할 때마다 탁상시계 앱을 켜두는 습관을 보고 아이디어가 시작됐습니다. 게임은 버튼을 누르는 순간 캐릭터가 움직이고 점수가 오릅니다. 공부는 오늘 세 시간을 투자해도 당장 달라지는 게 없습니다. 성적이 오르려면 몇 주에서 몇 달이 걸리고 그 사이 동기는 자연스럽게 사라집니다. 이 피드백 속도 차이를 좁히면 집중을 오래 유지할 수 있다고 봤습니다.
여기에 사람이 자기 성취를 남에게 보여주고 싶어 하는 욕구를 더했습니다. 집중한 시간이 실시간으로 눈에 보여서 바로 성취감을 느끼고 그 기록을 남들과 비교하며 동기를 얻는 구조입니다.
시작하기 전에 가정만 믿지는 않았습니다. 앱스토어 상위 타이머 앱 다섯 개를 2주간 직접 써 보고 지속적인 동기 부여가 안 된다는 지점이 공통적이라는 걸 확인한 뒤 방향을 정했습니다. 팀 프로젝트에서는 항상 정해진 사용자, 팀원과 심사위원만 서비스를 썼습니다. 실제 사용자에게 피드백을 받아 고쳐보는 경험을 하고 싶어서 개인 프로젝트로 시작했습니다.
누구를 위한 서비스인가
| 쓰는 사람 | 이 사람이 얻는 것 |
|---|---|
| 공부나 작업 시간을 기록하고 싶은 사람 | 집중시간과 휴식시간을 나눠 재고 할일과 시간표, 통계로 하루 기록을 남깁니다 |
| 혼자서는 동기가 잘 안 붙는 사람 | 집중한 시간만큼 연필, 볼펜 같은 보상을 얻고 상점에서 씁니다 |
| 친구나 스터디 상대와 비교하며 공부하고 싶은 사람 | 친구를 추가하거나 그룹을 만들고 매칭으로 새 상대를 만나 기록을 공유합니다 |
| 공부 중 다른 앱에 손이 가는 사람 | 집중 시간 동안 다른 앱 사용을 막는 기능을 씁니다 |
주요 기능
1. 집중 타이머
뽀모도로 방식으로 집중시간과 휴식시간을 나눠 잽니다. iOS에서는 Live Activity로 잠금화면에도 남은 시간을 보여줍니다. 집중 시간 동안에는 AppBlocker 모듈이 다른 앱 사용을 막아서 손이 딴짓으로 가는 걸 줄입니다.

2. 공부 기록
할일, 시간표, 통계, 리포트 탭으로 하루하루 기록을 남깁니다. 타임블록으로 공부 일정을 잡고 완료나 건너뛰기를 남기며 일일 할일 목록으로 그날 해야 할 일을 관리합니다.

3. 보상과 성장
집중한 시간만큼 연필, 볼펜 같은 인앱 화폐를 얻고 상점에서 씁니다. 결제 내역도 따로 확인할 수 있습니다.
4. 커뮤니티
친구를 추가하거나 그룹을 만들고 매칭으로 새 사람을 만나 공부 기록을 공유합니다. 채팅으로 서로 응원하는 흐름도 있습니다.
5. 계정과 알림
Google, Apple, Kakao 소셜 로그인으로 가입하고 기기마다 독립된 로그인 상태를 유지합니다. 타임블록 알림이나 학습 완료 알림은 FCM 푸시로 받습니다.
구조
백엔드는 도메인별 멀티모듈로 나뉩니다.
backend/├── auth/ 인증├── user/ 사용자, 설정├── upload/ 파일 업로드├── core/ 공통 유틸, 이벤트, 예외└── bootstrap/ 앱 실행모듈은 필요한 의존성만 가져서 upload 모듈에 Redis가 딸려오지 않습니다. User와 Upload처럼 서로를 참조하면 순환 의존이 생기는 모듈 쌍은 Core 모듈에 정의한 이벤트로 관계를 끊었습니다.
모바일은 React Native로 만들고 Zustand로 상태를 관리하며 React Navigation으로 화면을 전환합니다.
데이터는 읽기가 많은 타이머 기록과 랭킹 조회에 맞춰 MySQL을 쓰고 갱신이 잦은 로그인 세션은 Redis에 둡니다. 타임블록 알림이나 학습 완료 알림은 RabbitMQ의 Exchange와 Queue, DLQ를 거쳐 발행됩니다. 프로필 이미지 같은 파일은 Cloudflare R2에 Presigned URL로 올립니다. 전부 Oracle Cloud Free Tier의 ARM 인스턴스에서 돌아 인프라 비용이 들지 않습니다.
CI는 GitHub Actions로 백엔드와 모바일 빌드를 따로 돌립니다. Spotless, Checkstyle, SpotBugs, JaCoCo 같은 품질 검사를 빌드보다 앞에 둬서 문제를 빠르게 잡습니다.

고민한 선택
전부 정답이 하나로 정해지지 않는 결정이었습니다. 열다섯 개 결정 중 이 세 가지만 골랐습니다.
외부에 노출하는 ID를 어떻게 만들 것인가
옵션은 Auto Increment를 그대로 노출, UUID v4, UUID v7, Snowflake ID 네 가지였습니다. 내부 PK는 Auto Increment로 두고 파일 업로드처럼 외부에 노출되는 리소스에만 Snowflake ID를 따로 발급하는 쪽을 골랐습니다.
Auto Increment를 그대로 노출하면 전체 파일 수나 생성 속도 같은 정보가 새어 나갑니다. UUID v4는 16바이트라 인덱스가 부풀고 완전 랜덤이라 B-Tree 페이지 채움률이 순차 삽입의 94%에서 랜덤 삽입의 50% 수준까지 떨어집니다. UUID v7은 시계 동기화에 덜 민감하지만 여전히 Snowflake의 두 배 크기입니다. 기존 UUID 인프라가 없는 이 프로젝트에는 그 장점이 그대로 이점이 되지 않았습니다. 대신 얻은 건 시계 동기화 부담입니다. 서버 시계가 뒤로 가면 순서가 꼬일 수 있어 NTP 동기화가 필수이고 예외 처리를 따로 넣어야 했습니다. 싱글 서버 내부 시스템이면 Auto Increment로도 충분하고 기존 UUID 인프라가 있다면 UUID v7이 더 나을 수 있다고 보고 있습니다.
삭제한 파일을 언제, 어떤 경로로 정리할 것인가
프로필 이미지를 바꾸면 이전 파일이 스토리지에 남습니다. 여기서 두 가지를 같이 정했습니다.
삭제 시점은 프로필 수정 트랜잭션 안에서 바로 지우는 즉시 삭제와, deleted_at만 마킹해 두고 매일 새벽 3시 배치로 지우는 지연 삭제를 놓고 골랐습니다. 즉시 삭제는 DB 트랜잭션과 외부 스토리지 삭제가 한 트랜잭션으로 묶이지 않아 커밋 실패 시 파일만 사라지는 문제, 네트워크 I/O로 응답이 100에서 500밀리초 느려지는 문제, 되돌릴 수 없다는 문제가 있었습니다. 지연 삭제를 고르면서 받아들인 비용은 유예 기간 동안의 추가 스토리지입니다. 프로필 이미지 평균 500KB에 하루 교체 100건 기준으로 계산하면 7일치 추가 스토리지는 약 350MB, 월 0.005달러 수준입니다.
알림 경로는 이미 인프라에 있던 RabbitMQ와 Spring의 @TransactionalEventListener 중 후자를 골랐습니다. RabbitMQ는 Connection Factory, Exchange, Queue, 직렬화, 재연결 처리까지 붙는데 서버가 한 대뿐인 지금은 그 설정 비용에 비해 얻는 게 적었습니다. 버린 것은 서버가 여러 대가 되거나 DLQ와 재시도 정책이 필요해졌을 때 곧바로 쓸 수 있는 확장성입니다. 서버를 2대 이상으로 늘리거나 알림 서버 같은 외부 서비스와 연동할 일이 생기면 그때 옮길 계획입니다.
레이어 간 변환을 어디에 자동화할 것인가
모든 모듈에 MapStruct를 적용하는 방법과 모든 모듈에서 수동으로 변환하는 방법, 모듈마다 다르게 정하는 방법을 놓고 셋째를 골랐습니다. 필드가 20개를 넘고 Email, Nickname, Tier 같은 값 객체 변환이 잦으며 부분 업데이트(PATCH)를 지원해야 하는 user 모듈에는 MapStruct를 붙였습니다. 필드가 10개가 안 되고 R2 업로드 결과처럼 도메인 객체에 없는 값을 따로 넘겨야 하는 upload 모듈에는 붙이지 않았습니다.
모든 모듈에 MapStruct를 통일했다면 필드가 적은 모듈에서도 매퍼 인터페이스와 애노테이션 설정이 붙어 코드가 오히려 늘었을 것입니다. 모든 모듈에서 수동 변환으로 통일했다면 user처럼 필드가 많은 모듈에서 필드를 하나 빠뜨려도 컴파일 에러가 나지 않는 위험을 그대로 안고 갔을 것입니다. 기준으로 삼은 건 필드 수, 값 객체 변환 빈도, PATCH 지원 여부였습니다.
일정과 작업 방식
작업 흐름은 저장소에 남은 커밋과 Linear 기록으로 그대로 재구성할 수 있습니다.
저장소에 남은 첫 커밋은 2025년 11월 28일 “Tymee 프로젝트 구조 및 기본 기능 구현”입니다. 12월 2일에 뽀모도로 기능을 다듬고 앱 브랜딩을 정리하는 커밋이 하나 더 있습니다. 12월 28일에는 백엔드를 멀티모듈로 다시 정리하고 Spring Boot를 4.0.1로 올리는 커밋 두 개를 올렸습니다. 여기까지 커밋은 총 5개입니다.
1월 5일에 Linear에 “Tymee MVP v1.0 출시” 프로젝트를 새로 만들었습니다. 목표 구간은 1월 5일부터 1월 30일까지였습니다. 스코프는 이슈 117개였습니다.

이 시점부터 속도가 달라집니다. 1월 6일부터 16일까지 8영업일 동안 커밋 57개와 PR 13개가 올라갔습니다. 날짜별로는 1월 6일 9개, 8일 4개, 9일 20개, 10일 2개, 12일 12개, 13일 5개, 16일 4개입니다. PR 13개는 전부 TYM 번호가 붙은 Linear 티켓 하나씩과 연결됐고 브랜치 이름도 feature/TYM-49-time-blocks처럼 티켓 번호를 그대로 땁니다.
Linear의 GitHub Integration이 이슈 생성부터 브랜치 생성, PR 머지, 이슈 종료까지 이어 줍니다. PR을 머지하면 연결된 Linear 이슈가 자동으로 Done으로 바뀝니다.

병합까지 걸린 시간은 작업 크기를 따라갔습니다. 카테고리 API 같은 작은 단위(PR #9)는 생성 4분 만에 병합됐고 RabbitMQ 알림 시스템 같은 큰 단위(PR #10)는 생성부터 병합까지 약 17시간이 걸렸습니다.
Linear로 옮긴 이유도 기록에 남아 있습니다. 이전에는 GitHub Issues만 썼는데 PR을 머지해도 이슈 상태가 바뀌지 않아 117개 이슈 중 실제 진행률을 알 수 없는 상태가 됐습니다. 이 문제를 겪고 나서 이슈, 브랜치, PR, 알림을 자동으로 잇는 지금 구조로 옮겼습니다.
이 저장소에서 확인되는 마지막 커밋은 2026년 1월 16일 FCM 푸시 알림 PR입니다. Linear 캡처 시점 기준으로는 목표였던 1월 30일까지 이슈 78개가 아직 Todo로 남아 있었습니다.
직접 재 본 결과
| 확인한 것 | 기록 | 조건 |
|---|---|---|
| develop 브랜치 커밋 수 | 62개 | 2025-11-28 ~ 2026-01-16, GitHub API 조회 기준 |
| 병합한 PR 수 | 13개 | 전부 Linear 티켓(TYM 번호) 1개씩과 연결 |
| CI 빌드 누적 회차 | Backend 46회, Mobile 17회 | PR #13(2026-01-16) 병합 시점 캡처 기준, 둘 다 SUCCESS |
| Linear 스코프 진행률 | 완료 17 / 스코프 117 (15%) | 목표 구간 1월 5일에서 30일, 캡처 시점 기준, In Progress 22 · Todo 78 |
| 테스트 커버리지(JaCoCo) | 42%에서 60% 이상으로 상승 | 라인 60% · 브랜치 70% 게이트를 건 뒤 측정 |
아직 못 한 것
Linear 캡처 시점 기준으로 스코프 117건 중 78건이 Todo 상태로 남아 있습니다. 인앱결제 API, 앱 버전과 공지 관리, 알림 목록 화면 같은 항목이 여기 포함됩니다.
이 저장소에서 확인되는 마지막 기록 이후로 별도의 공개 진행 기록은 찾지 못했습니다. 앱스토어나 플레이스토어에 올라간 배포 링크도 없습니다.
Spring Boot 4의 API 버전 관리와 Swagger 조합처럼 아직 공식 문서에 없는 우회가 필요한 지점도 남아 있습니다. 화이트리스트 버전 리졸버와 시맨틱 버전 변환기를 직접 짜서 막아 뒀습니다. 프레임워크 쪽 가이드가 갱신되면 다시 정리할 계획입니다.
더 보기
댓글
댓글 수정/삭제는 GitHub Discussions에서 가능합니다.