SimpleIsBest.NET

유경상의 닷넷 블로그

닷넷의 발견: WinForm의 Shown 이벤트

by 블로그쥔장 | 작성일자: 2006-12-08 오전 1:06:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.

닷넷 프레임워크 2.0에는 새로이 추가된 것이 많이 있습니다. 가장 많이 변한 것이 ASP.NET 으로서 그만큼 공부해야 할 것이 많지요. WinForm 역시 상당 부분 변화되었고, 추가된 기능들이 많습니다. 그 외에도 닷넷 프레임워크 2.0에서는 상당히 유용하면서도 주목 받지 못한 것들이 많이 있는데... 오늘 그 중 하나에 대해 이야기 해볼까 합니다.

Discovery : WinForm Shown event

닷넷 프레임워크가 다루는 분야는 방대하다. 닷넷이 등장하기 전까지 WIN32 API와 더불어 다양하게 제공되었던 SDK 들, COM 객체들 등등이 대부분 닷넷 프레임워크라는 개발 플랫폼 하에 집대성 되어 있다고 해도 과언은 아닐 것이다. 물론 닷넷 프레임워크로 Windows 운영체제의 모든 프로그래밍을 다 할 수 있다는 것은 아니다. 하지만 대부분의 어플리케이션에서 요구하는 기능들은 닷넷 프레임워크로 커버할 수 있는 것 역시 부정할 수 없는 사실일 것이다.

닷넷 프레임워크의 중요한 요소로서 WinForm 이라 불리는 데스크톱 윈도우 어플리케이션 개발 프레임워크가 있다. 아직 닷넷 프레임워크가 모든 PC에 설치되지 않았기에, 아직은 큰 빛을 발하지 못하고 있지만, 기업에서 VB 6.0을 조금씩 대체해 가고 있는 효자 녀석일 뿐만 아니라 스마트 클라이언트라는 이름 하에 요즘 주가를 올리고 있는 녀석이다.

닷넷 프레임워크 2.0의 WinForm에는 새로이 추가된 기능들이 많이 있다. 대부분 새로이 추가된 컨트롤들에 관심을 갖게 되는 것이 인지상정(오오... 이런 어려운 한자 성어를 !!!)이겠지만... 필자는 1.1 버전에서 볼 수 없었던 하나의 이벤트에 유심히 관심을 갖게 되었다. WinForm에 관련된 프로젝트를 하다 보면 항상 걸리는 것 중 하나가 UI 폼(form)이 나타남과 동시에 초기화를 수행하는 부분이다.

대부분의 독자들도 필자와 마찬가지로 Load 이벤트에서 다양한 초기화를 수행할 것이다. 웹 서비스를 호출한다던가, DB를 액세스해서 Grid에 바인딩 하거나, 타이머를 설정한다 던가 등등... 하지만 Load 이벤트는 UX (User eXperience) 입장에서 봤을 때 몇 가지 문제를 가지고 있다.

Why not the Load event ?

Load 이벤트. 딱 이름만 봐서는 폼이 로드 되었다는 통지(notification) 이벤트이다. 그렇다면 당췌 '로드'란 어떤 시점을 말하는가? 이것을 이해하기 위해서는 닷넷과 WIN32 와의 관계를 쬐금 이나마 이해해야 한다. 이 이야기를 해야 한다면 필자의 고질적이며 불치로 진단된 "아는 척 하기 병"이 도지게 되고(진단은 태옹이라 불리는 의사가 했다), 글이 상당히 길어지게 되므로 꾸욱 참아 볼란다. 정말 궁금하다면 기다리지 말고 필자에게 항의성 메일을 날리면 2-3 주 후에 WinForm과 Win32와의 관계에 대해 졸라 아는 척하는 글이 올라올 것이다.

