Rails 5 on aws c9

N10. post 이미지 업로드 Carrierwave 적용

컴퍼 2020. 7. 5. 16:51

파일업로드를 담당하는 carrierwave 세팅을 해보자.

이미지를 업로드해 아바타 이미지 추가, 포스트 이미지 추가가 가능하다.

gemfile에

gem 'carrierwave'

추가

콘솔에

ubuntu:~/environment/noar (master) $ bundle

The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using rake 13.0.1
Using concurrent-ruby 1.1.6
Using i18n 1.8.3
Using minitest 5.14.1
Using thread_safe 0.3.6
Using tzinfo 1.2.7
Using activesupport 5.0.7.2
Using builder 3.2.4
Using erubis 2.7.0
Using mini_portile2 2.4.0
Using nokogiri 1.10.9
Using rails-dom-testing 2.0.3
Using crass 1.0.6
Using loofah 2.5.0
Using rails-html-sanitizer 1.3.0
Using actionview 5.0.7.2
Using rack 2.2.2
Using rack-test 0.6.3
Using actionpack 5.0.7.2
Using nio4r 2.5.2
Using websocket-extensions 0.1.5
Using websocket-driver 0.6.5
Using actioncable 5.0.7.2
Using globalid 0.4.2
Using activejob 5.0.7.2
Using mini_mime 1.0.2
Using mail 2.7.1
Using actionmailer 5.0.7.2
Using activemodel 5.0.7.2
Using arel 7.1.4
Using activerecord 5.0.7.2
Fetching public_suffix 4.0.5
Installing public_suffix 4.0.5
Fetching addressable 2.7.0
Installing addressable 2.7.0
Using execjs 2.7.0
Using autoprefixer-rails 9.7.6
Using bcrypt 3.1.13
Using bindex 0.8.1
Using bundler 1.17.2
Using byebug 11.1.3
Fetching mini_magick 4.10.1
Installing mini_magick 4.10.1
Using ffi 1.13.1
Fetching ruby-vips 2.0.17
Installing ruby-vips 2.0.17
Fetching image_processing 1.11.0
Installing image_processing 1.11.0
Fetching mimemagic 0.3.5
Installing mimemagic 0.3.5
Fetching carrierwave 2.1.0
Installing carrierwave 2.1.0
Using coffee-script-source 1.12.2
Using coffee-script 2.4.1
Using method_source 1.0.0
Using thor 1.0.1
Using railties 5.0.7.2
Using coffee-rails 4.2.2
Using orm_adapter 0.5.0
Using responders 3.0.1
Using warden 1.2.8
Using devise 4.7.2 from https://github.com/plataformatec/devise.git (at master@0e33f55)
Using jbuilder 2.10.0
Using jquery-rails 4.4.0
Using rb-fsevent 0.10.4
Using rb-inotify 0.10.1
Using listen 3.0.8
Using materialize-sass 1.0.0
Using puma 3.12.6
Using sprockets 3.7.2
Using sprockets-rails 3.2.1
Using rails 5.0.7.2
Using sass-listen 4.0.0
Using sass 3.7.4
Using tilt 2.0.10
Using sass-rails 5.0.7
Using spring 2.1.0
Using spring-watcher-listen 2.0.1
Using sqlite3 1.3.13
Using turbolinks-source 5.2.0
Using turbolinks 5.2.1
Using uglifier 4.2.0
Using web-console 3.7.0
Bundle complete! 18 Gemfile dependencies, 76 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

carrierwave는 uploader라는걸로 파일 업로드를 관리한다.

한 종류의 파일에 대해서 하나의 업로더를 만드는게 기본규칙이다.

그래서 우리는 유저모델에 쓸 아바타 이미지를 업로드하는 업로더를 하나 만들거고

포스트에 이미지를 추가하는 업로더를 하나 만들거다

서로 다로 만드는 이유는, 아바타의 경우 썸네일을 생성할 것이기 때문이다.

ubuntu:~/environment/noar (master) $ rails g uploader Avatar
Running via Spring preloader in process 2893
      create  app/uploaders/avatar_uploader.rb

ubuntu:~/environment/noar (master) $ rails g uploader PostImage
Running via Spring preloader in process 2921
      create  app/uploaders/post_image_uploader.rb

업로더가 잘 추가됐다.

이제 유저의 테이블과 포스트 테이블에 아바타와 포스트이미지를 올릴 수 있는 컬럼을 하나 추가해야한다.

스트링타입으로 추가하면됨

이를 마이그래이션 파일 하나로 추가해보도록 하자

ubuntu:~/environment/noar (master) $ rails g migration AddImageToUsersAndPosts
Running via Spring preloader in process 3024
      invoke  active_record
      create    db/migrate/20200705042853_add_image_to_users_and_posts.rb
class AddImageToUsersAndPosts < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :avatar, :string
    add_column :posts, :image, :string
  end
end

파일들은 project_name/public 에 보관될 것이기 때문에 그 주소만 저장하면 된다.(carrierwave의 uploader가 알아서 해준다.)

ubuntu:~/environment/noar (master) $ rake db:migrate
== 20200705042853 AddImageToUsersAndPosts: migrating ==========================
-- add_column(:users, :avatar, :string)
   -> 0.0055s
-- add_column(:posts, :image, :string)
   -> 0.0003s
== 20200705042853 AddImageToUsersAndPosts: migrated (0.0078s) =================

mount_uploader :avatar, AvatarUploader

