Home Ruby GEM 시리즈
Post
Cancel

Ruby GEM 시리즈

ZipLine

Zipline은 Rails 애플리케이션에서 동적으로 생성된 ZIP 파일을 스트리밍하기 위한 Ruby gem이다.

  • 스트리밍 방식으로 ZIP 파일 생성을 위해 전체 파일이 완성될 때까지 기다리지 않는다. 따라서 대용량 ZIP 파일 생성에도 큰 디스크 공간이나 메모리가 필요하지 않아 대기 시간과 다운로드 시간이 감소한다.
  • ZIP 생성 중 이전으로 되돌아가지 않고 HTTP를 통해 스트리밍한다.
  • “directory/file” 형식으로 파일 이름을 지정하여 디렉토리 구조 생성이 가능하다고함.
1
2
3
4
5
6
7
8
9
class MyController < ApplicationController
  include Zipline
  
  def index
    users = User.all
    files = users.map{ |user| [user.face, "#{user.username}.png"] }
    zipline(files, 'user_face.zip')
  end
end

Kaminari

페이지네이션을 위한 Ruby gem이다. 풀스택용인듯

1
2
3
4
5
6
7
class User < ApplicationRecord
  # 기본 페이지네이션
  User.page(params[:page]).per(25)
  
  # 스코프와 함께 사용
  User.where(age: 20..30).page(params[:page])
end

1
2
3
4
5
6
7
<%= paginate @users %>
<% @users.each do |user| %>
  <%= user.name %>
<% end %>

<!-- 부트스트랩 스타일 적용 -->
<%= paginate @users, theme: 'bootstrap4' %>

Puma

Ruby/Rack 애플리케이션을 위한 고성능 웹 서버 gem이다. 설정용 GEM

  • 멀티스레드 지원: 각 요청을 별도 스레드에서 처리
  • 멀티프로세스: 클러스터 모드에서 메모리 효율적인 fork 지원
  • 독립 실행형: SSL 지원, 무중단 재시작 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# config/puma.rb
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

# 클러스터 모드 설정
workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# 사전 로딩 설정
preload_app!

# 포트 설정
port ENV.fetch("PORT") { 3000 }

# 환경 설정
environment ENV.fetch("RAILS_ENV") { "development" }

Jbuilder

Json을 편하게 처리하기 위한 gem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
json.post do
  json.title @post.title
  json.content @post.content
  
  # 조건을 넣을수 있다.
  json.author @post.author if @post.author.present?
  
  # 중첩을 넣어서
  json.comments @post.comments do |comment|
    json.content comment.content
    json.user do
      json.name comment.user.name
      json.avatar_url comment.user.avatar_url
    end
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 결과값
{
  "post": {
    "title": "첫 번째 게시글",
    "content": "안녕하세요",
    "author": "김철수",
    "comments": [
      {
        "content": "좋은 글이네요",
        "user": {
          "name": "박영희",
          "avatar_url": "https://example.com/avatar.jpg"
        }
      },
      {
        "content": "감사합니다",
        "user": {
          "name": "이민수",
          "avatar_url": "https://example.com/avatar2.jpg"
        }
      }
    ]
  }
}

1
json.extract! user, :id, :name, :email
1
2
3
4
5
6
// 결과값
{
  "id": 1,
  "name": "김철수",
  "email": "kim@example.com"
}

1
2
# 복잡한 것은 따로 설정가능 코드의 복잡도를 해소
json.array! @users, partial: 'users/user', as: :user
1
2
3
4
5
6
7
8
9
10
11
12
13
// 결과값
[
  {
    "id": 1,
    "name": "김철수",
    "email": "kim@example.com"
  },
  {
    "id": 2,
    "name": "박영희",
    "email": "park@example.com"
  }
]

Pundit

권한 부여 시스템을 제공하는 gem, 간단하게 정책 기반 인증을 구현가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? || post.user_id == user.id
  end

  def destroy?
    user.admin? || post.user_id == user.id
  end
end

class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    authorize @post # 정책이 정의된 클래스를 자동으로 찾음
    if @post.update(post_params)
      redirect_to @post
    end
  end
end
# app/policy 디렉토리에 정의함
class Policy
  attr_reader :user, :post
  
  def initialize(user, post)
    @user = user  # current_user가 자동으로 전달됨
    @post = post  # authorize에 전달된 객체
  end

  def update?
    user.admin? || post.user_id == user.id  # 관리자이거나 작성자인 경우만 true
  end
end

Draper