뻘 소리는 그만하고... '로드' 시점은 요약해서 폼 혹은 컨트롤이 생성되었지만 아직 화면 상에 표시 되지 않은 시점을 말한다. 이 시점에서 폼이나 컨트롤의 폰트를 수정한다든가 캡션 텍스트를 바꾼다든가 등등의 대부분의 작업이 허용되지만 여전히 화면상엔 아무것도 표시되지 않은 시점이라는 것을 잘 기억해 두어야 한다.

그렇다면 Load 이벤트에서 어떤 초기화 작업을 수행하는 것이 어떤 문제를 일으킬까? 뭐 문제라고 하니깐 별 거창한 것을 생각할 수도 있겠지만, "문제"라고 말하기에도 좀 뭐한 "사용자를 위한 배려"가 부족한 현상이 일어나곤 하기 때문이다.

어떤 업무 화면을 위한 UI 폼이 나타남과 동시에 디폴트로 데이터를 조회하여 데이터 그리드에 표시를 해야 한다고 가정해 보자. 우리도 "가오"가 있으니깐 클라이언트가 DB를 직접 액세스하는 구시대적인 아키텍처는 배제하고, SOA(Service Oriented Architecture) 적인 접근으로다가 웹 서비스 호출로 데이터를 조회해 온다고 생각해 보자. 이 때 초기 데이터 조회를 위해 어느 시점에서 웹 서비스를 호출하는 것이 좋을까?

일단 생성자는 아닌 것 같다. 왜냐고? 그냥... (설명하려면 골치 아프다... 일단 아닌 것 같은 느낌이 드니깐 아니라고 생각하고 넘어가자...) 그렇다면 Load 이벤트가 초기 데이터 조회를 위해 웹 서비스를 호출하기 가장 적당하게 보이지 않는가?

많은 경우에 Load 이벤트에서 초기 데이터를 조회해 오는 것은 결코 나쁜 선택이 아니다. 하지만 웹 서비스 서버가 허접한 J 모 플랫폼으로 구현되어 3초 정도 소요되는 졸라 구리다고 가정을 다시 해보자. Load 이벤트에서 웹 서비스를 호출하면 생성된 폼이 아직 화면에 나타나지 않은 상황이므로 사용자는 3초 동안 바탕화면을 뚫어져라 쳐다봐야만 할 것이다. 왜냐면 웹 서비스 호출 등의 초기화 작업이 완료될 때까지 Load 이벤트의 처리가 끝나지 않았으므로 WinForm 은 폼을 화면에 표시하지 않을 것이기 때문이다. UI 화면이 상당히 복잡하여 많은 수의 컨트롤을 사용한다면 이 화면이 나타나는데 소요되는 시간은 더욱 더 느리게 느껴질 것은 당연하다. 이제 Load 이벤트에서 긴(?) 시간을 요구하는 초기화를 수행하면 UX 관점에서 좋지 못하다는 것을 조금이나마 이해하겠는가?

사용자가 느끼는 프로그램의 성능은 일단 반응이 빨라야 한다. 그 반응이 사용자를 기만하는 것일지라도 무엇인가 반응이 있어야만 프로그램이 느리다는 감을 갖지 않는 것이 일반적이다. 그래서 우리가 그 삽질을 하면서 어떤 작업의 진행 상황을 보여주는 프로그래스 바(ProgressBar)니 애니메이션이니 하는 "반응"을 사용자에게 보여주는 것이 아닌가?

일단 사용자가 데이터를 조회하는 화면을 메뉴 상에서 선택했다면, 무조건 그 화면이 먼저 나타나야, 아니 우리말로 화면이 잽싸게 떠야 한다. 그리고 나서 초기 데이터를 조회하기 위해 웹 서비스를 호출을 하건 DB를 직접 액세스하건 해야 한다는 것이다. 이런 관점에서 Load 이벤트에서 상당한(?) 시간을 소요하는 웹 서비스를 호출하는 것은 최종 사용자에게 프로그램이 "느리다"는 느낌을 심어주기 딱 알맞은 이벤트가 되어 버리곤 한다.

