클래스업 학생앱 - 타이머 서비스

2025.08 – 2025.10
Backend / Performance / Migration
클래스업 타이머
클래스업 타이머

About Project

  • 서비스: 클래스업 학생앱
  • 팀 구성: 3명 (FE 2, BE 1)
  • 담당: 백엔드 전반 (타이머 API · DB 설계 · Mongo↔DynamoDB 마이그레이션)
  • 기간: 2025.08 ~ 2025.10

Objective

앱이 종료되거나 백그라운드로 전환되어도 학습 시간이 안정적으로 누적되도록, 서버 중심으로 타이머 상태를 관리한 학생앱 타이머 기능.

Tools & Technologies

Express.js, TypeScript, MySQL, Sequelize, MongoDB, DynamoDB, Joi, Swagger

Challenge

문제 상황

학생앱의 타이머 기능은 단순히 화면에서 시간을 재는 기능이 아니라, 실제 사용자가 앱을 종료하거나 백그라운드로 전환하더라도 학습 시간이 끊기지 않고 안정적으로 누적되어야 했습니다. 또한 타이머 기록이 누적될수록 공부 기록 조회가 느려졌고, 개발 서버와 운영 환경 간 시간대 차이로 인해 다른 날짜에 기록이 저장되는 문제도 발생했습니다.

이를 해결하기 위해 서버 중심의 타이머 상태 관리 구조를 설계하고, 저장소 마이그레이션, 인덱스 최적화, 페이지네이션 개선, 시간대 변환 자동화까지 함께 적용했습니다.

서비스 흐름

  1. 사용자가 타이머를 시작하면 startedAt을 저장하고 활성 타이머 상태를 생성합니다.
  2. 서버는 tb_user_timer_status를 기준으로 사용자별 활성 타이머를 관리합니다.
  3. 타이머 조회 시에는 현재 시각 - startedAt으로 경과 시간을 계산합니다.
  4. 타이머 종료 시 실제 학습 시간을 확정하고 durationSec을 저장합니다.
  5. 확정된 기록은 일자별 누적 학습 시간 및 공부 기록 조회에 활용됩니다.

핵심 기술적 판단

1. 서버 중심 타이머 상태 관리 구조 설계

앱 환경에서는 사용자가 화면을 닫거나 앱을 종료할 수 있기 때문에, 클라이언트에서만 타이머를 관리하면 학습 시간의 신뢰성이 떨어질 수 있었습니다. 이를 해결하기 위해 타이머 시작 시점을 저장하고, 서버가 경과 시간을 계산하는 구조로 설계했습니다.

핵심 구조는 다음과 같습니다.

  • 타이머 시작 시 startedAt 저장
  • 활성 타이머 상태는 tb_user_timer_status에서 관리
  • 조회 시 현재 시각 - startedAt 계산
  • 종료 시 durationSec 확정 저장

이 구조를 통해 클라이언트 상태와 무관하게 서버 기준으로 학습 시간을 계산할 수 있었고, 실제 사용자 환경에서도 안정적인 타이머 기능을 제공할 수 있었습니다.

2. 한 사용자당 하나의 활성 타이머만 허용하는 제약 설계

타이머 서비스에서는 같은 사용자가 동시에 여러 개의 타이머를 활성화하면 학습 시간이 중복 계산될 수 있었습니다. 이를 방지하기 위해 tb_user_timer_status.userId에 유니크 인덱스를 적용해 한 사용자당 하나의 활성 타이머만 가질 수 있도록 제약을 걸었습니다.

이를 통해:

  • 중복 시작 방지
  • 상태 정합성 확보
  • 활성 타이머 조회 로직 단순화

가 가능해졌습니다.

3. MongoDB → DynamoDB 마이그레이션 판단

초기에는 개발 서버의 MongoDB를 기반으로 기능을 구현했습니다. 다만 실제 서비스 단계에서는 AWS 생태계와의 연동, 기존 운영 설정과의 호환성, 고가용성을 함께 고려해야 했습니다. 이에 따라 운영 환경에서는 DynamoDB로 마이그레이션하는 방향으로 구조를 전환했습니다.

이 경험을 통해 단순히 기능 구현에 그치지 않고, 운영 환경에 적합한 저장소 선택과 마이그레이션까지 고려하는 백엔드 설계의 중요성을 배웠습니다.

어려웠던 점과 해결

1. 공부 기록 조회가 느려지던 문제

MongoDB에 타이머 로그, 즉 사용자가 언제 어떤 과목을 얼마나 공부했는지를 지속적으로 저장하고 있었는데, 데이터가 많이 쌓이면서 공부 기록 조회 성능이 크게 저하되었습니다.

초기에는 userId 단일 인덱스만 사용하고 있었지만, 실제 조회 패턴은 사용자별 기록을 startedAt 기준으로 정렬해 가져오는 형태였습니다. 이를 반영해 userId + startedAt 복합 인덱스를 추가했고, explain 기준으로 주요 쿼리 실행 시간을 77% 단축했습니다.

