웹 로그 서버 구축기 with rails

현재 웹 로그 서버로 루비 온 레일즈를 사용해 개발중에 있습니다.

그 과정에서 간략한 기록 겸 정리로 포스팅해봅니다.

윈도우용 루비 개발에 이용한 Quick Installer 입니다.

RailsInstaller

루비 적응에 참고한 튜토리얼입니다.

Ruby on rails - getting started Tutorial

Ruby on Rails - Active Records Query Interface

scaffold를 이용해 쉽게 기본 기능을 만들어내는 구문입니다.

rails generate scaffold Post name:string title:string content:text

각종 책과 튜토리얼 등을 보면서, 튜토리얼을 보면서도 이해가 잘 안가서 가장 많이 시간을 소요한 부분… method 등록하는 법에 대한 포스트입니다.

routing error -> No route matches

요악하자면, 

# routes.rb
    resources :컨트롤러명 do 
      collection do 
       get :액션 이름
      end
    end

이렇게 추가해준 뒤,

rake routes를 해주면 등록됩니다.

루비 온 레일즈 사용해본 결과 MVC 모델로 설계 되어있어 역할 분담이 잘 되어있더군요.

model : 사용될 데이터 구조 (모델) viewcontroller에서 제어한 동작을 보여주는 코드 정의  controller : action 별로 controller에 동작을 정의

아직 많이 사용해보지 못했지만, 개발 속도는 압권입니다. routing error에서 헤멘 것 제외하면, 실제 개발 시간은 2시간 남짓에 웹 로그 서버를 구축했습니다. (운용 가능할 정도의 다량의 로그를 쌓는 작업에 대한 측정이나, 테스트는 제외하고 단순 기능 구현)

루비를 서버 제어 스크립트로 작업하고, 이번에 웹 로그 서버 작업을 하면서도 느끼는데, 루비에 대한 호감도가 계속 올라가네요.

자세한 내용은 공개하기 어렵지만, 간략하게라도 삽질기나 팁같은거 공유해보도록 하겠습니다. 

C++ 멀티 스레드 프로그래밍을 몇년간 해온 후 느낀 고찰

서버 프로그래머가 되기 이전엔 멀티 스레드 따위 관심도 없었다.

물론 그 시기까지가 클럭 향상 -> 멀티 코어로 변화가 이루어지기 전이기도 했지만… 여하튼 나는 그런 것 보단 다른 것들에 관심이 훨씬 많았다.

서버 프로그래밍을 시작하면서 멀티 스레드를 다루기 시작했고 만 7년이 된 지금까지 여러 프로젝트를 경험해왔고, 여러 사고를 쳐왔다.

그냥 정줄 놓은 누가 봐도 코드를 잘못 짜서 생긴 사고 (…)도 많았지만, 그보다 더 큰 사고는 주로 함정에서 발생했는데, 그 중 최고봉은 역시나 멀티 스레드 버그였다고 할 수 있다.

아니 아주 정확히는 멀티스레드에 맞게 코딩하지 못한 내 버그다.

내가 지금껏 프로그래밍을 공부해오며 생각해온 방식은 구조적 프로그래밍에 대한 이해를 전제로 해왔다. 그러던 중 멀티스레드를 받아들이려니 도무지 그 복잡도를 따라갈 수가 없었다.

지금은 여러가지 상황에 대한 경험으로 데이터 경합/동시 접근에 대한 문제를 찾는 노하우가 생겼고, 스레드 파이프라인 설계 등으로 문제를 회피하는 노하우등이 생겼을 뿐 여전히 멀티 스레드 코드를 작성하는 과정은 그것에 대한 고려가 없을 때 보다 수십배는 더 어렵다.

집중해야만 잘할 수 있는 일은 실수가 더 많아지기 마련이고, 당연히 골치를 썩고 있음은 물론이다.

성능 최적화에 대한 문제도, 새로운 이해를 요구하고 있다.

애초에 내가 당연시 여기던 많은 것들이 변한 것은, 멀티스레드 때문이다.

전치검사/후치보장을 잘 지켜 문제없이 돌아가던 코드가, 멀티스레드라는 환경으로 인해 무너지는 일은 절대 유쾌한 경험이 아니다.

