'Functional World' 카테고리의 글 목록 :: iopeni - Think of C#

병원에서 사용하는 시스템 중 EMR이라는 시스템의 중요기능 중 하나는 동적 서식 생성이라는 주제 이다.

 

몇년전 EMR이 뭔지도 모를 때 모 대학 병원에서 엉겁결에 동적 서식 생성기라는 프로그램을 만들었고.. 지금은 확실하게 서식생성기가 가져야 하는 기능들에 대해서 남들보다 잘 알고 있다고 자신 한다.

 

전에 근무하던 회사도 의료정보쪽 일을 하고 있는 회사 였고, 동적 서식 생성기라는 프로그램이 몇종류 있었다. 물론 지금도 팔고 있는 제품이고 나름 경쟁력 있는 제품들이라 생각 한다.

 

그러나 나의 입장에서 보면 뭔가 좀 어색하고 대단히 이상했다. 왜냐하면 서식 생성시 서식의 모양을 지정하고 모양의 위치를 지정하고 하는 것들을 전부 이미지로 처리하고 있었기 때문이다.

 

정확한건 아니지만 타회사(후지쯔코리아, 큐브플랙스)의 제품들을 봐도 서식을 생성하기 위해 처리 하고 있는 생성기는 이미지 처리 기반인 것으로 추측된다.

 

왜 일까? 왜 이미지로 했을까? 난 잘 모르겠다. 일부러 이미지로 처리한다는 것 자체만 보더라도 공수는 훨씬 많이 들어갈것 처럼 보인다.

 

생각을 살짝만 바꿔서 생각 하면 그냥 패널이나. 이미지박스에 동적으로 컨트롤을 들어 올리고 그걸 가지고 선택 하면 개발자가 Hit Testing 은 신경 쓰지 않아도 되며, 드래그는 Left와 Top 값만을 변경 하는 것으로 원하는 기능을 구현 할 수 있다.

 

물론 Copy & Paste 의 구현도 이미지 기반 처리에 비해 쉽게 할 수 있다.

 

국가연구사업으로 완성된 CCM(임상 컨텐츠 모델) 이라는 것을 보면 서식 생성시 중요하게 생각 하여야 하는 것은 어트리뷰트와 엔티티, 컴포넌트이다.

 

그래서 생각 하게 된거다... 만약 말이다. 우리가 동적 서식 생성이라는 문제를 가지고 Visual Studio의 폼 디자이너 처럼 할 수 있다면? 그럼 어떻게 될까?

 

폼 디자이너에는 어트리뷰트, 엔티티, 컴포넌트 따위 그냥 기본 개념일 뿐이다. 얼마든지 확장할 수 있고 사용자가 화면을 그리기도 이미지 에디터에 비해서 훨씬 쉽다.

 

더불어~~~~~~~~~ CodeDom으로 코드 까지 생성해 준다.......  뭐 자동생성하는 코드가 맘에 들지 않는 다면 나만의 규약으로 코드를 생성해도 된다.

 

예상컨데... 이거 서식 생성기 끝판왕을 만들 수 있겠구나??? 라는 생각을 함 해봤다.

 

그런데??????? 폼 디자이너는 어떻게 만들었을까?... 이라는 호기심이 문득 들었고 저를 아는 분들은?? 아시겠지만..... 한번 호기심이 생기면 끝까지 물고 늘어져서 ㅎㅎㅎ 알아내고야 마는 묘한 습성이 있다.

 

이번에도 그랬다.... 폼디자이너 어떻게 만들었는지 알고 싶었다. 몇일간의 구글링과 .... MSDN 검색질을 통해서 알아내고야 말았다......

 

일단 샘플로 만들어 놓은 화면이다. ㅎㅎㅎㅎ

 

 

드래그... 및 Anchor 기능이 기본으로 동작 된다. 이 코드..... 코드량 얼마나 많을까???? 많지 않다.

 

깜놀 했다.. ㅎㅎㅎ 이걸 알고 나서 어찌나 자랑질을 하고 싶었는지 아마 여러분은 모를 꺼다..

 

역시 고마운건 Microsoft 였다...

 

System.Design 어셈블리를 참조 하고 System.ComponentModel.Design 네임 스페이스에 존재 하는 DesignSurface가 답이었다.

 

대충 만든 C# 코드를 소개 한다.

 

내가 또 서식생성기를 만들게 될 일은 없겠지만.. 만약 또 만들게 된다면... 이번엔 이렇게 할 것이다. 만약 WPF디자이너에 CodeDom을 활용하게 될 지도 모르겠지만 말이다.

 

이렇게 만들어 놓은 서식으로 사용자가 리포트를 작성하게 하고 .. 이후 출력을 하게 될 경우에 대한 좋은 방법은 OpenXML SDK 인듯 하다.

 

아마 docx, xlsx, pptx 와 같은 문서들의 확장자를 Zip으로 바꾸고 압축을 풀어 보면 압축이 풀리는 신기한 경험을 하게 될지도 모른다.

 

미국 정부는 Microsoft에게 오피스 없이도 오피스 문서를 읽을 수 있도록 해달라는 요청을 하였고, 마이크로소프트는 그 의견에 수렴하여 OpenXML 이라는 문서 포맷을 표준으로 하였다.

 

ECMA와 ISO공히 표준 문서가 등재 되어 있고 이곳에 백업한다. EMR을 새로 만들거나 서식 생성기를 새로 만드는 분들에겐 아마도 도움이 될 것이다.

 

http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=51463

http://www.ecma-international.org/publications/standards/Ecma-376.htm

 

그런데 말이다... 지금 나에게 중요한건 이런 블로그질이 아니라... 취업이다... ㅎㅎ

