2018년 7월 19일 목요일

인터넷과 또래 집단의 확장

인터넷에 떠도는 각종 유행 자료를 보면 경향이 너무 쏠려있다. 남녀 유별하게 격리되어 있는데다 혈기방장하고 넘치는 에너지를 주체 못하는 10대 남자놈들이나 보고 낄낄댈만한, 살색 가득하거나 어처구니 없는 장면들, 혹은 그런 장면을 연출하는 자신들.

또래 집단(peer group)이란 개념이 있다. 학교 같이 동일 연령대의 집단이 모여있을 때 그 집단 안에서 서로 영향력을 미치고 그게 어느 정도 강제성을 띄는(peer pressure) 현상을 설명하는 데 쓰인다. 직장 내의 집단도 언급되는 걸 보면 꼭 연령만으로 구분하지는 않나보지만, 그동안 사회 안에서 집단은 대체로 유사한 연령대로 구성되었으므로 연령으로 구분한다고 생각해도 무방하다고 본다.

인터넷은 그 태생부터가 읽기와 쓰기를 통해 자료가 오가는 것을 기본으로 한다. 온라인 커뮤니티라는 이름 아래 읽기와 쓰기를 빈번히 하는 일군의 집단은 자연히 집단으로서 동질성이 늘어난다. 그만큼 peer pressure도 커진다.

그리고 인터넷은 또한 거의 제한없는 접근성을 기본으로 한다. 더군다나 대다수 온라인 커뮤니티는 규모를 키울수록 운영주체에게 좋기 때문에, 잘만 모이면 어디까지든 커지려고 하는 게 속성이다.

이렇다 보니, 온라인 커뮤니티는 말초적인 취향을 즐기는 거대한 집단을 만들어냈다. 그 안에서 어린 것들은 되바라지고, 나이 먹은 것들은 유치해진다.

오늘 이런 생각이 들었다.

2018년 7월 17일 화요일

디딤씨앗통장 = 아동발달지원계좌 = CDA

이런 게 있다는 걸 처음 알았다.

취지가 좋으니 혹하긴 하는데, 한 번 시작하면 어지간한 적금보다 훨씬 장기로 부어야 하니 막상 시작하기가 주저된다.

2018년 7월 10일 화요일

Re: 8년째 같은 제품을 만들고 있습니다

"8년째 같은 제품을 만들고 있습니다"

이래저래 머릿속이 복잡하고 한번쯤 회고를 해야겠다는 생각이 들던 차에, 나 또한 같은 시간을 지나왔기 때문에 그 제목을 따올만한 글이 보여서 내 나름의 지난 일들을 정리해보고자 한다.

1. 해온 일
  • 한 온라인 서비스의 미약하던 시절부터 개발팀이라는 이름의 조직에 속해 그 서비스의 눈에 보이는 것과 눈에 보이지 않는 것 사이의 대부분에 손을 댔다. IE6의 CSS 핵을 열심히도 찾아다니던 때가 있었는가 하면, MySQL의 특정 버전에서 어떤 쿼리를 쓸 수 있고 쓸 수 없는지 실험하기도 했고, nginx와 haproxy, keepalived라는 이름이 어떤 역할을 하는지 뒤적거리기도 했다. AWS니 Azure니 하는 것들을 좇아 몇 가지를 서비스에 도입하기도 했다.
  • 한 때는 2인 팀의 팀원이기도 했고, 또 한 때는 1인 팀처럼 지낸 적도 있었고, 팀장이라는 직함이 붙은 적도 있었다. 팀장과 팀원이라는 역할에 대해 고민해볼 수 있는 시간이었다. 팀장으로서의 나는 이를 테면 '어디선가 누군가에 무슨 일이 생기면 틀림없이 나타난다 홍반장' 정도였다는 평을 들었었다. 내가 원하던 방식과도 결이 같은 평가라고 생각한다.
  • 회사에 사람이 적을 때는 맞은 편 책상으로 걸려온 전화가 어떤 내용인지에 따라 미리 필요한 자료를 찾아서 던져주기도 했다. 서로 유기적으로 돌아가고, 서로 불필요한 수고를 덜 할수록 좋다고 생각해서 그랬던 거지만, 사람이 적다고 할 수는 없는 상황이 되고 팀제가 강해지고 역할이 나뉘면서 계속 그렇게 하기는 어려워졌다. 대신 자주 손이 가는 작업은 백오피스 도구를 제공하고, 혹시 작은 개선으로 일을 줄일 수 있는지 종종 물어보는 것으로 대신했다.

