늘모자란, 개발

늘모자란, 개발


MySQL 포트는 아주 공개적으로 열려있다. 3306이며 포트를 바꿔도 공격에 자연스럽게 노출되게 된다. 오픈소스의 숙명이라고 할 수 있겠다.

서버 세팅한지 얼마 되지도 않았는데 어찌나 많은 불나방들이 달려드는지 골머리를 앓고 있던차에,
이녀석들을 제거하는 일을 자동화를 해야겠다고 결심했다.

그래서 fail2ban을 깔아서 처리해보려고 했으나 이게 왠일, fail2ban은 정말 SSH(22)만 검사하는 프로그램이었던것이다.
찾아보니 그냥 검사나 제때하고 포트바꿔라 이런소리나 하고 있고...
간단하게 코딩해보기로 했다. 크게 어려울것 같지 않았다. (부작용은 아직 모르겠다)

mysql.log를 남기는것은 성능에 마이너스이므로 에러로그와 슬로우쿼리만 남기고 있다.
두개 로그는 파일 크기가 그렇게 크지 않기때문에 로드하는것에도 부하가 없을 것이라고 생각(근거 없는 믿음)한다

#!/usr/bin/env python
# -*- coding: utf8 -*-

import re
from subprocess import call

path = "/var/log/mysql/"
log = "error.log"

logContents = ""

excludeText = "etc ips............................"

with open("{}{}".format(path,log)) as f:
    logContents = f.read()

extractedIP = {}
for ip in re.findall( r'\'?\'@\'[0-9]+(?:\.[0-9]+){3}\'', logContents):
    ip = ip.replace('@','').replace('\'','')
    try:
        if type(extractedIP[ip]):
            extractedIP[ip] = extractedIP[ip] + 1
    except:
        extractedIP[ip] = 1

for index in extractedIP:
    if extractedIP[index] > 2 and index not in excludeText:
        call("iptables -A INPUT -s {} -j DROP".format(index), shell=True)

f = open("{}{}".format(path,log),"w")
f.write('')
f.close()

print "Done"


내가 로그를 살펴보아하니 공격은 거의 5분안에 이뤄지더라. [아래 로그 참조]
2016-02-07T18:46:47.070457Z 519468 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:47.330309Z 519469 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:47.521742Z 519470 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:47.670816Z 519471 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:47.796906Z 519472 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:47.915819Z 519473 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.037179Z 519475 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.146458Z 519476 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.253486Z 519477 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.355799Z 519478 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.470777Z 519480 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.670311Z 519481 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:48.891320Z 519482 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:49.034734Z 519483 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:49.167179Z 519486 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:49.361079Z 519487 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:49.555965Z 519489 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)
2016-02-07T18:46:49.800100Z 519490 [Note] Access denied for user 'root'@'180.97.215.141' (using password: YES)


1분에 약 적게는 3건 많게는 5건으로 시도하기때문에 3분마다 cron을 돌게 하고,
거절 된 아이피가 3건이상일 경우 해킹이라고 생각해 iptables를 이용해 블럭 하도록 처리했다.

단점이라면, 3분안에 공격이 연속되지 않으면 로그를 초기화하기때문에 잡아낼 수 없다는 단점이 있다.
즉시 처리 하기 위해 3분이라는 짧은 텀을 주었으나 10분정도를 줘도 무방할 것 같다.

심플한 코드인데 벌써 한 50개정도의 아이피를 블럭한것같다.
이 코드는 gist에서 버전 관리 될 예정이다.
2016/02/10 11:37 2016/02/10 11:37
페이스북.
속칭 따봉책이라고 불리는 요건 이미 친구들 소식지가 아니라 광활한 스팸더미이다.
광고는 짬툰, 탑툰 등 만화도 많지만 만남을 주선하는 앱/사이트 광고도 많이 올라오는데, 왜인진 잘 모르겠지만 사랑과전쟁이라는 사이트에 들어가보고 인증까지 받아보게 되었다.



이렇게 생긴사이트다.
소셜 데이팅이라고 되있는데 광고로 인해 들어가게 되면 뭐 은밀한 만남을 할 자격이 있다.. 어쩌고 하는데, 물음에 다 NO로 해도 준비가 된 사람이라고 가입을 시켜주는 아주 친절한 사이트라고 할 수 있다. 주소는 위에 있으니 따로 명시하지 않는다.


사이트는 Apache/PHP 기반이며 호스팅을 땡겨쓰는것 같기도하고 (최상위 경로가 public_html이다)
알고 싶어 안게 아니라 메뉴를 잘못치면 include 에러로 친절하게 알려준다. 요 글은 애시당초 해킹같은걸 한게 아님을 미리 알려둔다.

리스트에서 수많은 여성들을 만나 볼 수 있었는데 페이지가 쉬지않고 계속 리프레쉬된다.
콘솔로그를 보니까 쉴새없이 POST를 보내고 받는 ajax통신을 하고 있었다.

사진은 채팅에 들어가면 보여준다는데, 나는 가난하기때문에 포인트를 충전해보진 못해서 여성분들과 대화를 나눌 순 없었다.
그래서 어떻게 사진만 못 보나 해서 찾아보다가 유저 리스트를 요청하니 디테일한 유저의 정보를 알려주었다.
어느정도 디테일하느냐? 유저아이디/비밀번호/사진 빼고 다 알려준다.


{"result_code":0,"result_command":"OK","command":"TARGET_USER_INFO","callback":false,"target_user_info":[{"user_no":"[식별번호]","user_id":"","password":"","state":"1","partner_id":"[파트너번호]","partner_code":"auto","nick_name":"[닉네임]","sex":"[성별]","age":"[나이]","area_code1":"1","area_code2":"24","phone":[전화번호],"is_certify_phone":"f","is_recv_mail":"t","is_recv_sms":"t","is_recv_push":"t","charge_count":"0","charge_total":"0","left_point_view":"0","photo_count":"0","profile_count":"0","join_datetime":"","join_ip_addr":"[IP]","join_user_agent":"","login_datetime":"","login_ip_addr":"[IP]","login_user_agent":"","regid":null,"bad_level":"0","badge_level":"0","push_option":"1","gift_send_count":"0","gift_send_point":"0","gift_recv_count":"0","gift_recv_point":"0","is_cert_identity":"f","is_cert_videntity":"t","sex_kind_code":"8","sex_favorite_code":"0","height_code":"0","weight_code":"0","blood_code":"0","etiquette_code":"0","school_code":"0","job_code":"0","favorite_code":"0","meet_code":"0","chat_subject":"","datetime":""}],"user_point":"0"}


채팅해서 전화번호를 알려줘야 되는게 아닌가 싶었지만, 일반 사용자들은 요 반환값을 볼 수 가 없으니까 뭐 괜찮나 싶었다.
하지만, 너무 궁금해졌다. 여긴 도대체 몇명이나 되는 여성 유저가 가입되어있을까? 혹시 한명이 존나 많이 가입해놓고 속칭 알바를 써서 남자들 후리고 있는게 아닐까?