에혀.. 지방에는 튼실한 IT 기업이 없다.. 있어도 나 같은 허접한 개발자는 취급하고 싶지 않은 걸까???? 백수 생활이 점점 길어진다... 아혀~~

 

 

 

 

 

'Functional World > .Net Framework' 카테고리의 다른 글

Microsoft .Net Framework 4.5? 에 추가된 것들...  (0) 2013.11.01
Ms Chart Control Sample  (0) 2013.08.26
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,


정말 어려운 주제 입니다.. 디펜던시 인젝션이라는 주제는... 이걸 어떻게 설명 해야 할지 참 난감 합니다.


저의 블로그에도 이전에 이 주제를 가지고 한번 포스팅을 한적이 있습니다. 


http://iopeni.tistory.com/entry/Dependency-Injection-Container


Dependency Injection in Action 이라는 책을 보면 DI를 가정집에 전기 콘센트로 비유 하고 있습니다. 뻔한 내용 이겠죠? 헤어드라이든 냉장고든 TV든 콘센트에 꽂혀야 동작을 하니 콘센트는 하나의 가전 제품에 생명력을 불어넣어 주는 ~~ 신비한 곳입니다. 만약 집에 있는 가전 제품들이 모두 특수한 가전제품이라 각기 다른 전기 어댑터를 지니게 된다면, 아마 콘센트의 양은 정말 어마 무시하게 많아 지겠지만, 다행 스럽게 대한민국의 콘센트는 같은 모양을 지니고 있습니다.


이런 일을 컴퓨터 언어적인 측면에서 설명을 하자면, 추상 클래스와 인터페이스 같은 것들로 설명을 할 수 있습니다. 

즉 다형성이라는 주제 인걸로 보입니다. 추상이나 인터페이스의 목적이 좀 모호합니다만, 알고보면 별거 아닙니다.

 

사실 정확하게 이야기 하자면 추상은 인터페이스와 비교하면 목적 자체가 틀립니다. 추상은 콘센트 라기 보다 가전 제품의 종류 입니다. 세상에 에어컨이 있는데 이 에어컨의 종류가 벽면 에어컨도 있고 스탠드 에어컨도 있고 지붕에 달려 있는 에어컨도 있고... 많은 형태의 에어컨이 존재 하지만 모든 에어컨은 공통인 것들 즉 에어컨은 바람이 나오고, 실외기가 있어야 하고, 프레온 가스로 찬 바람을 만들고, 등등 기본적인 성격을 모든 에어컨이 지니게 되는데 이것이 바로 추상 클래스 입니다. 프레온 가스가 있는 이 프레온 가스는 A 회사 제품도 있고, B회사 제품도 있고 다 틀리니 말이죠.


인터페이스는 다릅니다. 


인터페이스는 항구, 공항, 콘센트, 주유소, 세차장, 책장 등과 같습니다. 항구에는 수많은 종류의 배들이 정박 할 수 있는 곳이며, 공항은 수많은 종류의 비행기가 뜨고 내리는 곳입니다. 하나의 객체와 또 다른 객체를 이어주는 연결 고리 라고 생각 하면 됩니다.


물론 이런걸 관점주의적 프로그래밍 이라는 주제로 바라 보면 또 약간의 차이점이 발생 하기도 합니다.


각설하고 말이죠 더 깊에 설명 하려고 하면 골 때리고, 닭이 먼저 인지 달걀이 먼저인지.... 니가 나 인지.. 내가 너 인지 모르는 사태가 발생 할 수 있으므로 우리는 여기서 딱 한가지만 정하고 가야 할 듯 합니다.


오늘은 인터페이스 입니다. 인터페이스 하나만 알면 됩니다.


이걸로 디펜던시 인젝션 이라는 주제를 설명 하고자 합니다. OOP적 관점에서의 인터페이스 라고 하면 이 아무것도 아닌 개념이 정말 난해하게 느껴져 절망을 부르짓고 있는 분이 계실지도 모르겠습니다.


사실 디펜던시 인젝션의 모든 테크닉은 이 인터페이스 에서 비롯 됩니다.


하나의 클래스에서 맴버가 등록 되는데 이 멤버의 타입이 인터페이스 인겁니다. 즉 맴버는 인터페이스를 상속 받은 다양한 클래스들이 이 멤버에 대입 될 수 있으며, 정보를 취득 가공함에 있어 형 변환 연산이 필요하지 않은 것입니다.


마이크로 소프트는 유니티를 다음과 같이 설명 하고 있습니다. https://msdn.microsoft.com/library/ff647202.aspx


오늘 우리는 이 유니티 컨트롤을 사용하는 기초적 용법을 습득하고 다음 포스트에서 다시 프리즘으로 넘어가서 또 다른 주제를 풀어 볼까 합니다.


콘솔로 또 헬로우 월드를 표시 하는 걸로 이 샘플을 예시 합니다. 일단 콘솔 어플리케이션 프로젝트를 하나 생성 합니다.


그리고 NuGet 패키지 관리자를 실행 시키고 Unity를 찾습니다.


화면으로 보여 드리죵~



요걸 설치 하시면 됩니다. 


코드 봐야 겠죠?



재미있는건 우리가 알고 있는 간단한 IoC 컨테이너는 타입을 넣어 주고 new로 생성된 인스턴스를 넣어서 사용 하고자 할때 타입으로 찾아 꺼내 쓰는 모습 이었는데 이건 좀 다르게 보입니다.


이녀석은 우리가 new를 사용하지 않아도 자신이 new를 해서 반환해 줍니다. 이렇게 생각 하면 좀 이상하게 보이는게 있는데 new를 하는게 언제하냐는 겁니다. 


그래서 아주 간단한 방법으로 테스트 해 보았습니다.


Container.ResisterType 항목을 주석 처리 하고 Resolve에 직접 클래스를 적어주니 객체를 생성해서 반환 합니다.

다음과 같이요..


