간혹 UML을 그려야 할 일이 있다. 이 경우, PPT로 작업하거나 staruml(http://staruml.io/ )를 사용하기 마련이다. (물론 내가 10년 전에 소개한 Dia http://yiunsr.tistory.com/566 라는 프로그램도 있긴 하다. ). 얼마전에는 Google Driver 와 연결된 draw.io 를 이용하기도 했다. 


 이런 툴들의 특징은 마우스로 그린다는 것이다. 나 같은 사람의 경우, 선이 삐뚤어지게 그려지게 되면 수동으로 어떻게든 직선을 그리기 위해 엄청난 노력은 한다. 그러다 얼마전 plantuml 라는 텍스트로 UML을 그리는 툴이 있다는 것을 알았다. 


 http://plantuml.com/download 에서 다운받을 수 있다. 해당 툴은 txt 파일을 UML로 변환하는 툴이기 때문에 실행하면 text 파일을 알려달라고 하니 너무 놀라지 말자. (텍스트를 치는 화면은 있는 줄 알았는데, 그런게 없다. ) 




@startuml

Alice -> Bob: Authentication Request

Bob --> Alice: Authentication Response

Alice -> Bob: Another authentication Request

Alice <-- Bob: another authentication Response

@enduml

 




너무 간단히 PNG 파일로 변경해 준다.  그리고 선도 반듯이 그려준다는 것이다. 


가장 좋은 점은 기존에 사용하는 개발툴의 plugin 이 다양하게 존재한다는 것이다. 내가 가장 많이 사용하는 eclipse 와 visual studio code 에서 플러그인 형태로 제공하고 있다. 
http://plantuml.com/running )

웬만한 툴들은 다 지원하는 것 같으니 UML 그릴 필요가 있다면 사용하기 좋을 것 같다. 





 어쩌다보니 이제 내가 회사에서 fullstack 개발자 처럼 일하고 있다. 서버 세팅 같을 것을 자주하게 된다.


서버세팅할 때 AWS 를 사용하다보니 인스턴스 이미지 형태로 저장하였다가 다시 이용하는 형태를 많이 사용한다. 그러나 서버세팅을 처음 할 때나, 실서버에서 세팅하게 될 때는 매우 불편하다. 이를 쉽게 하기 위해 파이썬 fabric 라이브러리를 이용할 수 있다.

(fabric 이라는 프로젝트는 참 많다. 자바스크립트 Javascript Canvas Library  fabric.js 라는 것이 있다. 그리고 모바일 앱 개발도구로 트위터에서 시작되고 지금은 google 이 운영하는 fabric 도 있다. 그래서 여기서는 파이썬 fabric 이라는 표현을 사용했다. )


 이 fabric  대해 간단히 설명하면 SSH 에 대신 접속해서 명령어를 보내는 파이썬 라이브러리라고 생각하면 된다. AWS 에서는 SSH 접속시 pem 이라는 프라이빗 키를 이용하는데 이도 지원한다. 


 아래 샘플은 fabric 의 일반적인 형태가 아닌 파이썬 프로그램 처럼 run 하는 방식으로 짜여졌다. 내 경우 eclipse + pydev 나 vscode 에서 사용하는 편이라서 이런 식으로 만들어졌다. 아래 코드를 사용하기 위해서는 fabric 이 필수 이다. 아래 코드은 python3 기준이다. 

========= fab.py  ======= 

#-*- coding: utf-8 -*-


import os

from fabric.contrib.files import append, exists, sed, put, uncomment, upload_template, contains

from fabric.api import env, local, run, sudo, execute, task, settings

from fabric.context_managers import cd

#from fab.main import main




PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))

BASE_DIR = os.path.dirname(PROJECT_DIR)

RES_DIR = os.path.join(PROJECT_DIR, "res")




def server_virtualbox():

    env.hosts = ['ssh_user@127.0.0.1:22']

    env.passwords = {"ssh_user@127.0.0.1:22" : "password"}

    env.URL_HOST = "localhost"

    env.PROJECT_NAME = "re2view"

    env.GIT_USER = "id_depoly"

    env.GIT_USER_EMAIL = "depoly_email@gmail.com"

    env.GIT_PASSWORD = "Depoly_pw1"

    env.GIT_URL_INFO = "https://id_depoly:Depoly_pw1@github.com/yiunsr/re2view.git"