그래서 1월 29일 기준,
LOVEWAR에 가입되어 있는 50만명의 데이터를 모두 긁어보았다. (정상 리퀘스트를 통해 얻어낸 값이다.)
간단히 요약하자면 가입되어 있는 사용자는 약 46~7만명이며, 여성으로 등록된 사람은 2만 2천여명이다. (남여성비가 정말...)

첫번째 의문으로 돌아가서 알바같은걸 쓰지 않나 싶어서, 여성 유저 검증에 나서기로 했다.
그렇다. 이 글은 사랑과 전쟁에 가입된 여성 유저 데이터를 파헤치는글이었던것이다.

코드

사용된 코드는 python으로 작성하였고, 브라우저로 세션을 맺어놓은 상태에서 돌렸다.
돌리기만했지 저장은 따로 안했음을 밝혀둔다. 원래는 코드가 있었으나 삭제했다. 그냥 단순히 GET을 때리는 코드여서 별 의미가 없고, 거기도 뭐.. 장사하는데니까 지장이 있을까봐..

파트너

먼저 놀랬던건 USERID 는 보여지지 않는데 파트너 아이디라는게 반환된다는것이다.
파트너 아이디가 뭔가 곰곰히 생각해보다가 뜨는거 여러개 검색해보고 결론을 얻은것은 이 DB를 공용으로 쓰고 있다는것이다
대표적으로 MATE, joyhunting 등의 아이디가 많이 떴는데 너무나 많이 겹치기때문에 다른 여지를 생각해볼 수가 없었다.

크고 작은 파트너 아이디는 약 208개가 도출되었다. 하지만 제일 많은것은 현재 보고 있는 LOVEWAR라는 아이디가 가장많았다. 2등은 약 2천여건정도..

나이

나이는 아주 다양했다. 최저령은 20세이며 최고령은 70세였다.
(아마 70세는 미성년자들이 궁금해서 할머니 핸드폰으로 인증하지 않은것인가 하지만..)
다음은 분포 표이다.

20 1188
21 2548
22 1306
23 1287
24 1308
25 1346
26 1186
27 957
28 827
29 776
30 654
31 728
32 472
33 536
34 474
35 467
36 593
37 416
38 409
39 415
40 405
41 526
42 295
43 349
44 328
45 292
46 517
47 226
48 247
49 255
50 223
51 300
52 127
53 125
54 106
55 74
56 121
57 44
58 54
59 48
60 44
61 26
62 7
63 11
64 3
65 3
66 7
67 4
68 4
69 26
70 32

21세가 가장 많고 56세가 뜬금없이 톡 튀어나온것 제외하면 젊은 나이쪽으로 편향되어 있음을 알 수 있다.
사실 좀 의외였다. 너무 젊은 층들이 이용하고 있나 싶었다.

전화번호

그 다음은 전화번호.
JSON을 보면 알겠지만 전화번호 인증이 된사람에게는 따로 't', 비인증자에게는 'f'라는 값으로 체크를 하고 있다.
희한할정도로 전화번호는 겹치지 않았는데, 아주 많이 겹치는게 하나 있었다.

'없음'이 만사천개나 된다는것이다.
총 22722명중 14714명만큼 전화번호가 등록되어 있지 않았다. 즉, 14714명은 허구의 유저일 수 있다는것이다.

아이피

궁금증이 생긴 나는 아이피를 조사해보기로 했다.
아이피도 조인/로그인 아이피를 아주 친절하게 반환해주고 있기때문에 겹치는게 있겠지 했는데 거의 겹치는게 없었다.
놀랄 노자... 그래서 C클래스만 따서 조회해보기로 했다.
겹치는 아이피가 상당 수 있었으나 프록시도 있는데다가, 통신사 아이피가 다수 있어 의미있는 데이터를 추출해내는건 실패했다.

사진

역시 사진이 궁금하다. default_photo 라는 컬럼이 있을 경우 사진을 추출하게 처리해봤다.
약 543장이 나왔다. 같은 사진은 한 5장정도 있던것 같다. 사진에 대해서는 별말 안하고싶다..

결론

사실 의미있는 결론같은건 없다. ajax 통신에 전화번호를 찍어준다는 어이없는 보안허점외에는 잘 감추고 있는 것 같다.
(아이피는 왜 알려주는지 잘 모르겠는데.. 어디 쓰나?)

단지 인증된 여성유저가 약 47만명 모든 유저중에 1.7%(약 8000여명)밖에 되지 않는다는것이다. 그중에서도 사진이 있는 사람은 500여명..
2만 2천여명으로 잡아도 매우 적은 수치임은 변하지 않는다.
그러므로 남자들은 이상한 사이버 세상말고 밖에서 찾아보는게 어떤가 조심스럽게 권해본다.
2016/01/31 23:24 2016/01/31 23:24
카페24 가상 서버(VPS)를 쓰고 있는데, 아무래도 쉐어드(Shared) 서버이다보니 자원을 공유하고 있고 내가 자원을 자꾸 드레인하고 있다는 말을 전달 받았다.
사실 말이 전달이지 먼저 차단하고 알아볼 수가 없게 해놓은 구조이다. 불평해도 개선을 안하니 몇마디 했더니 자꾸 이런식으로 서비스 하면 나가야된대서 짐싸겠다고 했다.

사실 무조건 내가 손해다. 카페24정도 규모에 딱 맞춰 서비스 하고 있었는데 별수가 있나. 서비스를 아홉시간씩 못하고 그러는데 (물론 그 시간에 엄청난 수익이 창출되진 않는다. 보통 주말 12시쯤에 맥시멈을 찍는거 같다). 하지만 서비스를 유지 못시켜준다는 협박을 듣고 어떻게 서비스를 하고 있나 무서워서. 결국 서버를 옮기기로 작정한김에, 옛날에 알아본대로 그냥 일사천리로 진행하기로 했다.

명령어들같은건 문제가 있었던 부분만 기재하였다

서버

내가 타겟팅 한 서버는 두개였다
하나는 통큰아이, 하나는 AWS 한국 리전 이었다
통큰아이는 옛날부터 보고 있었던거고 (반 임대식으로 서버 소유권을 나중에 주는 상품을 눈여겨 보고 있었는데 사라졌다), AWS는 이번에 한국에 리전이 생겨서 관심있었다.

친구에게 물어보니 AWS의 단점은 그냥.. 비싼거라고 했고 나머진 다 괜찮다고 했으나, 이왕 사는김에 완전히 독립된 서버를 확보 해놓자는 생각을 하게 됐다. 결국 통큰아이의 i7 서버를 구매. (제온을 하진 않았는데 제온도 괜찮을것 같다)

홈페이지가 좀 구식이라 신청도 힘들었다.
견적서 내는건 쉽지만 자동이체 문서를 내라는데 그냥 기타 라고만 나와있어서 문의를 하는 우여곡절까지..
자기 정보 관리에서 자동이체를 누르면 문서가 있다. 링크는 여기니까 헤매지말고 바로 결제할 수 있도록 하자.
(사실 좀 생소했던게 자동이체 관련해서 문서를 써서 메일이나 팩스로 줘야한다. 이런건 또 처음이라..)

