Rails (Ruby on Rails)

Rails (Ruby on Rails)

๐Ÿ” Introduction

Rails(Ruby on Rails, RoR)๋Š” Ruby์˜ ๋Œ€ํ‘œ์ ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ MVC ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋Š” ํ’€์Šคํƒ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. Ruby ํŠน์œ ์˜ ์‰ฝ๊ณ  ์ง๊ด€์ ์ธ ๋ฌธ๋ฒ•์œผ๋กœ ์ธํ•ด Rails ๋˜ํ•œ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ์–ด๋ ต์ง€ ์•Š์œผ๋ฉฐ scaffold ๋“ฑ์˜ ๊ธฐ๋Šฅ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์›น ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์™ธ์—๋„ ์›น ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ ์žˆ์–ด ํ•„์š”ํ•œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ธฐ์ˆ , ํ…Œํฌ๋‹‰, ๋ณด์•ˆ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ด ๋งŽ์ด ๊ณ ๋ ค๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

Installation

sudo gem install rails

Struct of Rails app

Model

  • config/database.yml : DB ์ ‘์†์ •๋ณด ๋ฐ ์„ค์ •
  • db/migrate/* : active_record ๊ด€๋ จ ์ฝ”๋“œ
  • app/models/* : model(db schema)์— ๊ด€๋ จ๋œ ์ฝ”๋“œ

View

  • app/views/* : Model, Controller์™€ ์—ฐ๊ฒฐ๋œ View ์ฝ”๋“œ๋“ค

Controller

  • app/controllers/* : ์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ
  • config/routes.rb : ์‹ค์ œ ๋ผ์šฐํŒ…์ด ๋ช…์‹œ๋˜๋Š” ์ฝ”๋“œ

Etc

  • config/* : ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์„ค์ •๋“ค
  • public/* : public ๋””๋ ‰ํ† ๋ฆฌ

๐Ÿ•น Snippet

New Web App

rails new {appname}

With TailWind

rails new {appname} --css tailwind

Specifies the DB

rails new {appname} --database=postgresql

New API App

rails new {appname} --api

or

# config/application.rb
config.api_only = true

Generate with scaffold

scaffold๋Š” ํ•œ๋ฒˆ์— ๋ช…๋ น์œผ๋กœ MVC(Model, View, Controller)๋ฅผ ํ•œ๋ฒˆ์— ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

rails g scaffold post title:string context:text

types

  • string
  • text (long text, up to 64k, often used for text areas)
  • datetime
  • date
  • integer
  • binary
  • boolean
  • float
  • decimal (for financial data)
  • time
  • timestamp
  • references

Migration

New model

rails g model Scans name:string url:string

Migration

rake db:migrate

Rollback

rake db:rollback # ๊ฐ€์žฅ ์ตœ๊ทผ DB ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.
rake db:rollback STEP=2 # 2๋ฒˆ Undo ํ•˜์—ฌ DB ์ƒํƒœ๋ฅผ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.

Redo

rake db:migrate:redo STEP=3 
# rollback ํ›„ ๋‹ค์‹œ migration ํ•ฉ๋‹ˆ๋‹ค. STEP์œผ๋กœ task๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

Add column

rails g migration add_columns_from_model

# Example
rails g migration add_content_from_post

Change column type

rails g migration ChangeColumnType

class ChangeColumnType < ActiveRecord::Migration[7.0]
  def change
    change_column :users, :age, :integer
  end
end

ERD

rails-erd ํฌํ•จ ํ›„ rake erd๋กœ ํ˜„์žฌ model์— ๋Œ€ํ•œ erd๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

gem 'rails-erd'
rake erd

Run Server

rails s
rails server

Run Console

Rails๋Š” console ๋ช…๋ น์„ ํ†ตํ•ด ์‹คํ–‰์ค‘์ธ ์•ฑ์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

rails c
rails console

with env

rails console -e <env>
# rails console -e developement
# rails console -e production

with sandbox mode

rails console --sandbox

sandbox ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋กœ ์ธํ•ด DB ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋„ console์ด ์ข…๋ฃŒ๋  ๋•Œ ๋กค๋ฐฑ๋ฉ๋‹ˆ๋‹ค.

๐ŸŽฎ Codes

Caching

Low Level Caching

# Read
value1 = Rails.cache.read 'cache_key'
value2 = Rails.cache.read({key: 'sequence', owner: @user})
## ๊ฐ’์ด ์žˆ๋‹ค๋ฉด ๊ฐ’์„ return, ์—†๊ฑฐ๋‚˜ ๋งŒ๋ฃŒ๋ฌ๋‹ค๋ฉด nil์„ ๋ฆฌํ„ด

# Write
Rails.cache.write 'cache_key', some_data
Rails.cache.write 'cache_key', some_data, expires_in: 3.hour

# Fetch
Rails.cache.fetch('cache_key', expires_in: 30.minutes) do
  # Code..
end

Active Record

Relationship

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Relation์€ ์•„๋ž˜์™€ ๊ฐ™์œผ๋ฉฐ ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ์„œ๋น„์Šค์˜ ๊ตฌ์„ฑ์— ๋”ฐ๋ผ ๋‹จ์ˆœํ•œ ๋ชจ๋ธ๋ถ€ํ„ฐ Polymorphic ๋“ฑ ๋ณต์žกํ•œ ํ˜•ํƒœ์˜ Relationship์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” Model์— ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ด€๋˜๋ฉฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์žˆ์–ด์„œ ํฐ ์ด์ ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

belongs_to              # 1:1 (ํ•œ ๋ชจ๋ธ์ด ๋‹ค๋ฅธ ๋ชจ๋ธ์— ์†ํ•  ๋•Œ)
has_one                 # 1:1 (ํ•œ ๋ชจ๋ธ์ด ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ)
has_many                # 1:N (ํ•œ ๋ชจ๋ธ์ด ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ์—ฌ๋Ÿฌ๊ฐœ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ)
has_many :through       # M:N (์ œ3์˜ ๋ชจ๋ธ๋กœ M:N์˜ ๊ฐ„์ ‘ ๊ด€๊ณ„๋ฅผ ๊ฐ€์งˆ ๋•Œ)
has_one :through        # 1:1 (์ œ3์˜ ๋ชจ๋ธ๋กœ ๊ฐ„์ ‘์  ๊ด€๊ณ„๋ฅผ ๊ฐ€์งˆ ๋•Œ)
has_and_belongs_to_many # M:N (M:N์˜ ๋ชจ๋ธ์ด ๊ด€๊ณ„๋ฅผ ๊ฐ€์งˆ ๋•Œ)

e.g

class Customer < ActiveRecord::Base
  has_many :orders
end

# Customer๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ Order๋ฅผ ๊ฐ€์ง

with scaffold

rails g scaffold Order data:string customer:references
# rails c
irb(main):001:0> Order.create data:"aa", customer_id:4

#<Comment:0x00000001095fbea0
 id: 2,
 data: "aa",
 customer_id: 4,
 created_at:
  Wed, 10 Aug 2022 14:35:07.140636000 UTC +00:00,
 updated_at:
  Wed, 10 Aug 2022 14:35:07.140636000 UTC +00:00>

๐Ÿ›ก Security

CORS

gem 'rack-cors'
# config/application.rb      
config.middleware.insert_before 0, Rack::Cors do
    allow do
    origins 'localhost:3000', '127.0.0.1:3000',
        /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
    resource '*', headers: :any, methods: [:get, :post, :options]
    end
end

Blocking & Throttling

https://github.com/rack/rack-attack

e.g

throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
  if req.path == '/login' && req.post?
    req.ip
  end
end

Security Guide

  • https://guides.rubyonrails.org/security.html
  • https://owasp.org/www-pdf-archive//Rails_Security_2.pdf

๐Ÿš‚ Middlewares

https://github.com/coopermaa/awesome-rack

๐Ÿ“š Articles

Rails ์ž‘์„ฑ ๊ธ€์€ ๊ฒ€์ƒ‰ ๋งํฌ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.

https://www.hahwul.com/search/?keyword=rails

๐Ÿ’ผ Resources

๐Ÿ“Œ References

  • https://guides.rubyonrails.org/getting_started.html
  • https://guides.rubyonrails.org/security.html
  • https://owasp.org/www-pdf-archive//Rails_Security_2.pdf