var test = container.Resolve<Hello>();


와 같이요... 


혹시 기억 하실지 모르겠습니다. Prism 1 에서 MainWindow 를 Resolve 하던 코드를... 거기에선 ResisterType을 하지 않았습니다.


그럼 ResisterType은 무엇일까요? 이건 Dictionary<T, T> 를 사용해 보신 분이라면 아실껍니다. 키를 가지고 실 데이터를 찾게 되는데 그 실 데이터의 타입을 정해 주는 겁니다. Dictionary와 같습니다. 단~ 틀린게 있다면 딕셔너리는 Object 를 등록하게 되지만 여기서는 Type을 등록 하네요~ 즉 정확한 의미는 이겁니다. 만약 인터페이스 타입으로 누군가 Resolve를 요청 하면 이 클래스를 생성해서 반환 하도록 한다.. 라는 사전 지정 입니다.


한번 생각해 보도록 하죠. 만약 유니티 컨테이너를 특정 유틸리티 클래스의 static member로 놓고 거기에 오만 가지 사용하고 있는 클래스를 등록 합니다. 예를 들어 이런 것들이요.. Logger나 File처리 클래스, 통신 클래스 같은 것들이요. 필요할 때 마다 불러서 쓸 수 있겠네요. 프로젝트 전역에서. 참 유용 하겠죠? 


한가지 더 주의 사항이 있습니다. container.RegisterType<IISayHelloWold, Hello2>(); 여기서 interface가 Key 입니다. 


같은 Key를 두번 등록 하면 어떻게 될까요? 바꿔 치기 됩니다. 주의 하시기 바랍니다.


오늘도 수고 하세요~   

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

Prism 1 포스팅에 이어 두번째 포스팅이다. 


우리는 프로젝트 수행 중 몇가지 요구 사항으로 인하여 프로그램 런타임 시에 동적으로 어셈블리를 로딩 하여야 할 경우가 있다. 이럴 때 리플렉션을 이용하여 할 수 있다는 것은 누구나 다 알고 있는 내용일 것이다. 물론 리플렉션은 프로젝트의 타입 안정성을 해친다는 등 단점들이 존재 한다. 리플렉션을 이 포스트에서 논하는 것은 범위 밖 이므로 리플렉션은 MSDN을 참고 하기 바란다.


리플렉션을 자세히 보시려면 이곳을 클릭 -> https://msdn.microsoft.com/ko-kr/library/ms173183.aspx 


원격지 모듈 로딩은 프리즘을 이용하여 원격지에 있는 구성 요소를 로딩 할 수 있는 방법이다.




포스트 1에서 작성 하였던 ModuleA 모듈을 일단 분리 하였다. 이 후 시작 프로젝트에 ModuleCatalog.xaml 을 생성 속성을 다음과 같이 수정 한다. 



코드를 확인 하자.


중요한 것은 xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;

assembly=Microsoft.Practices.Prism.Composition"

Modularity 클래스는 Ref 참조 와 ModuleType 이 필수 항목이다. Ref는 설명하지 않아도 될 듯 하다.

단지 ModuleType 만이 약간의 설명이 필요해 보인다. 


ModuleType의 첫번째 요소는 Type을 표시 한다. 두번째 요소는 Namespace를 의미 하며, 세번째 요소는 Version을 의미 한다. 프로젝트의 AssemblyInfo 클래스를 참조 하자.


이제 이 포스트의 마지막 요소이다. ModulesCatalog.xaml 을 만들었으니 Bootstrapper가 이 xaml 파일을 파싱 로드 해야 할 것이다. 





이상이다. 정말 쉽게 할 수 있다는 것을 알 수 있다. 리플렉션에 비하면 말이다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,



너무 욕심이 과한 건지 모르겠습니다. 사실 엔터프라이즈 라이브러리의 한 파트인 프리즘을 공부 하기엔 제 실력이 너무 허접할 지도 모릅니다. 


하지만... 안할 수 없기에 시작 했습니다. 아무래도 이젠 WinForm 은 버려야 할 듯 합니다. 다음 달이면 Windows 10이 출시 됩니다. 사실 너무 늦게 공부를 시작 하는 듯 합니다. 


국내에선 사실 제대로 된 번역서 한권 없고, 제대로 된 예제 코드 하나 없고, 공부 하기 쉬운 것도 아니고 암튼 그렇습니다. 몇몇 회사에서 프리즘을 사용하여 솔루션을 개발 하고 있다고는 하나 성숙도는 사실 낮습니다. 제가 이런걸 평가 할 수 있는 실력도 아니지만 사실 그렇습니다. 그 만큼 프리즘을 잘 알고 잘 하는 개발자는 드뭅니다. 선구자는 아니어도 세상에 처지지는 말자 라는 것이 저의 작은 소망 입니다만 늘 세상을 따라잡기는 벅차 군요~!!!


일단 프리즘을 다운 로드 받을 수 있는 사이트를 남깁니다. 전 그냥 Nuget으로 다운 받아 버리는 주의라.. 필요 없긴 합니다만... 그래도 프리즘 개발자들과 소통을 할 수는 있습니다.


http://compositewpf.codeplex.com/


프리즘을 설치 하고 제일 먼저 해야 하는 일은 Bootstrapper를 작성해야 합니다. 이 부트스트래퍼가 뭘 하고자 하는 놈이지 알아야 할 겁니다. 


자세한 내용은 MSDN 을 참고 하시길 바랍니다.

https://msdn.microsoft.com/en-us/library/ff921139.aspx


MSDN 가라사대~


"부트 스트랩은 프리즘 라이브러리를 사용하여 구축 된 응용 프로그램의 초기화를 담당하는 클래스입니다. 부트 스트랩을 사용하여 프리즘 라이브러리 구성 요소가 응용 프로그램에까지 연결되는 방법을보다 상세하게 제어 할 수 있습니다." 라고 정의 하고 있습니다. 


