DIY로...(Ⅱ)

Posted in 일상 // Posted at 2014. 2. 24. 11:07
728x90

지난 주말에... '방문 손잡이/세면대 수전 교체' 에 이어 좌변기 폴업 교체를 DIY 했다.

물을 내리면 다시 차오르는 게 신통찮아 막힌것인가 하고 뚫기 시도를 여러차례 해봤으나 상태는 여전 한지라 뒤쪽 뚜껑을 열고 관련 부품을 이리저리 훑어 보았다.

 

 

구조를 찬찬히 보니, 흔히 부레라 일컫는 폴업이 물의 공급을 제어하는 역할을 하는 것 같은데, 평생 처음 들여다 보는거라 무엇이 문제인지 감을 잡기 쉽지 않았다. 혹시나 해서 폴업을 분리해서 이것저것 만져보니 그 원리를 대략 짐작할 수 있었는데, 그래도 조심스러워 폴업의 수위만 조절해서 다시 장착하니 물이 올라오는데 걸리는 수십분의 시간을 상당히 줄일 수 있었다.

 

느낌에, 물이 안내려 가는 것이 아니라 안 차오를 것이 막혔다기 보다는 폴업의 교체가 요구되는 것이 아닌가 짐작하고 집 근처 철물점에 가서 폴업의 가격을 알아보니, 풀 세트 5만원리아 한다.

 

부속 세트는 폴업 뿐만 아니라 물 내리는 손잡이와 물을 막는 고무마개 등이 모두 포함되어 있어 풀업만 필요한 나에게는 낭비일까 하여 망설여지고, 혹여 선무당이 사람 잡듯 스스로 파악한 폴업 교체가 무의미 하다면 괜한 돈을 쓰는 거라 가격만 물어보고 구입하지 않았다.

 

결국 업자를 불러 원인 파악을 요청했더니, 예상대로 폴업 교체만 필요하다고 하여 직접 교체한다고 했다.

인터넷에서 폴업만 따로 구입하면 대락 2만 5천원에서 3만원 정도 가격에 팔고 있었다. 저녁에 산책 하다가 다른 철물점에 들러 폴업만 구입하고자 하니 3만 5천원 달라고 한다. 인터넷과 만원 정도 차이가 나지만 빨리 해치우고 싶은 마음에 바로 구입해서 교체하니 잘 된다.

 

음.. DIY의 소소한 맛이란...

 

업자에게 맡기면 부품값을 합쳐 약 8만원에서 10만원 정도 지출해야 하는 것 같다. 적어도 5만원 정도는 아꼈으니 이 돈을 소주값으로 지출해야 겠다고 마음 먹기에 이른다. ^^

 

'일상' 카테고리의 다른 글

평가(특히 면접)에 임하는 자세  (0) 2016.08.25
그대는 어디에...  (0) 2015.10.08
불혹에 즈음하여...  (6) 2013.12.31
정보통신망법 제23조의 2항 위반  (1) 2013.12.06
아버지는 잊어버린다  (0) 2013.11.25

Caching in WCF

Posted in .NET Framework // Posted at 2014. 2. 21. 14:42
728x90

 

WCF 기반 서비스들이 공통으로 사용할 프레임워크를 만들면서, 오퍼레이션의 결과에 대한 캐싱 기능을 프레임워크 아키텍처에 포함 시키기로 했다.

 

서비스 오퍼레이션이 수행되기 전에 제어권을 받아 분기처리가 가능하도록 지원하는 IOperationInvoker 기반의 Caching Invoker를 제작하는데 캐시 API의 구현을 고민하게 되었다.

 

WCF에서도 ASP.NET의 Cache(System.Web.Caching.Cache)를 사용할 수 있지만, 이를 사용하기 위해서는 ASP.NET 호환성을 위해 서비스 클래스에 AspNetCompatibilityRequirements Attribute를 설정하고 Config 파일에 <serviceHostingEnvironment> 요소 aspNetCompatibilityEnabled 속성을 활성화 시켜 주어야 하는 등 프레임워크 연동에 제약을 가하게 되며, 더불어 이와같은 환경은 IIS 호스팅을 강제하기 때문에 셀프 호스팅 환경에서는 프레임워크의 캐싱 기능을 활용 할 수 없는, 즉 범용성의 저해가 발생해 버린다.

 

따라서 ASP.NET의 Cache는 적절치 못하다는 생각이다.

 

다른 방편으로, 전역 static 변수로 Dictionary 형태의 자료구조를 직접 관리할 수 있으나, 캐시 만료시간과 우선순위, Dependency 및 캐시 상태 변화에 따른 콜백 이벤트 등이 필요할 경우 이를 직접 구현해야 하니 역시 적절치 못하다 하겠다.

 

그러던 중, .NET 4.0에 추가된 MemoryCache라는 넘을 알게 되었다.

MSDN의 다음 한 구절이 나를 흥분(?)하게 만들었다.

ASP.NET Cache MemoryCache 클래스 사이의 주요 차이는 ASP.NET 응용 프로그램이 아닌 .NET Framework 응용 프로그램에서 사용할 수 있도록 MemoryCache 클래스가 변경되었다는 점입니다

 

왜 이걸 이제야 알게 되었단 말인가? 프레임워크 버전업에 따른 기본 API 학습을 게을리 한 탓이다. ㅡ,ㅡ;

MSDN을 찬찬히 훑어보니 딱! 원하던 그것이다.

 

WCF 서비스 기반에서 ASP.NET 호환성과 무관하게 캐싱 기능을 제공하기 위해 사용하기로 했다.

본격적인 적용 이전에 확인해야 할 사항은, 이 캐시가 ASP.NET 캐시처럼 응용 프로그램 도메인 내 전역 메모리를 사용하는가 하는 것이었다.

 

WCF 서비스가 매 호출마다 새로운 인스턴스를 만들거나 Caching Invoker가 매번 새롭게 생성된다면 MemoryCache를 인스턴스 범위를 넘어서도록 관리해야 한다.

 

간단히 테스트를 해보니 자연스럽게 응용 프로그램 도메인내에서 공유가 가능한 영역에서 캐시를 관리할 수 있다는 것을 확인하였다. 기본적인 활용 코드는 대략 다음과 같다.

 

ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = aAbsoluteExpirationDateTime;
policy.SlidingExpiration = slidingExpirationTimeSpan;

                   

cache.Set(keyName, cacheValue, policy);

 

...

 

cache.Get(keyName);

 

 

'.NET Framework' 카테고리의 다른 글

Security Context Token(SCT) Expired on WCF  (0) 2015.03.03
미스터 빈, 너로구나  (0) 2014.07.31
ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13

불혹에 즈음하여...

Posted in 일상 // Posted at 2013. 12. 31. 15:40
728x90

 

지학 - 이립 - 불혹 - 지천명 - 이순 - 종심

 

공자께서 말씀하신 연령에 따른 자아의 발전상.

 

오십유오이지우학(吾十有五而志于學)

삼십이립(三十而立)

사십이불혹(四十而不惑)

오십이지천명(五十而知天命)

육십이이순(六十而耳順)

칠십이종심소욕 불유구(七十而從心所欲 不踰矩)

 

열 다섯에 학문에 뜻을 두었고

서른 살에 자립했으며

마흔 살에 미혹되지 않게 되었다

쉰 살에 하늘의 명을 알았고

예순 살이 되자 귀에 거슬리는 이야기가 없어졌고

일흔 살이 되니 마음 내키는 대로 해도 법도를 넘지 않게 되었다

 

미혹하지 않을 나이가 되었구나...

 

 

'일상' 카테고리의 다른 글

그대는 어디에...  (0) 2015.10.08
DIY로...(Ⅱ)  (0) 2014.02.24
정보통신망법 제23조의 2항 위반  (1) 2013.12.06
아버지는 잊어버린다  (0) 2013.11.25
6년 반 전에...  (0) 2013.11.22