네트워크 엔진/렌더링 엔진등 성능 최적화를 목표로 하는 경우 특히나 멀티스레드 코딩을 자연스레 하게 되고, 그로 인해 수 많은 문제를 내제하는 코드를 만들게 된다.

이를 디버깅하려면 해당 엔진의 스레딩 구조 (나는 이 것을 위에서처럼 스레드 파이프라인이라 부른다. 렌더링 파이프라인처럼 패킷이 처리되는 일련의 과정을 파이프라인이라 칭하는 것이다) 를 이해해야 하는데, 이 과정이 익숙해지기란 절대 쉽지 않다.

잠금 (파일 접근 잠금, 메모리 잠금 등..), 경합과 양보, 병목 등의 문제를 해결해야 하는 데, 이는 기본 설계 자체가 잠금을 용인하거나, 병목을 해결하기 위한 준비가 되어있지 않은 경우라면 매우 리스크 큰 작업을 해야 함을 의미한다.

이런 과정은 테스트 환경을 세팅하고 작업을 시작한다 해도 꽤나 큰 시간을 소비해야 되는 작업이라, 설계부터 Through-put 을 예측하고 보정하며 개발하는 것이 유리하고, 경합이 일어나지 않도록 스레드 디자인을 하려면 높은 이해도와 고찰이 필요한 작업이라고 밖에 할 수 없다.

특히 병목 지점을 profiling하고, 제거하는 과정은 수많은 테스트 케이스와 자동화 테스트 기반이 필요하고, 설계가 이미 병목 지점을 의도하고 있다면 지나치게 큰 작업을 요구하게 되는 경우도 빈번하다.

사실상 Framework은 안정적으로 돌아갔으면 하는 것이 일반적인 기대치이고, Framework에서 Logic의 멀티스레드화를 자연스럽게 매칭 시킬 수 있도록 (물론 전제 조건이야 있겠지만) 지원이 되면 더할 나위 없을테고, 멀티 프로세싱을 통한 Through-put 향상만 이루어져도 대게는 만족스럽다.

