Inertia

mailchimp

오픈 소스에 보면 항상 주제별로 메일링 리스트를 오픈하여 관리하는데, 이것은 mailman같은것으로 관리 되곤 했었다. 그래서 언젠가 이런곳에서도 혁신의 여지가 많이 있겠다 막연히 생각했었는데 그런 제품이 있었다. 이름하여 mailchimp. subscriber수가 많아지면 유료이므로 선택시 참고할 것.

mailchimp

mailchimp는 한마디로 메일링 리스트 관리 + 이메일 발송 + 데이터 분석의 역할을 하고 있다. 데이터 분석까지는 많이 써봐야 진가를 알 수 있을것 같으니 생략하고 메일링 리스트와 + 이메일 발송 까지 다뤄보기로 함.

static web site(github page 같은)를 사용하고 있는 경우, 메일링 리스트를 구현하려고 하면 DB까지 연결해야 하나라는 고민이 있을 수 있는데, 그럴때 mailchimp를 사용하면 좋겠다. subscibe form도 제공해줘서 static web에 바로 붙일 수 있다.

내홈페이지 footer에도 붙여 놨으니 참고.

oauth2

요즘 대부분의 사이트의 회원가입/로그인 화면을 보면 Facebook/Google/Twitter를 통해서 가입 및 로그인을 할 수 있게 만들어 놨다. 이것이 흥한 이유는 매번 같은 정보를 기입해야 하는 유저들이 버튼 한번만 클릭하면 되는 편리함 + 중/소 사이트들의 security에 불안을 느끼기 때문이기도 함.

이러한 것을 가능하게 해주는 것이 OAuth란 스펙인데 현재는 2.0이 대부분임.

OAuth2

OAuth가 없던 시절에 이런것을 하려고 하면, 우선 나의 앱(client)이 페이스북 계정과 연동하게 하려면 아이디/패스워드를 클라이언트에게 노출 될 수밖에 없고, 이렇게 되면 내가 페북의 비번관리를 잘못하면 비번이 노출되게 되고 그러면 페이스북의 비번을 뚫리게 되므로 보안적으로 허술해짐.

그래서 고안한 것이 OAuth인데, 비번 대신에 access_token 을 발급해주고, 필요한 정보가 있으면 access_token으로 쿼리를 하면 답을 주겠다는 것임. 아래 flow 참고.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+

facebook/google/twitter로서는 당연히 자신의 계정이 많으 퍼질수록 좋음. 그래서 3rd party가(sign up을 쉽게 하고자 하는) 자신의 앱을 등록할 수 있게 해줌. 이때 app idapp secret 을 발급하주고, 이것을 이용해서 oauth의 endpoint로 로그인을 요청하면 로그인 결과를 redirect_uri로 리턴해줌. 리턴시 access token을 보내주는 flow!

fb/google/twitter로서도 app_id로 3rd party들을 컨트롤할 수 있으니 이보다 계정 오너로서는 이보다 더 좋을순 없음. 점점 모든 데이터는 빅 플레이어들에게 유리하게 가는 구조.

Example : Google OAuth

이해를 돕기 위해 구글 web Oauth를 구현해 보자. google oauth reference 를 띄어놓고..

  1. Google cloud platform console에 로그인
  2. oauth를 사용하기 위한 project 생성
  3. 생성후, 해당 프로젝트의 메뉴에서 API&services>credential을 선택후, credential 생성.
    1. web application 선택
    2. origin 과 redirect uri 등록
  4. 위의 과정에서 client_idclient_secret이 발급이 되었다. 이제 이것을 이용해서 로그인을 할차례.
  5. 아래의 url로 구글 인증을 요청하고, 아뒤/비번을 맞게 입력했다면 redirect_uri로 token을 uri에 붙여서 보낸다. 이 토큰을 이용해서, 이메일/이름/프로파일 아이콘 등을 요청할 수 있다.

    https://accounts.google.com/o/oauth2/v2/auth?client_id=`client_id`&response_type=token&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&redirect_uri=`your_redirect_uri`

References

elastic search and kibana

ELK 스택으로 많이 알려져있고, Elastic search(이하 ES) + LogStash + Kibana 의 조합으로 많이 사용한다. ES는 document DB이고, logStash는 로그를 정제하여 ES에 전달하는 역할, Kibana는 로그들을 필터링 및 조합하여 visualization을 하는 역할을 한다. 특히 타임 series 데이터를 보여주는데 적합하고 멋들어지게 보여줄 수 있어 선호되고 있다.

설치

AWS에서는 Elasticsearch Service로 따로 존재하고 비교적 최신 버전의 ES와 Kibana를 설치되어 있어 사용하기 편리하다.

