express-serverless 구축(2)

aws-express-serverless 를 통해 express 를 통해 구축한 간단한 api 서버를 API GatewayLmabda를 이용하여 배포해 보았다.

API Gateway를 통해 Lambda 함수를 실행하기 위해서는 API Gateway에서 생성된 URL로 요청을 보내야한다. 요청을 보내기 위한 URL은 aws 콘솔 - API Gateway - API - Stage 창에서 확인이 가능하다.

URL 정보.


url 구조는 https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage}

https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage}와 같이 구성된 url로 요청을 보내면 Lambda에 배포한 함수가 실행되며 응답을 해준다.

하지만 이렇게 길고 불편한 URL을 매번 기억하고 요청하기에는 어려움이 있다.

요청을 보내고 싶은 URL은 https://{domain}/api/v1/lambda/{proxy}의 구조를 가진 URL이다. API Gateway에서 생성해준 URL이 아닌 직접 지정한 도메인으로 요청을 보내기 위해서는 커스텀 도메인을 등록하는 작업이 필요하다.

해당 작업을 진행하기 위해서는

  1. AWS 계정 (Lambda에 함수를 배포했으니 계정은 당연히 존재한다고 가정..)
  2. API Gateway에 연결할 도메인

이렇게 두가지만 있으면 된다. 그럼 진행해보도록 하자!!!

자세히 보기

express serverless 구축(1)

Lambda Express API 서버 구축하기

람다와 express 를 이용하여 API 서버를 구축해보자.

- 프로젝트 생성

1
npm init

- 패키지 설치

1
2
npm install express aws-serverless-express
npm install serverless-offline -d

- 로컬에서도 express를 실행하며 테스트하고 배포는 람다로 하고싶다.

개발환경과 배포환경을 구분하여 개발을 진행해야한다. (나누어 진행하지 않는다면?? 매우 힘들다…)
serverless 는 serverless-offline 이라는 서비스를 지원하며 람다를 통해 배포한 환경과 동일한 환경에서 테스트를 로컬에서 실행이 가능하도록 해주는 패키지이다.

그래서 난 로컬환경에서 serverless-offline 을 통해 실행함과 더불어 일반적인 express 개발 환경처럼 직접 express 를 구동할 수 있도록 설정하기로 했다.

자세히 보기

Rails Console Json Data

레일스 콘솔에서 데이터 편하게 보는법.

일단 그냥 보면 되지 굳이 명령어하나 더 치면서 데이터를 확인할까 하지만 데이터가 너무 길고 보기 힘들때 이해도 못하는 데이터 보기라도 편하게 바꿔보자 싶어서 찾아보았다.