Alternative

Load 이벤트 사용시 화면이 나타나는 것이 지연되는 것을 피하기 위해 필자가 썼던 방법은 타이머를 사용하는 것이었다. 뭐 대단한 것은 없고, 100ms (0.1초) 혹은 2~300 ms 짜리 타이머를 Load 이벤트에서 Enable 하고 타이머 핸들러에서 웹 서비스 호출과 같은 작업을 수행하도록 하는 것이다. 이렇게 하면 Load 이벤트는 빠르게 수행을 완료할 것이고 타이머 이벤트가 발생하기 전에 화면은 나타나곤 했기 때문이다.

아놔 무슨 병신도 아니고... 타이머를 쓰면서 까지 이 짓을 해야 하나 하는 생각도 많이 들었었고, 응가하고 밑 안 닦은 기분이 쒜~ 하게 드는 기분 나쁜 방법이었다. 게다가 어떤 화면은 매우 복잡해서 타이머 이벤트가 발생할 때까지 화면 상에 코빼기도 나타나지 않는 경우도 종종 있었다. 이런 줴길슨... -_-;

What's the Shown event ?

닷넷 프레임워크 2.0의 Form 클래스는 새로이 Shown 이벤트가 추가되었다. Shown 이벤트는 폼이 화면 상에 나타난 직후에 발생하는 이벤트이다. 앞서 언급했던 Load 이벤트의 문제(?)를 해결할 수 있도록 정확하게 필자가 원하는 시점에서 이벤트 핸들러가 수행될 수 있다.

Shown 이벤트가 수행되는 원리를 설명하자면 이렇다. WinForm은 Load 이벤트를 발생한 이후(Load 이벤트 처리가 모두 끝난 이후), 폼의 자식 컨트롤들을 모두 Invalidate 한다. 컨트롤에 대해 Invalidate 가 수행되면 컨트롤들은 자신을 화면에 갱신하게 된다(이 경우에는 최초 표시가 되겠지만...). 하지만 폼과 컨트롤에 대해 Invalidate는 동기적으로 작동하지 않는다. 다시 말해 Invalidate를 수행하는 메쏘드인 Control.Invalidate() 메쏘드는 컨트롤에게 화면을 갱신하라는 지시(정확히 말해 WM_PAINT 메시지를 메시지 큐에 집어 넣는 작업을 수행한다. 너무 어렵지? -_-)만을 하고 곧바로 리턴(return)해 버리므로 아직 Shown 이벤트를 발생시킬 수 없다. 따라서 WinForm은 특별한 방법을 사용하여 컨트롤들이 화면을 갱신하고 난 후에 Shown 이벤트가 발생하도록 한다. "특별한 방법"을 설명하기 위해서는 너무나 많은 것을 설명해야 하므로 다시 한번 꾸욱 참겠다.

Shown 이벤트의 한가지 아쉬운 점으로는 Form에만 존재할 뿐 UserControl 클래스에는 제공되지 않는다는 거... 브라우저 임베디드 스마트 클라이언트에서 UserControl 을 많이 사용하기 때문에 이 점이 상당히 아쉽다.

필자가 가끔씩 외쳐대는 "불가능은 없다. 다만 귀찮을 뿐..."에 해당하는 "UserControl에 Shown 이벤트 추가하기"는 독자들의 열화와 같은 요청이 있으면 함 다루어 볼 의향은 있다. (음냐... 뭘 믿고 이렇게 비싸게 구는지... 미친 것이 틀림없어... -_-;)

Conclusion

Load 이벤트는 WinForm의 UI가 아직 나타나지 않은 상태에서 발생하는 이벤트이므로, Load 이벤트 내에서 오랜 시간이 걸리는 초기화를 수행하면 프로그램이 느리다는 느낌을 사용자에게 줄 수 있다. 이를 피해 가기 위해 타이머를 사용하는 방법이 있을 수 있지만 그다지 깔끔하지 않다.