ASP.NET SignalR

Posted in .NET Framework // Posted at 2013. 12. 6. 16:34
728x90

오늘 우연히 다른 기술 자료를 검색하다, SignalR 이라는 라이브러리를 접하게 되었다. 실시간 웹 기능을 지원해 주는 ASP.NET 기반 오픈소스 라이브러리이다.

 

 

 

 

웹의 실시간 기능을 위한 다양한 기술과 기법이 사용되어 왔으나, HTML5에 와서야 Web Socket를 통한 진정한 양방향 실시간 통신이 가능해 졌다고 할 수 있다. 이 블로그에서도 관련 내용을 다룬바 있다.

 

=> [HTML5] Web Socket

=> [HTML5] Server-Sent Events

 

SignalR은 실시간 기능을 구현하기 위해 Web Socket을 사용하고 있으나, Web Socket를 지원하지 않는 환경을 위한 fallback 도 마련해 두고 있다.(이때 사용되는 기술이 Server-Sent Event, LongPolling과 같은 것들이다.)

(대부분의 환경을 지원하기 위한, 친절한 라이브러리가 아닐 수 없다. ^^)

 

다음 사이트들에서 SignalR의 개념과 소스, 응용 데모를 확인해 볼 수 있다.

 

눈이 번쩍 뜨여, 몇 가지 데모를 실행해 봤더니 놀라울 따름이다. 닷넷과 자바스크립트로 구성되어 실시간 웹 기능을 위한 서버와 클라이언트 측을 포괄하고 있다. 실시간 웹 기능을 위한 생산성과 안정성이 크게 향상될 것으로 보인다.

 

클라이이언트 측 Knockout.js와 묶으면 기가막힌 실시간 싱글 페이지를 매우 쉽게 구현할 수 있을 것 같다.

=> Knockout.js

 

이런 생각을 하고 있던 차에, 다음 블로그의 글을 발견했다. SignalR과 Knockout.js의 조합 뿐만 아니라 서버 측 자동 이벤트 발생을 위한 EF SqlDependency를 한데 묶어 데이터 변경 기반 실시간 웹 기능 사례를 보여주고 있다.

 

=> SignalR + SqlDependency + Knockout JS를 활용한 실시간 데이터 조회

 

환경적인 부분에서 참고할 사항은 SignalR이 어떤 통신 기반(WebSocket? Comet?..)으로 구동하는지는,

브라우저에 따라서도 다르지만 윈도우 서버의 IIS에 호스팅 될 경우 서버 버전도 염두해야 한다.

 

도우 서버 2012 또는 윈도우 8에서 WebSocket 프로토콜을 활성하 시킨 경우에만 웹 소켓을 기반으로 한다는 사실을 참고하자.

 

IIS외에 셀프 호스팅도 가능하니 다음 URL에서 간단한 힌트를 얻을 수 있겠다.

=> Signalr selfhosting

 

시간이 갈수록 개발자가 점점 편해지고 있는 듯 하다. 장/단이 있으며 양면성이 있으나, 개발자가 아닌 툴 이용자로 전락하지 않기 위한 노력이 필요하겠다.

 

'.NET Framework' 카테고리의 다른 글

미스터 빈, 너로구나  (0) 2014.07.31
Caching in WCF  (2) 2014.02.21
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07

정보통신망법 제23조의 2항 위반

Posted in 일상 // Posted at 2013. 12. 6. 10:31
728x90

한국인터넷진흥원이라는 곳에서, 이런 메일이 왔다.

 

 

http://mkex.pe.kr 사이트에서 주민번호를 수집하고 있어서 법에 위반되니 시정하라는 내용이다.

 

이 사이트는 닷넷 커뮤니티를 지향하던, 나의 두 번째 개인 사이트로 지금은 이전 글들을 보존하는 차원에서 유지하고 있는 사이트이다. 킴스보드를 기반으로 한 이 사이트는 그룹 메일링 및 회원들의 책갈피와 같은 편리한 기능 제공을 목적으로 원하는 사람에 한해 회원가입을 받도록 되어 있다. 물론 모든 글은 회원이 아니라도 읽을 수 있다.

 

그리고 회원가입시에도 주민번호는 받지 않도록 되어 있는데, 위반이라니.. 의아하다.

뭐 어쨋던... 지금은 가입 자체가 무의미하니 가입 부분을 제거하기로 했다.

 

 

'일상' 카테고리의 다른 글

DIY로...(Ⅱ)  (0) 2014.02.24
불혹에 즈음하여...  (6) 2013.12.31
아버지는 잊어버린다  (0) 2013.11.25
6년 반 전에...  (0) 2013.11.22
DIY 로...  (0) 2013.11.18

아버지는 잊어버린다

Posted in 일상 // Posted at 2013. 11. 25. 18:52
728x90

 

아버지는 잊어버린다


- W. 리빙스턴 라니드 -

 


아들아, 내 말을 듣거라. 나는 네가 잠들어 있는 동안 이야기하고 있단다.
네 조그만 손은 빰 밑에 끼어 있고 금발의 곱슬머리는 촉촉하게 젖어 있는 이마에 붙어 있구나. 

 

나는 네 방에 혼자 몰래 들어왔단다.

 

몇 분 전에 서재에서 서류를 읽고 있을 때, 후회의 거센 물결이 나를 덮쳐 왔다.

나는 죄책감을 느끼며 네 잠자리를 찾아왔단다.


내가 생각해 오던 몇 가지 일이 있다. 아들아, 나는 너한테 너무 까다롭게 대해 왔다.


네가 아침에 일어나 얼굴에 물만 찍어 바른다고 해서 학교에 가려고 옷을 입고 있는 너를 꾸짖곤 했지.

신발을 깨끗이 닦지 않는다고 너를 비난했고, 물건을 함부로 마룻바닥에 던져 놓는다고 화를 내기도 했었지.


아침식사 때도 나는 또 네 결점을 들춰냈다.

음식을 흘린다거나 잘 씹지도 않고 그냥 삼켜버린다거나, 또 식탁에 팔꿈치를 올리고 버터를 빵에 많이 바른다는 등. 그러나 너는 학교에 들어갈 때 출근하는 나를 뒤돌아 보며 손을 흔들며 말했지.


"잘 다녀오세요, 아빠!"


그때도 나는 얼굴을 찌푸리며 대답했지.
"어깨를 펴고 걸어라!"


애야, 기억하고 있니?

나중에 내가 서재에서 서류를 보고 있을 때 너는 경계의 빛을 띠고 겁먹은 얼굴로 들어왔었잖니?

일을 방해당한 것에 짜증을 내면서 서류에서 눈을 뗀 나는 문 옆에서 망설이고 서 있는 너를 바로보며

 

"무슨 일이냐?하고 퉁명스럽게 말했지.

 

너는 아무 말도 하지 않고 갑작스레 나에게로 달려와 두팔로 내 목을 안고 키스를 했지.

너의 조그만 팔은 하나님이 네 마음 속에 꽃 피운 애정을 담아 나를 꼭 껴안았다.

 

그것은 어떤 냉담함에도 시들 수 없는 애정으로 가득 차 있었다.

그리고서 너는 문 밖으로 나가 계단을 쿵쾅거리며 네 방으로 뛰어올라갔다.


내 손에서 서류가 마룻바닥에 떨어지고 말할 수 없는 공포가 나를 사로잡은 것은 바로 그 직후의 일어었단다.

 

내가 왜 이런 나쁜 버릇을 갖게 되었을까?

 

잘못만을 찾아내 꾸짖는 버릇을. 그것은 너를 착한아이로 만들려다 생긴 버릇이란다. 

너를 사랑하지 않아 그런 것이 아니라 어린 너한테 너무나 많은 것을 기대한 데서 생긴 잘못이란다. 