2. 다뤄본 것
  • CentOS 6.x를 썼다.
    • CentOS 7이 나왔을 때 여러 변화가 있었다고 듣기는 했지만 그 변화들이 장점으로 다가오기엔 변화의 폭이 너무 크게 느껴졌다. ifconfig와 netstat가 구식이 된 것 만큼은 손에 아직 완전히 익지가 않았다.
    • 기왕에 알짜 리눅스 때부터 쓰기도 했고 우분투를 계속 최신 버전으로 올려가며 쓰기 때문에 심각하고 전문적인 수준이 아니면 OS 범위에서 크게 어색한 적은 없었다.
  • 아파치를 썼다.
    • 하지만 아파치를 집요하게 파고들었다고는 할 수 없다. 웹서버 튜닝에 시간을 들여서 얻을 수 있는 소득보다는 다른 걸 하는 게 나은 나날들이 이어졌다.
    • 시간이 좀 지난 뒤에는 PHP CLI를 쓰는 방식을 부분적으로 도입해버렸고, 오히려 PHP-FPM은 제대로 써본 적이 없었다.
  • MySQL은 대체로 흔한 수준의 경험이었다. 여전히 DB에 일가견이 있다고 할 수준은 못 되다 보니 매번 아쉽다.
    • replication을 이런저런 면에서 만지작거려야 하는 시점들이 있었다. 클러스터 같은 구성은 경험해본 바가 없었다. replication도 확장이 쉽다거나 한 건 아니다보니 TiDB 같은 시도가 궁금하긴 하다.
    • PHP에 mysqlnd_ms라는 걸 붙여서 replication 구조 하에서 master/slave를 번거롭지 않게 다루는 방법을 도입했었고, 그 전에 직접 양쪽 연결을 모두 관리하던 것에 비해서는 아주 쓸만한 방식이라고 생각한다. 의외로 다른 언어 다른 프레임워크에서는 잘 다뤄지지 않는 영역인 것 같아서 아쉽다.
    • mysqlnd_ms의 다른 기능으로 slave들의 consistency를 처리해주는 건 여건상 정착시키지 못하고 haproxy로 slave를 하나로 묶는 선에서 끝이 났다. mysqlnd_ms 기본 코드 자체에 버그가 있어서 정상인(=차이가 0인) slave를 무시하는 문제도 있었고, slave의 상태를 확인하기 위해서 SUPER 권한이 필요하기도 했던 것으로 기억한다.
    • SQL 쿼리를 작성하는 방식으로 ORM도 쿼리 빌더도 써봤지만 아무래도 완성된 쿼리를 가지고 있는 게 좋다고 생각한다. DB 포팅을 고려한다고 하지만, 대부분 환경에서 DB 전체를 갈아치우는 일이 얼마나 생긴다고. 그보다는 완성된 쿼리를 가지고 있고 그걸로 성능을 높이고 병목을 없앨 수 있는 가능성을 키우는 게 좋다고 생각하게 됐다.
  • PHP를 쓰면서, 버전마다 새로 도입되는 기능들을 활용하기 위해 노력했다. 본래 설계 목적과는 좀 다를 것 같지만 trait는 초기부터 살이 붙어 비대해진 코드를 쪼갤 때 잘 써먹었다.  generator도 메모리 잠식이 우려되는 부분에서 간간히 쓰곤 한다.
  • RabbitMQ를 도입해, PHP에서 비동기식 처리가 필요할 때 아예 하나의 task로 큐에 등록하는 방식을 썼다. 꼭 RabbitMQ가 아니라 DB를 써도 되었겠지만 읽고 쓰기의 유연함이나 확장성, 우선순위 적용 같은 걸 생각하면 올바른 선택이었다고 생각한다.
    • 당시에 여러 *MQ를 겉핥기로 찾아봤고, 그나마 RabbitMQ가 OpenStack에서 쓰인다는 점 때문에 믿을만한 것 같아서 선택했다. 초기 도입한 버전에서 몇 가지 문제를 겪긴 했다. :P
    • 차라리 ReactPHP 같은 걸 진작에 도입해버렸으면 어땠을까 싶기도 하지만, 그것도 나름대로 여러가지 문제가 있었겠지.
  • 이메일 대량 발송을 매주 해야 해서, 어쩌다보니 RabbitMQ 기반으로 여러 대의 발송 서버를 구축하는 데까지 갔다.
    • 대체로 쓸만하지만 자동화할 여지가 있긴 하다. 작성 단계가 민감한 수작업의 성격을 완전히 벗지 못했고, 발송도 사람 손을 완전히 안 타지는 않는다.
    • DNS에 SPF니 뭐니 이런저런 것들을 설정하는 건 지금도 남한테 설명해줄 만큼 정확히는 모르겠다.
  • AWS는 익숙해질만큼 오래 쓰진 않았다.
    • Route 53은 개인 도메인을 옮겨간 상태다. DNSever를 잘 썼지만 AWS의 각종 기능에 잘 녹이려면 어쩔 수가 없었다.
    • S3 + CloudFront는 미투데이 백업 html을 올려서 아주 가끔 보곤 한다.
  • Azure. 애증의 MS. 몇몇 부분에서 요긴하게 쓰고는 있지만 개인적으로는 쓰고 싶지 않다고 매번 투덜거린다.
    • S3 + CloudFront에 대응하는 역할로 쓰고 있다.
    • Azure Function은 웹서버로 접속이 한 번에 확 몰릴 때를 대비하기 위해서 도입했다.처음에는 도입만 했지 제대로 구성을 못해놔서 한바탕 곤란을 겪고, 그 뒤에 Consumption Plan이라는 걸 썼어야 한다는 걸 배웠다. 지금 구성도 아주 깔끔하다고 생각하지는 않지만 개념 자체는 복잡하지 않기 때문에 앞으로도 원래 의도했던 역할은 잘 해줄 거라고 본다.
    • Azure Function이 대외적으로는 PHP나 Node.js를 지원한다고 선전하지만 실제로는 런타임 적재를 하느라 생기는 것 같은 초기 지연이 상당하다. 단순히 콜드 스타트 문제라고 하기엔 양상이 달랐던 걸로 기억한다. 콜드 스타트를 방지하기 위해 Consumption Plan이 아니라 App Service Plan을 써야 한다는 얘기도 있던데 필요한 방향이랑 안 맞아서 채택할 수 없었다.
  • C#은 맛만 봤지만 느낌이 좋았다.
    • Azure Function 동네에선 역시 C#이 1등 시민이다. 덕분에 PHP와 Node.js로 짠 코드를 C#으로 똑같이 다시 짜는 경험을 해본 적이 있다.
    • 그 뒤로 PHP와 달리 타입이 전제되는 언어에 호감이 생겼다. 익숙하지 않은 Azure Function 환경에서 PHP로 짰을 때는 어디선가 실제 서비스에서 오류가 터지고 나서야 놓친 부분이 있는 걸 알았지만, C#으로 짰을 때는 빌드 단계에서부터 오류가 보였기 때문에 실제 서비스에는 문제를 내보내지 않을 수 있었다.
  • React를 도입했다.
    • ReactDOM 모듈이 분화되기 직전의 버전으로 시작했었는데, 구버전 IE를 지원하기 위함이기도 했지만, JSX 렌더링을 Node가 아니라 PHP의 v8js 모듈로 처리하다 보니 굳이 React 버전을 올려가면서 쓸만한 상황까지 되지 않았다. v8 엔진 자체의 발전은 종종 구경거리 삼아 보곤 한다.
    • 눈에 보이는 영역이고 다양한 상태가 존재한다면 JSX를 쓰는 게 직관적이라고 생각한다. jQuery로 이미 그려진 DOM을 바꾸기보다는 한 방향으로만 흘러서 DOM을 만들어내는 게 머리도 덜 복잡하다. JSX를 도입하지 않은 부분에서도 JSX처럼 '주어진 조건에 맞게 그려내기만 하는' 방식을 쓰기도 했다.
    • 덕분에 함수형 프로그래밍이라는 것에도 호감이 있다.
    • 다만 업무 담당자가 분화될 때 JSX라는 게 진입장벽으로 작용해서 아쉽다. 흔히 눈에 보이는 것을 다룬다면 HTML5에 CSS3 정도면 전문적이라고 여겨지는데, 거기에 자바스크립트라는 생소한 물건을 들이밀면 그게 아무리 HTML처럼 생겼다 할지라도 동작 방식이 쉽사리 전달되지는 않았다. v8js를 채택한 게 이런 상황을 더 나쁘게 만들었던 것 같기도 하다. 차라리 흔한 Node 환경이었으면 적응하기는 낫지 않았을까 싶다.
  • Google Tag Manager를 도입했다.
    • 어느 정도 서비스가 궤도에 오르고 나서는 서비스의 현황을 파악하는 것도 일이었는데, 이런저런 분석 도구를 서비스에 붙이는 게 빈번해지고, 어떤 조건에서 어느 시점이 측정되어야 하는지도 점점 복잡해졌기 때문에 이 부분을 해소할 도구를 찾다가 ᅟGTM이 적당해서 서비스에 붙였다.
    • Google Analytics를 비롯해 이런저런 분석 도구를 상당한 자유도로 엮어넣을 수 있어서 좋다.
    • 하지만 역시 학습곡선이 가파르다. HTML 페이지의 렌더링이 어떤 방식으로 일어나는지 알아야 딱 맞는 설정을 할 수 있는 식이다 보니 아무래도 '기술과는 거리가 있지만 마케팅은 해야 하는' 사람들에게 친근하게 다가갈만한 물건은 못 되는 것 같다.
  • PG사를 옮겨봤다.
    • 이니페이, 나이스페이, 그리고 검토만 했던 여러가지들.
    • 아임포트 같은 중간 서비스가 왜 존재하는지 너무나도 잘 알 것만 같다.
    • 매번 '일단 되게'하는 데 급급하다 보니 내재한 논리는 설명해야만 알 수 있고 코드 수준에서 정리는 거의 안 되어 있지만. 
    • 거래 고유번호(!)가 겹칠 수도 있고, PG사도 부하가 걸리면 새벽에 통보없이 자사 IP를 변경할 수 있다는 것도 알게 되었다.
  • 분석업무에 R과 Python 쓰는 걸 도왔다.
    • 내가 분석업무를 직접 긴밀하게 다뤄본 적은 없지만 눈으로 따라가면서 막힌 부분을 설명해줄 수 있는 정도는 되다 보니 중간중간 나도 키보드 너머로 구경을 좀 했다. IPython Notebook이 Jupyter라는 이름이 되면서 다룰 수 있는 게 아주 많아진 것 같아 신기했다. 
    • 분석업무의 부담을 덜어줄 생각으로 아예 DB 내용을 가져다가 시각화까지 해준다는 곳을 몇 가지 구경해봤지만 개인정보 제3자 제공의 문제도 생기고 해서 본격적으로 검토하지는 않았다.
  • SVN을 쓰다가 Git으로 넘어왔다. FTP에 직접 접속해 편집하던 시절에 비하면 (하략)
  • 외부 서비스 연동도 이것저것 해야 했었다.
    • 제공되는 API를 다루는 건 OAuth 등으로 여러 서비스를 붙여봤다. API 버전이 바뀔 때마다 손을 대야하거나, OAuth 토큰에 생명주기가 들어가면서 생긴 번거로움은 아직 깔끔한 해결책을 찾지는 못했다.
    • 그래도 API가 제공되는 게 나은 편이라, SMS 발송 때 발신자 번호표시 정책 때문에 업체를 바꿀 때는HTTP API를 제공하는 업체를 선정했다. 여기도 API 버전이 한 번 바뀌어서 급하게 대응을 하긴 했지만 내부 서버에 데몬을 돌리는 것보다는 훨씬 만족스럽다.
    • 소위 크롤링이라고 하는, 외부 사이트의 멀쩡한 HTML 페이지 긁어서 원하는 작업을 수행하는 것도 해야 했었다. 거의 API 껍데기를 씌운 셈이었는데 다행히 이걸 쓰는 동안 그 사이트가 바뀌지는 않았는지 따로 손을 본 적은 없었다.

