가끔 프로그래밍하다 보면 새로운 걸 불현듯 깨닫는 때가 있다. 당연히 이미 알고 있어야 할 걸 말이지. 

 

오늘은 Unit Test를 하다가 Constructor와 OnInit의 용도 차이를 깨닫게 된다. 

OnInit은 Override 안 하면 안 생기니, 아무 생각 없이 Constructor에다 초기화 코드를 때려 넣었다. 

 

만들어낸 Unit Test를 하려고 보니 아.. 뭐냐.. 왜 이렇게 에러가 많이 생기는 거냐? 

Constructor에다 초기화 코드를 다 때려 넣었더니 Mocking을 안 하면 아예 동작을 안 한다. ㅋㅋ 

 

Assign와 DI만 빼고 다 옮겼더니 갑자기 Unit Test로 작업할 양이 확 줄면서, 동작하기 시작한다. 

왜 그랬을까나... 왜 몰랐을까나... 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

NgRx Effect에 withLatestFrom을 조합해서 사용하는 Production Code는 아주 잘 동작한다. 근데, 이걸 Effect에서 Unit Test와 MockStore를 조합해서 사용하면 동작하지 않는다. 그냥 undefined stream이라는 에러만 뱉어 낸다. 

 

구글링을 해도 실마리는 없고, 대충 여러 가지 글을 추정으로 다음과 같은 결론을 내렸다. 

 

withLatestFrom은 Observable이나 Array등을 입력으로 받는데, 이게 이미 초기화가 이루어져 있어야 한다. 초기화가 이루어져 있지 않으면 undefined stream이 된다. 즉, 현재 에러는 withLatestFrom의 source stream 들이 초기화가 이루어져 있지 않아 undefined stream 에러를 발생시키는 것으로 보인다.  

 

Production Code에서는 MockStore 등을 사용하지 않으므로, Store의 초기화, 각 Observable들의 초기화 등의 모든 게 이루어진 다음에 withLatestFrom의 Source Observable이 참조되는 것 같다. 

 

해결책은 Source Observable이 할당된 후, Effect를 생성시키면 된다. 아래 예는 동작하지만, isActive$를 testPolling$ 아래에 위치키는 순간 undefined stream 에러가 발생한다. 

 

export class RecentEventStoreEffects {
  isActive$ = this.facade.isActive$;
  
  testPolling$ = createEffect( () => this.actions$.pipe(
    ofType(RecentEventStoreActions.RecentEventStartPolling),
    withLatestFrom( this.isActive$ ),
    
    .................
    
  ));
  
