본문으로 건너뛰기

타임존 데이터 최신버전으로 업데이트하기

· 약 1분

타임존 데이터는 php의 버전을 따라 올라가는데, 실무에선 항상 최신버전을 사용하기 쉽지 않다. 그럴 때 데이터만 업데이트하는 방법을 쓸 수 있다.

설치​

2019.06 현재 최신 타임존 데이터베이스는 2019a (2019.01) 버전이다.

perl​

perl로 설치되는 일반적인 경우는 아래와 같이 쉽게 설치 가능하다.

## perl 로 설치
$ perl install timezonedb

## 끝!

phpize​

그렇지 않은 경우 라이브러리를 수동으로 빌드해줘야한다.

## 타임존 데이터 다운로드
$ curl -LO https://pecl.php.net/get/timezonedb-2019.1.tgz

## 압축 해제
$ tar -xvzf timezonedb-2019.1.tgz
$ cd timezonedb-2019.1

## 빌드
$ phpize
$ ./configure --with-php-config=${PHP_CONFIG_PATH}
$ make && make install

## 라이브러리를 extensions 폴더로 이동
$ mv timezonedb.so /usr/local/php/extentions

## 라이브러리 추가
$ vi php.ini
$ extension=timezonedb.so

## 아파치 재시작
$ apachectl restart

## 버전 확인
$ /usr/bin/php -r "echo timezone_version_get();"
2019.01

참조​

chrome usb 디버깅 장치를 못 찾거나 연결이 안될 때

· 약 1분

usb 디버깅 승인 후 chrome://inspect#devices 탭에 접근하면 기기가 보이면서 디버깅이 가능해야하는데, 그렇지 못한 경우에 아래처럼 진행해주면 된다.

해결​

먼저 usb 디버깅 권한을 모두 초기화하고 다시 연결한다.

## 디버깅 서버를 죽인 후 다시 실행한다.
$ adb kill-server
$ adb usb

개발자도구의 More tools > Remote devices의 목록에서 확인할 수 있다.

여담​

  • PTP 연결을 하라는데 이건 안드로이드 구버전에 대한거라 필요 없다.
  • 웹뷰 디버깅시에는 디버깅 권한이 있는 앱이여야 가능하다.

Kubernetes의 모든 것

· 약 4분

매번 용어 찾아보는 게 귀찮아서 정리했다. 어떤 기능을 yaml 로 구성해야하는지 감이 오는 듯하다.

노드​

  • 컨테이너가 배치되는 서버
  • 쿠버네티스 클러스터의 관리 대상으로 등록된 도커 호스트
  • 쿠버네티스 클러스터는 마스터와 노드의 그룹으로 구성
  • kubectl get nodes

마스터 노드​

  • 스웜의 매니저노드와 비슷한 느낌이다.
  • kube-apiserver: 쿠버네티스 API를 노출하는 컴포넌트 kubectl로부터 리소스를 조작하라는 지시를 받음
  • ectd: 고가용성을 갖춘 분산 Key/Value 스토어로 쿠버네티스 클러스터의 백킹스토어로 쓰인다. (CAS)
  • kube-scheduler: 노드를 모니터링하고 컨테이너를 배치할 적절한 노드탐색
  • kube-controller-manager: 리소스를 제어하는 컨트롤러 실행

일반적인 (non-managed, GKE 등이 없는) 환경에선 마스터 노드서버가 단일 장애지점 (SPOF)가 되지 않도록 마스터를 3대 두는 것이 필수다.

네임스페이스​

  • 쿠버네티스 클러스터 안의 가상 클러스터
  • 네임스페이스마다 권한을 줄 수 있으므로 개발팀이 클 때 유용하다.
  • 기본으론 default, docker, kube-public, kube-system 네임스페이스가 있다.
  • kubectl get namespace

파드​

  • 컨테이너의 집합 중 가장 작은 단위로, 컨테이너의 실행 방법을 정의
  • 적어도 하나 이상의 컨테이너로 이뤄져있다.
    • nginx reverse proxy + application web server
  • 파드는 노드 안에 들어간다.
  • 함꼐 배포해야 정합성, 일관성이 유지된다면, 같은 파드로 묶어 배포한다.
  • 파드엔 각각 고유의 가상 IP가 할당된다.
  • kubectl get pod