이 경험을 통해 조회 조건과 정렬 기준까지 함께 반영한 인덱스 설계가 중요하다는 점을 확인할 수 있었습니다.

2. 무한 스크롤 API가 아래로 갈수록 느려지던 문제

타이머를 사용하는 전체 사용자 목록을 조회하는 API는 무한 스크롤 방식으로 동작했는데, 기존에는 offset 기반 페이지네이션을 사용하고 있어 아래로 스크롤할수록 성능이 저하되는 문제가 있었습니다.

이를 해결하기 위해 startedAt 기준의 키셋 페이지네이션을 적용했습니다. 가장 오래 공부한 사용자가 상위에 노출되도록 정렬 기준을 맞추고, 다음 페이지 조회 시에도 offset 누적 없이 안정적으로 데이터를 가져올 수 있도록 개선했습니다. 그 결과 explain 기준으로 기존 대비 30% 성능 개선을 확인할 수 있었습니다.

3. 개발/운영 환경 간 시간대 차이로 기록 날짜가 어긋나던 문제

개발 서버의 MongoDB는 KST 기준으로 동작했고, 운영 AWS 환경은 UTC 기준이어서 시간대 차이로 인해 공부 기록이 다른 날짜에 저장되는 문제가 실제로 발생했습니다.

이를 해결하기 위해 Mongoose Hook을 활용해 KST 기준 입력이 들어올 경우 저장/조회 전 UTC 변환이 일관되게 수행되도록 구성했고, 이를 MongoDB 문서 전반에 공통 적용했습니다. 이를 통해 날짜 단위 학습 기록의 정합성을 확보할 수 있었습니다.

4. Express 레거시 구조에서 비동기 에러 처리와 응답 형식이 불안정하던 문제

기존 Express 구조에서는 try-catch 반복이 많았고, 누락된 비동기 에러가 전역 처리로 전달되지 않는 문제가 있었습니다. 또한 API마다 응답 형식이 달라 유지보수와 협업 비용이 발생했습니다.

이를 해결하기 위해:

  • async handler를 도입해 반복적인 try-catch를 제거하고 비동기 에러 전파를 일관화
  • success / data / error 구조의 응답 미들웨어를 설계해 응답 형식을 통일

했습니다. 이 구조는 제가 먼저 학생앱 서버에 직접 적용했고, 이후 문제 없음을 확인한 뒤 웹서버와 관리자 서버에도 동일한 방식이 확산되었습니다.

5. 프론트-백 간 API 스펙 불일치 문제

기존에는 요청/응답 검증 누락이 자주 발생해 프론트와 백엔드 간 소통 비용이 컸고, 연결 과정에서 타입 불일치 문제가 반복적으로 생겼습니다.

이를 해결하기 위해 Joi 기반으로 요청/응답 검증을 모두 적용하고, Joi to Swagger를 사용해 Joi 스키마 기반으로 Swagger 명세를 자동화했습니다. 이 체계를 적용한 이후에는 프론트-백 연결 과정에서 타입 검증 문제는 한 번도 발생하지 않았습니다.

구현 포인트

  • 서버가 타이머의 시작 시각과 활성 상태를 직접 관리하는 구조 설계
  • tb_user_timer_status의 유니크 제약으로 사용자별 단일 활성 타이머 보장
  • 조회 패턴에 맞춘 복합 인덱스 설계로 기록 조회 성능 개선
  • startedAt 기준 키셋 페이지네이션으로 무한 스크롤 API 최적화
  • Mongoose Hook을 활용한 UTC/KST 변환 자동화
  • Joi 기반 검증 + Swagger 자동화로 API 스펙 일관성 확보
  • MongoDB 기반 개발 환경에서 DynamoDB 기반 운영 환경으로 마이그레이션

성과 및 배운 점

  • 실제 학생앱에 적용되는 기능으로, 사용자 환경을 고려한 서버 중심 타이머 구조를 설계했습니다
  • 데이터 누적에 따른 조회 성능 문제를 인덱스 최적화와 페이지네이션 개선으로 해결했습니다
  • 시간대 차이, 비동기 에러 처리, API 검증 누락처럼 운영 단계에서 발생하는 문제를 구조적으로 개선했습니다
  • 백엔드 개발자로서 상태 관리, 저장소 선택, 성능 최적화, 레거시 구조 개선, 협업 명세 자동화를 한 기능 안에서 통합적으로 경험했습니다

한 줄 회고

이 프로젝트를 통해 단순 타이머 기능 구현을 넘어, 실제 사용자 환경에서 동작하는 서버 상태 관리, 조회 성능 최적화, 운영 환경 대응까지 함께 설계하는 백엔드 개발의 중요성을 배웠습니다.

© 2026 Lagoon Portfolio. All rights reserved.