  .....
  

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

iif가 흔히들 생각하는 conditional statement라고 생각했는데 아니다. 

 

아래 예에서 언뜻 생각하기에는 input is arrived in 출력이 하나만 찍힐 것 같은데, 두 개가 찍힌다. 

iif는 조건문의 실행이 아니라, 리턴된 두 개의 stream 중 subscription만을 할 거냐 말거냐를 결정하는 거다. 

즉, "input is arrived in #1", "input is arrived in #2" 둘 다 찍히게 된다. 

 

import { of, pipe, iif } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

const source$ = of('Input');
const one$ = (x) => {console.log(`${x} is arrived in #1`); return of('One')};
const two$ = (x) => {console.log(`${x} is arrived in #2`); return of('Two')};

source$.pipe(
  mergeMap(v =>
    iif(
      () => v === 'Input',
      one$(v),
      two$(v)
    ))
).subscribe( x => console.log('Output is ' + x ));

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

Angular 테스팅에서 jest를 사용하는데, Marble Testing을 해야하기에 테스팅 툴을 찾았다. 

 

jasmine-marble 과 비슷하게 jest-marble이 있기에 아무 생각없이 올려서 사용하는데, Time Progression Syntax가 지원이 안 된다. 사용 방법을 찾다가 알게 된 거, jasmine-marble이고 jest-marble이고 간에 모두 rxjs testing 툴을 그냥 Wrapping한 거였다. ㅎㅎ 

 

그냥 rxjs의 기본 testing tool을 사용하면 될 걸 삽질 하고 있었다. 

 

Reference


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

C++, Java....

Programming 2012. 3. 26. 16:43
처음 컴퓨터라는 걸 전공으로 삼았을 때는 많은 사람들이 Turbo Pascal, Turbo C를 사용했다. 그리고는 객체 지향이라는 게 갑자기 주변을 휩쓸었고, 어느 순간 모두 C++라는 걸 다들 사용하기 시작했다. 물론 그 사이에도 LISP, SmallTalk 같은 언어도 있긴 했지만 주류는 아니었다. 

그 후 여러 가지 일을 하면서도 항상 C++를 주로 사용했고, 그 외의 언어는 사용은 했지만 많이 파지는 않았다. 그냥 읽을 수 있을 정도, 그리고 레퍼런스를 보면 프로그래밍을 할 수 있을 정도.. LUA, Ruby, Perl.. 수 많은 언어다 그런 정도였다. Java도 그 중 하나였다. 

그러다 1-2년전부터 필요에 의해서 Java를 주로 쓰게 됐다. 내 Domain에서 Java를 쓰게 됐기 때문이랄까.. 아직까지는 프로젝트에서 코딩을 많이 하기보다는 모델링을 많이 하기 때문에 Java를 정말 깊숙이 쓰지는 않고 있지만, 두 가지는 뼛속 깊이 다른 언어라는 생각을 한다. 

물론 문법이나 시맨틱스 같은 것만을 본다면 거의 비슷한 언어겠지만. 이 동네를 구성하는 사람들의 분위기를 보면 완전히 다르다는 생각을 하게 된다.

C++는 다른 사람이 뭐하는지 모르겠지만, 산 속에서 도를 닦고 있는 구루 같은 느낌이랄까...
Java는 왁자지껄 시장 속에서 토론하고 있는 그리스의 철학자 같은 느낌이 든다...

왜일까나? 이런 생각이 드는 게..  

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,
Exception의 개념이 많이 쓰이기 시작했을 때, 항상 혼동되는 것 중 하나는 Return 값으로 Null을 주는 것과 Exception을 주는 것 중 어떤 걸 해야 할까였다. 

많은 시행 착오 끝에 결국 정착한 건..

1. 명백한 오류는 Exception
2. 그 외
   - Exception : 변수로 항상 값을 리턴해야 할 때. 즉 모든 값이 항상 Valid해야 할 때  
   - Null : null이 에러 값이 아니고, 정말 값일 때. 즉, 값이 존재할 수도 있고, 존재하지 않을 수도 있을 때

아마도 아직도 혼동하고 있는 중이지만, ㅎㅎ..  

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

Jetty

Programming 2011. 11. 12. 00:45
OSGi Framework 에서 돌려볼 웹서버로 일단 Jetty를 선택했다. 선택했다기보다는 이미 선택되어 있었다는 표현이 어울리겠다만, 뭐 어쨌든.. 

 

Jetty 아키텍처는 위와 같다.

