개발자로서의 나의 여정
Source: Dev.to
Level 1: Lunartech
The Beginning: The Experimentation Mindset
Hello readers, my name is Jim Amuto, and this is my story. One thing about me is that I love experimenting. In every project I do, I don’t follow the norm. I ask myself:
- What am I doing?
- What is the end goal?
- How much faster can I ship it to actual users or increase my workflow speed?
That curiosity turned into a passion for software development, especially DevOps. I’m grateful for my current position because it let me spot loopholes and the need to ship code faster.
The Challenge: Full‑Stack in an AI Startup
I remember being on a deadline with a mountain of tasks. In an AI startup you’re often required to wear many hats—full‑stack roles instead of a narrow specialization. That’s great for becoming a “jack of all trades” and later focusing on a niche as you grow.
Shipping Code Under Pressure
My big project was an AI translator that had to handle long‑form content—something my boss insisted on. The device I was using became a bottleneck; AI/ML workloads are computationally intensive, but there are no excuses. I tried repeatedly until it failed.
I reached out to a friend with a more powerful machine. We hopped on a call, he cloned the repo, and I told him what to install first. He ran a dependency‑install script and—literally—dependency hell erupted. He fixed what he could, but the build still failed because of a maze of specific version requirements. I eventually told my boss that my PC wasn’t enough.
I researched cloud computing and found many GPU/CPU rental options, but I didn’t have the budget. I suggested the idea to my boss and waited for feedback—a painfully long wait.
The Revelation: Why Docker?
Why am I telling you this? I had a revelation: if my code couldn’t run on my friend’s setup, how could I expect it to run in production?
Time was ticking. I had multiple Jira tickets, and two months in I still hadn’t completed them. Some tasks—like building a simple progress tracker—looked easy on paper but stalled because of stack incompatibilities.
At that point I accepted that the frontend could be harder than the backend. A broken progress tracker is non‑negotiable; no user wants to see it. The pressure to finish was intense, and I almost gave up.
Then I thought: maybe my approach is wrong. I dug deeper, researched, and decided to try Docker to package my code.
Docker는 코드가 필요로 하는 모든 것을 이미지에 패키징하여 격리된 일시적인 컨테이너 인스턴스를 실행할 수 있게 하는 도구입니다.
The Docker Gamble
Docker was a big gamble for me. It took hours to implement and came with many reality checks:
- Long build times.
- My AI coding agent (the “assistant”) kept performing actions I wouldn’t attempt now, erasing progress and forcing me to backtrack.
I sat down, planned with the agent, and asked:
- Why rebuild this and not that?
- When should I clear the cache and volumes?
These questions saved me hours that I’d previously wasted restarting containers instead of rebuilding them to capture new dependencies.
I enabled hot‑reload by binding the host code directory to the services (backend and frontend). The containers could instantly see my changes without a rebuild—a game‑changer that sped up my workflow while keeping the environment isolated.
The Persistence Challenge
Everything was smooth until I needed the application to survive restarts. Docker containers keep commands running continuously, so I didn’t have to restart a terminal to see changes, but the frontend still needed to reflect those changes.
My stack includes:
- Next.js – frontend
- FastAPI – backend
- Numerous model dependencies
- Ollama – local LLM serving
The Ollama Experiment
Why Ollama? I jumped on the hype train that local models protect data, have no rate‑limiting, and are free to query.
I was building a translator. Using external APIs gave li
Source: …
teral, context‑less translations. I scoured the internet for free models—painful, but that’s the trade‑off.
My boss sent me a link to TranslateGemma, an open‑source Google model. It was perfect at the time, and a pre‑built Ollama image existed, so I ran:
ollama pull translategemma:4b
My system couldn’t handle the 12B/27B parameter versions, but I was committed. I set up an Ollama container and everything worked for a few days.
When I tested long‑running content, Ollama ate all the memory allocated to the container, causing the resources to run out. I switched to running Ollama locally.
Question: If it consumes that much memory, is it viable in production? The answer implied high costs—something I kept in the back of my mind.
Enter Redis: The Swiss Army Knife
My project has two login methods:
- Demo login – for fast prototyping (boss insisted).
- Auth login – production‑grade authentication.
I built the demo login using local storage, which was quick but not secure. To unify the two flows and add a caching layer for session data, I introduced Redis. It became the “Swiss Army knife” of my stack, handling:
- Session storage
- Rate‑limiting for API calls
- Simple key‑value caching for model outputs
Redis allowed the demo and auth logins to share a common session store, making the transition from prototype to production smoother.
주요 내용
- 개발 머신이 프로덕션을 반영한다고 가정하지 마세요.
- Docker는 의존성을 격리하고 “내 머신에서는 작동한다”는 문제를 없앱니다.
- 바인드 마운트를 통한 핫‑리로드는 반복 작업을 크게 가속합니다.
- **로컬 LLM 서빙(Ollama)**은 데이터 프라이버시에는 좋지만 메모리를 많이 잡아먹을 수 있으니, 프로덕션에서는 항상 벤치마크하세요.
- Redis는 전체 스택 AI 앱에서 세션 관리와 경량 캐싱을 위한 과소평가된 도구입니다.
의존성 지옥에서 프로덕션‑급 설정으로 전환한 제 경험은 올바른 도구와 실험 의지가 불가능해 보이던 마감일을 관리 가능한 마일스톤으로 바꿀 수 있다는 것을 보여줍니다.
In Memory
인증 로그인을 나중으로 미루고 대부분의 시간을 데모 모드에서 보냈다. 그래서 상사가 진행 상황을 요청할 때마다 즉시 제공할 수 있었다.
하지만 로컬 메모리에 출력을 저장하는 것은 앱이 재시작 후에도 살아남아야 한다는 요구사항과 모순되었다. 로컬 메모리는 프로덕션에서 동작하지 않기 때문이다. 그래서 해결책을 찾아야 했다.
그때 나는 Redis를 발견했다. 캐시부터 영구 저장소까지 다양한 용도로 사용할 수 있는 작고 가벼운 인스턴스다. 매우 빠른 속도를 자랑한다. 처음에는 데이터베이스를 사용하지 않았기 때문에 데모 작업의 저장소로 활용했으며, 완벽하게 동작했다.
Redis의 잠재력 활용
나중에 나는 Redis를 충분히 활용하고 있지 않다는 것을 깨달았다. 백엔드가 작업 진행 상황을 조회하기 위해 Redis에 API를 보내고, 프론트엔드가 백엔드에서 진행 상황을 가져오도록 했지만—그것은 실패했다.
나는 진행 상황을 업데이트하기 위해 WebSockets를 사용해 보았고, 부분적으로 작동했다. 특히 다섯 개 작업에 대한 진행 바가 필요했기 때문에 나는 흥분했다.
Source: …
최종 조각: Celery
나는 Redis와 Celery가 함께 쓰인다는 말을 자주 들었다. RabbitMQ와 Kafka가 무엇인지 궁금했는데, 결국 메시지 브로커라는 것을 알게 되었다. 다시 말해, 우리 슈퍼히어로인 Redis가 메시지 브로커와 작업 큐 두 역할을 동시에 할 수 있다는 것이었다—네 가지 역할이 되는 셈이다.
Celery는 내가 이해하기로는 다음과 같은 작업을 수행하는 워커이다:
- 작업 큐에서 작업을 오프로드한다.
- 작업을 처리한다.
- 브로커에 완료 메시지를 보내어 프론트엔드가 결과를 가져오도록 알린다.
구현하는 데 몇 시간이 걸렸지만, 이것은 내가 지금까지 해본 일 중 가장 좋은 일 중 하나였다. 많은 문제를 해결해 주었다. 나는 FastAPI가 문제의 일부였다는 것을 깨달았다—필요한 만큼 많은 동시 작업을 비동기적으로 처리하지 못했기 때문이다. 그래서 이전에 제대로 된 업데이트를 받지 못했던 것이다.
이제 모든 진행 상황 트래커가 정상적으로 작동하고, 남은 일은 진행 바를 일관되게 보이게 만드는 것뿐이다.
계속됩니다…