나는 나의 어린 시절을 바탕으로 너를 재고 있었던 거란다.


그러나 너는 착하고, 따뜻하고, 진솔한 성격을 갖고 있다.

너의 조그만 마음은 넓은 언덕 위를 비치는 새벽빛처럼 한없이 넓단다.

그것은 순간적인 생각으로 내게 달려와 저녁 키스를 하던 네 행동에 잘 나타나 있다.

오늘밤엔 다른 것이 필요 없다.

 

얘야, 나는 어두은 네 침실에 들어와 무릎을 꿇고 나 자신을 부끄러워하고 있단다.

이것은 작은 속죄에 불과하다.

네가 깨어 있을 때 이야기를 해도 너는 이런 일을 이해하지 못하리라는 것을 나는 잘 알고 있다.

 

하지만 내일 나는 참다운 아버지가 되겠다.

 

나는 너와 사이좋게 지내고, 네가 고통을 당할 때 같이 괴로워하고, 네가 웃을 때 나도 웃겠다.

너를 꾸짖는 말이 튀어나오려고 하면 혀를 깨물겠다.


그리고 계속해서 의식적으로 되뇌어야지.
"우리 애는 작은 어린아이에 불과하다" 고.


너를 어른처럼 대해 온 것을 부끄럽게 생각한단다.

 

지금 네가 침대에 쭈그리고 자는 것을 보니 아직 너는 갓난애에 지나지 않다는 것을 알겠구나.

어제까지 너는 어머니의 어깨에 머리를 기대고 품에 안겨 있었지.

 

내가 너무나 많은 것을 너한테 요구해 왔구나. 너무나도 많은 것을.

 

 

 

'일상' 카테고리의 다른 글

불혹에 즈음하여...  (6) 2013.12.31
정보통신망법 제23조의 2항 위반  (1) 2013.12.06
6년 반 전에...  (0) 2013.11.22
DIY 로...  (0) 2013.11.18
사옥 이전  (1) 2013.10.07

6년 반 전에...

Posted in 일상 // Posted at 2013. 11. 22. 17:40
728x90

예전 블로그의 글들을 무심히 넘겨보다, 이런 글을 발견했다. 대략 6년 반 전이구나! 많은 시간이 흘렀구나..

6년이 넘는 시간을 어떻게 보냈던가? 스스로 한심해지려 한다. 쩝... 

 

 

'일상' 카테고리의 다른 글

정보통신망법 제23조의 2항 위반  (1) 2013.12.06
아버지는 잊어버린다  (0) 2013.11.25
DIY 로...  (0) 2013.11.18
사옥 이전  (1) 2013.10.07
미역 선물  (0) 2013.10.04

maxConcurrentSessions in WCF

Posted in .NET Framework // Posted at 2013. 11. 21. 15:05
728x90

일전에 WCF 서비스 스로톨링(Service Throttling)의 기본 값을 알아본 바 있다.

=> http://m.mkexdev.net/189

 

 

 

 

 

스로톨링 항목 중, 최대 연결 세션 수를 제한하는 maxConcurrentSessions 라는 속성이 있는데, 이 값이 세션을 사용하지 않는 바인딩 환경에서도 제한값으로 동작하는지 궁금해 졌다.

 

WCF 서적을 보니, 트랜스포트 세션/보안 세션/신뢰 세션 등을 사용하지 않는 바인딩에서는 이 속성은 의미없다라고 설명하고 있으나, 어떤 인터넷 자료에서는 그렇지 않다는 예기가 있다.

 

상식적으로 생각해 봐도, 세션에 기반하지 않는 바인딩이라면 세션 제한 수가 무의미한게 맞는 듯 하지만 확실히 해두기 위해 테스트를 수행해 보았다.

 

두 개의 WCF 서비스를 만들고, 하나는 basicHttpBinding 으로 나머지 하나는 wsHttpBinding 기반의 신뢰세션을 사용하도록 한 뒤 세션 제한 수를 5로 설정하고 클라이언트에서 각각 10번씩 호출해 본다. 이때 세션 제한 수에 걸리도록, 프록시 객체를 닫지(Close) 않도록 한다.

 

//WCF Service 설정

<serviceThrottling maxConcurrentSessions="5" /> 

 

//클라이언트 코드

for (int i = 0; i < 10; i++)
{
       ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client();                
       Console.WriteLine(string.Format("{0}-{1}",i,proxy.GetData(7)));
       //proxy.Close(); //세션 제한수에 걸리도록 프록시를 닫지 않는다.
}

 

결과는 예상했던 대로, 세션을 사용하지 않는 basicHttpBinding은 maxConcurrentSessions 제한 값과 무관하게 10번 모두 호출되었다. 반면 신뢰 세션을 사용하도록 한 wsHttpBinding은 이 제한값에 영향을 받아 5번 호출되고 대기하다가 결국 Timeout 오류가 발생했다.

 

그래서 이렇게 결론을 내렸다.

: 세션을 사용하지 않는 바인딩 환경에서는 maxConcurrentSessions 제한 값이 무의미하다.

 

좀 더 테스트 해보기 위해, wsHttpBinding에서 신뢰세션을 제거하고 테스트를 해 보기로 했다.

wsHttpBinding에서 신뢰 세션을 제거하면 결국 그 어떤 세션도 사용하지 않게 되므로 basicHttpBinding와 같이 제한 값이 무의미해져야 하는데, 테스트 결과는 여전히 5번 호출 뒤 대기하는 것이 확인되었다.

 

이해할 수 없는 결과로 몇 번의 삽질을 거듭하다가 문득, wsHttpBinding의 기본 보안 모드가 message이고 클라이언트 신원증명이 윈도우계정이라는 것이 떠올라 해법을 찾게 되었다.

 

간혹, wsHttpBinding 바인딩을 사용하는 WCF 서비스를 개발할 때, 본인의 PC에서는 잘 동작하던 것이 서비스를 원격 서버로 이동하면 보안문제가 발생하는 것을 경험해 보았을 것이다. 이것이 바로 wsHttpBinding가 기본적으로 Message 보안 모드에 윈도우 계정에 의한 클라이언트 자격증명이 이뤄지기 때문에 그런 것이다. 즉 로컬 환경에 서비스와 클라이언트가 모두 존재할 경우 윈도우 인증이 자동으로 가능해지기 때문에 로컬에서는 문제가 없었던 것이다. 필자 역시 과거 이러한 경험을 반추하여 위와 같은 해법을 찾게 된 것이다.

 

결국 wsHttpBinding에 기본 모안보드를 변경하기 위해 명시적으로 다음과 같이 설정을 하고 테스트를 해 보니 basicHttpBinding과 동일하게 10번 모두 정상 호출되었다.

<wsHttpBinding>
     <binding name="myBinding">
         <security mode="None"></security>
      </binding>
</wsHttpBinding>

 

즉 결론은 동일하다.
: 세션을 사용하지 않는 바인딩 환경(basicHttpBinding, wsHttpBinding 모두)에서는 maxConcurrentSessions 제한 값이 무의미하다.

(참고: wsHttpBinding 일 경우, 보안모드를 명시적으로 None로 설정해야 보안세션이 사용되지 않는다.) 

 

 

'.NET Framework' 카테고리의 다른 글

Caching in WCF  (2) 2014.02.21
ASP.NET SignalR  (0) 2013.12.06
SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06

DIY 로...

Posted in 일상 // Posted at 2013. 11. 18. 10:37
728x90

몇달 전 방문 손잡이를 직접 교체하면서 DIY의 사소한 기쁨을 맛본차에 욕실의 세면대 수전이 고장나서 이 역시 DIY 하기로 했다. 손보는 김에 세면대의 물마개가 고장난 채 몇달동안 방치되었는데 이 참에 모두 해결하기로 하고 철물점가서 원홀 방식의 수전과 자동 폽업 그리고 스플라이 겸용 몽키를 사들고 와서 교체했다.

 

