사업 운영 중 직접 만든 입출고 & 레시피 연동 시스템 – Rails로 다시 도전하기 2

보안을 고려한 인증 시스템 구축기

소개

Rails 기반으로 생산관리 시스템을 재구축하면서, 가장 먼저 해결해야 했던 과제는 "누가 어떤 데이터에 접근할 수 있는가?" 였습니다.

소규모 제조업체이지만 관리자와 직원들 사이에 각자의 업무에 필요한 기능만 접근할 수 있도록 하는 것이 중요합니다. 또한 보안 측면을 고려해 작업장 내부의 태블릿에서만 시스템에 접속할 수 있도록 제한하는 기능이 필요했습니다.

이 글에서는 Devise를 기반으로 한 기본 인증부터 디바이스 기반 접근 제어, 세밀한 페이지 권한 관리, 사용자 초대 이메일, 로그인 이력 추적까지 포함한 실전 구현 사례를 공유합니다. 가능한 빠르게 실무에 적용할 수 있도록 핵심 내용을 중심으로 설명합니다 😎

진행 방법

✅ Devise로 인증 시스템 기본 세팅

  • 왜 Devise를 썼나?

    • Rails에서 가장 널리 쓰이며, 다양한 인증 시나리오를 빠르게 구현할 수 있음

    • 기본적으로 회원가입, 로그인, 비밀번호 재설정, 세션 관리, CSRF 보호 등을 제공

    • Trackable 모듈로 로그인 횟수와 마지막 로그인 정보까지 자동 추적 가능

  • 설치 과정

# Gemfile
gem 'devise'

# 설치 및 User 모델 생성
rails generate devise:install
rails generate devise User
  • 사용자 모델에 커스텀 필드 추가

    • 사용자 이름, 관리자 여부 필드를 추가

    • Trackable 기능도 함께 적용해 로그인 이력 확인 가능

# db/migrate/xxx_devise_create_users.rb
create_table :users do |t|
  t.string :email, null: false, default: ""
  t.string :encrypted_password, null: false, default: ""

  # 로그인 이력
  t.integer :sign_in_count, default: 0
  t.datetime :current_sign_in_at
  t.datetime :last_sign_in_at
  t.string :current_sign_in_ip
  t.string :last_sign_in_ip

  # 커스텀
  t.boolean :admin, default: false
  t.string :name
  t.timestamps
end
  • 첫 사용자 자동 관리자 설정

# app/controllers/users/registrations_controller.rb
if User.count.zero?
  params[:user][:admin] = true
end

✅ 디바이스 Fingerprint 기반 접근 제어

  • 배경: 외부 접속 차단을 위한 보안 조치로, 작업장 내부에서만 접속 가능한 방식이 필요했습니다. 단순한 IP 제한은 불안정하므로 브라우저 지문(Fingerprint) 방식을 도입했습니다.

  • 브라우저 fingerprint란?

    • 브라우저, OS, 하드웨어 정보, canvas 렌더링 결과 등을 조합해 고유 해시값(SHA-256)을 생성

    • 이 fingerprint를 디바이스 식별자로 사용

  • 프론트엔드 구현 (요약)

// device_fingerprint.js
form.appendChild(createHiddenField('device_fingerprint', fingerprint));
  • 디바이스 인증 로직

    • 로그인 시 fingerprint를 함께 전송

    • 서버에서 해당 디바이스가 승인된 이력인지 확인

unless resource.device_authorized?(fingerprint)
  sign_out(resource)
  redirect_to new_device_authorization_path(...)
end
  • 디바이스 정보는 DB에 저장되어 추후 조회 및 관리 가능

✅ 이메일 기반 승인 요청 워크플로우

  • 승인되지 않은 디바이스로 접속 시, 이메일 전송

  • Rails credentials와 Gmail SMTP를 활용한 보안 설정

# credentials.yml
 gmail:
   email: [email protected]
   password: your-app-password
  • 문제: 개발환경에서 deliver_later가 작동하지 않음

    • 해결: 개발 시에는 deliver_now로 변경, 운영환경에서는 ActiveJob을 위한 큐 어댑터 설정

# production.rb
config.active_job.queue_adapter = :solid_queue

✅ 로그인 이력 추적 시스템

  • LoginHistory 모델 생성

    • 로그인 성공/실패 여부, 디바이스 fingerprint, IP, 브라우저, OS 등 저장

  • 관리자 전용 대시보드에서 전체 이력 확인 및 통계 제공

scope :today, -> { where('attempted_at >= ?', Time.current.beginning_of_day) }

✅ 페이지별 접근 권한 관리

  • PagePermission 모델을 만들어 15개 페이지에 대한 권한 설정

  • 관리자 여부와 상관없이 각 페이지의 접근 여부를 제어

  • ApplicationController에서 before_action으로 권한 체크 일괄 적용

def check_page_permission
  unless PagePermission.allowed?(page_key, current_user)
    redirect_to root_path
  end
end

✅ UI 필터링

  • 메뉴, 대시보드 카드 등에서 page_allowed?를 활용해 권한 없는 메뉴는 숨김 처리

  • UX와 보안을 동시에 만족시키는 구조

🐛 트러블슈팅: 체크박스 저장 문제

  • 문제: 체크박스를 해제하면 params가 전송되지 않아 기존 권한이 유지됨

  • 해결: 저장 전 전체 false 초기화 → 체크된 항목만 true로 설정

PagePermission.update_all(allowed_for_users: false)
params[:permissions].each do |id, allowed|
  PagePermission.find(id).update(allowed_for_users: true)
end

🐛 트러블슈팅: fingerprint 누락 버그

  • 문제: 회원가입 시 fingerprint 전송이 누락됨

  • 원인: JS에서 form selector가 /users 경로를 감지하지 못함

  • 해결: 다양한 action 패턴을 커버하도록 selector 확장

const deviseForms = document.querySelectorAll([
  'form[action*="sign_in"]',
  'form[action*="sign_up"]',
  'form[action="/users"]'
].join(', '))

✅ 사용자 초대 이메일

  • 관리자가 사용자 생성 시 자동으로 초대 이메일 전송

  • 임시 비밀번호 포함, 로그인 버튼 포함한 깔끔한 템플릿 구성

결과와 배운 점

🎯 구축 완료 기능 요약

  1. 인증 시스템

    • Devise 기반, Trackable, 자동 관리자 지정

  2. 디바이스 기반 접근 제어

    • Fingerprint 인증, 승인 워크플로우, 수동 관리

  3. 보안 감사 기능

    • 로그인 이력 저장, 관리자 대시보드

  4. 세밀한 권한 설정

    • 페이지별 권한 제어, UI 필터링 연계

  5. 사용자 경험 향상

    • 필요한 기능만 보이게, UX 혼란 최소화

💡 얻은 인사이트

  • Fingerprint 방식은 현실적인 보안 해법

    • 단, spoofing 가능성이 있어 2단계 인증 등 추가 보안 권장

  • Devise는 커스터마이징이 매우 유연함

  • Rails의 Convention을 따르되, 실제 HTML을 반드시 검증해야 함

  • 폼 동작(특히 checkbox 등)은 서버 처리 로직에 항상 영향을 준다

도움 받은 글

1
2개의 답글

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요