늘모자란, 개발

늘모자란, 개발


시작하며

싴갤러스가 햇수로 올해로 10년, 그간 싴갤러스를 되돌아보고 정리한적은 없는것 같아요. 이깟게 뭔지.. 하는 생각이지만, 오랜세월 함께 해왔는데 한번은 정리의 시간이 필요하지 않나해서 글을 작성하게 되었습니다.
기술적인 얘기도 좀 적을거라 글이 재미없고 길어질것 같은데, 소고(小考)가 아닌, 아주아주길어질것만 같은 기분이 듭니다.

배경 - 늘모봇

정확히 2008년, 늘모봇이라는 hanirc 내에서 서비스를 시작하면서 일은 시작되었습니다.
07년부터 오게임이라는 게임의 정보 스크립트를 제작하고 있었는데, 많은 이들에게 더 다양한 서비스를 제공할 수 있으면 어떨까? 하는 생각에 탄생한 늘모봇 서비스.
퀴즈라는 컨텐츠로 큰 성공을 거뒀고, 이런말을 스스로 하긴 부끄럽지만 irc를 대표하는 봇 중 하나였다고 생각합니다.




늘모봇은 mIRC라는 윈도우 기반 IRC 클라이언의 자체 언어인 msl(mirc-script language)로 제작되었습니다.
사실 이 클라이언트를 이용해 봇을 제작하는 사람은 아무도 없습니다. 왜냐면, 윈도우 기반의 프로그램이기때문에 더미 메모리가 너무 많고, 시스템 구조가 불편하기 때문입니다. 또한, 한개의 IP에서는 2개까지밖에 접속할 수 없다는 제약이 존재하기때문에 배포할때도 일일히 실행해야 된다는 점도 한몫합니다. (실제로, 늘모봇'만' 돌리기 위해 8대의 VM을 사용했습니다. 자원낭비, 과대포장 갑...)

헌데 왜 이런 봇을 만들었느냐? 당시엔 할줄아는게 너무 없었기때문입니다. C를 하긴했는데 소켓은 모르고, 소켓을 하자니 통신도 모르고..
다 생각안하고 그냥 덤벼들자니 IRC에 존재하는 raw event들을 일일히 처리하기가 더 귀찮았습니다. 그래서, 이미 다 구현되어 있는 mIRC로 부딪혀 봤던 것 같습니다. mIRC로 할수있는걸론 거의 극한까지 했다고 생각합니다. 혼자한건 아니고, 유카리코라는 친구와 함께 구현했습니다. 봇 버전관리까지 전부 싹다 msl로 짰었는데 어우...

너무 서론이 길어지고 있는데, 여튼 퀴즈라는 컨텐츠를 만들때, msl의 기본 I/O 단위인 ini를 사용해 사용자 랭킹을 구현했습니다.
그러자 두가지 이슈가 발생했습니다.

1. 봇 클라이언트들이 자체(로컬) ini를 사용하니, 랭킹 및 문제 동기화가 되지 않는다.
2. 트랜잭션 개념이 존재하지 않으니, 랭킹을 읽거나 동시에 새로 쓰는 상황이 오면 파일이 크래시가 나며 모두 사라진다.
3. 모든 봇이 동기화된 랭킹을 사용하면서 안정화를 꾀할순 없을까?

그래서 어떻게 했느냐? 우선 무작정 도움을 요청했습니다.
ac18rt 라는 분이 처음에는 perl 을 이용한 cgi스크립트로 처리하게 해주셨었습니다. 그래서 봇들의 랭킹 동기화가 이루어졌습니다
하지만, 웹에 올렸더라도 결국 txt기반으로 랭킹을 기록하다보니 I/O에 의해 랭킹이 다 날아가는 이슈가 반복되었습니다.

결국은, penguin이 PHP-MYSQL을 이용한 웹 + DB연동을 진행하게 되었고, 제가 유지보수를 담당하게 되면서 웹을 공부하게 되었습니다.
당연히 트랜잭션같은게 없는 msl에서 하던 고민을 웹에서 구현하면 고려하지도 않아도 되서 안정성이나 기술 난이도나 웹쪽으로 점점 무게가 실리게 되었죠. (대신 무료 호스팅에서 얻을 수 있는 무료트래픽이 부족해 여러번 이사를 가야만 했다는... 건 가슴 깊숙히 묻어둡시다)
결과적으로 봇의 후반엔 거의 대부분의 기능을 웹으로 포팅하게 됩니다

배경 - 마비노기

늘모봇을 제작하면서 웹을 깨작이고 있던 와중에, 거짓말 같이 게임을 같이 했던 길드원들이 접은걸 펴고 한날 한시에 모이게 됩니다.
길드원들은 마비노기의 레이드라는 컨텐츠에 매우 주력하고 있었고, 성애가 시간표를 만드는 일을 담당하고 있었습니다. (류트한정!)
첨) 쿠쿠타이머라는 웹페이지에서는 하프만 지원하고 있었고, 임시점검등의 시간지연에 제대로 대응하지 못하는 이슈가 있었기때문에 일일히 수동으로 시간 계산 노가다를 하고 있었습니다

당시 저는 웹을 배우고 있었고, 길드원인 성애에게 건의했습니다.
가장 빠른 시간내 출현하는 레이드를 볼 수 있고, 레이드 몹 별로 필터링을 해서 볼 수 있으면 좋겠다는 아이디어를 제시했죠.
그리고 성애가 ok함으로서 2011년 7월, 어느 여름날 싴갤러스 프로젝트가 시작 되었습니다. 결국 소수인원을 위해 시작한...


왜 싴갤러스였느냐? 시크서버는 오래된 얘기긴한데..
제가 마비노기 갤러리(마갤)의 고정닉으로 활동하고 있었기 때문입니다.(현재도 고정닉 늘모는 제가 맞습니다)

레이아웃을 보시면 알겠지만 조악 그 자체입니다. 레이아웃은 정말 말할 필요도 없고, 투데이는 게임어바웃 의존도 100%.
자게는 아마 제로보드였던것 같고, 그나마 다음 서버상태에 좀 공을 들였습니다.

이땐 정말.. 보정이고 뭐고 서버에서 오는 반응 그대로 표시했기때문에 신용자체가 없었습니다.
그래도 언젠가는, 레이드를 자동화할 수 있지 않을까 하는 마음에 이것저것 알아보고 열심히 했었죠.. (웹 지식은 정말 제로에 가까웠습니다)
하지만 메뉴들을 보시면 지금 유저분들이 많이들 사용하시는 주요기능들은 하나도 없죠. 

첨1) 당시 레이드 계산은 공식이 있었기 때문에 정확한 서버 계산이 필요시 되었습니다.
첨2) 처음에는 사이트 대부분 길드원들과 유틸리티란의 언패킹 유틸리티를 공유하는데 쓰였습니다. (뭐가 있을지 궁금해하던 분들이 있었을지도..)

시간이 흐르며

가닥을 잡고 앞으로 나아가고 있던 와중에 마비노기 타임즈 TIP란에 글이 하나 올라왔습니다. 로컬로 뿔피리를 저장할 수 있다는 프로그램이었는데, 신기해하며 사용해보고 있는 와중에 이건 도대체 어떻게 하는걸까란 생각에 공부를 시작했고, 아주 간단히 흉내를 내서 csv형태로 저장하게 만들 수 있었습니다. 해당 파일을 msl을 이용해 csv를 서버에 업로드함과 동시에, csv파일을 지우도록 로컬을 구성하고, 서버에서는 오픈소스를 이용해 csv를 파싱후 DB에 저장하도록 했습니다. 

이 작업을 실시간으로 할 수 없었기 때문에, 10분의 텀을 두고 처리하도록 했습니다. 기억하실진 모르겠지만 갱신 10분텀은 이 때문에 발생했었습니다. 그러다가 패킷에 대한 지식이 많이 없던 저는 밥도 안먹고(--) 이틀동안 패킷공부를 하여 지금 시스템의 근간이 되는 프로그램을 만들 수 있었습니다. (이 과정에서  뿔피리가 실시간이 되었습니다. 현재는 더 많이 개선되었습니다.)