레플리카세트​

  • 같은 스펙을 갖는 파드를 여러 개 생성/관리 하는 역할
  • 똑같은 파드의 레플리케이션 개수를 관리하는 리소스이다.
  • 삭제된 파드는 복원할 수 없기 때문에 웹 어플리케이션과 같은 stateless 파드에 사용하기에 좋다.
  • kubectl get replicaset
  • 레플리카셋의 파드명은 container-1b2e3f 처럼 임의로 정해진다.

디플로이먼트​

  • 레플리카세트의 리비전을 관리 (빠른 버저닝)
  • 리비전은 컨테이너 이미지가 수정된 경우에 생성된다.
  • kubectl rollout history deployment ..

데몬셋​

  • 모든 시스템에서 실행되어야하는 파드와 템플릿 제공
  • 노드 당 하나

서비스​

  • 파드의 집합에 접근하기 위한 경로를 정의
  • 쿠버네티스 클러스터 내에서 파드의 집합에 대한 경로나 서비스 디스커버리를 제공하는 리소스
  • 서비스의 대상이 되는 파드는 서비스에서 정의하는 레이블 셀렉터로 정해진다.
  • 서비스의 접근은 같은 네임스페이스라면 서비스명만으로 접근이 가능하다.
    • http://${svc} => http://${svc}.default => http://${svc}.default.svc.local

ClusterIP 서비스​

  • 서비스의 기본 타입
  • 쿠버네티스 클러스터의 내부 IP 주소에 서비스를 공개할 수 있다.
  • 다른 파드 그룹으로 접근할 때 서비스를 거쳐 가도록 할 수 있으며, 서비스명으로 네임 레졸루션이 가능해진다.
  • 외부에서는 접근할 수 없다.

NodePort 서비스​

  • 쿠버네티스 클러스터 외부에서 접근할 수 있는 서비스
  • 서비스를 L4 레벨에서 노출하는 것이므로 TCP/UDP 모두 다룰 수 있다.
  • kubectl get svc ...

LoadBanlancer 서비스​

  • 로컬 쿠버네티스 환경에선 사용할 수 없다.

ExternalName 서비스​

  • 쿠버네티스 클러스터에서 외부 호스트를 네임 레졸루션하기 위한 별칭이다.
  • gracefullighut.dev 를 gracefullight 로 접근 가능하게 만든다.

인그레스​

  • 서비스를 쿠버네티스 클러스터 외부로 노출
  • NodePort 서비스는 L4라 HTTP/S의 경로기반 라우팅이 불가능한데, 인그레스는 L7 레벨로 제어가 가능하다.
  • 주로 HTTP/S 서비스를 노출하는 경우 사용한다.
  • 로컬 쿠버네티스 환경에서는 인그레스를 사용해 서비스를 노출시킬 수 없으므로 nginx ingress controller 를 사용한다.

컨피그맵​

  • 설정 정보를 정의 후 파드에 전달

퍼시스턴트볼륨​

  • 파드가 사용할 스토리지의 크기 및 종류를 정의

퍼시스턴트볼륨 클레임​

  • 퍼시스턴트 볼륨을 동적으로 확보

스토리지클래스​

  • 퍼시스턴트 볼륨이 확보하는 스토리지의 종류를 정의

스테이트풀세트​

  • 같은 스펙으로 모두 동일한 파드를 여러 개 생성하고 관리
  • 스테이트풀셋의 파드명은 backend-0, backend-1 처럼 인덱스로 정해진다.

잡​

  • 일회성 파드를 여러 개 생성하고 종료 보장

시크릿​

  • 인증 정보 등 비밀 데이터 정의

롤​

  • 네임스페이스 안에서 조작 가능한 쿠버네티스 리소스의 규칙 정의
  • 각 쿠버네티스 API 사용 권한 정의
  • 지정된 네임스페이스 내에서만 유효