세상 모든 구조물에 표준의 중요성을 느끼게 된 또 하나의 경험이라 할 수 있다.

폽업의 크기와 수전의 너트 크기는 모두 통일되어 있는 듯 하다. 또한 폽업의 기둥이 분리 가능한 구조로 되어 있어 배수구와의 거리가 맞지 않을 경우 분리된 기둥만 교체(이것은 모듈화라 하겠다)하면 되는 구조로 되어 있었다.

 

세면대 수전에서 표준과 모듈화의 유용함을 느끼게 될 줄이야... 음...

어쩔 수 없는 전산쟁이라 하겠다. ㅎㅎ

 

http://navercast.naver.com/contents.nhn?rid=112&contents_id=35754

 

'일상' 카테고리의 다른 글

아버지는 잊어버린다  (0) 2013.11.25
6년 반 전에...  (0) 2013.11.22
사옥 이전  (1) 2013.10.07
미역 선물  (0) 2013.10.04
인생 노선  (2) 2013.08.02

knockout.js

Posted in 모바일/Javascript // Posted at 2013. 11. 14. 14:16
728x90

방안의 코끼리 마냥 본다본다 하던게 오래되어, 이제야 맘먹고 제대로 살펴 보게 되었다.

 

'넉아웃' 이라는 흥미로운 이름의 자바스크립트 라이브러리인데, 이놈 물건이네!

 

동적 웹 UI 구현의 생산성과 안정성이 매우 향상될 것으로 보인다.

점점 훌륭한 오픈소스 라이브러리들이 출현하고, 개발자는 점점 코어 구현에서 멀어지고...

 

빠르고 안정적이면서 유연성마저 좋은 스마트한 응용프로그램을 쉬이 개발할 수 있는 장점 이면에는, 소프트웨어 구조 설계와 코어 구현의 고민과 경험을 빌려 쓰게 되면서 궁극의 개발 스킬 향상을 저해하는 면이 있다 하겠다. 현 시대가 빠른 출시, 잦은 변경, 복잡한 비즈니스에 초점이 맞춰 지다 보니 이러한 라이브러리에 의존하는 것이 당연하게 받아 들여지지만 한편으로는 비즈니스가 아닌 소프트웨어의 코어 수준의 고민은 여전히 필요한게 아닌가 한다.

 

그럼... 잡설은 집어치우고.

 

knockout.js의 실행 흐름을 도식화 해봤다.

 

 

 

MVVM (모델, 뷰, 뷰모델) 패턴을 기반으로 뷰를 위한 뷰모델 계층이 존재하고, 이 뷰모델의 변경을 관찰하여 뷰와 자동 상호작용이 가능토록 구현된 라이브러리이다.

 

뷰모델 역할을 하는 객체를 knockout.js에 바인딩하면 knockout.js는 뷰모델 객체의 프러퍼티를 관찰/감시하게 된다. 만일 관찰 대상으로 지정된 뷰 모델 객체의 프로퍼티가 변경될 경우 연결된 HTML Element로 변경 내용이 전파된다. 또한 HTML Element에 연결된 프로퍼티는 HTML Element의 이벤트에 반응해 자동으로 값이 변경되기도 한다.

 

대부분의 예제에서 HTML Element의 이벤트에 기반해 뷰모델의 프로퍼티가 변경되는 것을 보여주지만, HTML Element에 의존없이 뷰 모델 프로퍼티만 변경되어도 자동으로 뷰가 갱신되도록 할 수 있어, 매우 참신하다.

 

간단한 예를 들어, 1초마다 시간을 갱신시켜 주는 코드를 다음과 같아 쉽게 구현할 수 있다.

데이터 변경에 따라 UI를 갱신하는 따위(?)의 코드는 존재하지 않는다.

<p>Time: <span data-bind="text: time" /></p>

 

 

   var viewModel = { time : ko.observable(new Date().toString()) };
   
   function onLoad(){             
       ko.applyBindings(viewModel);
       setInterval(clock,1000);
   }
   
   function clock(){
       viewModel.time(new Date().toString()); 
   }
  
   window.addEventListener('load', onLoad);

 

knockout.js 공식 사이트에 보면 주요 예제가 일목요연하게 정리되어 있어 적절한 활용처를 판단하는데 도움을 얻을 수 있다.

: http://knockoutjs.com/examples/

 

그리고 닷넷 기술과 함께 knockout.js를 활용하여  SPA(Single Page Application)를 구현한 좋은 사례를 다음의 MSDN 매거진에서 확인할 수 있다.

: http://msdn.microsoft.com/ko-kr/magazine/dn463786(en-us).aspx

 

 

'모바일 > Javascript' 카테고리의 다른 글

[AngularJS] MVC Pattern in Angular  (0) 2016.07.10
[AngularJS] 일주일 탐방기  (0) 2016.07.06
OOP in Javascript  (0) 2013.08.16
함수에 대하여  (5) 2013.07.31
null과 undefined 그리고 NaN  (0) 2013.07.29

SQL Double Split

Posted in .NET Framework // Posted at 2013. 11. 13. 18:38
728x90

SQL에서 Split 함수 구현은 쉽게 찾을 수 있다. 그런데 프로젝트 진행 중, 내가 필요한 것은 더블 스플릿이다.

(더블 스필릿이라는 공식 명칭은 없다. 그냥 내 맘대로 붙여 먹은 이름이니 어디가서 들먹이면 안됨!)

 

즉 하나의 구분자로 문자들을 분리하고, 분리된 문자에는 또 다시 구분자가 있어 다시 스필릿 해야 하는 구조.

대략 다음과 같이...

 

: key1=value1,key2=value2,key3=value3

 

위 예시에서 첫번째 구분자는 콤마(,)이고 두번째 구분자는 이꼴(=)이다.

이 값을 차례대로 분리해서 테이블 형태로 반환하고 싶은 것이다.

 

과거 훌륭한 DBA로 부터 수급한 싱클 스플릿 함수 구현체에, 꾸역꾸역 더블 스플릿이 가능하도록 끼워넣었다.

사소하게 기뿌다 ^^;

 

ALTER FUNCTION [dbo].[UFN_DoubleSplit] (@STRMORE AS VARCHAR(8000),@STRDELIMETER1 AS VARCHAR(10),@STRDELIMETER2 AS VARCHAR(10)) 
RETURNS @RETURN_TABLE TABLE
(
    idx int identity(1,1)
,   strVALUE1 VARCHAR(500)
,   strVALUE2 VARCHAR(500)
)
AS
BEGIN
DECLARE
    @NINDEX   INT
, @NINDEX2  INT
,   @DEL_LENGTH INT
, @SUBSET  VARCHAR(100)
,   @STRVALUE1  VARCHAR(100)
,   @STRVALUE2  VARCHAR(100)
 
SET @DEL_LENGTH = LEN(@STRDELIMETER1)
 
WHILE LEN(@STRMORE) > 0
    BEGIN
   
        SET @NINDEX = CHARINDEX(@STRDELIMETER1, @STRMORE)
 
        IF (@NINDEX = 0)
        BEGIN      
   SET @NINDEX2 = CHARINDEX(@STRDELIMETER2, @STRMORE)
   SET @STRVALUE1 = SUBSTRING(@STRMORE, 0, @NINDEX2)
   SET @STRVALUE2 = SUBSTRING(@STRMORE, @NINDEX2 + 1, LEN(@STRMORE))      
   INSERT @RETURN_TABLE (strVALUE1,strVALUE2) VALUES (@STRVALUE1,@STRVALUE2)
   RETURN
        END       
        ELSE IF (@NINDEX = 1)
        BEGIN
   SET @STRMORE = SUBSTRING(@STRMORE, @DEL_LENGTH + 1, LEN(@STRMORE))
   CONTINUE
        END
 
        SET @SUBSET = SUBSTRING(@STRMORE, 0, @NINDEX)
        SET @NINDEX2 = CHARINDEX(@STRDELIMETER2, @SUBSET)
        SET @STRVALUE1 = SUBSTRING(@SUBSET, 0, @NINDEX2)
  SET @STRVALUE2 = SUBSTRING(@SUBSET, @NINDEX2 + 1, LEN(@SUBSET))                                               
        INSERT @RETURN_TABLE (strVALUE1,strVALUE2) VALUES (@STRVALUE1,@STRVALUE2) 
                 
        SET @STRMORE = SUBSTRING(@STRMORE, @NINDEX + @DEL_LENGTH, LEN(@STRMORE) - @NINDEX)   
    END