  • Server : 이건 메인 관리 클래스다. 이런 저런 잡다한 일을 여기서 해준다. 
  • Connector :  외부 HTTP Connection을 실제 처리하는 통신 처리기다. 
  • Handler : HTTP Request에 대한 처리를 하여 Response를 생성 
  • ThreadPool : 외부에는 보이지 않지만, 어쨌든 쓰레드  

뭐 간단하게 이야기하자면, Server로 시작을 한 다음, Connector로 HTTP Connection을 받아 들인 후, Thread Pool로 처리할  Thread를 생성한 후, Handler가 Connector의 Request에 대한 처리를 해서 Response를 생성한다는 거다.
 

Jetty로 실제 프로그래밍을 할 때는 Handler에 대해 어떻게 잘 기술하냐가 관건이 될 것 같다. 현재는 Class Name을 기반으로 하여, 그 룰을 동적으로 등록하는 방법을 착안해야만 할 것 같다. 아마도 WebPlugIn 디렉토리 같은 걸 두고 거기에 뭔가 변동이 생기면 Context를 추가하는 방법을 생각해봐 야 할 듯 하다. 

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

= Overview =

Logging 체계는 현재 프로젝트의 초기 개발 단계에서 검토가 되었어야 하나, 검토가 이루어지지 않은 채로 와서 지금까지 계속되어 온 상태이다. 어쨌든 프로젝트를 이어 받은 내 입장에서는 로깅 체계를 만드는게 필요했다.
일단 로깅은 내 생각으로는 크게 다음의 세 가지로 나누어진다.
 # 디버깅을 위한 Console Logging
 # Major한 Event를 위한 System Level Logging
 # 사용자에게 보여주기 위한 User Level Logging

그러나 현재 프로젝트에서는 Console Logging과 User Level Logging 만이 존재하며, 그나마도 두 가지는 분리되어 있는 상태이다. System Level Logging이 없는 상태라, 뭐가 잘못돼어가는지 알 수 없는 상태다. 

어쨌든 세 가지는 통합되어야 가장 효율적이며 이에 따라서 일단 Console Logging과 System Level Logging에 대한 처리를 하기 위한 로깅 라이브러리를 검토하였다. User Level Logging은 현재 DB로 되어 있는 상태라 통합하지 않고, Console과 System Level Logging이 완료되면 이전할 생각이다.

 * [wiki:S3_Logging_Survey 로깅 라이브러리에 대한 검토 및 예제]

향후에는 세 가지가 모두 같은 체계를 통해서 Logging이 이루어질 것이다.

= Logging 체계 =

앞에서 말한 세 가지 형태의 분류에 따르면, Console Logging은 스크린 또는 파일로 기록이 되어야 하며, System Level Logging은 파일 또는 Syslog가 적당하다.

Media Server에서는 기본적으로 SysLog가 System Level Logging에 쓰이며, Screen이 User Level Logging에 쓰일 것이다. 다만 Release되는 실행 파일의 경우에는 System Level Logging만 활성화될 것이다.

== Logging 체계 ==

