본문으로 건너뛰기

Full Stack JavaScript Developer | Half-time Open Sourcerer.

View All Authors

DEV 도메인과 Github Pages 연결하기

· 약 3분

도메인 구입

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의 모든 것

· 약 14분

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$ 처럼 $을 뒤에 붙혀주는게 정형화되어있다.
  • subscribenext, 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 정리

· 약 4분

앤서블

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

용어

  • 애드훅: 일회성 실행
  • 플레이북: 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)

· 약 2분

프로토타입 오염

이 공격은 JSON.parseObject.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;
};

NARS 직구하기

· 약 5분

NARS Cosmetics

François Nars가 런칭한 프랑스발 유명 색조 브랜드 나스는 남자라도 갖고 싶게하는 매력있는 디자인을 가진 제품들이 많다. 한국에 들어오면 1.5배정도 가격이 뛰던지, 한정판은 들어오자마자 못 구한다. 이번 2019 Orgasm Collection 도 마찬가질 것 같다.

2019 Orgasm Collection

특히 립스틱 4개를 모아야 하나의 NARS 문구로 완성 되는 건 상술이지만 아름답다.

이름의 유래

나스 화장품 명칭은 대부분 섹슈얼한데 주된 명칭은 다음과 같다.

  • 오르가즘 '99 에 런칭된 베스트셀러이다.
  • 슈퍼오르가즘
  • 섹스어필
  • 섹스판타지
  • 쓰리썸
  • 게이어티
  • 패티쉬즈드
  • 딥쓰롯

왜 이런 이름을 지었는지에 대해 궁금해져서 여성도 성에 당당해지자 라는 카더라가 있는데, 카더라는 꺼무위키처럼 제껴두고 나스가 보그와 한 인터뷰를 확인해보자.

Q: I think it’s safe to say your products have some of the all-time best names—Orgasm, Jungle Red, Belle de Jour, Chinatown, Schiap. How do you name them?

네이밍은 상품에 개성을 부여하는 것과 같다고 생각했기에, 여성들이 뭔가 특별한 것을 입고 있는 것처럼 느끼게 하고 싶었습니다. 메이크업은 섹시 악세사리라고 생각하기 때문에 재밌있게, 섹시하게 만들길 원했습니다. 여성들은 섹시해보기 위해, 섹시함을 느끼기 위해 이걸 사용합니다.

A: You know, I always said we were either going to give them numbers, or interesting names. I didn’t want to go with boring names like Pink Peach. It had to be almost like giving each product its own personality, so that women could dream, could connect to the product, could feel like they were wearing something special. I wanted it to be fun. And sexy, too. Because makeup is a sexy accessory—women use it to look sexy and to feel sexy, for themselves and for the world. It gets difficult, because, you can imagine, we’ve been copied a lot. I have those little Hermès agendas, and 24 hours a day I keep writing names that come to my mind from books that I read, exhibits I go to, movies, places. I keep writing names. That’s how it works. I usually think about the name first, and then a color matches that name. Nothing is really planned.

구매하기

공홈

NARS 공식 홈페이지는 한국인이 한국카드를 쓰거나 배대지를 이용할 경우 구매 취소가 되어버린다. 이 것에 대해 카드, 페이팔, 배대지 변경까지 세 번의 도전을 모두 실패하고 결국 문의를 해서 이런 답변을 받았다.

Please be aware that at this time NARScosmetics.com can only bill and ship within the U.S. and both billing and shipping must be made to a U.S. residential address.

의역하면 응 안돼~ 가 되시겠다. 사은품도 많고, 정품 패키지도 엄청 예쁜데 구매하려면 미국 친구나 미국 공구를 알아봐야한다.

이 문제는 나에게 High Priority 이슈로 등록되어서 대안을 마련해야만 했다.

Sephora

편집숍인 세포라엔 나스 신상이 바로바로 입고되고 있었다.

가입 후 배대지 주소를 넣어주고, 오르가즘 컬렉션을 추가한 뒤 사은품도 꼭꼭 챙기자.

블러셔
Orgasm Blush JumboOrgasm Afterglow Lip Balm
Endless Orgasm PaletteOrgasm Oil-Infused Lip Tint

자주 이용할 듯 싶다.

여담

  • 도착하면 이 컬렉션을 한국의 어떤 뷰티 유튜버보다도 빨리 리뷰할 수 있을 것 같다.
  • 시놀로지 나스도 구매도 해야하는데

Pi-hole과 라즈베리파이로 모든 광고 차단하기

· 약 5분

앞서