def server_aws():

    env.hosts = ['ubuntu@11.123.13.15:22']

    env.user = "ubuntu"

    env.key_filename = "C:\work\key\pem.pem"

    env.URL_HOST = "ec2-11-123-13-15.ap-northeast-2.compute.amazonaws.com"

    env.PROJECT_NAME = "re2view"

    env.GIT_USER = "id_depoly"

    env.GIT_USER_EMAIL = "depoly_email@gmail.com"

    env.GIT_PASSWORD = "Depoly_pw1"

    env.GIT_URL_INFO = "https://id_depoly:Depoly_pw1@github.com/yiunsr/re2view.git"

    



def setting_virtualbox():

    server_virtualbox()

    common_setting_ubutu()


def setting_aws():

    server_aws()

    common_setting_ubutu()



def common_setting_ubutu():

    execute(ubutu_update)

    execute(common_install)

    execute(ubuntu_nginx_install)

    execute(ubuntu_nginx_conf)

    

    execute(ubuntu_nginx_host_conf)

    execute(git_clone)


def common_install():

    sudo("apt-get install -y build-essential")

    sudo("apt-get install -y python-dev")

    # Python 내 이미지를 다룰 때, 필요한 라이브러리 인데, 미리 설치해두자. 

    sudo("apt-get install -y libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk")

    sudo("apt-get install -y libmysqlclient-dev")

    sudo("apt-get install -y git")




def ubuntu_nginx_install():

    

    ## Nginx 설치

    sudo("apt-get install  -y  nginx")

    sudo("service nginx restart")

    

    ## Nginx 버전 확인

    sudo("nginx -v")



def ubutu_update():

    # 저장소 정보(/etc/apt/sources.list) 업데이트(-y 옵션은 Prompt 없이 가능하게 한다. )

    sudo("apt-get -y update")


    # 설치된 패키지 업그래이드, -y 옵션이 꼭 필요하다. 

    sudo("apt-get -y upgrade")




def ubuntu_nginx_conf():

    # /etc/nginx/nginx.conf 파일 편집


    # gzip 지원

    uncomment("/etc/nginx/nginx.conf", "# gzip_vary on;", use_sudo = True  )

    uncomment("/etc/nginx/nginx.conf", "# gzip_proxied any;", use_sudo = True  )

    uncomment("/etc/nginx/nginx.conf", "# gzip_comp_level 6;", use_sudo = True  )

    uncomment("/etc/nginx/nginx.conf", "# gzip_buffers 16 8k;", use_sudo = True  )

    uncomment("/etc/nginx/nginx.conf", "# gzip_http_version 1.1;", use_sudo = True  )

    uncomment("/etc/nginx/nginx.conf", "# gzip_types", use_sudo = True  )



    # log format 추가

    if contains("/etc/nginx/nginx.conf", "log_format  post_log", use_sudo=True) == False:

        old_string = r"""^http \{$"""

        new_string = r"""http {\n"""

        new_string += r"""\tlog_format  post_log $remote_addr - $remote_user [$time_local]    "$request"  \n"""

        new_string += r"""\t$status $body_bytes_sent "$http_referer"   \n"""

        new_string += r"""\t "$http_user_agent" $request_time "$request_body" \n"""

        

        

        with settings(warn_only=True):

            sed("/etc/nginx/nginx.conf", old_string, new_string  , use_sudo = True, shell = True, flags="m")


def ubuntu_nginx_host_conf():

    # /etc/nginx/sites-available/호스트명   으로 호스트 추가

    vhost_context = {

        'URL_HOST' : env.URL_HOST,

        'PROJECT_NAME' : env.PROJECT_NAME

    }

    local_path = os.path.join(RES_DIR, "config.txt")

    upload_template(local_path, '/etc/nginx/sites-available/%(URL_HOST)s' % vhost_context, context=vhost_context, use_sudo=True, backup=True)


    ## 파일이 있어 에러가 생긴 경우 그냥 넘어간다. 

    with settings(warn_only=True):

        sudo("ln -s /etc/nginx/sites-available/%(URL_HOST)s /etc/nginx/sites-enabled/%(URL_HOST)s" % vhost_context)


    

    ## 이미 디렉토리가 있어 에러가 생긴 경우 그냥 넘어간다. 

    with settings(warn_only=True):

        sudo("mkdir /usr/share/nginx/%(PROJECT_NAME)s/" % vhost_context)

    sudo("chown -R www-data:www-data  /usr/share/nginx/%(PROJECT_NAME)s/" % vhost_context)


    sudo("service nginx restart")