롤 바인딩​

  • 쿠버네티스 리소스 사용자와 롤을 연결

RBAC​

  • Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within an enterprise.
  • Docs

권한 디버깅​

## can-i 문으로 권한 확인 가능
$ kubectl auth can-i ...

## kubectl auth can-i create deployments
## yes

클러스터롤​

  • 클러스터 전체적으로 조작 가능한 쿠버네티스 리소스의 규칙 정의
  • 클러스터 전체에서 유효

클러스터롤 바인딩​

  • 쿠버네티스 리소스 사용자와 클러스터롤을 연결
  • 일반 사용자 및 그룹/서비스 계정과 클러스터롤을 연결

서비스 계정​

  • 파드가 쿠버네티스 리소스를 조작할 때 사용하는 계정
  • 구글의 서비스 계정 비슷한 듯

노드어피니티​

  • 노드 셀렉터보다 더 세밀한 선택이 가능 (스케줄링 정책 조건 등)

테인트​

  • taint
  • 오염 조건을 추가해 노드 스케줄링에서 제외
  • 파드를 특정 노드로 강제할 수 있다.

컨트롤 플레인​

  • kubelet, kubenetes master 등 컨트롤 플레인은 시스템 내 모든 쿠버네티스 오브젝트의 레코드를 유지하면서, 오브젝트의 상태를 관리하는 제어 루프를 지속적으로 구동

Dynamic Admission Control​

  • 웹훅으로 동작
  • ValidationWebhookConfiguration
  • MutatingWebhookConfiguration

클러스터 데몬​

  • 클러스터 모든 서비스에 검사, 실행 필요시 사용

클러스터어시스턴스​

  • SSL 인증 배포 등 자동화
  • cert-manager

Mac에서 lsd 설치하기 (ls 명령어 꾸미기)

· 약 1분

lsd​

ls -al

LSDeluxe 커맨드로 ls 명령어를 예쁘게 변경해보자.

설치​

## 설치
$ brew install lsd

## alias 설정
$ vi ~/.zshrc

alias ls='lsd'
alias ll='ls -alhF'

$ source ~/.zshrc

설치는 했지만 아이콘이 깨져서 보일 것이다.

글꼴 설정​

## 글꼴 설치
$ brew tap homebrew/cask-fonts
$ brew cask install font-hack-nerd-font

iTerm2 > Preferences > Profiles > Text 탭으로 이동해 Non-ASCII Font를 확장한 뒤 방금 설치한 Hack Regular Nerd Fonr Compelete 폰트를 설정해주자.

Use ligatures 옵션 또한 체크한다.

Non-ASCII Font 설정

nodejs triple des 암호화

· 약 1분

triple des 알고리즘으로 암호화하는 일은 요새는 드문데, 드물어서 그런지 구글링해도 아무 것도 나오지 않았다.

레거시 언어들에는 3des 암호화된 로직이 많은데, 포팅하면서 개발해야될 필요성이 생겼다.

암호화​

  • nodejs 의 암호화 패키지는 crypto 안에 들어있다.
  • crypto.getCiphers(); 메소드로 사용할 수 있는 알고리즘을 확인할 수 있다.
    • ciphers.filter(cipher => cipher.includes('des'));
  • 3des 알고리즘은 des-ede3 로 시작한다.
  • 키는 24bytes 이며, iv 는 8bytes 다.

소스​

import crypto from "node:crypto";

class TripleDes {
// #iv;
// #key;

constructor(key, iv) {
this.key = key;
this.iv = iv;
}

getKey() {
return this.key.padEnd(24, String.fromCharCode(0));
}

getIv() {
return this.iv;
}

encrypt(plain, iv) {
if (!iv) {
iv = this.iv ? Buffer.from(this.iv, "hex") : crypto.randomBytes(8);
}

const cipher3des = crypto.createCipheriv(
"des-ede3-cfb8",
this.getKey(),
iv,
);
let encrypted = cipher3des.update(plain, "utf8", "hex");
encrypted += cipher3des.final("hex");

this.iv = iv.toString("hex");
return encrypted;
}

decrypt(encrypted, iv) {
if (!iv) {
iv = this.iv;
}

const decipher3des = crypto.createDecipheriv(
"des-ede3-cfb8",
this.getKey(),
Buffer.from(iv, "hex"),
);
let decrypted = decipher3des.update(encrypted, "hex", "utf8");
decrypted += decipher3des.final("utf8");

return decrypted;
}
}