먼저 일반적으로 보던 모습이다.

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
{"id"=>321312312,
"product_id"=>5858457321321223335,
"title"=>"",
"price"=>"17000",
"sku"=>"H59554J1S16C",
"position"=>16,
"inventory_policy"=>"deny",
"compare_at_price"=>nil,
"fulfillment_service"=>"manual",
"inventory_management"=>"shopify",
"option1"=>"brown",
"option2"=>"-4.25",
"option3"=>nil,
"created_at"=>"2020-09-28T16:44:30+09:00",
"updated_at"=>"2022-03-31T23:36:06+09:00",
"taxable"=>true,
"barcode"=>"8809693505654",
"grams"=>37,
"image_id"=>20065729413287,
"weight"=>37.0,
"weight_unit"=>"g",
"inventory_item_id"=>38760799502503,
"inventory_quantity"=>996,
"old_inventory_quantity"=>996,
"requires_shipping"=>false,
{"id"=>36712615346343,
"product_id"=>5858457223335,
"title"=>"brown / -4.50",
"price"=>"17000",
"sku"=>"H59554J1S17C",
"position"=>17,
"inventory_policy"=>"deny",
"compare_at_price"=>nil,
"fulfillment_service"=>"manual",
"inventory_management"=>"shopify",
"option1"=>"brown",
"option2"=>"-4.50",
"option3"=>nil,
"created_at"=>"2020-09-28T16:44:30+09:00",
"updated_at"=>"2022-03-31T23:36:07+09:00",
"taxable"=>true,
"barcode"=>"8809693505661",
"grams"=>37,
"image_id"=>20065729413287,
"weight"=>37.0,
"weight_unit"=>"g",
"inventory_item_id"=>38760799535271,
"inventory_quantity"=>999,
"old_inventory_quantity"=>999,
"requires_shipping"=>false,
"admin_graphql_api_id"=>"gid://shopify/ProductVariant/36712615346343",
"compare_at_price2"=>""},

유니코드란 무엇일까?

인코딩 에러?

꼭 한번은 마주친다는 문제… 분명 한글로 데이터를 받았고 데이터베이스에도 한글 데이터가 잘 저장이 되고 있었다. 그런데?

20220301-_______________________________________________________.xls 왜 한글 파일이 이렇게 저장이 되는걸까…

에러로그에는 다음과 같이 로그가 찍혀있었다.

1
Encoding::UndefinedConversionError ("\xEC" from ASCII-8BIT to UTF-8):

분명 나는 한글로 데이터는 들어온다. 그런데 인코딩 문제라고 한다.. 그동안 별 문제 없이 한글 데이터를 사용했는데 뭐 어쨌든 중요한건 한글이 깨진다는 것이다.

해당 문제를 살펴보니 ROR 콘솔에서 발생하지는 않지만 변수에 UTF-8이 아닌 문자를 할당할 경우 발생하는 에러라고 나왔다.

문제 해결을 위해서는 해당 파일을 읽어올때 루비에서는 .force_encoding(‘utf-8’).encode 메서드로 인코딩 후 실행하면 되는 문제였다.

항상 인코딩 문제를 겪을 때마다 문자 인코딩 방식이 달라서 그래! UTF-8 로 인코딩 해주면 해결 되는 문제야! 라고만 생각하며 쉽게 넘어가는 경우가 많았다.

그리고 별로 중요한 문제라고 생각하지 않았던 것도 있다. 해결하면 그만이지 자주 있는 일도 아니고 그때 또 하면 되는걸 인코딩까지 공부해야돼? 라는 생각…

그래서 그냥 알아보기로 했다.

유니코드(UNICODE)는 뭐지?

유니코드는 무엇일까 아스키 코드는 뭘까 항상 UTF-8 EUC-KR 많이 들었는데 이게 유니코드인가?

유니코드란 숫자, 전세계의 문자들을 매핑해놓은 구조이다. 먼저 등장했던 아스키코드는 영어만 매핑되어 표현할 수 있는 문자열의 제약이 존재했다. 전세계의 많은 언어들을 표현하기 위해서 아스키코드가 등장하게 된것이다.

아스키코드에서 대문자 A는 16진수 0x41, 10진수 65로 매핑되어있다. 이러한 형식과 동일하게 유니코드또한 아스키코드에서 표현하지 못하는 문자까지 포함하여 이루어진 코드표인것이다.

간단하게는 A라는 문자가 0x41 인 것을 앞에 U+가 합쳐져 U+0x41로 매핑되어있는 형식이다.

UTF-8

두가지 모두 유니코드를 어떻게 인코딩 할것인지 방식들이다. 가장 많이 사용되는 UTF-8은 8비트를 기준으로 인코딩하는 방식을 작동하는 인코딩 방식이다.

라는 한글을 유니코드표에서 찾아보며 UTF-8로 어떻게 인코딩이 되었는지 알아보자

먼저 는 유니코드 표에서 찾아보면 U+B8E8 이라는 키로 매핑되어있는 문자였다. 그럼 U+B828 을 UTF-8을 통해 인코딩하면 어떤 형식으로 바뀌는 걸까

U+B8E8 은 2진수로 1011 1000 1110 1000 이다. 이때 UTF-8은 3바이트 가변 표기하는 인코딩 방식으로

1110xxxx 10xxxxxx 10xxxxxx에 x 자리에 2진수를 차례대로 넣어주면 된다. 따라서

11101011 10100011 10101000

파란색 자리에 채워넣어주면 되는 것이다.

또한 라는 문자의 UTF-8 값은 235, 163, 168인데 위에서 나온 숫자를 각각 변환하면 11101011 => 235, 10100011 => 163, 10101000 => 168과 일치한다.

이러한 방식으로 라는 문자열까지 알아보면

UNICODE => U+BE44
UTF-8: 235, 185, 132;
2진수 1011 1110 0100 0100
3바이트 가변 표현 방식 11101011 10111001 10000100
3바이트 가변 표현방식 2진수 10진수 변환 235, 185, 132 = UTF-8 10진수 3개와 일치

유니코드와 UTF-8에대해 간략하게 어떤식으로 구성되어 있는지 알아보았다.

이처럼 유니코드는 아스키를 포함한 문자 코드 표이고 UTF-8은 해당 문자를 어떤 방식으로 인코딩 할지 결정하는 수단 중 한가지라고 볼 수 있다. 시간이 된다면 인코딩은 어떤 방식으로 이루어지며 어떤 것인지 알아보도록 해야겠다.

Postgresql 데이터 csv로 저장 및 csv데이터 insert하기

쿼리결과 csv 파일로 저장하기

Database: Postgresql
실행환경: Docker

  1. psql 접속하여 실행하는 법

    1
    2
    3
    4
    5
    6
    7
    docker exec -it <데이터베이스 컨테이너> bash

    # 컨테이너 접속 후
    psql -U <name> -d <database>

    # psql 접속 후
    COPY (select * from <tablename>) To '/path/fiilename.csv' With CSV DELIMITER ',' HEADER;
  2. 스크립트 작성으로 만들기

    1
    2
    3
    4
    5
    6
    docker exec <데이터베이스 컨테이너> \
    su - postgres -c \
    '
    psql -U <username> -d "<database name>" \
    -c "COPY (select * from <table name>) To '"'/path/fiilename.csv'"' With CSV DELIMITER '"','"' HEADER; "
    '

    주의사항: 따옴표 내부에서 문자열 조건이나 경로 입력 시 ‘“‘문자열’”‘로 감싸서 작성해야 문자열로 인식한다.

csv 파일 import 하기

  1. psql 접속하여 실행하는 법

    1
    2
    3
    4
    5
    6
    7
    docker exec -it <데이터베이스 컨테이너> bash

    # 컨테이너 접속 후
    psql -U <name> -d <database>

    # psql 접속 후
    COPY <tablename> FROM '/path/filename.csv' DELIMITER ',' CSV HEADER;
  2. 스크립트 작성으로 만들기

    1
    2
    3
    4
    5
    6
    docker exec <데이터베이스 컨테이너 이름> \
    su - postgres -c \
    '
    psql -U <username> -d "<database name>" \
    -c "COPY <table name> FROM '"'/path/filename.csv'"' DELIMITER '"','"' CSV HEADER; "
    '

스크립트 작성 중 알게 된 것
도커로 실행하는 레일스 앱에 스크립트에서 명령을 실행하기위해서는 다음과 같이 작성한다.

1
2
3
docker exec -i <container> rails c <<EOF
# 실행하고 싶은 명령어
EOF

Ruby on Rails Grape Gem

루비 온 레일스 Grape Gem 사용하기

Grape 젬은 레일스 환경에서 REST 와 유사한 API 프레임워크이다.

Grape GitHub
Grape Gem GitHub HomePage

Install Gem

1
2
# Gemfile
gem 'grape'

Gemfile 에 grape 젬을 추가해준뒤 bundle install 명령어를 실행한다.

설정하기

적용하기 위한 프로젝트의 레일스 버전이 7버전으로 grape github README 를 따라 6버전 이상의 세팅 방법을 따라 진행 하였다.

path: ./config/initializers/inflections.rb
아래의 코드가 이미 작성되어있고 주석처리가 되어있다. 해당부분 주석을 제거하고 작성해주면 된다.

1
2
3
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "DefaultController" # 베이스가 되는 클래스의 이름을 입력하는 부분이다.
end

컨트롤러 생성

1
2
3
4
cd app
mkdir api
touch base_api.rb
touch default_controller.rb

path: ./app/api/base_api.rb

1
2
3
4
5
6
7
8
class BaseApi < Grape::API
# 기타 공용 함수들?
helpers do
def server_port
Rails.env['PORT'] || 3000
end
end
end

path: ./app/api/default_controller.rb

1
2
3
4
5
6
7
8
9
class DefaultController < BaseApi
version 'v1', using: :path
format :json
prefix :api

get 'alive' do
"rails server run #{server_port} port"
end
end

path: ./config/routes.rb

1
2
3
4
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
mount DefaultController => '/'
end

curl을 이용하여 요청하기


User Controller 생성하기

1
2
3
# work dir: ./app
cd user_controller
touch user_api.rb

path: ./app/api/user_controller/user_api.rb

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
module UserController
class UserApi < BaseApi

resource 'users' do
helpers do
def post_params
params.as_json(only: %i[email password name nick_name age gender])
end
end
# GET /api/v1/users/all
get 'all' do
users = User.all
{
total: users.count,
result: users.map { |r| Entities::UserEntity.represent(r) },
}
end

# POST /api/v1/users
post do
user = User.create(post_params)
present user, with: Entities::UserEntity
end
end
end
end

POST /users


GET /users/all


Ruby on Rails Docker Setting (레일스 도커 개발환경 세팅하기)

Ruby on Rails Docker Setting

레일스 개발 환경을 도커로 세팅해보자

크게 복잡하고 거창한 환경 세팅은 아니지만 처음 세팅이 항상 골칫거리이다..

개발 환경
ruby-on-rails -v 7.0^
ruby -v 2.7.1
RubyMine
Docker
docker-compose
postgres -v 14.2-alpine

1. 기본 세팅을 위한 파일 생성 및 작성

1
2
3
4
5
6
mkdir backend
cd backend

touch Dockerfile
touch docker-compose.yml
touch docker-compoes.env

Dockerfile 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# syntax=docker/dockerfile:1
FROM ruby:2.7.1

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

WORKDIR /usr/src/app
COPY Gemfile ./
COPY Gemfile.lock ./
RUN bundle install

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Configure the main process to run when running the image
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose.yml 작성

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
version: "3"
services:
## backend
database:
image: postgres:14.2-alpine
ports:
- "5432:5432"
env_file: docker-compose.env
volumes:
- ./psql/data:/var/lib/postgresql/data

## api
web:
container_name: web
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
ports:
- "3000:3000"
env_file: docker-compose.env
volumes:
- ./:/usr/src/app
depends_on:
- "database"
environment:
- RAILS_ENV=development

Gemfile, Gemfile.lock 생성

1
2
touch Gemfile
touch Gemfile.lock
1
2
3
4
# Gemfile
source "https://rubygems.org"

ruby "2.7.1"

docker-compose.env

1
2
3
4
5
POSTGRES_USER=user
POSTGRES_PASSWORD=password

PG_USER=user
PG_PASSWORD=password

2. 빌드 시작

1
docker-compose build

빌드를 시작해도 아직 레일스 세팅이 안되어있다. docker 컨테이너를 이용하여 레일스 설치를 진행한다.

1
docker-compose run --no-deps web rails new . --api --force --database=postgresql

실행 시 뭔가 쭉쭉 설치되고 로컬 디렉토리와 컨테이너 볼륨을 지정해줬기 때문에 작업 디렉토리에 레일스 폴더, 파일들이 생성된다.



3. database.yml 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ./config/database.yml

default: &default
adapter: postgresql
encoding: utf8
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: 5
host: database # docker 환경 데이터베이스와 연결을 하기위해서는 컨테이너 이름으로 지정해주어야 한다.
username: juren
password: juren

development:
<<: *default
database: juren_development

test:
<<: *default
database: juren_test

4. 데이터베이스 생성하기

아직 도커에서 실행중인 postgresql 에는 데이터베이스가 생성되어있지 않다. yml 파일에서 지정한 데이터베이스들을 생성해주기 위해서는 아래 명령어를 실행한다.

1
2
docker-compsoe up # 컨테이너 실행 명령어
docker-compose run web rake db:create # rails db 생성 명령어

5. localhost 접속

크롬 -> localhost:3000 접속 결과 확인



끝~👍👍

은 아니고 이제 시작...