문서를 보내주고, 돈을 입금하면 세팅에 들어가게 된다.
파티션을 LVM으로 하나처럼 해달라고 했는데 그건 어렵단다. (처음엔 해준다고 했는데 ㅠ_ㅠ)
세팅되면 문자로 안내가 오고, 메일로 아이디 비밀번호가 발송된다.
콘솔뿐이지만 가상서버와는 다른 아주 쾌적한 속도.. (사실 아무것도 없어서..

루트 패스워드를 바꿔주고 루트로 로그인하려니 루트 로그인이 안된다고 한다.
세팅하는데 대부분 루트권한이 필요하기때문에 요렇게 바꿔주고, 나중에 돌리기로 한다.

웹서버

PHP 서버를 돌리고 있으니 Apache를 하기로 한다. nginx 도 좋다곤 하지만 htaccess 설정을 새로 배우기 귀찮아서 그냥 Apache로 하기로 한다. 2.4.18 버전이 현재 나왔으나 Ubuntu 14.04 의 Apache는 2.4.7이 기본이다. 내 상식선에서 2.4.10에서 꽤 많은게 바뀌는걸로 아는데 아직도 2.4.7인걸보고 PPA를 사용하기로 한다.

PPA를 이용해 아파치를 깔았는데 http2를 지원해보고 싶어졌다.
대세라고 했는데, 이때까지만 해도 http2 가 반드시 SSL을 요구하는줄 몰랐다 -_-;
크롬으로 플러그인까지 깔아가면서 되나 확인했지만... 다들 나같은 삽질은 하지말길 바란다....
아파치로 http2를 지원하기위해선 mod_http2를 사용하면 되고, 이렇게 세팅해주면 된다.
다만 새 서버에 인증서가 없기때문에 어떻게 테스트는 못해봤다. 삽질만 실컷하다가 나중에 인증서는 LetsEncrypt를 사용하기로 생각하고 다시 세팅에 신경을 쓰기로 했다.

일반적으로 apt-get 으로 apache를 설치하게 되면 prefork 로 설치되기때문에 /etc/apache2/apache.conf에 서버 리밋을 잔뜩 늘려줬다.
 <IfModule prefork.c>
      StartServers 20
      MinSpareServers 100
      MaxSpareServers 100
      MaxClients 500
      ServerLimit 500
      MaxRequestsPerChild 0
 </IfModule>

PHP

다음은 PHP를 깔아줘야했다. 아시다시피, PHP7이 release 되었다
새 서버에서는 좀 삽질을 해도 PHP7을 써보고 싶었다. 그렇게 빨라졌다는데..
역시 같은 아저씨가 만든 PPA를 사용해서 설치했다. (memcached까지 같이 설치해줬다.)
설치는 apt-get install php7.0 이런식으로 해야하고, 부가적으로 php7.0-mysql / php7.0-dev(안깔면 phpize가 안된다)를 깔아줬다.
phpinfo()를 돌려보니 잘되는거보고 github에서 소스를 내려받아 돌려봤다. 될리가 없다...

문제는 두개로 생각했다.
하나는 redis, 하나는 mysql 인데.
redis는 phpredis 라이브러리를 사용하고 있었는데 양덕들이 phpredis-php7 브랜치를 만들어놓고 있었다.
똑같이 설치하고, php.ini 에 extension=redis.so 적어주는걸로 해결. 문제는 mysql이었다.

php7.0에서 mysql_connect와 같은 일반 mysql 함수를 모두 삭제해버린것이다...
PDO를 사용해서 prestmt를 진작 사용했어야했는데 귀찮음이 낳을 결과였다... 일단 당장 사용할수가 없게 되버렸기때문에...
서버 세팅을 잠깐 접어두고 모두 pdo로 고쳐야했다 ㅠㅠ... 욕을 얼마나 했는지... 귀찮다고 막 코딩하지 말자.............ㅠㅠ
만들면서 느낀점은 sql 을 처리하는 부분을 통일해서 처리해야겠다는것이다. 그리고 왜 프로시저를 쓰는지도 이제 이해했다... 빠른게 문제가 아니고, (거의 이런 대규모 수정은 일어나지 않겠지만) 수정할때 너무나 불편하기 때문이다........................

eregi 같은 패턴 검사도 deprecated에서 완전히 removed되었다. 꼼꼼히 참고 안하면 에러를 마주할 수 있다.

MYSQL

mysql은 처음엔 옮길생각이 없었는데 웹서버가 너무 빠르다보니 잦은 질의로 인해 DB서버가 죽을려고 해서 결국 옮기려는 계획을 세웠다. 다만 기존엔 5.6을 썼는데 이번엔 5.7로 세팅해보기로 했다. download 에 들어가면 각 운영체제 버전별로 deb를 받을 수 있게 되어 있는데 나는 ubuntu 14.04 이기때문에 요녀석을 받아 설치했다. 공식 PPA라고 생각하면 되겠다.
dbkg -i *.deb(mysql 5.7을 선택하고 Apply)
apt-get update
apt-get install mysql-server
하면 아주 간단하게 설치가 완료된다.  자세한 세팅은 알아서 하자.

덤프하는게 문제였는데, 옛날엔 replication을 구성해서 썼었으나 이젠 이원화되있던 서버를 불러들이고 가상서버를 정리해야하는 과정이었기때문에 어떻게 해야 최대한 서비스에 지장이 없을까 고민을 좀 했었다.
리얼타임으로 한 로우씩 불러와서 인서트 해주는 방법을 생각해봤는데 그냥 mysqldump를 이용하고 wget으로 sql을 불러와 넣는 방법을 생각했다. 나같은경우는 바로 데이터베이스 서버를 옮긴게 아니라서 서비스를 유지하고 있었다.
mysqldump -A -u{ID} -p > data.sql
wget {server}/data.sql
mysql -u{ID} -p < data.sql
약 7기가정도의 sql파일이 만들어졌고 이를 당겨와서 인서트했다.
물론 데이터 유입은 차단했다. 약 30분정도의 갭이 발생했으나 작업시간이 새벽 다섯시~여섯시쯤이라 거의 유실은 없었다.
이후에 웹서버가 바라보는 mysql 서버를 localhost로 수정해주는것으로 아주 쉽게 작업이 끝이났다.

mysqltuner를 이용해서 권장해주는 설정대로 세팅했다. 24시간이 걸리지만 일단은 그냥 설정했다. 추후에 설정하고..
다만 쿼리가 5.6에서 돌아가는것과 5.7에서 돌아가는게 조금 달라서 (정렬쪽에서) 이는 나중에 수정해주기로 했다.

nodejs

노드를 쓰기때문에 노드를 깔아줘야했다. 노드는 5.4.1이 현재 stable이고 lts로 4버전대가 형성되어 있다.
이왕하는김에 최신버전으로 도배를 하기로 했고 다음명령어를 통해 최신버전으로 깔아주었다. (관리는 npm이 편하다)
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
sudo npm install forever
forever까지 설치해줘서 준비 끝!
(그 외 필요한 모듈은 그냥 npm에서 설치해준다.)

mysql 데이터를 connection pool 해서 사용하는데 이후에 수정해주었다.

crontab

크론 서비스들도 옮겨줘야했다. 꽤 많이 등록되있기때문에 까먹으면 안됐다.
나의 경우 crontab은 대부분 wget으로 어떤 페이지를 호출하는 역할을 많이 담당하기때문에 우선은 옛날서버에서 데이터를 다 받아와야했다. 주 서비스들은 당연히 git으로 관리되고 있지만 그외엔 ftp로 일일히 다받아야했다. 지져스 (당연히 메인소스 외의 기능들은 막아놔야했고 소스코드도 PDO로 고쳐야했다. 정말 후회막심이다..)

또한 옛날엔 파이썬을 몰라서 php로 크론을 작성했었는데, 파이썬이 제대로 돌아가는지 확인해야했다.
대표적으로, 파이썬을 막 설치해서 python-mysqldb 를 설치해줘야했다. (pip인줄알았다. 설치한지 너무 오래되서..)

DNS

이제 본격적인 삽질에 돌입할 차례이다.
호스팅업체들은 대부분 네임서버를 따로 두어 아주 쉽게 할 수 있으나 나는 서버가 그냥 박혀 있는 상태이기때문에 DNS를 제공하는 네임 서버도 내가 만들어야했다. 도메인은 내가 관리하고 있는게 아니라, 대신 질러준 펭귄 이 관리하고 있어서 같이 합을 맞추며 작업했다.

bind9를 설치하고, zone을 만들어줘야하는데 이 설정이 보통 귀찮은게 아니다
여러군데 많이 참고 했으나 가장 많이 참고한 DigitalOcean 의 글이 가장 도움되었던거 같다.
DNS 작업은 내가 잘한다고 바로 반영되는게 아니기때문에 약간 시간과의 싸움이다. 인내를 가지고 기다리자ㅠㅠ
그래도 DNS는 그나마 쉽게 적용되었다. 수없이 많은 ipconfig/flushdns 이후에 되는거보고 환호성을 지른것 같다. (잘 따라하면 된다..)

다만 나는 ip가 한개이기때문에 약간 제약이 있다.
네임서버는 (왠만하면) 두개의 아이피로 구성되어야한다. (ex, ns.fantazm.net / ns2.fantazm.net)
하지만 권장이지 반드시 따라야되는건 아니기때문에 알아만 놓자. 나같이 아이피가 딱 한개인사람은 별 수가 없다.

다만 MX가 정말 골치였는데 표기되는데 너무 아리송하게 표기되어서 고생했다
MX는 진짜 반영이 잘 안되는데, 여기서 시킨대로 설정하면 된다. 다만 alt1.l.google.com.@domain 이런식으로 뜨는데 실제로는 안붙여도 된다.
여전히 CNAME은 잘 안되고 있는데 큰 문제는 아니라 넘어가기로 했다. 여기서 시간을 어마어마하게 까먹었다...

우여곡절끝에 요렇게 서버 세팅이 마무리되었다.
자잘자잘한 문제는 알아서 수정하고, 큼지막한 줄기만 적어봤는데 누군가에겐 도움이 되길 바라며~
2016/01/19 16:41 2016/01/19 16:41

2015년

Gossip 2015/12/31 23:59
다시 돌아오지 않을 이시간. 2015년의 12월 31일.
작년에 똑같은 글을 썼는데 아마 잘 안지켜졌을것 같다. 딱봐도 그렇다...
작년의 다짐을 복사해와 인용해본다.

2015 회고

논문

논문은 두세편 정도 쓰려고 한다. 그 이상은 쓸 주제가 있을진 잘 모르겠는데, 아무래도 웹 관련해서 주제선정하고 쓰는데 많은 시간을 보내게 될 것 같은 해이다. 2014년 한학기 동안 논문을 안써서.... 논문에는 많은 시간을 투자해야될 것 같다. 괜찮다. 1년은 기니까.주제는 웹으로 꾸준히 밀 생각인데, 글쎄 웹 관련 논문부터 많이 읽어봐야겠지도서는 딱 오십권을 읽는걸 목표로 하자.
50권.. 정말 지랄염병이다.  열권정도는 읽은 것 같다.
논문은 정확히 세편썼다. 사실 학술대회에 낸거라 질이 아주 저질이지만, 한학기에 하나씩은 써낸것 같다.
제주도에도 다녀오고, 필리핀에도 다녀올 수 있었던건 이 논문같지도 않은 논문들 덕을 봤던것 같다.

2015.04. 한국 정보 처리 학회(KIPS) SDN을 활용한 네트워크 검역시 패킷캡쳐 기능 개선 방안
2015.06. 한국 정보 통신 학회(KICS) SQN에 적용 가능한 XSS 우회공격 필터링 방법 제안
2015.12. CUTE 2015 Design and implementation of packet reassemble module using a multi-queue for network quarantine in SDN

웹으로 쓰고 싶다고 했지만 XSS 정도가 웹이라고 할 수 있으려나. 결국 과제따라가게 되는건 별 수 없는 것 같다.
하지만 그 시간이 헛되었다고 생각하진 않는다. 많은 공부를 했고 논문도 꽤(?) 읽었다

개발

역시 개발이 주제에서 빠질 수 없는데, 조금 장황한 계획이 있다.웹은 계속해서 할 생각이고, 생각나는데로 적어보자면python, ruby, scala node.js + framework - meteor나 express 등PHP - framework 체험도 체험이고, 가을에 나올 7.0도 개발해보고 싶다. 또한, 싴갤러스 클래스화를 진행하고..c# - c#은 정말 한다한다 하는데 하질 못하는 그런녀석이다. 꼭 해보고 싶다.java - android 개발자 등록하고, 푸시앱을 왕창 만들어보고 싶다. 다 개인용도로 쓰는거지....그리고 git와 github을 이용해서 버저닝을 할 계획이고, 전체적인 언어의 깊이를 주고 싶은 한해가 될 것 같다.너무 많은 계획이 아닌가 할 수 있겠지만 어느정도는 다 해본 녀석들이라... 새로운 도전은 없어뵌다.파이썬 루비 스칼라는 마스터는 못해도 개념은 반드시 알고 넘어갈 계획이고 python같은경우는 flask나 django를 이용해서 서비스 하나를 만들어보는게 목표다. 크던 작던 일단 만들어야 뭘 할 것 같다.
개발은 python 외에 손댄것이 하나도 없다.
다만 git을 적용해 체계적으로 개발을 하게 되었다(고 생각한다)
스칼라는 계획에서 빠졌고, ruby 도 ror열풍에 잠깐 흘끔거려봤으나 결국은 손도대지 않았다.
2016년의 목표라면 C와 C#을 다시 할 계획이고(이제 취업을 생각해야되니까?), 싴갤러스의 고질병이던 DB ERROR를 잡아내는데 성공한 한해였다(고 생각한다)
하지만, 역시 다짐과는 달리 언어의 깊이를 더했다고 하기엔 무리가 있을 것 같다. 나는 여전히 얕고, 더 노력해야만한다.

운동

그리고 운동을 할려고 한다. 반 강제로 시작하게 될 것 같지만, 건강이 적신호인건 내가 누구보다 잘 알고 있다몸뚱아리도.... 좀 깔끔하게 살기 위해서라도 운동을 해서 움직이는 습관을 들여야할 것 같다.학교 헬스장을 이용하면 좋을 것 같고, 구체적인 계획을 세워서 강제로라도 시간 맞춰 운동해볼 계획이다. 한시간만이라도..

운동이라는 항목이 있는것도 웃긴다. 맞다. 헬스장 3개월 등록하고 3일 가서 운동했다.
온몸이 너무 아파서 딱 하기 싫던데 정말 파오후들은 이걸 이겨내지 못하기때문에 돼지가 되는 느낌이다. 다만, 밤마다 먹는 맥주를 자제하고 건강도시락을 먹는등 나름의 노력은 하고 있다.....


공모전
2015.06. SKKU ICC BugBounty 특별상
2015.12. SKKU Security Idea Contest 최우수상

2015년엔 뜻밖의 소득이 있었다. 1학기에는 학교에서 열린 버그바운티, 2학기엔 아이디어 공모전에서 수상하는 쾌거가 있었다.
사실, 온전히 나만의 능력으로 해낸건 아니라서 조금 찝찝하다. 버그바운티는 Jake에게서, Idea Contetst는 수행하고 있는 과제에서 영감을 많이 얻었다. 그치만, 분명 축하해야 될 일이다. 잘했다 나!


2016



역시, 다짐의 순간이다. 바쁘게 살아야할 해이다.
이제 절벽에 몰렸다. 나는 나를 믿는다.

졸업

좋든 싫든 반년뒤 학교를 떠난다. 벌써 2년인가 싶은데 시간이 참 빠르다는 생각이 스쳐지나간다. 세미나를 준비하면서 밤을 새고 핫식스도 자기 최면 건다고 하루에 서너캔씩 먹던 날도 있었는데, 벌써 연구실의 최고 선배(박사를 제외하고)가 되고 연구실을 떠날날을 손꼽아 세어야 되는걸 보니 기쁘기도하고 벌써 두렵기도 하다. 하지만, 나는 나를 믿는다

논문

이 칼럼이 여전히 존재하는 이유는 석사학위를 위한 논문을 써야되기때문. 약 40페이지 정도의 논문을 작성해야하나, 예심정도의 길이라면 LNCS 폼으로 15장정도 작성하면 될것 같다. 하지만, 단순히 페이지 채우는게 목적이 아닌 앞으로 계속 따라다닐 논문이니 깊이 있는 논문을 작성하고 싶다. 글쎄, 잘 될지는 모르겠지만 차근차근 준비해보려고 한다.

개발

C를 공부할 생각이다. 아울러 c#까지.
나는 웹을 계속 공부하고 싶지만, 넥슨과 NC등 모집요강을 보면 여전히 C 개발자를 모집하고 있고, 선택하고 있다.
C는 언어중 가장 기본이 되는 언어이며, 앞으로도 가장 중요한 언어라고 생각한다. 그래서 조금 뜬금없지만, C를 다시 공부하려고 한다. 최종적으로는 프로그램을 개발해 서비스해보는것이 목표이다.

건강

최근 술만 먹으면 헛구역질도 하고 좀 상태가 이상하다. 생체리듬은 27세에 깨진다는 말이 있는데, 각별히 조심하고 내년엔 폭식하는 습관을 고쳐보고자 한다. 잘 될지는 모르겠지만, 2015년 말에 시작한 도시락 업체로부터 도시락 받아먹는걸로 시작(... 하지만 잘 지켜지진않는다...ㅠㅠ)

내 원래 좌우명이자 좋아하는 말은
위험에 처했을때 굳이 너와 내가 같이 도망칠 필요는 없다. 내가 너보다 더 빨리 뛰면 되니까.
였다.

하지만, 나는 남들보다 빨리 뛰지 못했고, 이제 절벽끝에 몰려 위험한 달리기를 하고 있다.
나는. 나를 믿는다. 나는 잘할 것이고, 잘해야만 한다. 그리고 그렇게 할 것이다.
2016년, 도약의 해가 되줄 것이라고 굳게 믿는다.
2015/12/31 23:59 2015/12/31 23:59
우선 실시간으로 공격당해서 너무 어이가 없다.
왜냐면, 너무 당연한건데 처리하지 않은 나한테 어이가 없는게 젤 큰것 같다.

우선, 어제 노드 스크립트를 좀 수정하고 redis 키를 갱신하도록 코드를 짰는데, 낮에 갑자기 뜬금없이 모든 키들이 증발하기 시작했다. 나는 코드에서 이슈가 나는줄 알고 얼른 복구해봤지만 그대로 날아가긴 마찬가지..
답답한마음에 redis 키를 살펴보던 와중에 정말 재수가 좋았던건지 이걸 발견하게 된다.

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcuHEVMRqY/Co/RJ5o5RTZmpl6sZ7U6w39WAvM7Scl7nGvr5mS4MRRIDaoAZpw7sPjmBHz2HwvAPYGCekcIVk8Xzc3p31v79fWeLXXyxts0jFZ8YZhYMZiugOgCKvRIs63DFf1gFoM/OHUyDHosi8E6BOi7ANqupScN8cIxDGsXMFr4EbQn4DoFeRTKLg5fHL9qGamaXXZRECkWHmjFYUZGjgeAiSYdZR49X36jQ6nuFBM18cEZe5ZkxbbtubnbAOMrB52tQX4RrOqmuWVE/Z0uCOBlbbG+9sKyY9wyp/aHLnRiyC8GBvbrZqQmyn9Yu1zBp3tY8Tt6DWmo6BLZV4/ crack@redis.io


RSA 키인데, 키 이름은 crackit 이었다. 뭔가 깨림칙해서 구글링해보니...

http://antirez.com/news/96

이 링크부터 시작이다.
결론은 대부분의 redis 사용자들이 bind를 0.0.0.0 이나 로컬인 127..... 로 하기때문에, 텔넷을 붙여보면 쉽게 돌파할 수 있다는 내용이었다. 그거에 라이브로 털리고 있고 -_-

단순한 장난같아보였는데, 키는 키대로 다 날아가고 있고.. 특히 나는 세션을 레디스로 관리하기떄문에 사용자 세션이 정말 승천하는 중이었다. bind 세팅을 바꿔보니 제대로 붙질 못하고, 호스팅 업체에서 제공하는 파이어월을 세팅하니 노드가 못붙는다... 왠진 모르겠다.

결국 아이피테이블로 모든 접근을 제한하고, 웹서버에서 요청하는것만 붙을 수 있도록 정책을 수정했다.

iptables -A INPUT -p tcp -s IP  --dport PORT -j ACCEPT;
iptables -A INPUT -p tcp --dport PORT -j DROP
iptables -nvL


redis자체에서도 AUTH를 해줄 수 있으니,
소스코드를 수정해서 보안적으로 접근할 수 있도록 해야할 것이다. 당연히 오픈소스인 MySQL 도 그렇고...
보이는것 외에 보이지 않는것에도 신경을 제대로 써야겠단 생각이 든다
2015/11/10 16:13 2015/11/10 16:13
이 글은 HSV(HSL) for DB query - Sort color by lightness 에서 이어진 글입니다.

HSL은 밝기 기준으로 색을 정렬하는 기준이다.
하지만, 무작위 배열을 선택해 정렬했을 때는 전혀 정렬한것 같게 보이지 않는다는 단점이 존재한다.
복습해보자면, 간단한 쿼리로 HSL을 계산해서 색을 정렬했었고 그 결과는 다음 이미지와 같다.



사실 표본이 작기 때문에 이정도로도 충분히 정렬되었다고 판단할 수 있다.
하지만 모든 표본을 나열해보면 그렇지가 않다.
다소 길지만 비교를 위해서 그냥 올려본다. 양해를 구한다.



모든 표본을 나열했을때 밝음에서 어두움으로 넘어오곤 있지만, 사실 밝기 diff는 몰라도 사람의 시각으로는 이 데이터는 '정렬' 되었다고 말할 수가 없다.
물론, HSL이 쓸모가 없다는것은 아니다. 단지, 무작위 데이터를 처리할때 한가지 더 처리를 해줘야된다는것을 얘기하고 싶은것이다.

color Difference를 구하는 방법은 여러가지가 있으며 위키피디아에서 다수의 방법을 확인할 수도 있으며,
휘도(Luminance)로 정렬하는 방법도 존재한다.

하지만 휘도는 결국 Brightness 로서 HSL과 별 다를바가 없으며, 정렬햇을때도 결과가 비슷했다.
여러 방향으로 접근을 해봤으나 결론은 그라데이션(gradient)를 구현하려면 한가지 처리를 더 해야겠다고 생각했는데 (말은 쉽게하지만 나름 고민 하느라 많은 시간을 보냈다 ㅠㅠ) 그러다가 이 글을 보게 되었다.

내가 얻은 결론은 한가지 색을 뽑고 그 색의 color DIff를 계산해서 근접한 값을 모아줘야된다것이다.

데이터가 200개도 안되는 작은 숫자였기때문에, 선택 정렬(selection sort)을 이용해 정렬을 해볼까 했다.
2단 루프로 o(n^2)로 엄청나게 느리지만 표본이 적고, 먼저 HSL로 정렬된 값을 뽑기때문에 가능할거라 생각했지만..
중복처리를 하려면 답이 안나와서 결국 멋대로 작업해보기로 했다.
루프를 하나 돌고, 어레이를 삭제하고 초기화하는 좀 극단적인 방법이지만 별 다른 수가 떠오르지 않았다.....ㅠㅠ

색 판별은 delta E(CIE 2000) 알고리즘을 사용하기로 했다. color diff를 계산하는 방법은 여러가지가 있었으나, 색사이의 distance를 계산해야했기때문에 이 알고리즘을 선택했다.
라이브러리르는 직접 구현하지 않고, github에서 찾아다 썼다.

$sArray = Array();
$sArray[0] = $array[0];
$count = 0;

$i = 0;
while ( count($array) ) {

    $diff = "";
    $pointer = "";

    for ($j = 0; $j < count($array); $j++ ) {
        $cDiff = (new color_difference())->deltaECIE2000([ $sArray[$i]['colorR'], $sArray[$i]['colorG'], $sArray[$i]['colorB'] ], [ $array[$j]['colorR'], $array[$j]['colorG'], $array[$j]['colorB'] ]);
            if ( $diff == "" ){
                $diff = $cDiff;
                $pointer = $j;
            } else {
            if ( $cDiff < $diff )  {
                $diff = $cDiff;
                $pointer = $j;
            }
        }
    }

    $sArray[] = $array[$pointer];
    if ( $i == 0 ) {
        unset($array[0]);
    }
    unset($array[$pointer]);
    $array = array_filter($array);
    $array = array_values($array);
    $i = count($sArray)-1;

}


그 결과이다.


아래 그림은 최종본은 아니고, 실제로는 정렬되어있다.
재밌는 점은 분명히 이렇게 엮일거같다고 생각한 색들도 알고리즘에 의해 계산된 숫자 수치로는 다르다는점이었다
사람의 시각으로 정렬하는게 맞겠지만.. 일단은, 여기까지!



색상 정렬 (Color sort) 맺으며.. 에서 이어집니다.


Reference
https://en.wikipedia.org/wiki/HSL_and_HSV
https://en.wikipedia.org/wiki/Color_difference
https://github.com/renasboy/php-color-difference
2015/10/20 03:16 2015/10/20 03:16
PHP 갖고 논지는 오래됐다.
문득 이런생각이 든게, 프로젝트도 커지고 했는데, 언제까지고 나만 이 프로젝트를 관리하려나 싶어졌다.
(대부분의 한국인들은 editplus나, sftp, ftp를 지원하는 에디터를 이용해 소스를 바로 수정해서 올릴거라 생각한다.
왜냐면 PHP는 별도의 빌드가 필요없기 때문이다. 아니면 별도의 개발서버를 둬서 한번에 다 덮어쓴다던가..)

누군가와 협업하게 된다면 버전관리와 diff 체크를 해야할텐데, 지금까지의 형태론 도저히 답이 안나왔다.

그래서, delpoy를 하는 방법에 대해 찾아보기로 했다.
우선 phpschool 과 생활코딩에 물어보니, php는 빌드가 필요없이 즉각 적용되기때문에 어떻게든 올리기만하면 된단다.
그래서... 다들 따로 관리는 안하는구나 생각했다.

나는 우선 버전관리는 별도의 git 서버를 구축하지 않을것이라 굳게 다짐했기때문에,
github를 이용하기로 했다. private repo를 다섯개를 사용하기때문에 비공개적으로 관리할 수 있을것이라 생각했다.
깃헙을 사용하지 않겠다면 gitlab이나 bitbucket 과 같은 서비스도 있으니 고려할 수 있겠다.

우선 ftp 로 파일을 싹 받고 .gitignore를 설정했다. 자주 바뀌고 추가되는 이미지들은 버전관리할 필요가 없기 때문이다.
http://dolfalf.tistory.com/58
http://trend21c.tistory.com/1471

두곳을 참조해 적당한 룰을 만들면된다.
로컬 세팅은 귀찮음에 의거해 따로 하지 않기로하고, push 관리같은것도 github desktop을 사용하기로 한다.
즉, IDE에서 수정한 내용을 compare하고 push 하는 git의 기능을 그냥 프로그램에 전적으로 (-_-) 의존하기로 해본다.

git사용을 극대화하기 위해서, github에서 만든 atom 에디터를 사용하기로 했다.
atom은 현재 .git와 비교해 달라진 파일, 생성된 파일을 별도로 표시해준다.



내용을 변경했다면 github desktop에서도 확인할 수 있다.



여기서 pull request나 commit을 관리할 수 있다.
이후 sync 버튼을 클릭해 파일을 싱크하고, 해당 작업이 완료되면 서버의 ssh로 들어가 git 명령어를 친다.
서버에서 git 명령어를 사용할 수 있도록 설치되어 있어야한다. (apt-get install git)

SSH등 여러 방법이 있지만 나는 아주 귀찮았기 때문에, 별다른 등록이나 인증과정없는 HTTPS를 사용하기로한다.
사용법은 이렇다.

git clone https://{user}:{password}@github.com/{user}/repository.git {direcotry}
git pull https://{user}:{password}@github.com/{user}/repository.git {direcotry}


git clone과정은 현재 버전을 받기 위해서 하는 과정이고, 실제 가장 많이 쓰는 deploy는 git pull이다.

사실 git에 대해 자세히 알고 싶을수도 있는데, 그런 사람들을 위해 쉽고 빠르게 이해할 수 있는 여러 링크를 준비했다.
http://classic.scottr.org/presentations/git-in-5-minutes/?hc_location=ufi
http://blog.outsider.ne.kr/865
http://blog.outsider.ne.kr/866

특히 outsider 님의 글은 이 프로세스를 거의 모두 설명하다시피 하고 있으니 참고하면 좋겠다.

git pull을 하면 다음과 같은 화면을 보게되고, 정상적으로 deploy되게 된다.
# git pull https://{user}:{password}@github.com/{user}/repository.git {direcotry}
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 7 (delta 6), reused 7 (delta 6), pack-reused 0
Unpacking objects: 100% (7/7), done.
From https://github.com/{user}/repository
 * branch            HEAD       -> FETCH_HEAD
Updating ...
Fast-forward
 *.php  |    2 +-
 *.php  |    2 +-
 *.php |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)