def git_clone():

    git_context = {

        'GIT_URL' : env.URL_HOST,

        'GIT_URL_INFO' : env.GIT_URL_INFO, 

        'PROJECT_NAME' : env.PROJECT_NAME,

        'GIT_USER' : env.GIT_USER,

        'GIT_USER_EMAIL' : env.GIT_USER_EMAIL,

    }

    

    with cd("/usr/share/nginx/%(PROJECT_NAME)s/" % git_context):

        sudo("rm -rf ..?* .[!.]* *")

        sudo("git clone %(GIT_URL_INFO)s  ." % git_context)

        sudo("""git config --global user.email "%(GIT_USER_EMAIL)s" """% git_context )

        sudo("""git config --global user.name "%(GIT_USER)s" """% git_context )


    sudo("chown -R www-data:www-data  /usr/share/nginx/%(PROJECT_NAME)s/" % git_context)

    sudo("service nginx restart")




def common_deploy_ubuntu():

    execute(git_pull)


def git_pull():

    git_context = {

        'GIT_URL' : env.URL_HOST,

        'GIT_URL_INFO' : env.GIT_URL_INFO, 

        'PROJECT_NAME' : env.PROJECT_NAME

    }

    

    with cd("/usr/share/nginx/%(PROJECT_NAME)s/" % git_context):

        sudo("git pull")

    sudo("chown -R www-data:www-data  /usr/share/nginx/%(PROJECT_NAME)s/" % git_context)

    sudo("service nginx restart")



SERVER_TYPE = "VX" ## "VX" 또는 "AWS


if __name__ == "__main__":

    print("======== Start ========")

    

    if SERVER_TYPE == "VX" :

        #AWS 서버 세팅시

        server_virtualbox()

        common_setting_ubutu()

    

    elif SERVER_TYPE == "AWS" : 

        #AWS 서버에 소스 배포시

        server_aws()

        common_setting_ubutu()

        

    print("======== End ========")

==============================

server_aws 에서 보듯이 pem 파일을 사용 할 수 있다. 

그리고 여기서는 서버 1대만 적용되었다.

그러나

env.hosts = ['ubuntu@11.123.13.15:22']

이 부분에 다른 서버를 추가 한다면 동일 작업을 그 서버에게도 적용할 수 있다. 

uncomment 함수는 주석을 제거하는 함수이다. Nginx 를 설치하면 기본적으로 gzip 옵션이 주석처리되어 있기에
이 부분의 주석을 해제 시켰다. 


contains 함수는 특정 파일에 특정 스트링이 있는지 검사하는 부분이다. 보통 서버세팅 로직을 한 서버에 한 번만 돌리지 않고

여러번 돌릴 수도 있다. (경우에 따라서는 실수로, 경우에 따라서는 예기치 않은 에러 때문에 다시 한 번 돌리기도 한다.)

따라서 이렇게 될 때, 아래 있는 코드 부분이 너무 여러번 동작하면 이상히기 때문에 검사하는 코드를 넣었다. 


with settings(warn_only=True):

            sed("/etc/nginx/nginx.conf", old_string, new_string  , use_sudo = True, shell = True, flags="m")


sed 부분은 해당 파일에서 스트링을 replace 하는 함수이다.  flags="m" 의 의미는 multi line 이라는 의미이다. 

변경하려는 string 이 multi line 이기 때문에 넣었다. 

실제 동작하는 것은 replace 가 아니라 스트링 추가이다. 위에 코드를 보다시피 new_string 이 old_string을 포함하고 시작하기 때문에

실제로는 스트링 추가이다. 