닷넷 프레임워크 2.0에서는 UI가 나타난 직후에 수행되는 Shown 이벤트가 추가되었다. 이 이벤트는 폼이 화면에 나타난 직후에 발생하는 이벤트로서 일단 폼이 화면에 나타난 이후 이므로 시간이 소요되는 초기화나 초기 데이터 조회 등의 작업을 이 이벤트 내에서 수행하더라도 일단 UI 화면이 나타나므로 사용자에게 느리다는 느낌을 주지 않을 수 있다. 물론, 프로그래스 바나 애니메이션을 Shown 이벤트 내에서 사용하는 센스를 발휘하면 더욱 좋아하는 속없는 인간들을 볼 수 있을 것이다.

내용은 졸라 간단한 Shown 이벤트에 대한 소개였지만 무쟈게 긴 서론에 배신감을 느낀 독자가 있다면 미안할 따름이다. 하지만 어쩌겠는가? 이것도 다 말만 많은 쥔장 마음인 것을...


이번 포스트는 단 한 줄의 예제 코드도 없이 써 봤다. 뭐 특별하게 이유가 있어서 코드를 안 넣은 것은 아니다. 그냥 밀려드는 귀차니즘에 몇 개월 전에 작성한 소스를 찾는 것 조차 안 했다는 거... 사실 Shown 이벤트를 설명하는데 예제코드 보여주기가 영 거시한 것도 없지 않아 있었다. 요럴 때는 Load 이벤트로 작성한 UI와 Shown 이벤트로 작성한 UI의 차이를 동영상으로 보여주는 것이 짱 인데... 요즘 필자가 술 마시랴... 프로젝트 하랴... 블로그 글 쓰랴... 출판 준비하랴... 상당히 바쁘기도 하다. 독자들은 넓은 아량으로 한번만 봐주기 바란다. 쌩유~



Comments (read-only)
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 토이 / 2006-12-08 오전 8:48:00
음 책쓰신 모양인데.. 책제목이..ㅌㅌㅌ
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 컴맹 / 2006-12-08 오후 12:56:00
앗 오늘도 주옥같은 글이 올라왔군요..ㅎㅎ
감사합니다.^^
그래도 오늘올라온글은 많이 이해가 되네요..^^;;
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 박항록 / 2006-12-09 오후 6:28:00
그렇쿤요. 좋은 내용 감사합니다..
하지만 UserControl에서 안된다는게 정말 아쉽네요. ^^;;
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 이방은 / 2006-12-11 오전 11:06:00
요청하면 해 주신다는 약속을~~~
해줘여해줘여~~~~~~~~~~~~~~~~~~~~~~~~~~~~

근데..이런 이벤트가 있었군요...첨 알았네요 ㅋ~

잘 읽었습니다.
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 이방은 / 2006-12-11 오전 11:08:00
근데 궁금한게 있는데요..
저 위에 합성(??)이미지도 직접 만드신건지?????
아님 디자이너를 살살 꼬드껴서 하신건지요??
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 블로그쥔장 / 2006-12-11 오전 11:21:00
아니 저를 뭘로 보시고 !!!
저 정도의 뽀샵질은 저도 할 줄 안다는...
음냐... 이 사이트에 나온 모든 뽀샵 이미지는 제가 만든거랍니당...
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 김효정 / 2006-12-11 오후 1:37:00
오호~ 이렇게 좋은 이벤트가 있었다니..
쌩유~ 입니다.
매일 글만 있다가 첫코 날려봅니다..
^^
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 위시 / 2006-12-11 오후 1:57:00
흐흐흐...언제나 멋진글 잘읽고 갑니다..
요청하면 해주신다고 했죠?


