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
- 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