나처럼 버전관리는 하고 싶은데, 귀찮다면 이렇게 하는 방법도 있다고 소개해본다.
2015/09/29 16:56 2015/09/29 16:56

1년

Gossip 2015/08/24 20:38
1년전 오늘 8월 30일.

수원. 그리고 성균관대학교 대학원에 입학하게 되었다.
그동안 나는 무얼했나 되새겨보면 정말한게 없는것 같다.
누군가 석사과정은 글쓰기를 연습하는 시간이라고 하던데, 글쓰기 능력도 그렇게 나아진것 같진 않고 오히려 퇴화한 기분이다.

연구실에서는 많은것을 배웠고 수업은 글쎄. 썩 만족스럽진 않고 별로 중요하게 생각되지 않는것이 사실이다.
연구실 과제들중 도움되는 과제들도 있었다만 대부분 문서 작업이었으며 문서는 대부분 그냥 보여주기식이 대부분이다.
정보 검색 대회하는 느낌이며 이만큼 찾아봤다 하고 과시하는 느낌이 강하게 들지만. 리서치라는게 그런것인가 하고 넘어가고..

시간 흐름대로 한 일을 짚어보자면,

4월, 6월에 논문 한편씩 발표를 하고,
교내에서 열린 버그바운티 대회에 참여해 3위 입상. 이게 큼지막한 이벤트고 나머진 공부..? 글쎄.