mount_uploader :image, PostImageUploader 추가

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :posts
  has_many :liked

  # 1:N 관계인 likes를 이용해서 Post와 N:N 관계를 구현 
  # 특정 유저가 좋아요를 누른 posts를 얻을 수 있음. 
  # ex) user.likes_posts

  #유저입장에서 좋아하는 글을 가져오는데,
  #가져올 때 Likes를 토대로 찾게되고,
  #그 속에서 post만 묶어서 보여준다.(그 출처는 post다)
  has_many :liked_posts, through: :likes, source: :post
  has_many :comments
  mount_uploader :avatar, AvatarUploader

  #User 모델의 한 객체에 대해 특정 글에 대한 좋아요 유무를 확인
  def is_like?(post) 
    Like.find_by(user_id: self.id, post_id: post.id).present? 
  end
end
class Post < ApplicationRecord
    belongs_to :user
    has_many :likes

    # 1:N 관계인 likes를 이용해서 User와 N:N 관계를 구현 
    # 특정 글에 좋아요를 누른 users를 얻을 수 있음. 
    # ex) post.liked_users
    has_many :liked_users, through: :likes, source: :user
    has_many :comments
    mount_uploader :image, PostImageUploader
end

이것으로 파일을 업로드 받을 수 있는 상태가 되었다.

이제 컨트롤러와 뷰를 꾸며서 이미지 업로드를 하고 사이즈 조절을 하도록 하자.

먼저 포스트에 이미지를 업로드해보자

views/posts/new.html.erb

<div class="container">
    <div class="row">
        <div class="card col s12">
            <div class="card-content">
                <div class="card-title">
                    새 글 작성하기
                </div>
                <!--이미지를 추가하기 위해 멀티파트 적용-->
                <%= form_tag posts_path, multipart :ture do %>
                <div class="imput-field">
                    <%= file_field_tag :image %>
                </div>

                <div class="input-field">
                    <%=text_area_tag :content, nil, class: "materialize-textarea", placeholder: "무슨 생각을 하고 있나요?" %>
                </div>
                <div class="input-field">
                    <%= button_tag "작성하기", class: "btn" %>
                </div>
                <% end %>
            </div>
        </div>
    </div>
</div>

controllers/post_controller.rb

class PostsController < ApplicationController
  before_action :authenticate_user!
  before_action :check_ownership, only: [:edit, :update, :destroy]
  respond_to :html, :json

  ...

  def create
    #new_post라는 임시변수를 만들고
    #Post 모델에서 새로운 객체를 생성해서
    #user_id: 는 현재 로그인한 사람의 id를 넣고
    #내용은 파리미터로 온 content를 넣어서 저장해주겠다.
    #views/posts/new.html.erb에서 넘어온 image를 carrierwave가 처리
    new_post = Post.new(user_id: current_user.id,
                        content: params[:content],
                        image: params[:image])
    if new_post.save
      redirect_to root_path
    else
      redirect_to new_post_path
    end
  end
  ...

end

public폴더에 잘 올라간걸 볼 수 있다.

이제 이를 view에 표현해보도록 하자

<div class="card">

    <div class="card-content">
        <!-- 해당포스트의 작성자의 이름을 출력 -->
        <span class="card-title"><%= post.user.name %></span>
        <span><%=post.created_at %></span>

        <div class="card-image">
            <% if post.image.url %>
                <img src="<%= post.image.url %>">
            <% end %>
        </div>

        <p class="pre-line"><%= post.content %></p>
    </div>

    ...

</div>

 

이미지를 원본으로 올리면 트래픽이 너무 많이 발생할 수 있다.

이를 해결하기위해 이미지프로세싱을 해주는 mini_magick 를 사용하자

gemfile에 gem 'mini_magick'추가

bundle 명령어 실행
class PostImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

 ...

  # Process files as they are uploaded:
  # process scale: [200, 300]
  process :resize_to_limit => [1024, 768]

    ...
end

c9에 imagemagick 설치

ubuntu:~/environment $ sudo apt-get install imagemagick

Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  fontconfig fonts-droid-fallback fonts-noto-mono ghostscript
  gsfonts hicolor-icon-theme imagemagick-6-common
  imagemagick-6.q16 libcairo2 libcupsfilters1 libcupsimage2
  libdatrie1 libdjvulibre-text libdjvulibre21 libfftw3-double3
  libgraphite2-3 libgs9 libgs9-common libharfbuzz0b libijs-0.35
  libilmbase12 libjbig0 libjbig2dec0 liblqr-1-0
  libmagickcore-6.q16-3 libmagickcore-6.q16-3-extra
  libmagickwand-6.q16-3 libnetpbm10 libopenexr22 libpango-1.0-0
  libpangocairo-1.0-0 libpangoft2-1.0-0 libpaper-utils
  libpaper1 libpixman-1-0 libthai-data libthai0 libtiff5
  libwmf0.2-7 libxcb-render0 libxcb-shm0 netpbm poppler-data
...
Setting up imagemagick (8:6.9.7.4+dfsg-16ubuntu6.8) ...
Setting up libpangocairo-1.0-0:amd64 (1.40.14-1ubuntu0.1) ...
Setting up libmagickcore-6.q16-3-extra:amd64 (8:6.9.7.4+dfsg-16ubuntu6.8) ...
Processing triggers for mime-support (3.60ubuntu1) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...

이렇게 하고 이번엔 큰 사이즈의 이미지를 올려보겠다.

업로드된 이미지를 다운받아보면 잘 리사이징 된걸 확인할 수 있다. (intrinsic : 1024 x 576 pixels)