2019년 8월 5일 월요일

WPF 쓰레딩 모델, Invoke, BeginInvoke

일반적으로 WPF 응용 프로그램은 렌더링 처리와 UI 관리를 위한 두개의 쓰레드로 시작한다. UI 쓰레드는 작업 항목을 Dispatcher라는 오브젝트 내부에 대기시킨다. Dispatcher는 우선 순위에 따라 작업 항목을 선택하고 각각을 실행한다. 모든 UI 쓰레드에는 하나 이상의 Dispatcher가 있어야 하며 각 Dispatcher는 정확히 하나의 쓰레드에 의해서만 동작해야 한다.

응답성이 높은 애플리케이션을 구축하는 방법은 가능한 작업 항목의 실행 시간을 적게 유지하여 Dispatcher의 처리량(throughput)을 극대화 하는 것이다. 따라서 일반적으로 큰 작업은 별도의 쓰레드에서 처리하게 된다. 그리고 큰 작업이 완료되면 결과를 UI 쓰레드에 보고하여 표시하는 식이다.

WPF에서 대부분의 클래스는 DispatcherObject에서 파생된다. 생성시 DispatcherObject는 현재 실행 중인 쓰레드에 연결된 Dispatcher에 대한 참조를 저장한다. 프로그램을 실행하는 동안 DispatcherObject는 VerifyAccess 메서드를 호출할 수 있다. VerifyAccess 메서드는 현재 쓰레드와 연관된 Dispatcher와 생성시에 저장된 Dispatcher 참조 간의 비교 검사를 수행한다. 일치하지 않으면 VerifyAccess는 예외를 throw한다. VerifyAccess는 DispatcherObject에 속한 모든 메서드의 시작 부분에서 호출되도록 고안되었다.

...
하나의 쓰레드만 UI를 변경할 수 있다고 하였다. 그렇다면 백그라운드 쓰레드와 사용자 간 상호작용 하는 방법은 무엇인가? WPF에서 백그라운드 쓰레드는 UI 쓰레드에 작업을 수행하도록 요청할 수 있게 구성되어 있다. 백그라운드 쓰레드는 작업 항목을 UI 쓰레드의 Dispatcher에 등록할 수 있다. Dispatcher 클래스는 백그라운드 쓰레드가 작업 항목을 등록할 수 있게 'Invoke'와 'BeginInvoke' 메서드를 제공한다. Invoke는 동기 호출 방식이고 BeginInvoke는 비동기 호출 방식이다. 두 메서드 모두 실행을 위해 대리자를 스케줄에 등록시킨다.

아래와 같은 코드에서, Dispatcher는 startStopButton 컨트롤이 idle일 때만 delegate를 실행하게 된다. 우선순위가 SystemIdle로 지정되었으니 아래의 코드는 CheckNextNumber 호출보다 UI의 응답이 중요하다는 의미를 갖게 된다. Dispatcher는 Dispatcher 큐에 등록된 작업 항목을 우선 순위에 따라 정렬하여 순차적으로 처리한다. 제공되는 우선 순위 레벨은 10가지 이다. 이러한 우선 순위는 DispatcherPriority 열거 형에서 유지되고 관리된다.


public delegate void NextPrimeDelegate();

public void CheckNextNumber()
{
...
    if (continueCalculating)
    {
        startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, new NextPrimeDelegate(this.CheckNextNumber));
    }
}

자세한 내용은 아래 링크를.
https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/threading-model

2019년 8월 1일 목요일

싸이킷런의 주요 설계 원칙 - estimator, transformer, predictor

핸즈온 머신러닝을 읽고 있는데 이 책 정말 좋다. 싸이킷런의 주요 설계 원칙이라고 101쪽에 적혀있는 내용을 옮겨본다.


싸이킷런의 주요 설계 원칙


