Helper를 쓰지 않는 이유
My Convention — 데이터 로직은 Controller에, Helper는 최소한으로
Rails의 Helper는 뷰에서 사용할 수 있는 메서드를 정의하는 모듈입니다. ApplicationHelper에 메서드를 추가하면 모든 뷰에서 호출 가능합니다.
언뜻 편리해 보이지만, 실제 프로젝트에서 Helper를 적극적으로 사용하면 다음과 같은 문제가 쌓입니다.
왜 Helper를 피하는가?
1. 전역 스코프 오염
Rails Helper는 기본적으로 모든 뷰에 include됩니다. PostsHelper에 정의한 메서드가 UsersController의 뷰에서도 호출됩니다. config.action_controller.include_all_helpers = false로 끌 수 있지만, 대부분의 프로젝트에서 기본값(true)을 유지합니다.
결과: Helper 파일이 늘어날수록 네임스페이스 충돌 위험이 커지고, 어디에 정의된 메서드인지 추적이 어려워집니다.
2. 데이터 가공 로직의 잘못된 위치
Helper에 DB 조회, 배열 변환, 조건부 데이터 가공 같은 로직이 들어가면 MVC 경계가 무너집니다.
# ❌ Bad — Helper에 데이터 가공
def recent_posts_for_sidebar
Post.published.order(created_at: :desc).limit(5)
end
# ✅ Good — Controller에서 준비
# controller
@recent_posts = Post.published.order(created_at: :desc).limit(5)
Controller에서 @인스턴스변수로 넘기면 흐름이 한 눈에 보입니다: 요청 → Controller(데이터 준비) → View(표시). Helper에 넣으면 View가 직접 데이터를 가져오는 형태가 되어 디버깅이 어려워집니다.
3. 테스트 어려움
Helper 메서드는 뷰 컨텍스트(self가 ActionView)에서 실행됩니다. url_for, content_tag, t() 같은 뷰 헬퍼에 의존하는 순간, 단위 테스트에서 이 컨텍스트를 재현해야 합니다.
Controller의 인스턴스 변수는 Request Spec에서 자연스럽게 테스트됩니다.
4. 비대해지는 Helper 파일
Helper는 "잡동사니 서랍"이 되기 쉽습니다. 분류 기준이 모호하고 파일 하나에 관련 없는 메서드가 쌓입니다.
5. IDE/에디터 지원 부족
Helper 메서드는 뷰에서 method_name으로 직접 호출되므로, IDE의 "정의로 이동(Go to Definition)"이 잘 작동하지 않는 경우가 많습니다. Controller의 @변수는 추적이 명확합니다.
6. 흔한 이름 + 출처 불명 = 의도치 않은 삭제 사고
Helper 메서드는 이름이 평범한 경우가 많습니다: format_date, status_label, badge_color 등. 뷰에서 badge_color(user) 형태로 호출되면, 이것이 어느 Helper 파일에 정의된 건지 즉시 알 수 없습니다.
문제는 리팩토링이나 정리 작업 중에 발생합니다. 누군가가 PostsHelper를 정리하면서 '이거 안 쓰이는 것 같은데?' 하고 badge_color를 삭제하면, 전혀 다른 컨트롤러의 뷰에서 undefined method 에러가 터집니다. Helper가 전역이기 때문에, 정의된 파일과 실제 사용처가 완전히 다른 곳에 있을 수 있기 때문입니다.
# app/helpers/posts_helper.rb
def badge_color(status) # Posts 전용인 줄 알고 삭제 →
status == 'active' ? 'green' : 'gray'
end
# 그런데 실제로는 app/views/admin/users/index.html.erb 에서도 사용 중!
# → NoMethodError: undefined method 'badge_color'
Controller의 @변수라면 해당 Controller 파일만 보면 되므로 이런 사고가 원천적으로 발생하지 않습니다.
그러면 대안은?
추천: Decorator / Presenter 패턴
Helper의 근본 문제는 모델에 속하는 표시 로직인데 전역 함수로 떠돌아다닌다는 것입니다. Decorator/Presenter는 이 로직을 모델 객체에 감싸서 해결합니다.
# app/decorators/user_decorator.rb
class UserDecorator < SimpleDelegator
def badge_color
active? ? 'green' : 'gray'
end
def display_name
name.presence || email.split('@').first
end
def joined_at
created_at.strftime('%Y년 %m월 %d일')
end
end
# Controller
def show
@user = UserDecorator.new(User.find(params[:id]))
end
# View — 그냥 모델처럼 쓰면 됨
<span class=\"bg-<%= @user.badge_color %>-500\"><%= @user.display_name %></span>
<p>가입일: <%= @user.joined_at %></p>
Decorator vs Helper 비교:
Helper:
badge_color(user)→ 어디에 정의? 전역 탐색 필요Decorator:
@user.badge_color→ UserDecorator 파일 하나만 보면 됨Helper: 삭제해도 어디서 에러 날지 모름
Decorator: 해당 모델 관련 뷰에서만 사용, 추적 명확
SimpleDelegator를 상속하면 gem 없이 순수 Ruby로 구현 가능합니다. 원본 모델의 모든 메서드를 그대로 위임(delegate)하므로, @user.email처럼 기존 속성도 그대로 접근됩니다.
기타 대안
Controller @변수: 데이터 가공/조회는 여기서
뷰 인라인: 1~2곳에서만 쓰는 삼항연산자 수준의 표시 로직
Partial: 재사용 가능한 UI 조각 (
render 'shared/badge')Service Object: 복잡한 비즈니스 로직
구조 다이어그램
display_name, etc.
핵심 포인트
Helper에 메서드를 추가하려는 순간 → "이건 모델의 표시 로직인가?" 자문
모델의 표시 로직이면 → Decorator(SimpleDelegator)를 만들어 모델에 감싸기
DB 조회/데이터 가공이면 → Controller에서 @변수로 전달
복잡한 비즈니스 로직이면 → Service Object로 분리
재사용 가능한 UI 조각이면 → Partial로 추출
1~2곳에서만 쓰는 간단한 표시이면 → 뷰에 인라인 (삼항연산자)
그래도 Helper가 필요하면 → 순수 표시 함수(SVG, 날짜 포맷)만, 5곳 이상 재사용 확인
장점
- ✓ MVC 흐름이 명확: Controller(데이터) → View(표시)
- ✓ Request Spec에서 자연스럽게 테스트 가능
- ✓ IDE에서 @변수 추적 용이
- ✓ 전역 네임스페이스 오염 방지
- ✓ Helper 파일이 비대해지지 않음
- ✓ 새 팀원이 코드 흐름을 빠르게 파악
단점
- ✗ Controller가 약간 길어질 수 있음
- ✗ 공통 표시 함수도 Helper 없이 구현하면 중복 가능성
- ✗ Rails 공식 가이드와 다소 다른 접근 (Helper를 적극 권장하는 자료가 많음)
- ✗ render_pattern_icon 같은 순수 표시 함수는 Helper가 자연스러움