module.exports = TripleDes;

위의 encrypt 메소드는 아래처럼 버퍼를 합친 후에 헥스로 바꾸는 것과 동일하다.

let encrypted = Buffer.concat([
cipher3des.update(plain, "utf8"),
cipher3des.final("utf8"),
]);

encrypted = encrypted.toString("hex");

사용법​

const TripleDes = require("./TripleDes");

const tripleDes = new TripleDes("encryptionKey");
// 암호화
const encrypted = tripleDes.encrypt("yummy");
// 복호화
const decrypted = tripleDes.decrypt(encrypted);

참조​

DEV 도메인과 Github Pages 연결하기

· 약 2분

도메인 구입​

Google Domains 에서 구입한다.

  • 미국 주소가 있어야된다는 말이 나오는데, 무시하고 구매하기를 하자.
  • 도메인 기관에서 연락이 갈 수 있다는 내용에 주소를 대한민국으로 변경 후 입력해주고
  • 결제지 우편번호는 대충 쓰고 미국으로 하고 넘어가면 쉽게 구매할 수 있다.

DNS 설정​

DNS > 맞춤 리소스 레코드 탭에서 Github Domain IP 를 A 레코드로 추가한다.

  • 185.199.108.153
  • 185.199.109.153
  • 185.199.110.153
  • 185.199.111.153

http 예외​

http 로 서빙하는 경우가 있다면 www.gracefullight.dev 도 사용할 수 있게 CNAME 을 등록하자.

www CNAME 1h gracefullight.github.io.

물론 HSTS 를 적용하는 경우 필요없다.

CNAME 파일 추가​

public 경로 아래 CNAME 파일을 생성 후 도메인을 적는다.

CNAME
gracefullight.dev

github pages 설정​

github.com/gracefullight/gracefullight.github.io/settings 의 GitHub Pages 탭으로 이동해 Custom domain 에 설정할 도메인을 넣고, Enforce HTTPS 를 체크한다.

아래 문구가 보이면 성공한 것이다.

Your site is published at https://gracefullight.dev

웹마스터 설정​

Google​

구글 검색에서 도메인 변경을 다시 인덱싱해주기 위해 마이그레이션이 필요하다. 새로운 사이트를 등록해 준 뒤 이전 버전의 서치콘솔에서 기존 사이트의 설정버튼을 누르면 바로 설정이 가능하다.

주소 변경

기타​

네이버와 빙은 같은 site verification 코드가 나와서 사이트만 추가해주면 되는데, 얀덱스의 경우는 코드를 변경하고 빌드해줘야한다.

여담​

  • CNAME 변경하면 Github setting의 Custom domain이 빠지기 때문에 다시 등록해줘야하는 번거로움이 있다.
  • 애널리틱스는 도메인만 바꿔주면 바로 연동된다.
  • 애드센스가 도메인 승인이 2주가 걸려서 그전까진 애드-프리 사이트가 될 듯하다.

RxJS의 모든 것

· 약 7분

RxJS​

리액티브 프로그래밍은 개인적으로 비동기 프로그래밍과 함수형 프로그래밍의 종착지라고 생각한다. Stage1 Draft로 제안되어 더 이상 피할 수 없는 Observable 을 알아보자.

정의​

위키피디아에선 다음과 같이 정의되어있다.

reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. 데이터 스트림과 변화의 전파에 중점을 둔 프로그래밍 패러다임

2011년 태초에 RX 의 개념을 만든 MS 문서에서는 Reactive eXtensions 다음과 같다.

Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. 옵져버블 시퀀스와 링큐 쿼리 연산자를 사용하는 비동기, 이벤트 기반 프로그래밍 라이브러리

