Quantcast
Channel: 개발 노트
Viewing all 302 articles
Browse latest View live

HashCorp 밋업 - ZEPL 사용기

$
0
0

발표자

  • 박훈
  • ZEPL
  • 아파치 제플린 커미터

ZEPL에서 발생한 문제

  • 엔터프라이즈 서비스 시 고객들이 멀티 클라우드를 사용한다는 이슈가 발생
    • 이를 위해 멀티 클라우드 지원은 포기하고, 오케스트레이션 레이어를 정하고 컨테이너로 제공
  • 고객에게 제공할 인프라에 쿠버네티스 구성을 해야하는 이슈 발생

어떻게 자동화 했나

  • 레이어 분리


    • 인프라 - 쿠버네티스 - 어플리케이션

Install

  • Terraform
    • Versionning = S3
    • Locking = Dynamo
  • Ansible
    • ZK 클러스터링과 같은 프로비저닝에 사용
  • kops
    • 쿠버네티스 설치
  • HELM
    • 쿠버네티스로 구성된 어플리케이션 레이어의 설치

Monitor

  • CloudWatch
    • AWSLOG (AWS Log Agent)
    • 인스턴스 구동 시 User Data에 부트스트랩 스크립트 실행을 넣어주어야함
    • 기본 영구저장이라 테라폼을 사용해서 Expire 일 수 지정하는 것을 권장
    • Disk, 메모리 모니터링을 위해서는 aws-script-mon 참고해서 EC2 User Data에 스크립트 추가
    • Alert 설정은 Lambda를 이용해서 오토스케일링 시 등록
  • Prometheus
    • 어플리케이션 모니터링
    • prometheus-operator를 사용하면 HELM으로 쉽게 설치 가능
  • Grafana
    • Import/Export 기능이 없어서 Wizzy 사용

Deploy

  • Spinnaker
    • 멀티 클라우드 배포
    • 구성이 어려워서 현재 진행 중
  • Helm Chart 또는 Operator
    • 온프레미스 배포
  • MINIO
    • K8s 위에서 S3 사용

그 외

  • k8s와 ECS를 병행해서 사용하는 이유
    • 내부 시스템으로는 k8s를 사용하지만 사용자에게 컨테이너를 제공해주기때문에 k8s를 사용하면 해당 호스트의 모든 권한이 사용자에게도 부여됨
    • 이를 해결하는 방법이 여러가지 있지만 현재 도입을 못해서 ECS를 사용

참고



HashCorp 밋업 - 왜 테라폼인가?

$
0
0

발표자

  • 신근우
  • cypher

CloudFormation과 비교

  • AWS 인프라를 프로비저닝하기 위한 서비스
  • 여러개의 인스턴스를 구동시키기 위한 스크립트가 굉장히 복잡.
    • 유지보수도 어려움
    • 재사용 어려움
    • 템플릿이 S3에 올라가야함 (로컬 테스트 시에도)
  • 템플릿이 커질 수록 가독성이 떨어짐
  • 모듈화 어려움

Terraform

  • building, changing, versioning 도구
  • HCL 사용
  • 다양한 Provider 지원
  • Built-in function이 강력함

장점

  • 다른 사용자가 만든 모듈을 가져다 사용할 수 있음
  • Private Registry는 테라폼 엔터프라이즈를 사용하거나 Registry API를 통해 직접 구성도 가능
  • 테라폼에서 State가 핵심기능이라 생각
    • Remote backed를 사용할 수도 있음
    • import 기능으로 다른 인프라 구성을 참조해서 사용 가능
    • State를 Resource로 사용 가능
    • Region/Provider에 상관 없이 State의 ouput을 참조해서 사용 가능
      • 이로 인해 멀티 클라우드간의 연계 구성이 가능
  • 심플하고 강력함
  • 자사 제품들과 연계가 좋음

단점

  • AWS만을 사용한다면 CloudFormation에 비해 지원이 완벽하진 않음
  • 느린 속도로 인해 모듈화가 반 강제됨
  • HCL이 익숙하지 않음
  • 사소한 버그들이 있음

그외

  • 모듈화와 디렉토리 구조를 어떻게 잡을 것인가에 가장 신경써야 함


하이브 런칭기 #1 - 사내 환경 구성

$
0
0

초기 개발환경

처음에 사내 개발환경은 위키에 작성된 매뉴얼에 따라 물리 머신에 개발에 필요한 각 프로그램을 다운받고 설치하여 구성을 하였습니다. 개발을 진행하면서 새로운 머신에 개발환경을 셋팅하는 일은 드물게 발생하진 않지만 새로 설치할 때마다 위키 매뉴얼을 참조하며 한땀한땀 환경을 구축해나가야 합니다. 매뉴얼 대로 문제없이 진행이 되면 좋겠지만 대부분의 경우에 구축할 때마다 어디선가 오류가 발생합니다. 이전에 봤던 오류였는데 메모를 제대로 해놓지 않아서 다시 구글링을 하기도 하고, 같은 과정을 반복하며, 환경 설정에만 하루 이틀의 시간을 낭비하며 보내게 됩니다. 환경을 구성하는 여러 프로그램들 중에서 버전업을 한다거나 설정을 변경해야하는 일이 발생한다면 그 때마다 위키 매뉴얼을 수정해주어야 하고 팀원들에게 공유도 해야하기 때문에 설정 변경에도 많은 시간이 소모되기도 합니다.

Docker 도입

그래서 항상 동일한 환경을 구축할 수 있도록 하기 위해 docker를 도입하기로 결정하였습니다. docker 이미지를 생성하기 위한 Dockerfile을 정의하면, 위키 매뉴얼도 필요 없이 모든 팀원들이 어떻게 환경이 구셩되었는지 이해할 수가 있습니다. 전부 개발자들이기 때문에 오히려 문서로 하나하나 설명한 것보다 Dockerfile에 기록된 스크립트가 보기에 명확하고 이해도 빨랐습니다. 초기에는 ubuntu 이미지에 필요한 패키지들을 설치해서 사용하는 방식으로 사용했지만 이미지 빌드 시간이 너무 오래걸리고, 불필요한 패키지들이 포함되기 때문에 각 서비스 별로 기본 이미지를 사용하는 방식을 사용하였습니다. http://hub.docker.com페이지에서 사용할 서비스를 검색한 후 official로 등록된 이미지들을 최우선으로 사용하였습니다. 예를 들어 tomcat의 경우에는 아래 이미지와 같이 tomcat으로 검색한 후 오른쪽 상단의 드롭다운 버튼을 클릭한 후 Official을 선택하면 공식 이미지 리스트가 출력됩니다.

여기서 DETAILS 버튼을 클릭하고 들어가면 해당 이미지에 대한 다양한 정보와 명령어에 대한 예시들도 포함되어 있기 때문에 손쉽게 사용해볼 수가 있습니다. 어떻게 구성되었는지 확인하고 싶은 경우에는 아래와 같이 각 버전별로 제공되는 Dockerfile 링크를 타고 들어가면 Github 레파지토리에서 Dockerfile 내용을 볼 수도 있습니다.

이 tomcat 이미지를 그대로 사용해도 되지만 언어 설정이나 ssh 접속을 위한 openssh 설치와 같이 추가적으로 구성해야 될 패키지들이 있을 수 있습니다. 저 또한 필요한 패키지들이 점점 추가되었기 때문에 그 때마다 Dockerfile에 갱신해서 다음번에 환경을 구성할 때도 패키지를 빼먹는 일 없이 동일한 환경으로 구성할 수가 있었습니다. 아래와 같이 기본 이미지를 FROM에 명시하고, 추가적인 내용들을 아래에 정의하였습니다.

  • Dockerfile 예시

    FROM tomcat:8.0-jre8
    MAINTAINER Server Team <yongho1037@vinusent.com>
    
    RUN apt-get clean && apt-get update -y && apt-get install -y openssh-server locales vim sudo
    
    # SSH 관련 설정 (사용자 계정 생성 및 sudo 권한 부여)
    RUN adduser --disabled-password --gecos "" hive  \
        && echo 'hive:' | chpasswd \
        && adduser hive sudo \
        && echo 'hive ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
        && mkdir /var/run/sshd
    
    # 시간 설정
    RUN rm /etc/localtime && ln -s /usr/share/zoneinfo/UTC /etc/localtime
    
    # 언어 설정
    RUN sed -i -e 's/# ko_KR.UTF-8 UTF-8/ko_KR.UTF-8 UTF-8/' /etc/locale.gen && \
        echo 'LANG="ko_KR.UTF-8 UTF-8"'>/etc/default/locale && \
        dpkg-reconfigure --frontend=noninteractive locales && \
        echo 'export LANG=ko_KR.utf-8' >> /etc/bash.bashrc
    
    # filebeat 설치
    RUN curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.3.0-amd64.deb
    RUN dpkg -i filebeat-5.3.0-amd64.deb
    RUN rm filebeat-5.3.0-amd64.deb
    
    # 설정 파일 복사
    COPY conf/catalina.sh /usr/local/tomcat/bin/
    COPY conf/filebeat.yml /etc/filebeat/
    COPY conf/server.xml /usr/local/tomcat/conf/
    
    RUN chmod go-w /etc/filebeat/filebeat.yml
    
    CMD ["/usr/sbin/sshd", "-D"]
    
    • 사내에서 배포를 위해 jenkins를 사용하고 있는데 jenkins를 통해 war 파일을 배포하고 tomcat을 재시작하게 되면 tomcat 프로세스가 내려감과 동시에 컨테이너가 종료되기 때문에 이를 방지하기 위해 CMD 명령으로 sshd를 실행하도록 설정하였습니다.

    • CMD 명령은 컨테이너가 구동 될 때 실행되는 스크립트 또는 실행 파일을 의미하는데 여기서 실행된 프로세스가 PID 1번이 됩니다. 리눅스 커널에 의해 시작된 첫번째 프로세스가 PID 1을 얻게 되는데 이 프로세스가 종료되면 다른 프로세스들도 KILL 신호로 종료되어 결국 컨테이너가 종료됩니다.

      UID        PID  PPID  C STIME TTY          TIME CMD
      root         1     0 30 20:05 ?        00:00:03 /docker-java-home/jre/bin/java -
      root        53     0  1 20:05 pts/0    00:00:00 bash
      root        57    53  0 20:05 pts/0    00:00:00 ps -ef
      

사내에서 사용되는 모든 서비스들을 Docker로 구성하다보니 Dockerfile이 점점 많아지게 되어 현재는 아래와 같은 디렉토리 구조가 구성되었습니다. 매번 Dockerfile로 빌드를 해서 사용하지는 않지만 Dockerfile이 변경된 히스토리나 구성된 내용을 확인하기 위해서 하나의 디렉토리에 각 서비스를 위한 파일들을 비슷한 디렉토리 구조 하위에 파일들을 관리하고 있습니다.

  • Dockerfile을 모아둔 디렉토리의 구조

    ├── elasticsearch
    │   ├── Dockerfile
    │   └── conf
    │       ├── elasticsearch.yml
    │       ├── jvm.options
    │       └── sysctl.conf
    ├── httpd
    │   ├── Dockerfile
    │   ├── conf
    │   │   ├── httpd.conf
    │   │   └── workers.properties
    │   └── modules
    │       └── tomcat-connectors-1.2.42-src.tar.gz
    ├── jenkins
    │   ├── Dockerfile
    │   └── script
    │       └── init.sh
    ├── kibana
    │   ├── Dockerfile
    │   ├── conf
    │   │   ├── kibana.yml
    │   │   └── logtrail.json
    │   └── dashboard
    │       └── export.json
    ├── kibana-nginx
    │   ├── Dockerfile
    │   ├── conf
    │   │   ├── kibana-admin.conf
    │   │   └── kibana-readonly.conf
    │   └── start.sh
    ├── logstash
    │   ├── Dockerfile
    │   └── conf
    │       ├── logstash.conf
    │       └── logstash.yml
    ├── mariadb
    │   ├── Dockerfile
    │   ├── README
    │   ├── conf.d
    │   │   ├── mariadb.cnf
    │   │   ├── mysqld_safe_syslog.cnf
    │   │   └── tokudb.cnf
    │   └── sql
    │       ├── init.sql
    │       └── truncate.sql
    ├── mongo
    │   ├── Dockerfile
    │   ├── README
    │   ├── conf
    │   │   ├── disable-transparent-hugepages
    │   │   └── mongod.conf
    │   └── run.sh
    ├── nginx
    │   ├── default.conf
    │   └── ssl
    │       ├── nginx.crt
    │       └── nginx.key
    ├── pinpoint-collector
    │   ├── Dockerfile
    │   └── conf
    │       ├── hbase.properties
    │       └── pinpoint-collector.properties
    ├── pinpoint-hbase
    │   ├── Dockerfile
    │   ├── conf
    │   │   ├── hbase-env.sh
    │   │   └── hbase-site.xml
    │   └── start.sh
    ├── pinpoint-web
    │   ├── Dockerfile
    │   └── conf
    │       ├── hbase.properties
    │       └── pinpoint-web.properties
    ├── redis
    │   ├── Dockerfile
    │   └── conf
    │       └── redis.conf
    ├── registry
    │   ├── 1.run-registry.sh
    │   ├── 2.run-nginx.sh
    │   ├── conf
    │   │   └── nginx.conf
    │   └── ssl
    │       ├── server.crt
    │       ├── server.csr
    │       └── server.key
    └── tomcat
        ├── Dockerfile
        ├── conf
        │   ├── catalina.sh
        │   ├── filebeat.yml
        │   └── server.xml
        └── start.sh
    