요청!!!!!!!!! ㅋㅋㅋ
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 정용호 / 2006-12-11 오후 8:14:00
잘 읽었습니다. 조금씩 아쉬운 부분이 해소되가는것 같아 보입니다.
아! 언젠쯤 개발자의 아쉬운 부분이 모두 해결될 날이 올지 ...

그건 그렇고 Winform과 win32의 관계가 무지 궁금합니다.
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 즈믄 / 2006-12-13 오후 2:26:00
또 잘 읽고 갑니다.
그리고 또 피식~ 웃고 갑니다.

쥔장님의 글 쎈스가 너무 재미있어서요...ㅋㅋㅋ
#ㅎㅎㅎ / 조조 / 2006-12-14 오후 4:51:00
본문의 내용을 재밋게 다 읽고 결론을 보니...
결론에 읽은 모든 내용이 잘 요약되어있군요. ㅎㅎ 원츄
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 에스엠짱 / 2006-12-20 오후 4:11:00
좋은글 잘보고 갑니다..
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 신동열 / 2006-12-21 오전 12:31:00
임배디드 스마트클라이언트 사용시 윈폼처럼 Shown 이벤트가 너무나도 필요하더군요. 그래서 편법으로 올려쓰고 있었는데.. ㅋㅋ 드디어 "UserControl에 Shown 이벤트 추가하기"를 열화와 같이 요청하오니 해주삼... ㅎㅎ
언제나 많은 도움에 감사하며...
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 이문석 / 2006-12-21 오후 1:01:00
^^ 안녕하세요.. 늘 정보 잘 보고 갑니다.
저 같은 경우는 느린 Loading을 해결하기 위하야...
BeginInvoke를 종종 사용합니다.

BeginInvoke(초기화할 내용들 덕지 덕지());
ShowProgress();
CallBack() { 다 끝나면, CloseProgress(); }

미친척하고, thread날리는 경우도 있고요... ^^

한번은 InitializeComponent를 사용하지 않고 (디자이너 사용하지 않고),
날 코딩으로, 생성자까지 최소화하고,
LoadEvent에서 모든 것을 처리해 버리기도 하였습니다.

뭐, 결과야... 사용자가 만족해 하니까.. 일단은 성공했다고 봐야겠죠.. ^^?
이럴 때 보면, 조삼모사가 생각납니다.
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 이문석 / 2006-12-21 오후 1:08:00
덧붙여...

성능 문제가 나올 때 마다, 늘 고민하는 것이
Win32 API로의 회귀 더군요.
쥔장님의 글들에도 다분히 나타나 있는 Windows Message들을 이해하지 못하면,
결국, .NET은 Java와 다를바 하나도 없는 녀석이 되어 버리는 거 같습니다.

오늘 밤에는 UserControl에 대해 Reflector로 쫘악 뜯어봐야겠네요.
시간이 된다면, 함 도전! 해 보고 싶은 과제인데요... ^^
UserControl의 ShownEvent!! 충분히 .NET 1.1에서도 응용 가능할 거란 생각이 번뜩 드네요.
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 블로그쥔장 / 2006-12-21 오후 1:19:00
네 ! 그렇습니다... ShowEvent의 핵심이 바로 BeginInvoke 랍니다.
단 몇 줄이면 Shown 이벤트를 구현할 수 있습니다. 하지만... 왜 그런 코드가 Shown 이벤트가
되는 것인가를 이해하는 것이 어려울 뿐이죠.
BeginInvoke가 작동하는 방식을 잘 아는 사람이라면 Shown 이벤트의 구현은 상당히 쉬우리라 생각됩니다.
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 벼리 / 2008-12-31 오전 10:36:00
저만궁금한 것인지 ㅠㅠ Control에서 구현하는 단몇줄의 코드 ㅠㅠ
부탁드려요 :)
#re: 닷넷의 발견: WinForm의 Shown 이벤트 / 벼리 / 2009-01-05 오후 3:43:00
답은항상 가까운 곳에... 좋은정보 감사합니다.