조금 더 디테일하게 말하면 데이터스트림을 Pulling 방식의 이터레이터 패턴인 IEnumerable<T>/IEnumerator<T> 로 만들어 Pushing 방식의 옵저버 패턴인 IObservable<T>/IObserver<T>로 전파/구독하는 것이다.

Rx를 모두 이해한 뒤 이 정의를 보면 어쩜 이렇게 깔끔하게 한 줄로 이 내용을 다 담았을까? 란 생각이 드는데, 처음보는 입장에선 비동기인 건 알겠네 정도로만 이해가 되는 듯하다. 어려운 게 당연하다. 멀티쓰레드 프로그래밍을 처음 배울 때의 감정을 생각해보자.

용어​

  • 비동기 프로그래밍: 이 문서를 볼 정도면 설명이 필요 없을 것 같다.
  • 함수형 프로그래밍: 함수를 FirstClass 로 취급해 파라미터, 리턴, 변수에 할당 가능하며 함수 합성이 가능하고 원하는 시점에 호출이 쉽다.
  • 데이터스트림: file stream, event stream, http stream 의 친구로 data 식 표현
  • 이터레이터: 설명 필요 없을 듯.
  • 옵져버: 디자인패턴 책의 옵져버패턴 단원 참조, 자바스크립트에선 이미 이벤트리스너가 옵져버 패턴이다.
  • 옵져버블: 특정 객체를 관찰하는 옵저버에게 여러 이벤트나 값을 전파하는 역할
  • 풀: 데이터를 받을지 결정
  • 푸쉬: 데이터를 보낼지 결정
  • 싱글: 하나의 값이나 이벤트를 다룸
  • 멀티플: 여러 개의 값이나 이벤트를 다룸

이 모든 용어를 하나의 표로 정리하면 다음과 같다.

싱글멀티플
풀함수이터레이터
푸쉬프로미스옵져버블

패턴​

  • 옵져버블의 변수 스타일은 이름 뒤에 const click$ 처럼 $을 뒤에 붙혀주는게 정형화되어있다.
  • subscribe 는 next, error, complete를 파라미터로 받는다.

마블 다이어그램​

마블 다이어그램은 연산자를 쉽게 이해하기 위해, 옵져버블을 테스트하기위해 도식화된 다이어그램이다. 타이밍과 값의 변화를 한 눈에 파악할 수 있다.

map

  • 위에 줄은 input 이고 연산자를 만나고나면 아래 줄에서 output 이 어떻게 바뀌는지 보여진다.
  • 가로 줄은 하나의 옵져버블이다.
  • 줄의 | 파이프는 구독 완료를 나타낸다.

소스​

생성​

가장 간단한 옵져버블을 만들고 확인해보자.

const { Observable } = require("rxjs");

const test$ = Observable.create((observer) => {
console.log("create");
observer.next(1);
observer.next(2);
observer.complete();
console.log("done");
});

test$.subscribe(
(item) => {
console.log(item);
},
(error) => {},
() => {
console.log("complete");
},
);

/*
create
1
2
complete
done
*/

간단하지만 이터레이터이면서 구독가능하다는 걸 확인할 수 있다.

구독해제​

옵져버블의 리턴함수로 구독해제 콜백을 지원한다, 콜백이 필요하지 않다면 unsubscribe() 를 호출해주기만 하면 된다.

const test$ = Observable.create((observer) => {
const interval = setInterval(() => {
console.log("test");
}, 1000);

return () => {
clearInterval(interval);
};
});

const subscription = test$.subscribe();
subscription.unsubscribe();

파이퍼블 연산자​

Pipeable 연산자는 옵져버블 인스턴스를 pipe 함수 안에서 다룰 수 있는 연산자이다. 기본적으로 rxjs/operators 라이브러리 안에 들어있다.

const { map } = require("rxjs/operators");

const test$ = Observable.create((observer) => {
observer.next(1);
observer.next(2);
observer.complete();
});

test$.pipe(map((value) => value * 2)).subscribe((item) => console.log(item));

/*
2
4
*/

소스 옵져버블에서 발행된 값을 원하는대로 바꿀 수 있다.

연산자​

of​

args 순서대로 값을 반환한다.