이론적으론 좀 강해진 기분이 들지만,
1년전의 아무 선택지가 없어서 이 곳에 올수밖에 없었던 내가. 거의 시궁창급이었던 내가.
지금의 나에게 얼마나 더 나아졌냐 하고 물어본다면 할말이 있겠나 싶다.

앞으로 1년 남았는데
예심/본심을 쓰는 기간이다.
처음에는 단순히 석사라는 타이틀만 달러 왔는데
어느 누구도 내 앞길을 책임져주진 않는다.

쌓여만 가는 학자금대출 빚을 보고 있으면 마음이 무겁다.
출세해야지. 그럴려면 공부해야지.
2015/08/24 20:38 2015/08/24 20:38
디비 쿼리 최적화는 정말 끝나지 않는 싸움같다.
싴갤러스에서 사용하고 있는 쿼리도 여러번 바뀌었는데 다 기억은 못하고..

글 내용을 말하기에 앞서 테이블 구조부터 소개를 하자면,
아래와 같이 생겼다. 아주 심플한 테이블이라 할 수 있다.

CREATE TABLE `TABLE` (   
`index` int(11) NOT NULL AUTO_INCREMENT,   
`date` varchar(10) NOT NULL,   
`time` varchar(10) NOT NULL,   
`nick` varchar(30) NOT NULL,   
`data` varchar(200) NOT NULL,  
 PRIMARY KEY (`index`),   
 KEY `indx_date` (`date`),   
 KEY `indx_nick` (`nick`) ) 
 ENGINE=InnoDB AUTO_INCREMENT=4016741 DEFAULT CHARSET=utf8