Repository 구성

위와 같이 프로젝트에 필요한 각종 서비스들을 Dockerfile로 작성 후 이미지화 해서 사용하기 시작했는데, 팀원들도 로컬에 구성을 하기 위해서 각각 Dockerfile로 빌드하여 사용하다보니 docker에 대해 잘 모르는 팀원들에게는 절차를 알려줘야 하기도 하고, 버전이 조금씩 다른 이미지를 사용하게 되기도 해서 사내에 private repository를 구성하게 되었습니다. Repository를 사용하게 되면 이미지를 간단하게 pull 받아서 사용할 수 있기 때문에 다른 팀원들도 Dockerfile을 빌드해야 하는 번거로움이 없어지고 변경사항이 발생하더라도 commit 명령과 push를 통해서 간편하게 이미지 업데이트가 가능하기 때문에 편리했습니다.

나중에는 아마존 웹 서비스를 사용하면서 서비스 중 하나인 ECR(Elastic Container Repository)를 사용하여 AWS 상의 Repository에 이미지를 push하여 Repository에 대한 관리 부담도 덜어낼 수 있었습니다.

docker-compose 구성

각 컨테이너들 간의 통신이 필요한 경우에는 --links 옵션을 사용하여 컨테이너들 간에 통신이 가능하도록 설정을 해서 사용을 하였습니다. 하지만 각 컨테이너들을 생성할 때마다 docker run 명령을 통해 볼륨지정과 포트 지정, 링크 지정 등 옵션들을 길게 나열해서 작성하다보니 이 또한 번거로운 작업이 되었습니다. 그리고 각 컨테이너들간의 관계도 기억하고 있어야 한다는 것 또한 불편하게 느껴졌습니다.

그래서 이러한 과정도 한번 정의하면 그대로 사용할 수 있도록 docker-compose를 사용하여 구성을 하였습니다. docker-compose를 사용하면 각 컨테이너들 간의 통신이나 볼륨지정, 포트 포워딩 등 다양한 옵션 설정을 파일에 기록하여 더이상 신경쓰지 않아도 되었기 때문에 편하게 느껴졌습니다. 이로 인해 새로운 환경을 구축할 때도 OS 설치 시간과 docker를 설치하는 시간을 제외하면 10분 내로 환경 구성이 가능해졌습니다.

  • Docker-compose 예시

    version: '2'services:
      redis:
        image: 1234.dkr.ecr.ap-northeast-1.amazonaws.com/hive-redis:latestrestart: alwaysvolumes:
         - ~/service/log/redis:/var/log/redis
         - ~/service/data/redis:/dataports:
         - "6379:6379"mariadb:
        image: 1234.dkr.ecr.ap-northeast-1.amazonaws.com/hive-mariadb:latestrestart: alwaysenvironment:
         - MYSQL_ROOT_PASSWORD=rootvolumes:
         - ~/service/log/mariadb:/var/log/mysql
         - ~/service/data/mariadb:/var/lib/mysqlports:
         - "3306:3306"tomcat:
        image: 1234.dkr.ecr.ap-northeast-1.amazonaws.com/hive-tomcat:latestrestart: alwayslinks:
         - mariadb
         - redis
         - redis_eventvolumes:
         - ~/service/log/server-1:/usr/local/tomcat/logsports:
         - "8080:8080"
         - "9122:22"httpd:
        image: 1234.dkr.ecr.ap-northeast-1.amazonaws.com/hive-httpd:latestrestart: alwaysvolumes:
         - ../httpd/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
         - ../httpd/conf/workers.properties:/usr/local/apache2/conf/workers.properties
         - ~/service/log/httpd:/usr/local/apache2/logsnetwork_mode: "host"
    • httpd의 경우 외부로부터 패킷을 받는 진입점이므로 network_mode를 host로 지정하였습니다. host로 지정하지 않으면 내부적으로 docker proxy를 사용하기 때문에 약간의 지연이 발생하게 됩니다.
    • tomcat의 경우 jenkins와 ssh 통신을 하기 위해 22번 포트를 임의의 포트(위에서는 9122)로 포트포워딩 설정 하였습니다.

빌드 및 배포

하이브 프로젝트를 진행하면서 개발환경에서부터 라이브환경까지 각종 서버들과 툴들에 대한 빌드 및 배포를 젠킨스에서 관리하도록 구성하였습니다. 빌드는 커밋된 내용이 있는 경우 주기적으로 자동 빌드를 하도록 설정을 해두었고, 배포가 필요할 때는 해당 서버로 원클릭 배포가 가능하도록 구성해두었습니다. 

서버에서 제작하는 모든 프로젝트는 Subversion을 통해 소스 코드가 관리되고 있고, gradle을 사용하여 빌드하고 있습니다. 그래서 jenkins를 통해 빌드 시 먼저 아래와 같이 Subversion을 통해 소스 코드를 갱신합니다. 

그리고 아래와 같이 Gradle 플러그인을 사용하여 빌드를 수행합니다. 

Tasks 항목에 build를 입력하면 빌드와 더불어 테스트 코드까지 함께 수행되어서 매 빌드 시 자동적으로 회귀 테스트를 수행 할 수 있기 때문에 코드의 안정성을 유지할 수가 있습니다. 하지만 프로젝트를 진행하다보니 테스트 코드를 항상 최신으로 유지하는 것이 쉽지 않은 일이라는 것을 깨달았고, 빠듯한 일정으로 인해 팀원들이 모두 테스트를 신경쓸 수 없는 지경에 이르게 되었습니다. 그래서 팀 내 협의를 통해 구현 시 반드시 테스트 코드는 작성하되 오래된 테스트 코드의 관리는 빡빡하게 하지 말자는 결론이 나왔고 결과적으로 빌드 시 테스트는 제외하도록 설정하게 되었습니다. 그래서 빌드 옵션에 -x test를 추가하여 테스트 수행은 제외시켰습니다. 라이브 환경에서는 Amazon Elastic Beanstalk를 사용하기 때문에 배포를 위해 war을 업로드하는 절차가 필요합니다. 이를 위해 war파일을 S3에 업로드하고, Beanstalk 배포 시에 해당 S3 경로에서 war 파일을 사용하는 방식으로 배포를 진행하고 있습니다. 그래서 아래와 같이 빌드가 완료되면 생성된 war 파일을 S3로 업로드 합니다. (참고로 aws 커맨드를 사용하기 위해서는 jenkins가 설치된 곳에 AWSCLI를 설치해야 하고 credential 설정이 되어 있어야합니다.) 

마지막으로 아래 그림과 같이 Archive the artifacts 플러그인을 통해 생성된 war파일을 아카이빙 합니다. 이렇게 하면 추후 다른 jenkins Item에서 이 Item을 참조하여 생성된 war파일을 손쉽게 가져다 사용할 수가 있습니다. 이를 이용해서 Amazon Elastic Beanstalk가 아닌 war파일을 직접 tomcat에 복사하여 구동시켜야 하는 서버들에 배포를 수행하고 있습니다.

빌드가 완성되고 나면 이제 배포를 수행해야합니다. 배포 시에는 대부분 최근 빌드를 배포하게 되지만 필요에 따라 이전 빌드를 배포해야하는 경우도 생깁니다.(예를 들어 최근 빌드에서 에러가 발생하여 급하게 이전 빌드로 되돌려야 하는 경우) 그래서 배포 시 build를 통해 생성된 Artifact를 선택할 수 있도록 아래와 같이 Build selector for Copy Artifact 매개변수를 사용합니다. 

이렇게 설정하고나서 배포 Item의 빌드 버튼을 클릭하면 아래와 같이 Artifact를 선택할 수 있는 페이지가 출력됩니다. 최근 빌드를 선택할 수도 있고, Artifact의 빌드 번호를 지정할 수도 있습니다. 

이제 어떠한 Artifact를 사용할지 선택했으니 해당 Artifact를 어디에서 참조할지 선택할 차례입니다. 그래서 Build 항목에 Copy artifacts from another project 플러그인을 사용하여 빌드를 수행했던 프로젝트를 지정하고 Parameter Name에는 앞서 선택했던 매개변수를 지정합니다. 

마지막으로 지정된 Artifact를 원격지에 있는 웹 서버로 전송하고 해당 war파일을 tomcat 서버에 적용한 후 재시작 하는 절차를 스크립트로 작성해야 합니다. 빌드를 수행했던 프로젝트에서 생성된 artifact를 ssh로 전달하기 위해 Source files 항목에 artifact의 경로를 선택하고 Remote directory에 디렉토리를 지정하면 해당 디렉토리로 Artifact가 복사됩니다.(참고로 Remote directory는 Jenkins 환경 설정에서 SSH Server 설정 시 지정한 Path를 기준으로 한 상대경로입니다.) 아래 그림에서는 스크립트 작성 부분은 생략하였는데, 이 부분에 ssh로 전달된 war 파일을 tomcat의 webapps 디렉토리 하위로 복사한 후 tomcat을 재시작하는 스크립트를 작성하면 됩니다. 

지금까지 설명한 것은 원격지 서버로 배포하는 절차였습니다. Amazon Elastic Beanstalk의 경우에는 SSH를 통해 파일을 복사하고 재구동하는 절차 필요 없이 AWSCLI를 사용하여 더욱 손쉽게 배포를 수행할 수가 있습니다. 앞서 빌드 시에 생성된 Artifact를 S3로 업로드하는 부분이 있었는데 여기서 이를 사용하게 됩니다. 아래 그림과 같이 먼저 S3에 업로드 된 Artifact를 Amazon Elastic Beanstalk의 Application Version으로 업로드하고, 업로드된 war파일을 배포합니다. (그림에서 커맨드라인이 길어서 일부 생략했습니다.) 

개발을 진행하면서 기획팀에서 가장 곤란해 했던 부분이 갱신된 테이블을 서버에 반영하는 것이었습니다. 기존에는 반영할 때마다 기획팀에서 서버팀에 부탁을 해야했기 때문에 작업자들이 매번 부탁하기를 미안해했었습니다. 그래서 이로 인해 공격적인 테스트가 불가능 했기 때문에 결과적으로는 개발 속도에 영향을 미치게 되었고 버그를 제때 발견하지 못해 더 큰 문제로 이어지기도 했었습니다. 그래서 기획팀에서 자유롭게 서버에 반영을 할 수 있도록 jenkins를 통해 제공을 하였습니다. 이 과정에서 실수로 다른 프로젝트의 빌드를 수행하거나 배포를 할 수 있기 때문에 아래와 같이 권한 관리를 추가하게 되었습니다. 

이렇게 개발환경이든 라이브환경이든 jenkins를 통해서 자동화한 덕분에 수시로 빌드 및 배포가 가능했고 문제가 생기더라도 언제든 이전 버전으로 돌릴 수 있어서 편리하였습니다.

마무리

사내 테스트 환경의 아키텍처는 아래와 같습니다.

Docker를 통해서 간편하게 환경을 구성할 수 있었고, 테스트를 진행하는 도중에 컨테이너에 문제가 생길 경우에도 컨테이너를 제거하고 다시 생성하는 절차도 빠르고 간단하기 때문에 개발 환경에 대해서는 더이상 신경쓰지 않고 개발에만 집중 할 수 있게되는 장점이 있었습니다. 데이터나 로그파일과 같은 경우에는 볼륨을 지정하여 사용하기 때문에 이미지에 대한 업데이트가 발생하더라도 컨테이너 재생성 시에도 개발에는 지장이 없도록 구성을 하였습니다.