from​

  • Observable
  • Array
  • Promise
  • Iterable
  • String
  • ArrayLike

위 타입을 옵져버블로 변환해준다.

fromEvent​

EventEmitter 클래스의 객체와 조합하거나 브라우저의 이벤트를 옵져버블로 바꿀 때 사용한다.

defer​

팩토리 함수로 옵져버블을 생성한 후 구독한느 시점에 팩토리 함수를 호출해 이미 생성한 옵져버블을 리턴받아 구독한다.

from 과의 차이는 다름과 같다.

  • from
    • 프로미스 내부 구현부가 언제 실행되던지 상관 없을 때
    • 이미 실행 중이거나 완료한 프로미스를 옵져버블로 만들 때
  • defer
    • 옵져버블을 구독하는 시점에 프로미스를 생성해 구현부가 실행되어야할 때
    • 프로미스 객체 생성 시점이 구독하는 시점이여야할 때

range​

범위 지정 후 그 값을 순서대로 발행한다. 반복문이 필요할 때 사용된다.

interval​

ms 단위로 값을 발행한다.

timer​

파라미터가 하나일 경우 ms 이후에 한 번 값을 발행하고, 두 개일 경우 ms 이후에 두번 째 파라미터만큼 주기적으로 값을 발행한다.

empty​

값 발행 후 중간에 멈춰야하는 상황에 사용한다. 이 함수만 사용하지는 않고 다른 함수나 연산자와 조합해서 complete 함수를 호출해야 할 때 사용된다. 즉, 바로 구독을 완료해야될때 사용된다.

// 상수로 사용된다.
const { EMPTY } = require("rxjs");

never​

아무 것도 하지 않고 옵져버블 생성이 필요할 때 사용된다.

// 상수로 사용된다.
const { NEVER } = require("rxjs");

throwError​

옵져버블로 값을 발행하다가 에러를 발생시키고 종료해야하는 상황에 사용한다.

filter​

주로 파이퍼블 연산자와 연결해서 사용된다.

const { filter } = require("rxjs/operators");
// 1~10 중 짝수 필터
range(1, 10)
.pipe(filter((x) => x % 2 === 0))
.subscribe((x) => console.log(x));

first​

처음으로 일치하는 값을 발행한다. 두 번째 인자로 기본 값을 줄 수 있다.

last​

마지막으로 일치하는 값을 발행한다. 두 번째 인자로 기본 값을 줄 수 있다.

take​

정해진 갯수만큼 구독하고 구독을 해제한다. interval 과 같이 무한 반복이 실행되는 연산자와 같이 쓰면 된다.

const { take } = require("rxjs/operators");

interval(1000)
.pipe(take(3))
.subscribe((x) => console.log(x));

/*
0
1
2
*/

takeUntil​

특정 이벤트가 발생할 때까지 옵져버블을 구독해야할 때 사용한다. 예시로 보는 게 빠르다.

interval(1000)
.pipe(
take(100),
takeUntil(fromEvent(document.querySelector("#btn"), "click")),
)
.subscribe((x) => console.log(x));

takeWhile​

take 와 filter 가 합쳐진 연산자이다.

interval(1000)
.pipe(takeWhile((x) => x <= 10))
.subscribe((x) => console.log(x));

takeLast​

Last 의 파라미터 수 만큼 저장해뒀다가 구독 완료시에 일괄적으로 발행한다. 발행하는 값이 [0, 2, 4, 6, 8, 10] 일 때 takeLast(3) 일 경우 내부에 저장된 값은 다음과 같다.

발행값내부배열
0[0]
2[0, 2]
4[0, 2, 4]
6[6, 2, 4]
8[6, 8, 4]
10[6, 8, 10]

skip​

이름 그대로 n 개만큼의 발행을 건너 뛴다.

skipUntil​

takeUntil 과의 반대로 옵져버블이 실행될 때까지 건너 뛴다.

const time = 1000;
interval(time)
.pipe(skipUntil(interval(time * 5)), take(2))
.subscribe((x) => console.log(x));

/*
4
5
*/

skipWhile​

조건을 만족하지 않는 순간부터 값을 발행한다.