RETURN
END

 

 

2014.02.21 업데이트>>

이 글을 작성하고 난 뒤 'Double Split'를 프로젝트에 적용해서 잘 사용하던 중, Split 해야 할 또 하나의 값이 추가되어 버렸다.

 

즉,  Double Split 기준의 매개변수인 'key1=value1,key2=value2,key3=value3' 형태가 다음처럼 바뀌었다.

: key1=value1&value11,key2=value2&value22,key3=value3&value33

 

그렇다면, Triple Split를 다시 구현할 것인가???

이런.. 유연성이 전혀 없잖어. 또 다른 기준 값이 추가되면 역시 힘겹게 수정해야 한다. 사실 이 글에서 제시한 Split 구현이 그리 깔끔하거나 쉽게 확장가능한 코드가 아니다.

 

결국 XML형태로 매개변수를 입력 받고 파싱해서 테이블로 반환하기로 했다. 구현이 훨씬 깔끔해져 나중에 수정을 하려고 해도 쉽게 가능해 진다.

 

Double Split 이여 안녕~~

 

CREATE FUNCTION [dbo].[UFN_ConvertFromXmlToTable] (@XMLString AS NVARCHAR(MAX))
RETURNS @RETURN_TABLE TABLE
(
    idx int identity(1,1)
,   strValue1 VARCHAR(500)
,   strValue2 NVARCHAR(100)
,   intValue  BIGINT
)
AS
BEGIN
 declare @xml xml = cast(@XMLString as xml)
    
 INSERT @RETURN_TABLE (strValue1,strValue2,intValue)
  select
     x.value('data(@strValue1[1])', 'VARCHAR(500)')
   , x.value('data(@strValue2[1])', 'NVARCHAR(100)')
   , x.value('data((text())[1])', 'BIGINT')
   from
    @xml.nodes('/s') x(x)

RETURN
END

  

 그리고 이렇게 활용하면 된다.

SELECT * FROM dbo.[UFN_ConvertFromXmlToTable]('<s strValue1="1" strValue2="11">1000</s><s strValue1="2" strValue2="22">2000</s><s strValue1="3" strValue2="33">3000</s>')

 

 

'.NET Framework' 카테고리의 다른 글

ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06

Optimizing IIS Performance

Posted in .NET Framework // Posted at 2013. 11. 7. 15:27
728x90

다음 글은 MSDN의 다음 글을 발번역한 것이다.

 

> Optimizing IIS Performance

 

IIS 7.0을 기준으로 하고 있지만, 생뚱 맞게 ASP 가이드가 웬말이냐...

어쨋던 ASP.NET에서도 유사한 설정이 있으니 참고할만하며, ASP.NET 가이드 부분은 실질적 도움이 된다.

 

 

 

 

 

IIS 성능 최적화

 

IIS 성능 향상을 위한 구성 옵션 적용

IIS는 성능에 영향을 미치는 다양한 구성 매개변수를 노출한다. 이 토픽에서는 이러한 매개변수 몇 가지를 설명하고 IIS 성능 향상을 위해 필요한 매개변수 설정의 일반적인 지침을 제공한다.

 

* 로그는 필수적인 정보만 저장하거나 전혀 사용하지 않도록 하라.

프로덕션 환경에서는 IIS 로깅을 최소화 하거나 비활성화 해야 한다. 다음 단계를 따르라.

단계 요약: IIS 관리자>해당 사이트>로깅>사용 안 함 설정

 

 

* 프로덕션 환경에서는 IIS ASP 디버깅 기능을 비활성화 시켜라.

프로덕션 환경에서는 IIS ASP 디버깅을 사용하지 말아야 한다. 다음 단계를 따르라.

 

단계 요약: IIS 관리자> 사이트> ASP> 컴파일> 디버깅속성> 서버 쪽 디버깅 사용, 클라이언트 쪽 디버깅 사용 항목을 false로 설정

 

ASP.NET 응용프로그램 또는 웹 서비스의 디버깅 비활성화는 web.config 파일의 <compliation debug=”false” />로 설정

 

 

* 프로세서 당 ASP 스레드 제한 값을 조정하라.

ASP 스레드 제한 속성은 프로세서 당 생성할 수 있는 작업 스레드의 최대값을 나타낸다. 이 값을 프로세서 사용률이 적어도 50% 이상 충족할 때까지 값을 높여라. 이 설정은 웹 응용프로그램의 확장성과 전반적인 서버의 성능에 많은 영향을 미칠 수 있다. 이 값은 동시에 실행할 수 있는 ASP 요청의 최대 값을 정의하기 때문에 당신의 ASP 응용 프로그램이 외부 구성요소로 확장된 요청들을 하지 않는 한 기본 값을 유지해야 한다. 확장된 요청을 하는 경우라면, 값을 높이는 것이 좋다. 이렇게 하면 서버가 더 많은 동시 요청을 처리하기 위해 더 많은 스레드를 만들 수 있다. 기본 값은 25이다. 이 속성의 최대 권장 값은 100이다. 다음 단계를 따르라.

 

단계 요약: IIS 관리자>서버>ASP>제한속성>프로세서 당 스레드 제한 값을 원하는 값으로 설정(기본: 25)

 

IIS 7.0ASP 제한 설정에 대한 더 더 많은 정보는 다음 URL에서 확인

http://www.iis.net/configreference/system.webserver/asp/limits

 

노트1> 이 속성은 서버 레벨에서 적용할 수 있기 때문에, 값의 수정은 서버의 모든 웹 사이트에 영향을 미친다.

노트2> IIS 7.0의 이 속성은 IIS 6.0 ASPProcessorThreadMax ASP Metabase 세팅을 대체한다

 

 

* ASP 큐 길이 조정하라.

이 값을 설정하는 이유는 ASP 요청 큐가 찼을 때 HTTP 503(Server Too Busy)가 반환되는 빈도를 최소화 하면서 좋은 응답성을 보장하기 위함이다. 만일 큐 길이가 너무 낮으면, 서버는 503 오류를 자주 뱉어 내게 될 것이다. 반면 큐 길이가 너무 높으면, 요청이 큐에 대기하는 동안 사용자는 서버가 응답하지 않는다고 인식할 것이다. 높은 트래픽 상황에서 큐를 주의 깊게 살펴보고 피크 타임의 대기 요청 수를 기록하고 이 값보다 높은 값으로 큐 길이를 설정해야 한다. 응답 시간을 보장하고 단기적인 스파이크를 처리하기 위해 큐를 사용하며 지속적인 예상치 못한 스파이크가 발생할 때 과부하를 방지하기 위해 이 값을 조정한다. 당신이 큐 길이 제한에 대한 데이터가 없는 경우, 총 스레드 큐의 일대일 비율로 설정하는 것을 권장한다. 예를 들어 4개의 프로세서 환경에서 프로세서당 ASP 스레드 수의 제한 값이 25일 경우 4 * 25 = 100으로 설정하고 여기서부터 출발하는 것이다. 큐 길이 조정을 위한 다음 단계를 수행하라

 

단계 요약: IIS 관리자>서버>ASP>제한 속성>큐 길이에서 원하는 값 지정

 

