맥 구글 계정 연동시 인증 실패
맥에서 구글 계정 연동 중에 Google 계정 인증에 실패했다는 오류메세지가 나온다면 다음과 같이 해결하면 된다.
해결
설정 > 일반 > 기본 웹브라우저를 Safari.app으로 변경한다.
구글링시 아래 기능들을 해제해보라는데 다 쓸모없다.
- 2차 인증 해제
- 앱 비밀번호 사용
- 보안되지 않은 앱 허용
맥에서 구글 계정 연동 중에 Google 계정 인증에 실패했다는 오류메세지가 나온다면 다음과 같이 해결하면 된다.
설정 > 일반 > 기본 웹브라우저를 Safari.app으로 변경한다.
구글링시 아래 기능들을 해제해보라는데 다 쓸모없다.
타임존 데이터는 php의 버전을 따라 올라가는데, 실무에선 항상 최신버전을 사용하기 쉽지 않다. 그럴 때 데이터만 업데이트하는 방법을 쓸 수 있다.
2019.06 현재 최신 타임존 데이터베이스는 2019a (2019.01) 버전이다.
perl로 설치되는 일반적인 경우는 아래와 같이 쉽게 설치 가능하다.
## perl 로 설치
$ perl install timezonedb
## 끝!
그렇지 않은 경우 라이브러리를 수동으로 빌드해줘야한다.
## 타임존 데이터 다운로드
$ 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
usb 디버깅 승인 후 chrome://inspect#devices 탭에 접근하면 기기가 보이면서 디버깅이 가능해야하는데,
그렇지 못한 경우에 아래처럼 진행해주면 된다.
먼저 usb 디버깅 권한을 모두 초기화하고 다시 연결한다.
## 디버깅 서버를 죽인 후 다시 실행한다.
$ adb kill-server
$ adb usb
개발자도구의 More tools > Remote devices의 목록에서 확인할 수 있다.
매번 용어 찾아보는 게 귀찮아서 정리했다. 어떤 기능을 yaml 로 구성해야하는지 감이 오는 듯하다.
kubectl get nodes일반적인 (non-managed, GKE 등이 없는) 환경에선 마스터 노드서버가 단일 장애지점 (SPOF)가 되지 않도록 마스터를 3대 두는 것이 필수다.
default, docker, kube-public, kube-system 네임스페이스가 있다.kubectl get namespacekubectl get podkubectl get replicasetkubectl rollout history deployment ..http://${svc} => http://${svc}.default => http://${svc}.default.svc.localkubectl get svc ...## can-i 문으로 권한 확인 가능
$ kubectl auth can-i ...
## kubectl auth can-i create deployments
## yes

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 옵션 또한 체크한다.

triple des 알고리즘으로 암호화하는 일은 요새는 드문데, 드물어서 그런지 구글링해도 아무 것도 나오지 않았다.
레거시 언어들에는 3des 암호화된 로직이 많은데, 포팅하면서 개발해야될 필요성이 생겼다.
crypto.getCiphers(); 메소드로 사용할 수 있는 알고리즘을 확인할 수 있다.
ciphers.filter(cipher => cipher.includes('des'));des-ede3 로 시작한다.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);
Google Domains 에서 구입한다.
DNS > 맞춤 리소스 레코드 탭에서 Github Domain IP 를 A 레코드로 추가한다.
http 로 서빙하는 경우가 있다면 www.gracefullight.dev 도 사용할 수 있게 CNAME 을 등록하자.
www CNAME 1h gracefullight.github.io.
물론 HSTS 를 적용하는 경우 필요없다.
public 경로 아래 CNAME 파일을 생성 후 도메인을 적는다.
gracefullight.dev
github.com/gracefullight/gracefullight.github.io/settings 의 GitHub Pages 탭으로 이동해 Custom domain 에 설정할 도메인을 넣고, Enforce HTTPS 를 체크한다.
아래 문구가 보이면 성공한 것이다.
Your site is published at
https://gracefullight.dev
구글 검색에서 도메인 변경을 다시 인덱싱해주기 위해 마이그레이션이 필요하다. 새로운 사이트를 등록해 준 뒤 이전 버전의 서치콘솔에서 기존 사이트의 설정버튼을 누르면 바로 설정이 가능하다.

네이버와 빙은 같은 site verification 코드가 나와서 사이트만 추가해주면 되는데, 얀덱스의 경우는 코드를 변경하고 빌드해줘야한다.
리액티브 프로그래밍은 개인적으로 비동기 프로그래밍과 함수형 프로그래밍의 종착지라고 생각한다. 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를 모두 이해한 뒤 이 정의를 보면 어쩜 이렇게 깔끔하게 한 줄로 이 내용을 다 담았을까? 란 생각이 드는데, 처음보는 입장에선 비동기인 건 알겠네 정도로만 이해가 되는 듯하다. 어려운 게 당연하다. 멀티쓰레드 프로그래밍을 처음 배울 때의 감정을 생각해보자.
이 모든 용어를 하나의 표로 정리하면 다음과 같다.
| 싱글 | 멀티플 | |
|---|---|---|
| 풀 | 함수 | 이터레이터 |
| 푸쉬 | 프로미스 | 옵져버블 |
const click$ 처럼 $을 뒤에 붙혀주는게 정형화되어있다.subscribe 는 next, error, complete를 파라미터로 받는다.마블 다이어그램은 연산자를 쉽게 이해하기 위해, 옵져버블을 테스트하기위해 도식화된 다이어그램이다. 타이밍과 값의 변화를 한 눈에 파악할 수 있다.