같이 보면 좋은 포스팅


하이브 런칭기 #2 - AWS 기본 구성

$
0
0

DNS 서비스(Route 53) 이용

아마존 웹 서비스를 사용하여 웹 서비스를 구성하면 일반적으로 EC2 인스턴스와Elastic Load Balancer를 사용하여 구성을 하게 됩니다.그러면 제일 처음 만나게되는 서버의 주소는 xxx.ap-northeast-2.elb.amazonaws.com와같은 ELB의 DNS name을 사용하게 됩니다. 이는 굉장히 길고 복잡한 주소이기 때문에 사용자로부터 접근성을 떨어뜨리게 되기 때문에 좋지 않습니다. 접근하기 쉬운 주소를 제공하기 위해 먼저 Route 53의 도메인서비스를 사용하여 주소를 할당 받도록 하였습니다. DNS 서비스 이용 시에는 AWS에서 도메인을 구매하여 사용할 수도 있고, 외부 업체를 통해 구입한 도메인의 경우에는 해당 업체의 Name Server설정을 AWS의 Name Server로 설정함으로써 사용이 가능합니다. 도메인을 등록하더라도 ROOT DomainServer까지 반영되기 까지는 1~2일 정도의 시간이 소요될 수 있습니다. 접속이 되지 않을 경우 nslookup 명령을 통해 Name Server 등록이 잘 되었는지 확인할 수 있습니다. Route 53을 통해 서브 도메인을 사용하기 위해서는 Record Set을 생성해야 하는데 IP주소의 경우 A Type을 선택하여 생성하고, DNS name의 경우에는 CNAME Type을 선택하여 생성합니다. 여기서는 ELB의 DNS name을등록해야 하기 때문에 아래와 같이 CNAME 레코드를 생성합니다.

Route 53을 통해 아래와 같이 아키텍처를 구성 할 수 있습니다.

사내에서 사용하는 모든 도메인은 Route 53으로 레코드를 추가해서 사용하였습니다. Route 53을 사용하게 되면 서버의 주소가 변경되어도 사업부나 클라이언트 팀에 변경된 주소를 알릴 필요 없이 Route 53에 주소만 갱신하면 되기 때문에 주소와 관련된 부분은 필수적으로 Route 53을 활용해야 한다고 생각합니다.

보안에 대한 기초 구성

외부 접근 최소화

웹 서버가 구동 중인 EC2 인스턴스는 Private Subnet으로 이전하여 외부에서의 접근을 완전히 차단하는 것이 좋습니다. 하지만 이렇게 될 경우 개발자나 관리자들 또한 해당 EC2 인스턴스로 SSH 접근이 불가능하기 때문에 관리적인 측면에 있어서 문제가 발생하게 됩니다.이러한 경우에는 일반적으로 Bastion이라 불리는Public Subnet에 속한 관리용 EC2 인스턴스를 통해 내부 네트워크 통신으로 웹서버가 구동 중인 EC2 인스턴스로 접속하는 방법이 있습니다. 이때 Security Group 설정을 해당 관리자의 IP만을 허용하도록 하여 다른 외부 접근을 차단하도록 해야 합니다. 이렇게 구성을 하게 되면 외부에서는 서비스중인 EC2 인스턴스로 접근할 방법이 없기 때문에 대량의 악의적인 트래픽에 대해 안전하고, 보안이 강화되는 이점이 있습니다.

Private Subnet에 속한 EC2 인스턴스는 인터넷에 연결되어 있지 않기 때문에 보안 패치나 OS 업그레이드조차 불가능합니다. 이 경우에는 NAT Gateway를 사용하여 Public Subnet을 통해 외부로의 통신이 가능하도록 설정하면Private Subnet의 EC2 인스턴스에서는 외부와 통신할 수 있지만 외부에서는 Private Subnet으로 접속할 Public IP가 존재하지않기 때문에 인터넷을 사용하면서도 여전히 보안을 강화할 수가 있습니다. NAT를 이용하기 위해서는 외부와 통신할 고정된 IP가 필요하기 때문에 NAT Gateway에 Elastic IP할당을 해야합니다. VPC 설정을 위 그림과 같은 구조로 설계하면 고객은 ELB를 통해서만 서버에 접근을 할 수 있고, 개발자 및 관리자들은 Bastion 서버를 통해야만 서버에 접근할 수 있기 때문에 외부로부터의 접근을 최소화 할 수가 있습니다.

키 관리

Key-Pair는 공개키 암호화 방식을 사용하며 Key-Pair 생성 시 다운로드받게 되는 비밀키를 통해서만 복호화가 가능합니다. 즉 비밀키를 소유한 사용자만이 해당 EC2 인스턴스에 SSH로 접속이 가능합니다. 이 비밀키는 AWS 상에 사본이 저장되지 않기 때문에 비밀키를 분실하면 복구할 방법이 없고, 유출 시에는 로그인 정보를 탈취당할 수 있기 때문에 비밀키 관리에 주의해야 합니다.

Key-Pair를 분실한 경우에는 해당 키를 제거하고 교체하는 작업을 진행해야 합니다. EC2 인스턴스에 접속하지 못하기 때문에 새로운 Key-Pair를 적용하기 위해서는 AMI를 생성한 후 EC2 인스턴스를 새로 생성하는 과정에서 새로 생성한 Key-Pair를 할당하거나, EBS 볼륨을 Detach 한 후에 새로 생성된 EC2 인스턴스에 Key-Pair 적용 후 다시 EBS 볼륨을 Attach하여 기존 데이터를 유지할 수 있도록 하는 방법이 있습니다. AMI를 생성하는 과정에서는 현재 구동 중인 EC2 인스턴스를 재시작하지 않고도 생성이 가능하지만 생성 과정중에 EC2 인스턴스의 변경사항에 의한 데이터 불일치가 발생할 수 있기 때문에 EC2 인스턴스를 중지 후에 진행하는 것이 좋습니다. 키를 추가하거나 갱신하는 경우에는 EC2 인스턴스에 접속하여 ~/.ssh/authorized_keys 파일을 수정하는 것으로 적용할 수 있습니다. 예를들어 새로 생성한 Key-Pair를 추가하는 경우 비밀키 이름이 updatekey.pem이라면 아래와 같이 추가가 가능합니다.

$ ssh-keygen -y -f updatekey.pem >> .ssh/authorized_keys

EC2 인스턴스를 중지 시키고 다시 시작하는 경우 대부분 새로운 호스트 머신에서 EC2 인스턴스가 생성되고 기존에 사용했던 EBS 볼륨을 마운트하여 데이터를 유지하기 때문에 Elastic IP를 지정하지 않은 경우에는 Public IP가 변경되어 해당 주소를 사용하는 곳이 있다면 서비스에 장애가 발생할 수 있습니다. Private IP의 경우에는 EC2 인스턴스를 재시작하여도 변경되지 않으므로 내부 통신에 있어서는 IP 변경의 위험 없이 사용할 수 있습니다. 현재 저희의 웹 서버가 있는 EC2 인스턴스로의 접근은 ELB를 통해서 이루어지기 때문에 Public IP를이용하는 곳이 존재하지 않기 때문에 Elastic IP는 사용하지 않았습니다.

IAM을 통한 권한 관리

IAM은 AWS의 보안 인프라 구성 요소 중 하나입니다. IAM을 사용하면 AWS의 서비스와 리소스에 액세스 할 수 있는 권한 정책을 구성하여 중앙에서 관리할 수가 있습니다. 그룹 단위로 권한을 부여하여 사내의 각 팀의 역할에 맡게 권한을 부여할 수 있고, CloudTrail을 통해 각 사용자들이 어떠한 행위를 했는지를 빠짐없이 검토할 수도 있습니다. 이와 같은 구성은 협력, 커뮤니케이션, 투명성을 위해 반드시 필요한 사항이기 때문에 개발 초기부터 적용하는 것이 좋습니다. 아래와 같이 각 부서별로 부여할 권한을 그룹으로 관리하고 IAM 계정에 할당하여 다른 서비스들에 영향을 줄 수 없도록 설정하면 사내 직원들의 실수로 인한 서비스 장애를 방지할 수가 있습니다.

루트 계정으로의 로그인은 가급적 피하는 것이 좋고, OTP 인증과 같은 2차 보안 인증을 통해 보안을 강화하는 것이좋습니다. IAM 대시보드를 확인하면 아래와 같이 보안에 대한 지침사항들이 표시가 됩니다. 여기서 경고하는 루트 계정 권한이나 강력한 패스워드 정책 등에 대한 지침들을 하나씩 해결해 나감으로써 보안을 강화하는 것이 좋습니다.

IAMRole을 생성하여 사용자뿐만 아니라 AWS 서비스들에도 권한을 설정할 수 있습니다. 예를 들어 S3에서 특정 EC2 인스턴스를 제외한 모든 접근을 차단하는 Role을 적용하여 정해진 경로를 통해서만 업로드가 가능하도록 하거나, Elasticsearch 서비스에 특정 관리자만 write가 가능하도록 Role을 적용할 수도 있습니다. 이와 같이 최초 루트 계정만으로 접근하던 것을 피하고, 최소한의권한으로 AWS 서비스를 이용하는 것이 중요합니다.

참고


같이 보면 좋은 포스팅


하이브 런칭기 #3 - 데이터베이스 구성

$
0
0

데이터베이스(MySQL)

개발 환경에서는 서버에 Mysql을 docker로 구동하여 사용하고 있는데, 대규모 트래픽을 감당하기 위해서는 이 데이터베이스 서버 또한 replica를 구성하거나 백업 관리 및 성능을 위한 튜닝을 해야하는 등의 관리 요소가 증가하게 됩니다. 더구나 데이터베이스는 서비스에 있어서 가장 중요한 부분이기 때문에 작은 실수도 서비스에 큰 영향을 끼칠 수가 있습니다. 그래서 이러한 위험성과 서버팀 3명이서 모든 것을 관리해야하기 때문에 이러한 위험성을 줄이고 관리 요소도 줄이기 위해 AWS의 데이터베이스 서비스를 사용하기로 결정하였습니다. 그 중에서도 Aurora를 선택하게 되었는데, Aurora는 현재 RDS 서비스 중 가장 가파른 성장세를 보이고 있고, 사용자들이 점차 증가하고 있다는 점과 내부적으로 AWS의 여러 서비스를 사용함으로써 기존의 MySQL만으로 해낼 수 없었던 여러가지 긍정적인 효과(예를 들어 스토리지로 S3를 사용하여 빠른 속도로 replica와 동기화를 맞추고, 여러 가용영역에 복제되는 S3의 특성으로 인한 내구성 또한 가질 수 있음)로 인한 높은 성능을 내고 있다는 점으로 인해 선택하게 되었습니다.

Aurora를 비롯한 Amazon RDS를 활용하게 되면 데이터베이스의 버전을 항상 최신으로 유지할 수 있도록 자동으로 패치되고, 백업이나 장애로부터의 복구 등이 자동으로 수행되기 때문에 개발자가 관리에 신경쓰지 않아도 되는 이점이 있습니다. 적은 인원으로 인프라를 관리하려면 이러한 관리 요소는 최대한 줄이는 것이 좋다고 판단을 하였고, EC2 인스턴스에 비해서 관리 비용을 추가로 지불해야하지만 직접 관리를 함으로써 소비되는 인력에 대한 비용이나 머신 비용을 생각한다면 이러한 관리형 서비스를 이용하는 것이 더 효과적이라고 생각하였습니다.

다행히 개발 중에 겪었던 이슈였는데, Amazon RDS의 데이터베이스 엔진에 대한 업그레이드 스케쥴 설정을 자동으로 할 경우 업그레이드로 인해 데이터베이스가 중단될 수 있습니다. 업그레이드에 대한 부분은 점검 시 필요에 따라 수동으로 수행함으로써 의도치 않은 데이터베이스 종료를 방지하였습니다.

Amazon RDS의 인스턴스는 외부에 노출될 필요가 없고, EC2 인스턴스에서만접근이 가능하면 되기 때문에 Private Subnet에 생성하고 Security Group 설정을 통해 웹 서버와 관리를 위한 Bastion 서버가 속한 Security Group에서만 접근이 가능하도록 설정합니다.

Multi-AZ 구성

