티스토리 뷰

루비 언어도 모르고, RoR은 당연히 모르고, 그 외 사용하는 프레임워크들을 전부다 모르는 상태에서 이걸 하려고 하니 정말 삽질을 많이 했다.

사실 코드 자체는 정말 심플한데 구조를 잘 모르다보니 뭐가 어떻게 돌아가는지 몰라서 더 삽질을 심하게 했던 것 같다.


일단 환경은 다음과 같다.

RoR로 API 서버가 돌아간다. URL 라우팅 같은건 RoR을 쓰는게 아니라 Grape라는 semi-framework를 사용한다. 파일을 저장할 때 로컬에 저장하는게 아니라 AWS S3에 저장하려고 한다.


0. 라이브러리 추가

# Image Upload

gem 'paperclip', '~> 4.2'

# AWS

gem 'aws-sdk'

Gemfile에 위 내용을 추가하고 'bundle install'을 해준다.

paperclip 버전은 RoR 버전에 따라서 조율을 해줘야 한다. github에 있는 paperclip repository에서 버전 설정에 대해 참고하면 된다.


1. 모델 생성

paperclip을 이용해서 파일을 저장하기 위해서는 일단 Model 클래스가 필요하다. 이 필드명에 의해서 자동으로 DB에 row를 추가하거나 조회하게 된다. 

has_attached_file :{{컬럼 이름의 접두사}}

이런 식이다. 따라서 테이블에 컬럼이 다음과 같이 정의되어 있다면...




아래와 같이 모델을 생성해주면 된다.

class Attachment < ActiveRecord::Base

  has_attached_file :attached

  validates_attachment_content_type :attached, :content_type => /\Aimage\/.*\Z/

end


첨부파일의 포맷 같은걸 validator를 통해서 결정하는데, 컬럼 이름이 반드시 매치되어야 한다. rails의 migrate 기능을 이용해서 컬럼을 자동으로 생성되도록 해도 된다. 다음을 참고하라. 그리고 모델 인스턴스를 생성할 때 에러가 나는 경우가 있는데, 이럴 때는 컬럼의 옵션(맞는 표현인지 모르겠다)을 확인해보자. 나 같은 경우에는 테이블 생성 자체는 SQLAlchemy를 통해서 하다보니 문제가 발생했다. Table Inspector는 다음과 같으면 일단 에러는 안 난다. 괜히 Not Null 옵션 줬다가 삽질했다....





2. S3와 연동을 위한 설정 추가

나중에 paperclip 모델을 생성하고 save를 할 때 S3에 저장되도록 설정을 해줘야 한다. config/environments 폴더 안에 있는 적절한 루비 파일에서 다음과 같은 config 코드를 추가한다.

config.paperclip_defaults = {

    :storage => :s3,

    :s3_credentials => "#{Rails.root}/config/amazon_s3.yml",

    :bucket => 'ssuplydepot',

    :path => ":attachment/:id/:style/:filename"

  }

paperclip에 저장할 storage는 s3이고, credential 파일 경로, bucket 이름, 저장될 경로는 어떻게 하겠다는 설정이다. 


당연히 이제 amazon_s3.yml이 필요하다. 이건 S3에 접근하기 위해 필요한건데, 발급받았던 Access Key ID, Secret Access Key 등을 입력해두는거다. 만약 발급받은 적이 없다면 여기에서 발급 받아야 한다. 파일은 위에서 config.paperclip_defaults.s3_credentials에서 지정한 경로에 저장한다.(rails 폴더의 config 폴더에 저장) 그리고 region을 잘 적어줘야 한다. region을 Tokyo로 설정했었으면 아래와 같이 하면 되지만, 만약 다른 region일 경우 수정해줘야 한다. 각 region별 string 값을 어떻게 해야 하는지는 여기를 참고.


development: &default

  access_key_id: {{발급 받은 AWSAccessKeyId}}

  secret_access_key: {{발급 받은 AWSSecretKey}}

  region: ap-northeast-1


production:

  <<: *default


test:

  <<: *default



3. API 만들기

API를 만든다. 특이한 점이 있다면 RoR 위에 Grape를 올려서 쓰는거라서 기존 paperclip에 있는 가이드와는 조금 다르다는 것이다.

일단 흐름을 설명하자면 다음과 같다.


(1) content_type 필터링을 해제한다. content_type :txt, 'text/plain' 이렇게 하면 된다. 이 부분은 나도 잘 모르겠는데 이렇게 하면 일단 된다. 테스트를 할 때는 paw라는 HTTP 클라이언트로 했었는데, 크게 중요하진 않고 혹시 나중에 content_type 필터링에 의해서 파라미터가 제대로 오지 않는 경우에는 이 부분을 조율해봐야 할 것이다.


(2) post 통신을 통해 전달 받을 파라미터를 설정한다. 여기서 파일을 전달받을 파라미터명을 설정한다. 다음과 같은 식이다.

params do

        requires :attached, :type => Rack::Multipart::UploadedFile, :desc => "attached file"

end


이렇게 파라미터를 설정하면 params[:attached] 라고 접근할 경우 Rack::Multipart::UploadedFile 타입의 변수를 얻게 된다. 


(3) 파일 생성

파라미터로 들어온 데이터를 통해서 파일을 생성한다. 

received_file = ActionDispatch::Http::UploadedFile.new(params[:attached])


이렇게 하면 파일이 생성된다. 이 변수가 어떻게 구성되어 있는지 보려면 pp 라이브러리를 설치하고 pp received_file 와 같은 코드를 추가해서 보면 된다. 일단 대표적으로 received_file.tempfile으로 접근하면 파일 자체를 얻을 수 있고, received_file.original_filename으로 접근하면 첨부된 파일의 원래 파일 이름을 얻을 수 있다.


(4) paperclip 모델 인스턴스 생성

여기서부터는 거의 비슷하다. 단지 기존 RoR에서 넣어주던 방식과 다르게 위에서 생성한 파일을 넣어준다는 것에 차이가 있다.

attachment = Attachment.create()

attachment.attached = received_file

attachment.save!


attached라는 변수명은 위에서 모델 클래스를 생성할 때 썼던 변수명과 일치해야 한다. 그리고 save!를 하면 이제 드디어 DB에 해당 파일의 정보가 추가되고, 스토리지에도 저장이 된다.

스토리지라는 표현을 쓴 이유 - 원래는 로컬에 저장된다. 로컬 스토리지도 스토리지니까.... 근데 우리는 위에서 기본적으로 S3에 저장되도록 했다. 그러니까 저 save라는 메소드를 호출하면 S3에 전송을 하게 된다는 것이다. 로컬에 저장될 때는 RoR 프로젝트 루트 폴더에서 public/system/{{모델 클래스이름}} 폴더 안에 primary key 기준으로 저장된다. 스타일 등에 관련된 설정도 있어서 꽤나 복잡한 구조다. 물론 이 경로 구조 자체는 S3에 저장될 때도 마찬가지다.



이렇게 하면 목적했던 S3에 파일을 저장시키는 것이 완료된다. 그런데 파일을 전송 받으면서 temp 폴더에 파일이 계속 생성된다. 나 같은 경우에는 '/var/folders/qm/zr1gk76952bb312jk3vw6t300000gn/T/' 라는 굉장히 temp 파일을 위한 경로 같은 곳이었다. 검색하다보니까 cron으로 여기를 주기적으로 정리해줘야 하는 듯 하다.


혹시 저장 경로를 커스터마이징 하는 부분이 관심있는 분은 이 링크를 참고

http://stackoverflow.com/questions/27669413/paperclip-dynamic-use-of-path




댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday