카테고리 보관물: 개발

개발과 관련된 이야기들~*

“길벗과 함께” 이용자 폭증 사태

이틀간 폭풍같이 이용자들이 “길벗과 함께”를 쓸고 지나갔다.

엄청 많은 사용자가 몰린 게 아님에도 불구하고, 서비스의 서버 성능 의존도가 발목을 잡아서 이용자들이 서비스를 제대로 이용하지 못하는 상황이 발생했다. 우선은 급한 불을 끈 상태다.

이번 경험을 통해 느낀점을 정리하면,

  1. 현재 “길벗과 함께”는 서버에 부하를 많이 주는 구조다. 즉, 현 상태를 지속하면 추후 다수의 접속이 발생했을 때 서비스 불가 상태가 또 발생할 수 있으므로 해결책을 찾아야 한다.
  2. 폭풍 같은 접속이 있다고 해서 사람들이 꾸준히 이용하는 것은 아니다. 특정 이벤트가 발생했을 때 이용자가 증가한다. 특히, 퀴어페스티벌 전후로도 버틸 수 있어야 할 것이다.
  3. 부하를 최대한 서버에서 클라이언트 단으로 옮겨야 한다고 잠정적으로 결론을 내렸다. 하지만 그걸 어떻게 할 것인가! 사실 대강의 청사진은 그려 놓은 상태다ㅎㅎ 과연 얼마나 잘 작동할지는 테스트 해 봐야 할 것 같다.

당장은 할 일이 있으니 조만간 다시 살펴보는 걸로!

‘길벗과 함께’ 출시