유튜브 광고가 많아졌다. 명색이 프로그래머인데 AdGuard를 정액제로 사용하고 싶지 않았다. 이 문서엔 없지만 피캡을 구현해보고 싶었다.

기기 구매

라즈베리파이3 B+ 스타터킷을 구매한다. 아래 재료가 있을 경우 빼고 구매해도 된다.

  • SD 카드 리더기
  • 5핀 충전기
  • 라즈베리파이용 방열판
  • 라즈베리파이 케이스

환경설정

포맷

SD 카드를 비워주기위해 파워쉘이나 CMD를 관리자 모드로 접근한다.

PS > diskpart

DISKPART > list disk
DISKPART > select disk ${SD_CARD_DRIVE_NUMBER}

DISKPART > clean

라즈비안

운영체제인 Raspbian Stretch Lite 버전을 다운로드한다. 데비안 기반이다.

etcher

컴퓨터에 SD 카드용 이미지 굽는 프로그램을 설치한다.

다운로드된 라즈비안과 etcher로 이미지를 굽는다. 직관적이라 1, 2, 3 선택하듯이 설치하면 된다. 도중에 깨질 경우 다시 포맷한다.

ssh 접근

이미지 설치 후 다시 USB 를 인식시키면 Boot drive 가 보이는데 접근 후 루트에 ssh 란 이름의 터치파일을 만들어준다.

touch ssh

이 파일이 있어야 ssh 접근이 가능하다. 공유기에 꼽고 내부아이피 확인하자.

## ssh 접근
$ ssh pi@YOUR_RASPBERRY_INTERAL_IP

## 초기 비밀번호는 raspberry 이다.

패키지 설치

부팅 후 apt-get 을 사용할 시간이다.

sudo apt-get upgrade -y && sudo apt-get update

vim

vim 도 없다.

sudo apt-get install vim

oh-my-zsh

sudo apt-get install zsh
chsh -s $(which zsh)
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

agnoster theme, zsh-syntax-highlighting, zsh-autosuggestion 세팅해주자.

pi-hole

이제 파이홀을 설치할 수 있다.

라즈베리파이를 유선연결했으니 eth0 인터페이스만 잘 체크해주면, 나머진 기본옵션으로 다음 다음만 해주면 된다.

$ sudo curl -sSL https://install.pi-hole.net | bash

## 비밀번호 변경
$ sudo pihole -a -p

dns-encrypt

이왕 DSN 가로채는 거 암호화도 해주자. 매뉴얼 내용을 정리했다.

$ cd /opt

## 설치
$ sudo wget https://github.com/jedisct1/dnscrypt-proxy/releases/download/2.0.22/dnscrypt-proxy-linux_arm-2.0.22.tar.gz

## 폴더 생성
$ sudo tar -xf dnscrypt-proxy-linux_arm-2.0.22.tar.gz
$ sudo mv linux-arm dnscrypt-proxy && cd dnscrypt-proxy

## 기본 설정 복사
$ sudo cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml

## 설정 변경
$ sudo vi dnscrypt-proxy.toml

## 이 세 옵션을 찾아서 변경해준다.
server_names = ['dnscrypt.nl-ns0']
## 기본 53번 포트는 pi-hole에서 쓰고 있다.
listen_address = ['127.0.0.1:54', '[::1]:54']
require_dnssec = true

## 서비스 등록
$ sudo ./dnscrypt-proxy -service install

## 시작
$ sudo ./dnscrypt-proxy -service start

dnscrypt.nl-ns0 Public DNSCrypt 서버이다. 다른 서버는 여기를 참조하자. 일본이나 싱가폴 쪽에 있으면 좋을텐데 아쉽다.

Upstream DNS 변경

관리자 화면의 Settings > DNS > Upstream DNS Servers 탭에서 '127.0.0.1:54', '[::1]:54'를 넣어주고 저장한다.

Primary DNS Address 변경

공유기 설정에 들어가서 라즈베리파이 내부 아이피를 기본 DNS 주소로 설정한다. IPTIME 의 경우 고급 설정 > 네트워크 관리 > 인터넷 설정 정보 에 있다.

BlockLists

Settings > Blocklists 에서 추가하고 Save and Update 하면 차단할 주소가 추가된다. 아래는 참고할만한 링크다.

확인

Cloudflare Browsing Experience Security Check서 체크하자.

DNSSEC, TLS1.3에 초록불 들어오면 된다. 그리고 유튜브를 쾌적하게 즐기자.