3. 다뤘어야 하는 것
  • DB를 읽는 시점에 많은 계산을 하면 안 되는 거였다. DB에 자료가 기록되는 시점에 이런 저런 일련의 작업들이 일어나고, 읽을 때는 그 흔적만 가져오는 식이어야 했다. 나중에 일어나는 많은 문제들이 여기서 비롯하는 셈이었다.
  • 캐시 관리를 진작 강화해야 했었다. DB의 읽기/쓰기를 똑똑하게 다루지 못했다면 캐시 정책이라도 똑똑해야 했다. Decorator 입히는 수준으로 캐시 적용을 쉽게 만들었어야 했는데 그러지 못했다. 한편으로는 static site generator의 껍데기를 입히면 어떨까 생각도 해보았지만 거기까지 가기엔 그 사이에 건너야 할 것들이 많았다.
  • 미들웨어 개념이 있는 프레임워크를 도입해야 했다. 꼭 Laravel 같이 널리 알려진 물건일 필요는 없어도, 하나의 HTTP Request가 최종 Response로 끝나기 전에 여러 동작을 다양한 층위에서 다루는 방식이 필요했다. 미들웨어라는 게 없는 상태에서 여러 다양한 로직을 처리하려다 보니 길고 난삽한 코드가 만들어지기 십상이었다. klein.php라는 걸 라우팅 강화 목적으로 도입하면서 미들웨어를 지원하는 기본적인 틀은 갖추었는데 더 발전을 시켰어야 했다.
  • 검색 엔진. Elasticsearch를 도입하려는 시도는 했지만, 결과적으로 접혀버렸다. 검색 엔진 자체보다는 인덱싱 부문에 공을 들여야 했는데 아무래도 내가 너무  쉽게 덤볐다.
  • 다 적자면 끝이 없겠지.

4. 다루고 싶은 것
  • 여전히, 자료를 다양한 기준으로 표현하는 게 충분하지 않다고 생각한다. 시간 순서로는 iCalendar 같은 게 있고, 위치 기준으로는 지도가 있고, 그 밖에도 여러 기준점이 존재할 수 있는데 쌓여있는 데이터에 비해 가공과 정리와 표현의 노력이 충분치 않았다.
  • PHP 외의 환경을 다뤄야 한다. 꼭 PHP를 벗어나기 위함이라기보다는 다른 언어나 프레임워크에서 어떤 접근방식을 쓰는지, 어떤 간단한 방법이 있는지를 구경하다 보면 딱 이럴 때 쓰면 좋겠구나 싶은 지점들이 있어서 도움이 된다. 개인적으로는 Django를 선호하는 편이고, Node도 좋고.
  •  템플릿 엔진을 개선하고 싶다. 흔히 템플릿 엔진이라는 건 무슨 자료가 채워져야 하는지 다 아는 상태에서 미리 준비를 하고 템플릿 엔진으로 넘겨주는 식인데, 방향을 바꿔서 템플릿에 어떤 요소들이 있는지에 따라 그에 맞는 동작을 하는 방식에 장점이 있다고 본다. Level-2/Transphporm이라는 게 DOM+CSS 선택자(내부에선 XPath) 구성으로 이런 게 가능하도록 만들었는데 잠깐 검토한 바로는 CSS 선택자의 해석을 직접 구현하고 있어서 버그라고 할까 의도와 다른 동작들도 보이고 실사용하기엔 적합하지 않아 보였다. 하지만 개념만은 충분히 훌륭하다.
  • 프로비저닝과 배포 자동화, 혹은 CI라고 하는 것까지를 다뤄야 한다. 생각이 여기에 이르면 매번 지치는 기분이 들지만 필요한 작업이다. 그동안 기술적인 고민을 나름대로는 많이 했지만 당장 매출이 나는 서비스에 나쁜 영향이 조금이라도 갈 것 같다면 쉽게 저지를 수가 없었다. 배포 쪽이 매끄럽게 다듬어진다면 이런 부분이 덜 무서워지겠지. 
    • Docker는 환경 빌드


2018년 7월 5일 목요일

코드로 인프라 관리하기, 왜?

http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=103384462

이런 책이 있다. 꽤 마음에 드는 내용이었다.

가상화 이후에 컨테이너 시대가 되었는데 흔히 거론되는 장점은 확장성이나 배포의 편리, 구동 환경을 동일하게 유지한다거나, 테스트를 실서버에서처럼 한다 같이 인프라를 대체하는 수준의 얘기인데, 그런 장점을 취하지 않더라도 (규모가 작아서 서버 한 대 안에서 다 돌릴 수 있더라도) Docker 류의 기술을 도입할 장점이 충분히 있다고 생각한다.


서비스 하나를 구성하는 데는 여러 데몬이 개입하게 된다. 흔하게는 DB, 혹은 캐시. 무슨 서비스가 어떤 설정으로 돌아가고 있는지 알아야 서비스를 구성할 수 있고, 어느 하나라도 같이 돌지 않으면 서비스에 지장이 생긴다.

그럼 서비스를 만들 때 그런 주변 요소들도 같이 고려할 수 있어야 한다. 가상머신이든 컨테이너든  미리 청사진을 준비하고 그 청사진에 맞춰 서비스를 구성하면 된다. 청사진 자체를 코드로 관리할 수 있으면 코드의 변화와 함께 청사진도 바꿀 수 있기도 하다.

이런 면에서 Docker 류를 설명하는 얘기도 자주 보이면 좋겠다.

서비스형 블로그 DNS 설정

유명한 가입형 블로그 서비스는 대체로 커스텀 DNS를 붙일 수 있게 지원한다.
한동안은 텀블러를 2차 도메인에 붙여놨었다. 텀블러는 2차 도메인을 직접 지원하기 때문에 보기에 그럴듯해서다.
그러다가 구글 블로거로 다시 옮겼다. 구글은 2차 도메인 자체를 유지할 수는 없고 2차 도메인에 접속하면 3차 도메인으로 이동하는 수준까지는 지원한다.

여기에 한 가지 더해서, 3차 도메인으로 이동까지 했는데 원하는 주소가 없으면 다른 도메인으로 이동하는 것까지 지원한다. 자기 도메인 유지하는 사람은 다른 서비스를 썼을 수도 있으니 지원하는 기능일 테다. 텀블러에 물렸던 2차 도메인이 한동안 구글 검색에 노출됐던 모양인데 그래서 검색 결과가 텀블러 주소 형식으로 들어오게 된다. 이걸 구글이 받아서 3차 도메인의 텀블러로 이동시켜준다. 편한 기능이다.

HTTPS 지원도 잘 되기 때문에 요즘 시대에 뒤떨어지는 느낌도 없다.

2018년 6월 23일 토요일

Droid Sans Fallback 폰트 버그

http://cjonmart.net 라는 데를 들어가서 이것저것 장바구니에 담는데 중간중간 잘못 적힌 글자가 보였다. 처음 몇 번은 주말에 바빠서 오타를 냈나 싶었지만 같은 패턴이 반복되고 있었다.
가령 "건강식품관"이 "건가ㅈ식품관"처럼. "떡갈비"는 "떡가ᄅᅠ비"로. "밀감"이 "밀가ㅁ"로.

잘못 적힌 글자를 긁어다 복사해서 다른 창에 붙여보니 멀쩡하게 나왔다. 크롬이 폰트를 깨먹거나, 폰트가 잘못 만들어졌을 터였다.

브라우저에서 F12를 눌러 개발자 도구를 열고 잘못된 글자가 보이는 HTML 요소를 선택했다. 개발자 도구에서는 글자가 멀쩡하게 보였다.
 str=document.querySelector('[title="건강식품관"]').childNodes[0].text; for(i=str.length;i-->0;){console.log(str.charAt(i), str.charCodeAt(i), str.charCodeAt(i).toString(16));}
라고 가져다가 찍어봐도 "강"은 AC15로 잘 나왔다. 어쩌다 입력된 NFD가 잘못 표시되는 것도 아니란 얘기였다.

document.querySelector로 그 요소를 가져다가 window.getComputedStyle(x).fontFamily를 봤다. AppleSDGothicNeo-Medium, "Apple SD Gothic Neo Medium", "Noto Sans CJK Korean", "Noto Sans", "Droid Sans Fallback", "Malgun Gothic", sans-serif - 한켠에 gedit 설정창을 띄워서 폰트 이름을 찾아봤지만 애플도 Noto도 나오지 않았다. 잘못 표시되는 글자를 gedit 폰트 설정의 미리보기 칸에 넣고 차례로 폰트를 봤더니 Droid Sans Fallback이 깔려있었고, 실제로 글자도 크롬에서와 같이 잘못 나왔다.


하지만, 크롬이 gtk를 쓰던가? 폰트 파일의 잘못일 수도 있지만, 리눅스 세계이니만큼 폰트 래스터라이저를 먼저 의심해볼 필요가 있었다.