아니라면, official site에서 ES와 Kibana를 따로 다운 받아서 쉽게 돌려볼수 있다. JAVA만 설치되어 있다면 별다른 어려움은 없다.

index

index는 RDB의 table개념이고 table의 shcema는 mapping이라고 불린다. 각각의 row는 document라고 한다.

index 생성 및 추가는 모두 rest api 방식으로 할 수 있고, PUT으로 index 생성, POST로 data를 추가할 수 있다.

아래처럼 PUT으로 <server>/twitter 로 생성해주면, <server>/twitter POST로 데이터를 추가할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUT twitter
{
"mappings": {
"tweet": {
"properties": {
"message": { "type": "text" },
"user": { "type":"text" },
"created": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}
}
}
}
}

Kibana

위에서 들어온 데이터를 Kibana로 visualize해주면 되는데 kibana의 직관적인 UI가 훌륭하다. 리프레쉬 간격도 설정 가능하고 시간간격도 자동으로 조절해준다. 그래프 수준은 훌륭하고 손쉽게 다양한 옵션으로 그래프를 그릴수 있다.

kibana time line

전체적인 흐름은 Management 에서 index설정을 하고, Discover에서 실험을 한후, visualize에서 시각화 설정을 해주고, dashboard에서 시각화한 요소들을 조합해서 한번에 볼수 있게 해주면 된다. 아래는 visual builder로 2개의 값을 비교해서 시간 축으로 나타낸 것이다.

kibana time line

query string

1
worker:seoul*    : worker filed가 seoul* 를 만족하는 쿼리

redirecting www.domain to domain

www.domain.com은 domain.com의 서브 도메인이라고 할 수 있다.
그러니 route53에서 www.domain.com의 A 항목을 만들어서 s3 web으로 보내고, s3 web은 domain.com으로 리다이렉션 시키면 된다.

  • 이미 domain.com의 route53 설정이 되어 있다고 가정
  • www.domain.com 과 domain.com 2개의 s3 bucket 생성. 이름은 꼭 도메인 네임과 같아야 함.
  • route53에서 www.domain.com의 record set 생성. type은 ‘A’로 하고, www.domain.com의 s3 bucket으로 alias를 건다. 아래 스샷 참조.
    image

  • aws>s3> www.domain.com으로 가서 properties tab의 static web hosting 을 인에이블하고 redirection을 domain.com으로 해준다.

image

  • 시간이 얼마 지난후, www.domain.com을 들어가면 domain.com으로 redirect되는 것을 확인 할 수 있다.

Angular2 에서 semantic js 사용하기

Angular2와 React는 동시에 뜨더니 요즘은 React가 더 많이 쓰는것 같긴 하다. 하지만 그전에 이미 Angular2를 사용하기로 정했기에 계속 사용하고 있는데 문제는 Angular2와 딱 맞아 떨어지는 CSS Framework이 없어서 불편하다는 것.

Angular2는 typescript를 사용하고 IDE에서 fully 사용하려면 type definition도 필요히다. 그래서 선택에 제약이 좀 있다.
처음에는 Angular Material을 사용했는데, 보다시피 layout쪽 컴포넌트가 많이 약하다. 그래서인지 버전명에 아직도 beta가 붙어있다.
Bootstrap/Sematic 등은 모두 훌륭한데, Angular에서 완벽하게 사용하려면 java script를 사용해야 하는데 이것을 사용하려면 설정을 더 해야 한다.
새로운 ng-bootstrap이나 ng-semantic도 있긴 하지만 컴포넌트 지원이 완벽하진 않다.

그래서 내가 semantic의 javascript api를 사용하기 위해서 했던 방법을 공유해보고자 한다. 여기선 semantic에 대해서만 얘기했지만 bootstrap도 다를것은 없다.
그리고 장기적으로는 Material처럼 Angular native css framework이 나오는 것이 제일 좋겠다. 그것이 Angular Material이 되겠지만.. 이것만 쓰기에는 컴포넌트가 많이 부족하다.

setup

우선 semantic는 내부적으로 jquery를 사용하고 있기 때문에 jquery를 추가 해줘야 된다. 패키징 방식에 따라서 아래처럼 CDN 링크를 사용할 수도 있고,
angular-seed라면, {src: 'jquery/dist/jquery.min.js', inject: true}, 를 NPM_DEPENDENCIES에 추가해주면 된다.

1
2
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.js"></script>

typescript 에서 js 호출하기