1. 일관성 : 모든 객체가 일관되고 단순한 인터페이스를 공유한다.

  • 추정기(estimator) : 추정은 fit() 메서드에 의해 수행되고 하나의 데이터셋만 파라미터로 전달받는다. fit()에서 쓰이는 하이퍼파라미터는 생성자의 매개변수로 전달받는다.
  • 변환기(transformer) : transform() 메서드가 하나의 데이터셋을 전달받아 변환 작업을 한다. 그리고 fit()과 transform()을 연달아 호출하는 것과 동일한 결과를 갖는 fit_transform() 메서드를 가지고 있다.
  • 예측기(predictor) : predict()는 새로운 데이터셋을 전달받아 예측값을 반환한다. 그리고 테스트 세트를 사용해 예측 품질을 측정하는 score() 메서드를 갖는다.


2. 검사 기능 : 하이퍼 파라미터는 인스턴스 변수로 직접 접근할 수 있다. 학습된 모델 파라미터도 접미사로 '_'를 붙여서 공개 인스턴스 변수로 제공된다.

3. 조합성 : 기존 구성요소를 최대한 재사용한다. 예를 들어 여러 개의 변환기를 연결한 다음 마지막 추정기 하나를 배치한 Pipeline 추정기를 쉽게 만들 수 있다.

4. 합리적인 기본값 : 일단 동작하게끔 해준다. 대부분의 매개변수에 합리적인 기본값을 지정해두었다.

2019년 7월 29일 월요일

DI와 스프링

DI는 외부 인스턴스 혹은 특정 container가 두 object 간의 종속성을 (대신) 주입해주어 서로에 대한 종속성을 낮추게 해준다. DI를 사용함으로써 어떤 클래스가 특정 클래스에 의존성을 갖을 때 구체적인 구현에 대해 모르게 할 수 있다. (구현에 대해 갖는 종속성을 제거.) 이렇게 하면 모듈화에도 좋고, DI를 적용하지 않았을 때보다 독립적인 단위 테스트를 하기 쉬워진다. 독립적인 단위 테스트를 하기 쉬워진다는 말은 결합도(coupling)가 낮아지는 것을 의미한다. 알다시피 낮은 결합도(=모듈의 높은 응집도)는 좋은 소프트웨어 설계의 목표 지점 중 하나다.

DI가 구현에 대한 의존성을 제거하는 유일한 방법은 아니다. 이 작업을 수행하는 데 사용할 수 있는 또 다른 패턴으로 Service Locator가 있다. Service Locator 패턴을 따르는 경우엔 애플리케이션이 service locator에게 필요한 서비스를 메시지로 직접 요청하는 형태를 띄게 된다. 반면 DI의 경우엔 명시적인 요청 없이 서비스가 주입된다. 이런 형태를 일반적으로 'Inversion of Control' 이라 한다.

아래는 마틴 파울러가 15년전 작성한 글인데 정말 좋다.
https://martinfowler.com/articles/injection.html

DI 구현방식

DI엔 constructor injection, setter injection, field injection의 세가지 방식이 있다. 그리고 이 중에선 일반적으로 constructor injection 방식이 가장 권장된다. Constructor injection 방식을 쓰면 생성자만 보고도 의존 관계와 복잡성을 쉽게 알 수 있게 되기 때문에 작성하는 코드가 올바른 방향으로 가고 있는지 알 수 있게 된다. 그리고 final 키워드를 이용해 객체의 상태를 변하지 않게 만들 수 있다. 이는 setter, field injection 방식에서는 가질 수 없는 형태다.
일반적으로 객체를 생성할 땐 가능하면 하나의 방식을 갖는 것이 좋고, 의존 관계가 있다면 setter method 보다 매개 변수가 있는 생성자를 통해 드러내는 것이 좋다고 알려져 있다. (객체를 구성하는 방법이 여러개라면 Factory Methods를 쓰고..) 이와 비슷한 맥락에서 DI도 constructor injection 방식을 권장하는게 아닌가 한다.

반대로 setter injection의 경우엔 특정 의존 관계를 인스턴스의 생성 시점과 구분해서 지정하거나 또는 지정하지 않거나, 교체할 수 있는 형식을 가질 수 있기 때문에 setter injection 방식도 쓰임새가 있다.