위 그림을 보면 첫 단계는 컨테이너 설정 입니다. 뭔가 담길 저장 공간을 설정 하는 군요. 그 이후 레전 맵핑 이라는 것을 합니다. 그리고~~ 대망의 화면을 생성 하는 군요. 그 다음에 각각의 구성 요소들 즉 모듈을 초기화 합니다.


Prism은 부트스트래퍼로 다양한 외부 라이브러리 또는 프리즘에 포함된 MEFBootstrapper등을 사용 할 수도 있습니다만, 저는 프리즘에 포함 되어 있는 UnityBootstrapper를 사용하고자 합니다. 


UnityBootstrapper의 원형을 살펴 보면


UnityBootstrapper는 Bootstrapper를 상속 받았군요. Bootstrapper의 원형도 살펴 보아야 할 듯 합니다.


원형만 바라 보면 이걸 어떻게 하라는 것인지 좀 막연합니다. 이런 코드를 보고 아하~~ 하고 뭔가 눈치를 챌 수 있으면 좋으련만... 전 아직 멀었나 봅니다. 그래도 원형을 살펴 봤으니 억측이라도 해 봐야 겠죠.


원형을 가만히 들여다 보시면 아직 구현 되지 않은 항목이 딱 하나 보입니다. 바로 Bootstrapper 클래스의 CreateShell 메소드가 구현되지 않았습니다. 


이건 확실하게 사용자가 만들어야 하는 클래스가 맞겠군요. 더군다나.. Shell 이라는 건 WPF의 MainPage를 이야기 하는 것일 테니 만들어진 메인 화면을 CreateShell 메소드를 오버라이드 하여 재정의 하도록 하죠.


그런데 말이죠.. 여기 Bootstrapper에 보면 멤버로 IUnityContainer 항목이 있습니다. UnityContainer는 Dependency Injection(의존성 주입) 컨테이너 입니다. 즉 여기에 메인 화면을 넣어 주면 될 듯한 분위기 입니다.


코드 확인 하도록 하죠.


햐~ 어렵습니다. 이게 MainWindow Shell = new MainWindow(); 와 같은 문장이라고 해야 겠죠?
그 다음입니다.. 생성을 했다면 초기화를 해야 겠죠.



일단 이까지가 윈도우를 띄우는 루틴 입니다. 이거 참... 그냥 윈도우 띄우는 것보다 한참 복잡하고 어렵네요 코딩량도 장난 아니구요. 그런데 이걸 왜 쓸까요? 이걸 왜 써야 하는지는 천천히 차근 차근 하다 보면 알아 질껍니다. 명확한 이유는 이거죠. 프리즘은 마이크로소프트의 대표적 의존성 주입 패턴의 결정체 입니다. 


쉘을 생성하고 호출 하여 화면에 띄우기는 했는데... 그 다음은 화면에 뭔가 표시 해야 겠죠. 여기서 나오는 방법이 이제 Region 이라는 것과  Module 이라는 것입니다. 


MSDN 가라사대~~


"모듈은 독립적으로 개발, 테스트 및 (옵션) 배포 할 수있는 기능 패키지입니다. 많은 상황에서는 모듈이 개발되어 다른 팀에 의해 유지됩니다. 전형적인 프리즘 응용 프로그램은 여러 모듈에서 구축되어 있습니다. 모듈은 (예를 들어, 프로파일 관리) 특정 비즈니스 기능을 설명하며, 이러한 기능을 구현하는 데 필요한 모든 뷰, 서비스 및 데이터 모델을 캡슐화하는 데 사용할 수 있습니다. 모듈은 여러 응용 프로그램에서 재사용 할 수있다 (예를 들어, 로깅 및 예외 관리 서비스를위한) 일반적인 응용 프로그램 인프라 나 서비스를 캡슐화하는 데 사용할 수 있습니다."


라고 정의 되어 있습니다. 


Bootstrapper의 원형을 다시 한번 확인 해 보면 대충 눈에 보이는 것이 ConfigureModuleCatalog()라는 원형을 볼 수 있습니다. 반환값은 void 이군요.


일단 Module 클래스를 하나 만들어 보겠습니다. 저 ConfigureModuleCatalog 메소드도 뭔가 넣어야 될 클래스가 필요 할 테니 말이죠.


이렇게 작성을 하고 Bootstrapper의 모듈카탈로그에 등록 하도록 하죠. 추후 Region 맵핑을 하기 위한 키로 "MainRegion" 이라는 스트링으로 등록 합니다.


이 코드를 보시면 ModuleCatalog는 Generic Collection 이라는것 정도는 설명 드리지 않아도 아시겠죠? 

자 이제 까지 하고 보면 참 이상한게 보입니다.코드 어디에서도 ModuleA를 생성 호출 하는 녀석이 없습니다. 이게 바로 Region의 마술 입니다. 일단 우리는 앞에서 MainWindow를 초기화 하고 Show 하였습니다.


그 MainWindow에 xmlns:cal="http://www.codeplex.com/prism" xml namespace를 추가 하도록 합니다. 그리고 Region 맵핑 하도록 하겠습니다. 전체 xaml 코드를 보도록 하죠.


MainRegion을 RegionManager가 모듈 카탈로그를 뒤져서 타입을 찾고 생성하고 초기화 하여 화면에 표시 하는 일을 수행 합니다.

 

정말 어렵습니다. 지금까지 작성한 코드를 보면 단 한군대에서도 명시적으로 new를 이용하여 클래스를 생성하고 사용한 적이 없습니다.