우선 jQuery 변수를 전역으로 선언한뒤, ngAfterViewInit에서 jQuery의 API를 통해서 semanctic의 dropdown API를 콜하는 예제.
이 아이디어는 순수히 ngSemantic을 참조한 것이다.
dropdown component인 경우 https://github.com/vladotesanovic/ngSemantic/blob/master/src/dropdown/dropdown.ts 을 참고하면 된다.

1
2
3
4
5
6
7
declare var jQuery: any;

export class MyComponent implements AfterViewInit {
ngAfterViewInit(): void {
jQuery('#dropdown').dropdown();
}
}

aws basic

aws 사용하면서 알아두면 좋은것들 정리해 보자.

CIDR

vpc를 구성할때 CIDR notation이 나오는데 여기를 보면 자세하게 설명 되어 있다.

ip주소는 255가 맥스인 4개의 int로 구성되니까 한 자리에 8bit(255) 즉, 32bit가 된다. a.b.c.d/x의 의미는 a.b.c.d에서 앞의 x bit까지는 같다는 것을 의미.
예를 들어 10.10.1.44는 10.10.1.32와 초기 27비트가 같으므로(아래 참조) 사이더 블록 10.10.1.32/27에 일치한다. 10.10.1.0/24는 10.10.1.까지는 똑같고 마지막 자리만 다른 ip가 할당될 것이라는 뜻이다.

1
2
10.10.1.44: 0x0A.0x0A.0x01.0x2C(0010 1100)
10.10.1.32: 0x0A.0x0A.0x01.0x20(0010 0000)

VPC

이 영상이 vpc를 쉽게 설명하고 있다. 개념이 안 와 닿는다면 한번 보시길.

route table은 패킷이 어디로 가야 할지를 명시해 줌. most specific이 먼저 처리 됨. 아래의 경우 172.31.x.x는 같은 vpc로 흐르고, 나머지는 외부 인터넷으로 라우팅 되게 된다.

1
2
172.31.0.0/16   local
0.0.0.0/0 igw-3376c756

internet gateways로 패킷을 보내면 internet(외부)로 나가게 된다.

RDS

  • rds launching시에 subnet 관련 에러가 나면, 이것 참고. vpc의 subnet이 2개 이상이어야 하고, 그 2개는 availability zone이 달라야 한다.

security group

logical firewall이랄까.. inbound/outboud로 ip 대역이나, 특정 security group, ip 로 액세스를 허용할 수 있다.

EC2에서 RDS로 접속이 안될때 보통 security group에 막혀서 인 경우가 많다.

route53

  • hosted zone 설정한후, 브라우져에서 www.domain.com 은 잘 접속이 되는데, domain.com이 안될 경우는 A record set을 추가해주면 되는데 domain.com을 www.domain.com으로 alias해주면 www.domain.com으로 redirect됨.

  • A record는 ip address로 리졸브가 되지만, cname record은 다른 도메인으로 리졸브 될 수 있다. record type은 위키피디아를 참조

aws ECS 사용하기

ECS는 EC2 instance를 사용해 container image를 사용할 수 있게 해주는 AWS의 컨테이너 서비스 이다. ECS는 크게 docker registry, Tasks Definition, Clusters 로 구성되어 있다.

docker registory는 docker hub같은 docker image를 저장할 수 있는 서비스인데 private registry서비스 이니 소스코드가 들어간 이미지도 push할 수 있고 secure하다.

Tasks definition은 docker run할때 많은 옵션을 줄 수 있는데 이것을 Tasks definition으로 추상화 한것 이다. 예를 들어 포트 매핑(-p), volume mount, 메모리 제한, network setting 등등..

Clusters는 주어진 클러스터에서 유동적으로 EC2 instance에 명시한 task를 실행할 수 있게 하는 추상화 이다. 어떤 EC2 instance를 사용할지, security group은 무엇을 사용할지, 허용가능한 instance는 몇대나 되는지 등등을 설정할 수 있다.

create docker registry

AWS console > ECS > Repositories > Create repository 에 들어가서 repository 이름을 명시하면 private docker registry를 생성할 수 있다. 이때 읿반적인 docker registry와 다른 점은 이름은 정해져 있다는 것이다. 즉, <registry_ip>/<image_name>:<tag> 에서 image_name<registry_url>/<image_name>은 정해진다는 것. tag만 수정할 수 있다.

이렇게 repository 생성후, 아래 순서로 docker image를 push 하면 된다.

1
2
3
4
5
aws configure # awscli를 사용하려면 access_key와 secret key등록이 되어 있어야 함.
aws ecr get-login --region us-west-2 # 이 명령을 내리면 docker login command가 출력되니, 이 명령으로 docker login을 수행.
docker build -t test . # docer image 빌드
docker tag test:latest <id>.dkr.ecr.us-west-2.amazonaws.com/test:latest # 이름 수정후
docker push <id>.dkr.ecr.us-west-2.amazonaws.com/test:latest # 푸쉬