여전히 조악하죠? 이름도 tRader였는데 당시엔 ~러 어투가 유행이어서 일부러 뿔피리러라고 붙였는데 인기가 있어서 이름은 유지했던 것 같습니다. 익숙한 이름도 제법 있군요. 공부한걸 응용해서 던배른 와쳐를 만들고, 이때부터 생긴걸 의식해서 레이아웃을 바꾸고 그랬습니다. 다른 기능은 제공하지 않는 타갤러스도 이때 부터였습니다.(이때쯤엔 룬다,골렘, 모리안 서버도 서비스했습니다. 아래는 타갤러스 화면)




요거는 모바일 싴갤러스입니다. 주소도 다르게 뒤에 /m/ 으로 했는데 그땐 그게 대세였죠



레이드도 이때쯤엔 공부한걸 기반으로 완벽히 시간 계산을 하기 시작했습니다. 위 이미지는 싴갤러스 v3 때인데 이 레이아웃을 쓰기 시작한 시점부터는 더 이상 성애의 시간표는 필요 없을정도로 계산을 자동화 하는데까지 성공했습니다. 되도않는 웹표준이라고 마크도 붙여놨군요.
그리고 이제 따로 올리진 않겠지만 수야의꽃을 꼬드겨 블로그에서 개선된 지염도서관을 만들었고, 특별조나 음벞시뮬 같은 큰 기능들을 넣어가며 공부를 하고 동시에 저 스스로도 성장하는 계기가 된 것 같습니다. 가끔은 신기능이 되게 느린데 이것저것 꼼꼼히 검토하느라 그렇습니다.. (게으름 필때가 더 많습니다마는..)

서버 구조

싴갤러스의 시작은 PHP-MYSQL 구조로 시작되었습니다. 웹 구조는 현재도 비슷하고요. 당시엔 코딩 능력도 많이 부족했기때문에 배치 작업들도 전부 PHP로 만들었습니다. cron을 이용해서 php를 실행하게 .. 심지언 php 커맨드를 쓰는게 아니라 wget으로 호출시켰습니다.. 이후 배치 작업은 쉘파일이나, python 과 같은 언어로 대체되었습니다. (서버체커의 경우 아주 대대적인 성능개선을 수행했습니다. 티는 안나지만...)
모바일 푸시나 웹푸시를 담당하는 nodejs 서버가 두개있고, 별개의 다른 로직을 처리하기 위한 3개의 docker 컨테이너가 있습니다.

맨땅으로 시작을 했던거라 DB 최적화나 구조등에 대해 고려하지 않고 만들었기때문에 성능이슈가 빈번했고, 성능뿐 아니라 안정성에도 많은 장애가 있었습니다. 이를 해소하기 위해서 mysql 은 이중화 (replication) 되었고, 전체 db 형상들은 docker 컨테이너로 관리하고 있습니다. 적고보니 컨테이너가 꽤 많네요. 그외에도 c 및 c# 같은 언어들도 많이 사용되었습니다. 처음에 비하면 꽤 많이 복잡해졌습니다. 사이트 덩치가 커지면 커질 수록 처음보는 장애들을 마주했고 커널패닉이 떠서 자료가 전소되기 직전의 위기도 있었습니다.

호스팅을 받아서 쫓겨나기도 하고 이리저리 옮겨다닌 날도 있었으나 지금은 2대의 물리 서버에서 동작하고 있습니다. IDC도 다름.. (출세함)

여러가지 시도를 했습니다.

페이스북을 운영하기도 했고, 옐로우 아이디(카카오톡 + 채널), 트위터, 문의채널, 무엇이든 말해요 등등 여러 유저분들의 소리를 듣고자 했는데 사실 잘 되진 않았습니다. SNS 운영은 아무나 하는게 아닌것 같아요. 아직도 활성화되지 못했고 ... 기술적으로 궁금한걸 많이 해본것 같습니다. 요즘은 잘 안하지만 당시에 핫했던 Logstash - MYSQL bridge를 통핸 Elastic Search 를 연동까지 거의 다하고 취소했던 기억같은...  데이터 메세지들을 처리하기 위한 kafka 검토등... 등등......

앱을 만들었던것도 도전 중 하나였습니다. 당시에 앱은 내고 싶었는데 처음부터 만들기에는 너무 삽질을 하게 될거같아서 (API를 따고 연동하고 하는게...) 웹을 그대로 보여주는 하이브리드 앱으로 만들기로 결정하고 보여지는 부분을 많이 다듬었습니다. 그리고 약간의 토큰 작업들을 하고.. 이때 친절한주인, Jake가 많이 고생했습니다.

브라우니라는 거래 게시판도 만들고 있었는데, 앱이 꼭 필요하다 생각해서 작업을 했었으나.. 여가부에서 성인이 아닌사람들의 사이트 제한을 해야 된다고 답변 받아서 접었던 기억이 납니다. 외압아닌 외압에 이제... 접게된 최초의 사례인데 이건 글로 남겨놨습니다.. 여가부 남자직원이 아덴으로 설명을 해주시더군요.. (현거래가 아니어도 제재 대상..) 

관련글:
싴갤러스 가판대(브라우니) - 리뉴얼에 대해 고민하다
싴갤러스 - 하나의 기능 리뉴얼을 준비하며 (1)
싴갤러스 - 하나의 기능 리뉴얼을 준비하며 (2)

받은 질문 모음

Q. 게임도 안하는사람이 굳이 고생해서 좆망겜 관련사이트 운영하는 이유가 뭐임?
A. 게임은 2011년에 싴갤러스를 하면서 접었습니다. 우선 염려되었던게 좆목에 휘말릴까봐도 있었고 다행히도 당시에 게임에 대한 애정이 많이 식은 상태였습니다. 아본과 세공이 막 나오던 시절이었는데 세공에 투자할 재력도 없었고, 실제로 현실에서 많이 바빴습니다. 그래서 게임을 정리했던건데 결과적으론 접어서 다행인거 같습니다.

운영을 계속하는 이유는 보람이 젤 크지 않을까 합니다. 우리가 설계하고 기획한 기능들 (거의 대부분 유저분들의 자료를 긁어모으지만)이 잘 쓰이는걸 보면 기분이 좋습니다. 뭐 그게 다 인거같아요.

Q. 사이트 원리가 뭐에요
A. 네트워크 공부를 하시면 누구나 쉽게 할만한 기술들입니다. 마비엔 초고수들이 아주 많으나 그냥 다들 하기 귀찮아서가 아닐까... 싶습니다.

Q. 개발 및 유지보수는 혼자하나요
A. 처음엔 그랬는데 지금은 도와주는 드자이너들도 있고, 앱같은경우는 개발은 직접하지 않았고 유지보수는 맡아서 하고 있습니다.

Q. 트럭시위때 사이트는 왜 닫은건가요
거상 팬 커뮤니티중에 '파란만장 거상' 이라는 카페가 있습니다. 이 카페는 공식사이트보다도 더 큰 영향력과 지분을 가지고 있는것으로 알고 있는데, 약 1주일간 운영에 시위의 의미로 카페를 닫은적이 있습니다. 그 결과로 게임사에서 운영개선을 약속했던것으로 알고 있습니다.
마비노기는 유저층이 모두가 꼭 전투나 생활을 할 필요가 없어서 이해 관계가 나뉜다고 생각합니다. 따라서 관심없는 유저들은 세태를 끝까지 모르게 될것이라고 생각했고, 상황을 알리고 정리하는 지면의 역할을 해야겠다고 생각했습니다.

Q. 광고 수입은 얼마쯤 되나요
A. 서버 비용을 제한 광고 수입은 월 10만원 정도입니다.
도메인 결제가 겹치게 된다면 한 5만원정도로 잡는게 맞지 않을까요? 현재는 애플에 서비스하고 있지 않아서 이정도이고, 애플 결제를 진행하면 한 3만원대..의 수입이 있지 싶습니다. 그래도 치킨한마리는 먹는거 같네요.

아시다시피, 싴갤러스는 중간 컨텐츠에는 광고를 삽입하고 있지 않습니다.  총 광고는 4개이나, 화면이 1920 사이즈로 풀 확대되었을때 양쪽 세로에 두개가 출력되고, 그게 아니면 페이지의 가장 상단 및 하단에 출력되는 구조입니다. (특히 하단은 사이트가 스크롤이 많기때문에 거의 출력안되는거 같습니다)

그래서 밥벌이는 세로로광고로 거의 하는거 같고, 본문의 광고로는 수입이 거의 발생하지 않습니다. (생각보다 인터넷 창을 다 키워놓고 하는 분들이 많아서 놀랬습니다) 