여러분이 감 잡으셨을 지도 모르겠지만 new를 쓰지 않았다는 것은 정말 커다란 의미를 내포 하고 있습니다. 이것은 다시 말해 명시적으로 하위 클래스를 강하게 결합하지 않고 있다는 것 입니다. 결국 하위 클래스는 존재 하여도 그만 존재 하지 않아도 그만 이라는 소리 인데, 이것이 바로 마틴 파울러가 처음 이야기 하였던 제어 역행화(IoC) 이론이며, 어플리케이션을 더욱 견고 하게 만드는 방법 이며...어쩌구 저쩌구.... 


어찌 되었든 프리즘 라이브러리가 어디선가는 new를 이용하여 객체를 생성하고 반환할 것입니다. 그것이 우리 눈에 보이지 않아 정말 어렵게 느껴지고, 개발자가 하지 않은 일을 라이브러리가 순서에 맞춰 대신 해주고 있기 때문에 연결 고리가 쉽게 와 닿지도 않습니다. 정말 어렵습니다. 그러나 멀찍이 서서 조금만 쉽게 바라보면 명확하게 각각의 영역이 분리 되어 있습니다. 개발자는 조금 더 비지니스에 집중 할 수 있도록 만들고 있습니다.


이것이 프리즘의 매력 입니다. 이제 시작 입니다. 해야지 해야지 했던 프리즘 공부를 이제 시작 하려고 합니다. 오늘 부터 말이죠....


장문의 글 읽느라고 고생 많으셨습니다.


그럼 전 이만~~~~~ 


 

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,



뜬금이 없다.. 랄까? 갑자기 웬 API??랄까? 이젠 쓸 일도 없는 녀석을 왜??? 랄까? 암튼 좀 그렇다.

작금의 상황은 이런 SendMessage보다... 더 좋은 모델들이 많이 있다. 굳이 SendMessage를 쓰는 것은... ㅜ,.ㅜ;; 좀 절망 이라고 할까.... 물론 가볍고 빠르다는 장점으로 인해 이 SendMessage를 사용하는 분들도 많이 있다.


어찌되었든.. 이 SendMessage라는 녀석을 이해 하려고 하면 기본 지식 부터 정리 하는 것이 좋을 듯 하다. 


옛날 DOS 시절을 생각 하면 그 땐 너무 행복한 시절이었다. 이런 SendMessage같은 녀석들이 없었으니 말이다. 하긴 그땐 인터럽트 때문에 어려웠다.


우리가 사용하고 있는 윈도우은 멀티 태스킹 환경이라는 것은 뭐 설명하지 않아도 다들 알고 있는 내용일 것이다. 모르는 사람들을 위한 팁으로 멀티 태스킹 환경이라는 것은 동시에 여러개의 프로그램을 실행 할 수 있는 환경 이라는 소리다. 


여러개의 프로그램이 동시에 실행 된다면 각각의 프로그램은 메모리 영역을 할당 받게 되는데 이는 보호되고 있어서 해당 프로그램 이외에 다른 프로그램은 메모리 영역에 접근 할 수 없다. (옛날 윈도우 3.1 또는 윈도우 95 시절에 윈도우가 무수히 블루 스크린을 띄웠는데 이게 이 메모리 영역의 침범으로 인한 오류로 컴퓨터를 재부팅 해야 하는 시절도 있었다. 지금은 거의 없어진 증상이지만...)


이렇게 여러개의 프로그램이 실행되어 있는 상황에서 각각의 프로그램들이 특정 데이타를 공유 할 필요성이 대두 되었고 여러개의 프로그램이 데이타를 공유 할 수 있는 방법을 통칭 IPC(Inter-process Communication)라고 한다.


그러하기에 IPC라는 것은 여러가지 방법을 가지게 될 것이다. 프로세서간 정보만 공유 하면 되는 것이기에 소켓 또는 파일 또는 레지스트리등을 사용할 수도 있을 것이다. 정말 많은 방법이 있을 것이므로 우리는 이 많은 방법들 중에 어떤 방법을 선택 해야 하며, 이때 고려 해야 할 요소들이 있는데 대충 다음과 같다.


1. Network을 고려 해야 하는가?

   1-1 통신 하려고 하는 대상의 OS가 같은가?

   1-2 통신 하려고 하는 대상의 OS종류는 한가지 인가?

2. 통신의 속도가 중요하게 고려 되어야 하는가?

3. 반복적 정보 공유 인가?

4. 통신하고자 하는 정보의 사이즈는 큰가? 작은가?


여러가지 고려 요소를 생각 하여 방법이 정해 지면 추 후 방법을 바꾸고자 할 경우 무척 어려운 상황이 발생 되므로 처음 선정 할때 부터 주의를 요해야 한다.


그럼 우리가 사용하는 Windows 환경에서의 메시지 전송 방법엔 어떤 방법들이 있는지 보자. 


1. 메지시 : 사용자 정의 메시지로 정보 전달 한다.

2. 클립보드 : 중앙 저장소를 이용한 대량의 정보 교환

3. DDE : Do Data Exchange를 이용한 지속적 정보 교환

4. 파일 맵핑 : 파일 맵핑을 이용한 메모리 공유

5. 메일 슬롯 : 메일 슬롯 사용

6. 파이프 : 파이프 사용

7. 윈속 : 소켓 사용

8. RPC, RMI : Remote Procedure Call, Remote Method Innovation 을 이용한 방법

9. COM, DCOM, COM+, WebService, WCF 등을 이용한 방법.


뭐 이 포스트에서 이야기 되지 않은 다른 방법들도 많을 것이다. 사실 IPC라는 것이 데이타를 공유만 하면 되는 것이기에 그 목적에만 맞으면 IPC라고 할 수 있을 것이다.


오늘의 포스팅 목적은 사실 위 많은 IPC 중에 메시지에 해당하는 내용을 살펴 볼 것이다.


