Using Terraform Backend With S3

configuration.tf 파일 생성

1
2
3
4
5
6
7
8
9
terraform {
  # terraform 버전
  required_version = "= 0.9.5"
}

provider "aws" {
  # aws 도쿄 리전
  region  = "ap-northeast-1"
}

configuration.tf 파일을 생성해서 terraform 기본 버전 설정값과 provider 설정값을 위와 같이 추가 해준다.

Terraform Init 명령으로 초기화 하기

1
2
3
4
5
6
7
8
9
10
11
$ terraform init

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your environment. If you forget, other
commands will detect it and remind you to do so if necessary.

init 명령을 실행해서 디렉토리를 테라폼을 사용할 수 있도록 초기화 한다.

0.9.x 버전에서는 provide가 terraform 바이너리에 포함되어 있어서 0.10.x 버전과 init이 다르다.

현재 최신버전은 0.10.6 이다. 다음 포스트에서 backend를 사용하면서 버전 업데이트 하는걸 하기 위해서 지금은 0.9.x 버전 기준으로 작성했다.

Backend S3 관련 리소스 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# terraform remote state를 lock table 위한 dynamodb_table
resource "aws_dynamodb_table" "test_terraform_lock" {
  name           = "init-terraform-lock"
  read_capacity  = 2
  write_capacity = 2
  hash_key       = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

# terraform remote state를 위한 s3_bucket
resource "aws_s3_bucket" "test_terraform_state" {
  bucket = "test-terraform-state"
  acl    = "private"

  versioning {
    enabled = true
  }
}

위와 같이 remote.tf을 생성한다.

aws_dynamodb_tabl 리소스는 table lock을 위해서 추가하고 aws_s3_bucke 리소스는 tfstate파일 저장을 위해서 추가 한다.

dynamodb 테이블의 primary key 이름은 LockID로 해야 한다. dynamodb_table

Terrafom Plan 명령으로 변경되는 리소스 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_dynamodb_table.init_terraform_lock
    arn:                       "<computed>"
    attribute.#:               "1"
    attribute.2068930648.name: "LockID"
    attribute.2068930648.type: "S"
    hash_key:                  "LockID"
    name:                      "init-terraform-lock"
    read_capacity:             "2"
    stream_arn:                "<computed>"
    stream_enabled:            "<computed>"
    stream_view_type:          "<computed>"
    write_capacity:            "2"

+ aws_s3_bucket.init_terraform_state
    acceleration_status:     "<computed>"
    acl:                     "private"
    arn:                     "<computed>"
    bucket:                  "init-terraform-state"
    bucket_domain_name:      "<computed>"
    force_destroy:           "false"
    hosted_zone_id:          "<computed>"
    region:                  "<computed>"
    request_payer:           "<computed>"
    versioning.#:            "1"
    versioning.0.enabled:    "true"
    versioning.0.mfa_delete: "false"
    website_domain:          "<computed>"
    website_endpoint:        "<computed>"

Plan: 2 to add, 0 to change, 0 to destroy.

리소스를 실제로 추가 하기전에 plan 명령으로 어떤것들이 변화가 있는지 확인한다.

위와 같이 aws_dynamodb_table, aws_s3_bucke 리소스가 추가 되는걸 확인할 수 있다.

Terrafom apply 명령으로 리소스 AWS에 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ terraform apply
aws_s3_bucket.init_terraform_state: Creating...
  acceleration_status:     "" => "<computed>"
  acl:                     "" => "private"
  arn:                     "" => "<computed>"
  bucket:                  "" => "init-terraform-state"
  bucket_domain_name:      "" => "<computed>"
  force_destroy:           "" => "false"
  hosted_zone_id:          "" => "<computed>"
  region:                  "" => "<computed>"
  request_payer:           "" => "<computed>"
  versioning.#:            "" => "1"
  versioning.0.enabled:    "" => "true"
  versioning.0.mfa_delete: "" => "false"
  website_domain:          "" => "<computed>"
  website_endpoint:        "" => "<computed>"
aws_dynamodb_table.init_terraform_lock: Creating...
  arn:                       "" => "<computed>"
  attribute.#:               "" => "1"
  attribute.2068930648.name: "" => "LockID"
  attribute.2068930648.type: "" => "S"
  hash_key:                  "" => "LockID"
  name:                      "" => "init-terraform-lock"
  read_capacity:             "" => "2"
  stream_arn:                "" => "<computed>"
  stream_enabled:            "" => "<computed>"
  stream_view_type:          "" => "<computed>"
  write_capacity:            "" => "2"
aws_dynamodb_table.init_terraform_lock: Creation complete (ID: init-terraform-lock)
aws_s3_bucket.init_terraform_state: Creation complete (ID: init-terraform-state)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path:

apply 명령으로 aws에 적용을 한다.

apply 하고 나면 dynamodb 와 s3 bucket이 생성된걸 확인 할 수 있다. 이제 backend s3를 쓰기 위한 준비는 끝났다.

configuration.tf 파일 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
terraform {
  required_version = "= 0.9.5"

  # 아래 부분 추가
  backend "s3" {
    # s3 bucket 이름
    bucket     = "test-terraform-state"
    # 실제 저장될 파일 이름
    key        = "terraform.tfstate"
    # s3 bucket 리전
    region     = "ap-northeast-1"
    # s3 서버 암화호 사용 여부
    encrypt    = true
    # dynamodb table 이름
    lock_table = "test-terraform-lock"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

위와 같이 backend s3로 추가해 준다.

Configuration variables Backend Type: s3 – Terraform by HashiCorp

서버 측 암호화는 저장된 데이터를 보호하기 위한 것입니다. Amazon S3가 관리하는 암호화 키(SSE-S3)를 사용하는 서버 측 암호화는 강력한 멀티 팩터 암호화를 제공합니다. Amazon S3는 각 객체를 고유한 키로 암호화합니다. 또한 추가 보안 조치로 주기적으로 바뀌는 마스터 키를 사용하여 키 자체를 암호화합니다. Amazon S3 서버 측 암호화는 가장 강력한 블록 암호 중 하나인 256비트 Advanced Encryption Standard(AES-256)를 사용하여 데이터를 암호화합니다.

Terraform Plan 명령으로 확인하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ terraform plan
Backend reinitialization required. Please run "terraform init".
Reason: Initial configuration of the requested backend "s3"

The "backend" is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you're using is using a custom configuration for
the Terraform backend.

Changes to backend configurations require reinitialization. This allows
Terraform to setup the new configuration, copy existing state, etc. This is
only done during "terraform init". Please run that command now then try again.

If the change reason above is incorrect, please verify your configuration
hasn't changed and try again. At this point, no changes to your existing
configuration or state have been made.

Failed to load backend: Initialization required. Please see the error message above.

plan을 하게 되면 위와 같이 backend 가 변경되어서 init을 다시 하라고 나온다.

Terraform Init 명령으로 초기화 다시하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ terraform init
Initializing the backend...
Do you want to copy state from "local" to "s3"?
  Pre-existing state was found in "local" while migrating to "s3". No existing
  state was found in "s3". Do you want to copy the state from "local" to
  "s3"? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes

Releasing state lock. This may take a few moments...


Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your environment. If you forget, other
commands will detect it and remind you to do so if necessary.

위와 같이 init을 다시 하면 local에 있는 tfstate 파일을 s3로 복사 할지 물어본다. yes 하면 s3로 복사가 된다.

이제 terraform backend s3 설정이 끝났다.

Learning Phoenix Framework

Install Elixir

1
2
$ brew update
$ brew install elixir

Install Hex

Package manager for the Erlang ecosystem

Hex는 elixir, erlang 패키지를 관리해주는 시스템이다. node의 npm 이랑 같은거다.

1
$ mix local.hex

Install Phoenix Framework

phoenix를 설치 하기 위해서 아래와 같은 명령어를 입력한다.

1
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

Create Phoenix Project

  • phoenix는 brunch를 사용하고 있는거 같다. 사용하고 싶지 않으면 옵션으로 --no-brunch를 주면 된다.
  • phoenix는 ecto를 사용하고 있는거 같다. 사용하고 싶지 않으면 옵션으로 --no-ecto를 주면 된다.
  • phoenix는 PostgreSQL을 기본으로 사용하게 되어 있는데 MySQL을 사용하고 싶으면 --database mysql 옵션을 추가 해주면 된다.

ecto는 database관리, 쿼리를 위한 라이브러리이다.

1
$ mix phoenix.new project_name --no-brunch --database mysql

Modify database config

config/dev.exs 파일을 열어 보면 아래와 같이 설정이 되어 있는데 환경변수를 사용하기 위해서 다음과 같이 수정 해준다.

1
2
3
4
5
6
config :project_name, Project_name.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "",
  database: "hello_phoenix_dev"
  hostname: "localhost"

database 정보를 수정하기 위해서 config/dev.exs 파일에서 아래와 같은 부분을 수정 해준다.

1
2
3
4
5
6
7
# Configure your database
config :project_name, Project_name.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: System.get_env("DATABASE_USERNAME"),
  password: System.get_env("DATABASE_PASSWOR"),
  database: System.get_env("DATABASE_DB"),
  hostname: System.get_env("DATABASE_HOST"),

Create and migrate your database with

아래와 같은 명령어를 입력하면 database가 생성 되고 마이그레이션을 해준다.

1
$ mix ecto.create && mix ecto.migrate

Start Phoenix

아래와 같은 명령으로 phoenix를 서버를 실행하면 http://localhost:4000로 다음과 같은 화면을 볼 수 있다.

1
$ mix phoenix.server

"start phoenix server"

Run your app inside IEx

만약 IEx (Interactive Elixir)과 함께 서버를 실행하고 싶으면 아래와 같은 명령어로 실행하면 된다.

1
$ iex -S mix phoenix.server

Cocos2d-x 세로 모드

xcode 를 열어서 ios 디렉토리 안에 RootViewController.mm 파일을 열어서 아래 부분을 수정한다.

1
2
3
4
5
6
7
8
9
// 이부분을
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsLandscape( interfaceOrientation );
}

// 이렇게 수정
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsPortrait( interfaceOrientation );
}

자동회전없이 항상 세로화면으로 설정하고 싶으면 아래 부분도 수정한다.

1
2
3
4
5
6
7
8
9
// 이부분을
- (BOOL) shouldAutorotate {
    return YES;
}

// 이렇게 수정
- (BOOL) shouldAutorotate {
    return NO;
}

React 개발환경 설정

요즘 react에 흥미를 느끼면서 이거저거 해보고 공부를 하고 있다.

이전에는 프론트엔드 개발을 하면서 build 만 watch 같은 것들을 따로 설정하지 않고 작업을 했었는데

react를 공부하는 김에 겸사 겸사 공부를 하게 되었다.

기본 디렉토리 구조

디렉토리 구조는 아래와 같다.

src 에는 개발단계에서 파일들을 쪼개서 개발을 하고 webpack 으로 build 한 파일들을 build 디렉토리에 모아둔다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── build
│   ├── bundle.js
│   └── index.html
├── src
│   ├── css
│   │   └── style.css
│   ├── img
│   └── js
│       ├── components
│       └── app.js
├── preprocessor.json
├── package.json
├── gulpfile.js
└── webpack.config.js

webpack

webpack을 사용해서 파일들을 하나의 파일로 만들어준다.

webpack config 파일은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var path = require('path');
var webpack = require('webpack');
var config = {
  entry: {
    src: ['./src/js/app.js']
  },

  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },

  resolve: {
    extensions: ['', '.js', '.jsx']
  },

  module: {
    loaders: [
      { test: /\.js$/, loader: 'jsx-loader!babel-loader' },
      { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  }
};

module.exports = config;

gulp

gulp 를 사용해서 파일이 변경이 되면 빌드를 다시 하고 서버를 다시 시작한다.

gulpfile은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var gulp = require('gulp');
var connect = require('gulp-connect');
var webpack = require('gulp-webpack');
var webpackConfig = require('./webpack.config.js');

// Initialize watch tasks
gulp.task('watch', ['run'], function() {
  gulp.watch(['./src/**/*'], ['build']);
});

// Build files for distribution
gulp.task('build', function() {
  return gulp.src('./src/js/app.js')
      .pipe(webpack(webpackConfig))
      .pipe(gulp.dest('build/'))
      .pipe(connect.reload());
});

// Run example server
gulp.task('run', ['build'], function(){
  connect.server({
    root: './build',
    port: 8080,
    livereload: true
  });
});

gulp.task('default', ['watch']);

React-native 시작하기

설치

1
$ npm install -g react-native-cli

프로젝트 생성

1
$ react-native init testProject

프로젝트 실행

1
2
$ cd testProject
$ open testProject.xcodeproj

위와 같이 명령어를 치면 xcode 에서 testProject가 열립니다.

run 을 실행하면 build 가 되고 터미널 창이 하나 열리면서 서버가 실행이 된다.

그리고 iOS Simulator 이 실행되고 다음과 같은 화면을 확인 할 수 있다.

"react-native run screen shot"

만약 xcode 에서 run 해서 터미널창이 열리는게 싫으면 testProject 디렉토리에서 다음과 같은 명령어로 서버를 실행 할 수 있다.

1
2
3
4
5
$ react-native start

or

$ npm start

Install Elasticsearch

설치

source 설치

1
2
3
$ curl -L -O http://download.elasticsearch.org/PATH/TO/VERSION.zip
$ unzip elasticsearch-$VERSION.zip
$ cd elasticsearch-$VERSION

실행

1
2
3
4
5
6
7
8
9
10
11
12
$ ./bin/elasticsearch
[2014-09-22 23:11:58,615][INFO ][node                     ] [Bevatron] version[1.3.2], pid[62710], build[dee175d/2014-08-13T14:29:30Z]
[2014-09-22 23:11:58,616][INFO ][node                     ] [Bevatron] initializing ...
[2014-09-22 23:11:58,631][INFO ][plugins                  ] [Bevatron] loaded [marvel], sites [marvel, bigdesk, head]
[2014-09-22 23:12:01,453][INFO ][node                     ] [Bevatron] initialized
[2014-09-22 23:12:01,453][INFO ][node                     ] [Bevatron] starting ...
[2014-09-22 23:12:01,577][INFO ][transport                ] [Bevatron] bound_address {inet[/127.0.0.1:9300]}, publish_address {inet[/127.0.0.1:9300]}
[2014-09-22 23:12:01,613][INFO ][discovery                ] [Bevatron] elasticsearch_J2P/fhrTURb8SSywjSVwOj3H8g
[2014-09-22 23:12:04,631][INFO ][cluster.service          ] [Bevatron] new_master [Bevatron][fhrTURb8SSywjSVwOj3H8g][Jung-ui-MacBook-Pro.local][inet[/127.0.0.1:9300]], reason: zen-disco-join (elected_as_master)
[2014-09-22 23:12:04,663][INFO ][http                     ] [Bevatron] bound_address {inet[/127.0.0.1:9200]}, publish_address {inet[/127.0.0.1:9200]}
[2014-09-22 23:12:04,663][INFO ][node                     ] [Bevatron] started
[2014-09-22 23:12:05,312][INFO ][gateway                  ] [Bevatron] recovered [3] indices into cluster_state

elasticsearch 압축을푼 디렉토리 안에 bin 디렉토리에 있는 elasticsearch 파일을 실행한다.

OSX brew install

1
2
3
4
5
6
7
8
9
10
11
12
$ brew update
$ brew install elasticsearch
Data:    /usr/local/var/elasticsearch/elasticsearch_J2P/
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_J2P.log
Plugins: /usr/local/var/lib/elasticsearch/plugins/

To have launchd start elasticsearch at login:
    ln -sfv /usr/local/opt/elasticsearch/*.plist ~/Library/LaunchAgents
Then to load elasticsearch now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
Or, if you don't want/need launchctl, you can just run:
    elasticsearch --config=/usr/local/opt/elasticsearch/config/elasticsearch.yml

실행

1
2
$ ln -sfv /usr/local/opt/elasticsearch/*.plist ~/Library/LaunchAgents
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist

링크를 걸어주고 launchctl 로 실행한다.

Testing

1
$ curl 'http://localhost:9200/?pretty'

위와 같이 curl 로 request 를 날리면 다음과 같은 결과를 볼수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "status" : 200,
  "name" : "Bevatron",
  "version" : {
    "number" : "1.3.2",
    "build_hash" : "dee175dbe2f254f3f26992f5d7591939aaefd12f",
    "build_timestamp" : "2014-08-13T14:29:30Z",
    "build_snapshot" : false,
    "lucene_version" : "4.9"
  },
  "tagline" : "You Know, for Search"
}

How to Install Psycopg2 on Os X

문제

djagno 에서 postgresql을 쓰기 위해서 psycopg2를 install 하려고 했는데 다음과 같은 에러가 발생했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Error: pg_config executable not found.



Please add the directory containing pg_config to the PATH

or specify the full executable path with the option:



    python setup.py build_ext --pg-config /path/to/pg_config build ...



or with the pg_config option in 'setup.cfg'.

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /Users/J2P/.virtualenvs/coupon/build/psycopg2
Storing debug log for failure in /var/folders/m7/mvzr8gc13yb5tk_b2zrqg3l80000gn/T/tmpy7yqQi

해결방법

고맙게도 참 잘 설명해준다.

먼저 pg_config PATH 설정하고 해봤지만 똑같은 오류가 발생해서 그냥 소스를 다운받아서 설치해보기로 했다.

소스를 다운받고 압축을 푼다.

1
2
$ wget http://initd.org/psycopg/tarballs/PSYCOPG-2-5/psycopg2-2.5.3.tar.gz
$ tar xzf psycopg2-2.5.3.tar.gz

압축이 풀린 디렉토리로 이동해서 build, install 을 한다.

pg_config 옵션을 주고 자신의 postgresql 설치되어 있는 곳에 bin/pg_config 파일의 path 를 넣어서 build, install 하면 된다.

나같은 경우엔 brew 로 postgresql을 설치해서 “/usr/local/Cellar/postgresql/9.3.4/bin/pg_config” path 를 넣어주면 됐다.

1
2
3
$ cd psycopg2-2.5.3
$ python setup.py build_ext --pg-config /path/to/pg_config build
$ python setup.py build_ext --pg-config /path/to/pg_config install

Remove Duplication From Python List

프론트 단에서 중복된 데이터를 삭제해서 사용하다가 백엔드에서 처리하고 데이터를 넘겨주는 방식이 더 좋다고 생각해서 Python으로 list에서 중복을 제거 하는 방법을 찾아봤다.

코드는 다음과 같다.

1
2
3
4
5
6
7
8
>>> arr = ['a', 'b', 'c', 'a', 'a', 'b', 'f', 'g', 'c']
>>> set_arr = set(tickets)

>>> print set_arr
>>> set(['a', 'c', 'b', 'g', 'f'])

>>> print list(set_arr)
['a', 'c', 'b', 'g', 'f']

위에서 set 함수가 몬가 하고 알아보니.

순서하고 상관없는 컬렉션(unorderd collection) 이고 집합이기 때문에 중복된 값을 넣을 수 없다는 것을 알게 되었다.

이렇게 중복을 제거 한 다음 다시 list 함수로 감싸주면 배열에서 중복된 값이 제거 list를 얻을 수 있다.

node.js로 Html파일 Indent 적용해서 파일 생성하기

간단하게 파일을 쓰려고 할때 다음과 같이 사용하면 된다.

1
2
3
4
fs.writeFile('파일명', '내용', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
});

근데 html 파일을 작성할때 내용을 자동으로 indent 해주는 모듈은 없을라나… 했는데

htmltidy

요런게 있구나~

1
2
3
4
5
6
7
8
9
10
11
12
var tidy = require('htmltidy').tidy;
var opts = {
  doctype: 'html5',
    indent: true
};
tidy('<ul><li>1</li><li>2</li><li>3</li></ul>, opts, function(err, html) {
 if (err) throw err;
 fs.writeFile('index.html', html, function (err) {
     if (err) throw err;
     console.log('It\'s saved!');
  });
});

요렇게 해주면 index.html 파일에 이쁘게 indent 가 적용되서 저장된다~ 좋은데?!

node.js로 크롤링해서 메일로 보내주기.

우리 회사는 교육 부분에 많은 서비스 하고 있다.

Google Play 교육 카테고리 최고 매출 순위를 보는 회사분들이 있는거 같다.

그래서 크롤링해서 메일로 보내주는 스크립트를 node.js로 한번 만들어 봤다.

크롤링은 jsdom 모듈을 사용하고, jsdom

메일 보내기는 nodemailer를 사용하였다. nodemailer

코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var fs = require('fs');
var jsdom = require('jsdom');
var nodemailer = require('nodemailer');

var smtpTransport = nodemailer.createTransport('SMTP', {
  service: 'Gmail',
  auth: {
    user: '구글 메일 주소',
    pass: '구글 메일 패스워드'
  }
});

function sendMail(mailOptions) {
  smtpTransport.sendMail(mailOptions, function (error, response) {
    if (error) {
      console.log(error);
    } else {
      console.log('Message sent :' + response.message);
    }

    smtpTransport.close();
  });
}

(function getMailOptions() {
  jsdom.env({
      url: 'https://play.google.com/store/apps/category/EDUCATION/collection/topgrossing',
      scripts: ['http://code.jquery.com/jquery-2.1.0.min.js'],
      done: function (err, window) {
          var $ = window.jQuery;
          var cards = $('.card-list .card');
          var result = '<div style="width: 100%; font-size: 16px;">';
              result += '<h1 style="font-size: 0.875em;">Google Play 최고 매출 - 교육</h1>';
              result += '<ul style="width: 100%; list-style:none; margin: 0 auto; padding: 0; overflow: hidden; position: relative;">';

          $.each(cards, function(i, card) {
            var $card = $(this);
            var cover = $card.find('.cover-image').attr('src');
            var title = $card.find('h2').text();
            var subtitle = $card.find('.subtitle').text();
            var link = $card.find('h2 a').attr('href');

            ++i;

            result += '<li style="border: 1px solid #ccc;padding: 0;margin: 5px; width: 15%; text-align: center; float: left; border-radius: 5px;">';
            result += '<h2 style="background: #ccc; margin: 0 0 5% 0; padding: 2% 0; font-size: 0.813em;">'+ i +'위</h2>'
            result += '<img src="'+ cover +'" alt="'+ title +'" style="width: 80%; height: auto;">';
            result += '<p style="word-wrap:break-word; width: 70%; height: 10%; font-size: 0.750em; max-height: 50px; overflow: hidden; margin: 2% auto; 0">';
            result += '<a href="'+ link +'">'+ title +'</a></p>';
            result += '<p style="word-wrap;break-word; font-size: 0.623em; color: #ccc;">'+ subtitle +'</p>';
          });

          result += '</ul></div>'

          // 파일로 저장해서 미리보기
          fs.writeFile('index.html', result, function (err) {
            if (err) throw err;
            console.log('It\'s saved!');
          });

          // 메일 보내기
          sendMail({
            from: '보내는사람 mail',
            to: '받는사람 mail',
            subject: '제목',
            html: result
          });
      }
  });
})();

html을 꾸며준답시고 css를 쑤셔 넣어서 코드가 좀 지저분 하다 ㅋㅋ