Q. 기억에 남는 썰이 있다면?
학교 다닐때 용돈을 받아서 생활했었는데, 당시에 서버비를 내고 나면 컵라면 두개를 살 수 있었습니다. 주말동안 컵라면 두개로 살아야했는데 다 먹어버리면 끼니 해결이 안되니까 ㅋㅋㅋ 컵라면을 반반씩 쪼개서 먹었던 기억이 있습니다. 불쌍하다고 후원해주셔서 해당일엔 치킨 먹었습니다. 아직도 호르헤님 닉이 안잊혀지네요...

두번째로는 훈련소 들어가있는데 인터넷 편지로 서버 터졌다고 관리자중에 한명이 에러 메세지를 적어서 보내줬었습니다... 장황하게...
어떻게 고쳐야 최대한 빨리 고칠 수 있을까 원인은 뭘까 고민 및 시뮬레이션과 답답함에 미쳐버리는 시간을 보내게 해주었습니다. 고마워...

10년

10년이면 3650일인가요? 러닝타임이 그정도까진 되지 않을거 같습니다. 잠깐 닫은적도 있고.. 하지만 햇수로는 10년이 맞겠죠.
울고 웃고 많은 일들이 있었습니다. 여기까지 오기엔 혼자만의 힘으론 불가능했습니다. 도와준 관리자들에게 다시한번 감사의 인사를 드리고 또 사이트를 계속 사용해주시는 여러분들도 정말 고맙습니다.

사실은, 싴갤러스의 graceful shutdown 을 생각하고 있습니다.
마비노기 타임즈나 쫀득쫀득 인챈트와 같이 이미 10주년을 넘긴 사이트들앞에서 주름잡는게 조금 민망합니다.

커뮤니티들이 떠오르고 지고를 많이 반복했습니다만 그 끝은 사이트를 접은게 아니라 접힌쪽에 가까웠습니다.
접히는게 아니라, 사이트를 내손으로 접을 수 있다면, 또 내가 이 사이트를 평생 운영할 계획이 아니라면 10년이 되는 시점에서 커튼을 내리는것도 아름답지 않을까? 하는 생각을 했습니다. 그보다 적절한 시점이 없지 않을까 생각했어요.

그래서 디데이 옆 아이콘도 전원버튼을 달아놨는데요. (응원을 바라고 쓰는글이 아닙니다. 그냥 생각했던 내용입니다) 남은 시간동안 계속 생각해볼것 같은데 아마 계속 운영하지 싶습니다. 아마도...

지난 10년간 싴갤러스를 이용해주셔서 감사했고, 앞으로도 감사드리겠습니다!!
2021/04/07 10:30 2021/04/07 10:30

Intent setResult 관리

Android 2019/08/02 15:57
금번에 앱을 수정하는데 intent 전환시 반환되는 requestCode가 무슨짓을 해도 0이 나와서 삽질을 했다
아무래도 기본적인 이해가 없이 땜질만하다보니 (주로 퍼온코드들을 넣다보니) 이런일이 발생하게 된것 같은데...

대상이 되는 액티비티는 A, B, C 세개였는데

처음에는 A가 B를 불러주고, B가 C를 부르는 구도인데
B에서 C를 무슨수를 써도 C에서 받는 RequestCode가 0이었다. 이게 왜그럴까 하면서 많은 삽질을 했는데...

좀 파보니 A가 B를 이렇게 부르고 있었다

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
...
startActivityForResult(intent, 0);


근데 이렇게 하면 A가 B를 부르고 다시 A에게 돌아간거나 다를바가 없었다.
호출되는 상황이 다르기때문에 B의 result를 그대로 써야하는데 이렇게 되면 0으로만 값이 반환되는것이었다

그래서 이런 상황에서는 B의 반환값을 그대로 사용하기 위해서 다음과 같이 써야한다.

A를 다음과 같이 고친다

intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
...
startActivity(intent);



B는 하던데로 setResult에 값을 적고 반환한다.

setResult(Value, intent);


A는 B와 C사이의 커뮤니케이션에 아무 상관이 없는 친구이므로, 관심꺼라고 명시를 해줘야 제대로 전달된다.
사실, 이 디자인은 문제가 좀 있었는데 B가 A를 상속받고, 핸들러로 처리하면 될 문제였으나... A를 상속하지 않은채로 만들어진 쌩짜의 액티비티라 이런일이 생길수도 있던것 같다...
2019/08/02 15:57 2019/08/02 15:57

서론


글쓰기에 앞서서, 별로 권하고 싶진 않습니다.
좀 고행길이고, 저는 판교테크노밸리 기업은행에서 진행했는데 그 은행에서 2번째로 성공한 사람이랍디다.

우선, 집주인들이 왜 전세자금대출을 꺼려하는지 알 필요가 있습니다.

집주인들은 전세금을 받아서 대부분 다른곳에 투자합니다. 그리고 새로운 세입자가 오면 새 세입자에게 전세금을 받고, 투자했던곳에 입금 후 전세금을 빼서 이사가는 세입자에게 돌려주는 식이죠. 이 구간은 보통 유연하게 처리 됩니다. 뭐 하루 이따가 돈 줘라, 하루 이따가 돈 주겠다. 뭐 이런식으로 투자한 전세금의 로스가 생기지 않도록 처리한다는거죠

그런데 전세자금대출로 전세금을 내면 그렇지 않습니다.
딱 하루만 지나도 은행은 집주인에게 이자를 부과합니다. 빨리 돈 갚으라고요. 돈을 유연하게 운용할 수 없고 칼같이 은행에 입금해야합니다. 집주인들로서는 귀찮은 일이죠. 그래서 전세자금대출을 안해준다는 주인들은 이게 귀찮아서 안해준다고 합니다. 근데 여기까진 그냥 기본만 지키면 되는거라, 왠만하면 해줍니다. 

중소기업청년전세자금대출도 80%는 비슷합니다. 거의 필요한 서류가 없고 빨리빨리 진행되는 편입니다.
하지만 그게 궁금하진 않잖아요? 100% 대출은 왜 권하지 않느냐?

집주인의 의지와 상관없이 대출이 나오지 않기 때문입니다.
집주인들은 전세금 및 건물을 담보로해 대출을 받아 또 다른 투자를 합니다. 대부분의 건물은 융자가 걸려있습니다.
100%대출의 경우에선 진짜 작은 융자도 허락하질 않습니다. 그냥, 융자가 있다면 안된다고 보면 됩니다.
그리고 보증보험에 가입되고 이로 인해 내용증명이 발송됩니다. 사실 이건 발송된다고만 알려주면 되는 사항이고 집주인이 해야할 필요는 없습니다.

그럼, 시작해볼까요.

주제파악


너무 센 워딩인가 싶은데 3500 이상 연봉받는 사람은 이거 신청못합니다. 그리고 입사 1년이상안되면 포기하세요.
또, 회사가 너무 약해도 문제가 됩니다. 여기서 확인해보세요. http://nhuf.molit.go.kr/FP/FP05/FP0502/FP05020604.jsp
그리고, 은행에서 자기가 얼마까지 대출되는지도 파악해야합니다. 1억짜리 집구했는데 대출 1억 못준다고 하면 안되잖아요? 대강 연봉의 3배 정도까지 나온다고 보면 될 것 같습니다.

집구하기


가장 먼저 해야할 일은 집을 구하는것입니다. 너무 당연한 소리지만 위에 얘기한대로 융자가 없는 건물이어야 합니다.
다가구주택 및 아파트, 오피스텔의 경우에는 한명한명이 세대주이기때문에 별로 신경쓸 필욘없습니다. 그러나 단독주택같은 건물은 융자체크를 반드시 해야합니다.
100%를 받을시 최대 금액은 1억원입니다. 1억 이상의 집은 반전세 돌리면 해결됩니다. 그러니까 조건이 까다로운셈입니다. 간단히 정리해봅시다

- 좋은집을 구한다.
- 전세금을 확인한다.
- 집 크기를 확인한다. (85m^2 이상의 크기는 입주 불가)
- 융자가 없는 집인지 확인한다