AWS는 여러개의 Data Center들의 클러스터로 이루어진 AZ(Availability Zone)과 이 AZ들로 이루어진 Region 단위로 서비스가 제공됩니다. 각 AZ들은 장애에 대해 서로 영향을 받지 않도록 독립된 구조로 설계되어 있습니다.그러므로 서버를 구성할 때 여러 AZ로 확장될 수 있는 구조로 구성하는 것이 더 안정적입니다. 참고로 AWS에서는 장애가 발생한 경우 SLA에 의해 서비스 이용 요금의 일부를 크레딧으로 제공해주는 계약 내용이 존재합니다. 이 때, 하나의 AZ로만서비스를 구성한 경우에는 장애가 발생하더라도 SLA 정책에 포함되지 않기 때문에 이러한 보상을 받을수가 없습니다.

하나의 Availability Zone에만 웹 서버 기능을 하는 EC2 인스턴스와 데이터베이스를 둘 경우에는 AZ에 장애 발생하게 된다면 서비스가 중단될 수밖에 없습니다. 동일한 환경을 다른 AZ에 구성하면 하나의 AZ에 장애가 발생하더라도 서비스는 계속해서 유지할 수 있게 됩니다.

EC2 인스턴스를 다른 AZ에 복제할 때 AMI(Amazon Machine Image)를 사용하면 편리합니다. AMI는 해당 EC2 인스턴스의 환경 그대로 저장하기 때문에 손쉽게 다른 AZ로 동일한 환경의 EC2인스턴스를 생성할 수 있습니다.

위 그림과 같이 Multi-AZ를 적용하고, 사용자에게 단일 경로를 통해 웹 서버에접속이 가능하게 하려면 각 AZ로의 트래픽 분산 할 수 있도록 ELB사용이 필수적입니다. ELB는 현재 ClassicLoad Balancer와 Application Load Balancer 그리고 Network Load Balancer를 사용할 수가 있는데 ALB를사용하는 경우에는 Layer 7을 이용하는 로드밸런서 이기 때문에 도메인이나 HTTP Header 정보를 통해 더욱 유연한 로드 밸런싱이 가능하고, WAF(Web Application Firewall) 적용이 가능하기 때문에 보안을 강화할 수 있는 이점이 있습니다. 개선해야 할 웹 서버는 연결지향적인 요소나 HTTP 헤더를 이용하는기능이 요구되지 않기 때문에 Classic Load Balancer가 적합하다고 판단하였습니다.

ELB는 트래픽을 분배할 EC2 인스턴스에 주기적인 헬스 체크를 통해 정상 여부를 판단합니다. 만약 연결된 EC2 인스턴스가 비정상인 경우에는 로드밸런싱 리스트에서 제외시켜서 정상적인 EC2 인스턴스로만 트래픽을 전달하여 서비스에 문제가 발생하지 않도록 합니다. RDS의 경우 Multi-AZ 기능을 활성화 하는 것만으로 다른 AZ에 Stanby 데이터베이스를 생성하여 고가용성을 확보할 수 있습니다.

서비스를 런칭하기 전에 모집했던 사전 가입에서 50만명정도의 고객들이 신청을 해주었기 때문에 초반에 굉장히 많은 트래픽이 몰릴 것으로 예상하고 AWS에 ELB 프리워밍 신청을 하였습니다. 티켓 발행에 있어서 몇 번 실수를 해서 AWS 담당자분께 도움을 받아 무사히 프리워밍을 신청할 수 있었습니다. 그래서 처음 2대가 가동되어 있던 ELB 머신이 8대로 증가한 것을 확인할 수 있었습니다. 트래픽이 줄어들어 8대까지 불필요해지는 경우에는 일주일 정도 유지되다가 자동으로 축소됩니다.

데이터베이스의 부하 분산

오토 스케일링을통해 EC2 인스턴스의 수가 증가될 수록 이에 연결된 단일 데이터베이스인 Amazon RDS로의 쿼리량 또한 점점 증가하게 됩니다. RDS가 관리형 서비스이긴 하지만 확장에 대한 설정 없이는 한정된 머신 리소스를 사용하기 때문에 모든 처리량을 감당해낼 수는 없습니다. 이를 위해 Read Replica를 분리하여 하나의 데이터베이스에서 처리하던 것을 분산하여 처리를 할 수 있습니다.

Aurora는 Read Replica 사용시 MySQL처럼 binLog를 전달하여 동일한 쿼리를 Read Replica에서도 수행하는 로그 재생 방식이 아닌 마스터와 Replica가 공유하는 스토리지를 사용함으로써 로그 재생이 필요 없기 때문에 즉각적인 동기화가 가능합니다. 또한 내부적으로 비동기식 처리와 캐시 사용, 여러 AZ간 데이터 복제 등 빠른 성능과 안정성을 위한 처리가 이루어지고 있습니다.이러한 성능을 내면서도 저렴한 비용으로 이용할 수 있는 이유는 서비스 중심의 아키텍처를 적용한 클라우드 환경이기 때문입니다. 즉, 내부적으로 DynamoDB, S3, Route 53 등의 다른 AWS 서비스들과 유기적으로 연결되어 있습니다. 백업을 위해 S3를 사용하여 연속, 증분 백업을 하기 때문에 빠르고 안전하게 데이터를 보관할 수 있습니다.

자주 사용되는 데이터와 이벤트 성 데이터의 경우에는 ElasticCache(Redis)를 활용하였습니다. 개발 초기에는 데이터베이스의 부하를 생각하여 고객의 모든 데이터를 데이터베이스와 병행하여 Redis에도 동일하게 저장한 후 1차 적으로 Redis에서 데이터를 가져오고 갱신이 일어날 경우 데이터베이스와 동기를 맞추는 방식을 사용하였었습니다. 하지만 데이터 이중화로 인해서 개발 생산성도 떨어지고, 심적으로 데이터를 불신하게 되는 것을 느끼고 이중화를 제거하였고, 자주 사용되지만 유실되더라도 고객에게 이득으로 돌아가는 데이터들만 Redis에 저장하는 방식으로 방향을 전환하였습니다.

로그성 데이터의 경우에는 읽기보다 쓰기가 더 빈번하게 발생하기 때문에 초기에 DynamoDB를 사용했었습니다. 하지만 로그 데이터를 통해 통계를 내고 집계를 해야하는 부분들이 존재해서 결과적으로는 Amazon Elasticsearch Service로 변경하였습니다. 로그 데이터 수집에 대한 부분은 추후 포스팅에서 다루도록 하겠습니다.

참고



같이 보면 좋은 포스팅


하이브 런칭기 #4 - 오토스케일링 및 알림 설정

$
0
0

Auto Scaling Group을 통한 서버 확장 및 축소

CloudWatch를 통해 사용자의 트래픽과 EC2 인스턴스의 리소스 사용량을 주시하고, 이에 따라 EC2 인스턴스의 확장이 필요하게 된 시기에 AMI를 사용하여 EC2 인스턴스를 추가하고, 추가된 인스턴스를 ELB의 인스턴스 리스트에 추가해주면 해당 서비스는 더 많은 트래픽을 받을 수 있게 됩니다. 하지만 이 과정을 수동으로 하게 되면 그만큼 관리자의 피로도도 증가하게될 것입니다. AWS에서는 이를 위해 Cloud Watch를 활용하여 트래픽이 증가하거나 감소하는 경우를 인지할 수 있도록 설정한 후 이에 따라 서버를 확장 또는 축소를 자동으로 수행해주는 Auto Scaling Group이라는 기능을 제공합니다.

Auto ScalingGroup을 설정하면 아래와 같이 그룹 내의 EC2 인스턴스들의 평균 리소스 사용률 또는 네트워크 트래픽에 따라 CloudWatch에서 알람을 발생하여 Email이나 다른 전송 수단을 통해 관리자에게 알리거나 Auto Scaling Group과 연계하여 미리 생성해 둔 AMI를기반으로 서버를 자동으로 확장/축소 할 수가 있습니다. 서버가 확장되면 ELB의 대상 그룹에 해당 EC2 인스턴스가 등록되어 추가된 EC2 인스턴스로도 로드 밸런싱이 가능해짐으로써 부하를 줄이고, 이후 트래픽이 감소하면 서버를 축소하여 자동으로 ELB의 대상그룹에서 제외시킴으로써 불필요한 비용을 지불하지않도록 구성할 수가 있게 됩니다.

Auto Scaling Group을 적용하려면먼저 어떠한 AMI를 사용할지와 인스턴스 유형, 스토리지, 보안그룹 등의 설정을 해야 합니다. 이 때, 인스턴스 구매 옵션으로 스팟 인스턴스를 사용하게 되면 비용을 크게 절약할 수가 있습니다. 스팟 인스턴스는 경매와 비슷하게 EC2 인스턴스를 사용하는 방식인데 지정한 스팟 가격 내에 사용 가능한 EC2 인스턴스가 존재하는 경우 저렴한 가격으로 이용 가능하지만 지정한 스팟 가격 금액이 입찰 가격 아래로 하락하게 되면 EC2 인스턴스가 종료될 수 있기 때문에 지속적인 서비스에 이용하기 보다는 이벤트 성으로 서비스를 제공하는 경우에 사용하는 것이 좋습니다. 저는 부하테스트 시에 이 스팟인스턴스를 사용하였습니다. 부하 테스트에 대한 내용은 추후 포스팅에서 다루도록 하겠습니다. CloudWatch가 EC2 인스턴스로부터 데이터를 수집하는 주기가 기본적으로 5분이지만 Cloud Watch 세부 모니터링 활성화를 하는 경우 1분 간격으로 데이터를 수집할 수 있으므로 더 빠르게 트래픽에 대응할 수도 있습니다.

모니터링 서비스 설정

서비스를 원활하게 유지시키기 위해서는 각 서비스들에 대해 모니터링이 필요합니다. AWS에서 제공하는 CloudWatch를 통해 다양한 통계치를 확인할 수가 있습니다. 통계치를 얻기 위해서는 각 서비스에서CloudWatch Logs를 활성화 시켜 CloudWatch에 로그가 쌓일 수 있도록 설정을 해야 합니다.

로그 데이터를 통한 각종 메트릭 데이터를 통해 알람을 설정하여 그에 따른 처리를 수행할 수도 있습니다. 예를 들어 EC2 인스턴스의 머신 리소스 데이터를 수집하여 CPU나 메모리 사용량에 따라 알람을 발생 시키도록 설정한 후 그에 따른 대응을할 수가 있습니다. Amazon Simple Notification Service와 연동하면 이메일이나 SMS, Push를 통해 즉각적으로 관리자에게 알람을 전달할 수가 있습니다.

SMS의 경우에는 현재 한국에서는 AWS를 통해 전달이 불가능하므로 외부 업체의 SMS API와 연계해야만 전송이 가능합니다. 사내에 항상 구동되어 있는 서버를 활용하여 SMS 이벤트 발생 시 해당 서버로 Callback을 받을 수 있도록 구성하여 SMS API를 호출하여 관리자에게 전달하도록 구성하였습니다. 알람 이벤트에 따라 메시지 형식이 달라서 이에 대한 부분을 하나씩 확인하고 파싱하여 문자 메시지로 전달하도록 해야해서 조금 번거로운 부분이 있었습니다.

참고




하이브 런칭기 #5 - 웹서버 관리는 Beanstalk에게

$
0
0

관리 요소를 줄이기 위한 서비스 선택 고민

앞서 언급했던 것 처럼 오토스케일링 기능을 사용하기 위해서 AMI를 사용했었습니다. 하지만 AMI 를 사용한 자동 확장 구조에서는 컨텐츠나 설정과 같은 변경사항이 발생하게 되면 해당 AMI를 인스턴스로 구동시킨 후에 변경 사항을 적용해야 했습니다. 이렇게 변경된 EC2 인스턴스는 다시 AMI로 생성하여 이를 오토스케일링 그룹 설정에 적용을 해야 합니다. 이를 수동으로 할 경우 번거로울 뿐만 아니라 실수를 할 가능성이 크고, 그로 인해 빈번하게 배포를 수행하기가 어려워지게 됩니다. 이러한 절차는 서비스 개선에 있어서 악영향을 끼치게 될 것입니다. 그래서 조금더 편하게 배포를 할 수 있는 방법을 찾다가 AWS 의 서비스 중 Elastic Beanstalk 나 OpsWorks, ECS(EC2 Container Service)와 같은 관리형 서비스들을 검토하게 되었습니다.