여담

  • Query Log 메뉴에서 어느 도메인을 확인하려는지 알 수 있다.
  • Instagram 광고 차단 필터를 찾아봐야겠다.
  • 정작 내 블로그엔 애드센스 달려있는데..
  • 세팅비 2만원 받아도 되지않을까? 😄

RecursiveDirectoryIterator 사용하기

· 약 2분

앞서

보통 디렉토리 순회를 한다고하면 무슨 메소드를 쓸까?

<?php
// 쉘실행하고?
exec('find dir');

// 아니면 for문과 scandir?
scandir('dir');

위 방법은 간단하지만 /home/gracefullight/tmp/**/*.bak 와 같은 중첩된 디렉토리 파일의 데이터를 가져오려면 엄청난 if/else 처리가 들어갈 것이다.

RecursiveIterator

RecursiveDirectoryIterator

오토로딩을 하기 위해 필수로 들어가있는 Standard PHP Library엔 파일 순회에 사용할 수 있는 이터레이터 클래스가 들어가 있다.

<?php
// $path 하위를 가져오고 .. 와 . 는 제외한다.
$directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($directory);

foreach ($iterator as $file) {
$file->getPathname();
$file->getMTime();
}
?>

$fileSplFileInfo 이다.

RecursiveFilterIterator

위의 foreach 문 안에서 조건을 줘서 필터링할 수 있지만 다른 깔끔한 방법이 있다.

<?php
class TextFilterIterator extends RecursiveFilterIterator
{
public static $FILTERS = [
'txt'
];

public function accept() {
// 순회하기위해 자식 트리가 있을 경우 true
if ($this->hasChildren()) {
return true;
}

$current = $this->current();
if (!$current->isFile()) {
return false;
}

// 확장자가 txt 인 파일만 필터링
return in_array($current->getExtension(), self::$FILTERS);
}
}

// 감싸주면 끝난다.
$iterator = new RecursiveIteratorIterator(
new TextFilterIterator($directory),
// 이 옵션은 하위 폴더만 순회하게 해준다.
RecursiveIteratorIterator::LEAVES_ONLY,
// 이 옵션은 Read 에 실패할 경우 오류를 스킵한다.
RecursiveIteratorIterator::CATCH_GET_CHILD
);

RecursiveCallbackFilterIterator

콜백으로 만들어 더 예쁘게 짤 수 있다.

<?php
$FILTERS = ['txt'];

$textFilterIterator = new RecursiveCallbackFilterIterator(
$directory,
function ($current, $key, $iterator) use ($FILTERS) {
if ($iterator->hasChildren()) {
return true;
}

if (!$current->isFile()) {
return false;
}

return in_array($current->getExtension(), $FILTERS);
}
);

$iterator = new RecursiveIteratorIterator($textFilterIterator);

여담

쓸 수 있다면 symfony/finder 쓰자.

Mac에서 Kubernetes is starting이 지속되는 현상

· 약 2분

원인

모하비 문제인지 최근 엣지 버전 Docker for Mac 이 문제인지 잘 모르겠지만, 부팅시에 Kubernates is starting 문구가 지속되며 CPU의 온도를 90도까지 올려버린다.

맥북이 트랜스포머가 되어 곧 제트기가 될 것처럼 굉음이 나는데 해결해보자.

해결

  • Docker > Prefereneces > Reset 에서 Reset to factory defaults 로 공장 초기화를 진행한다.
  • ~/.kube 폴더를 강제로 삭제한다.
  • 다시 쿠버네티스를 실행한다.

여담

쿠버네티스 뿐 아니라 도커 자체가 맥에서 CPU 가 튀는 현상이 있는데, 여기 이슈에서 관리가 되고 있다.

file watch 기능을 끄거나, 볼륨을 해제하거나, 맥 디스크 암호화 기능을 꺼보라는데 신뢰할 수 있는 방법은 아니다.

도커 켜놓고 잠자기 해놓으면 과열로 맥북 켜지지도 않을 수 있어서 해결될 때까진 윈도우에서 돌려야할 듯 싶다.

querySelectorAll 로 배열만들기

· 약 1분

document.querySelectorAll('.class') 와 같이 엘레먼트를 받았는데, for문을 돌리면 오류가 발생한다.

해결

// 이 방법이나
let classes = Array.from(document.querySelectorAll(".class"));
// 이렇게 가능하다
classes = [...document.querySelectorAll(".class")];

// 구버전 브라우저를 지원해야한다면
classes = Array.prototype.slice.call(document.querySelectorAll(".class"));

여담

puppeteer 로 크롤링할 때 항상 헷갈린다. 삽질로 찾았는데 stackoverflow 에 정리된 게 있는 듯..