http {

======>
http {
        log_format  post_log $remote_addr - $remote_user [$time_local]    "$request"
        $status $body_bytes_sent "$http_referer"
         "$http_user_agent" $request_time "$request_body"


같이 log_format 을 추가하는 코드이다. 

upload_template  함수는 템플릿 파일을 업로드 하는 함수이다. 템플릿 파일은 아래 res\config.txt 에 있는 파일이다. 

업로드 하는 파일은  아래와 같이 변수를 적용 할 수 있다. 

======= res\config.txt ===============

server {

    listen 80;

    server_name localhost %(URL_HOST)s;

    #server_name 133.130.101.36 %(URL_HOST)s;

    client_max_body_size 100M;


    root /usr/share/nginx/%(PROJECT_NAME)s/;

    access_log /var/log/nginx/%(PROJECT_NAME)s_access.log;

    access_log /var/log/nginx/%(PROJECT_NAME)s_request.log   post_log;

    error_log /var/log/nginx/%(PROJECT_NAME)s_error.log;


    index index.html index.htm;


    # Make site accessible from http://localhost/


    index index.html;

    location / {

       try_files $uri $uri/ =404;

    }

    

}

=============================   

    







메인 부분을 

if __name__ == "__main__": git_pull()

이라고 수정하면 서버 세팅이 아니라 git 를 통한 소스 업데이트를 편하게 적용할 수 있다. 


여기에서는 간단하게 서버세팅과 git 를 통한 소스 업데이트를 보여줬지만 uwsgi 까지 완전히 세팅을 완료 할 수 있을 것이다. 

이 부분은 나중에 구현 하면 여기에 추가 하도록 하겠다. 

 보통 유니코드 대표적인 인코딩은 UTF-8 이다. 그래서 간혹 UTF-8 이라는 명칭대신 그냥 유니코드를 사용한다고 해서 개발자들을  괴롭히기도 한다. 


우선 UTF-32 부터 알아보자. 

유니코드는 0x10FFFF 까지 존재한다. 이것을 그냥 4byte 로 메모리에 표시한것이 UTF-32이다.따라서 유니코드 자체를 그냥 


UTF-8 은 유니코드 인코딩 중 하나로 가변 길이 인코딩 방식이다. (http://ko.wikipedia.org/wiki/UTF-8 ) 다시 말해 문자마다 바이트 수가 다를 수 있다. 

코드 범위
(십육진법)
UTF-16BE 표현
(이진법)
UTF-8 표현
(이진법)
설명
000000-00007F00000000 0xxxxxxx0xxxxxxxASCII와 동일한 범위
000080-0007FF00000xxx xxxxxxxx110xxxxx 10xxxxxx첫 바이트는 110 또는 1110으로 시작하고, 나머지 바이트들은 10으로 시작함
000800-00FFFFxxxxxxxx xxxxxxxx1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF110110yy yyxxxxxx 110111xx xxxxxxxx11110zzz 10zzxxxx 10xxxxxx 10xxxxxxUTF-16 서러게이트 쌍 영역 (yyyy = zzzzz - 1). UTF-8로 표시된 비트 패턴은 실제 코드 포인트와 동일하다.


 위 공식을 보면 알겠지만 UTF-8에서 아스키 영역(제어 문자와 라틴 기본 영역)은 1byte 이다. 그런데 한글 음절(0XAC00 ~  0XD7AF) 영역은 3byte 로 변환된다. 그래서 EUC-KR에서 사용했던 것이 10byte 였다면 UTF-8로 변환한다면 30byte 가 될 수 있다. (조금도 생각을 확장해서 첫가끝 방식을 사용하는 옛 한글 한글자 라면 첫소리 + 가운데 소리 + 끝소리에 대해서 3*3 = 9 byte 가 될 수도 있는 것이다. )

 그냥 보기에는 비트수의 낭비가 있는 것 같이 보이나 이러한 인코딩 방법으로 처음과 중간과 끝을 구별할 수 있음로 특정 위치에서 다음 문자를 찾을 수 있고 반대로 앞 문자를 찾기에 빠르도록 되어있다. 


 UCS2는 2byte 의 고정된 길이로 유니코드를 표시하는 방법이다. 영문도 한글도 모두 2byte로 고정된다. 아스키로 영역의 영문자의 경우 앞 byte가 0x00 이 붙어서 2byte를 고정시킨다. 그리고 그 보다 더 큰 영역은 그냥 표시가 안된다.  유니코드의 기본 다국어 평면만 표시한다. 인코딩이 Character Set 의 전영역을 표시하지 않는다는게 이해하기 어렵겠지만 예전에 유니코드가 2byte 였다는 것을 생각해보면 자연스러운 현상이긴 하다. 

 이 방식으로 현대한글을 표시하면 2byte 만 있으면 된다. 그래서 한글 표시하기에 참으로 좋은 방식이긴 하지만 UTF-8 보다 많이 사용하는 편은 아니다. 



 UTF16은 표시 방법에 따라 Big endian 방식과 Little endian 방식이 있다. ( 이 endian 은 따로 검색을 하시기 바란다. ) 그리고 2byte 이거나 4byte 로 표시하면서 유니코드 전 영역을 표시할 수 있다. 기본 다국어 평면의 경우 2byte 로 표시되고 그 외의 경우 4byte 로 표시된다. 전에 잠시 설명했던 서러게이트를 이용해서 4byte 를 표시한다.  상위 서러게이트와 하위 서러게이트의 조합으로 더 많은 비트 자유도를 가져서 유니코드 전 영역을 표시 할 수 있다.  (  http://ko.wikipedia.org/wiki/UTF-16 )