사내 환경이 docker로 되어 있고, docker의 매력을 느끼던 차라서 조금 더 활용해보고 싶은 마음에 처음에는 ECS를 적용했었습니다. ECS에서 컨테이너가 동작하기 위해서는 AWS 상에 docker image들이 업로드 되어 있어야 하는데 이를 위한 서비스로 ECR(EC2 Container Registry)가 제공됩니다. 그래서 자연스럽게 사내에 docker registry를 구성하여 사용하고 있었는데, ECR을 통해 이미지들을 통합 관리하게 되어서 따로 repository 관리도 필요 없고 등록된 이미지들의 버전관리 또한 한 곳에서 할 수 있었기 때문에 편리했습니다. 하지만 ECS를 검토했을 당시에는 아직 서울 리전에 출시하지 않아서 도쿄 리전을 이용해야했고, Elastic Beanstalk 서비스를 살펴보면서 ECS를 사용할 생각은 아주 잠깐의 고민 후에(?) 접게 되었습니다. ECS 서비스보다 Elastic Beanstalk 서비스가 더 나아서가 아니라 저희가 구동할 서버가 tomcat 위에 아주 간단한 구조로 되어 있었기 때문에 tomcat을 지원하는 Elastic Beanstalk가 딱이라는 생각이 들었기 때문이었습니다. 앞서 얘기했던 잠깐의 고민은 docker를 활용하고 싶은 엔지니어의 욕심 때문이었는데 역시나 오버엔지니어링이라는 생각이 들어서 깔끔하게 접고, 이번 프로젝트의 프로덕션 환경에서는 docker를 사용하지 않는 것이 좋겠다는 판단이 들었습니다. 그래서 현재 사내 환경 구축에서만 docker를 활용하고 있습니다.


Elastic Beanstalk는 코드를 업로드하기만 하면 용량 프로비저닝, 로드 밸런싱, 오토 스케일링, 상태 모니터링까지 자동으로 구성해주고 처리를 합니다. 이러한 편리함을 얻으면서도 사용되는 리소스 외에는 추가 비용이 없다는 것이 큰 장점입니다. tomcat을 사용하다보니 war 파일을 업로드하여 배포를 하게 되는데 이 과정은 jenkins를 통해 자동화하였습니다. 빌드 시 마다 버전 번호를 붙여서 업로드를 하기 때문에 언제든지 이전 버전으로 배포를 할 수 있고, 롤링 패치를 지원해서 무점검 패치가 매우 손쉽게 가능합니다. 더 나아가서 Elastic Beanstalk의 상태에 따라 알람 설정을 해놓으면 문제가 발생할 경우 즉각적으로 인지를 할 수가 있습니다.

Elastic Beanstalk를 사용하면서 주의해야할 사항이 있는데, Autoscaling의 Launch configuration(시작 구성)을 변경하거나 VPC 설정을 변경하거나 인스턴스 type 변경, AMI 변경, ssh key 변경 등 인스턴스에 영향이 가는 설정들이 변경될 경우에는 Elastic Beanstalk 환경의 모든 인스턴스를 종료하고 다시 배포하게 됩니다. 처음에는 이에 대한 경고 메시지가 출력되지 않아서 생각없이 변경을 수행했었는데 ELB를 포함해서 EC2 인스턴스가 전부 터미네이트 되고 다시 생성되서 무척 당황했었습니다. 심지어 ELB 프리워밍 신청까지 해둔 상태에서도 설정 변경으로 인해 프리워밍된 ELB가 제거되어 다시 신청을 했던 적도 있었습니다. 최근에는 경고 페이지가 출력되어서 주의하면 이러한 실수를 방지할 수 있습니다. 그래서 Elastic Beanstalk 까지 적용한 아키텍처는 다음과 같았습니다.


다음 프로젝트에서는 마이크로 서비스까지는 아니지만 서비스를 조금 분할해서 진행해볼 예정이라서 ECS나 새로 출시된 EKS를 활용해볼 수도 있을 것 같습니다. 하지만 이번 프로젝트처럼 단일 서비스로 구성될 경우에는 Elastic Beanstalk를 적극 활용할 예정입니다.(너무 좋음)



같이 보면 좋은 포스팅


[양재동코드랩] 자바스크립트 강의 1일차 - 수업소개, var, let, const

$
0
0
  • 김영보 강사님

자바스크립트의 궁극적인 목적

  • 관련 기술을 통합, 제어하여 사용자에게 콘텐츠 제공
  • 백엔드와는 다른 관점으로 접근해야 함
    • HTML은 구조적인 관점으로, CSS는 비유, DOM은 이벤트 핸들러, 자바스크립트는 통합 컨트롤 등 관점이 다 다름
  • 사용자에게 콘텐츠를 어떻게 제공할 것인가 생각하는 것이 가장 중요

ES6 스펙

  • ES5와 ES6 스펙이 두배가량 차이 남
    • 그만큼 많은 내용이 바뀜
    • 객체지향적인 면이 많이 보완됨
    • ES6에는 활용 부분이 많음
      • 쉽게 코딩할 수 있도록
      • 객체지향도 쉽게 접근할 수 있도록
  • 자바스크립트는 객체지향 언어
    • ES5에서는 new 연산자를 사용하는 빈도가 적었음
    • ES6부터는 빈번하게 사용됨
    • ES6의 기본은 class
    • ES5와 ES6는 확실히 다르다.
  • Property key와 property name의 차이
    • ES5에서는 오브젝트 생성 시 프로퍼티의 key에 변수를 쓸 수 없었음
      • 엔진이 문자열로 취급
    • ES6에서는 Symbol이 등장
      • 문자열이 아닌 값
      • 프로퍼티에 Symbol value를 사용할 수 있기 때문에 변수도 key로 할당 가능
    • 즉, property name은 문자열, property key는 symbol이 포함됨
  • function과 method
    • Array.isArray()는 함수
    • Array.prototype.forEach()은 메서드
    • ES6 오면서 이 논리가 깨짐
      • Array.of()가 메서드
    • Prototype 내에 위치한 함수들은 method
    • ES6에 static method가 추가됨


var 키워드

  • 변수 선언 구분
    • 로컬 변수
    • 글로벌 변수
      • 자바스크립트의 발목을 잡았다고 생각함
      • 대부분의 언어에서는 클래스 내에 변수와 메서드를 선언해서 사용
      • 자바스크립트는 예외적으로 전역적인 오브젝트를 사용할 수 있음
        • Var 키워드를 사용하지 않고 변수를 선언하면 글로벌 변수가 됨
          • 전역 변수라 오해하게 됨
          • window 오브젝트를 명시해야하지만 엔진이 대신함
        • 사실 글로벌 변수도 글로벌 객체의 지역변수임
        • 개발자들이 글로벌 오브젝트에 대한 인식 없이 글로벌 변수라 생각하고 구현하면서 개념을 혼동하는 문제들이 발생함
        • ES6부터는 글로벌 변수에 대한 개념을 제거함
      • 자바스크립트를 객체지향적으로 사용하지 않게 됨
  • 변수 선언 구분 목적
    • 스코프
      • 모든 변수와 함수들은 오브젝트 내에 속함
        • 함수도 오브젝트이기 때문에 함수 내에 선언한 변수도 함수에 속함
      • 글로벌 변수 또는 함수도 window 라는 오브젝트 내에 속함
    • 함수 안에서 글로벌 변수 선언 방지를 위해 use strict 사용
      • ES5에서는 선언
      • ES6에서는 디폴트
        • 사실 전체에 대해 디폴트는 아니지만 class내에서는 use strict 모드가 기본이고, ES6에서는 class 사용이 원칙이 되다보니 디폴트와 다르지 않음

let 변수

  • 스코프를 가진 변수 선언

  • var는 함수 내의 스코프

    • 함수 내의 if문 내에 사용해도 함수 전체가 스코프
    • 호이스팅 문제 발생
  • let은 블록, 문장, 표현식이 스코프

    • 스코프가 많아짐에 따라 엔진 성능에 영향을 줄 수 있기 때문에 트레이드오프가 있음
    • 일반적인 경우에는 성능에 크게 지장을 주는 로직이 없지만 WebGL이나 머신러닝 쪽에서는 영향이 있을 수 있기 때문에 성능을 요구하는 곳에서는 고민해볼 필요가 있음
  • let은 호이스팅되지 않음

    • 자바스크립트의 유연성을 떨어뜨리는 단점도 됨
  • Temporal dead zone

    • 같은 스코프안에 같은 이름 선언 불가
      • 에러발생
  • Memo : 자바스크립트는 key, value를 식별자로 사용하기 때문에 함수 오버로딩이 존재하지 않음

  • 글로벌영역에서 var로 선언한 변수는 글로벌 오브젝트 내에 선언되지만 let은 글로벌 오브젝트에 선언되지 않음

    • 별도의 로컬영역에 선언되는 것으로 추정됨
    • 디버깅 모드로 확인해보면 스크립트 영역에 선언됨
  • ES6에서는 글로벌영역에서 오브젝트를 지정하지 않고 함수를 호출하면 함수는 호출되지만 this가 undefined로 설정됨

    • ES5에서는 오브젝트를 지정하지 않더라도 글로벌오브젝트(window)가 this로 자동 지정됨
  • 브라우저 개발자도구로 보면 let변수도 var변수처럼 선언 위치가 아니더라도 스코프에 진입하면 지역변수로 잡힘

    • 브라우저의 표현일 뿐인지, 엔진에서 실제 그렇게 처리하는지는 모르겠음
    • 하지만 선언 위치보다 상위에서 변수에 접근하면 에러 발생
    • var변수의 경우에는 호이스팅으로 인해 undefined일 뿐 에러가 발생하지는 않음
  • 예제

    var node =document.querySelector("ul");
    for (let k =0; k <node.children.length; k++) {    // for 루프가 돌 때마다 let 변수가 Block 영역에 생성됨var el =node.children[k];
        el.onclick=function(event) {
            event.target.style.backgroundColor="yellow";
    
            // let 변수 k는 Block 영역에 잡힘// for 루프가 돌때마다 Block 영역이 생성되고 해당 영역에 각각의 let 변수가 선언됨//그래서 클릭 이벤트가 발생할 때마다 다른 Block 영역의 k값이 호출됨console.log(k);
        }
    }
    • let 사용 시에는 Block 영역이 별도로 생성되기 때문에 트레이드오프가 존재함

const 변수

  • let 변수와 유사하지만 선언과 동시에 값을 초기화 해야함
    • let과 마찬가지로 Block 스코프
  • 변경 불가
  • 다른 변수들보다 const 사용을 우선적으로 검토
    • 값이 변경되지 않는 다면 const 사용
  • 기존에는 상수의 경우 대문자로 변수명을 지정하는 것이 관례였음
    • 하지만 값 변경 가능
  • const의 경우에는 문법적으로 상수이기 때문에 굳이 대문자 선언을 안해도 되지만 컨벤션에 따라 유연하게 사용
  • 오브젝트의 경우 값의 재할당은 불가능하지만 오브젝트의 프로퍼티는 변경 가능
    • 즉, const 변수의 개념은 const로 선언한 변수에 재할당이 불가능한 것을 의미
  • 대부분 let보다는 const를 사용하는 경우가 많음



[양재동코드랩] 자바스크립트 강의 1일차 - Arrow Function

$
0
0

Arrow Function

  • 코드 형태 : (param) => {함수코드}
    • 자바 람다와 유사
    • 람다와 조금 다른 표현들
      • (param1, param2, ...rest) => {코드}
        • rest는 변수명. 관례적으로 rest 사용
        • rest변수에 배열형태로 값이 추가됨
      • (param1, parma2=123) => {코드}
        • Default 파라미터
  • Memo : 자바스크립트에서의 함수는 무조건 return이 있음. 생략하면 undefined 반환
    • () => {} 는 undefined 반환
  • 함수와 유사하지만 new로 인스턴스 생성 불가
    • 함수는 new로 인스턴스 생성 가능함
      • new 연산자를 사용하여 인스턴스를 생성하면 대상의 prototype 하위의 construct를 찾아감
      • construct에는 해당 오브젝트의 생성 정보를 포함하고 있어서 인스턴스 생성이 가능함
      • 인스턴스 생성시 prototype 하위의 프로퍼티들로 인스턴스를 생성
      • 오브젝트에서 prototype이 아닌 다른 프로퍼티에 선언된 변수나 함수들은 인스턴스에 포함되지 않음
    • Array function에는 prototype 변수가 없음
      • 그래서 인스턴스 생성이 불가능
  • Arrow 함수에서는 arguments 사용 불가
    • 일반 함수의 경우에는 함수가 호출되면 arguments 오브젝트가 생성됨
      • 함수에서 선언된 파라미터 수보다 전달받은 인자의 수가 더 많은 경우 arguments 오브젝트를 통해 인자 값 사용 가능
    • 함수의 실행이 끝나면 arguments 오브젝트를 제거
    • Arrow 함수에서는 사용이 안되기 때문에 rest 파라미터 사용 권장

