똑같은 삽질은 2번 하지 말자

Ruby on Rails CRUD ( + Vue) 본문

Ruby On Rails

Ruby on Rails CRUD ( + Vue)

곽빵 2021. 9. 12. 15:07

개요

Mysql + Rails + Vue + REST API로  CRUD를 구현해 보자.

Rails를 학습하기 위한 글이므로 딱히 Vue쪽 구현에 대해서는 거의 적지 않을꺼 같다.

 

구현순서

1. Front

  1. UI

  2. API Request

 

2. Back

  1. Model (처음에 한번하면 끝이라 Create에서 하고 나면 그 뒤로는 필요없다.)

  2. Routes

  3. Controller

  4. Service

  5. return json(아마 Read부분만 ? )

Create

대충 구성한 UI

1. Model

DB와 연결하기 위한 Model를 만들어주어야한다.

 

app/models/product.rb (rails 단수 복수 주의)

class Product < ApplicationRecord
end

위의 Product는 밑의 ApplicationRecord를 상속받는데 기본이 ActiveRecord이다

ActiveRecord에 아주 많은 기능들이 탑재되어있다는 걸 인지해두자.

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

https://railsguides.jp/active_record_querying.html

 

Active Record クエリインターフェイス - Railsガイド

Active Recordが提供するすべてのデータベースクエリインターフェイスについて解説します。

railsguides.jp

2. Routes

앞서 개요에서도 말했듯이 이번에 REST API를 이용해 CRUD를 만드는게 목적이므로

Rails에 내장되어있는 REST API를 쉽게 routes 설정할 수 있는 resources가 있다.( + json으로 반환 나중에 jbuilder라는 잼이 필요)

Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  namespace :api, { format: 'json' } do
    resources :products, only: %i[create]
  end
end

이렇게 하면 

POST:: /api/products => api/products create method에 연결된다.

 

3. Controller

api/products의 create method까지 연결하는게 완성했으므로 계속해서 컨트롤러를 만들어주자

컨트롤러의 역할은 받은 request를 비지니스 로직을 가진 service에 연결시켜주는 역할이다.

 

controllers/api/products_controller.rb (이름 주의)

module Api
  class ProductsController < ApplicationController
    def create
      service = Api::Products::CreateService.new(params)
      service.execute
    end
  end
end

 

 

4. Service

실제로 Model을 이용해 디비에 저장하며, 다양한 비지니스 로직을 수행하는 역할을 하는 Service단을 만들어보자.

(rails는 디렉토리명, 함수명, 파일명에 규칙이 있으므로 가이드북을 참고해 주의해서 작성하길!!)

 

service/api/products/create_service.rb

module Api
  module Products
    class CreateService
      def initialize(params)
        @params = params
      end

      def execute
        add_product
      end

      private

      attr_reader :params
      def add_product
        image_url = upload_image
        ApplicationRecord.transaction do
          product = Product.new(
            pr_us_id: 1,
            pr_ca_id: 1,
            pr_br_id: params[:productBrand],
            pr_name: params[:productName],
            pr_price: params[:productPrice],
            pr_barcode: params[:productBarcode],
            pr_expiration: params[:productExpiration],
            pr_img: image_url
          )
          product.save!
        end
      rescue StandardError => e
        Rails.logger.error(e.message)
        Rails.logger.error(e.backtrace.join("\n"))
      end

      def upload_image
        image_url = params[:productImage].present? ? "public/product_images/#{params[:productImage].original_filename}" : ''
        File.binwrite(image_url, params[:productImage].read) if image_url.present?
        image_url
      end
    end
  end
end

트랜잭션, 이미지 작성 처리도 포함되어있다. (이미지 넣는게 이렇게 간단하게 되는거에 놀랬다.)

주의해야 할 점은 save!로 해야 트랜잭션 처리가 되는것, 루비라는 언어특성상 마지막 줄에 적은 친구가 return된다는것

 

5. return json(아마 Read부분만 ? )

필요없으므로 생략, 예외처리를 하게되도 아마 HttpResponse의 형태로 반환할 것이기때문에 전혀 필요없을듯 하다.

 

Read(select all)

1. Model  -> 생략(create에서 했으므로)

2. Routes

read를 위한 index를 추가

Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  namespace :api, { format: 'json' } do
    resources :products, only: %i[index create]
  end
end

index로 잘 연결되었다.

3. Controller

controllers/api/product_controller.rb

module Api
  class ProductsController < ApplicationController
    def index
      service = Api::Products::IndexService.new
      @result = service.execute
    end

    def create
      service = Api::Products::CreateService.new(params)
      service.execute
    end
  end
end

@result를 리턴해주는 컨트롤러를 작성

 

4. Service

service/api/products/index_service.rb

module Api
  module Products
    class IndexService
      def initialize; end

      def execute
        get_product_list
      end

      private

      def get_product_list
        @result = Product.all
      end
    end
  end
end

Product라는 모델을 이용해 전체 상품리스트를 리턴해준다.

 

5. return json(아마 Read부분만 ? )

views/api/products/index.json.jbuilder

json.results do
  json.array!(@result) do |product|
    json.pr_id product[:pr_id]
    json.pr_ca_id product[:pr_ca_id]
    json.pr_br_id product[:pr_br_id]
    json.pr_us_id product[:pr_us_id]
    json.pr_price product[:pr_price]
    json.pr_barcode product[:pr_barcode]
    json.pr_img product[:pr_img]
    json.pr_expiration product[:pr_expiration]
    json.pr_name product[:pr_name]
  end
end

views안에 api/products/index 와 controller의 api/products_controller의 index는 자동으로 바인딩된다.

단, 역시 이름을 주의하자.

그리고 controller와 공유하는 @result 변수도 주의 레일즈안에서 @는 인스턴스 변수이며 클래스 내의 모든 메서드에서 사용가능

하다는 의미인데 jbuilder에서도 공유가 되는? 것 같다..

 

Update

 

Destroy

Comments