여기서 융자가 없는 건물을 셀프로 확인하는 방법은, 건물 등기를 떼보면 됩니다. 융자가 있을떄는 '을구' 란에 기재되어 나옵니다. 뭐라도 하나 나오면 그 집은 그냥 포기하세요. 이사인만큼 이 과정이 가장 어렵고 치열합니다. 대부분 한개씩 하자가 있습니다. 심지어는 저는 계약날 전에 주인이 마음바꿔서 물러진적도 있습니다. 진-짜 힘듭니다. 된다고 하는 중개사들 찾기도 어렵고, 집주인도 찾기 어렵습니다. 100%는 정말 어렵습니다.

그리고 입주날짜가 중요합니다. 대출 서류 검토 및 심사는 최대 3주정도가 소요됩니다. 역시 만족되지 않으면 안되겠죠.
집을 찾았다면 은행에가서 대출할건데 어쩌면 되냐고 물어보면 이것저것 따져주고, 양식 두개를 줍니다. 그건 아래단에서 작성합니다.

계약하기


집을 구하셨다면 대단한겁니다.
계약전에 등기등본을 들고 은행에 가면 대출이 나오겠네요 안나오겠네요 이런 소릴 해줍니다. '을구'에 뭐가 없으면 대부분 가능하지만, 은행에서는 국토부 시세 등을 따져보기 때문에 그냥 등기등본들고 은행가서 여기 진행해도 되냐고 물어보는게 가장 속편합니다.

집 주인과 계약을 하실 차례입니다. 계약은 일반적인 계약과는 다를게 없습니다만, '확인서'를 작성해야합니다.
그 건물에 누가 살고 있고, 전세금은 얼마, 월세는 얼마를 내며 살고 있는지 작성되어야 합니다. 이건 나중에 세입자 증명서를 제출해야할때 이름이 안맞으면 다시 써야할수도 있으니까 임대인에게 제대로 물어봐야합니다. (보통 역정냄)

중개사에서도 건물 시세 조사서를 작성해줘야합니다. 이건 대출할거다 하면 은행에서 양식을 주면서 써오라고 합니다. (계약전에 은행에 꼭 들릅시다)

이래저래 계약을 하되 반드시 다음 특약을 챙겨야합니다. 대출이 안나올수도 있기 때문입니다.
'은행대출이 불가능할시, 계약을 무효로 하고 전액 반환한다.' 와 같은 뉘앙스면 됩니다. 이거 못써주겠다고 하면 그냥 조용히 자리에서 일어나시면 됩니다. 보증금을 지불하고, 영수증을 꼭 챙깁시다.

계약을 완료했으면 확정일자를 신청합니다. 확정일자를 작성하지 않으면 애초에 신청을 못하고, 나중에 세입자 증명서를 뗄수가 없으니까 바로 합시다.
신청이 끝났다면 이제 문서와의 전쟁입니다.

대출 신청

드디어 대출신청란까지 왔습니다.
대출엔 다음과 같은 서류들이 필요했던것 같은데 누락이 된게 있을수도 있습니다.

- 재직 증명서 (회사에 요청)
- 근로소득원천징수영수증 (회사에 요청)
- 건강보험자격득실확인서
- 사업자등록증 (사본, 회사에 요청)
- 고용보험 피보험자격 이력 내역서
- 회사 주업종코드 확인 자료 (회사에 요청)
- 주민등록 등본, 초본, 가족관계증명서
- 집 등기 등본
- 분양계약서, 임대차 계약서 (사본X)
- 확정일자가 작성된 계약서 (이건 돌려받습니다)
- 확인서 (세들어 사는 사람의 전세금액 및 월세금액, 집주인)
- 세입자 확인서 (확정일자가 있지 않으면 발급안됨. 주민복지센터에서. 반드시 본인게 아니라 건물전체의 내역 필요.)
- 공인중개사 등록증
- 건물 주변 시세 조사서 (중개사에서 작성)

아 많다 증말 진짜
위에건 예외없이 다 필요하니 그냥 미리 준비들 합시다. 근데 너무 오래되면 못쓴다고 돌려보내니까 너무 빨리 뽑아두진 말고...
잔금일자를 확인후에 은행에 가면 무지막지한 대출 신청서에 계속 사인하고 날짜 적으라고 합니다. 시키는대로 하면 됩니다.. 
뭐 보증금은 어떻고 잔금은 어디로 들어가고 이런거는 굳이 알아가야할 필요가 없습니다. 그냥 은행에서 시키는대로 하면 다 됩니다.

기다림


기다리는 동안 은행에서 가끔 연락이 옵니다. 이 문서가 잘못됐는데, 특약에 사인해주셔야하는데 하면서 챙겨줍니다.
오랄떄 왠만하면 빨리빨리 다녀야합니다. 일정이 늦춰질 수 있거든요. 신청 후 임대인에게 내용증명 등기가 날아가는데 이것도 꼭 수령해야합니다.
제발 수령해달라고 합시다. 그러면 은행에서 그거 받았냐고 임대인에게 전화가 따로가고, 받았다하고 뭐 동의같은거 해주면 진짜 기다리기만 하면 됩니다. 골때리는게 별도의 승인 문자 이런게 없습니다. 어떻게 진행되는지도 모르겠고 그냥 기다려야되는데 저는 하도 답답해서 은행가서 어찌되고 있냐고 물어본적도 있어요..

이사


잔금일자가 되면 이사를 가면 됩니다. 아~ 이사 왔다~
끝이 아닙니다. 전화가 옵니다. 실사단이 파견됩니다. 약 5분정도 걸리는 시간인데 집 문사진이랑, 실제로 세입자가 떠나고 내가 들어간 집인지 등을 보고 갑니다. 근데 짤없이 평일만 된다고 합니다. 하...... 

전입신고도 해야합니다. 이사후 3일이내에 전입안하면 황입니다. 전입신고를 했다면 주소 변동이력이 작성된 등본을 뽑아다 은행에 제출하면, 모든 과정이 끝이 납니다.

결론


문서는 사실 얼마든 쓸수있습니다. 좀 귀찮으니까요.
그런데 100%로 되는 집을 구하는게 너무 힘듭니다. 중개사들 시선도 곱지도 않고요. 차타고 가다가 한두개만 보여주고 우린 그런거 없다하고 내려준 중개사도 있습니다 ㅋㅋ 
마음을 넉넉하게 가지시고, 천천히 알아보심을 권합니다. 그럼!
2019/03/25 10:04 2019/03/25 10:04
TL;DR
apktool로 디컴파일-컴파일 했을때 망하면 다음을 확인해보자
classpath 'com.android.tools.build:gradle:3.1.4' -> 3.1.4 이상부터 뭘 해도 안된다.


자세히
내가 만든 앱을 디컴파일 및 컴파일 해야되는 계기가 생겼는데 (왜 이런 삽질을 하냐고 묻진 말자... 자본주의...)
갑자기 이런 에러가 나면서 재 컴파일이 되지 않았다.

Public symbol array/XXXXXX declared here is not defined.


이런게 막 수십줄이 뜨고 마지막에는 

brut.androidlib.AndrolibException 이 뜨면서 으마으마한것들이 잔뜩 나오며 실패했다.
minSDK 문제인지 targetSDK 문제인지 진짜 뭔 별 안해본게 없는데 커밋로그들을 쭉 둘러보면서 바뀐게 뭔가 하다가 설마 gradle 버전 문제인가 싶어서 기존에 쓰던 3.1.4로 낮춰서 빌드를 해 재컴파일 해보았더니 그냥 된다...