Arrow와 This

  • 함수 내에서의 this는 함수를 호출한 오브젝트를 의미

  • 관례적으로 인스턴스로 생성할 오브젝트는 첫글자를 대문자로 선언

    • 첫글자가 대문자인 오브젝트는 prototype 정의를 포함함
  • constructor에 arrow function을 사용할 수 있지만 this의 스코프 범위가 혼동될 수 있으므로 권장하지 않음

  • 예제

    constSports=function() {
        this.count=20;
    };
    Sports.prototype= {
    	plus:function() {
          this.count+=1;  
    	},
        get:function() {
        	setTimeout(function() {		// setTimeout은 window의 메소드console.log(this===window);	//결과는 true. setTimeout 함수는 window.setTimeout으로 호출한 것이기 때문에 함수 내에서 사용하는 this의 주체는 windows가 됨console.log(this.plus);		// this가 window이기 때문에 undefined임            
        	}, 1000);
        },
        arrow:function() {
            setTimeout(() => {
                this.plus();	// arrow 함수 내의 this는 인스턴스가 됨. 즉, 여기서 this는 Sports 인스턴스console.log(this.count);	//정상 출력됨
            }, 1000);
        },
        add: () => {
            this.count+=1;	//여기서의 this는 window를 가리킴. 그래서 값이 NaN.// prototype 내의 this는 동일한 인스턴스를 가리켜야하는데 prototype 내 arrow 함수의 this는 window를 가리키기 때문에 이런식으로 사용하면 안됨
        }
    };
    
    constsportsObj=newSports();
    sportsObj.arrow();

Lambda Function

  • 자바스크립트에서의 람다는 익명 함수를 의미함


[양재동코드랩] 자바스크립트 강의 1일차 - Iteration, Spread, Rest

$
0
0

Iteration

  • 반복을 의미
  • 반복하기 위한 프로토콜 필요
    • 프로토콜은 규약을 의미
  • 반복을 처리하기 위한 함수를 가지고 있는 오브젝트여야 함
  • 구성
    • iterable 프로토콜
    • Iterator 프로토콜
  • 빌트인 오브젝트는 기본적으로 이터러블 프로토콜을 가지고 있기 때문에 반복 가능
    • String, Array, TypedArray, Map, Set
    • Arguments, DOM NodeList
  • 이터러블 오브젝트는 빌트인 오브젝트 외에 이터러블 프로토콜이 설정된 오브젝트를 의미
  • 이터러블 오브젝트 조건
    • Symbol.iterator()가 있어야 함
      • 실제로 반복을 수행하는 메소드
    • Array를 상속받으면 이터러블 오브젝트가 됨
    • 개발자 코드로 이터러블 프로토콜 정의 가능

iterator protocol

  • iterator의 next 메소드를 통해 값 순회
    • 반환 값은 {value: 1, done : false}의 형태
      • value에 undefined가 들어갈 수 있기 때문에 value로 반복의 끝을 판단하면 안됨
      • done이 true가 되면 반복이 끝

spread

  • 문법 : [...iterable]

  • 이터러블 오브젝트를 하나씩 전개

  • 예제

    consttwo= [21,22];
    constfive= [51,52];
    constone= [11, ...two, 12, ...five]
    • array안에 array가 또다시 들어가는 것이 아니라 분리시켜서 하나의 array를 만듦
    • 결과는 [11, 21, 22, 12, 51, 52]
  • 문자열 분리

    conststr="music";
    constchars= [...str];
    console.log(charas)
    • 결과는 ['m','u','s','i','c']
  • 파라미터로 전달

    consttwo= [21,22];
    constfive= [51,52];
    two.push(...five);
  • 오브젝트 spread

  • constone= [{name1:11}, {name2:22}];
    consttwo= [{name3:33}, ...one];
  • function spread (가장 많이 사용됨)

    functionget(one, two, three){};
    constvalues= [10,20,30];
    get(...values);
    • 배열의 순서대로 함수의 파라미터에 각각 할당됨

rest 파라미터

문법 : function(param, paramN, ...rest)

  • 호출받는 function 파라미터에 ...에 이어서 파라미터 이름 작성
  • 파라미터 값들을 모아서 배열 형태로 사용
    • Array.isArray로 타입검사를 해보면 true 반환. 즉, 배열타입임
  • spread로 분리시키고 rest로 다시 묶는 식으로 활용
  • Rest 파라미터를 사용하면 arguments 오브젝트를 만들지 않음
    • 명확하게 명시할 수 있는 Rest 파라미터 사용 권장
    • argument는 보이지 않는 곳에서 생성됨

Array-like

  • 배열처럼 반복이 가능한 오브젝트

  • Argument 오브젝트가 바로 Array-like 오브젝트임

  • 프로토콜을 지켜야함

    • 프로퍼티 키 값이 0부터 순서 값

      • 순서 값의 순서가 지켜지지 않으면 오류
    • 전체 프로퍼티수를 length로 작성

      constvalues= {0: value, 1: value, length:2}
      for(constkeyin values) {	// for-in의 경우 작성 순서가 아닌, 숫자 > 영문 > length 순서로 전개console.log(key, values[key]);
      }
      
      for(let k =0; k <values.length; k++){ //작성한 순서대로 전개하려면 for문 사용console.log(values[k]);
      }


[양재동코드랩] 자바스크립트 강의 1일차 - Operator

$
0
0

operator

destructuring

  • Destructuring Assignment

  • 코드 형태

    let one, two, three;
    [one, two, three] = [1, 2, 3];	// array 분할 할당
    • 분할 할당의 개념
  • 중첩된 array라도 형태만 맞추면 변수에 값 할당 됨

    [one, two, [three, four]] = [1, 2, [3, 4]]
  • 변수할당이 필요 없는 경우 콤마로 구분해서 pass 가능

    [one, , , four] = [1,2,3,4]
  • 오브젝트 분할

    const {one, two} = {one:10, two:20}
    • 같은 프로퍼티명을 사용할 경우 ES5에서는 에러, ES6에서는 마지막 값으로 대체
  • 파라미터 분할

    total({one:1, two:2})
    functiontotal({one, two, five =5}) {
        
    }
    • 함수의 받는 파라미터에도 destructuring 사용 가능
    • 디폴트 파라미터 사용 가능

Object Operator

  • Object에 초깃값 설정 (Shorthand property name)

    constone=1, two =2;
    constvalues= {one, two};
    console.log(values);
    • 결과 : {one: 1}, {two: 2}
  • Object 내에서는 function 키워드를 작성하지 않음

    constobj= {
        getTotal(param) {
            return param +123;
        }
    }
    • 강사님께서는 static function 작성 시 이 형태를 주로 사용

Descriptor

  • 프로퍼티의 값을 설명

  • 자바스크립트에서는 프로퍼티나 어트리뷰트(속성)의 차이가 크게 없어서 어트리뷰트를 거의 사용하지 않지만 Descriptor에서는 사용

  • 프로퍼티 디스크립터 타입

    • 데이터
      • value
      • writable
    • 악세스
      • get
      • set
    • 공용
      • enumerable
      • Configurable
  • API로 제공하는 경우 해당 오브젝트의 사용을 제한을 위해 활용

  • 데이터와 악세스 설정을 함께 할 수 없음

  • 악세스의 get과 set을 함께 설정할 수 없음

  • defineProperty를 사용하기 전에는 오브젝트의 설정은 기본 값이 전부 true임

  • defineProperty를 사용하게 되면 전부 디폴트가 false가 되고 지정을 해야 true가 됨

    // ES5var obj = {book:"초기값"}
    Object.defineProperty(obj, "book", {
        get:function(){return"";},
        enumerable:true
    });
    console.log(obj.book);	//책이 출력됨// ES6constobj= {
        value:123,
        getgetTotal() {
            returnthis.value;
        }
    }
    • enumerable만 true이고 나머지 설정들은 전부 false
    • value, get, set의 경우 undefined
    • obj.book을 실행하면 descriptor 설정에 따라 데이터인지 악세스인지 확인
      • value에 값이 존재하는 경우 데이터 타입
      • get 또는 set이 존재하는 경우 악세스 타입
    • descriptor 내용을 찾아서 get을 호출함
    • 명시적으로 obj.book.get()을 호출하면 에러
      • 엔진이 하는 일임

프로퍼티 이름 조합

const name = "tennis";
const [name+"func"] = func(){  
};

거듭제곱

  • Exponentiation 연산자
  • ** : 곱하기 문자를 연속하여 2개 작성

default value

  • 값을 할당하지 않으면 default 값 할당

  • 디폴트 값 적용 순서는 왼쪽에서 오른쪽으로

  • 값에 undefined를 설정하면 default value가 적용됨


[양재동코드랩] 자바스크립트 강의 1일차 - for of

$
0
0

for-of

  • 이터러블 오브젝트 반복

    • Symbol.iterable이 존재해야 가능
  • 예제

    for(constvalueof [10, 20, 30]) {
        console.log(value);
    }
  • NodeList 엘리먼트를 하나씩 반복하여 전개

  • 예제

    constnodes=document.querySelectorAll("li");
    for(constnodeof nodes) {
        console.log(node.textContent);
    }
    • Node는 자바스크립트 영역이 아닌 DOM 영역
    • 자바스크립트는 웹을 표현하기 위한 각 요소들(HTML, CSS, DOM 등)을 통합해서 처리할 수 있는 아키텍처를 가지고 있음
  • for-in과 for-of의 차이

    • For-in : 오브젝트에서 열거 가능한 프로퍼티가 대상
    • For-of : 이터러블 오브젝트가 대상
      • property에 연결된 대상은 제외


[양재동코드랩] 자바스크립트 강의 1일차 - Object

$
0
0

Object

  • Object.is()
    • 오브젝트의 비교가 목적이 아니라 값의 비교가 목적임
    • == : 타입은 비교하지 않고 값만 비교 (사용 권장하지 않음)
    • === : 값과 값 타입 모두 비교
    • Object.is()와 === 차이
      • +0 === -0은 true지만 Object.is()는 false
        • 사람 입장에서는 같다고 생각할 수 있지만 기계적으로는 틀림
          • 음/양 표현을 하는 비트 플래그 값이 다름
      • NaN === NaN은 false, Object.is()는 true
        • === 비교로는 문제가 있음
        • NaN도 값인데 ===로 비교 시 false라는 것은 잘못됨
  • Object.assign()
    • 첫번째 인자의 오브젝트에 두번째 인자를 복사
      • String의 경우 분할되어 배열 형식으로 들어감
    • 프로퍼티 디스크립터는 복사하지 않음
    • 오브젝트에 오브젝트를 할당하기 위한 용도로 사용
      • 변수에 오브젝트를 할당하면 프로퍼티가 연동됨
        • 한쪽의 프로퍼티값을 바꾸면 다른 곳도 바뀜
      • 연동되지 않게 하려면 별도 처리 필요 (deep copy)
        • 방법1 : For 루프를 돌면서 프로퍼티 단위로 복사
        • 방법2 : assign copy
          • 1 depth만 프로퍼티 연동되지 않음
          • {key: {two: value}} 형태는 value 값이 연동됨
          • 여러 오브젝트를 전달하여 merge 용도로 사용할 수도 있음

proto

  • prototype과 __proto__의 차이

    • prototype은 prototype.메소드명으로 호출
    • __proto__ 메소드는 바로 호출 가능
  • 예제

    constSports=function(){
        this.member=11;   //인스턴스 프로퍼티
    };
    Sports.prototype.getMember=function(){};
    
    constsportsObj=newSports();
    console.log(sportsObj.__proto__===Sports.prototype);
    • new 연산자를 사용하게되면 prototype에 연결되어 있는 것들만 가지고 인스턴스를 생성
    • prototype에 연결되어 있던 것들을 인스턴스 생성 시 __proto__ 하위로 가져옴
    • 인스턴스 하위에 빌트인 __proto__ 오브젝트가 존재함. 이는 모든 오브젝트에 포함됨 (상속)
    • 인스턴스 생성 시 prototype은 원본 오브젝트를 참조함
    • 원본을 변경하면 생성한 모든 인스턴스들은 변경된 내용을 실행 함
  • __proto__ 는 ES5 까지는 표준이 아니었음. ES6부터 표준에 추가

    • 개발자가 직접 사용하는 것은 권장하지 않음
    • __proto__ 의 내용은 원본의 prototype의 내용이기 때문에 변경이 필요한 경우 원본을 수정할 것을 권장
    • 엔진이 사용