 * Prioirity : CRIT 이상만 Syslog를 통해서 출력
 * Facility : Syslog를 통해서 출력할 경우 LOCAL2를 통해서 출력

=== Log4cpp Priority ===

Log4cpp의 Loggin 우선 순위는 아래와 같이 정해져 있다. 이 중 ALERT 이상만 SysLog로 보내어지게 설정될 것이다. 그리고 그 외는 일단
{{{
typedef enum {EMERG  = 0,
    FATAL  = 0,
    ALERT  = 100,
    CRIT   = 200,
    ERROR  = 300,
    WARN   = 400,
    NOTICE = 500,
    INFO   = 600,
    DEBUG  = 700,
    NOTSET = 800
} PriorityLevel;
}}} 

== Log4CPP Configuration File ==

앞의 원칙에 따라서 미디어 서버에서 사용될 log.cfg 라는 파일의 샘플이다. Release 할 때 사용될 파일은 모든 Logging이 꺼져 있고, 아주 높은 Priority만 SysLog로 보내지는 예가 될 것이다.

디버깅이 필요할 시에는 주석 처리된 부분을 풀면 File이나 Console로 보내어지고, rootCategory의 Priority를 수정하면 될 것이다.

실제 지원되는 문법에 대해서는 PropertyConfigurator를 직접 보는 수 밖에 없다.

{{{
log4j.rootCategory=CRIT, basic_syslogger
#log4j.rootCategory=INFO, basic_syslogger, fileAppender,console_a

log4j.appender.basic_syslogger=org.apache.log4j.LocalSyslogAppender
log4j.appender.basic_syslogger.threshold=CRIT
log4j.appender.basic_syslogger.facility=LOCAL2
log4j.appender.basic_syslogger.layout=org.apache.log4j.BasicLayout

#log4j.appender.fileAppender=org.apache.log4j.FileAppender
#log4j.appender.fileAppender.fileName=/tmp/A1.log
#log4j.appender.fileAppender.layout=org.apache.log4j.BasicLayout

#log4j.appender.basic_syslogger=org.apache.log4j.LocalSyslogAppender
#log4j.appender.basic_syslogger.facility=LOCAL2
#log4j.appender.basic_syslogger.layout=org.apache.log4j.BasicLayout

#log4j.appender.console_a=org.apache.log4j.ConsoleAppender
#log4j.appender.console_a.layout=org.apache.log4j.BasicLayout

}}}


= Implementation =

== 라이브러리 설치 ==

개발 서버에 일단 필요한 라이브러리를 모두 설치하였다. 크로스 컴파일 환경이라면 별도의 설치 위치가 있어야 하나, 두 개의 환경이 동일하므로 그냥 설치하였다.
미디어 서버 상에는 /mnt/dom/work/lib 에 항상 동적 라이브러리가 설치되어 있는 것으로 가정한다.

== configuration file 생성 ==

실행 파일과 같은 디렉토리에 위의 예제와 같은 파일을 생성해준다.

== logging 체계의 초기화 ==

Exception을 Catch하는 부분에서 필요한 초기화를 더 해주어야 한다. 예를 들면, 자기가 선호하는 기본 Appender와 Layout은 제공해주어야 할 것이다.

{{{
int init_log_system(void)
{
    /// Step 1 : If the configuration is exist, use that.
    ///          Else, set root priority to Crit

    try {
        log4cpp::PropertyConfigurator::configure("log.cfg");
    }
    catch (log4cpp::ConfigureFailure e) {
        log4cpp::Category::setRootPriority(log4cpp::Priority::CRIT);
    }

    return 0 ;
}

}}}

== 실제 Logging ==

로깅할 Object Instance를 얻기 위해서는 log4cpp::Category::getInstance("Camera")를 사용한다. 이 함수는 Reference를 돌려주며, 이름이 동일할 경우 다시 Object를 생성하지 않고, 그 Instance를 돌려준다.

클래스 멤버로는 다음과 같이 사용하였다.
{{{
Constructor
{
    log4cpp::Category & m_logger ;
}

Constructor::Constructor(int x) : m_logger( log4cpp::Category::getInstance("Camera") )
{
    m_logger.notice("Con is created");
}
}}}
클래스 멤버가 아닌 즉각적인 사용이 필요할 경우는
{{{
log4cpp::Category &logger = log4cpp::Category::getInstance("Camera") ;
logger.notice("Instant notice");
}}}

= Reference =