nginx cluster 띄워 보기

nginx task 생성

에제로 nginx task 를 생성

  1. AWS console > ECS > Task Definitions > Create new Task Definition 에서
    1. task definition name : nginx
    2. network mode: bridge
    3. Add Container click
      1. container name : nginx
      2. Image: nginx:latest
      3. port mapping: 80:80
      4. add click
    4. Create click

docker cluster 생성 및 실행

  1. AWS console > ECS > Clusters > Create Cluster click
    1. cluster name: default
    2. create click
  2. 다시 AWS console > ECS > Task Definitions 로 돌아거서
    1. nginx 선택
    2. nginx:1 선택 후 Actions> Run Task
      1. Cluster에 아까 생성했던 cluster인 default 기입
      2. Run task 클릭
  3. 이렇게 하면 클러스터에 다커 이미지가 몇분이 흐른후 띄게 된다.
  4. EC2 메뉴로 가서 해당 ip로 접속을 해보면 nginx 서버가 떠 있다는 웹페이지가 열릴것이다.

Reference

Django Rest Framework

요즘 추세인 REST + {Angular, React,} 등을 만들기 위해서는 백엔드가 rest api가 되어야 하는데 그것을 Django Frameowrk위에서 구현한 것이 Django Rest Framework이다. 기존에 Django에 관한 post를 올렸는데, REST API backend를 가지게 되면 안드로이드/iOS 어플등도 지원할 수 있게 되고 테스트도 쉽게 만들 수 있어 장점이 있다.

아래애서 사용할 Snippet 모델을 아래 처럼 정의한다.

1
2
3
4
5
6
7
8
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
owner = models.ForeignKey(User, on_delete=models.CASCADE)

Serializer

우선 이전 포스트에서 Model의 개념은 나왔고, Serializer개념을 이해해야 한다. Model 이 Serializer를 통해서 request/response를 받는다고 생각하면 된다. Serializer는 어떻게 매핑하여 클라이언트에게 돌려줄지, 클라이언트에서 받은 데이터를 어떻게 가공하여 모델에 집어넣을지를 맵핑해주는 레이어라고 생각하면 된다.

예를 들어, 위의 Snippet 모델에 owner field는, DB에는 primary key인 int로 저장 된다. 이것을 REST API로 보낼때는 int로 돌려줄 수도 있고, 아니면 owner의 정보를 볼 수 있는 url(HyperlinkedRelatedField)로 보내줄 수 도 있다. 이렇든 같은 데이터라도 어떤 형식으로 보내줄지는 요구사항마다 달라질 수 있다. 그래서 이것을 유연하게 대처하도록 Serializer라는 개념으로 추상화 했다.

1
2
read (from client to server): request -> serializer -> model
write(from server to client): model -> serializer -> response

fields vs read_only_fields

fields에 명시된 것은 get/post 시에 나타날 field들을 명시하는 것이다. get에는 필요하지만, post/put시에는 넣을수 없는 정보는 read_\only_fields 로 명시해 주면 된다.

아래 예저의 경우 get시에는 user 필드도 받지만 post시에는 넣어주지 않아도 된다.

1
2
3
4
5
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'user')
read_only_fields = ('user', )

ViewSet

ViewSet은 Serializer를 통한 오브젝트를 Json으로 렌더링하거나 html로 렌더링을 담당하는 View 이다. 보통은 Model기반으로 하는 ModelViewSet을 많이 사용한다. get/post등의 default implementation이 있기 때문에 대부분은 그대로 사용하면 된다.

perform_create는 post로 실제 모델을 생성하기 전에 불리는 콜백인데, 여기서 POST에서 전달받지 못한 필드들을 초기화 해줄수 있다. 여기서는 owner정보를 채우고 있다.

permission_classes는 access control를 위해서 사용되는데, 로그인한 사용자들에게만 POST를 허용하고 READ는 다 허용하겠다는 정책이 permisson.IsAuthenticatedOrReadOnly이고, owner가 write권한을 아니면 read권한만 주는 것이 IsOwnerOrReadOnly 퍼미션이다. 여기서 read라 함은 get/list 이고, write는 post/put을 얘기한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SnippetViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.

Additionally we also provide an extra `highlight` action.
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)

def perform_create(self, serializer):
serializer.save(owner=self.request.user)

Additional filtering

