Devise에서 Custom Auth로: `has_secure_password`만이 전부는 아니다

발행: (2026년 2월 1일 오후 08:04 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

Devise to Custom Auth: 단순히 has_secure_password만은 아니다 - 표지 이미지

전환의 충동

Devise는 Rails 인증의 800‑파운드 고릴라다. 신뢰성이 높고, 보안도 뛰어나며 모든 것을 해준다.
하지만 결국 “벽”에 부딪히게 된다. 비밀번호 없는 로그인 흐름을 원하거나, 리다이렉트를 바꾸기 위해 수십 개의 컨트롤러 메서드를 오버라이드하는 데 지치거나, 혹은 자신의 앱이 어떻게 사용자를 로그인시키는지 이해하고 싶을 수도 있다.

그래서 당신은 결심한다: “Devise를 완전히 뽑아내고 Rails 내장 has_secure_password를 사용할 거야.”

나는 이 마이그레이션을 직접 해봤다. 코드베이스가 더 깔끔하고 빠르게 변하지만, 그 과정은 지뢰밭이다. 여기서 마주하게 될 가장 큰 도전 과제들을 소개한다.

챌린지 1: 데이터베이스 스키마 불일치

Devise는 데이터베이스 컬럼에 대해 의견이 강합니다.

  • Devise가 사용하는 컬럼: encrypted_password
  • Rails(has_secure_password)가 기대하는 컬럼: password_digest

두 가지 옵션이 있으며 각각 장단점이 있습니다.

옵션 A: 별칭 (빠르고 대충)

# app/models/user.rb
class User < ApplicationRecord
  has_secure_password :password, validations: false
  alias_attribute :password_digest, :encrypted_password
end

위험: 해킹 같은 느낌이 듭니다. 표준 Rails 관례를 기대하는 향후 gem이나 도구가 문제를 일으킬 수 있습니다.

옵션 B: 마이그레이션 (깨끗하지만 위험)

# db/migrate/xxxx_rename_encrypted_password.rb
class RenameEncryptedPassword < ActiveRecord::Migration[6.1]
  def change
    rename_column :users, :encrypted_password, :password_digest
  end
end

위험: 배포 중에 다운타임이 없도록 보장해야 합니다. 롤백은 금방 복잡해질 수 있습니다.

챌린지 2: “Recoverable” 모듈

Devise는 “Forgot Password” 기능을 무료로 제공했습니다. 이를 제거하면 직접 구현하지 않는 한 아무도 비밀번호를 재설정할 수 없습니다.

안전한 비밀번호 재설정 흐름은 다음을 만족해야 합니다:

  1. 고유하고 고엔트로피 토큰을 생성한다.
  2. 그 토큰의 다이제스트를 저장한다 (원본 토큰은 절대 저장하지 않는다).
  3. 만료 시간을 설정한다 (예: 2시간).
  4. 이메일 전송을 처리한다.
  5. 핵심: 사용 후 토큰을 무효화하여 재사용 공격을 방지한다.

Devise는 타이밍 공격 방지와 토큰 해싱을 대신 처리해 주었지만, 이제 그 보안 책임은 여러분에게 있습니다.

Challenge 3: 쿠키 보안 및 “Remember Me”

session[:user_id]는 기본 로그인에 작동하지만 “Remember Me”는 더 복잡합니다. Devise의 Rememberable 모듈은 영구 쿠키, 토큰 회전, 안전한 비교를 관리합니다.

직접 구현한다면:

  • 쿠키에 서명/암호화하기.
  • 세션 하이재킹 방지(도난당한 쿠키가 장기 접근을 허용).
  • 모든 세션을 “무효화”할 방법 제공(예: 사용자가 비밀번호를 변경할 때).

챌린지 4: 뷰 및 컨트롤러 정리

Devise 헬퍼가 널리 사용되고 있습니다. 코드베이스 전반에 걸쳐 이를 교체해야 합니다:

  • authenticate_user! → 직접 만든 require_login 필터
  • user_signed_in?logged_in?
  • current_userApplicationController에 정의된 커스텀 헬퍼
  • devise_error_messages! → 직접 만든 에러 파셜

팁: Gem을 바로 삭제하지 마세요. 기존 Devise 메서드 이름을 새로운 로직에 매핑하는 shim을 만들어 점진적으로 마이그레이션할 수 있도록 하세요.

# app/controllers/concerns/auth_shim.rb
module AuthShim
  extend ActiveSupport::Concern

  included do
    helper_method :current_user, :logged_in?
  end

  def authenticate_user!
    redirect_to login_path unless logged_in?
  end

  def user_signed_in?
    logged_in?
  end

  def logged_in?
    !!current_user
  end

  def current_user
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
  end
end

AuthShimApplicationController에 포함하고 호출을 점진적으로 교체하세요.

챌린지 5: 레거시 비밀번호 호환성

Devise와 has_secure_password는 기본적으로 BCrypt를 사용하므로 보통 서로 호환됩니다. 하지만 오래된 Rails 애플리케이션(예: Rails 3/4에서 마이그레이션된 경우)에서는 Devise가 레거시 해싱 전략(SHA‑1 + 소금)으로 설정되어 있을 수 있습니다. has_secure_password는 이러한 해시를 인증할 수 없습니다.

해결 방법:

  1. 저장된 비밀번호가 BCrypt 해시인지 감지합니다.
  2. 그렇지 않은 경우 레거시 검증 로직을 적용합니다.
  3. 레거시 로그인에 성공하면 비밀번호를 BCrypt로 다시 해시하고 레코드를 업데이트합니다.

결론: 가치가 있을까?

하지 말아야 할 경우:

  • 클라이언트를 위한 MVP를 구축 중이라면 (속도가 중요함).
  • OmniAuth에 크게 의존한다면 (Devise가 OAuth 통합을 원활하게 해줍니다).
  • 세션 고정이나 타이밍 공격 같은 보안 개념에 익숙하지 않다면.

해야 할 경우:

  • 5년 이상 지속될 모놀리식 앱을 구축 중이라면.
  • 특수한 흐름이 필요하다면 (OTP만, 매직 링크, 다단계 온보딩).
  • 메모리 사용량과 의존성 부풀림을 줄이고 싶다면.

Devise를 제거하는 것은 Rails에서 가장 교육적인 작업 중 하나입니다. 플래시라이트만 챙기세요—배관 안은 어두우니까.

Back to Blog

관련 글

더 보기 »