노트1> 이 속성은 서버 레벨에서 적용할 수 있기 때문에, 값의 수정은 서버의 모든 웹 사이트에 영향을 미친다.

노트2> IIS 7ASP 큐 길이 속성은 IIS 6.0 AspRequestQueueMax ASP Metabase 셋팅값을 대체한다.

 

 

* MaxPoolThreads 레지스터리 항목을 조정하라.

이 값은 CPU당 풀에 생성할 수 있는 스레드 수를 나타낸다. 풀 스레드는 들어오는 요청을 감시하고 처리한다. MaxPoolThreads 수는 ISAPI 응용프로그램에서 소비하는 스레드는 포함하지 않는다. 일반적으로 프로세서당 20개 이상의 스레드를 생성할 필요는 없다. 이 설정은 레지스터리 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\InetInfo\Parameters\ REG_DWORD 값으로 지정할 수 있으며 기복 값은 4로 설정되어 있다.

 

 

* WCF 서비스 트레이싱을 비활성화 시켜라.

프로덕션 서버에 WCF 구성 설정 편집기(SvcConfigEditor.exe)를 사용하여 트레이싱을 비활성화 시켜라.

이 툴에 대한 더 자세한 정보는 다음 url에서 확인

http://msdn.microsoft.com/en-us/library/ms732009.aspx

 

 

* IIS 7.0 통합모드를 위한 ASP.NET MaxConcurrentRequests를 구성하라.

IIS 7.0 통합모드에서 ASP.NET 2.0이 호스트 되는 경우, 스레드 처리가 클래식 모드나 IIS 6.0과는 다르게 처리된다. 이 경우 ASP.NET 2.0은 동시 실행 스레드 수 대신 동시 실행 요청 수를 제한한다. 동기 시나리오에서는 요청 수가 스레드 수와 동일하기 때문에 간접적으로 스레드 수를 제한할 것이다. 그러나 비동기 시나리오에서는 스레드 수 보다 훨씬 더 많은 요청이 있을 수 있기 때문에 요청수와 스레드 수가 많이 다를 수 있다. ASP.NET 2.0 IIS7.0 통합모드에서 실행될 경우, machine.config 파일의 httpruntime 요소의 minFreeThreads minLocalRequestFreeThreads 값이 무시된다. IIS 7.0 통합모드에서는 레지스터리의 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\2.0.50727.0에 있는 MaxConcurrentRequestsPerCPU 값에 의해 CPU당 동시 요청 수를 결정하게게 된다. 기본적으로 이 레지스터리 키는 존재하지 않으며 기본 값은 CPU 12개 이. net framework 3.5 SP1에는 aspnet.config 파일을 통해 IIS 응용프로그램 풀 구성을 할 수있는 2.0 바이너리에 대한 업데이트가 포함되어 있다. 이 파일은 통합모드에서만 유효하다.(IIS 6.0 혹은 클래식 모드일 경우 이 파일의 설정은 무시된다.) 이 새로운 설정 파일의 기본 값은 다음과 같다.

 

<system.web>

   <applicationPool maxConcurrentRequestsPerCPU="12" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/>

</system.web>

 

경험상으로 봤을 때, MaxConcurrentRequestsPerCPU 값은 5000 정도로 큰 값으로 설정해야 한다.

IIS 7.0 통합모드에서는 machine.config 파일의 processModel 요소의 maxWorkerThreads, maxIOThreads 설정은 본질적으로 실행중인 요청 수를 제어하는데 사용되지 않는다. 그러나 여전히 ASP.NET에 의해 사용되는 CLR 스레드 풀의 사이즈를 제어하는 데는 사용된다.

machine.config 파일의 processModel 섹션의 autoConfig 설정이 true(기본 값)일 경우, 응용프로그램 풀에 (논리적인) CPU당 최대 100까지의 스레드(MaxWorkerThreads)를 생성할 수 있다. 2 듀얼 코어 CPU일 경우에는 400개가 된다. This should be sufficient for all but the most demanding applications.

 

IIS 7.0 6.0 ASP.NET 스레드 활용법을 위한 더 많은 정보는 다음 사이트에서 확인 하시길.

http://msdn.microsoft.com/en-us/library/ee377050%28v=bts.10%29.aspx

 

 

* HTTP 압축을 사용하라.

대역폭을 보다 효율적으로 사용하고 싶다면 IIS HTTP 압축을 활성화하라. 압축을 사용하면 콘텐트가 로컬 저장소에 있거나 UNC 리소스이거나 관계없이 (압축을 허용하는) 브라우저와 IIS 간 더 빠른 전송 효과를 볼 수 있다.

(단계 요약: IIS 관리자>서버(또는 사이트)>압축>원하는 압축 옵션 설정)

 

 

 

'.NET Framework' 카테고리의 다른 글

maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05

클라이언트의 동시 연결 수(maxconnection)

Posted in .NET Framework // Posted at 2013. 11. 6. 16:41
728x90

응용프로그램의 성능 저하 문제는 사소하게 간과한, 몇 가지 이유로 유발되는 경우도 있다.

여기서 언급하고자 하는 것도 그러한 것 중 하나이다.

 

바로 클라이언트의 동시 연결 수 제한에 따른 성능 저하 현상이다. 닷넷 응용프로그램에서 원격 호스트와 통신할 때 적용되는 동시 연결 수의 제한인데, 특별히 값을 지정하지 않는 경우(기본 값) 2개의 연결만 허용하게 된다.

 

보편적인 (단순한) 환경이라면 이 제한 값은 문제 되지 않을 것이다. 하지만 매우 바쁜 호스트가 멀티 쓰레드 환경에서 원격시 서버와 통신을 빈번히 하는 경우라면 예기가 달라진다.

 

특히 두 호스트가 Server to Server 방식이라면 이 제한 값은 문제될 소지가 높아진다. 

 

예를 들어, A 서버가 클라이언트의 요청을 받아서 작업을 수행하는데 이 작업 중에서 원격지 서버와 통신하는 부분이 있다고 가정해보자. 이때 A 서버는 그 자체가 서버 응용프로그램이기 때문에 멀티 쓰레드 환경에서 요청을 처리하게 되는 경우(이 말은 꼭 개발자가 직접 작성한 멀티 쓰레드 프로그램만을 말하는 것은 아니다. 프로그램이 얹혀지는 호스팅 환경 자체에서 풀을 기반으로 동시 처리를 하게 되는 경우도 포함한다. 이러한 사례는 웹 응용프로그램이 대표적이다.)가 많으며 요청이 과도하게 몰릴 경우 원격 서버와 통신이 그 만큼 빈번하게 이뤄지게 된다. 이런 환경에서 최대 동시 연결 수가 2개 밖에 되지 않는다면 병목 현상으로 이어질 확률이 높다. 특히 원격지 서버의 작업 수행 시간이 길어진다면 연결을 위한 대기 시간은 점점 늘어나게 될 것이다.

 

더욱 문제는 이 값의 설정이 기본적으로 노출되어 있지 않다는 데 있다. 닷넷 응용프로그램의 config 파일에는 몇 가지 기본 설정들이 자동으로 포함되는데 이 설정 값은 그렇지 않아, 기본 값인 2가 사용되도록 되어 있다.

 

이러한 이유로 많은 개발자들이 이 값의 설정을 간과하게 되며, 심지어 이러한 설정이 있는지 모르는 경우도 많다. 복잡한 환경에서 서버의 응답 속도가 느려 진다는 것은 참으로 많은 구간을 확인해야 하는 일이다. 네트워크, 시스템, DB, 응용프로그램 등 다양한 구간에 가능성을 두고 분할 정복해야 한다.

 