사실 처음에 몇만건 몇십만건 됐을때는 아무렇게나 짜도 무리가 없었는데 백만단위로 들어가면서부터 검색 범위를 제한하게 됐다.
유쾌하지 않은건 사실이다. 성능 때문이라는 변명에 괜히 숨는 느낌이라고 해야하나...
그래서 항상 고민이 많다. 이 글을 작성하는 시점에는 멀쩡한데 저녁에 서비스가 몰리는 시간엔 어찌될지...

검색 쿼리는 다음 조건을 가진다.
1. 날짜 범위 지정
2. 닉네임은 매칭 / 내용은 와일드카드 검색
3. 최근 순서대로 정렬

이를 간단히 생각하면 다음과 같이 작성해볼 수 있을 것 같다.
(실제로 생활코딩에 질문을 올렸을때 어떤 누가 이런식으로 쿼리를 짜서 주었음)

select `index`, `date`,`time`, `nick`, `data` from TABLE 
where `date` BETWEEN '20150709' AND '20150716'  and `data` like '%검색어%' OR `nick` = '검색어' 
order by date desc


별 다른 특이사항이 보이지 않는 쿼리이다. 과연 성능은 어떨까?

Explain extended 를 사용하여 쿼리가 어떻게 수행되는지 확인을 해보자.
SIMPLE    TABLE    ALL    indx_date,indx_nick                387809    100.00    Using where; Using filesort