setPrototypeOf()

  • 파라미터에 오브젝트 또는 인스턴스 작성
  • Object.isExtensible()이 false이면 Type Error
  • 빌트인 오브젝트의 경우 prototype에 두번째 파라미터를 첨가하지 않고 해당 인스턴스가 반환됨
  • __proto__에 첨부는 권장하지 않음
    • prototype에 첨부하는 Object.create() 사용 권장
    • ES6의 class를 사용하면 이런 문제들도 전부 해결됨


[양재동코드랩] 자바스크립트 강의 2일차 - Number

$
0
0

Number

  • 자바스크립트는 IEEE 754에 정의된 double-precision floating-point format numbers로 숫자 표시

    • 변수 생성 시 타입 지정이 없는 자바스크립트는 엔진이 알아서 소수인지 정수인지 판단
    • 64비트 유동 소수점 형태로 수를 표시
      • RGB 표현의 경우에는 1바이트만으로도 충분하게 표현이 가능한데 64비트는 8바이트이기 때문에 7바이트가 낭비됨
        • 이를 방지위해 typed array가 등장함
        • 숫자 표현의 경우에는 typed array 사용 권장
  • safe integer란

    • 지수(e)를 사용하지 않고 나타낼 수 있는 값까지만 표현
      • Number.MAX_SAFE_INTEGER
      • Number.MIN_SAFE_INTEGER
    • 2의 53승 보다 큰 값(Number.MAX_SAFE_INTEGER)의 경우 지수표현
  • Number.EPSILON : 미세한 값 차이로 인해 일치되지 않을 때 사용

    • 예를 들어 0.1 + 0.2는 0.3이어야 하는데 실제 값은 0.30000000000000004가 되어서 0.1 + 0.2 === 0.3 이 false가 됨
  • 8진수 표현시 ES5에서는 첫자리에 영문 o/O를 작성했었음. 다른 진수 표현(2진수의 경우 0b0101과 같이 표현)과 달라서 혼동되는 문제가 발생

    • ES6에서는 영문자 o/O 앞에 숫자 0 추가
  • isNaN()

    • NaN === NaN의 결과가 false
      • NaN은 자바스크립트에서 값임 (숫자가 아니라는 것을 나타내는 값)
      • 값 비교인데 false가 반환되었기 때문에 논쟁이 있었음
    • 이 후 isNaN(), Number.isNaN(), Object.is(NaN, NaN) 이 출현함
      • isNaN()은 글로벌 오브젝트
        • Number에 의존하고 있는 기능을 글로벌 오브젝트에 포함시킨 것은 잘못된 것이라 생각
      • ES6에서 Number.isNaN()이 등장
        • Number에 포함되면서 제자리를 찾음
        • 사용 권장
  • Tip

    빌트인 오브젝트에 내장된 타입들의 프로퍼티나 메소드에 대해서는 외우지말고 MDN 참조해서 필요한 것을 찾아 사용하자.
    - 각 함수 또는 메소드마다 사용 방법이나 반환 값이 미세하게 다를 수 있기 때문에 기억에 의존하지 말고, 정확히 확인해서 사용.
    
  • isInteger()

    • 소수점 뒷자리가 0인 경우에도 정수로 판단
      • 1.0도 정수로 판단
  • isSafeInteger() : 2의 53승 범위 내의 정수인지 확인

  • isFinite() : 파라미터가 유한 값인지 확인

    • 처음에 글로벌 오브젝트에 포함됨
    • ES6에서 Number의 메소드로 포함됨
      • 글로벌 오브젝트와 결과가 다름
      • Number.isFinite() 사용 권장

Math

  • Math.trunc() : 소수를 제외한 정수 반환

    • 양수이면 Math.floor()와 같음
    • 음수이면 Math.ceil()과 같음
  • 32비트 계산 관련

    • 이전에 C++로 작성된 게임의 경우에는 32비트 정수를 사용하기 때문에 자바스크립트와 호환이 되지 않아서 웹에서 동작하기가 어려웠음
    • 웹 어셈블리를 통해 C++ 바이너리를 컨버팅 할 수 있게 되면서 일부 자바스크립트로 제어가 가능해짐
    • 이 때 32비트와 맞추기 위해 Math.clz32()와 Math.fround() 사용
    • Emscripten 참고


[양재동코드랩] 자바스크립트 강의 2일차 - Unicode, String

$
0
0

Unicode

  • ES6에 유니코드 관련 프로퍼티와 메소드 추가
  • 유니코드는 U+0031 형태로 표현
  • 코드 포인트
    • 0031이 코드 포인트 또는 문자 코드로 알려져있음
    • 코드 포인트 값으로 문자/기호/이모지/아이콘 등 표현
    • 4자리 이상의 UTF-16 진수 형태
    • 110만개 이상 표현 가능
  • plane : 코드 포인트 전체를 17개 평면(plane)으로 나눔
    • 하나의 plane은 65535개
    • 첫번째 plane을 BMP(Basic Multillingual Plane)
      • 일반적인 문자가 여기에 속함
  • euc-kr은 사용하면 안됨.
    • 해외에서는 깨진 문자열로 표시될 수 있음
  • 유니코드 이스케이프 시퀀스
    • \x31\x32를 유니코드로 작성한 형태
      • \u0031\u0032
  • 유니코드 코드 포인트 이스케이프
    • \u{1f418} 과 같은 형태는 ES6에서 처음 제시
    • ES5에서 호환하기 위해 surrogate pair 사용
  • Unicode Table 추천

String

  • String.raw : Template와 유사

    • Template과 달리 유니코드 또는 개행과 같은 것도 문자열로 인식

Template

  • tagged template

    • template에서 문자열과 값을 구분해서 인자로 전달

    • Template 함수의 첫번째 인자로 문자열 배열, 두번째 인자부터는 값에 매핑됨

      `1+2=${one + two}이고, 1-2=${one-two}이다.`
      
      • 1+2= 는 문자열 배열의 0번 인덱스
      • ${one + two}는 표현식이기 때문에 두번째 인자값에 매핑됨
      • 이고, 1-2= 는 문자열 배열의 1번 인덱스
      • ${one - two}는 표현식이기 때문에 세번째 인자값에 매핑됨
      • 이다. 는 문자열 배열의 2번 인덱스
    • Rest 파라미터 사용 가능

      • function restParam(text, ...values)



[양재동코드랩] 자바스크립트 강의 2일차 - Array

$
0
0

Array

  • from()

    • 이터러블 오브젝트를 Array로 변환
      • Array-like 포함
  • entries() : Array를 이터레이터 오브젝트로 생성하여 반환

    constvalues= [10, 20, 30];
    constiterator=values.entries();
    
    for (const [key, value] of iterator) {
        console.log(key, ": ", value);
    }
  • find()

    • find()와 filter()는 모두 Array에서 특정 값을 찾는 메소드이지만 find는 값과 일치하는 것을 찾으면 찾기를 중단하지만, filter는 값과 일치하는 것을 찾은 후에도 배열 끝까지 찾음

    • 첫번째 인자는 콜백 함수

      • 실제 값의 비교는 콜백함수에서 수행하고 반환되는 값에 따라 find 메소드가 발견 여부를 판단
      • ES6 들어서면서 콜백함수의 활용도가 높아짐
      • Find 메소드 자체는 실제 작업을 콜백 함수에 위임
      result = [1, 2, 1].find(
      	function(value, index, all) {	// value는 현재 값, index는 현재 인덱스, all은 배열 전체 값return value ===1&& value ===this.key;
      	},
      	{key:1}
      );

정규표현식

  • 플래그
    • u(unicode) : 매치 대상을 유니코드로 인식
    • y(sticky) : lastIndex 값을 설정
      • lastIndex는 기본값이 0이기 때문에 정규표현식의 패턴매칭을 0번 인덱스부터 시작함.
      • 만일 매치 시킬 문자열의 인덱스를 알고 있다면 lastIndex값을 설정해서 패턴 매칭할 위치를 지정할 수 있다.


[양재동코드랩] 자바스크립트 강의 2일차 - Generator

$
0
0

Generator

  • Generator function : function* 키워드를 사용한 함수

  • Generator function을 호출하면 함수 블록을 실행하지 않고 Generator 오브젝트를 생성해서 반환

    • 오브젝트를 만드는 과정과 블록을 실행하는 부분을 나누어서 관리
  • Generator function을 통해 반환된 오브젝트를 사용해서 함수 블록을 실행(next 메소드)

    • bind의 경우에도 이와같이 함수를 실행할 오브젝트를 반환해서 사용한다는 면에서 비슷
    constsports=function*(one, two) {	// Generator 함수 선언console.log("함수 블록");
        yield one + two;
    };
    
    constgenObj=sports(10, 20);  //이 때는 함수가 호출되지 않고 generator object가 반환됨constresult=genObj.next(); // next 메소드를 호출하여 함수 블록을 실행console.log(result);
  • yield : 제너레이터 함수를 멈추거나 재실행

    function*genFunc1(one) {
        consttwo=yield one;
        constparam=yield two + one;
        yield param + one;
    };
    
    constgenObj=genFunc1(10);
    
    let result =genObj.next();
    console.log(result);
    
    result =genObj.next();
    console.log(result);
    
    result =genObj.next(20);
    console.log(result);
    
    function*genFunc2() {
        return10;
    }
    
    constgenObj2=genFunc2();
    result =genObj2.next();
    console.log(result);
    • yield 표현식 평가를 완료하면 {value: 값, done: true/false} 형태로 반환
      • yield가 정상적으로 수행되면 done 값은 false
      • next를 호출했을 때 더이상 수행할 yield가 없다면 done 값은 true
  • next() : yield 또는 return을 만날 때까지 Generator Function 내용을 실행

    • Next 메소드를 수행하면 yield를 만날 때까지 함수 내용을 진행함
      • yield를 만나기 전에 return으로 반환 될 경우 함수처럼 값을 반환하여 {value: 값, done : true}가 됨
      • done이 true 이기 때문에 또 다시 next()를 호출하게되면 value는 undefined가 됨
    • Next 메소드에 인자를 전달하면 현재 수행 중인 yield 구문의 왼쪽 변수에 값이 할당됨
      • 위 예제에서 첫 yield를 수행한 후 next 인자로 20을 전달하면 two에 20이 할당됨
    • Generator 함수를 정의할 때는 yield를 항상 작성해주고, Generator 오브젝트에서 반환된 값에서 done을 체크하여 Generator 오브젝트 수행 완료를 판단
  • return() : Generator 오브젝트의 iterator 수행을 종료

    • next를 수행하고 값을 반환한 것과 동일, 이 때 done 값은 true
  • throw() : Generator 함수 내에서 Exception을 발생시킴 (마지막으로 수행한 yield 위치에서)

  • yield* : 이터러블 오브젝트를 오른편에 선언할 경우 해당 이터러블 오브젝트를 수행


[양재동코드랩] 자바스크립트 강의 2일차 - Class

$
0
0

Class

  • Function 오브젝트가 바탕
    • 별도로 class가 존재한다기 보다 function을 조금 더 객체지향적으로 사용할 수 있게끔 만들었다고 생각하면 좋을 듯
    • 객체 지향에서 사용하는 Syntax 추가
      • static, super
    • 자바스크립트의 객체지향은 C++이나 자바와 같은 기본적인 객체지향의 개념이라기 보다는 기존과 동일하게 prototype을 기반으로 한다.
      • 스펙의 Object 절 참고

class 선언문

window.onload=function() {
    classMember {
        getName() {
            return"이름";
        }
    }

    constobj=newMember();
    console.log(obj.getName());
};
  • 기존에 생성자 역할을 하는 function을 정의한 경우 prototype을 정의하고 new 연산자로 인스턴스로 생성할 경우 __proto__ 프로퍼티 하위에 prototype에 정의한 것들을 할당하게 되는데 class를 사용할 경우 prototype 정의 없이 메소드 선언만 해도 엔진이 알아서 __proto__ 하위에 메소드를 할당한다.

  • Member class의 내용을 확인해보면 function으로 정의한 것과 동일한 구조를 가진다.

    constMember=class {
    	getName() {
    		return"이름";
    	}
    }
    • Member:class

      • arguments:(...)

      • caller:(...)

      • length:0

      • name:"Member"

      • prototype:

        • constructor:class
        • getName:ƒ getName()
        • __proto__:Object
  • Class도 function 정의와 동일하기 때문에 property에 메서드를 직접 접근해서 추가가 가능하다.

  • prototype에 메소드를 추가하면 모든 인스턴스에서 공유한다.

  • Class 정의는 Window 오브젝트에 설정되지 않는다. 즉, 글로벌 오브젝트에 포함되지 않는다.