지금이 멀티코어 시대고, 단일 프로세스 내에 퍼포먼스 극대화에 대한 시도를 나도 하고 있고, 몇몇 회사가 시도하고 있지만 (GDC2013의 길드워2에 대한 세션 참고 : http://blog.naver.com/PostView.nhn?blogId=spacesun&logNo=140185766419&redirect=Dlog&widgetTypeCall=true) 소규모 팀 및 회사에서 마저 이런 시도를 당연시 여기고 개발해온 근래까지의 C++ 기반 서버는 내가 봐도 납득이 잘 안간다. (특히 네트웍 라이브러리 자체 개발 같은거 말이지)

그렇지만, 로직에서라도 병목 지점을 profiling해야 되는 것은 어느 규모의 서버나 매한가지다.

이 작업 자체는 기본적인 이해도라 할지라도, 멀티스레드에 모든 프로그래머가 익숙해져야 했던 기존의 C++ 서버 개발 방식은 그다지 좋지 않다.

멀티스레드에 신경써야 되는 일은 소수의 시니어 프로그래머가 해야 하며, 대다수의 주니어 프로그래머 내지는 컨텐츠 프로그래머는 적절한 함수 콜만으로 원하는 동작을 구현해낼 수 있어야만 한다.

멀티스레드에 내가 익숙해지기까지의 시행착오는 쉽지 않았다. 멀티스레드에 신경써야 될 요인들은 그렇게 마냥 간단하지 않기 때문이다.

특히 병목지점 여부를 파악하는 것이나, 설계가 갖춰지지 않은 상태에서 자주 내리는 판단인 잠금 객체 사용 (critical section, semaphore, mutex 등)은 결과적으로 성능 저하를 이끈다.

그래서 나도 다음 서버를 만든다면 C++서버와 웹서버 조합으로 짤 예정이다. (ruby on rails 또는 node.js가 될거라 예상한다.)

C++ 만으로 모든 서버 로직을 개발하며 겪에 되는 여러가지 문제로 소비될 시간에 더 많은 테스트 커버리지와 또 다른 방식에서의 (스케일 아웃등의 매커니즘이 웹서버 기반에서 많은 노하우와 솔루션이 많기에) 최적화를 시도 할 수 있기 때문이다.

꽤나 많은 서버에 포함되는 기능이 웹서버로 빠질 수 있다. 그렇게 되면 서버 프로세스내에 through-put은 저절로 상승하게 된다. Anti packet flooding 같은 기능이 활성화 되어있어, 인증받지 못한 동작은 guard된다는 전제하에서지만 말이다.

현재 만들어지고 있는 꽤나 많은 게임은 connection을 상시 맺는걸 전제로 하지 않아도 된다. 그리고 RESTful API를 사용함으로써 생기는 DB 접근 비용 (캐시 서버를 쓴다 할지라도, 그 자체도 비용일 수 밖에 없다) 문제도, 해쉬를 통해 변화가 없을땐 갱신하지 않는 방법 등으로 줄일 수 있다.

허나 한편으로는 반응성이 중요한 멀티플레이 게임에서는 C++, C#, JAVA등으로 만들어진 TCP connection 기반 서버가 여전히 필요할 수 밖에 없다.

그렇기에 앞으로의 흐름에 맞추고, 효율성을 높이며, 게임의 한계를 줄이기 위해선 더 많은 연구가 고찰이 필요하지 않나 싶다.

C++ 11 주요 Feature 정리

Ten C++11 Features Every C++ Developer Should Use

위 article을 간략하게 요약해봤습니다.

  1. Auto
    • Auto는 C++ 0x에서도 주요 Feature였죠.
    • 컴파일 타임 타입 유추 기능입니다.
    • 일반적으로 typedef 해서 자료형을 정의해두고, iterator, value_type 등을 사용해야 했던 번거로움을 한번에 날릴 수 있는 좋은 기능이죠.
  2. nullptr
    • NULL을 대체하는 type 타입입니다.
    • NULL이 ((void*)0)이나 0으로 define 되어있는 점을 감안 했을 때, type 검사가 이루어질 수 있는 nullptr은 모호함을 제거하기에 적합하다고 보여지네요.
  3. range-based for loops
    • for each와 같은 느낌의 범위 전체 순환 기능입니다.
    • for each ( obj in data_structure ) 와 동일한 기능으로 보여지네요.
    • 추가적인 장점이라면 배열에도 사용 가능해졌다는 점입니다.
  4. override and final
    • override는 가상함수를 override했음을 의미하고, 상위 클래스중에 overriding될 메소드가 없으면 에러를 뱉는 키워드인데요, 이 키워드가 표준화임을 의미하는군요.
    • final은 override를 더이상 할 수 없게 막는 키워드죠.
  5. Strongly-typed enums
    • enum을 강화해줬네요. 기존의 enum은 상수 나열형으로 유용하긴하지만, 다른 타입들보다 사용시 어려운 점이 많아 힘들었다는 점을 감안 했을 때, 아주 강해졌다고 보여집니다.
  6. Smart pointers
    • 개념 및 life-cycle의 이해를 위해 unique_ptr (구 auto_ptr), shared_ptr (흔히 smart_ptr을 shared_ptr만을 지칭하는 걸로 오해하곤 하는데 아닙니다.), weak_ptr을 적절히 사용하면 코드 가독성을 높일 수 있습니다. 이 세가지 smart pointer 에 대한 설명이네요.
  7. Lambdas
    • 익명 함수의 유용성은 더 말해 입이 아플 정도겠죠?
  8. non-member begin() and end()
    • 반복자를 member외에 추가로 template method화 했다는 의미입니다.
    • 일관성을 위해 변경했다고 하고… 배열에도 사용가능해졌다는 장점도 있다고 하네요.
  9. static_assert
    • GPG 3권에서 나왔던 compile_assert와 유사한 기능이 표준화되었군요.
    • 컴파일 타입 검사를 강화하는 기능입니다.
  10. Move semantics
    • R-value reference의 효율을 의미하는 것으로, 동일한 객체의 리소스를 다른 객체로 전송해서, 복사 비용을 줄이는 매너니즘을 의미합니다.
    • 실제로 이로 인한 성능 향상을 c++ 0x에서도 많이 봤었죠.
    • 정확히 하자면 move semantics의 구현중 r-value reference가 있는 개념이라고 하네요.

이렇듯 C++이 진화하고 있습니다. 루비같은 언어와는 애초에 접근자체가 다르긴하지만, 자바나 기타 C계열 파생 언어등에 비해 갖고 있던 리스크를 지속적으로 해결하고 있는 걸 보면 뿌듯하네요. 서버 개발은 node.js 등으로 많이 옮겨가고 있지만, 아직 C++은 죽지 않았다고 생각합니다. (개인적으론 앞으로도 계속 유지될 언어라고 생각합니다. 왠만한 언어들보다 라이프 사이클이 더 길거라고 믿는 편이죠)

여기까지 C++ 11에 대한 간략한 정리였습니다. 감사합니다.

C++ 11 Wiki

루비 사용기

최근 업무 관련해서 루비를 쓰게 됐다.

2007년에 루비를 잠깐 했었으나 숫자 야구 만들었던 게 다였고, 거의 6년만에 다시 해본 루비에 대한 감상을 정리한다.

우선 과거에 사용했던 시기를 떠올려본 루비에 대한 감상을 먼저 나열해보겠다.

  • 스크립트 디버깅이 힘들다. IronRuby로 쓰면 좀 낫다.
  • C와의 연동은 준수한편.
  • 문자열 다루기 쉽다. 아주!
  • 그렇지만 역시나 trim, chop 같은 메소드를 통해 문자열을 섬세하게 다뤄줘야 함은 여전함.

이번에 새로 루비를 쓰기 전까지 주로 써온 스크립트로는 batch script, jscript (java script의 윈도우 내장 스크립트), 펄, 루아 였다.

이번에 루비를 써보고 느낀 건 역시 쉬운 편이라는 점이었다.

# 파일 오픈
file = File.open(ARGV[0], "r+")
# 파일 라인 단위 파싱
file.each_line do | line |
    blah blah
end
# 라인 공백 단위 파싱    
ip, port, servername, gsid = line.split(/ /)

이렇게나 쉽게 파일과 문자열을 파싱 할 수 있다.

JScript가 C++이나 Java와 유사한 문법과 심지어 API 사용 마저 얼추 C++과 비슷하다는 장점(?)이 있다보니, 루비를 접하면서 C계열 보다는 펄 쪽에 좀 더 가깝지 않나 싶은 생각이 들었다. (파이썬은 안써봄.)

펄이 문자열 다루기에 강자인 반면, 코드 가독성을 있는 대로 떨굴 수 있다는 단점이 있었던 데에 비해, 루비는 간결함을 모토로 가독성 좋은 코드를 짧게 짜는 데에 장점이 있었다.

물론 복잡하게 짤라면 짤 수 있음은 여타 언어와 흡사하나, 의지(!)를 가졌을 때 간결하게 짜기에 적절하게 라이브러리도 필요한 기능 대다수가 기본 지원이었고, 준수했다.

위에 예제들 중에서 가장 맘에 들었던 것이 라인 공백 단위 파싱해서 적절한 변수에 담는 코드였다.

메타 데이터를 쉽게 읽고 처리할 수 있다는 점은 JScript, Batch Script 등처럼 쉽게 프로그램 사이의 연동 스크립트로써 사용할 수 있다는 의미이고, 루비 자체의 기능도 함께 사용하면 어지간한 기능은 구현 가능하다는 의미다.

이외에 든 생각은 아래와 같다.

  • 루비가 쉽긴 하지만, 그렇다고 학습비용이 안들진 않는다.
  • 느리다. 확실히 느림.
  • 코딩이 유연하다지만, 실수는 여지없이 스크립트 오류를 발생 시키기에 논리적인 빈틈을 줄이는 노력은 다른언어와 다를 바 없다.

얼마나 많은 일을 할 수 있냐보다, 얼마나 짧은 시간에 직관적인 코드로 결과물을 만들어 낼 수 있느냐의 관점에서 봤을 때 분명히 루비는 좋은 언어라는 생각이 든다.

루비 온 레일즈도 어느정도 공부 해볼 생각이다. 루비에 좀 더 익숙해졌을 때의 감상이 지금과 같을지 궁금하니… 좀 더 써보고 다시 포스팅하겠음.

인공 지능에 대한 간략 정리

인공 지능에 대한 간략 정리

유전적 알고리즘 (GA)

선택에 점수를 메긴다.

좋은 성적 받은 상위 2개 (이 갯수는 임의 선택일뿐이다)는 유전 시키고, 하위 2개는 도태시킨다.

유전 시킬 때에는, 2개의 부모 개체에서 어떤 세포들을 교차 시킬지도 꽤나중요하다. 그렇지만 GA에서는 대부분 랜덤으로 교차할 세포를 정한다.

유전 시켜 생겨난 새 개체에게선 임의로 특정 세포를 변이 시킨다.

이는 좋은 개체를 만들어내기 위함이고, 돌연변이만 만들어내려하거나 도태된 개체를 만들기 위했을 때는 다른 방식으로 얼마든지 응용이 가능하다.

뉴럴 네트워크 (NN)

자신이 흥분할 여지가 있는 (좋아하는 기호의) 신호가 왔을 때에만 반응함으로써, 이 것이 정답 (원하는 답) 인가를 판단해내는 방법이다.

모범 답안 (정답)을 알고 있기에, 그에 맞지 않는다면 파라미터 (신호)를 조정해가며 원하는 답인지를 학습해나간다.

마음에 드는 답을 얻을 때 까지!

카오스

어떻게 변할지 모르는 변화 무쌍한 것을 카오스라 부른다.

보간, 난수, AI 등의 다양한 분야에서 쓰인다.

엑스퍼트 시스템

학습형 시스템이 아니다.

상황과 그것에 대한 대처 방법인, 판단과 예측을 미리 준비해둔다.

상황별로 추론할 수 있는 가능성에 따라 점수를 메기므로, (높은 가능성은 높은 점수) 얻어낸 정보 (상황)을 바탕으로, 해결책을 알아 낼 수 있다.

단점으로는 모든 상황을 캐치할 수 없다는 점과, 대처 방법을 만들어낸 사람의 범주 안에서만 결론이 난다는 점 등이 있다.

퍼지 이론

흑백 논리로 나뉘어 판단하는 것이 아니고 (180 이상이면 위너, 179이하면 루저라던지) 170은 0.7 178은 0.95, 180은 1.0 등의 점수를 메겨서, 키가 큰편에 속한다 또는 키가 크다 라는 식의 차이의 정도를 판단을 할 수 있게 도와주는 이론이다.

강화 학습 시스템

원하는 결과를 얻거나, 실패할때까지 시도해본다.

실패했을 경우에는 그때까지 내린 선택에 벌점을 내리고, 성공 했을 경우 점수를 준다.

그리고 다시 시도를 하게 되는데, 이 때 점수가 높은 곳과 점수가 적은 곳이 있을 때 이는 절대적인 수치가 아니고, 어떤 선택지를 선택할지에 대한 확률 역할을 한다.

하지만, 계속되서 잘못된 판단이라 여겨진다면 (벌점이 높아 점수가 하나도 없게 된다면) 그 선택지는 버려지게 되는 것이다.

L 시스템 (Lindenmayer가 만든 시스템이라 L시스템)

규칙을 통해 변화를 줘서 발전 시키는 시스템.

L-system의 구성은 의외로 간단한다. 초기 문자셋(w)와 바꿔쓰기 위한 규칙(p)이 있을 뿐이다. 물론 위 복잡한 식물을 표현하기 위해서는 혹은 3D 식물을 모델링하기 위해서는 더 복잡하다. 간단한 아래 예를 살펴보자.

w : b
p : b->a, a->ab

위 문구를 초기 문자는 b이고 b를 a로 바꾸고 a 문자를 ab 로 바꾸라는 간단한 L-system의 구성이다. 위 규칙으로 인하여

b
a
ab
aba
abaab

식으로 자라나는? L-system을 구성한 것이다.

참고 자료