길벗체로 이미지를 쉽게 만들 수 있는 서비스 ‘길벗과 함께(https://gilbeot.osg.kr)‘를 지난 주에 출시 하였다.

길벗체로 작성한 문구를 프로필 사진으로 활용하고 싶은 분들이 많다는 사실을 알고서, 어떻게 하면 이용자들이 쉽게 이용할 수 있을지 몇 달 동안 고민하고 있었다.

두어 달 전에 기술적 가능성을 살펴봤는데, 그때까지 확인한 방법으로는 컬러폰트를 사용할 수 없었다. 그런데, 최근에 작업했던 내용들과 관련해서 지난 주에 갑자기 아이디어가 떠올랐다. 그래서 확인해 본 결과 가능성이 있음을 확인하고, 작업에 착수하였다.

대략 3일간 전체적인 개발을 마치고, 이틀 정도 디버깅을 마친 후 출시하였다. 물론 문제가 있다. 사람들은 예상치 못했던 이모티콘을 사용하고 싶어했고, 준비되지 않은 글자를 사용하는 경우 오류가 발생하는데, 사용자가 인지할 수 있는 UI/UX가 준비되어 있지 않아서, 이용자들이 아직도 불편을 겪고 있다. 이에 대해 미안한 마음이 든다. 혹시 길벗과 함께 이용자가 이 글을 읽는다면 이해를 부탁드린다.

빨리 개선하고 싶은데 개인 프로젝트로 하는 것이다보니 아무래도 완전히 여기에만 집중할 수 없음이 아쉽다. 그래도 짬짬이 시간을 내어서 조금씩 개선해가고 있다. 매일 방문하고 이용해 주시는 분들이 계셔서 ‘내가 뻘짓으로 시간을 버리지는 않았구나’ 싶은 생각이 들기도 한다.

앞으로도 필요한 서비스들을 틈틈이 만들어 가려고 한다.

numba guvectorize 활용하기

numba의 guvectorize를 사용하면 array 형태의 값 처리의 속도를 매우 빠르게 할 수 있다.

우선 실행 결과를 먼저 보겠다. aaa는 guvectorize를 사용해서 연산한 경우이고, bbb는 그냥 python 코드로 실행한 결과이다. ccc는 numpy의 벡터 연산을 실시한 결과다. guvectorize를 적용했을 때 단순 for loop를 사용했을 때에 비해 약 300배 가까이 빠른 실행을 보였다. numpy의 벡터 연산을 활용하면 for loop을 사용했을 때 보다 1400배 가까이 빠르게 실행되었다.

aaa: elapsed=0.0275056362s
bbb: elapsed=9.2325780392s
ccc: elapsed=0.0066127777s
>>> 9.2325780392 / 0.0275056362
335.6613158142476
>>> 9.2325780392 / 0.0066127777
1396.1724494685493

결론부터 말하자면, Array의 단순 연산은 numpy의 벡터연산이 가장 빠르다. 특정 로직에 따른 연산이 필요할 땐 guvectorize를 활용하자.

guvectorize의 사용법을 간단히 살펴보자. guvectorize의 경우에는 값을 return하지 않는다. 대신에 파라미터에 대한 선언을 정확히 해 주면 된다.

@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def function_name(x, y):

순서대로 int64[:], int64[:]는 뒤의 (n) -> (n)과 매칭된다.

Array가 아닌 단순 값을 파라미터로 추가하는 경우에는 아래와 같이 처리하면 된다. 데이터 타입의 정의 개수와 입력, 출력 파라미터의 형식, 그리고 함수의 정의까지 모두 일치해야 한다.

@guvectorize(['void(int64[:], boolean, int64[:])'], '(n),() -> (n)')
def function_name(x, twist, y):

아래에는 위의 결과를 만든 소스 코드를 붙인다.

import time
import numpy as np
from numba import guvectorize


def timer(func):
    def measure(*args, **kwargs):
        begin = time.time()
        val = func(*args, **kwargs)
        end = time.time()
        print(f">> {func.__name__}: elapsed={'%.10f' % (end - begin)}s")
        return val

    return measure

@timer
@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def aaa(x, y):
    for i, val in enumerate(x):
        y[i] = val + 3 * 3 / 5 * 1.23 / 6.53

@timer
def bbb(x):
    y = np.empty([len(x), 1])
    for i, val in enumerate(x):
        y[i] = (val + 3 * 3 / 5 * 1.23 / 6.53)
    return y

@timer
def ccc(x):
    return x + 3 * 3 / 5 * 1.23 / 6.53

a = np.int32(np.round(np.random.rand(5000000) * 1000))
out = aaa(a)
out = bbb(a)
out = ccc(a)

매매로직 재사용

빠른 배포가 중요하다는 포스팅에서 이야기 한 방법은 사실 안정적이지 않다. 더 좋은 방법은 백테스트할 때 활용한 매매 시스템의 로직을 트레이딩 시스템에서 동일하게 활용하는 방법이다. 1호 시스템을 만들고 나서, 앞으로 2호 이후의 시스템들을 만들 생각을 하니 매번 모든 코드를 작성해야 하는 것은 비효율적이라는 생각이 들었다. 매매로직을 모듈화해서 백테스트와 트레이딩 시스템에 모두 동일하게 활용할 수 있게 한다면, 백테스트에서 실전 매매 시스템 개발까지의 기간을 최소화 할 수 있을 것이라는 생각이 들었고, 이 작업에 이제 곧 착수할 예정이다.

백테스트의 성능을 저하시키지 않으면서, 트레이딩 시스템에 어떻게 장착할 수 있을지에 대한 설계가 필요하다. 특히 키움의 QEventLoop 사용방식은 코드의 복잡도를 증가시키기 때문에 ES7의 async, await 처럼 비동기를 동기식으로 사용할 수 있는 방법을 찾아보려고 한다.

그리고 앞으로는 TDD를 사용하여 시스템을 개발하려고 한다. 과거의 경험을 비추어봐도 TDD로 개발할 때 생산성이 가장 좋았고, 오류가 적었다. 1호 시스템을 개발하면서 매매로직 테스트하는 부분만 TDD를 활용하긴 했는데, case에 대한 정리가 부족한 탓에 오류 상황들이 발생했다. 그럼에도 테스트코드가 있어서 빠르게 문제를 발견하고 해결할 수 있었다.

쨌든 Do not Repeat Yourself(DRY 원칙)를 시스템 구축에도 활용해서 시스템 제작의 생산성을 높여야겠다.

트레이딩 시스템도 역시 빠른 배포가 필요하다!

시스템을 제작하고 이번 주에 시스템을 구동했다.

개발을 하면서 늘 느끼는 것은 ‘빠른 출시’는 언제나 항상 옳다는 것이다. 서버에 배포가 되고나면 문제해결에 보다 집중하게 된다.

작성한 코드에서 문제가 발생하면 안되기 때문에, 빠르게 버그를 잡아내고 문제를 해결해야 한다. 심지어 트레이딩 시스템은 현물로 거래하는 구조이기 때문에, 자칫 잘못하면 금전적 손실을 떠안을 수도 있다.

트레이딩 시스템 구축시 반드시 해야 하는 것은, 1차적인 테스트는 절대적으로 모의투자 기능을 적극적으로 활용해야 한다. 사실 모의투자와 실전투자의 차이는 키움의 경우 모의투자에서 제공되지 않는 API가 있는 정도고, 그 외에는 모두 동일하다. 단지 계좌번호가 다를 뿐이다.

본인이 만든 시스템에서 매수/매도 신호를 제때 일으키고 정해진 룰에 따라 주문을 하는지 확인하는 과정은 필수적이다. 이 과정을 굳이 실전계좌를 사용해서 현물을 잃어가면서 할 필요는 없다.

적정한 테스트 기간이 필요할 것이고, 안정적으로 테스트가 된 후에 실전계좌로 변경해서 수익화 해 가면 될 것이다.

IntelliJ IDEA에서 한글 입력이 안 되는 현상(Ubuntu 18.04, UIM 입력기)

최근에 IntelliJ IDEA 2019.3 으로 업데이트 후 한글입력이 안되는 현상이 발생했다.
그래서 Jetbrains에 문의하였다.

한글입력을 위해서 UIM(벼루)을 사용하고 있었고, IDEA 2019.2에서는 작동했지만, 업데이트 후 작동하지 않음을 보고 했다.

idea.log를 첨부해달라는 메시지를 보고, idea.log를 살펴봤는데, 문제가 되는 로그를 확인했다.

2019-12-26 21:13:15,112 [ 10848] INFO – ntellij.idea.ApplicationLoader – canDisableInputMethod spent 33 ms, found keyboard layouts: [(us, xkb), ], result==true
2019-12-26 21:13:15,209 [ 10945] INFO – #com.intellij.idea.Main – InputMethods was disabled

UIM은 영문입력기만 있으면 되고, 한글 입력기가 별도로 필요없다.

그런데, 영문 입력기 하나만 있으면 IDEA에서 canDisableInputMethod를 true로 반환하는 것이다. 그러니 Shift + Space를 눌러도 아무 반응이 없이, Space 처리만 되면서 한글을 입력할 수 없었던 것이다.

그래서 ibus용 한글 입력기를 하나 추가한 이후에 다음과 같은 로그를 확인했다.

2019-12-26 21:24:45,771 [ 11841] INFO – ntellij.idea.ApplicationLoader – canDisableInputMethod spent 77 ms, found keyboard layouts: [(hangul, ibus), (us, xkb), ], result==false

이제서야 IDEA에서 한글 입력이 잘 된다. 물론 입력기는 그냥 영문으로만 선택해 놓고, UIM을 사용하여 한글을 입력한다.

ibus를 사용하면 웹 브라우저에서 한글 입력이 자음만 되는 이상한 현상이 있어서 UIM을 사용하고 있다.

어쨌든 이렇게 해결을 했다.

Jetbrains에서 관련 이슈를 파 줬다.
https://youtrack.jetbrains.com/issue/JBR-2049

동일한 문제로 어려움을 겪는 분에게 도움이 됐으면 좋겠다.

Python에서 용량이 큰 테이블의 insert, update 속도 문제

테이블 하나에 약 800만개의 레코드를 가진 테이블(약 1.2GB)을 만들고 분석할 기회가 생겼는데, 매일의 데이터 변경사항에 대해 추가하거나 업데이트를 해야 한다.

약 1,000개의 레코드를 업데이트하는데 25초 정도 걸렸다. 분명 뭔가 잘못됐다는 신호다. 초당 40개 정도의 업데이트밖에 되지 않았다.

해결책은 그리 복잡하지 않았다. 필자가 했던 적용법들은 다음과 같다.

1. select query를 사용하여 레코드 하나씩 1,000번씩 쿼리를 던져서 필요한 값들을 확인하던 것을 한 번의 쿼리로 작업 단위에 필요한 만큼 불러온 후, python에서 관련 값들을 확인하고 처리하도록 변경했다.

2. insert시 1,000개의 insert query를 실행하던 것을 다음과 같이 한 개의 insert query로 변경했다. 이 방법을 사용하는 경우 query문이 너무 길어지는 경우 실행되지 않을 수 있다. my.cnf에 관련 값을 얼마로 설정했느냐에 따라 달라지는 것으로 보이는데, key값이 지금은 기억나지 않는다. 추후에 확인해 보고 추가해 놓으려 한다.

insert into 테이블명 (필드1, 필드2, ...) values (값1a, 값2a, ...), (값1b, 값2b, ...), ..., (값na, 값 nb, ...)

3. update시 1,000개의 query를 실행하는 것은 동일하지만, where절에 키 두 개를 사용해서 update 하던 것을 primary key 하나만 사용하도록 변경하였다.

위의 3가지 방법을 모두 적용한 후 1,000개 레코드 처리에 25초 정도 걸리던 것은 평균 0.6초 이내로 처리시간이 단축되었다.

적용하기 전에는 Mysqld 프로세스의 CPU 점유율이 단일코어기준으로 100%이었는데, 변경 후에는 약6% 언저리에서 작동했다. 작업 환경은 다음과 같다. CPU는 인텔 G4400, 메모리 8G, SSD 128G, OS는 Windows 10, MariaDB 버전은 10.2를 사용하였다. 그리고 프로그래밍 언어는 Python3에서 MySQLdb를 사용하였다.

테이블 용량이 조금 크다고 해서 테이블을 여러개로 쪼개는 것이 우선이 아니라는 것을 이번에 배웠다. 특히 테이블 크기가 큰 경우에는 DB의 부하를 줄이는 방향으로 최적화할 필요가 있음을 느꼈다. 재미있는 경험이었으며, 기본적인 원칙이 얼마나 중요한지 다시금 생각해 보는 계기가 됐다.

mocha, jasmine 원하는 테스트만 실행/제외

Mocha에서 원하는 테스트만 실행하려 할 때는 .only를 사용하면 된다.

다음 예제의 경우 test 1만 실행된다.

describe('description', () => {
  it.only('test 1', () => {})
  it('test 2', () => {})
})

그리고, 다음의 경우에는 description 1의 test 1, 2만 실행된다.

describe.only('description 1', () => {
  it('test 1', () => {})
  it('test 2', () => {})
})

describe('description 2', () => {
 it('test 11', () => {})
 it('test 12', () => {})
})

그리고 제외하는 것은 .skip을 사용하면 된다. 작동되는 원리는 .only와 같다.

describe.skip(~)

it.skip(~)

Jasmine에서도 작동원리는 Mocha와 동일하다.

Jasmine에서는 focused의 의미로 f를 붙여서 원하는 테스트만 실행한다.

fdescribe(~)

fit(~)

그리고 제외하는 것은 exclude의 의미로 x를 붙여서 테스트를 제외시킨다.

xdescribe(~)

xit(~)

jasmine이 사용하기는 더 편해보이지만, mocha는 처음 보는 누구라도 쉽게 이해할 수 있도록 되어 있다.

<요약>
1. 원하는 테스트만 실행:
– mocha(describe.only, it.skip)
– jasmine(fdescribe, fit)
2. 원하는 테스트만 제외:
– mocha(describe.skip, it.skip)
– jasmine(xdescribe, xit)

IntelliJ IDEA Preview 한글 깨짐

IntelliJ IDEA를 설치하면, 안드로이드 개발시 Layout xml의 Preview 창에 한글이 깨진다. 해결하기 위해서는 폰트를 재설정 해 주어야 한다.

필자는 금번에 Jetbrains의 Toolbox를 사용하여 IntelliJ IDEA를 설치하였다. 그래서 설치 경로는 다음과 같았다.

$HOME/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/172.4574.11/

폰트 설정 파일은 위의 경로 하위로 다음 경로에 있다.

plugins/android/lib/layoutlib/data/fonts/fonts.xml

노토 산스 CJK면 한글이 깨지지 않아야 할 것 같은데 깨지는 문제가 있다. 아래의 내용 중 NotoSansCJK-Regular.ttc를 나눔고딕으로 변경해 주면 된다.

<family lang="ko">
<font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
</family>

다음과 같이 변경하면 된다.

<family lang="ko">
<font weight="400" style="normal" index="1">NanumGothic.ttf</font>
</family>

IntelliJ IDEA 단축키 사용기

Jetbrains Night 서울에 참석한지 아직 일주일도 지나지 않았지만, 지난 한 주간 단축키 몇 개 더 쓰면서 들었던 생각과 금주에 학습하고 활용한 단축키를 몇 가지 소개하려 한다.

이전 포스팅에서도 언급했듯 한글97의 단축키는 대략 80% 이상을 외워서 사용했다. 물론 마우스 사용은 극도로 꺼렸는데, 그 이유는 문서 작업의 흐름이 끊기기 때문이었다. 단축키 5-6개로 작업 할 동안 마우스로는 한 두 가지 작업 밖에 못한다는 게 너무 답답해서, 늘 단축키를 습관처럼 사용했었다.

그러던 내가 IntelliJ IDEA를 사용하면서 활용하는 단축키가 30개도 안된다는 게 사실 이번 컨퍼런스에서 느낀 불편함이었다. 그래서 이번 행사에 참석한 후 intelliJ IDEA에서 최소한 일 평균 5번 이상 클릭이 반복된다 싶으면 단축키를 외워버리기로 마음을 먹었다.

이번 주에 사용하기 시작한 단축키가 몇 가지 있어서 소개하려 한다. 이미 아시는 분들도 많겠지만, 필자와 같이 그동안 단축키를 사용하지 않는 분들이 계실 수도 있으니 소개한다.

자주 사용하는 툴박스들의 단축키는 Linux에서는 Alt + 숫자키 그리고 맥에서는 Cmd + 숫자키이다. 특히 노트북을 사용할 때에는 화면이 비좁다. 때때로 툴박스를 접고 펴기 위해서 그동안은 마우스 클릭으로 토글해서 사용했는데, 단축키 한 번이면 마우스 움직이는 몇 초를 아낄 수 있다. ‘뭐 그깟 몇 초를 아낄려고 그렇게까지 하나’라고 생각할 수도 있는데, 그건 하루 이틀만 코딩하는 사람이라면 그렇게 말할 수 있을 것 같다. 뭐 똑똑하신 분들이야 시간이 좀 흘러도 작업하던 맥락을 잘 챙기시겠지만, 필자는 그리 똑똑한 편은 아니라 흐름이 끊기면 맥락을 다시 떠올리느라 고생한다. 뭐 그런 면에서 단축키는 분명 작업 중에 맥이 끊기지 않게 해 준다는 면에서 유용하다.

그리고 IDE 내의 터미널은 Alt + F12이다. 바로 해당 창을 열거나 닫을 때 해당 단축키들을 사용하니, 정말 편리하다.

기본으로 제공하지 않는 단축키들은 Settings(or Preferences)에서 직접 Keymap을 지정해 주면 된다. 안드로이드 개발 시에는 Android Monitor의 logcat을 활용할 때가 많은데, 기본으로는 단축키가 설정돼 있지 않아서 수동으로 설정해 주었다.

문자열 선택을 손쉽게 할 수 있는 단축키가 Mac에서는 Alt + up 또는 down 키를 사용해서 문자열 선택 범위를 넓히고 좁힐 수 있다. Ubuntu를 사용하면서 이 단축키를 찾았다. Ctrl + w 와 Ctrl + Shift + w로 각각 선택영역을 넓히고 줄일 수 있다.

마지막으로 단축키는 아니지만 가끔 문자열을 선택하고 따옴표나 쌍따옴표나 괄호로 묶어야 할 때가 있다. 문자열 시작점에 따옴표 하나 찍고 끝에 가서 따옴표 하나 더 찍기도 귀찮고 따옴표도 찍고 괄호도 쳐야하면 정말 귀찮기 짝이 없다. 그럴 땐 Settings(또는 Preferences)에서 Editor > General > Smart keys에서 Surround selection on typing quote or brace를 체크하고 적용한다. 그런 다음, 문자열 선택 후 따옴표나 괄호를 쳐보면 해당 문자열에 손쉽게 따옴표나 괄호 처리를 할 수 있다.

그동안 마우스로 클릭하면서 이걸 단축키로 할 수 없을까 싶었던 것들은 대체로 단축키가 있었다. 빠르게 단축키를 외우는 방법은 많이 사용해서 손에 익숙하게 하는 것보다 더 빠른 길은 딱히 없는 것 같다.

언젠가 기회가 되면 단축키 사용에 대해 정리를 한 번 해보고 싶다. 일단은 Jetbrains에서 제공하는 keymap 링크를 여기에 공유하며 글을 맺는다.

https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf