jansorry · holiday nagging receipts

Period  — 

The project started from the stress young people feel from holiday nagging and the gap between generations. Users share nagging and how they handled it, then turn those stories into receipts and statistics. A preliminary survey with 300+ participants collected holiday stress data, and the project won first place among SSAFY common projects and the Project Excellence Award. I handled more than half of the backend work and set up the servers and CI/CD alone.

About 200 sign‑ups over Lunar New Year

Java 17Spring Boot 3.2.1Spring Data JPASpring SecuritySpring BatchQuerydslMySQL 8.0.35Redis 7.2AWS EC2GitLab RunnerDocker

  • Feed of nagging and responses
  • Likes and follows
  • Receipt printing and sharing
  • Account page
  • Statistics visualization
  • Deployment to EC2 through GitLab Runner and Docker Hub
  • Next.js, Spring Boot, MySQL and Redis containers behind Nginx

Two‑way follow relations in Redis

Problem
Follow relationships had to live in a non‑relational store. Unlike one‑way likes, follows are many‑to‑many, so storing both directions uses more space while storing one direction slows lookups.
Solution
I decided wildcard scans should be avoided wherever possible and stored follows in both directions. Keys carry following and follower prefixes, so each user's following and follower lists are read directly. Follower and following lists became much faster to look up.

Full scans in the batch sync

Problem
The daily batch moving Redis data to MySQL scanned the entire dataset. I needed a way to move only the data that had changed.
Solution
Changed keys go into a Redis sorted set, scored by their update time. The batch writes only changes since the previous run to MySQL, then clears the set so it holds only new changes. Rows left empty after likes or follows were canceled were also removed from the table to free space.

Top 5 receipts by price

Problem
The final statistics needed the 5 highest‑priced receipts. Searching every receipt of every user at once was costly.
Solution
Reused the sorted‑set approach from the batch sync. Saving a receipt adds it to a top‑5 set scored by price, and the receipt pushed to sixth place is removed right away. This spreads the search load across saves instead of one moment, so no query scans all receipts at once.

Deploying frontend and backend from one repository

Problem
The project could use only one repository, so the frontend and backend code shared it and deployment had to be automated from there. Jenkins had a high setup cost and took up a lot of server resources.
Solution
Chose GitLab Runner, which has fewer plugins but sets up a pipeline with little overhead. The pipeline has 4 stages: create the environment file, build, build the Docker image and push it to Docker Hub, then run it on the server. Secrets such as database credentials live in GitLab CI/CD variables and become a .env file at deploy time.

Architecture

jansorry deployment diagram: GitLab Runner, Docker Hub, and Nginx, Next.js, Spring Boot, MySQL and Redis on EC2
Images built by GitLab Runner and deployed as Docker containers on EC2
Angled smartphone mockup showing the jansorry start screen
Smartphone mockup showing the jansorry start screen
jansorry feed with hashtag filters
jansorry screen with a receipt button and saved nagging cards
jansorry receipt screen with a QR code
jansorry statistics screen with average count and gender breakdown