다시 파이어폭스에서 같은 주소를 열어봤다. 글자가 멀쩡하게 나왔다. F12를 눌러 요소를 선택하고 오른쪽에 어떤 폰트가 적용되었다는 설명을 열어보았다. 이번엔 Noto가 적용된 걸로 나왔다. Korean이라는 꼬리표가 붙은 폰트가 없기는 하지만 자동으로 KR로 적용하는 건지, 아니면 항상 Noto로 적용하는 건지. 이래서야 비교가 안 됐다. 다른 한글 폰트를 몽땅 제거해서 영문만 나오는 상태로 만든다면 확인이 되겠지만 그렇게까지 하고 싶지는 않았다.
혹시나 해서 Droid Sans Fallback을 웹폰트로 보여준다는 웹페이지도 몇 개 열어봤지만 증상을 재현해줄만한 상황은 아닌 것 같았다.

찾아보니 Droid Sans Fallback은 구글 안드로이드 때문에 배포되는 폰트인 것 같았다. 널리 쓰이는 걸 고려한 폰트인만큼 다국어 부문에서 잘못된 정보를 포함할 수도 있겠다 싶었다. gedit 설정창에 한글 AC00부터 11184글자를 모두 긁어다 붙여봤다. (설정창을 닫고 본문창에서는 증상이 나타나지 않았다) 과연, "가" 다음에 "각-갛" 범위의 글자가 모두 받침이 떨어진 상태로 나오고 있었다. "나" 이후로는 폰트 영역이 없을 때 나오는 채움 표시가 나오고 있었고 중간은 너무 많아서 다 못 봤지만, 끝에서는 다시 "힢힣" 두 글자가 나오는 걸로 끝이 났다. 빈 영역이 대부분인 걸로 봐서, 의도적으로 채워진 걸로는 보이지 않는다.

dpkg -l | grep -i droid 해보니 fonts-droid-fallback 패키지가 나온다. dpkg L fonts-droid-fallback 해보면 ttf 파일이 2개. fontstools 패키지의 ttx 명령으로 풀어보니 과연 HANGUL 어쩌고 하는 줄이 잔뜩 있다.

아마도 폰트가 잘못 만들어진 것 같긴 한데, 이걸 어디에 어떻게 전달을 할 수 있으려나 모르겠네.

https://github.com/google/fonts/issues/1352 첫번째 반응을 보면 Droid는 이제 Noto로 대체되었으니 그냥 바꾸고 말라는 얘기다. 그것도 2017년 말.
그래서 그냥 지웠다. apt remove fonts-droid-fallback -y

그러게. 이 사회가 능력으로 사람을 고르는 데가 아니었지

영특한 재주가 있는 이를 두고도 쓰지 않는다면 멍청한 짓 아닌가, 하는 문장을 읽었다.
그리곤 서글프지만 자연스럽게, 여기가 그런 동네가 아니란 걸 새삼 떠올렸다.

2018년 6월 17일 일요일

리눅스에서 5.1채널 스피커 테스트

이리저리 물건을 정리하다 보니 스피커 연결선을 모두 뽑았다가 다시 꽂았다.

맞게 연결했는지 확인하려고 늘 쓰는 검사 동영상을 틀었는데 smplayer + mpv 조합으로 음분리가 잘 되지 않았다. speaker-test 명령이 있는 건 알고 있어서 몇 가지로 실행해봤는데 딱히 이거다 싶은 결과가 나오지 않았다.

그래서 아래와 같이 성공한 방법을 적어둔다.
  • aplay -L | grep -i pdif -B2 | grep MAYA -B1 -A1
  • pasuspender -- speaker-test -Ddca:CARD=U5,DEV=0 -c6 -l1 -twav -s1

MAYA U5 외장 사운드에서 광출력을 뽑아 야마하 TSS-10 쪽으로 신호가 넘어간다.
https://github.com/darealshinji/dcaenc 에서 dcaenc를 가져다 설치한 상태라서 dca 장치를 썼다.
pulseaudio가 기본으로 구동되는 환경이다 보니 pasuspender를 씌워서 pulseaudio를 거치지 않고 장치에 직접 접근했다.
s 옵션뒤의 숫자를 하나씩 바꿔주면 각 스피커 이름을 담은 음성이 재생된다. 스피커가 맞게 연결된 걸 알 수 있다.

자, 이제 smplayer + mpv에서 뭐가 문제인지 알아볼 차례구나(...)
왜인지 모르지만, 껐다 켜고 나니 /etc/pulse/ 설정 하나 건드린 거 없고 smplayer는 모든 오디오 옵션을 기본, 자동 혹은 끈걸로 뒀을 때 mpv에서 샘플 영상으로 음 분리가 잘 된다. mplayer를 통해서는 소리가 깨지는 걸로 봐서 뭔가 다른 게 있긴 한가본데.
패스쓰루 옵션을 켜면 안 되는 건 이상하긴 한데, 일단 잘 되니까 넘어간다.

2018년 6월 9일 토요일

kubectl을 다른 호스트에서도

k8s 클러스터를 3개 가상머신으로 구성한 뒤에 항상 master에 직접 들어가 kubectl 명령을 실행해왔다.

최근 vscode를 쓰고 있는데, terraform 확장을 테스트해볼 겸 해서, kubectl은 우분투 snap으로 설치하고 master의 ~/.kube 디렉토리를 몽땅 복사해서 로컬로 가져왔다. kubectl을 실행하니 과연 잘 된다. .kube/config 파일에 적힌 IP가 192인 걸 보니 master 안에서도 127.0.0.1이 아니라 DHCP가 부여하는 IP를 써서 접속하는 구조였던 것 같다.

이렇게 되니 로컬에서 terraform 테스트하는 건 그냥 가능하게 되었다. 문제는 master에서라면 어떻게든 접속할 수 있었던 k8s 내부 네트워크에 접근할 길이 없어졌다는 것. external loadbalancer 대응 문제가 좀 더 크게 느껴진다.

kubectl expose --type=LoadBalancer

traefik 설치 과정에서도 검토해봤지만, 외부 접속을 k8s 내부로 어떻게 끌어올지가 여전히 문제다. 여러 서비스가 k8s 세계 안에서 조화롭게 돌아가고 ingress controller를 traefik으로 설정해서 특정 도메인과 포트로 접속하면 k8s 내부의 원하는 리소스에 접근할 수 있게 되어 있다 한들, 실제 공인IP의 세계로 연결하는 게 오리무중인 상황이어서야 의미가 없다.