검색은 SIMPLE하고, possible한 key도 기재가 되지만, 실제로는 인덱스를 타지 않는다.
왜냐면 컬럼중에 인덱싱 범주에 포함되지 않는것들이 다수 있기때문이다.
또한 검색에 소요되는 Row의 수도 거의 테이블 전체를 seeking 해야하는 수준이며, 검색 조건중 반드시 배제되어야 된다는 filesort를 통해 order by를 수행한다.

따라서, 위 쿼리로 서비스를 했다간 그냥 db가 뻗어버릴것이다

그래서 두번째로 생각한것이 서브쿼리를 이용해보는것이었다.
내 착안은 이렇다.

1. 날짜 범위별로 검색범위를 제한한다.
2. 서브쿼리에서 배달된 (delivered) 쿼리중에서만 추가검색을 진행한다.

결론부터 말하자면 근접한 방법이긴한데 정답은 아니다.
쿼리를 보면서 계속하자
SELECT `index`, `date`,`time`, `nick`, `data` 
FROM     (         
                 select `index`, `date`,`time`, `nick`, `data` from TABLE
                 WHERE `date` BETWEEN '20150700' AND '20150716'
              ) as origindata
WHERE (    `data` like '%검색어%' OR `nick` = '검색어' )
ORDER BY `index` DESC


서브쿼리를 이용해서 인덱싱이 되어있는 Date 컬럼을 이용해 범위를 제한하고,
나온 결과에서만 검색을 수행하겠다는 계획이고, 실제로 위 쿼리로 서비스를 오래 했다.

이 쿼리의 explain은 다음과 같다.
1    PRIMARY    <derived2>    ALL                    177838    100.00    Using where; Using filesort
2    DERIVED    TABLE    ALL    indx_date                386966    45.96    Using where