Snippet list 중 내가 만든 Snippet만 보려고 할때는 어떻게 해야 할까. 우선 query param으로 mine=true이런식으로 요청을 한다고 하자. 위의 SnippetViewSet 에서 qet_queryset 함수를 오버라이드하면 된다. 아래처럼.

1
2
3
4
5
6
def get_queryset(self):
queryset = Snippet.objects.all()
mine = self.request.query_params.get('mine', None)
if mine is not None:
queryset = queryset.filter(owner=self.request.user)
return queryset

Angular2 file upload

요즘 한창 Django 와 Angular2를 사용해서 웹사이트를 만들고 있다. 헌데 angular2에서 여러개의 file upload하는 방법이 정확하게 나와 있지 않아서 여기에 정리해 본다.

1
2
3
4
5
<form  [formGroup]="myform" (ngSubmit)="submit()">
<label>Name</label>
<input name="name" type="text" formControlName="name">
<label>Image</label>
<input name="image" type="file" change="onChangeImage($event)">

우선 form의 형식은 위처럼 name과 image 두개.

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
export class ProjectformComponent {
public myform: FormGroup;
private image: File;


onChangeImage(event) {
const files = event.target.files || event.srcElement.files;
this.image = files[0];
}

submit(): void {
// validate fields locally
if(!this.image) {
console.log('image is required.');
return;
}

let data = new FormData();
for( var p in this.myform.value) {
data.append(p, this.myform.value[p]);
}

if(this.image) {
data.append('image', this.image);
}

let header = new Headers({'enctype': 'multipart/form-data'});
this.http.post('/api/url', data, header).subscribe(
resp => console.log('response: ', resp),
error => console.log(error)
);
}
}

위처럼 FormData를 만들어서 이미지와 일반 필드들의 값을 append한후 post의 data로 날리면 multipart로 서버로 전송된다. 나의 경우는 django rest framework을 사용해서 서버를 구성했다.

proxy setting

어느 정도의 규모가 있는 회사는 보통 보안 및 관리상의 이유로 proxy서버를 많이 사용한다. 각 툴마다 설정하는 방법등이 상이해서 매번 설정에 애를 먹기에, 여기에 정리해 보고자 한다.

general

대부분의 shell command등은 환경변수 http(s)_proxy 을 설정하는 것만으로 대부분 잘 동작한다.

`~/.bash_profile’ 에 아래 내용을 추가.

1
2
export http_proxy=http://<ip:port>
export https_proxy=http://<ip:port>

npm

~/.npmrc 에 아래 내용을 추가

1
2
3
4
registry=http://registry.npmjs.org/
strict-ssl=false
proxy=<ip:port>
https-proxy=<ip:port>

pip

~/.pip/pip.conf 에 아래 내용 추가하거나 pip install --proxy http://<ip:port> ipython 이런식으로 불러주면 된다.

1
2
3
[global]
cert = <cert_path>
proxy = <ip:port>

bower

~/.bowerrc 에 아래 내용 추가.

1
2
3
4
5
{
"proxy":"http://1<ip:port>",
"https-proxy":"http://<ip:port>",
"strict-ssl" : false
}

cert

회사 proxy에 사설 인증서를 사용하는 경우가 있는데(우리회사 –), 이런 경우 인증서가 공인인증기관에서 인증된 것이 아니기 때문에 https handshake에서 에러가 난다. 해킹 기법의 하나인 main in the middle 을 회사가 하고 있는 것이다. 이런 회사의 경우 모든 패킷은 회사가 들여다 볼 수 있기때문에 privacy 침해의 소지가 다분하다.

이럴때 https 통신이 실패하지 않게 하려면 어떤 설정이 필요한지 각각의 경우에 대해서 정리해 봤다.

Mac

맥은 기본적으로 사설 인증서를 키체인에 등록하고 그 인증서를 System Keychains에 추가한다. 그 인증서를 선택하여 always trust로 해준다. 이렇게 하면 Chrome이나 curl등이 cert error를 내는것을 해결할 수 있다.

Java

JDK가 설치된 폴더의 cacerts 파일에 keytool을 이용해서 추가해주면 된다. 아래처럼

/Library/Java/JavaVirtualMachines/jdk1.7.0_75.jdk/Contents/Home/jre/lib/security$ sudo keytool -import -keystore cacerts -file ~/Desktop/samsung.cer

python

python의 cacert가 위치한 파일을 찾아서 samsung.cer을 추가

1
2
3
4
5
# find cacert.pem in vritualenv direcgtory
find . | grep cacert
#./venv/lib/python2.7/site-packages/requests/cacert.pem
# append samsung.cer to bottom of cacert.pem
cat samsung.cer >> ./venv/lib/python2.7/site-packages/certifi/cacert.pem