특히 Server to Server 통신 환경에서, 원격 서버의 응답 지연 현상이 반드시 원격 서버만의 문제가 아닐 수 있다는 가능성도 열어 두길 바란다. 앞서 시나리오라면 클라이언트의 설정 값의 변경만으로도 성능 개선이 많이 이뤄질 수도 있다. (물론 원격 서버의 응답시간 자체는 허용 범위 내에 있다는 가정을 둔다.)

 

이런 시나리오를 간단히 테스트 해 보자. 

 

* 원격 서버: ASP.NET MVC Web API 로 기본 Getter 구현. 의도적으로 응답을 지연 시킨다.

 

* 클라이언트: 콘솔 어플리케이션으로 멀티 쓰레드로 원격 서버의 Getter 호출(대략 다음의 코드)

   for (int i = 0; i < 20; i++)
   {
           Thread thread = new Thread(new ThreadStart(Start));
            thread.Start();  //원격 서버 호출하는 스레드 시작
    }

 

 

1. 기본 값을 사용한 경우

특별히 maxconnection 설정을 추가하지 않은 경우, netstat 나 TCPView 프로그램으로 연결 수를 확인해 보면 20개의 요청이 동시에 이뤄지지만, 연결 수는 단 2개만 존재하는 것을 확인할 수 있다.

 

 

 

2. maxconnection을 20으로 설정한 경우

<system.net>
    <connectionManagement>
       <add address="*" maxconnection="20"/>
    </connectionManagement>
</system.net>

 

클라이언트 프로그램의 설정 파일에 위와 같이 maxconnection을 설정하고 다시 확인해 보면 20개의 요청만큼 동시 연결이 되어 있는 것을 확인할 수 있다.

 

 

앞서 2개의 연결만으로 20개의 원격 호출을 처리하는 것보다, 20개의 연결으로 20개의 원격 호출을 병렬적으로 처리하는 것이 훨씬 응답성을 높일 것이다.

 

참고로 이 값의 정해진 규칙은 없다. 응용프로그램의 시스템 환경과 사용량을 파악해 적절한 값을 지정해야 할 것이다. 다만 Server to Server 방식에서 멀티 쓰레드로 원격 통신이 빈번한 환경이라면 기본 값인 '2'는 너무 작은 값임에는 분명하다 하겠다. 

 

 

'.NET Framework' 카테고리의 다른 글

SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05
Custom Configuration  (6) 2013.08.26

비동기 프로그래밍에서의 메모리 누수

Posted in .NET Framework // Posted at 2013. 11. 6. 11:24
728x90

응용프로그램의 성능, 확장성 향상의 이유로 비동기 프로그래밍 방식이 자주 사용되곤 한다.

 

닷넷 프레임워크에서도 스레드(전용 스레드 or 스레드 풀) 혹은 비동기 프로그래밍 모델을 활용해 비동기 작업을 수행할 수 있다.

 

여기서 비동기 프로그래밍 모델을 APM(Asynchronous Programming Model)이라 칭하는데, 닷넷 프레임워크의 I/O 관련 클래스들은 모두 이 모델을 이용할 수 있도록 설계되어 있다.

 

 

I/O를 위한 동기 방식 메서드 이름과 동일한 BeginXXX, EndXXX 쌍의 비동기 메서드가 제공되는데, 다음과 같은 I/O 클래스들에서 이를 활용할 수 있다.

 

System.Net.WebRequest

System.IO.Stream

System.Data.SqlClient.SqlCommand

System.Net.Sockets.Socket

 

필자의 회사에서 운영하는 시스템도 많은 곳에시 비동기 패턴을 이용하고 있다. 그 중 한 예로, 여러 시스템이 혼재되어 하나의 서비스를 완성하는데 이때 각 시스템의 구간 안정성 현황을 파악하기 위해 원격 서버로 로그를 전송하는 로직이 비동기로 구현되어 있다.

 

그런데 이 로그는 실제 비지니스와는 무관한 부분이라(크로스 커팅 기능), 종속성을 배제하기 위해 로그 서버는 단방향 서비스로 구현하고 이를 소비하는 클라이언트는 APM 방식으로 구현되어 있다.

 

그런데 문제는 로그 저장의 성공/실패에 관심을 두지 않는다는 것이, 비동기 호출 후 콜백을 받지 않는 구현으로 이어졌다는 것이다. 즉 BeginXXX만 호출하고 EndXXX는 구현하지 않은 것이다.

 

사용량이 많지 않는 환경에서는 이것이 그리 큰 문제가 되지는 않는다. 하지만 필자의 시스템 환경은 Server to Server 방식으로 로그서버를 소비하는 클라이언트 역시 서버 응용프로그램이며 이 서버 응용프로그램에 수많은 요청이 동시에 몰리면 메모리 누수는 점차 누적되어 프로그램이 심각한 타격을 입을 수 있다는 것이다.

 

다음의 글에서 밝히고 있듯이, 비동기 호출 결과에 관심이 없다고 해서 EndXXX를 호출하지 않아도 된다는 것은 아니니 주의해야 할 일이다.

 

개발자는 EndXXX 메서드를 꼭 호출해야 한다. 그렇지 않으면 리소스가 누수되기 때문이다. 일부 개발자는 데이터 장치에 자료를 저장하기 위해서 BeginXXX를 호출하고, 이후의 결과 값이나 진행 상태에 관심이 없다면 EndXXX를 호출하지 않는 경우를 종종 봐왔다. 하지만 EndXXX를 호출해야 하는 이유는 두 가지가 있다.

 

첫째 CLR은 이 리소스들을 EndXXX 메서드가 호출될 때까지 유지시킨다. 그리고 EndXXX 메서드가 결국 호출되지 않는다면 이 리소스들은 계속 반환되지 않다가 프로세스가 종료될 때에 반환된다. 둘째, 비동기 작업을 초기화할 때 실제로 개발자는이 작업이 성공할 것인지 알지 못한다. 성공유무를 확실하게 확인하는 방법은 EndXXX 메서드를 호출하는 것이며, 이 메서드를 호출함으로써 결과 값이 정상적으로 반환되는지 아니면 예외가 발생하는지 알 수 있다.

- CLR via C#

 

 

'.NET Framework' 카테고리의 다른 글

Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05
Custom Configuration  (6) 2013.08.26
간단한 C# 문법 Quiz  (1) 2013.07.11

[HTML5 Game] Shooting Down

Posted in 모바일/HTML5 // Posted at 2013. 10. 16. 22:37
728x90

앞서 전개한 글들에서 충돌처리, 총알발사, 오브젝트 이동 처리 등을 구현해 보았다.

이번 글에서는, 이러한 개별 개념을 조합하여 총알로 적(enemy) 비행체를 격추하는 샘플을 구현해 보자.

 

간략한 시나리오

- 적(enemy) 비행체는 캔버스 상단에서 좌/우로 방향을 바꿔가며 계속 이동한다.

- 키보드의 스페이스 키를 누르면 총알이 발사된다.

- 총알이 적 비행체에 명중하면 폭발한 듯한 이미지 효과를 준다.

 

격추라는 시나리오에만 충실하기 위해 아군 비행기는 등장시키지 않았으며, 총알의 출발 위치도 캔버스 하단 중앙에서만 출발하도록 한다. 또한 지속적인 테스트를 위하여 적 비행체가 격추되어도 충돌을 표현하는 이미지 효과만 줄 뿐 비행체는 계속 살아서 움직이도록 할 것이다.

 

먼저 적 비행체와 총알, 그리고 충돌 처리를 위한 객체 기반을 작성할 것인데 그전에, 주요 코드부터 간략히 살펴보자.

 

총알 발사

이전 글에서는, 과도한 연사 속도를 조절하기 위해 FPS를 기반의 조절값을 사용한 반면 여기서는 총알 발사의 전/후 시간을 속도 조절의 기준 값으로 사용했다. 즉 직전 총알 발사 시간과 현재 총알 발사 시간의 간격이 0.1초 이상 되어야만 새 총알이 배열에 추가되도록 처리한다. 그리고 총알의 발사 위치는 캔버스의 하단 중앙으로 설정한다.