데코레이터 패턴을 사용하도록 모델에서 자주 사용되는 함수들을 하나의 파일로 만들어 사용함.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# app/decorators/user_decorator.rb
class UserDecorator < Draper::Decorator
  delegate_all

  def full_name
    "#{object.first_name} #{object.last_name}"
  end

  def member_since
    object.created_at.strftime("%B %Y")
  end

  def status_label
    if object.active?
      h.content_tag(:span, "활성", class: "badge badge-success")
    else
      h.content_tag(:span, "비활성", class: "badge badge-danger")
    end
  end
end

class UsersController < ApplicationController
  def show
    @user = User.find(params[:id]).decorate
  end
end

Paper Trail

모델의 변경 이력을 추적하고 관리할 수 있게 해주는 gem

1
2
3
4
5
6
7
8
9
10
11
12
class Post < ApplicationRecord
  has_paper_trail
  
  # 특정 필드만 추적
  has_paper_trail only: [:title, :content]
  
  # 메타데이터 추가
  has_paper_trail meta: {
    editor_id: :editor_id,
    category: :category_name
  }
end

Sidekiq

백그라운드 작업 처리를 위한 gem입니다.

설정 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# app/workers/email_worker.rb
class EmailWorker
  include Sidekiq::Worker
  
  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome_email(user).deliver_now # 대충 user한테 알림을 보낸 메서드
  end
end

# 기본으로 제공하는 메서드
EmailWorker.perform_async(user.id)  # 비동기 실행
EmailWorker.perform_in(2.hours, user.id)  # 2시간 후 실행
EmailWorker.perform_at(2.days.from_now, user.id)  # 특정 시간에 실행

장점 단점

  • Redis를 사용하여 빠른 처리 속도 하지만 그만큼 비용이 많이 들고 레디스 메모리도 증가함
  • 실시간 모니터링 대시보드 제공
  • 실패한 작업 재시도 기능
  • 다중 큐 지원

Carrierwave

간단한 코드 작성으로 파일 업로드를 위한 gem. 로컬이나 AWS s3 버킷에 저장가능

설정 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick  # 이미지 처리

  storage :file  # 로컬 저장
  storage :fog  # AWS S3 등 클라우드 저장

  # 썸네일 생성
  version :thumb do
    process resize_to_fit: [100, 100]
  end

  # 허용하는 파일 형식
  def extension_allowlist
    %w(jpg jpeg gif png)
  end
end

# 모델에서 사용
class User < ApplicationRecord
  mount_uploader :avatar, ImageUploader
end

사용 예시

1
2
3
4
5
6
7
8
9
10
# 컨트롤러
def create
  @user = User.new(user_params)
  @user.avatar = params[:file]  # 파일 업로드
  @user.save
end

# 뷰
<%= image_tag @user.avatar.url %>  # 원본 이미지
<%= image_tag @user.avatar.thumb.url %>  # 썸네일, S3로 부터 받음

Figaro

환경 변수 관리를 위한 gem. application.yml에 들어가는 민감정보를 방지함.

설정 예시

1
2
3
4
5
6
7
8
# config/application.yml
development:
  AWS_KEY: "dev_key_123"
  API_SECRET: "dev_secret_456"

production:
  AWS_KEY: "prod_key_789"
  API_SECRET: "prod_secret_012"

사용 예시

1
2
3
4
5
6
# 어플리케이션 코드에서 접근
ENV["AWS_KEY"]
Figaro.env.aws_key

# 필수 키 지정
Figaro.require_keys("aws_key", "api_secret")

Figaro를 설치하게되면 config/application.yml 파일 생성되고 .gitignore 파일에 /config/application.yml 자동 추가된다. 그래서 직접적인 파일공유를 해야하므로 별도의 환경변수 설정이 필요함. Heroku를 안쓰면 Rails credentials이 난듯?


RSpec-Rails

Rails 애플리케이션을 위한 테스팅 프레임워크

테스트 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
  # 모델 유효성 검사
  it "vaildation이 잘되나" do
    user = User.new(
      email: "test@example.com",
      password: "password123"
    )
    expect(user).to be_valid
  end

  # 연관 관계 테스트
  it "has many posts" do
    should have_many(:posts)
  end
end

# spec/controllers/users_controller_spec.rb
RSpec.describe UsersController, type: :controller do
  describe "GET #index" do
    it "returns a success response" do
      get :index
      expect(response).to be_successful
    end
  end

  describe "POST #create" do
    it "creates a new user" do
      expect {
        post :create, params: { user: valid_attributes }
      }.to change(User, :count).by(1)
    end
  end
end

다양한 테스트 도구 제공 (목업, 스텁 등)과 리포트를 제공함. 하지만 나는 rails c or s 하고 직접값 입력하는게 루비를 잘쓰는게 아닐까 싶다.

This post is licensed under CC BY 4.0 by the author.