개발일지/광고데이터 연결 PJT - 차콜진센

20201228_시스템 구조 - 메인스크립트 구조 및 환경설정

parksisi 2021. 1. 9. 18:39

시스템의 작동원리는 단순하다. 

 

ruby main.rb

 

위의 커맨드를 통해 매시간 main.rb라는 루비 스크립트를 실행시키는 것이다.

main.rb의 대략적인 구조를 살펴보자면 아래와 같다. 

 

# main.rb

require 'capybara'
require 'active_record'
require 'selenium-webdriver'
require 'google_drive'
...

project_root = File.dirname(File.absolute_path(__FILE__))
config_database_yml_file = File.join(project_root, 'config', 'database.yml')
config_database_yml = YAML::load(ERB.new(File.read(config_database_yml_file)).result)

Time.zone = 'Tokyo'
ActiveRecord::Base.establish_connection(config_database_yml['development'])
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.logger = Logger.new(STDOUT)

Dir.glob(project_root + '/app/models/*.rb').each { |f| require f }
Dir.glob(project_root + '/lib/*.rb').each { |f| require f }

def update_dtoc(date)
...
end

def retrieve_dtoc_data(date)
...
end

if $0 == __FILE__
  begin
    start_date = Date.yesterday
    end_date   = Date.today
    
    (start_date..end_date).each do |date|
      retrieve_dtoc_data(date)
      update_dtoc(date)
    end
  rescue => e
    puts "#{e}: #{e.message}\n" + e.backtrace.join("\n")
  end
end

 

이게 뭔가 싶을 수도 있다. 차근차근 설명해보겠다.

 

 

if $0 == __FILE__

 

핵심은 if $0 == __FILE__이다. 

$0은 현재 실행되고 있는 파일을 뜻하고 __FILE__은 현재의 파일명이다. 즉 지금 실행되고 있는 파일이 현재 파일일 경우에만 아래 코드를 실행시키는 것이다. 

예를 들어보자.

 

# hello_ruby.rb

puts "hello ruby"

 

커맨드 창에서 ruby hello_ruby.rb 를 실행시킨다면 당연하게도 "hello ruby"라는 값이 출력된다. 

 

# hello_code.rb

require_relative "hello_code"
puts "hello code"

 

위의 파일을 실행시킬 경우 역시나 당연히

"hello ruby"

"hello code"

이렇게 두 가지 값이 출력되게 된다. 

 

그렇다면, hello_ruby.rb에 아래와 같은 코드를 추가 후 ruby hello_code.rb 를 실행시킨다면 어떤 값을 출력하게 될까?

 

# hello_ruby.rb

if $0 == __FILE__
  puts "hello ruby"
end

 

출력되는 값은

"hello code" 

하나이다. 

 

위에서 말했듯이 if $0 == __FILE__는 지금 실행되는 파일(위의 예에선 ruby hello_code.rb라는 커맨드를 통해 실행시킨 hello_code.rb이다)이 현재의 파일명과 같을 경우에만 실행시키라는 의미이다. __FILE__가 기재되어있는 파일의 파일명은 hello_ruby.rb이다. 즉

$0 = 현재 실행되고 있는 파일 = hello_code.rb

__FILE__ = 현재의 파일명(혹은 파일 패스) = hello_ruby.rb

이므로 "hello ruby"는 출력되지 않게 된다.

 

ruby는 스크립트언어이기에 JAVA나 C와 같이 main 메서드가 없다. 그렇기에 위의 조건문과 같이 세팅하여 마치 main 스크립트처럼 작동하게 할 수 있다.

이번 프로젝트에서도 main.rb라는 파일이 직접 실행될 경우에만 해당 조건문 아래의 코드들이 실행되도록 설정하였다.

(하지만 사실 main.rb 파일이 다른 곳에서 호출되어 실행될 확률은 극히 적다. 그럼에도 이러한 방식으로 작성한 이유는 이것이 일반적인 루비 스크립트 작성 패턴이기 때문이다)

 

 

Active Record

 

데이터베이스 모델에 쉽게 접근하기 위해 active record를 사용했다. 기존 rails에서는 이미 설정되어있기에 신경 안 써도 되는 부분이었지만, 루비 스크립트 파일에서 active record를 사용하기 위해선 몇 가지 세팅이 필요했다.

 

기본적인 사용법은 사실 간단하다. 아래 메소드로 database connection 설정만 해주면 끝이다. 

 

ActiveRecord::Base.establish_connection(...)

 

 

(...) 부분에는 database.yaml파일이 들어가면 된다. 이를 위해 아래와 같이 추가적인 세팅이 필요했다.

 

project_root = File.dirname(File.absolute_path(__FILE__))
config_database_yml_file = File.join(project_root, 'config', 'database.yml')
config_database_yml = YAML::load(ERB.new(File.read(config_database_yml_file)).result)

 

1. project_root

main.rb 파일이 들어있는 폴더를 찾아 project_root 변수에 담는다. dirname 은 파일을 파라미터로 받아 해당 파일의 폴더경로를 출력해주는 메서드이며, absolute_path는 파라미터로 넘겨진 파일의 절대 경로를 출력해준다. 

 

2. config_database_yml_file

File 클래스의 join 메소드를 사용하여 database.yml 파일의 경로를 변수에 담는다.

 

3. config_database_yml

database.yml 파일은 아래와 같다.

 

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host:  <%= ENV['DB_HOST'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>

development:
  <<: *default
  database: exportame_v2020_development

 

보시다시피 erb코드가 섞여 들어가있다. 그냥 간단히 database.yml파일을 로드할 수 없던 이유다. 임베디드 루비 코드가 작동하게 하기 위해서 ERB클래스를 통해 불러와야 했다. 

 

이러한 3가지 과정을 거쳐 마침내 야믈파일 매개변수를 만들었다. 아까 위에서 말했던 ActiveRecord의 establish_connection에 해당 야믈파일을 넘겨주면 rubyscript에서의 active record설정은 마무리된다.

 

 

require files

 

main.rb 외의 경로에 있는 파일들을 사용하기위해선 require로 불러와야 한다. 

model 폴더에 있는 모델파일들, lib 폴더에 있는 crawler 파일, spread sheet관련 파일을 아래와 같이 불러오고 있다. 

 

Dir.glob(project_root + '/app/models/*.rb').each { |f| require f }
Dir.glob(project_root + '/lib/*.rb').each { |f| require f }

 

Dir.glob()는 주어진 폴더 내에서 원하는 패턴의 파일만 골라내는 메서드이다. 위의 예에서는 models과 lib 폴더 밑에 있는 rb로 끝나는 모든 파일들을 골라내어 require하고 있음을 알 수 있다. 

 


이번 글을 통해 main.rb의 대략적인 구조와 필요한 환경설정에 대해 설명해 보았다. 

다음 편에서는 본격적으로 핵심 메소드들에 대해 설명해 보겠다.

 

 

참고 :

www.devdungeon.com/content/ruby-activerecord-without-rails-tutorial

www.thoughtco.com/using-glob-with-directories-2907832

abh0518.net/tok/?p=144

stackoverflow.com/questions/4687680/what-does-if-file-0-mean-in-ruby