if(Date.now() - this.lastShootTime >= 100){        
   this.bullets.push({x: (this.canvasSize.width / 2)
                       - (this.gameAssets.bullet.width / 2), y: this.canvasSize.height});
   this.lastShootTime = Date.now();  
  } 

 

 

오브젝트 이동

적 비행체와 총알의 이동을 위해 좌표값을 변경하는 코드이다. 게임 루프의 update() 메서드에서 매 프레임마다 호출하는 부분으로, 총발 위치는 위쪽 방향으로 이동하도록 Y 좌표값을 변경하고 적 비행체는 캔버스의 좌/우우를 계속해서 왔다갔다 하도록 방향값을 기준으로 X 좌표값을 변경시켜준다.

for(var i = 0; i < this.bullets.length; i++){    
   this.bullets[i].y -= this.speedBullet; 
}      

 

if(this.directionEnemy == 1
        && this.enemyPostion.x + this.gameAssets.enemy.width  > this.canvasClientRect.right)

{
   this.directionEnemy = -1;
}  


if(this.directionEnemy == -1
        && this.enemyPostion.x + 5 < this.canvasClientRect.left)

{   
   this.directionEnemy = 1;
}
    
this.enemyPostion.x += (this.directionEnemy) * 5; 

 

 

오브젝트 그리기 및 충돌처리

객체의 마지막 코드로, 게임 오브젝트들을 캔버스에 그린다. 게임 루프의 display() 메서드에서 매 프레임마다 호출하는 부분으로, 먼저 캔버스의 모든 내용을 지우고 적 비행체를 그린다. 다음으로 총알을 배열 수 만큼 그려주는데 총알이 캔버스 영역 밖으로 이동했다면 배열에서 제거하고 루프를 빠져나간다. 그리고 총알과 적 비행체와의 충돌처리를 통하여 격추되는 이미지를 그려줄지 판단한다. 

this.canvasContext.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height); 
  
this.canvasContext.drawImage(this.gameAssets.enemy, this.enemyPostion.x, this.enemyPostion.y);
       
for(var i = 0; i < this.bullets.length; i++){
   if(this.bullets[i].y <= 0) {
      this.bullets.splice(i,1); continue;
   }
   
   this.canvasContext.drawImage(this.gameAssets.bullet, this.bullets[i].x, this.bullets[i].y);
   
   if(this.bullets[i].x +  this.gameAssets.bullet.width > this.enemyPostion.x 
       && this.bullets[i].x < this.enemyPostion.x + this.gameAssets.enemy.width    
       && this.bullets[i].y > this.enemyPostion.y 
       && this.bullets[i].y < this.enemyPostion.y + this.gameAssets.enemy.height)
    {           
      this.canvasContext.drawImage(this.gameAssets.pow, this.enemyPostion.x, this.enemyPostion.y);
    }                
}

 

 

여기까지 해서 객체기반 작성이 완료되었다. 나머지 코드는 각종 초기화 및 이미지 리소스 다운로드, 위의 객체를 기반으로 게임 루프를 구현하는 것이다. 다음 코드는 객체를 포함한 전체 소스이다.

 function ShootingDownObj(gameAssets,canvasElement){
    this.canvasSize = {width: canvasElement.width, height: canvasElement.height};
    this.canvasContext = canvasElement.getContext('2d');
    this.canvasClientRect = canvasElement.getBoundingClientRect();     
    this.enemyPostion = {x: 10, y:10}; 
    this.gameAssets = gameAssets;
    this.bullets = [];
    this.speedBullet = 15;   
    this.directionEnemy = 1;
    this.lastShootTime = Date.now();
 
    this.addBullet = function(){   
        if(Date.now() - this.lastShootTime >= 100){        
         this.bullets.push({x: (this.canvasSize.width / 2)
             - (this.gameAssets.bullet.width / 2), y: this.canvasSize.height});
         this.lastShootTime = Date.now();  
        }  
    }
 
    this.movePosition = function(){
        for(var i = 0; i < this.bullets.length; i++){    
           this.bullets[i].y -= this.speedBullet;
        }      
        if(this.directionEnemy == 1
          && this.enemyPostion.x + this.gameAssets.enemy.width  > this.canvasClientRect.right){
            this.directionEnemy = -1;
        }  
        if(this.directionEnemy == -1
         && this.enemyPostion.x + 5 < this.canvasClientRect.left){   
            this.directionEnemy = 1;
        }
    
        this.enemyPostion.x += (this.directionEnemy) * 5;   
    }
   
    this.shotBullets = function(){  
       this.canvasContext.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height); 
       this.canvasContext.drawImage(this.gameAssets.enemy,
                                                                        this.enemyPostion.x, this.enemyPostion.y);
       
      for(var i = 0; i < this.bullets.length; i++){
          if(this.bullets[i].y <= 0) {
             this.bullets.splice(i,1);
             continue;
          }
   
          if(this.bullets[i].x +  this.gameAssets.bullet.width > this.enemyPostion.x 
           && this.bullets[i].x < this.enemyPostion.x + this.gameAssets.enemy.width    
           && this.bullets[i].y > this.enemyPostion.y 
           && this.bullets[i].y < this.enemyPostion.y + this.gameAssets.enemy.height){           
            this.canvasContext.drawImage(this.gameAssets.pow,
                                                         this.enemyPostion.x, this.enemyPostion.y);
          }           
            this.canvasContext.drawImage(this.gameAssets.bullet, this.bullets[i].x, this.bullets[i].y);
       }    
    } 
}

var fps = 30;
var canvasElement;
var gameContext; 
var shootingDownObj;   
var gameAssets;  
var currentAssetImageLoadCount = 0;    
var isKeyDown = [];

 

function init(){
  canvasElement = document.getElementById('GameCanvas');
  gameContext = canvasElement.getContext('2d');
   
  var bulletImage = new Image();  
  bulletImage.src = 'image/bullet.png';       
  bulletImage.onload = onAssetImageLoadComplete; 
 
  var enemyImage = new Image();  
  enemyImage.src = 'image/enemy.png';       
  enemyImage.onload = onAssetImageLoadComplete; 
 
  var powImage = new Image();  
  powImage.src = 'image/pow.png';       
  powImage.onload = onAssetImageLoadComplete;  
 
  gameAssets = {bullet: bulletImage, enemy: enemyImage, pow: powImage};     
}

 

function onAssetImageLoadComplete(){ 
  if(++currentAssetImageLoadCount >= 3){  
    shootingDownObj = new ShootingDownObj(gameAssets,canvasElement);      
    setInterval(gameLoop, 1000 / fps);
  } 
}

 

function gameLoop(){
  update();
  display();
}

 

function update(){   
  if(isKeyDown[32]){      
    shootingDownObj.addBullet();
  } 
  shootingDownObj.movePosition();
}

 

function display(){
  shootingDownObj.shotBullets();

}

 

function onKeyDown(e){  
  isKeyDown[e.keyCode] = true;   
}

 

function onKeyUp(e){
  isKeyDown[e.keyCode] = false;
}

 

window.addEventListener("load", init, false);
window.addEventListener("keydown",onKeyDown,false);
window.addEventListener("keyup",onKeyUp,false);

 

 

 

브라우저를 통해 확인해보면, 다음 그림과 같이 총알이 명중하면 폭발한듯한 이미지 효과를 확인할 수 있다.

 


 


 

 

'모바일 > HTML5' 카테고리의 다른 글

[HTML5 Game] Sound Effect  (2) 2013.10.11
[HTML5 Game] Firing Bullet  (0) 2013.10.08
[HTML5 Game] Moving Object Ⅱ  (1) 2013.10.02
[HTML5 Game] Moving Object  (0) 2013.10.02
[HTML5 Game]Calculating FPS  (0) 2013.09.30