[코드 예시: https://multifrontgarden.tistory.com/214]


DI와 스프링

잘 알려진 DI 구현체를 꼽으면 스프링을 예로 들 수 있다. 스프링에선 스프링 DI 컨테이너가 관리하는 객체를 빈(bean)이라고 표현한다. 여기서 빈이 특별한 개발 사양을 갖는 것은 아니고 빈은 단지 스프링 컨테이너에 의해 관리를 받는다는 의미로 쓰인다. 그리고 스프링 컨테이너는 이 빈들을 관리한다는 의미로 빈 팩토리(bean factory)라 불린다. 빈 팩토리는 단어 그대로(factory=공장) 빈을 생성(instantiating)하고, 설정(configuring)하고, 관계를 갖는 빈들을 묶어주는(assembling) 역할을 맡는다. 여기에 비즈니스 애플리케이션을 지원하는데 필요한 기능 몇가지가 추가되면 바로 그(?) ApplicationContext가 된다.

TCP 제어 플래그 (Control Flag)

TCP 통신에서 TCP 헤더의 컨트롤 플래그는 연결 상태를 나타내는데 쓰인다. 이 값에 대해 잘 알고 있으면 관련된 문제 발생시 꽤나 도움이 된다. 플래그에는 SYN, ACK, FIN, RST, PSH, URG 가 있다. 각 플래그는 1비트씩 소비한다.



  • Synchronization (SYN) : 두 호스트 간 연결을 맺기 위한 3-way handshake 프로세스의 첫 번째 단계에서 쓰인다.
  • Acknowledgement (ACK) : 상대방으로부터 패킷을 받았다는 것을 알려주는데 쓰인다. SYN 세그먼트 전송 이후(TCP 연결 시작 후) 모든 세그먼트에는 항상 이 비트가 1로 셋팅된다. 1로 셋팅되면 'Acknowledgement number' 필드에 유효한 값이 적혀있다는 의미를 갖는다.
  • Finish (FIN) : 커넥션 종료 요청으로 더 이상 보낼 데이터가 없을 시에 sender가 마지막 패킷으로 보내게 된다. 정상적인 종료에 해당하므로 데이터 유실은 없고, receiver가 꼭 연결을 끊어야 할 의무는 없다. 연결을 종료하고 싶다는 뜻을 상대에게 보내기 위한 플래그다.
  • Reset (RST) : TCP 연결에 문제가 있다고 판단될 때 연결을 종료하는데 쓰인다. 정상적인 연결과 종료를 할 수 없는 경우에도 사용된다. 비정상적인 중단으로 데이터 유실이 생길 수 있고, 양쪽 모두에 영향을 준다. RST는 반-개방 또는 연결 문제 등의 상황 처리를 위한 초기화용 제어 비트다.
  • PUSH (PSH) : 전송 계층은 일반적으로 네트워크의 효율을 위해 충분한 양의 데이터가 모인 다음에 데이터를 전달한다. 그러나 이러한 장치는 채팅과 같이 빠른 응답을 필요로 하는 경우엔 적합하지 않을 수 있다. PSH 플래그를 1로 설정하면 세그먼트는 버퍼링되지 않고 즉각 전송된다. 수신측도 PSH 플래그가 1로 설정된 세그먼트는 바로바로 애플리케이션 계층으로 전달한다. (in-sequence)
  • Urgent (URG) : 긴급성을 갖는 데이터로 간주되어 URG가 1로 세팅된 세그먼트는 전송 큐의 제일 앞으로 보내지게 된다. (out-of-sequence)
https://www.geeksforgeeks.org/computer-network-tcp-flags/

2019년 7월 28일 일요일

JMX란

JMX = Java Management Extensions.

JMX는 JDK 1.5부터 포함된 사양이라 한다. JMX는 실행 중인 애플리케이션의 상태를 모니터링 하고, 설정을 변경할 수 있게 해주는 API라고 이해하면 될 듯 하다. 이해하기 쉬운 단순한 규격을 가지고 있어서 어렵지 않게 런타임 상태의 애플리케이션을 관리할 수 있다.

외부에선 JMX Connectors로 MBean 서버에 접근하면 되지만 JConsole과 같은 미리 구현된 모니터링 도구가 있기 때문에 직접 모든 것을 만들 필요는 없다.

JMX를 통해 리소스 관리를 하려면 MBeans라는 Managed Beans를 생성해야 하고, 생성한 MBean을 MBean Server에 등록해야 한다. MBean 서버는 등록된 MBeans를 관리하는 agent 역할을 수행하게 된다. 당연히 MBean 서버는 구현한 애플리케이션 내부에서 띄운다.

MBean을 구현하는덴 룰이 있는데 interface와 구현체를 쌍으로 만들어야 하고 MBean interface의 이름은 'MBean'으로 끝나야 한다. 노출하는 변수를 모니터링만 할 것이라면 setter는 생략해도 된다.

다음은 예제 코드.
public interface MonitoringBean {
    long getStorageSize();
}

public class Monitoring implements MonitoringBean {
    @Override
    public long getStorageCount() {
        return ...;
    }
}

// boot up MBean server
Monitoring monitoring = new Monitoring();

// 팩토리 클래스로 부터 MBeanServer 인스턴스를 생성한다.
MBeanServer server = ManagementFactory.getPlatformMBeanServer();

// ObjectName은 domain, key의 구성을 갖는다.
// domain은 이름 충돌이 발생하지 않도록 자바 패키지 이름을 쓰는 것이 관례.
// key는 key=value 쌍을 콤마로 구분해 여러 개를 지정할 수 있다.
ObjectName jmxObjectName = new ObjectName("com.okminseok.example:type=basic,name=mymon");
server.registerMBean(monitoring, jmxObjectName);

! 소스 참고: https://github.com/eugenp/tutorials/blob/master/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmx/JMXTutorialMainlauncher.java

! 애플리케이션의 실행 파라미터.
-Dcom.sun.management.jmxremote.port = 8686 // 8686이 기본 포트.
-Dcom.sun.management.jmxremote.ssl = false // 설정 편의를 위한 ssl off
-Dcom.sun.management.jmxremote.authenticate = false // 설정 편의를 위한 인증 off
=> https://www.ibm.com/support/knowledgecenter/ko/SSHS8R_8.0.0/com.ibm.worklight.installconfig.doc/install_config/t_optional_config_app_server_tomcat.html


2019년 7월 25일 목요일

npm 기본 사용법

Node Package Manager, npm의 사용법을 간단히 정리해보았다.

자주 쓰게되는 명령들


# npm init: package.json 파일을 만들어 준다. package.json은 프로젝트가 참조하는 의존 라이브러리의 버전 정보를 기록해두는 파일이다. JavaScript 생태계에서 Java의 maven, gradle과 같은 역할을 하는 파일이다.
npm init

# npm install/uninstall: 패키지 설치와 삭제. -g 옵션을 붙이면 사용하는 PC 환경 전체에 영향을 준다. 붙이지 않으면 명령을 날린 하위 폴더에 적용된다.
npm install 모듈
npm uninstall 모듈

# 배포 시 참조하게 되는 라이브러리 정보를 package.json에 추가한다. (dependencies에 추가)
npm install 모듈 --save

# 개발 단계에서만 참조하는 라이브러리 정보를 package.json에 추가한다. (devdependencies에 추가)
npm install 모듈 --save-dev

# npm이 npm을 업데이트 해준다.
npm install -g npm

# npm ls: 설치된 패키지 정보를 확인할 수 있다. -depth=0 옵션을 주면 참조의 참조 패키지는 제외하고 표시해준다. -g 옵션은 이전과 동일한 의미.
npm ls
npm ls -g
npm ls -depth=0

# npm cache: clean 명령은 캐쉬 폴더를 비운다. verify는 캐쉬 폴더에서 필요하지 않은 정보를 날리거나 캐쉬 데이터가 올바른지 확인하는 명령이다.
npm cache clean [<path>]
npm cache verify

! package-lock.json


npm 특정 버전부터 package-lock.json이라는 파일이 추가로 생성된다. package-lock.json 파일은 pakcage.json 파일이 업데이트 되는 시점에 참조하는 모듈들의 자세한 의존성 트리 정보(참조의 참조, 참조의 참조의 참조~ 모듈의 버전 정보)를 기록해두기 위한 장치다. package.json 보다 더 정확한 정보를 담고 있기 때문에 특정 개발자와 협업하는 경우 미세한 버전 틀어짐 없이 정확히 같은 버전을 바라보며 개발을 할 수 있다.


! 버전 표기시 ^(캐럿)과 ~(틸드).


캐럿은 참조 모듈들이 메이저, 마이너, 패치의 SemVer 규약을 잘 따르고 있다고 가정하고 모듈의 버전을 하위 호환성이 보장되는 버전까지 올릴 수 있게 해준다. 반면에 틸드는 참조 모듈의 버전 업그레이드에 대해 비교적 보수적인 version range를 두고 접근한다. outsider님 블로그에 올라온 글을 읽으면 이해가 빠르다. (https://blog.outsider.ne.kr/1041)
~(틸드)
~0.0.1 : >=0.0.1 <0.1.0
~0.1.1 : >=0.1.1 <0.2.0
~0.1 : >=0.1.0 <0.2.0
~0 : >=0.0 <1.0
^(캐럿)
^1.0.2 : >=1.0.2 <2.0
^1.0 : >=1.0.0 <2.0
^1 : >=1.0.0 <2.0

2019년 7월 24일 수요일

Angular 히스토리

Angular History


상세

* 2010년: AngularJS의 탄생. Misko Hevery라는 사람이 Google에서 Side Project로 시작. 이후 몇몇의 동료 개발자가 참여하여 오픈 소스로 공개하였다. 하나의 코드 셋으로 웹과 모바일 환경에 바로 적용할 수 있었기 때문에 많은 호응을 받았다.

* 2014-2015년: 다시 쓰다. AngularJS가 호평을 받았지만 웹 환경은 변화했고, 더 나은 성능을 보여주는 경쟁 프레임워크가 등장하게 되었다. AngularJS는 벽에 부딪혔고, Google의 핵심 팀은 기존 AngularJS의 디자인에 얽매이지 않고 프레임워크를 다시 작성하게 되었다. 새로 만드는 Angular의 목표는 여전히 대규모, 크로스 플랫폼 애플리케이션을 구축할 때 생기는 어려운 문제점들을 해결하기 위한 것이다.
  • AngularJS vs Angular
  1. AngularJS는 기본적으로 양방향 데이터 바인딩(2-way data binding)을 채택하고 있는데 여기서 오는 성능 문제가 있었다. → Angular는 단방향 데이터 바인딩, 양방향 데이터 바인딩을 개발자가 선택할 수 있다.
  2. AngularJS의 컨트롤러, 스코프($scope) 개념은 컴포넌트와 Directive로 대체되었다.
  3. Angular는 TypeScript를 채택한다. RxJS 사용시 리액티브 프로그래밍을 지원한다.

* 2016년: 혼란스러운 시기. AngularJS 프로그램을 새로운 Angular로 이식하는 명확한 마이그레이션 전략은 없다. AngularJS 기반 프로젝트를 운영하는 개발자에겐 안좋은 소식이었다. → 우려와 다르게 AngularJS는 지금도 지원이 이루어지고 있다.

* 2017-2018년: 새로운 버전의 Angular가 자리를 잡은 시기. 기존의 AngularJS와 비교해서 더 나은 빌드 크기, 안정적인 API와 향상된 성능을 제공할 수 있게 되었다. React와 다르게 Angular는 대규모 응용 프로그램을 빌드할 때 필요한 모든 솔루션을 제공한다. React와 같은 라이브러리는 누락된 부분을 채우기 위해 다른 외부 프로젝트를 필요로 한다. Angular가 React보다 낫다는 말은 아니다.