debounce​

많이 사용하진 않지만, 디바운스할 옵져버블을 계산값을 리턴해주는 함수를 파라미터로 주면 된다.

debounceTime​

로대쉬나 다른 라이브러리의 debounce 와 같다.

distinct​

중복은 제거하고 발행한다. 값 비교에는 === 연산자가 사용된다.

of({ id: 1 }, { id: 1 }, { id: 2 }, { id: 3 })
.pipe(
distinct((data) => data.id),
map((data) => data.id),
)
.subscribe((x) => console.log(x));

/**
1
2
3
*/

두번째 파라미터로 flush 조건을 옵져버블로 넘겨 중복 조건을 초기화시킬 수 있다.

distinctUntilChanged​

중복값이 연속으로 발행된 경우만 제거한다.

of(1, 2, 3, 3, 4, 1)
.pipe(distinctUntilChanged())
.subcribe((x) => console.log(x));

/**
1
2
3
4
1
*/

첫번째 파라미터로는 비교함수 (prev, next 를 파라미터로 받는)를 넣어 연속 비교조건을 변경할 수 있다.

두번째 파라미터로는 비교할 값 셀렉터를 변경해줄 수 있다.

of(
{ a: 1, b: 10 },
{ a: 1, b: 10 },
{ a: 2, b: 20 },
{ a: 3, b: 30 },
{ a: 3, b: 30 },
{ a: 2, b: 20 },
)
.pipe(distinctUntilChanged(null, (data) => data.a))
.subscribe((x) => console.log(x));

sample​

이건 마블 다이어그램으로 이해하는 게 빠르다.

sample

notifier 옵져버블(x 옵져버블)이 발행되면 이전 최근 값을 발행한다. 값 c 처럼 소스 옵져버블에 새로운 값이 없을 경우 값을 중복으로 발행하지 않는다.

sampleTime​

일정 간격 사이에 있는 최근 값을 발행한다.

pluck​

기본 map 연산자는 뻔하기에 적지 않았다. map 처럼 동작하지만 소스 옵져버블에서 객체를 리턴할 때 객체의 property 를 뽑아낸다.

mergeMap​

switchMap​

concatMap​

scan​

partition​

groupBy​

buffer​

bufferCount​

window​

windowCount​

여담​

  • RxJS 의 장점은 이벤트 구독을 취소하고, 모든 이벤트를 하나의 스트림으로 제어할 수 있는 것이라고 생각한다.
  • 이해하는데 상당한 기간이 소요되었지만, 웹에선 다음과 같은 상황에서 사용될 것 같다.
    • SPA
    • 에디터와 같이 많은 컴포넌트끼리 통신이 필요할 때
  • 이 상황에서도 앵귤러가 아닌 스토어 기반의 리액트, 뷰를 사용한다면 RxJS를 쓸거야 라고 보여주기 위해 사용하는 느낌이 든다.
  • 모두가 같이 가기엔 꽤 어려운 개념이다.
  • 싱글 스레드 기반의 백엔드에서 더 유용하게 쓰일 수 있을 듯
  • learn-rxjs의 예제를 분석하는 게 더 많은 도움이 될 것 같다.

Ansible 정리

· 약 2분

앤서블​

서버 구성 관리 자동화 프로그램

용어​

  • 애드훅: 일회성 실행
  • 플레이북: yaml 로 재사용

CMDB​

  • Configuration Management DataBase (구성 관리 데이터베이스)
  • 분산 설치된 서버, 스토리지, 네트워크 장비 등의 CPU, 메모리, 디스크 등의 하드웨어 정보와 OS, 응용프로그램 종류 등의 소프트웨어 정보 관리
  • 다양한 시스템의 정보를 한 번에 파악 가능하고, 각 장비 간 연관 관계 파악이 쉬워 예방, 장애 파악, 유지보수에 좋음
  • 앤서블 CMDB 는 앤서블 호스트 파일 또는 지정된 인벤토리를 통해 설정 관리 대상 시스템의 정보를 setup 모듈을 통해 수집, 저장
  • 앤서블 vault 로 중요 정보 암호화 가능
  • Device42, servicenow 등이 지원
  • ansible-cmdb 를 사용해 간단한 대시보드로 볼 수 있다.