3.1.4에서 올린 이유는 이런 에러가 나왔기 때문인데
The rule `-keep public class *extends java.lang.annotation.Annotation {


gradle 버전을 올리니 더 뜨지 않아서 올렸던 기억이 있다. 그런데.. 이런 사태가 난 것이다.
좀 실험을 했는데 3.2.1 도 해보고, 3.3.1도 해봤는데 뭘 해도 안된다. 그냥 3.1.4만 된다. 혹은 그 아래버전도 되겠지만 그건 필요가 없으니..
어이가 없어서 남긴다. 여튼 겨우 한건 해결...
2019/02/27 11:31 2019/02/27 11:31

2018년

Gossip 2018/12/31 09:51
2018년은 참 다사다난해였고, 진행중이다.

흠..
2017년도에 블로깅은 꽤 열심히 했다. TIL (Today I Learn) 위주의 글들도 제법 작성했고 워게임을 풀이하는 바람에 꽤 많은 글을 작성한 해 였다. 방문자가 20명대에서 매일 40명대 찍히는거보면 헛짓거리는 아니었던 모양이다. 그래봐야 40명이긴하지만...
두번째로 일상이 챗바퀴..
자전거를 열심히 탔다. 인천에 경인항에도 갔다오기도 하고 꾸준히 탔으나 그외에는 기존의 나를 탈선시킬정도까지 어마어마한 취미는 없었다. 좋은 사람이 생긴거 정도는 ... 음...
세번째 건강은 꽤 호전이 있었다.
좀 많이 쳐줘서 2 3키로만 감량하면 20키로쯤 감량된거같다. 좀 어거지긴하지만 감량이 된건 된거고, 혈압도 훈련소안에서 제자리를 찾아왔기때문에 이대로 쭉 유지도 하고, 감량도 계속해볼 생각이다. 그래도 근력이 딸리는건 마찬가지라 운동도 필요하고...
2018년
훈련을 다녀오니 이제 다 끝난 느낌이다. 날 가로막는 모든 장애물이 사라진 느낌?
좀 더 적극적으로 공부를 해야겠다. 그리고 보안 공부를 소홀히 하지 않겠고.. 자격증을 취득해보는게 목표다. 자격증을 취득한다는 거도 의미가 있지만, 일단 목표가 있고 공부를 한다는 행위에 의미를 두기로 한다. 한자, 토익, 리눅스 마스터 등이 생각하고 있는 목표인데 잘 모르겠다.
그리고 알고리즘과 자료구조를 다시 공부할 생각이다.
결국 기초가 중요한건데 모래성을 계속 쌓아갈 순 없다. 워게임 대신 온라인 저지를 좀 풀어볼 생각이다.
두루뭉술한 목표가 되었지만 잘 실천한다면 도약을 위한 발판을 준비하는 한 해가 되지 않을까? 아듀 2017.


직무


훈련소에 다녀왔었다. 2018년이 껴있는 날은 8일정도밖에 안되지만 어쨌든 2018년엔 훈련소를 다녀왔다.
사람이 변했나? 그렇진 않다. 다짐같은건 정말 많이 했고 그렇기에 남긴글이긴한데, 사실은 하나도 이룬게 없다. 지금 보니 우습기만하다. 자격증? 한자? 토익?;;; 적어놓은것의 나열을 보면 정말 한심한 한해를 보낸것 같다.

그래도 여러 시도는 하려고 했다. 역시 이놈의 싴갤러스의 프레임을 벗어나지 못한...
첫번째로, 채팅이 SSL을 지원하지 않아 HTTPS 를 적용할 수 없었었다. 그래서 웹 푸시 역시 사용할 수 없었기 때문에, 채팅을 직접 개발했었다.
socket.io를 이용했고 서버는 노드. 간단한 채팅정도는 왔다갔다 쉽게 만들 수 있었지만 관리자 측면에서 생각해본적은 없었기 때문에 괜찮은 시간이었다. 그러나 완성되진 못했다.. 가능한 모든 가능성과 변수를 고려하고 프로젝트에 돌입해야겠다는 교훈을 얻었다. 그래도 주석이 잔뜩 남긴 소스들은 깃헙에 남게 되었으니까 완전 망한건 아닌것 같다.

두번째 역시 싴갤러스.
셀레늄을 이용한 프로젝트였고 토이 프로젝트인데 너무 프로젝트에 끌려다니는것 같아 인간다움을 추구해 진행된 프로젝트였다. 자동화 관련인데.. 
소스 자체는 2016년 10월에 작성했었는데 한가지 난관을 돌파하지 못하다가 4월쯤 겨우 적용할 수 있었다. 꾸준히 해왔던건 아니고, 자전거 타고 돌아오는길에 왠지 할 수 있을 것 같아서 피느에게 부탁해 프로그램하나 만들고, 적용했다. 현재까지도 잘 돌아가고 있고, 간간히 오작동하고는 있으나 계속 보강할 생각이다.

세번째는 서버. 하지만 역시 싴갤러스;;
docker 를 이용해 mysql replication 컨테이너를 만들고, 컨테이너 자체 백업을 적용했다. 그러니까 백업이 어떻게 보면 삼중화 된 것인데, 새벽경 정기 기 백업, repl, 그리고 컨테이너 커밋을 이용한 백업 체계를 만들(었)었다. 이미지가 크기때문에 당연히 몇개 저장하지 못했고, 결국 서버에 ssd를 하나 더 붙이는 대작업까지 수행했다. 이 작업은 훈련소에서 디비가 깨졌단 소리 듣고 혼자 빡쳐서 꼭 해야겠다고 생각했던건데, mysql 의 인덱스 크래시 문제는 해결이 안된다. 현재 이 백업 체계는 동작하지 않고 있고 -_- 조만간 다시 진행할 예정이다. 뭐가 문제인지 어느순간부터 컨테이너 커밋이 진행되지 않는데, 용량 문제인가 싶기도하다...

그외 여러가지 기능 업뎃을 진행했으나 기억에 남는것은..

1. 키트 데이터 통계를 낼 수 있는 득템전광판 제작
2. 네이드 퇴역
3. 앱 아이콘 변경
4. HTTPS 및 HTTP2 적용 (엄청 미루다가 드디어) , prefork 에서 event 기반으로 변경

이지만 뭐니뭐니해도 레이드, 지염도서관 리팩토링이 기억에 남는다.
내용은 따로 썼었으니 적진않겠지만 코드관리관점에서 가시적인 성과가 있어서 참 좋았다. 기분도 엄청 좋았고.


그외에 회사일은,
뛰어난 일을 한건 없고 잡기술들이 많이 늘었다. 잡지식도 많이 늘고, 애플에 버그리포트도 했다. 먹히진 않았다만.....
내년 싴갤러스는 할 수 있는 목표부터 적어야겠다. docker 안정화하고, 웹 푸시 적용하고, 장기적으로는 아파치를 nginx 로 변경하는게 목표이다


좋은 사람이 있었었다. 2017년 후반과 1월 초까지는. 그런데 4월까지 내내 힘들다가 4월말에 또 좋은 사람이 나타나 좋은 시간을 보내고 있다.
마냥 좋기만 한건 아니지만, 서로 맞춰가는 재미도 있는것 같고 또 한번도 생각해본적도 없던 결혼에 대한 인식도 바뀌었다. 그러나 대부분 즐겁고 행복한 느낌이다. 언제까지 이 감정이 지속될진 모르겠지만, 2019년에 끝나버릴지도 모르겠지만 적어도 2018년은 그렇게 보냈다. 2019년엔 다투지 않고 즐겁게 보냈으면 좋겠는데. 지금까진 그냥 바램.2019년을 돌아볼때 어떤 느낌일까 싶다. 내곁에 계속 남아 있어주려나. 아니면 2018년 초반처럼 아파한 시간을 보냈으려나.
그리고 이사하고 싶다. 이사가 될진 잘 모르겠다만..

공부

정말 열심히 살 생각이다. 11월까진 조용히 아무일도 없게 보내고 싶다. 그러나, 11월엔 이직을 목표로 하려고 한다.
몸이 편한건 사실이다. 일이 많지 않은것도 맞고. 그러나 뭔가 얻는 기분도 들지 않고 뭣보다 일이 재미가 없다. 힘들어도 부딪히면서 일궈내는 느낌이 너무 없어서 좀 더 높은곳을 향하는게 좋지 않나 싶다. 이직하지 않더라도 결국 나 스스로에겐 도움이 될테니까 말이다. 


역시 구체적인 목표는 없는 두루뭉술한 2018년 돌아보기와 2019년 목표였다.
사실, 2019년은 마지막 해라서 조용히만 보내자가 목표이다. 2019년의 나는 어떨까? 앞자리도 바뀌고, 정신적으로 더 성숙해질까? 스스로도 궁금하다.
2018/12/31 09:51 2018/12/31 09:51
W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://dl.google.com stable Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 1397BC53640DB551
W: Failed to fetch http://dl.google.com/linux/mod-pagespeed/deb/dists/stable/Release
W: Some index files failed to download. They have been ignored, or old ones used instead.


나는 pagespeed 를 아파치 모듈에 붙여서 사용하고 있다. 그런데 언제부터인지 계속해서 업데이트가 안되었다 (위 내용처럼)
오늘은 정말 거슬려서 구글링을 좀 해봤고 해당을 찾을 수 있었는데... 


apt 명령어 사용시 공개키를 이용하게 되는데 모종의 이유로 이 키가 삭제되거나 누락되면 자동으로 다시 받지 않는 모양이다
https://askubuntu.com/questions/766883/there-is-no-public-key-available-for-the-following-key-ids-1397bc53640db551

여기서 볼 수 있지만, 다음과 같은 명령어로 키를 내려받아주면 해결된다

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys [KEY NUMBER]


2018/11/12 10:48 2018/11/12 10:48
There is no vulnerability.
Just find the rules.

nc wargame.kr 10034


배스킨 라아빈스 써리~원

배스킨 라빈스 게임은 어릴적 UN의 맴버중 하나가 나와서 필승법이 있다고 예능 인터뷰때 했다고 해서 사기게임이라고 뇌리에 깊게 박혀있다.
실제로, 1:1에서는 배스킨라빈스 게임은 실수하지 않는한 먼저 하는 사람이 무조건 이기는 방법이 있다.

1:1 베스킨 라빈스를 제시하라. 단 필승법을 아는지 대충 떠 봐야한다. (그건 알아서. 귀찮아) 
먼저 시작한다면, 처음에는 1, 2를 외쳐라. 그 다음에는 [4개-상대방이 말한 갯수]만큼씩 외쳐라.

두번째 시작한다면, 어떻게든 당신이 마지막으로 외치는것이 4의 배수보다 1개 많도록 29, 25, 21, 17, 13, 9, 5개가 남도록 한다.
즉 당신이 2, 6, 10, 14, 18, 22, 26으로 끝을 내게 외쳐야 한다.

그 다음에는 마찬가지로 [4-상대갯수]를 말하면 이긴다.
출처: http://yews.tistory.com/entry/베스킨-라빈스-31-11승리전략 [생각의 화장실]


예를 들면 이런식으로 많이 나와 있다. 이런건데, 요점은 반드시 내가 말해야 하는 수가 있다는 것이다.
이런 배경지식을 가지고 시작해보자

Welcome to STITCH's baskinrobbinsN game!
Your Purpose is to win Baskin Robbins31 game from AI. 

Rule)
As you already know, it seems like real Baskin Robbins31 game. But maximum number is not 31.

ex) N=101 , count 10
It situation, You can saying up to ten at a time of number. because count is 10.
if you speaking 101 faster than AI , you lose because N is 101

you will input smaller than count...
you will input the numbers in order.

----------------------------------
hint) 
N = 31, count = 3
input your name -> ab
Hi! ab
user first!
input your number -> 1 2 3
user say -> 1 2 3
computer say -> 4 5 6
input your number -> 7
user say -> 7
computer say -> 8 9 10
input your number -> 11 12 13
user say -> 11 12 13
computer say -> 14
input your number -> 15 16
user say -> 15 16
computer say -> 17 18
input your number -> 19 20 21
user say -> 19 20 21
computer say -> 22
input your number -> 23
user say -> 23
computer say -> 24 25 26
input your number -> 27 28
user say -> 27 28
computer say -> 29 30
AI win!
----------
------------------------

**** Timeout is 150sec... ****
**** Total 31 round... ****
Good luck~




N = 2166, count = 133

input your name -> Hi! fantazm
computer first!
computer say -> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
input your number -> 


문제가 예시와 함께 잔뜩나온다. 근데 기존에 알던 배스킨게임이 아니다. 최대 수와 부를 수 있는 수자가 랜덤으로 주어진다.
사실 이 글을 쓰기전에 살짝 해봤는데 컴퓨터를 이길 수가 없다. 생각을 다시 해보기로 했다.
읽고 나니 드는 생각이 있다.

나온 문제에서 보면, 내가 불러야될 숫자는 정해져있다. 컴퓨터 턴이 되어도 절대 끝낼 수 없는 수를 불러야한다. 그럼 컴퓨터는 2164까지밖에 부를수있고, 부르더라도 나는 2165를 말하고 승리한다.

http://lg-sl.net/product/scilab/sciencestorylist/ALMA/readSciencestoryList.mvc?sciencestoryListId=ALMA2018040003&subjectId=ALL

https://blog.naver.com/jong1003min/220348353765

https://namu.wiki/w/%EB%B0%B0%EC%8A%A4%ED%82%A8%EB%9D%BC%EB%B9%88%EC%8A%A4%2031(%EA%B2%8C%EC%9E%84)

참고한 링크들을 보자. 왜 하필이면 4일까? 4가 28을 부를 수 있는 배수의 마지노선이기 때문이다. 즉.. 워게임이지만 알고리즘에 가까운 코딩문제인것 같다.

핵심은, 나머지를 구한후 필승 수열을 구하는것이다.
r = (N - 1) % (count + 1)


그리고 목표 수치도 N이 아니라 N-1을 불러야는게 핵심이다. 때문에 내가 한개 더 많이 말해야한다. (베스킨라빈스게임에서도 3까지 부를 수 있지만 4로 맞춰 말하면 이긴다)
따라서 count + 1 을 해서, 필승 수열을 만든다

solns = []
while r < N:
    solns.append(r)
    r += (count + 1)


이제 여기에 등장하는 숫자까지 맞춰서 말하면 이긴다.
코드를 다 올리는건 좀 아닌것 같고 문제 풀면서 정말 X같았던 점들만 좀 적어본다.

1. 아무리해도 이길수가 없다. 컴퓨터가 먼저 시작을 하기 때문이다. 그리고 기가 막히게도 컴퓨터는 이 필승수열의 첫번째 수를 말한다.. 진짜 한참 삽질했는데
처음에 이름을 입력받는데... 이 입력을 짝수로 하면 내가 먼저 시작하고, 홀수로 하면 컴퓨터가 먼저 시작한다. 아........ 이건 처음 수열을 만들때 0으로 시작할 경우, 일부러 컴퓨터에게 턴을 주는식으로 구현하면 된다.
2. 소켓이 짤려서 온다. 숫자가 길어지고, 정확히 1460 자 이상이 되면 개행한다. computer say -> 로 파싱하고 있다가 의문의 payload wrong을 만날 수 있는데, 게임이 시작되었고, 개행이 된채 숫자만 던졌다면 해당 숫자를 마지막으로 다시 계산해야한다

요컨데 이런식으로 온다.

computer say -> 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 (개행)
(탭) 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652

별도의 처리가 없으면 이때 1632를 마지막 숫자로 읽어서 시간낭비를 하게 된다.
다음과 같이 정규식으로 처리해주면 된다.

 ext = re.findall(r"^\s+((?:\d+ )*\d+)$", line)
 if len(ext):
    response = re.findall(r"^\s+((?:\d+ )*\d+)$", line)[0].split(" ")
 


socket.recv 같이 너무 작으면 개행을 막 두번씩 하기도 하니, 애초에 넉넉하게 잡고 수신해주면 좋을 것 같다.
2018/11/07 09:09 2018/11/07 09:09
wargame.kr이 새단장을 해서 보니 문제가 꽤 추가 되어 있었다.
custom OS 빼곤 다 풀었었던거 같은데... 저 문제는 아직도 잘 모르겠고, 짬을 내서 풀어보기로 한다.

먼저 시도할 문제는 plz variable 이라는 문제.

Can you find the solution quickly in polynomials? 
nc wargame.kr 10004


nc는 netcat 의 줄임말로, 간단히 데이터를 읽고 쓰는 프로그램이라고 한다.
여러 문제를 눌러보니 이제 문제를 웹에서 푸는게 아니라 저 프로그램을 이용해야 되는거 같다.
그냥 telnet을 이용해도 되는거 같은데 일단 시킨대로 nc로 연결해보았다.
그러자 문제가 나왔다.

Please match the correct answer 30 times.

Submit format -> a,b,c,d,,,(Ascending order)
Timeout = 60sec
a,b,c,d,,, is natural number
a,b,c,d,,, is 100 <= <= 1000

1th...
b * a + f + e * d - c = 491787
a * b * c - d * e + f = 359074294
e - b + a * c - d - f = 677922
d + c + e + f + a + b = 3215
a * f - c + e + b + d = 162962
a * b + c + f - e - d = 368086
Answer -> 



예 잘봤습니다. 제 점수는요.
여러번 연결해보니 나오는 변수 만큼 문제가 출력되는거 같다. polynomials. 다항식, 연립방정식이다.
그전에 내가 알파고가 아니니까 60초만에 저 문제들을 풀어낼순 없다. 코드로 문제를 받는거부터 시작이다.
딴건 없고, 그냥 소켓 연결을 생각하고 코딩을 하면 되었다.

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('wargame.kr',10004)
sock.connect(server_address)

data = sock.recv(1024)
print data

sock.close()


데이터 전송은 sock.send로 하면 되는거 같다. 어쨌든 잘 연결이 되었다.
이제 문제를 풀어야하는데.. python 연립방정식 따위의 키워드로 검색을 하니 몇개 나왔다. sympy 나 numpy scipy 등을 이용하면 된다고 한다.
그중에 sympy 의 solve를 많이 쓰는거같은데, 이 문제에서는 답까지 주니까 nsolve를 사용하면 될것 같았다.

우선 nsolve 를 사용하기 위해서는 어떤 알파벳이 변수로 사용되고 있는지 Symbol을 선언해주어야 했다.
그리고 이건 식입니다. 하고 명세하기 위한 Eq도 필요했다.
우리는 그리고 nsolve를 사용하기로 ...

from sympy import Symbol, Eq, nsolve


그리고 식은 정직하게 주지만 다음 문제를 보지 못했기 때문에, 수학식임을 판단하기 위해서 다음과 같이 검사했다.
각 라인이 개행되있기때문에 라인별로 정규식으로 검사했다. 좀 과한거 같긴한데 아래같이 할 필욘없다.. 여튼 이렇게 식과 답을 분리해서 넣었다.

for line in data.split('\n'):
 if "Answer" not in line and "Submit format" not in line and re.match(r"[a-zA-Z]",line) != None and re.match(r"^(-?\d+)$",line) == None and re.search(r"[()\\*+-]", line) != None:
 eq.append(re.findall(r"(.+?) \=",line)[0].replace("\t"," "))
 answ.append(int(re.findall(r"\=\s(.+)",line)[0]))


사용되는 변수들은 중복을 제거해 Symbol 로 선언해주기 위해 다음과 같이 했다.

list(set(re.findall(r"[a-z]","".join(eq))))


이제 list 세개를 모두 확보했기때문에 nsovle에 넣고 돌림 되겠지 하고 돌렸는데 에러가 났다-_-
Eq로 list를 하나하나 해줘도 Symbol 이 변수명으로 선언되지 않으면 돌아가질 않았다. 즉 요렇게

a = Symbol('a')


허미.. 이게 몇개나 나올줄 알고 심볼을 선언해주나. exec를 쓰면 된다.

exec("{0} = Symbol('{1}')".format(name, str(name)))


이런 느낌으로 돌려주면 선언된다. Eq도 똑같이 해줬는데 그래도 안된다.
그래서 찾아보니, (e1,e2,e3,e4) / (x,y,z) 이런식으로 기재는 하는데 내가 만든 리스트는 ('e1','e2','e3') ('x','y','z') 처럼 문자열같이 되있었다.
하아.. eval 도 해주기로 했다.

for i,j,k in zip(exps, symbols,answ):
 epack = "{},{}".format(epack,i)
 spack = "{},{}".format(spack,j)
 apack = "{},{}".format(apack,k)

epack = "("+epack[1:]+")"
spack = "("+spack[1:]+")"
apack = "("+apack[1:]+")"


이후 nsolve에 eval(epack) 이런식으로 실행했다. 안된다.
코드상 문제는 없는거 같은데 단순히 값을 꾸겨넣는정도로는 안되나보다. matrix 가 0이니, divisionZero 에러니 뭐 별에 별 에러가 나와서 그냥 때려쳤다. solve도 써보고 뭐 다른 방법도 여러개 해봤는데 안됨.
그러다가 내가 못찾은 다른 solver가 있는건 아닐까 싶어 찾아보다가 z3라는 solver를 찾게 되었다.

z3는 리버싱때 많이 쓴다고 한다. 주소 계산같은데 활용되는거 같다. 어쨌든 z3라는 무기를 다시 확보했으니, 풀어보자.

먼저 Solver 라고 문제 풀이 머신을 하나 추가해줘야한다.

import z3 as z
s = z.Solver()


이런느낌이다. 그리고 조건들을 추가해줘야하는데, 수식에서 = 를 ==로 변환해서 넣어주어야 한다.
그리고 100보다 크고, 1000보다 작은 수이기때문에 조건을 입력해주어야한다. 아니면 시간이 너무 오래걸리거나 중간에 퍼지는 경우가 생긴다.
또한, 위에서 Symbol 을 선언해주었던것 처럼 eval 로 Int 임도 먼저 선언해주어야 한다. 그러면..

for i in variables:
 exec("{} = z.Int('{}')".format(i,i))
 s.add(eval("{} >= 100".format(eval(i))))
 s.add(eval("{} <= 1000".format(eval(i))))

이런 느낌이 된다. 그리고 수식을 = 를 변환해주어서 추가해준다.

for i in exps:
 s.add(eval(i.replace("=","==")))


이러고 문제 해결을 위해 다음과 같이 해준다.

s.check()
result = s.model()



여기서 내가 헷갈렸던건,
Submit format -> a,b,c,d,,,(Ascending order)
이부분인데, 아.. 나는 값을 정렬하라는줄 알았는데 그게 아니라 key 이름대로 정렬해서 출력하는것이다. 진짜 풀이에 문제가 있는줄 알고 한참헤맸다..
새로운 라이브러리를 알게 되니 뭔가 강력한 무기를 갖춘 느낌이다. 플래그를 보니 정답이었던것 같다.

2018/10/31 15:41 2018/10/31 15:41

레이드.

마비노기라는 게임에 레이드라는 단어가 이제 어울릴지 모르겠습니다.
유저 하나하나가 너무 강해지는 바람에 체력이 못따라가는 상황인 것 같더군요..



그럼에도 불구하고 싴갤러스에서는 유저분들의 제보를 이용해 보다 더 나은 레이드 환경을 만들려고 항상 노력하고 있습니다 (진짜)
오늘 이 글에서 다룰 이야기는 바로 그 레이드 개선에 관한 이야기입니다

구조적 문제를 안고 시작하다


처음 싴갤러스의 레이드를 다룰때는 네이드, 거대사자, 거대악어, 평원 드래곤 그리고 이프리트 5종만 다루었습니다.



아마 위 레이아웃은 본적도 없으신분들이 많을 것 같은데요.
이때는 웹, 즉 PHP를 막 배우고 있을때 만든 홈페이지라 레이드 별로 페이지를 전부 하나씩 생성했습니다.
그리고 DB에서 보스별로 불러와 처리했죠. 그러니까 즉, SQL 쿼리만 살짝 다른 페이지가 5종, 전체를 볼 수 있는 페이지까지 해서 6종이죠.
그리고 자바스크립트도 잘 할 줄 몰라서 하나의 페이지를 가지고 인자를 전달해 다른 결과를 보여준다는건 생각도 못했고, 무조건 페이지 이동이 필요했습니다


js를 좀 배우고 나서도 문제가 해결되진 않았습니다.
이미 나눠진 페이지를 굳이 합칠 필요가 없었기 때문입니다. 그리고 이땐 또 ajax와 REST개념을 잘못 이해하고 있어서 ajax로 데이터를 불러오는게 아니라 페이지 자체를 그대로 불러와 대상 dom 에 씌우는 식이었습니다. 그러니까, 불러오는 페이지에서 표까지 완전히 작성하는 구조였죠.

그렇게 많은 페이지들이 만들어진 상태에서 레이드가 계속 늘어났습니다. 레드 드래곤부터 시작해서, 시간이 고정이었던 사막 드래곤, 신규 레이드인 블랙, 화이트 드래곤부터 지금의 실반 드래곤, 모쿠르칼피까지. 첫 단추를 잘못꿴 상태였기때문에 똑같은 페이지를 계속 만들어낼 수 밖에 없었습니다. 잘못된건 아는데, 빠르게 결과물을 내야했기때문에 -당시에 레이드는 경쟁구도의 사이트가 있었습니다- 속도를 올릴 수 밖에 없었고, 나중을 기약하며 조악한 페이지들을 계속 늘려만 갔죠. 그 결과는...


(여기서 strings는 작업하려고 추가해놓은거고 실제론 없었음)

raid3.php 이라는 뷰 페이지에서 page를 include 해서 보여주는 식이었고, 각 페이지에서는 각 프로세스를 처리할 (레이드 마다 시간이 다르기때문에) 페이지에 1:1로 매칭이 됩니다. 초록색은 작업하려고 만든거라 상관없고... 그러니까, 거의 20여개에 달하는 페이지로 구성되어 있던거죠... 그리고 스크립트도 PHP 변수를 사용해 처리하기때문에 인라인 스크립트로 작성되어 있고, css도 인라인 css로 작성되어 있기때문에...

단 하나의 수정이 발생하면 최소 8개 페이지(레이드가 다르더라도 인터페이스는 공통된 경험을 줘야하니까)가 수정되어야했고, 좀 심하면 처리 페이지까지 수정해야했으니 1글자를 고치기 위해 20페이지가 수정되어야 하는 기염을 토하기도 했습니다.
구조적인 문제가 분명히 있었으나, 다들 잘 쓰고 있으니까요. 굳이 수정이 안되어도 문제가 없었습니다. 어차피 레이드는 완성형이라고 생각했고, 뭐 하나 추가되면 페이지 늘려야지, 이젠 도가 텄으니 빨리빨리 수정할 수 있다 이런식으로 애써 신경안쓰려고 하고 살았습니다..


곪은건 터지게 된다


신규 기능 요청이 들어왔습니다. 별건아니고, 전광판을 여러개 띄울 수 있게 해주세요.... 인데
모든 보스몬스터가 별도로 구분되어 있기때문에 현재의 구조로는 제공할 수가 없었습니다. 정확히는 가능은 하지만 불필요한 함수를 작성해야할 수도 있고, 뭣보다 중간에 수정이 일어나게 되면 모든 페이지에 같은 내용으로 또 작성... 아... 더 이상은 얘기하기 싫네요.

그래서 이왕 이렇게 된거 레이드 자체를 다시짜자고 결심하게 되었습니다.
단 한번의 수정으로 많은 경험을 선사해주자는거죠. 결론부터 작성해보자면 다음과 같이 변했습니다.
여기에 raid.js 라는 별도로 분리된 js 파일이 하나 더 있습니다. 뷰를 담당하는 파일이 하나 더 있고요.



먼저 페이지를 통일하는게 중요했습니다.
어차피 설명만 좀 다르고 마크업은 똑같기 때문에 (아주 약간만 다르던지) assets에 strings 를 추가해 파라미터별로 설명을 다르게 표시해주는것부터 시작했습니다. 같은 이름의 변수를 case마다 계속 작성해나가는게 유쾌한 경험은 아니었지만 필요한 작업이니까, 좋게 말하면 string을 분리한거고, 나쁘게 말하면 우겨넣은거죠. 그래도 다음과 같아졌습니다.



이제 이 수많은 print_보스들... 왜 이럴까 소스를보니 초반부는 다른데 후반부는 완전 같습니다.
이상하네 하고 보니까(내가 짠건데 이상하네라고...) 그냥 시간을 체크해서 레이드 시간인지 아니만 판단하는 부분만 다를뿐 모두 같더군요.
그리고 이상하게 뿔피리데이터를 이쪽에서 만들고 있었는데 검색 키워드만 조금씩 다르고 쿼리도 같고요. 자갈치 시장도 아니고 한번에 처리하려고 발악을 한 느낌이었습니다. (print_smd_data , print_smd_horn 이렇게 안하려고...?)

해야할건 명확했습니다. 각 보스별로 시간 체크 및 시간, 제보 정보를 합치고, 뿔피리 데이터를 분리해 처리하는 두개파일로 나누는거죠.
그리고 전광판을 여러개 띄웠을때도 같은 페이지로 온전히 동작해야하니까 범용성을 살짝 넣고.

그렇게 서버에서 주는 값들을 대강 명세하고, js 를 수정했습니다.
기존엔 js안에 서버 변수를 넣어서 그대로 작성한 케이스가 많았는데 이번엔 어떤 서버변수도 사용하면 안되었기때문에, url 을 이용해 구분했습니다. 그리고 대부분의 DOM을 dynamic 하게 만들어야했기때문에 js안에 html div를 많이 작성했습니다. 이게 맞는 방법인진 모르겠는데.. 맞겠죠?;

js를 작성하면서 느낀건데 multi line을 작성할때 쓰는 ` (back qoute)와 new URL, filter arrow 등은 IE에서 사용할 수가 없었어서 다 만들었다 생각했는데 대체하기 위해 다시 작업을 하고.... 뭐 그랬던거 같네요. 그리고 신규 관리자분이 들어왔는데... 시련이 시작되었습니다. css 지옥이었는데.. 봐주는 관리자 한명이 잠을 안자...