 * log4cpp
  * [http://log4cpp.sourceforge.net/ log4cpp Homepage]
  * [http://joysofprogramming.com/log4cpp-tutorial/ Tutorial]


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,
애플 무선 키보드를 하나 샀다. 뭐 맥 용으로 사용하려고 하는 건 아니고 윈도우 용으로 사용하기 위해서다. 어쨌든 붙이기 위해서 애러 사항 많군. 일단, 블루투스로 붙이는 건 아무 문제가 안되는데, 매핑이 문제다.

1. 블루 투스로 Paring하기

인터넷에서는 뭐가 잘 안 붙네 하는 것 같은 이야기가 많은데. 아마도 예전 블루투스 스택 문제인 걸로 보인다.
요즘 나오는 건 왠만하면 되는 걸로 보인다.
그래도 혹시 몰라 내가 사용한 건, Belkin의 미니 블루투스 동글이다. 거기 딸려 나온 블루투스 스택으로는 아주 잘 붙는다. 다만 다른 블루투스 장치와 연결되어 있을 때는 잘 안 붙는 것 같다. 물론 당연한 이야기겠지만.

2. 키 매핑하기

문제가 되는 건 키보드를 붙이는 게 아니라 키보드를 매핑하는 것 같다. 키보드 매핑 방법은 여러 가지가 존재한다.

A. BookCamp 의 애플 키보드 드라이버 사용
    - 이걸 사용해야 할 것 같은데, 왜인지 모르나 올라가질 않는다.
    - 블루투스 키보드로 올라가야 할 것 같은데, 올리면 비활성화된다.
    - 인터넷에 또는 AppleKeyboardInstaller.exe 같은 게 이런 부류다. 

B. KeyMapping 프로그램 활용
    - AutoKey, KeyTweak 같은 프로그램을 활용한다.
    - 가장 범용적이긴 하지만, 프로그래밍 하긴 귀찮다는 결론이다.

C. KeyMapping 프로그램을 애플 키보드에 커스터마이징한 프로그램
    - uawks, AppleWirelessKeyboardHelper 같은 프로그램
    - B의 장점에다, 귀찮니즘을 해결해주는 적절한 해결책일 듯
   
결국 C에 안착했다. 언젠가 A로 해결되길 바라면서.. 킬킬


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

libevent

Programming 2010. 8. 29. 03:38

현재 프로젝트는 여러 장비에 대한 정보를 저장하는 대규모 서버다. 대량이라고 해봐야 최소 5000TPS, 목표는 50000TPS 정도이긴 하다. 어쨌든 대량의 통신을 처리하기 위해서는 대량의 connection을 처리하는 방법이 필요하며, 이런 라이브러리에 대한 탐색을 해본 결과, 직접적으로 epoll 같은 걸 사용하거나, libevent와 같은 라이브러리를 사용하는 방법이 존재하였다.
다른 방법을 사용할 수도 있으나 어쨌든 앞으로도 많이 사용해야 할 일이 있어서 안정적인 라이브러리를 선택하기로 하였으며, 이에 적당한 것이 libevent로 보였다. 근거가 있어야 하지만, 어쨌든 전문가의 추천이 있었으므로 일단 libevent를 사용하기로 하였다.


사용법
  
라이브러리를 초기화하기 위해서는 event_base를 생성하며 해당 event_base에 event를 할당하는 방식으로 이루어진다.
구체적인 것은 소스 코드를 보아야 하겠지만, API 만을 봤을 때 event_base는 event_loop를 위한 추상화로 보이고, event는 실제 이벤트를 의미하는 것으로 보인다. 즉, eventbase를 생성한 후에 event loop를 돌린 후에, 자신이 감시하고 싶은 event를 생성한 후, 이를 다시 eventbase에 할당하면 된다.

1. eventbase 생성 : 기초적인 라이브러리의 초기화
2. event loop 실행 : 계속적인 감시와 event에 따른 callback을 실행하기 위해 지속적으로 event loop를 실행한다.
3. event 초기화 : 감시 하기 위한 event의 초기화
4. event 추가 : eventbase에 감시할 event를 추가

1과 2 사이에 최초에 감시할 event를 추가하기 위해 한 번의 3,4가 추가되기도 한다.


eventbase의 생성하는 방법
 - event_init
 - event_base_new

두 가지는 기본적으로 동일하지만, event_init는 global한 default event_base를 만들어내지만, event_base_new는 global event_base가 존재하지 않는다. event_init는 의식하지 못 한채로 Global event base를 교체해버릴 위험성이 존재하므로 앞으로 없어질 함수이다. event_init는 obsolete지만, 현재도 지원되고 있으며 이전에는 이것만이 존재하였으므로 많이들 현재도 사용하는 것으로 보인다. 장점이 있다면 event를 생성하면 자동으로 attach될 event_base가 존재한다는 게 장점이라면 장점이랄까?

event_base_new를 사용한다면, 기본적으로 event는 어떤 eventbase에도 할당되지 않으므로, 생성 후에 항상 event를 eventbase에 할당해야 한다. event_set이던가로 할당한다.

event를 생성/할당


Reference
  - libevent Book( http://www.wangafu.net/~nickm/libevent-book/TOC.html )
  - libevent homepage ( http://www.monkey.org/~provos/libevent/ )


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,