가장 간단한 옵져버블을 만들고 확인해보자.
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
*/
소스 옵져버블에서 발행된 값을 원하는대로 바꿀 수 있다.
args 순서대로 값을 반환한다.
위 타입을 옵져버블로 변환해준다.
EventEmitter 클래스의 객체와 조합하거나 브라우저의 이벤트를 옵져버블로 바꿀 때 사용한다.
팩토리 함수로 옵져버블을 생성한 후 구독한느 시점에 팩토리 함수를 호출해 이미 생성한 옵져버블을 리턴받아 구독한다.
from 과의 차이는 다름과 같다.
범위 지정 후 그 값을 순서대로 발행한다. 반복문이 필요할 때 사용된다.
ms 단위로 값을 발행한다.
파라미터가 하나일 경우 ms 이후에 한 번 값을 발행하고, 두 개일 경우 ms 이후에 두번 째 파라미터만큼 주기적으로 값을 발행한다.
값 발행 후 중간에 멈춰야하는 상황에 사용한다. 이 함수만 사용하지는 않고 다른 함수나 연산자와 조합해서 complete 함수를 호출해야 할 때 사용된다. 즉, 바로 구독을 완료해야될때 사용된다.
// 상수로 사용된다.
const { EMPTY } = require("rxjs");
아무 것도 하지 않고 옵져버블 생성이 필요할 때 사용된다.
// 상수로 사용된다.
const { NEVER } = require("rxjs");
옵져버블로 값을 발행하다가 에러를 발생시키고 종료해야하는 상황에 사용한다.
주로 파이퍼블 연산자와 연결해서 사용된다.
const { filter } = require("rxjs/operators");
// 1~10 중 짝수 필터
range(1, 10)
.pipe(filter((x) => x % 2 === 0))
.subscribe((x) => console.log(x));
처음으로 일치하는 값을 발행한다. 두 번째 인자로 기본 값을 줄 수 있다.
마지막으로 일치하는 값을 발행한다. 두 번째 인자로 기본 값을 줄 수 있다.
정해진 갯수만큼 구독하고 구독을 해제한다. interval 과 같이 무한 반복이 실행되는 연산자와 같이 쓰면 된다.
const { take } = require("rxjs/operators");
interval(1000)
.pipe(take(3))
.subscribe((x) => console.log(x));
/*
0
1
2
*/
특정 이벤트가 발생할 때까지 옵져버블을 구독해야할 때 사용한다. 예시로 보는 게 빠르다.
interval(1000)
.pipe(
take(100),
takeUntil(fromEvent(document.querySelector("#btn"), "click")),
)
.subscribe((x) => console.log(x));
take 와 filter 가 합쳐진 연산자이다.
interval(1000)
.pipe(takeWhile((x) => x <= 10))
.subscribe((x) => console.log(x));
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] |
이름 그대로 n 개만큼의 발행을 건너 뛴다.
takeUntil 과의 반대로 옵져버블이 실행될 때까지 건너 뛴다.
const time = 1000;
interval(time)
.pipe(skipUntil(interval(time * 5)), take(2))
.subscribe((x) => console.log(x));
/*
4
5
*/
조건을 만족하지 않는 순간부터 값을 발행한다.
많이 사용하진 않지만, 디바운스할 옵져버블을 계산값을 리턴해주는 함수를 파라미터로 주면 된다.
로대쉬나 다른 라이브러리의 debounce 와 같다.
중복은 제거하고 발행한다. 값 비교에는 === 연산자가 사용된다.
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 조건을 옵져버블로 넘겨 중복 조건을 초기화시킬 수 있다.
중복값이 연속으로 발행된 경우만 제거한다.
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));
이건 마블 다이어그램으로 이해하는 게 빠르다.

notifier 옵져버블(x 옵져버블)이 발행되면 이전 최근 값을 발행한다. 값 c 처럼 소스 옵져버블에 새로운 값이 없을 경우 값을 중복으로 발행하지 않는다.
일정 간격 사이에 있는 최근 값을 발행한다.
기본 map 연산자는 뻔하기에 적지 않았다. map 처럼 동작하지만 소스 옵져버블에서 객체를 리턴할 때 객체의 property 를 뽑아낸다.
서버 구성 관리 자동화 프로그램
ansible-config dump --only-changed 로 변경된 설정 확인 가능
with_items, with_nested, with_dict, with_file을 사용해 여러 개의 태스크를 한 번에 실행 가능aes256으로 암복호화-v, -vv, -vvv의 옵션으로 암호화 파일의 유추가 가능한데, no_log 옵션을 줘서 해결 가능윈도우의 경우에는 telnet-client 를 켜주면 되고, 맥에서도 brew 로 설치하면 되지만 더 좋은 내장 툴이 있다.
네트워크 유틸리티 > 포트 스캔 을 사용하자.

IP 를 입력하고 테스트할 포트 범위를 입력 후 스캔 버튼을 누르면 된다.
Ping, DNS, Netstat, Traceroute (MTR), Whois 등의 기능이 있어서 유용하게 사용할 것 같다.