화면을 같이 보고 있는게 아니라서 캡쳐 후 보여주기를 계속 반복해야했고 결국 대화방 앨범이 이거 관련 내용으로 한페이지를 넘게 차지하게 되더군요. 개인적으로 디자인 작업은 안좋아하기때문에 아주 많이 힘든 시간이었습니다.... ㅠㅠ
신규 기능 추가를 마치고, 테스트 서버에서 기능이 실제 데이터 기반으로 잘 돌아가는걸 확인하고 기능을 릴리즈 했지만 IE 호환문제, 창이 자동으로 안뜬다던지, 채팅 제보 정규식에 문제가 있다던지 하는 소소하면서도 안되면 불편한 문제들이 계속 발견되면서, 한번에 완벽하긴 정말 어렵구나 하는 생각을 했습니다

돌아보며


금번 작업은 보기엔 별거 없습니다. 기능한개가 추가 되었고, 사이트 외형적으로도 거의 변한게 없습니다.




그러나,
5,000여 라인의 코드 삭제가 있었고, 2,200 여 라인의 코드 작성이 있었습니다.. 이 작업은 단 이틀만에 수행되었고요..
훨씬 많을줄 알았는데 이거밖에 안돼 같은 생각이 들지만, 범용성을 위한 코드 리팩토링은 싴갤러스를 운영하면서 처음인거같습니다.