읽는 순서는 역순으로..
인덱스를 타라고 일부러 서브쿼리로 나눠놨는데 위에서 언급한것과 마찬가지로 인덱싱이 되지 않는 컬럼을 select하기때문에 결국 전체 테이블을 seeking하게 된다.

결과적으로 놓고보면, 첫번째 쿼리와 다를게 없다....
filesort도 그대로 사용하고 있고, 인덱스도 여전히 이용하지 않고 있다.

그럼 어떻게 해야할까? 우선 인덱스를 타게 해야겠다고 생각했다.
그래서 쿼리를 좀 고쳐봤다. 요렇게
select b.`index`, `date`,`time`, `nick`, `data` from TABLE as b
left join (
   select `index` from TABLE where `date` BETWEEN '20150709' AND '20150716' ) a
on a.`index` = b.`index` 
where a.`index` = b.`index` and  `data` like '%검색어%' OR `nick` = '검색어'
ORDER BY b.`index` DESC


위 쿼리는 인덱싱이 되어있는 `index`컬럼과 `date`컬럼을 사용해보자고 생각을 했다.
그래서 서브쿼리 조인을 사용해서, 날짜에 해당하는 index들만 뽑아낸 후 바깥 테이블에서 해당 primary index에 해당하는 녀석들에서만 검색을 해보자고 시도를 해봤다. 결과는 실패였지만 과정은 괜찮았다.
1    PRIMARY    b    index    PRIMARY,indx_nick    PRIMARY    4        387047    100.00    Using where
1    PRIMARY    <derived2>    ref    <auto_key0>    <auto_key0>    4    lute.b.index    10    100.00    Using where; Using index
2    DERIVED    TABLE    range    indx_date    indx_date    32        179508    100.00    Using where; Using index


드디어 Extra에 using index가 등장하기 시작했다.
하지만 결국 b 테이블은 전체 테이블을 seek해서 배달되는 index랑 비교를 해야되기때문에 컬럼수가 무자비했고, 간혹 돌리다보니 using filesort뿐 아니라 using temporary까지 .....
오히려 더 안좋은 결과가 되었다. 실제 쿼리타임도 쿼리를 두번 돌려야되는 꼴이기때문에 더 느린건 두말할것도 없다

하지만 아이디어 자체가 나쁘다고 생각하지 않았기때문에 using temporary라는 키워드로 검색해보다가 이곳을 발견하게 되었다.
이제 내가 해결해야될 문제는 명확했다.

1. using index를 무조건 띄워야된다.
2. filesort 및 temporary가 뜨면 안된다
3. 최소한의 Row를 사용해야한다

링크한 블로그의 커버링 인덱스(covering index)를 참조하여 다시 작성한 쿼리는 다음과 같다.
select a.`index`, a.`date`,a.`time`, a.`nick`, a.`data` 
from (      
       select `index`  from TABLE
       where  `date` BETWEEN '20150700' AND '20150716'
       order by `date` desc
       ) b 
join TABLE a
on b.`index` = a.`index`
where a.`data` like '%검색어%' OR a.`nick` = '검색어'


사실 뭐가 다른지도 잘 구분이 안된다만 엄연히 다르니 잘 보자
따로 말로 설명하면 길어질것 같으니 성능부터 보자
1    PRIMARY    <derived2>    ALL                    193547    100.00    
1    PRIMARY    a    eq_ref    PRIMARY,indx_nick    PRIMARY    4    b.index    1    100.00    Using where
2    DERIVED    TABLE    range    indx_date    indx_date    32        193547    100.00    Using where; Using index


확연한 차를 보이고 있고, 제시한 모든 문제가 다 해결되었다.
최소한의 Row를 사용하고, index를 이용해 order by 까지 처리하는걸 볼 수 있다.

이번글에서는 엄청난 내용을 소개한것은 아니다.
하지만 쿼리 한개라도 아무렇게나 작성하면 안되고 충분한 테스트 및 성능 검증이 필요하다고 말하고 싶었다.
그래서 일일히 삽질한 쿼리도 공개를 해보면서 풀어나가듯이 작성해봤다.
어쨌든 디비쿼리와의 전쟁은 계속되겠고 최적화를 위해서 계속 삽질을 하자.



Reference
http://gywn.net/2012/04/mysql-covering-index/




2015/07/16 15:43 2015/07/16 15:43
색을 정렬하는 방법은 무엇인가.

운영하는 사이트 컨텐츠중 지정 염색 앰플 도서관에 들어갈 색상을 정렬해야 하는 문제가 생겼다.

db set은 대강 이렇게 생겼다.

빕분홍 255 170 170 #ffaaaa
실버분홍 164 129 140 #A4818C
그레이체리 142 128 138 #8E808A
탁분홍 197 141 141 #c58d8d
진달래 172 72 126 #ac487e
진한딸기우유(진딸우) 255 111 189 #ff6fbd
딸기우유(딸우) 255 129 211 #ff81d3
딥핑크 160 19 64 #a01340
핫핑크 199 25 103 #c71967


처음에는 대수롭지 않게 hex (web color code)대로 정렬해봤다.
간단히 webcode를 desc대로 정렬해봤다.


정렬은 되는데 뭔가 이상한..?
정렬이 안된다는 느낌이라고 해야하나..
다른 결과를 좀 내려다보니 아니나 다를까, 정렬이 되고 있지 않았다.
아니 정확히 말하자면 이렇게 정렬이 되어선 안됐다



딱봐도 이상하잖아..

그래서 이것도 그냥 정렬해서 안되겠구나 하고 찾아보니 색은 HSV나 HSL 이라는 하이라이트(밝기) 기준으로 정렬해야된단다.
관련 문서 같은건 위키나 이곳의 샘플코드를 보면 되겠다.

이렇게 한창 만들고 있다가, 잘 생각해보니 정렬을 위해선 모든 dataset에 배열안에 다들어가 있어야되는데,
read해서 바로 뿌려주는 내 페이지에는 비적합하다는 생각이 들었다. dbset이 몇개가 될줄알고 그걸 정렬을 위해 다 들고 있어야되나 이 생각이 들었다. (물론 이건 케이스마다 다른얘기)

그래서, 애초에 쿼리로 HSL을 적용해서 불러오면 어떨까 생각이 들었다.

결론부터 말하자면 웹컬러코드는 필요 없다. RGB색상이 필요하다.

select * from TABLE order by SQRT(colorR * colorR * 0.241 + colorG * colorG * 0.691 + colorB * colorB * 0.068) desc


HSV나 HSL은 결국 채도, 밝기에 의해 정렬되는것이기때문에 색상별로 가중치를 줘서 더한값을 정렬해야한다.
그 값은 쿼리에 나와 있듯이 R*R*0.241 + G*G*0.691 + B*B*0.068 값으로 구성된다.
즉, 그라데이션 같은 값으로 나오진 않는다. 하지만 색상정렬이 밝기별로 되고 있다는 느낌은 확실히 받을 수 있다.

다음은 위 쿼리를 설정해서 검색해본 결과.



1) Sort color using delta E(CIE 2000) - Similar Color gradient 에서 이어집니다
2) 색상 정렬 (Color sort) 맺으며.. 에서 이어집니다

2015/06/23 15:15 2015/06/23 15:15