constructor

  • constructor를 작성하지 않으면 디폴트 constructor가 호출됨
  • 빌트인 오브젝트를 반환하는 경우 이를 무시하고 Class의 오브젝트를 반환
  • 빌트인 오브젝트 외 오브젝트를 반환할 경우 해당 오브젝트가 반환됨. (주의)
classMember {
    constructor(name) {
        this.name= name;
    }
}
  • 기존에는 constructor가 prototype 하위에 가려져 있었는데, class 사용 시에는 밖으로 드러남

getter

classMember {
    getgetName() {
        return"이름";
    }
};

constobj=newMember();
// console.log(obj.getName());	// 오류 발생console.log(obj.getName);
  • 메소드 선언 시 get 명시
  • obj.getName()으로 호출하면 에러남
  • obj.getName으로 호출해야함

setter

classMember {
    getgetName() {
        returnthis.name;
    }

    setsetName(param) {
        this.name= param;
    }
};

constobj=newMember();
obj.setName="이름변경";
console.log(obj.getName);
  • Class 멤버 변수(프로퍼티) name을 선언하지 않더라도 존재하지 않으면 this.name으로 프로퍼티가 추가됨

상속

  • ES5에서는 prototype에 Object.create를 사용하여 상속 받을 super 클래스를 할당하는 방식으로 상속

    Soccer.prototype=Object.create(Sports.prototype, { ...메소드 선언 생략 ... });
    Soccer.prototype.constructor= Soccer; // Object.create로 인해 constructor가 제거되기 때문에 다시 할당
  • ES6에서는 extends 키워드로 상속 구현

    classsubClassextendssuperClass {}
  • ES6 상속 예제

    classSports {
        constructor(member) {
            this.member= member;
        }
    
        setItem(item) {
            this.item= item;
        }
    }
    
    classSoccerextendsSports {
        setGround(ground) {
            this.ground= ground;
        }
    }
    
    constobj=newSoccer("박지성");
    obj.setItem("축구");
    obj.setGround("상암");
    console.log(obj.member);
    console.log(obj.item);
    console.log(obj.ground);
  • 상속 구조

    • obj:Soccer
    • ground:"상암"
    • item:"축구"
    • member:"박지성"
    • __proto__:Sports
      • constructor:class Soccer
      • setGround:ƒ setGround(ground)
      • setItem:ƒ setItem(item)
      • __proto__:
        • constructor:class Sports
        • setItem:ƒ setItem(item)
        • __proto__:Object
  • 같은 이름의 메소드가 존재하는 경우 상속 구조에서 Sub 클래스를 우선적으로 메소드를 찾음.

    • Soccer 클래스 > Sports 클래스 > Object 클래스
  • super 키워드 사용 방법은 자바와 동일

  • 빌트인 오브젝트 상속도 가능

  • Object.setPrototypeOf를 사용해서 상속을 구현할 수 있음

    Object.setPrototypeOf(Soccer, Sports)
  • ES6는 OOP 구현이 기반을 제공

  • OOP는 설계가 필요

static

  • class에 정적 메소드 선언

  • prototype에 연결되지 않고 class에 직접 연결됨

  • 인스턴스에서 접근이 불가능하고 Class 명을 통해서 접근

    classSports {
        staticgetItem() {
            return"sports";
        }
    }
    
    console.log(Sports.getItem());
  • 코딩하다보니 인스턴스로 들어갈 메소드와 static 메소드의 구분이 어려워짐.

    • 문법적인 부분은 아님
    • 그래서 static 메소드만 모아 놓은 오브젝트를 따로 만들어서 사용하는 것도 괜찮은 방법인 듯
  • static 메소드 내의 this는 해당 클래스를 나타냄

    • this로 변수에 접근하면 이는 인스턴스화되지 않는 클래스 내의 프로퍼티가 됨

hoisting

  • class는 호이스팅이 되지 않음

  • 즉, class 선언문보다 먼저 사용할 수 없음

  • 메소드명에 변수를 사용할 수 있음

    • 대괄호로 표현

      classSports {
          static ["get"+Type]() {
              
          }
      }

DOM Interface 상속

classExtendsImageextendsImage {
    constructor() {
        super();
    }
    
    setProperty() {
        this.src="file/rainbow.jpg";
        this.alt="그림 설명";
        this.title="무지개";
    }
}

constobj=newExtendsImage();
obj.setProperty();
document.querySelector("body").appendChild(obj);
  • DOM Interface를 상속받아 Custom 한 오브젝트를 만들 수 있음
  • 복잡하게 DOM 코딩하지 말고, 이런 방식으로 컴포넌트화 하면 깔끔


[리뷰] 모던 스타트업 (팀 생산성을 높여주는 21가지 도구와 서비스) - 한빛 미디어

$
0
0


나에게 도구란?

백엔드 개발을 이어오면서 회사의 규모에 상관없이 도구의 중요성은 갈 수록 더해지는 것 같다. 처음 백엔드 개발자로 일하면서는 개발에 필요한 IDE 외에는 별다른 도구를 사용하지 않았었다. 그게 불과 7년 전인데 그간 정말 많은 도구들이 생겨나고 접해보게 되었다. 그 과정에서 잘 만들어져있는 도구들을 굳이 직접 만들 필요가 없다는 것을 느끼게 되었고 많은 사람들이 개발에 참여한 오픈소스나 상용 프로그램들이 나 또는 팀에서 직접 만드는 것보다 안정적이고 퀄리티가 더 좋다고 생각한다. 그래서 현재 나는 지금 나에게 적합한, 그리고 트렌드에 맞는 도구들을 찾으려고 애쓰고 있다.


책의 공감 포인트

이런 면에서 이 책 모던 스타트업은 굉장히 큰 도움이 되었다. 책이 두껍지는 않은 만큼 최근 많이 사용되고 있고 유용한 도구들에 대해 핵심을 잘 요약했다고 생각한다. 또한 내가 스타트업에서 일 할 때나 현재 꽤 규모가 있는 회사에서 일할 때 접해보았던 대부분의 도구들이 포함되어 있었다. 책에서 포함하고 있는 도구들 중 현재 내가 사용하고 있는 도구는 슬랙, 지라, 깃허브, 포스트맨, 레스큐타임, AWS, 도커, 앤서블, 자빅스가 있다. 그만큼 실무에서 많이 사용하고 있는 도구들을 위주로 책에 담았다고 볼 수 있다.

스타트업에 있을 때는 사내 시스템이 갖춰져 있지 않기 때문에 구글의 G 스위트를 굉장히 유용하게 사용했었다. 이 책이 스타트업을 위한 책이니만큼 가장 첫번째 등장하는 것이 바로 G 스위트다. 팀 또는 사내 전체에서 슬랙을 사용하기도 했었고, 현재도 슬랙은 유용하게 사용하고 있다. 레스큐타임 같은 경우에는 회사가 아닌 개인에게도 유용한 도구인데 현재는 귀찮아서 사용안하고 있지만 한 때 효율적으로 시간관리를 하는데 도움을 얻기도 했었다.

지라의 경우 비용 문제로 스타트업에서는 사용해보지 못했지만 현재 재직 중인 회사에서는 없어서는 안될 만큼 매우 활발하게 사용하고 있다. AWS에 대해서는 너무 방대하기 때문에 이 책에 담기에는 무리라서 딱 주요 기능에 대해서만 요약 설명하고 있다.

그 외에도 모니터링부터 장애 대응을 위한 도구, 데이터 분석 도구 등을 담고 있어서 이 책을 읽어 본다면 스타트업에서 서비스를 운영할 때 유용할 수 있는 도구들에 대한 안목을 넓힐 수 있는 좋은 기회가 될 것이라 생각한다.


정리

책은 얇은데 많은 도구들을 담고 있는 만큼 디테일한 내용을 담고 있지는 않다. 사실 유용한 도구들의 존재조차 알지 못해서 비효율적으로 서비스를 운영하는 경우도 많기 때문에 이 책의 목적 중에 하나는 스타트업을 운영하는 필요한 도구들에 대해서 소개하고, 진입장벽을 낮춰주는 것이 아닐까 생각된다. 책에서 소개하는 도구들에 대한 용도를 이해하고 도입을 결정할 때 자세한 내용은 각 도구들의 레퍼런스를 참고하는 것이 좋을 것이라 생각한다.

[양재동코드랩] 자바스크립트 강의 3일차 - Number, Math

$
0
0

Number

  • 자바스크립트는 IEEE 754에 정의된 double-precision floating-point format numbers로 숫자 표시

    • 변수 생성 시 타입 지정이 없는 자바스크립트는 엔진이 알아서 소수인지 정수인지 판단
    • 64비트 유동 소수점 형태로 수를 표시
      • RGB 표현의 경우에는 1바이트만으로도 충분하게 표현이 가능한데 64비트는 8바이트이기 때문에 7바이트가 낭비됨
        • 이를 방지위해 typed array가 등장함
        • 숫자 표현의 경우에는 typed array 사용 권장
  • safe integer란

    • 지수(e)를 사용하지 않고 나타낼 수 있는 값까지만 표현
      • Number.MAX_SAFE_INTEGER
      • Number.MIN_SAFE_INTEGER
    • 2의 53승 보다 큰 값(Number.MAX_SAFE_INTEGER)의 경우 지수표현
  • Number.EPSILON : 미세한 값 차이로 인해 일치되지 않을 때 사용

    • 예를 들어 0.1 + 0.2는 0.3이어야 하는데 실제 값은 0.30000000000000004가 되어서 0.1 + 0.2 === 0.3 이 false가 됨
  • 8진수 표현시 ES5에서는 첫자리에 영문 o/O를 작성했었음. 다른 진수 표현(2진수의 경우 0b0101과 같이 표현)과 달라서 혼동되는 문제가 발생

    • ES6에서는 영문자 o/O 앞에 숫자 0 추가
  • isNaN()

    • NaN === NaN의 결과가 false
      • NaN은 자바스크립트에서 값임 (숫자가 아니라는 것을 나타내는 값)
      • 값 비교인데 false가 반환되었기 때문에 논쟁이 있었음
    • 이 후 isNaN(), Number.isNaN(), Object.is(NaN, NaN) 이 출현함
      • isNaN()은 글로벌 오브젝트
        • Number에 의존하고 있는 기능을 글로벌 오브젝트에 포함시킨 것은 잘못된 것이라 생각
      • ES6에서 Number.isNaN()이 등장
        • Number에 포함되면서 제자리를 찾음
        • 사용 권장
  • Tip

    빌트인 오브젝트에 내장된 타입들의 프로퍼티나 메소드에 대해서는 외우지말고 MDN 참조해서 필요한 것을 찾아 사용하자.
    - 각 함수 또는 메소드마다 사용 방법이나 반환 값이 미세하게 다를 수 있기 때문에 기억에 의존하지 말고, 정확히 확인해서 사용.
    
  • isInteger()

    • 소수점 뒷자리가 0인 경우에도 정수로 판단
      • 1.0도 정수로 판단
  • isSafeInteger() : 2의 53승 범위 내의 정수인지 확인

  • isFinite() : 파라미터가 유한 값인지 확인

    • 처음에 글로벌 오브젝트에 포함됨
    • ES6에서 Number의 메소드로 포함됨
      • 글로벌 오브젝트와 결과가 다름
      • Number.isFinite() 사용 권장

Math

  • Math.trunc() : 소수를 제외한 정수 반환

    • 양수이면 Math.floor()와 같음
    • 음수이면 Math.ceil()과 같음
  • 32비트 계산 관련

    • 이전에 C++로 작성된 게임의 경우에는 32비트 정수를 사용하기 때문에 자바스크립트와 호환이 되지 않아서 웹에서 동작하기가 어려웠음
    • 웹 어셈블리를 통해 C++ 바이너리를 컨버팅 할 수 있게 되면서 일부 자바스크립트로 제어가 가능해짐
    • 이 때 32비트와 맞추기 위해 Math.clz32()와 Math.fround() 사용
    • Emscripten 참고


Viewing all 302 articles
Browse latest View live