최적화​

pipelining​

  • 앤서블 실행 단계를 한 단계로 줄일 수 있음
  • 플레이북 내에서는 ansible_ssh_pipelining 으로 설정 가능
  • /etc/ansible/ansible.cfg 에서 설정 가능

forks​

  • 한 번에 실행할 수 있는 노드의 수 제어 (default: 5)
  • serial 은 단순히 전체 노드에서 어느 정도를 실행할 것인지를 설정하는 거라 forks 와는 다르다.

async​

  • 태스크 별 지정된 주기에 체크하고 모든 노드의 동일 태스크가 완료되어야 다음 태스크 실행
  • 주기를 0으로 설정하면 상태 체크 없이 다음 태스크가 진행된다.

strategy free​

  • 상태 체크는 하지만 다른 노드의 진행을 기다리지 않고 각 노드 간 독립적으로 태스크 진행
  • 빠르게 작업이 끝나는 노드가 생기므로 forks 수의 제한을 가능한 늘려줘야한다.

설정 확인​

ansible-config dump --only-changed 로 변경된 설정 확인 가능

블록​

  • with_items, with_nested, with_dict, with_file을 사용해 여러 개의 태스크를 한 번에 실행 가능

fact_caching​

  • 기본적으로 앤서블의 fact 는 실행 시마다 수집되는데, 이 옵션을 설정으로 디스크 또는 메모리에 캐싱해 fact 수집 시간을 단축시킴
  • 기본 값은 24시간

ansible-vault​

  • 평문 파일을 aes256으로 암복호화

no_log​

  • 플레이북 실행시 -v, -vv, -vvv의 옵션으로 암호화 파일의 유추가 가능한데, no_log 옵션을 줘서 해결 가능

여담​

  • 설정이 많고, 모듈도 많아서 사용할 때 홈페이지를 참고해가며 삽질해야할 듯
  • Docker 설치 전 물리적인 서버의 설정을 맞춰줄 때나 디버깅할 때 필수로 보인다.
  • 시스템 관리자가 아니거나 AWS 쓴다면 사용할 일이 거의 없을 것 같다.
  • AWX로 Web 콘솔에서 관리가 가능하다.

Mac 에서 포트 접근 확인하기

· 약 1분

윈도우의 경우에는 telnet-client 를 켜주면 되고, 맥에서도 brew 로 설치하면 되지만 더 좋은 내장 툴이 있다.

네트워크 유틸리티​

네트워크 유틸리티 > 포트 스캔 을 사용하자.

Network Utility

IP 를 입력하고 테스트할 포트 범위를 입력 후 스캔 버튼을 누르면 된다.

Ping, DNS, Netstat, Traceroute (MTR), Whois 등의 기능이 있어서 유용하게 사용할 것 같다.

jQuery 프로토타입 오염 취약점 (CVE-2019-11358)

· 약 1분

프로토타입 오염​

이 공격은 JSON.parse 와 Object.deepCopy 를 사용하는 모든 로직에서 발생할 수 있다.

2019-04-17 에 발표된 이 취약점은 $.extends 구문 상에서 프로토타입 XSS 공격이 가능하다.

예시​

const myObject = '{"myProperty" : "a", "__proto__" : { "isAdmin" : true } }';
const newObject = $.extend(true, {}, JSON.parse(myObject));
console.log(newObject.isAdmin); // true

const temp = {};
console.log(temp.isAdmin); // true

패치​

취약점 패치는 아래 부분을 변경해준다.

jQuery.extend = jQuery.fn.extend = function () {
// 사이 로직 생략
for (; i < length; i++) {
if ((options = arguments[i]) != null) {
for (name in options) {
src = target[name];
copy = options[name];

// [AS-IS]
// if (target === copy) {
// [TO-BE]
if (name === "__proto__" || target === copy) {
continue;
}

if (
deep &&
copy &&
(jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))
) {
if (copyIsArray) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}

target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}

return target;
};