최근 과제로 Ruby on Rails를 사용하여 API 서버를 구축하게 되었다. 백엔드 개발을 하다 보면 프레임워크의 철학이 얼마나 개발 경험에 큰 영향을 미치는지 느끼게 된다.
Rails의 마법이 주는 편안함
”이미 다 준비되어 있다”라는 안정감
Rails로 프로젝트를 시작할 때 가장 놀랐던 것은 아무것도 결정할 필요가 없다는 점이었다.
# 단 한 줄로 완벽한 인증 시스템
has_secure_password
Node.js에서는 bcrypt 설치부터 시작해서, hash 라운드 설정, 비밀번호 검증 로직까지 모두 직접 구현해야 했다. 하지만 Rails는 이미 최선의 방법을 알고 있었고, 나는 그저 따르기만 하면 됐다.
class User < ApplicationRecord
has_secure_password
validates :email, uniqueness: { case_sensitive: false }
end
이 몇 줄이면 대소문자 구분 없는 이메일 유니크 검증과 안전한 비밀번호 저장이 완성된다. Node.js였다면 최소 50줄은 작성했을 것이다.
컨벤션이 주는 예측 가능성
Rails 프로젝트는 어느 회사의 코드를 봐도 구조가 비슷하다. app/controllers
, app/models
, config/routes.rb
-
모든 것이 정해진 자리에 있다. 새로운 팀원이 와도, 다른 회사의 Rails 프로젝트를 봐도 익숙하다.
# routes.rb만 보면 전체 API 구조가 한눈에
namespace :api do
namespace :v1 do
resources :contents, only: [:create, :update, :destroy]
end
end
반면 Node.js 프로젝트는 매번 새로운 모험이다. 어떤 프로젝트는 src/controllers
, 어떤 곳은 api/routes
,
또 어떤 곳은 services/handlers
. 자유롭지만 때로는 피곤하다.
Node.js의 자유가 그리울 때
내가 원하는 대로 구성할 수 있는 자유
Rails의 마법은 편하지만, 때로는 답답하다. 특히 Rails Way와 다른 방식으로 무언가를 하고 싶을 때 그렇다.
// Express에서는 미들웨어 순서를 내 마음대로
app.use(rateLimiter);
app.use(customAuth);
app.use(bodyParser.json());
// 원하는 순서로, 원하는 조건으로
Rails에서는 before_action의 순서도 정해진 규칙이 있고, 컨트롤러의 상속 구조도 따라야 한다. 물론 Override할 수 있지만, 그 순간 “Rails답지 않은” 코드가 되어버린다.
투명한 로직의 편안함
Node.js의 가장 큰 장점은 모든 것이 명시적이라는 점이다.
// JWT 토큰 검증 - 모든 과정이 눈에 보인다
const token = req.headers.authorization?.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.userId);
Rails의 경우:
# 어떻게 동작하는지는 숨겨져 있다
include Authentication
# current_user가 마법처럼 생긴다
디버깅할 때 Node.js는 console.log 하나면 충분하지만, Rails는 프레임워크 내부를 들여다봐야 할 때가 많다.
두 철학의 충돌 지점
테스팅에서 느낀 차이
RSpec은 아름답다. 정말로.
it { should validate_presence_of(:email) }
it { should have_many(:contents).dependent(:destroy) }
하지만 이 아름다움 뒤에는 복잡한 매처 시스템이 숨어있다. 테스트가 실패했을 때, 왜 실패했는지 이해하려면 RSpec의 내부 동작을 알아야 한다.
Jest는 투박하지만 직관적이다:
expect(user.email).toBeDefined();
expect(user.contents.length).toBe(0);
에러 처리의 철학
Rails는 에러도 “마법적으로” 처리한다:
# 404는 알아서 처리된다
Content.find(params[:id])
Node.js는 모든 것을 명시해야 한다:
const content = await Content.findById(id);
if (!content) {
return res.status(404).json({ error: "Not found" });
}
귀찮지만, 에러 처리 로직을 완전히 제어할 수 있다는 안정감이 있다.
개발자 경험의 본질
Rails - “빠른 시작, 느린 마스터”
Rails는 처음 며칠은 놀라울 정도로 생산적이다. CRUD API를 만드는 데 하루면 충분하다. 하지만 Rails Way를 벗어나려는 순간, 복잡도가 기하급수적으로 증가한다.
마법을 이해하지 못한 채 사용하다가, 문제가 생기면 Stack Overflow를 뒤지며 시간을 보낸다. “왜 이렇게 동작하지?”라는 질문에 답하려면 Rails 내부를 깊이 이해해야 한다.
Node.js - “느린 시작, 빠른 이해”
Node.js는 처음부터 모든 것을 설정해야 한다. Express 프로젝트 설정에만 반나절이 걸린다. 하지만 한 번 설정하고 나면, 모든 것이 내 통제 하에 있다.
문제가 생겨도 디버깅이 직관적이다. 내가 작성한 코드니까. 프레임워크의 마법이 아닌, 내 로직의 문제다.
결론: 상황에 맞는 선택
Rails와 Node.js, 둘 중 무엇이 더 나은 개발자 경험을 제공하는가? 답은 “상황에 따라 다르다”이다.
Rails가 빛나는 순간:
- 표준적인 웹 애플리케이션을 빠르게 만들어야 할 때
- 팀 전체가 동일한 컨벤션을 따라야 할 때
- 보일러플레이트 코드를 최소화하고 싶을 때
Node.js가 필요한 순간:
- 특수한 요구사항이 많은 프로젝트
- 세밀한 제어가 필요한 시스템
- 프레임워크의 제약을 받고 싶지 않을 때
두 프레임워크를 모두 경험해본 지금, 나는 Rails의 편안함도, Node.js의 자유로움도 모두 소중하다는 것을 안다. Rails를 사용하면서 Node.js의 투명함이 그리웠고, Node.js로 돌아가니 Rails의 마법이 그립다.
결국 좋은 개발자란 도구의 철학을 이해하고, 상황에 맞게 선택할 수 있는 사람이 아닐까. Rails의 마법을 즐기되 그 원리를 이해하려 노력하고, Node.js의 자유를 누리되 일관성을 유지하려 노력하는 것.