메시지는 사실 정보를 교환 할 수 있는 방법중에 가장 간단하고 가장 가볍고 가장 빠른 방법이다. 이 메시지를 이용하기 위하여 우리는 OS에 대한 어느 정도 기초적 지식을 습득해야 한다. 


가장 처음 이해 해야 되는 내용은 Message Queue 이다.


일단 힘들게 그림 그린 메시지의 흐름도 부터 확인 하자.




메시지 큐는 여러분이 키보드를 이용하여 입력 하거나 마우스를 가지고 아이콘을 클릭 하거나 하는 일련의 동작들이 담기게 되는 일종의 자료 구조 이다. 이 메시지 큐는 무한 루프를 돌면서 사용자의 입력을 기다리게 된다.


메시지루프의 형태는 다음과 같다.



GetMessage 함수는 메시지 큐로 부터 메시지를 가지고 온다. DispatchMessage는 WndProc 함수가 메시지 구조체에 들어 있는 윈도우 핸들을 이용할 수 있도록 메시지 데이터를 WndProc함수로 보낸다. 결국 우리는 메시지를 가로채기 위해 WndProc함수를 Override 하여 재정의 사용한다. 


TranslateMessage()는 DispatchMessage 전 키보드 또는 입력 메시지를 전 처리 할 수 있다. 이 함수는 PreProcessMessage로 보낸다. PreProcessMessage를 Override 하여 재정의 사용한다. 매개 변수에는  WM_KEYDOWM, WM_SYSKEYDOWN, WM_CHAR 및 WM_SYSCHAR 값을 사용할 수 있다. 


다음 그림을 통해 메시지 루프를 조금 더 자세히 들여다 보자. 


이 그림에서 주의 깊게 보아야 할 항목은 PostMessage는 시스템 메시지 큐에 담긴다는 것이다. 이 때문에 SendMessage와 PostMessage가 몇가지 다른 특성을 보이게 된다. 


첫번째 차이점은 PortMessage는 비 동기 이다. (메시지 큐로부터의 응답을 기다리지 않는다.) SendMessage는 동기 이다. (메시지 큐로부터의 응답을 기다린다.)


이쯤 공부 했으니.. 이제 샘플을 보자. 