terraform 설명 글을 찾아봐도 당연스럽게 LoadBalancer 타입을 사용하도록 되어 있을 뿐, on-premise 환경에서 어떤 처방이 있는지를 언급하는 데가 없다.
(https://www.hashicorp.com/blog/managing-kubernetes-applications-with-hashicorp-terraform 를 보고 복붙만 해봤는데 plan에서 주루룩 지정될 항목이 나오는 점은 좋았다. apply가 끝이 나질 않아서 살펴보니 LoadBalancer를 사용하고 있어서 중단했을 뿐)

https://github.com/kubernetes/kubernetes/issues/36220#issuecomment-351782407 얘기에 https://kubernetes.io/docs/tasks/administer-cluster/developing-cloud-controller-manager/ 같이 CCM을 직접 만들면 될 거라는 얘기가 그럴듯해 보인다. type:LoadBalancer에 대응하는 구현체가 있으면 되는 건 맞을 테니까.

CCM은 
https://zetawiki.com/wiki/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4_%EC%9A%A9%EC%96%B4 설명을 보면 k8s system의 일부로 돌아가면서 해당 환경에 맞는 관리 동작을 하는 정도로 이해가 된다. ingress controller가 k8s 안쪽으로 진입한 트래픽을 다루는 거라면 CCM은 k8s 가장 바깥이 그 밖의 세계와 어떻게 이어져야 하는지를 처리해야 하겠지.

2018년 6월 6일 수요일

Blogger 첫 페이지에 글 노출하지 않기

커스텀 도메인이나 HTTPS 적용 등 여러 면에서 블로그 서비스로는 구글의 Blogger가 가장 무난해서 쓰고 있다.

하지만 도메인으로 처음 들어왔을 때 무턱대고 글을 보여주는 게 마음에 들지 않아서, 첫 페이지라는 개념을 적용할 수 있는지 찾아봤다. https://www.quora.com/How-can-I-edit-a-home-page-on-blogger-com/answer/Elliyas-Ahmed 이런 답이 나온다.

Blogger 테마도 XML 수준에서 직접 편집하면서 필요한 부분에 조건문을 넣으라는 거다.
  • https://support.google.com/blogger/answer/46995?hl=ko&ref_topic=6321969
  • https://support.google.com/blogger/answer/47270?hl=ko
 아래와 같은 태그를 필요한 위치 앞뒤에 넣으면 첫 페이지가 아닐 때만 영역이 보이게 된다.
<b:if cond='data:blog.url != data:blog.homepageUrl'>
</b:if>

하지만 아무 데나 되는 건 아니고 넣을 수 있는 위치에 제약이 있다. 테마 마크업 구조를 조금 알아야 이해가 되는데, 크게 봐서 b:section과 b:widget 태그가 페이지에 표시되는 단위라고 할 수 있고 b:includable 태그로 영역을 잡아주면 그걸 b:include 태그로 가져가서 쓰는 식이다. 그리고 모바일 레이아웃이 따로 있는 테마라면 b:if 태그로 data:mobile 값을 검사해 어떤 경우에 블럭을 표시할지 말지 결정하는 부분도 있을 것이다.

그래서 지금은 일단 모바일 접근이 아닐 때 글을 표시하는 영역이 안 나오도록만 해놨다. 본문과 본문 위에 날짜 나오는 부분 2개를 막는 걸로 처리가 됐다.

비워둔 영역은 따로 안내 페이지를 하나 만들고 그 본문을 가져오는 수준에서 채우고 싶었는데 이건 방법이 있는지 어떤지도 모르겠다. 아마 안될 것 같긴 한데.

Firefox CSD 기능을 쓰려고 시작한 모험

CSD란?

client-side decoration이라고 해서, 창 관리자가 그려주는 창 테두리와 버튼들 대신 프로그램이 직접 그런 부분들을 창 안에 표시하는 기능이 있다. decoration을 없애는 거야 일찌감치 가능했다. Openbox 같은 경우엔 창 메뉴를 열어서 "Un/Decorate" 기능으로 끄고 켤 수 있다. 프로그램이 창을 띄울 때 undecorate 창이라고 선언할 수도 있다. (wmctrl로도 될 줄 알고 찾아봤는데 그런 거 없나보다)

그렇게 없앤 영역이 담당해야 할 기능 그러니까 창의 크기를 바꾸고 창을 닫거나 옮기는 걸 어떻게 처리하는지가 문제가 되는 건데, 개별 프로그램들이 알아서 잘 할 방법을 찾아보자는 얘기가 본격적으로 나오는 거다.

CSD 도입 상황

얼마전까지 CSD가 두드러지게 적용된 건 구글 크롬이었다. 처음 깔았을 때는 탭이 맨 위에 오는 크롬 특유의 모습이지만 설정에서 '시스템 제목 표시줄 및 테두리 사용' 옵션을 켜면 창 관리자가 그려주는 테두리가 붙는 걸 볼 수 있다.

그리고 csd에 대한 건 아니지만 gtk3에 (OS X의 동작 같이) '창 메뉴바를 분리해서 최상단 패널에 몰아서 보여주는' 패치가 들어간 것도 맥을 같이 하는 흐름이었다고 생각한다. 그 뒤로 Unity나 gnome-shell이 '창 관리자' 역할을 넘어 프로그램 안쪽의 기능들과 긴밀하게 연계하면서 창 관리자가 중간에 끼는 게 데스크탑 환경을 만들어가는 입장에서 거추장스럽게 느껴졌을 것 같기도 하다.
 그러다가 (클라이언트 창이 뜰 수 있도록 해주는 공간 자체를 만들어내는 게 서버인데) 창 시스템 서버가 X, X.org의 틀을 벗어나 Wayland라는 걸로 넘어가면서 CSD를 진지하게 다뤄야 하는 상황이 되었나보다. 아래는 참고할만한 글이다.
  • (영어) https://blogs.gnome.org/tbernard/2018/01/26/csd-initiative/
  • (영어) https://blog.martin-graesslin.com/blog/2018/01/server-side-decorations-and-wayland/
  • (영어) https://kver.wordpress.com/2014/10/25/presenting-dwd-a-candidate-for-kde-window-decorations/
  • (한국어) https://nemoux00.wordpress.com/2014/10/29/wayland-csd-ssd-dwd/

사실 윈도우 동네에서는 WIN16 API 운운하는 시절부터도 이미 나온 얘기였다. 윈도우 환경이 제공하는 기본 모양이 워낙 고정적이다 보니 프로그램마다 자기 취향대로 창을 그려대는 통에 전반적인 통일감이 없고 사용성이 떨어지고 윈도우가 발전해도 개별 프로그램은 발전을 따라가지 못해 구닥다리가 된다는 거였다. 꼭 데스크탑 얘기만이 아니라 웹이나 모바일 앱 환경에서도 OS가 제공하는 환경을 따를지 말지에 대해서 논란이 많다.

Firefox와 CSD

한편, 이런 '주어진 환경을 따를지 아니면 직접 해결할지'를 더 많이 고민해온 게 Firefox 같이 여러 환경을 지원하는 프로그램이다. 크롬이야 워낙 독창적인 인터페이스를 들고 나왔기 때문에 모든 걸 무시하고 바닥부터 시작할 수 있었지만 파이어폭스는 오랜 기간을 거치면서 그때그때 OS의 변화와 UI의 유행을 고스란히 따라와야 했다.

그리고 드디어 파이어폭스가 CSD 옵션을 포함한 게 60 버전부터다. 방침이나 당부를  따지지 않더라도, 어떤 식으로 동작할지 궁금하긴 했다. 실은 58 버전 정도부터도 개발 버전에는 들어갔었기 때문에 일찌감치 깔아서 CSD 옵션을 켜봤지만 왜인지 탭이 있는 줄의 공간 배치가 조금 변하는 것 말고 실제로 창 테두리가 없어지거나 하지는 않았다.

Openbox 때문일 거라고 생각하지는 않았다. 그냥 아직 도입 초반이니까 뭔가 문제가 있는 거라고만 짐작했다. 하지만 정식 버전에 포함이 된 뒤에도 여전히 창 테두리가 남아있는 건 이상했다.

CSD 적용된 Firefox를 향한 모험

그러다가 Openbox가 아닌 다른 세션을 잠깐 썼을 때 파이어폭스에 온전히 CSD가 적용된 걸 확인했다. gnome-session의 변종으로 존재하는 것들에선 모두 CSD가 잘 동작하는 것 같았다. 특이하다면 어떤 환경에선 (윈도우에서의 크롬처럼) 탭 위에 여백이 하나도 없고 어떤 환경에서는 (OS X에서의 크롬처럼) 약간 여백이 생겼다. 나는 여백이 없는 걸 선호하기 때문에 이것저것 바꿔가면서 띄워봤다.

하지만 그대로 Openbox를 버리고 데스크탑 환경을 갈아타기에도 문제가 있었다. 애초에 단촐한 구성이라 Openbox를 쓰고 있던 터에 다시 이런저런 프로그램이 잔뜩 뜨고 구성에도 제약이 있는 환경으로 가는 게 손에 딱 맞질 않았다.

가장 걸림돌이 된 건 여러 모니터에 배경 화면을 각각 띄울 수 없다는 거로, gnome-shell은 왜인지 배경 화면을 하나로만 제공했다. 아마 Xinerama 같은 걸로 panning 옵션을 통해 모든 화면을 하나로 뭉쳐서 쓰기 위해 그런 것 같았다. 이리저리 뒤져보면 gnome-shell 안에서 panning을 풀어버리는 방법도 있을 거라고 생각은 했지만 시간도 오래 걸리고, 다른 모든 설정 프로그램이 단일한 배경만을 전제하는 상황에서 강제로 환경을 바꾸는 게 얼마나 효과가 있을지도 의문스러웠다.
그리고 이것도 아마 gnome-shell의 특성 때문에 생기는 문제인 듯한데, xrandr에서 여러 모니터 중에 하나를 제대로 사용하지 못하고 꺼버리거나 해상도를 엉뚱하게 맞추는 문제가 있었다. 인텔 내장 그래픽을 쓰기 때문에 어쩔 수 없는 건가(웃음) 싶기도 했다. 찾아보니 EDID나 mode 같은 용어가나오면서 직접 정하라는 얘기도 있었고, gnome-shell이 하드웨어 가속을 쓰려고 하기 때문에 생기는 증상이라는 의심도 들었다. Openbox를 쓰면서는 겪은 적이 없는 상황이었기 때문에 그냥 Openbox로 빨리 전환하는 걸 택했다.

Openbox를 기본으로 띄우고 뭔가 (아마도 XAtom 수준까지 내려가는) 영향을 주는 프로그램을 찾아서 똑같이 띄워주면 Firefox CSD를 그대로 쓸 수 있을까 싶어 ps 명령으로 각 환경마다 어떤 프로세스가떠 있는지도 확인해봤지만 이거다 싶은 게 없었다. 그래서 방법을 바꿨다.

lightdm에서 Openbox를 직접 선택하지 않고 다른 세션 안에서 openbox --replace 명령으로 창 관리자만 바꾸면 원하는 결과물을 만들어낼 수 있을 것 같았다. 실험해보니 파이어폭스는 여전히 CSD가 잘 동작했고, 그러면서도 Openbox의 기능을 쓸 수 있었다.

"apt-cache pkgnames | grep -- -session$" 명령으로 어떤 세션들이 있는지 찾아봤다. openbox-gnome-session 패키지는 혹해서 깔아봤지만 lightdm에서 진입하지 못하고 튕겼다. 찬찬히 보다가 선택한 게 xfce4-session이었다. 너무 요즘 거라서 gnome-shell의 영향을 강하게 받을만한 것도 아니고 적당히 독립적인 노선을 유지하기 때문에 Openbox랑 조합해서 쓸만할 것 같았다. 물론 파이어폭스 CSD도 잘 됐고, 탭 위로 여백도 없었다.

xfce4 환경은 만족스러웠다. lightdm에서 xfce4 세션을 선택하고 진입했을 때 처음 환경은 온통 xfce4 기본 프로그램들로 채워져 있었지만, 얼마든지 교체할 수 있었다. openbox --replace로 창 관리자를 바꾸고, ~/.config/openbox/autostart 명령을 세션 관리자에 등록해서 그동안 Openbox에서 쓰던 초기 실행 명령들을 그대로 살려 쓸 수도 있었다. 바탕화면에 아이콘을 보여주는 xfdesktop4는 삭제를 해도 다른 패키지 의존성에 전혀 영향을 주지 않을 정도였다. xfce4는 오직 세션 매니저만 제공하는 셈이었다.

이렇게 해서 Openbox 환경에 Firefox를 쓰면서 CSD 기능을 쓸 수 있게 되었다.

2018년 6월 2일 토요일

ZOTAC Nano CI327

윈도우 10 홈 라이센스가 동봉된, 32기가 저장장치가 내장된 모델을 굳이 샀었다. 꼭 윈도우를 써야 하는 경우가 어쩌다 있기도 하고, 저장장치가 하나라도 더 붙은 걸 고르면 쓸모가 있겠지 싶은 마음이었다.

그리고 9개월 여가 지난 지금, 32기가 디스크가 인식되지 않는 걸 발견했다. 32기가로는 단독으로 쓰기에 한계가 있어서 애초부터 SSD를 따로 붙여서 쓰고 있었다. 한동안 종종 윈도우로 부팅을 하다가 안 건드린지 한참 되긴 했는데 그래도 이렇게 속절없이 망가져 있으니 아까운 건 어쩔 수 없다. 아낀다고 뭐 많이 쓰지도 않았는데.

전화를 넣어본다 한들 수리를 할 수나 있을지 의문이다.

2018년 5월 26일 토요일

한글 soundex

구글의 Did you mean 같은 걸 어떻게 만드나 하는 생각 다음에 떠오른 게 soundex인데, 그럼 한글은 어떻게 하나 하는 생각 다음에 떠오른 게 자소 분리를 해서 알파벳으로 바꾸고 soundex를 돌리면 되겠네, 였다.
검색해보니 역시나 똑같은 구성으로 이미 만든 사람들이 있다.

영문 soundex 까지는 괜찮은 결과가 나오는 걸 봤었는데 한글 자소를 영문으로 바꿔서 하는 것도 괜찮을지는 잘 모르겠다.

2018년 4월 28일 토요일

AirPort 단종 뒤에 AirPlay 기술은 어떻게 되나?

가로 세로가 어른 손가락 길이 정도 밖에 안 되는 크기에 무선 공유기와 프린터 공유, 오디오 출력 공유 기능이 들어가 있다. 소비전력을 찾아보니 구동중에도 6와트 정도라고 하던데 계속 켜두었을 때 좀 따뜻해지는 정도의 발열을 생각하면 그보다는 좀 더 나오지 않을까 싶다.

다른 기능보다는 주로 AirPlay 때문에 쓴다. (원래는 AirTunes라는 이름이었다. iTunes와 연관성을 주고 싶었나보다.) 네트워크로 받은 음성 신호를 기기에 내장한 3.5파이 minijack 내지 원형 광출력 단자를 통해 출력하는 기능이 있고, 그 위에서 DAAP나 DACP 프로토콜 기반으로 재생과 제어를 담당하는 기술을 합한 게 AirPlay라고 할 수 있다.

유선과 무선 모두를 통해 신호를 받을 수 있기 때문에 거실에 스피커를 갖추어 놓고 방에서 iTunes로 재생하는 노래를 끌어다 듣는 식으로 쓰는 게 기본 용법이고 (DAAP 프로토콜), 아예 iTunes에 해당하는 음원 저장 및 재생/제어까지를 분리하는 구성도 가능하다. (DACP 프로토콜) iOS에 있는 Remote 앱이 제어만 분리한 형태다.



오디오 기기부터 자동차 옵션까지 몇몇 호환장비가 존재한다. 당연히 리눅스에서도 구현체가 있다. 가장 일반적이라고 할 수 있는 게 (FreeNAS에도 포함된 것으로 아는) forked-daapd로, iTunes의 재생 기능에 AirPlay로 출력을 보내는 기능이 있고 Remote 앱의 제어를 받아들인다. 음원을 구비하는 게 합법적이기만 하다면 아주 유동적인 구성으로 음악을 감상할 수 있다.

물론 전체 과정에서 핵심이 되는 건 스피커에 소리를 전달해주는 AirPlay 수신기의 존재다. 그리고 그게 AirPort 장치다.

AirPort가 공식적으로 단종된다고 발표가 났다. 새로운 기계가 안 나온지 이미 오래되긴 했다. 그리고 애플이라는 기업의 라인업 면에서는 AirPlay가 없어진다고 할수는 없을 거다. 그 사이에 Apple TV가 나오면서 AirPlay가 소리만이 아니라 영상도 전달하는 방식이 되었다. 소리만으로 한정해도 HomePod이라는 기계가 나왔다. HomePod이 AirPlay 기능이 있는 스피커라서 지금의 AirPort와 완전히 같지는 않지만.

지금 시장에 나와있는 AirPort 장치가 모두 없어지기 전까지야 AirPlay 기술이 계속 쓰이긴 할 것이다. 하지만 그 다음엔 어떻게 될까? 수중에 있는 장치가 고장이라도 나면 그 다음부터는 forked-daapd고 뭐고 써먹을 수가 없게 된다. 아니면 좀 더 연구를 해서 AirPlay 리시버 자체도 라즈베리파이 같은 걸로 구성해서 쓸 수 있으려나?shairport-sync 프로젝트가 있으니 지금도 가능한 일이다.

2018년 4월 23일 월요일

렌딧 중단

작년 여름부터 적금 말고 다른 방법도 찾아보기로 했다.
그 과정에서 P2P 금융이라는 이름이 붙은 업체들을 몇 개 정해서 조금씩 넣었다.

렌딧은 그 중에서도 5천원으로 단위 금액이 가장 적은데, 그만큼 위험이 분산되며, 소액에 세금이 붙는 거기 때문에 절세 효과가 생긴다는 설명이 그럴듯하게 보여서, 자동투자도 유지하고 개중 중점적으로 유지했다.
그런데 이제 몇 달이 지나고 보니 렌딧에서 계산해주는 수익률이 너무 떨어져 있었다. '실질 연환산 수익률' 항목은 8.37%로 낮지 않은데 '연체 채권의 추정손실률'이 7%라서 '예상 연환산 수익률'이 1.37%이 되었다. 세후 1.37이면 무난한 적금 정도로도 달성할 수 있는 이율이라서 아무래도 흥이 식는다.

2018년 4월 하순 현재 단기연체 5, 장기연체 3.

혹시 '수익추구형'으로 선택했던 수동 투자에서 연체가 발생한 건가 싶어서 채권상태 화면을 뒤적거려봤는데 그렇지도 않았다. '균형투자형'으로 만든 2017-08-21 첫 투자 100건 중에서 단기4, 장기3, 그리고 그 다음의 '수익추구형' 2017-11-23 투자 20건 중에서 단기1이 있다.
무엇보다 첫 투자에서 발생한 것들이기 때문에, 나는 이걸 '시간이 지나서 터질 게 터진' 상황으로 인지한다. 바꿔 말하면 '시간이 더 지나면 다른 투자건에서도' 연체가 더 계속 꾸준히 생길 거라고 본다.

이미 몇 년째 업계에 있었고 꽤 이름있는 업체인 렌딧의 채권에 대한 분류와 예측이 시원찮다는 의미일 수도 있겠고, 그 사이에 사람들 자금 사정이 한층 나빠져서 예측 모델을 벗어날만큼의 상황이 되었다고도 할 수 있겠지. 혹은 그냥 내가 투자에 대해서 너무 감이 없어서 지금 상황을 완전히 잘못 이해하고 있는 걸지도 모르겠다.

어쨌거나 일단은 자동투자를 껐고, 앞으로 회수되는 금액은 모두 출금할 생각이다.

2018년 4월 10일 화요일

로그 남기는 코루틴

일전에 뭔가 긴 코드를 짜다가, 로그 남기는 동작을 코루틴으로 돌리면 좋겠다는 생각이 들었었다.
어설프게 개념만 이해하고 실제로 써본 적은 없는 터여서 재밌겠다는 생각이 들었지만 내 것 아닌 코드에 집어넣기가 저어해서 진행은 하지 않았다.
그리고 오늘 마침 그때 생각이 나서 예제 코드를 짜보려고 했는데 막상 손을 대니 그럴듯한 코드가 나오지 않는다. 그때는 어떤 착상을 했던 건지 제대로 떠오르지 않고. 그냥 개념을 오해해서 잘못 생각했던 걸까?

2018년 4월 8일 일요일

kubernetes cluster 하나 설치, 일단 성공

https://blog.alexellis.io/kvm-kubernetes-primer/

위의 내용대로 br0 네트워크 인터페이스를 만들고 virt-install를 통해 vm을 생성하고 k8s를 설치했다. 손수 vm을 만들고 손수 docker와 k8s 패키지를 깔아서 세팅했을 때 네트워크 문제로 보이던 pod 실패도 없었고 반응 속도도 매우 빠르다.
br0 장치를 따로 만든거나 sysctl 설정을 바꾸고 진행한 게 vm끼리의 통신 문제를 해결한 것 같은데 어느 쪽이 맞는지는 모르겠다. 나중에 다시 궁금해지면 확인해봐야겠다.

대시보드는 github에 나와있는 명령대로 설치하고, master의 crontab에 kubectl proxy를 실행하도록 한 다음, ssh -L 명령으로 로컬 8001에 접속하면 master의 8001에 접속하게 했다.

대시보드 인증은 https://blog.heptio.com/on-securing-the-kubernetes-dashboard-16b09b1b7aca 에 나온대로 serviceaccount를 하나 만들어서 token을 썼다.

minikube라는 선택지도 있었지만, 그렇게 간단한 구성만으로는 충분히 맛을 볼 수가 없을 것 같아서 손을 대지 않았다.

2018년 4월 4일 수요일

rabbitmq priority queue support in php

https://www.rabbitmq.com/priority.html

rabbitmq 3.7.0
phpamqplib


큐 선언
    $args = new AMQPTable();
    $args->set('x-max-priority', 10);

    $ch->queue_declare($queue, false, true, false, false, false, $args);


메시지 생성
    if ($priority > 1) {
      $args['priority'] = $priority;
    }
    $msg = new AMQPMessage($message, $args);


x-max-priority 속성이 있는 임의의 큐에 10개 메시지를 priority 속성 부여한 상태로 publish 했을 때, consumer가 10개를 받는 순서대로 AMQPMessage의 priority 속성을 확인하면 숫자가 높은 순서대로 전달되는 것을 확인함

큐에 x-max-priority 속성이 없이 선언되었으면 각 메시지에 priority 속성을 부여해도 효과가 없는 것으로 보임

traefik, the kubernetes ingress controller

https://docs.traefik.io/user-guide/kubernetes/

kubernetes 클러스터도 하나 생겼으니 기념삼아 이것저것 생각나는 걸 깔아봤다.

안내문에 적힌 치즈 웹서비스를 다음과 같이 테스트해봤고, 까먹었을 때 보기 위해 적어둔다.

export ADDR=kube-node-3 PORT=32479; for h in cheeses.minikube cheddar.minikube stilton.minikube wensleydale.minikube; do curl $ADDR:$PORT -H "Host: $h" -v; done

kube-node-3은 ds가 아니라 deployment 방식으로 설치ᅟ한 traefik이 테스트 시점에 구동중인 노드였다.

원래 하고 싶었던 건 kubernetes 클러스터에 external loadbalancer를 붙이고 (그게 traefik이 되면 좋고) 거기서 다시 hostname 기반으로 분산을 하는 거였다. 외부에서 접근했을 때 traefik이 실행중인 노드로 접근하게 만드는 걸 아직 몰라서, 일단 hostname 방식으로 동작한다는 걸 확인하기 위해 Host 헤더를 강제로 집어넣었다. 원했던 대로 각 호스트의 웹서버가 각각 반응해줘서 기뻤다.

http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/ 여기서 설명하는 내용을 보면 traefik이 제공하는 ingress controller를 잘 쓰면 애초에 원하던 걸 처리할 수 있는 것 같다.

여전히 남는 의문은, 그럼 ingress controller가 동작중인 node를 어떻게 다시 공인IP의 세계로 노출시키는가 하는 점인데.

https://stackoverflow.com/a/37796383 를 보면 externalIPs라는 설정을 언급하고 있지만, 이건 hostname에 반응하는 것처럼 특정IP를 목표로 지정된 트래픽이 (미리 설정된 외부의 route에 따라) k8s 안쪽까지 들어가면, 그 IP에 반응하도록 서비스를 설정하는 것인데, 임의의 hostname을 지정할 수도 있는 것과 달리 숫자IP를 고정적으로 지정해야 해서 그다지 좋은 방식은 아닌 것 같다. 내가 찾던 방향도 아닌 것 같고.

https://medium.com/@maniankara/kubernetes-tcp-load-balancer-service-on-premise-non-cloud-f85c9fd8f43c 내용을 보면 yaml에서부터 externalIPs 설정을 적어주지 않고 kubectl expose 명령으로도 정할 수 있나보다. expose 명령의 옵션을 자세히 보지 않았던 게 잘못이다.
(이어서 tcp-echo-server라는 예제를 통해 NodePort 대신 hostNetwork:true 속성을 켜는 방법을 보여주는 것도 재밌는 방법처럼 보이지만, 권장되는 방법도 아니고, traefik을 쓸 거니까 관심사는 아니다)

expose 명령의 --external-ip 옵션에 traefik이 떠 있는 node의 IP를 지정하고 (kubernetes dashboard에 EXTERNAL-IP 항목으로 나온다) 80 포트를 80 포트로 연결하라고 한 다음에, (/etc/hosts에 kube-node-3와 같은 IP로 지정된) curl cheddar.minikube 명령을 실행하면 Cheddar라는 페이지가 출력된다. 앞서 curl에 Host 헤더를 강제로 먹였던 것과 같은 결과다.

AWS 같은 클라우드 서비스에서야 LoadBalancer 타입으로만 지정해도 expose에 사용될 공용IP를 받아서 지정하면 끝이니 복잡할 게 없이 클라우드 API 호출로 IP만 받으면 바로 끝날 동작이다.

공유기 안쪽에서 192.168.x.x 범위로 구성할 때를 기준으로 바꿔서 생각해보면 이렇게 되겠다.
  1. 공유기 밖에서 공유기 안으로 넘어오는 단계 - 포트포워딩, DMZ 등
  2. 넘어온 트래픽을 받을 내부IP를 어떻게 고정적으로 유지할지 - DNS에서 IP 고정
  3. 그 내부IP가 받은 신호가 traefik 같은 ingress controller로 넘어가는 과정을 어떻게 유지할지 - expose 명령을 쓰고, 고정된 내부IP를 external-ip로 지정

2018년 4월 3일 화요일

노조

예전에 (아마도) 금속노조에 가입했다는 어느 얘기를 본 기억이 있다.

왜 기억하냐면, 거기가 금속노조 하면 떠올릴만한 강성 파워 거친 업장이 아니어서다. 아마도 아줌마로 불릴만한 노동자가 대부분인 곳이었던 것 같다.

왜 하필 금속노조인가 물으니 여기도 쇠를 쓴다며 기왕 노조 들 거면 쎈 데가 좋지 않을까 해서 금속노조를 택했다고 답했다고 본 걸로 기억한다. 남아있는 이미지는 활기찬 인터뷰 분위기에서 마지막에 말하는 본인도 웃겨서 깔깔 웃으며 답하는 것.

오늘 네이버에서 노조가 출범했다는 기사를 봤다. 여기는 화학섬유노조, 2017년 11월부터는 화학섬유식품노조 산하로 들어갔다고 한다.

네이버가 화섬이라니, 하는 생각 끝에 일전의 금속노조 얘기가 새삼 떠올랐다. 금속노조 지부마다 지회 목록은 있나본데 하나씩 다 뒤져보기도 뭐하고 해서 정확히 찾아보진 않았다. 그렇다고 금속노조나 민주노총 게시판에 뜬금없이 '혹시 이러저러한 지회가 정말로 있는 것입니까?' 하고 물어보는 것도 분위기 상 안 맞을 것 같고.

그 금속노조인 이유가 식판이었나 하는 생각에 식당 쪽으로 검색해보니 나오긴 한다. http://www.redian.org/archive/14204 현대푸드. 근데 2006년 얘기라서 이건 아닌 것 같다.

어디 물어볼만한 데도 없고. 궁금하네.

찾았다. https://twitter.com/coke_cloud/status/955701669328666624 담터. '차'를 만드니까 금속노조, 라고. '금속노조 이색 지회' 따위로 찾았을 때는 안 나왔고, 하다 하다 '우리도 금속노조'라고 던져봤는데 덥석 나온다.

https://twitter.com/labordream/status/959698451914084352 노조를 뭘로 들든 업종이랑은 상관없다니 과연 헌법이 보장하는 권리.

2018년 3월 31일 토요일

내가 왓챠플레이를 만든다면

일전에 왓챠플레이 앱한테서 분발하라는 격려를 받기도 했거니와, 실제로도 왓챠플레이에 다달이 돈은 내고 있지만, 그다지 긴요하게 써먹지는 못하고 있다. 책을 사놓고 안 읽는 것처럼, 왓챠도 볼 수 있는 영화가 많고 찜해둔 것도 있지만 막상 보려고 고르면 이거다 싶게 땡기는 게 없다.

예전에도 어디에 적었었는지 기억은 안 나는데, 보유한 영상물들이 티비처럼 항상 준비되어 있는 건 어떨까? Kodi 류의 미디어 재생기를 보면 그런 확장기능이 실제로도 유통되고 있다. 왓챠에서라면 앞뒤로 어울리는 영화를 연결한다거나, 중간중간 다른 영화의 예고편을 틀어주는 식으로 하루종일 돌아가는 채널을 자체 구성할 수도 있지 않을까? 그러다가 중간부터 본 영화가 괜찮은 것 같다 싶으면 그것부터 새로 봐도 될 거고. (꼭 계속 스트리밍을 할 필요는 없을 거다. 미리 짜여진 시간표만 놔두고, 사용자가 실제로 접속했을 때만 그 시간표대로 틀어주면 되니까)

혹은 자체적으로 예고편이랄까, 미리보기를 만들어서 그 사람한테 소구할만한 장면을 보여주는 건 어떨까? 왓챠 안에서 영상물을 분석해서 클라이막스 직전이라거나 풍광이 좋은 장면이라거나, 로맨틱한 분위기의 장면 같은 특징 있는 부분을 조금 잘라서 미리 볼 수 있으면 그 장면이 무슨 맥락인지 궁금해지고 영화에 진입할 여지가 더 있을 것 같다. 그 사람의 축적된 취향에 맞는 장면이라면 더 그럴 테고. (어떤 영화들은 도입부가 너무 안 땡겨서 그걸 견뎌내고 다음 장면 다음 장면으로 넘어가는 게 고역일 때도 있었다. 뒤로 가면서 재밌어질지라도.)

2018년 3월 29일 목요일

perplexity

오늘 또 새로운 걸 들었다.

군집을 나눌 때 몇 개로 나눠야 좋은가를 볼 수 있는 지표라고 하는데.

진작 알았으면 조금은 덜 고민했으려나.

2018년 3월 16일 금요일

영단어 한글 표기 폰트

Opentype의 GSUB 기능을 극단적으로 써먹으면, alphabet이라는 문자의 연속을 알파벳이라는 glyph로 묶어서 보여주는 식이 가능하지 않을까?

일반적인 용법이 무리라면 고유명사에 써먹을 수 있지 않을까?

가령 아래는 PPAP라는 유명한 밈을 폰트의 GSUB 기능으로 소화한 것이다. 다운로드 받아서 ttx 명령으로 xml 포맷으로 바꿔보면 기본 영문 글리프에 더해서, 파인애플·애플·펜 그리고 그것들이 조합된 글리프가 이어진다.
  • http://dainippon.type.org/DTAP/
  • http://joonfont.com/forum/?mod=document&uid=9
  • https://github.com/dainippon-type/DTAP

그럼, alphabet을 알파벳으로 대치하려면 "알파벳" 전체를 한 글리프로 가지고 있어야 된다는 건가?
그렇진 않은 것 같다. https://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#5 설명에 따르면 GSUB도 몇 가지 종류가 있다고 한다. 위의 DTAP는 표현하려는 최종 글리프가 조합마다 모두 다른 형태여서 하나씩 따로 만든 것이겠고.

2018년 3월 15일 목요일

열쇠공방

아마 열쇠를 받아서 수시로 드나든다는 개념으로 열쇠라는 이름을 붙인 것 같다.

내가 찾는 건 이런 쪽이라고 한다.

2018년 3월 7일 수요일

ABAP, Drools

새로운 걸 들었다.

내가 잘 모르는 세계의 말이어서 전혀 못 들을만하긴 한데.

신기하네.

2018년 2월 8일 목요일

핀테크와 기술 지체

일전에 세븐일레븐에서 엘페이 결제가 매번 문제라고 적었다.

집 주변에 편의점이 여럿이고 그 중엔 노년이라고 밖에 할 수 없을 분들이 계산대를 지키는 때도 왕왕 있다.

그리고 나는 여기서도 불편을 감수해야 한다. 바코드 리더기로 차곡차곡 물건이 찍히면 통신사 포인트 차감 할인을 먼저 내밀고 그 다음에 내가 무슨 결제수단을 쓰려는지 얘기하고 폰에서 결제 앱을 켜고 지문으로 바코드를 표시한다. 그럼 계산대의 점원은 익숙하지 않아 보이는 얼굴로 이런저런 버튼을 누르고 바코드 리더기를 폰 화면이 가져다 댄다. 한 번에 될 때도 있지만 안될 때도 자주 겪는다.

방금도 그게 제대로 되지 않았다. 분명 결제 바코드를 찍고 비닐봉투를 받아들고 가게를 나서서 몇 걸음 가는데, 결제 앱에 떠야 할 영수증이 없는 거다. 통신사 포인트 차감은 기록이 떠 있었다.

결제가 안 됐구나 해서 가게에 되짚어 가니 계산대 앞의 할머니는 계산이 됐는지 어떤지도 모른 채로 다른 돈 문제로 고심하고 있었다. 이러저러해 다시 왔다고 설명해서 계산대에 남은 기록을 봐달라고 했는데 정말로 결제 기록은 없었다. 다시 결제를 했지만 현금으로 결제했다는 영수증이 뜨는 등 몇 번이나 결제가 진행되지 않다가 겨우 결제 앱으로 결제가 되었다.

그 와중에 이미 차감된 통신사 포인트는 취소가 되지도, 결제 금액에서 차감이 제대로 되지도 않고 그냥 공중에 흩어져 버렸다. 아마 다음 달 즈음에 그 가게의 차감분으로 정산되겠지. 얼마 되지 않는 금액이고 통신사 포인트는 달리 쓰임새가 많은 것도 아니어서 별 말은 안 했다.

2018년 1월 21일 일요일

어떤 생각에 도달했다

자력갱생-이라고 할 수 있겠지.

생각해보면 이런저런 호스팅 서비스와 클라우드 서비스를 두고서 나는 여전히 자체 서버를 굴리고 있고, 그게 돈 버는 일에 도움이 된다.

프로그래밍이라고 다를 게 없다는 생각이 든다. 내 뜻대로 할 꺼리가 필요하다.

토이 프로젝트 수준보다는 높게, 창업보다는 낮게, 지속적으로 손을 댈 필요가 있다.

2018년 1월 14일 일요일

선물(futures) 거래와 비동기 프로그래밍의 Future

…를 엮어서 설명하는 게 있을까나?