코드자체가 대단한것도 아니고 난이도가 있는 코드도 아니지만, 짧은 시간내에 끝내려고 일정을 빡빡하게 잡았고, 오히려 시간이 타이트했기때문에 한줄한줄 쓸모없이 작성하지 말자 이렇게 임했던 느낌입니다. 유저 수요도 고려하고, 중간에 기획을 바꾸기도 하면서..
개인적인 성취를 이룬 느낌이 더 강한 업데이트였습니다. 구조 통일도 했으니 앞으로 레이드쪽에 더 많은 기능 추가가 있을....수도 있겠죠?

다음엔 더 재밌는 글로 찾아뵙겠습니다~
2018/09/17 09:43 2018/09/17 09:43

앱 업데이트는 매번 참 두근거리는 일입니다. 
검수를 받기때문에 웹 처럼 빠르게 업데이트가 불가능하고(상대적으로), 기기의 이상 유무, 앱 설치와 제거 여부등 보고 있자면 흥미로운 통계가 많기 때문입니다.

사실, 싴갤러스는 모바일 레이아웃을 지원하기때문에 네이티브로 앱을 구성할 필요가 없고, 굳이 쓴다면 푸시 기능때문에 앱을 존속시키고 있는 것인데요. 그래서 그런가 많은 공수를 들이고 있지 않은것도 사실입니다. 그래도 이번에는 아이콘 그림도 많이 교체했고, 자주 볼 수 없는 화면에 신경도 (...) 썼습니다. 예를 들면 서버를 선택하는 화면에서 UI의 색이 바뀐다던지하는...

기본 웹에서 지원하는 아래로 끌어서 새로고침하는 기능도 넣어보았습니다. 이젠 당연하게 여겨지는 기능이고..
또, 안드로이드에서 갑자기 네트워크 문제가 발생했을경우 에러 다이얼로그 폭탄이 나오는 문제들도 수정했고, 캐시가 계속 쌓여서 앱이 비대해지지 않도록 신경도 써봤습니다.

그외에도 뭐 ... 다 말하지 못하는 사소한 것들도 참 많습니다만 어쨌든, 싴갤러스 1.13 앱을 즐겨주세요!
2018/09/11 09:14 2018/09/11 09:14