위의 샘플은 WM_COPYDATA 메시지를 이용하였다 이것은 메시지 전송을 할 경우 간단한 문자 메시지를 교환 하더라도 아주 복잡한 방법이 동원 되어야 하기 때문에... (이 예제는 다음 사이트에서 볼 수 있다. http://www.pinvoke.net/default.aspx/user32/SendMessage.html ) 윈도우에 추가된 메시지 이다.


두번째 차이점이 여기에 있다. PostMessage는 시스템 메시지큐에 들어가기 때문에 이 WM_COPYDATA 메시지를 사용할 수 없다.


추가적인 사항으로 SendMessage를 이용한 Broad-Casting 이다. 말 그대로 브로드 캐스팅이면... 여러개의 프로그램에 동시에 메시지를 날릴 때 이용 하는 방법 이란 것을 알 수 있을 것이다. 



위 예제 에서 주의 깊게 보아야 할 항목은 RegisterWindowMessage 이다. MSDN - https://msdn.microsoft.com/ko-kr/library/vstudio/ms644947(v=vs.110).aspx에서 확인 할 수 있다.


참고로 약간의 설명을 하자면 다음과 같다. RegisterWindowMessage는 응용 프로그램에서 정의한 메시지를 등록하고 그 메시지가 중복되지 않도록 해서 이미 등록된 다른 메시지와 충돌이 일어나지 않도록 한다. 


두 가지 별개의 프로그램에서 메시지를 주고 받으면서 의사 교환을 할 때 일반적인 (WM_USER, WM_USER + 1 등) 메시지 번호를 사용하는 것은 안전하지 않다. 


갑자기 정리 하게된 내용이 너무 많다... 아 배고파~~~~~ 


Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

이전 포스팅에서 하드 코딩 된 맵핑 정보에 대한 글을 쓴 적이 있습니다.


마이크로소프트도 했는데... 나라고 못 할쏘냐~~~~ 


나의 어플리케이션에서도 나만의 네임스페이스를 맵핑 하고 싶을 수 있습니다.


이때 사용 할 수 있는 방법을 정리 합니다.


프로젝트의 Properties 항목의 AssembleyInfo.cs 파일을 Open 해서...


제일 하단에 이렇게 추가 합니다.


이제 코드에서 사용 할 수 있습니다.


xmlns:mc="http://iopeni.com/compressor"


으로 말이죠...!!!!!!!!!

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

double 형이 가지고 있는 자리수 제한의 문제로 인하여 동일 한 수를 동일 한 수로 비교 하지 못할 경우....에 사용 가능한 짧은 코드 샘플....

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

어느날 아는 동생으로 부터... (나 보다 훨 스킬이 좋은 개발자다.) ODP.Net을 이용하여 Merge 쿼리를 작성 하면... 반환값이 언제나 무조건... -1이 나온다는 제보를 받았다.


물론 동생은 이걸 해결 하기 위하여 정말 열심히 검색을 했고 결국 얻어 낼 수 있는 정보는 오라클의 버그라는 것이었다.


이거 참... 나보다 훨 스킬이 좋은 동생이기에 이 이야기를 의심할 수 조차 없었다. 그 동생이 그렇다면 그런것이기에...


그럼 이걸 어떻게 해결 해야 할까? 오라클이 이런 버그를 만들어 놓고.. 패치는 하지 않은 걸까? 정말 열심히 찾았다... 그런데... 발견 할 수 있는 모든 글은.... 몇개 되지도 않았고 전부 다들 이걸 어떻게 해결 해야 할지에 대한 문의 와.. 간단하게 PL/SQL을 이용하여 sql%rowcount 를 활용하라는 내용이 전부 였다.


해결 방법 : 닭치고.... 

System.Data.OracleClient

을 쓰면 된다......


이렇게 이야기 하면... 정말 속 편하겠다... 근데 이눔의 호기심이 젠장..... 그냥 넘길 수 없었다.


일단 System.Data.OracleClient에 속한 ExecuteNonQuery 는 잘 동작하니까..... 마이크로소프트는 뭐라고 하는지 MSDN을 봤다...


https://msdn.microsoft.com/ko-kr/library/vstudio/system.data.oracleclient.oraclecommand.executenonquery(v=vs.100).aspx


물론 여기서 중요한 이야기는 


UPDATE, INSERT 및 DELETE 문의 경우, 반환 값은 해당 명령의 영향을 받는 행의 수입니다. CREATE TABLE 및 DROP TABLE 문의 경우 반환 값은 0입니다. 다른 형식의 문의 경우에는 반환 값이 -1입니다.


이 이야기가 되시겠다...


자 그렇다면.... ODP.Net은 그럼 Merge문을 다른 형식의 문장으로 보고 있다는 말인가? 이런.. 퐝당한..... 


Oracle.DataAccess.dll을 .Net Reflector로 까 봤다...


---- 중략 ----

if ((errCode != -1) && (((this.m_pOpoSqlValCtx.CommandType == 4) || 

                           (this.m_pOpoSqlValCtx.CommandType == 2)) || 

                           (this.m_pOpoSqlValCtx.CommandType == 3)))

{

    this.m_rowsAffected = this.m_pOpoSqlValCtx.RowsAffected;

    

    if ((this.m_arrayBindCount > 1) && (null != this.m_pOpoSqlValCtx.RowsAffectedForArrayBind))

    {

        this.m_rowsAffectedPerBind = new long[this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind];

        for (num7 = 0; num7 < this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind; num7++)

        {

           this.m_rowsAffectedPerBind[num7] = this.m_pOpoSqlValCtx.RowsAffectedForArrayBind[num7];

        }

     }

 }

 else

 {

     this.m_rowsAffected = -1;

 }


---- 중략 ----


보셨는가? 명령어 타입이.. 딱 3가지... 4, 2, 3이 아니면 무조건... 반환값은 -1 이다. 뭐 이쯤 되면 대충 짐작으로도 저건.. Insert, Delete, Update 쯤 될꺼란 짐작 될것이다.


아 퐝당~~~~


그럼 System.Data.OracleClient.dll 에 있는 코드는 어떻게 되지????? 그냥 넘길 수 없다. 

역시나 .Net Reflector 로 까 봤다.. 


---- 중략 ----


int num = -1;

            try

            {

                try

                {

                    ArrayList resultParameterOrdinals = new ArrayList();

                    statementHandle = this.GetStatementHandle();

                    this.Execute(statementHandle, CommandBehavior.Default, needRowid, out rowidDescriptor, out resultParameterOrdinals);

                    if (resultParameterOrdinals != null)

                    {

                        num = 0;

                        foreach (int num2 in resultParameterOrdinals)

                        {

                            OracleParameter parameter = this._parameterCollection[num2];

                            if (OracleType.Cursor != parameter.OracleType)

                            {

                                num += (int) parameter.Value;

                            }

                        }

                        return num;

                    }

                    if (OCI.STMT.OCI_STMT_SELECT != this._statementType)

                    {

                        statementHandle.GetAttribute(OCI.ATTR.OCI_ATTR_ROW_COUNT, out num, this.ErrorHandle);

                    }

                    return num;

                }

                finally

                {

                    if (statementHandle != null)

                    {

                        this.ReleaseStatementHandle(statementHandle);

                    }

                }

            }

            catch

            {

                throw;

            }

            return num;


---- 중략 ----


뭐 대충 보면... 처음에 -1로 초기화 하고 실행 후 파라미터 반복하며,,,, 조건이 맞으면 하나씩 카운트 한 다음에 반환하는 것을 볼 수 있다.. 


Oracle 프로그램 졸라 잘 만드는지 알았더니.. 이거 참... 실망이다...  이런... 멍청한 것들...


그럼 이걸 어떻게 해결 하면 되는 건지 아니 궁금하다 하면 좀 그럴것이다... 뭐 결국 업데이트 갯수가 몇개가 되었다 보다 적용 된게 있느냐 없느냐가 관건이겠으므로 나는 다음과 같은 방법을 생각 했다..



using (OracleConnection conn = new OracleConnection(oradb))

            {

                conn.Open();

                OracleCommand cmd = new OracleCommand();

                cmd.Connection = conn;

                cmd.CommandText = @"BEGIN

                                       MERGE INTO employee

                                            USING DUAL

                                               ON (companyid = 1235)

                                       WHEN MATCHED

                                       THEN

                                          UPDATE SET empname = '홍길동'

                                       WHEN NOT MATCHED

                                       THEN

                                          INSERT     (companyid, empname, phone)

                                              VALUES ('1111', 'choi', '111111');


                                       IF SQL%ROWCOUNT = 0

                                       THEN

                                          RAISE_APPLICATION_ERROR(-20801, '에러 났어요.');

                                       END IF;

                                    END;

                                    ";

                

                cmd.CommandType = CommandType.Text;


                try

                {

                    int dr = cmd.ExecuteNonQuery();

                }

                catch (OracleException ex)

                {

                    if (ex.Number == 20801)

                    {

                        System.Console.WriteLine(ex.Message);

                    }

                    else

                    {

                        throw;

                    }

                }

            }


뭐 대충 이렇게 말이다....... 만약 하나의 로우도 변경 되는 것이 없다면 오라클이 오류를 뱉을 것이다... 젠장 나 못해먹겠어... 라고 말이지......



BEGIN

   MERGE INTO employee

        USING DUAL

           ON (companyid = 1235)

   WHEN MATCHED

   THEN

      UPDATE SET empname = '홍길동'

   WHEN NOT MATCHED

   THEN

      INSERT     (companyid, empname, phone)

          VALUES ('1111', 'choi', '111111');


   IF SQL%ROWCOUNT > 0

   THEN

      RAISE_APPLICATION_ERROR(-20801, SQL%ROWCOUNT);

   END IF;

END;


이렇게 하면 뭐... 적용된 갯수를 Exeception으로 잡아 낼 수도 있겠다... 에효... ㅜ,.ㅜ;;; 이런 꼼수는 싫은 데.....


오라클은 당췌 이 버그는 언제 수정해 줄껀지..... 알고는 있으려나?????????

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

Visual Studio 2013 에는 우리가 알고 있는 Off-Line용 배포 프로젝트가 빠져 있습니다.

Microsoft는 Flexsa Soft와의 계약을 통하여 제한 버전인 Install Shield LE를 제공 하고 있습니다.


정품 Visual Studio를 사용하신다면 이메일을 등록 하고 다운로드 받으면 시리얼넘버가 메일로 발송 됩니다.


그런데 말이죠... Install Shield가 쉬운듯 하면서도 뭔가 좀 고급기능을 사용하고자 하면 제한 버전이기 때문에 지원이 안되는 건지... 이것 저것 좀 곤란한 상황을 많이 만나게 되기도 합니다.


예를 들자면.... 윈도우 서비스 프로그램 배포라던지... 또는 외부 확장 라이브러리의 실행 설치본 같은 것들을 추가 하고자 하면 살짝 좀 난감해 집니다.


이런 상황을 만나고 나니 Microsoft에서 Visual Studio에 기본 제공해 주던 설치 프로젝트가 그리워졌고.... Visual Studio 에서도 사용할 수 있는 방법을 찾기 시작 했습니다.


역시 Microsoft는 우리를 버리지 않았습니다.


있더군요......


https://visualstudiogallery.msdn.microsoft.com/9abe329c-9bba-44a1-be59-0fbf6151054d?SRC=Featured


이곳에 들어가셔서 다운로드 받은 후 설치를 하고 보면 


Visual Studio 2010에서 보던 바로 그 설치 프로젝트 템플릿을 볼 수 있습니다.. 아래 처럼 말이죠.





VSI_bundle.exe


위 클릭 하시면 이곳에서도 다운로드 받으실 수 있습니다. 암튼...... 그렇습니다.



Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

아무 것도 아닌(?) 팁이라고 해야 할까요?


사실 아무 것도 아닌 아주 간단한 팁입니다. C# 1에는 아주 특수한 타입이 하나 있습니다.


System.Type 을 상속 받은 값 형식의 자료형들은 null을 대입 할 수 없습니다.


그런데 이게 좀 곤란한 상황이 발생 할 때가 있습니다. 정의 할 수 없는 상태의 값들이 바로 그렇습니다. 사람이 살아 있는데 사망일을 기록하여야 한다면 DB에는 아마도 null을 대입하게 될 것입니다.


이 null을 어플리케이션 레이어로 들고 나와서 DateTime 타입의 변수에 저장 하려고 하면 값 형식 이기 때문에 대입 할 수 없고 이 상황은 오류를 발생 시킵니다.


이 문제를 해결 하기 위하여 Nullable 이라는 아주 특수한 자료형이 있습니다. 


Nullable<DateTime> a = null; 


과 같이 DateTime 자료형에 null을 대입 할 수 있도록 하는 아주 특수한 자료형이죠!


이 자료형이 C# 2 로 오면서 좀 더 사용하기 쉬워졌습니다.


DateTime? a = null;


과 같이 말이죠 Type 에 ?을 적어 줌으로 인해서 null을 대입 할 수 있습니다.


오늘 이야기를 하려고 하는 것은 Nullable 자료형을 이야기 하려고 하는 것은 아닙니다.


널인 값을 널이 대입될 수 없는 값 형식 자료형에 대입 하려고 하면 오류가 리턴 될 것이고, 이 변수가 널인지 아닌지 우리는 if else 문 또는 bool ? true : false;의 3항 연산자를 사용하게 됩니다.


참 이런 문장을 쓸 때 마다 답답합니다. 특히나 DB에서 들고 나와야 하는 필드가 무지 많은 상황이고 쿼리에 NVL을 이용한 널 처리를 하지 않았다면 또는 DateTime에 아주 특수한 의미의 MagicValue 라고 불리우는 개발자 나름대로의 규칙 즉 지금은 사용하지 않을 어떤 값을 특수한 목적의 상태를 가리키는 값으로 활용 아직 죽지 않은 사람이라면 사망일에 1년 1월 1일 과 같은 값을 대입하고 어플리케이션에서 1년 1월 1일 인 사람은 살아 있다고 판단하게 하는 ....


뭐 이런 상황이라면 각 필드 별로 많은 조건 판단을 하게 됩니다. 


이런 상황에서 만약 널이 아니면 값을 그대로 대입하고 널이라면 다른 값을 대입하라 라는 삼항연산자의 조건을 조금 더 쉽게 정리 할 수 있는 식이 있다면 얼마나 좋을까요?


Microsoft는 기대를 저버리지 않았습니다.


바로 널 병합 연산자라고 불리우는 ?? 가 바로 그것 입니다.


아래 와 같은 문법이죠..


int? a = null;

int b = a ?? 5;


와 같이 쓰는 거죠. 이 녀석은 중첩 될 수 있습니다.


int b = null ?? null ?? null ?? ........ ?? 5;


처럼 말이죠.


이 널 병합 연산자는 저에게 매마른 대지에 단비 처럼.. 저의 복잡하고 읽기 힘든 코드를 깔끔하게 정리 하게 해준..... 정말 고마운 녀석입니다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,