“이번엔 금방 되겠지.”
배포는 늘 이렇게 시작된다.
그리고 늘 같은 문장으로 끝난다.
“아, 또 이거네…”
이번 배포는 Senior HealthNote.
건강 기록 앱이다.
작은 서비스 하나 올리는 일이었지만,
결과적으로는 며칠간의 전쟁이었다.
Rails 8, Kamal, Docker, PostgreSQL.
하나씩 나를 배신했다.
하나씩 다시 붙잡았다.
1. DATABASE_URL의 배신
URI::InvalidURIError: the scheme postgresql does not accept registry part
에러 메시지가 이상했다.
코드는 맞는 것 같은데.
알고 보니 환경변수에 오타가 있었다.
YOUR_DB_PASSWORD가 그대로 들어가 있었다.
한 글자가 모든 걸 망친다.
2. SQLite vs PostgreSQL
NoMethodError: undefined method 'uuid' for SQLite3
Docker가 옛날 이미지를 캐시하고 있었다.
SQLite 어댑터가 계속 살아남아 있었다.
docker system prune -a
다 지우고 새로 빌드했다.
문제는 코드가 아니라 히스토리였다.
3. “db”는 어디 있는가
PG::ConnectionBad: could not translate host name "db"
Docker 안에서 “db”라는 호스트를 찾을 수 없다.
컨테이너 이름이 달랐다.
네트워크 설정이 어긋나 있었다.
Docker에서는 이름이 주소다.
4. 포트의 혼돈
Health check failed: {"status":"unhealthy"}
Rails는 3003,
Proxy는 3000,
Healthcheck는 3003.
완벽한 불일치.
전부 3000으로 통일했다.
일관성이 기능보다 중요하다.
5. Zeitwerk의 규칙
expected file to define constant Entities::HealthRecord
폴더 구조를 멋대로 만들었더니,
Rails가 화를 냈다.
Rails가 기대하는 구조로 바꿨다.
프레임워크를 이기려 하지 말자.
6. 약어의 함정
uninitialized constant Bmi (Did you mean BMI?)
BMI를 Bmi로 읽고 있었다.
inflect.acronym 'BMI'
한 줄 추가.
Zeitwerk는 똑똑하지만, 추측은 안 한다.
7. Ruby 예약어
for라는 이름의 메서드를 만들었다.
Ruby가 거부했다.
for는 예약어다.
언어의 규칙은 엄격하다.
8. Rails 8의 Solid 시리즈
The `cable` database is not configured
SolidCable, SolidQueue, SolidCache.
각각 별도의 DB 설정이 필요했다.
Rails 8은 “알아서 해준다”가 아니라
“알고 있으면 강력하다”에 가깝다.
9. libjemalloc 경고
libjemalloc.so.2 cannot be preloaded
치명적인 에러는 아니었다.
하지만 찜찜했다.
원인을 찾고, 주석 처리했다.
모든 에러가 치명적인 건 아니다.
하지만 왜 나오는지는 알아야 한다.
결국
15개가 넘는 에러를 만났다.
15번 넘게 좌절했다.
하지만 결국 배포에 성공했다.
무엇을 얻었는가.
에러를 무서워하지 않게 됐다.
로그를 읽을 수 있게 됐다.
“다시 하면 더 빠르게 할 수 있다”는 확신이 생겼다.
이게 바로 늦게 시작한 사람의 강점이다.
한 번 겪은 건, 절대 헛되지 않는다.
다음 배포는 더 짧을 것이다.
그리고 언젠가는,
누군가 이 글 덕분에 덜 헤맬지도 모른다.
그럼 그걸로 충분하다.
시지프는 오늘도 바위를 굴린다.
하지만 그 안에 자기만의 반항을 담아낸다.
나도 그렇게 한다.
