천객만래 [千客萬來] (It has an interminable succession of visitors)

프로그래밍 언어 하나만이 아닌 여러개가 아직 가능하다

개발이 가능하다. 파트 부분 개발은 혼자서 설계부터 개발까지 가능하다.
요즘 트렌드 분야와 거리가 가깝지 않아서 안타깝지만
아직은 배워서 수행하는데 문제가 없다.



내가 좋아하는 편의점 커피는 늘 곁에 있다.

Posted by SB패밀리

[개발/컬럼] 프로그래밍, 얼마나 배워야 하나요?

개발자에게 도움이 될만한 컬럼 같아서 올려봅니다.

Post-It Note Art Collage (PINAP)
Post-It Note Art Collage (PINAP) by Adrian Wallett 저작자 표시비영리변경 금지



 얼마나 배워야 하나요? | Codeway  2003.12.18  

류(ryujt)   http://cafe.naver.com/codeway/6 


얼마나 배워야 하나요?


가끔 프로그래밍을 시작하려고 하거나 입문과정에 있는 분들에게 받는 질문이다. 그리고, 때로는 자신의 위치가 어느 정도가 되는지 항상 의문을 가지는 사람들에게로부터 같은 질문을 받는다.

본인이 그러한 것에 대한 권위적인 기준을 남에게 이야기할 만한 자격은 없지만, 나름대로 생각한 기준을 말해보고자 한다. 지금부터 이야기하는 것들은 본인 자신의 개인적인 기준일 뿐, 그 어떠한 권위적인 해석이나 정보를 바탕으로 하고 있지 않음을 미리 알려둔다.

우선 본인은 개발자의 등급분류를 준비과정, 입문과정, 초급, 중급, 고급, 특급으로 나누고자 한다. 여기서 입문과정과 준비과정이 다소 애매하다. 프로그래밍을 시작하면 무조건 입문과정이라고 분류할 수 있을 것이다. 하지만, 그 입문과정을 준비과정과 입문과정 둘로 나누고 싶은 데에는 이유가 있다.

준비과정은 공부를 시작한 지 최소 1개월 정도를 생각한다. 이 과정 중에는 개발에 대한 전반적인 이해와 개념을 공부하는 과정이다. 이러한 공부는 상당히 지겨운 편이다. 공부하는 동안 그 결과가 겉으로 들어나지 않기 때문이다. 하지만, 본인은 조금은 여유를 가지고 살펴보기를 강력히 권장한다.

대부분의 사람들은 무엇을 배울 때, 상당히 조급한 경향이 있다. 그리고, 그 조급함으로 인해 무엇인가 빨리 터득했다고 믿는다. 또한, 항상 자신이 빠르게 배웠음을 자랑으로 여긴다. 하지만, 이러한 사람은 임계점에서 실력이 향상되지 않는 슬럼프를 꼭 겪게 된다.

준비과정에서는 프로그래밍 자체보다는 자신이 선택한 개발영역에 대한 포괄적인 이해에 대하여 집중해야 한다. 이것을 공부해 나가는 동안 여러분들은 개발에 관한 전체적인 안목을 얻을 수 있는 좋은 기회가 된다. 편협한 지식은 가끔 문제해결을 하고자 할 때, 상당한 방해가 된다. 마땅히 공부할 자료를 제시하기는 어렵지만, 전산학개론 정도가 될 것이다.

입문과정은 6개월 정도를 생각한다. 이 과정 중에는 선택한 프로그래밍 언어 자체에 대한 수련을 하는 과정이다. 우선은 문법체계와 프로그래밍에 대한 절차에 대해서 공부를 하고, 이후 다양한 문제해결에 대한 실전적인 연습이 필요하다. 굳이 책에 집착하지 말고 자신이 하고자 하는 새로운 프로젝트 또는 문제를 설정하여, 이를 선택한 언어를 통해 해결해 나가는 동안 그 언어에 대한 감각을 길러 가는 것이다.

프로그래밍은 지식보다는 감각적인 요소가 상당히 중요하다. 그것은 프로그래밍을 실무에 접했을 때, 그 문제의 해결을 위한 그 어떠한 정석도 존재하지 않기 때문이다. 비슷한 업무에 대한 개발을 계속적으로 진행하더라도, 결국 구현단계에서는 언제나 새로운 문제가 기다리고 있다. 이러한 것들을 단순히 암기된 지식의 결합 및 수정으로는 해결할 수가 없다. 특히 알고리즘에 관한 공부를 게을리하지 않기를 바란다.

이제, 위에서 거론된 두 과정을 지나면 여러분들은 초급 프로그래머라고 할 수 있을 것이다. 그리고 이 초급은 최소 1년에서 2년 이상 거쳐야 한다고 생각한다. 이후 중급은 4년 이상 그리고 고급과 특급은 기간자체가 중요한 요소라고 생각하지 않는다.

입문과정이 전반적이 이해와 감각을 키우는 과정이라면, 초급과정에서는 스킬을 중점으로 공부하는 과정이라고 생각한다. 입문과정은 다소 포괄적인 공부를 하는 단계라고 한다면, 자신의 개발영역을 조금씩 넓혀가면서 심도 깊게 연구하는 단계이다.

초급, 중급, 고급, 특급 개발자들의 기준은 다음과 같이 정리해봤다.


초급 
     - 제한된 범위의 단위 모듈에 대한 코딩 능력
     - 작성된 모듈에 대한 수정 및 변경 능력
     - 프로그래밍에 대하여 상급 개발자의 조언에 의존하는 단계

중급 
     - 설계서를 토대로 스스로 프로그래밍의 문제를 해결할 수 있는 능력
     - 개발절차와 흐름에 대한 완벽한 이해

고급 
     - 프로젝트의 위험요소를 미리 판별하고 대응하는 능력
     - 설계능력
     - 리더십
     - 풍부한 개발 경험

특급 
     - 기술적인 사회적 흐름을 예측하는 능력
     - 대인관계와 대응에 대한 능력
     - 풍부한 사회 경험
     - 회사내의 개발전략을 작성하고 영업과 경영에 대한 조언 및 보조 능력

Posted by SB패밀리




얼마나 배워야 하나요?
2003.12.18 17:35  

류(ryujt)   http://cafe.naver.com/codeway/6 


얼마나 배워야 하나요?


가끔 프로그래밍을 시작하려고 하거나 입문과정에 있는 분들에게 받는 질문이다. 그리고, 때로는 자신의 위치가 어느 정도가 되는지 항상 의문을 가지는 사람들에게로부터 같은 질문을 받는다.

본인이 그러한 것에 대한 권위적인 기준을 남에게 이야기할 만한 자격은 없지만, 나름대로 생각한 기준을 말해보고자 한다. 지금부터 이야기하는 것들은 본인 자신의 개인적인 기준일 뿐, 그 어떠한 권위적인 해석이나 정보를 바탕으로 하고 있지 않음을 미리 알려둔다.

우선 본인은 개발자의 등급분류를 준비과정, 입문과정, 초급, 중급, 고급, 특급으로 나누고자 한다. 여기서 입문과정과 준비과정이 다소 애매하다. 프로그래밍을 시작하면 무조건 입문과정이라고 분류할 수 있을 것이다. 하지만, 그 입문과정을 준비과정과 입문과정 둘로 나누고 싶은 데에는 이유가 있다.

준비과정은 공부를 시작한 지 최소 1개월 정도를 생각한다. 이 과정 중에는 개발에 대한 전반적인 이해와 개념을 공부하는 과정이다. 이러한 공부는 상당히 지겨운 편이다. 공부하는 동안 그 결과가 겉으로 들어나지 않기 때문이다. 하지만, 본인은 조금은 여유를 가지고 살펴보기를 강력히 권장한다.

대부분의 사람들은 무엇을 배울 때, 상당히 조급한 경향이 있다. 그리고, 그 조급함으로 인해 무엇인가 빨리 터득했다고 믿는다. 또한, 항상 자신이 빠르게 배웠음을 자랑으로 여긴다. 하지만, 이러한 사람은 임계점에서 실력이 향상되지 않는 슬럼프를 꼭 겪게 된다.

준비과정에서는 프로그래밍 자체보다는 자신이 선택한 개발영역에 대한 포괄적인 이해에 대하여 집중해야 한다. 이것을 공부해 나가는 동안 여러분들은 개발에 관한 전체적인 안목을 얻을 수 있는 좋은 기회가 된다. 편협한 지식은 가끔 문제해결을 하고자 할 때, 상당한 방해가 된다. 마땅히 공부할 자료를 제시하기는 어렵지만, 전산학개론 정도가 될 것이다.

입문과정은 6개월 정도를 생각한다. 이 과정 중에는 선택한 프로그래밍 언어 자체에 대한 수련을 하는 과정이다. 우선은 문법체계와 프로그래밍에 대한 절차에 대해서 공부를 하고, 이후 다양한 문제해결에 대한 실전적인 연습이 필요하다. 굳이 책에 집착하지 말고 자신이 하고자 하는 새로운 프로젝트 또는 문제를 설정하여, 이를 선택한 언어를 통해 해결해 나가는 동안 그 언어에 대한 감각을 길러 가는 것이다.

프로그래밍은 지식보다는 감각적인 요소가 상당히 중요하다. 그것은 프로그래밍을 실무에 접했을 때, 그 문제의 해결을 위한 그 어떠한 정석도 존재하지 않기 때문이다. 비슷한 업무에 대한 개발을 계속적으로 진행하더라도, 결국 구현단계에서는 언제나 새로운 문제가 기다리고 있다. 이러한 것들을 단순히 암기된 지식의 결합 및 수정으로는 해결할 수가 없다. 특히 알고리즘에 관한 공부를 게을리하지 않기를 바란다.

이제, 위에서 거론된 두 과정을 지나면 여러분들은 초급 프로그래머라고 할 수 있을 것이다. 그리고 이 초급은 최소 1년에서 2년 이상 거쳐야 한다고 생각한다. 이후 중급은 4년 이상 그리고 고급과 특급은 기간자체가 중요한 요소라고 생각하지 않는다.

입문과정이 전반적이 이해와 감각을 키우는 과정이라면, 초급과정에서는 스킬을 중점으로 공부하는 과정이라고 생각한다. 입문과정은 다소 포괄적인 공부를 하는 단계라고 한다면, 자신의 개발영역을 조금씩 넓혀가면서 심도 깊게 연구하는 단계이다.

초급, 중급, 고급, 특급 개발자들의 기준은 다음과 같이 정리해봤다.


초급 
     - 제한된 범위의 단위 모듈에 대한 코딩 능력
     - 작성된 모듈에 대한 수정 및 변경 능력
     - 프로그래밍에 대하여 상급 개발자의 조언에 의존하는 단계

중급 
     - 설계서를 토대로 스스로 프로그래밍의 문제를 해결할 수 있는 능력
     - 개발절차와 흐름에 대한 완벽한 이해

고급 
     - 프로젝트의 위험요소를 미리 판별하고 대응하는 능력
     - 설계능력
     - 리더십
     - 풍부한 개발 경험

특급 
     - 기술적인 사회적 흐름을 예측하는 능력
     - 대인관계와 대응에 대한 능력
     - 풍부한 사회 경험
     - 회사내의 개발전략을 작성하고 영업과 경영에 대한 조언 및 보조 능력

Posted by SB패밀리

백번 듣는 것 보다 한 번 직접 보는 게 낫다는 말처럼

책으로만 보는 것보다는 직접 만들어보고 경험해보는 것이,

알고 있는 것보다는 직접 가르쳐보는 것이

확실하게 자기의 것으로 만들 수 있다는 것을....


출처: 인터넷

아래의 글을 읽어 보자....



우유를 시켜먹는 사람보다 배달하는 사람이 더 건강하다.  
2004.06.10 10:23  

류(ryujt)   http://cafe.naver.com/codeway/137 

만년 초보 딱지를 달고 다니는 사람들이 있다.  스스로 초보 수준임을 모르는 사람까지 합하면 그 수는 무시 못할 정도에 이르게 된다.


무엇이 문제일까?  물론 수 많은 개발자들의 숫자만큼 수많은 이유가 있을 것이므로 몇 가지 말로 일반화하기란 너무 어렵다.  다만, 만약 지금 내가 거론하려는 조건들이 그대에게 적용된다면 부디 그 거추장스러운 딱지를 어서 떼어내기를 바란다.



1. 우유를 시켜먹는 사람보다 배달하는 사람이 더 건강하다.


대한민국 국민은 참으로 특이하다.  건강에 좋다고 하면 지렁이도 씨가 말라 버린다.  이러한 요상한 취미는 학문을 하는 사람에게도 고스란히 적용된다.  어떤 이의 방석을 훔쳐 앉으면 합격한다든가, 무엇을 어떻게 하면 성적이 오른다는 등의 비법들이 난무한다.


그 정도는 애교로 봐줄 수 있다.  하지만, 전문가의 길을 걷는, 프로의 길을 걷는 개발자들이 의식적이든 무의식적이든 젖어버린 이 비법 중독은 참으로 어처구니 없다.


간혹 좋은 책이 나오고 이것이 소문이 나면 금새 모든 개발자들은 반드시 그것을 읽어야 한다고 생각한다.  그리고, 그 책을 읽고 나면 자신이 무엇인가 업그레이드 된듯한 만족감을 느낀다.  좋은 강좌나 세미나에 참석했을 때는 그 환각증상이 더욱 심각해 진다.


하지만, 그것은 자신의 것이라고 할 수 없다.  아무리 좋은 것을 듣고 아무리 좋은 것을 읽어도 여전히 여러분의 실력은 그대로이다.  무엇인가 깨닫고 무엇인가 느낀 것이 있다면, 이제 여러분 스스로의 이론과 생각을 창조해 내고, 수없이 연마하는 시간이 필요한 것이다.


누구나 배트를 휘둘러 공을 쳐내는

"기술" 자체는 쉽게 이해할 수 있지만,

실재로 멋지게 공을 쳐내기 위해서는

수없이 헛 방망이질을 해야 하는 것처럼...




2. 재능의 부족을 탓하지 마라


재능이 부족한 그대여.  우리 부족한 재능의 덫에 걸려 넘어지지 말자.


자신감 부족이야 말로 가장 심각한 재난이다.  자신감을 잃어버린 자는 마치 사이드 브레이크를 걸어둔 채로 달리려는 자동차와 같다.  여기에 열정마저 잃어버리면 기름마저 떨어진 꼴이 된다.  계속 뒤를 돌아보고 자신의 부족한 위치에 한숨을 짓는 동안 그대의 발걸음은 늪 속으로 빠져들게 될 것이다.


재능의 차이는 있다.  인정해야 할 수밖에 없는 것을 외면하기 위해 시간을 낭비하지 말자.  하지만, 여러분들이 느끼는 것만큼 그 재능의 차이가 심각한 결과를 가져오는 경우는 없다.


가끔 소위 잘나가는 사람들을 보면, 무엇인가 특별하고 능력이 충만하여 보인다.  그리고, 각종 매스컴들은 그 차이점에 조명을 들이대기 시작한다.  그 때문에 그들이 겪었던 수많은 고난과 땀방울에 대해서는 쉽게 지나치게 된다.  그 어느 누구도 재능과 운으로 인해 쉽게 성공의 길에 들어설 수는 없다.  내가 장담한다.


곰곰이 생각해 보라, 자신보다 능력 있는 자들과의 자신의 차이점을.  현재 그 엄청난 차이에는 분명 재능이라는 거리도 있지만, 그들이 열정과 땀을 흘려가면 노력했던 시간이 더욱 크지 않게는 가?




3. 시작은 호기심의 힘으로, 중반은 열정으로, 마무리는 끈기와 신념으로


싹수가 노랗다고 한다.  그렇다.  그 동안 수많은 개발자들의 팀장을 맡으면서 나름대로 관상 보는 능력이 생긴 듯 하다.  싹수가 노란 놈이 보이기 시작한다.  반대로 “저 넘은 무엇인가 해낼 놈이다” 하고 느낌이 꽂히는 때가 있다.


시작부터 요란한 자는 중도에 반드시 포기한다.  잘나가다가 슬럼프에 빠지는 타입이다.  처음에 프로그래밍을 공부할 때는 모든 것이 신기할 뿐이었다.  무엇인가 만들어 보고는 대견한 자신을 뽐내고 싶어서 친구들에게 자랑하지 못해서 안달이 난다.  아직까지는 지극히 정상이다.  하지만, 그 정도를 넘어서 초보의 영역을 뛰어넘고자 안달이 나기 시작하면 일은 틀어지게 된다.


입문자는 반드시 그 호기심과 기본기 연마 사이에서 균형을 잃지 말아야 한다.  무엇인가 화려하고 어려워 보이는 문제에 대한 호기심은 지극히 정상이며 권장할만한 일이다.  하지만, 누군가에게 물어서 해결할 수 있는 일 보다는 자신 스스로 해결할 수 있는 문제에 대한 도전을 통하여 기본기를 연마할 시기에 너무 힘 빼지 말아야 할 것이다.


나중에 돌이켜 보면 입문자 시절만큼 돌아가고 싶은 시기도 없을 것이다.  그때의 그 기분을 다시는 느낄 수 없기 때문이다.


중도의 길을 들어선 자는 거침이 없어야 한다.  이제 서서히 개발하는 재미에 푹 빠져버려서 “이것이 내 천직이다” 하는 말을 거침없이 내뱉는 시기가 온다.  개발에 대한 예찬이 그 입에서 쉴새 없이 쏟아져 나온다.


하지만, 이 시기에서 입문 시기의 호기심으로만 행동하는 자는 반드시 쓰러지게 된다.  호기심과 열정의 차이는 무엇인가?  누군가 멋진 이성을 만났을 때 가슴 떨림이 호기심이라고 한다면, 사랑을 느끼고 그에게 완전히 녹아 버린 모습이 바로 열정이다.


프로그래밍의 세계에 녹아버려라.  일말의 망설임도 없이, 사랑할 때의 그 뜨거운 가슴으로 개발에 몰두하라.  돌부리를 반기고, 어려운 난관을 즐겨라.  절대 뒤돌아 서지 말고 언제나 승부를 걸어라.  해낼 수 있을 까 걱정하지 말고, “반드시 해내자” 하고 스스로를 위로하라.  자신만의 무용담을 훗날 후배들에게 자랑스럽게 이야기하게 될 그날을 기약하면서……


그리고 이제 완성의 시기.  이 시기는 조용하면서도 험난하다.  스스로 정체기를 느끼는 시절이기도 하다.  이제 교만을 버려야 할 시기이다.  어쩌면 이미 주눅이 들었을 지도 모른다.  여러 프로젝트를 통해 성공과 실패를 겪었을 것이고, 실무란 잘한 것보다 못한 것이 엄청난 위력으로 자신을 짓누르기 때문이다.

그대는 이제 잠시 쉬어도 좋다.  다만, 의미 있는 휴식을 취해야 할 시기이다.  객관적인 안목 그리고 보다 넓은 안목으로 개발을 이해하고 공부해야 할 시기이다.

지금까지는 개발이라는 것이 “고객과 나”, “문제와 나”와 같은 이차원적인 시야가 주된 관심사였을 지도 모른다.  하지만, 개발은 고객, 자신의 회사의 경영자, 마케팅 인원, 기획자, 등 수없이 많은 개발 이해관계자들과의 상관관계를 생각하지 않으면 안 되는 시기가 됐을 것이다.

문치무공(文治武功)이라고 했다.  무장도 결국 정치를 알아야 그 힘이 배가되며, 정치하는 자도 힘의 원리를 알고 인정할 줄 알아야 나라를 제대로 다스릴 수 있는 것이다.  이제는 호기심과 열정보다는 책임감과 끈기로 정체된 자신을 한 단계 업그레이드 시켜야 할 시기 인 것이다.


출근 길에 전철과 버스 안에서 이러 저러한 생각에 빠졌다가 생각난 데로 출근하자 마자 적어본 것입니다.  개인적인 생각일 뿐이니 넓은 아량으로 읽어주시길 바랍니다.

Posted by SB패밀리

1. DC얻기

CClientDC dc(this);


2. Client 영역 구하기

GetClientRect(&rect);

WM_SIZE 메시지발생후 cx,cy 사용


3. 문자열 사각형안에 그리기

pDC->DrawText(문자열,사각형,Style);

Style : DT_BOTTOM - 문자열을 사각형 맨아래줄에배열 반드시DT_SINGLELINE과 함께사용

DT_CENTER - 문자열을 가로중앙에 배치

DT_VCENTER - 문자열을 세로중앙에 배치

DT_LEFT,RIGHT - 문자열을 좌,우로 배치

DT_SINGLELINE - 문자열을 한줄로만 쓴다


4. Brush 사용법

CBrush brushname(RGB(red,green,blue)); //브러쉬 생성

//이전Brush 저장, 새로운 Brush 선택

CBrush *oldBrush=pDC->SelectObject(&brushname);

pDC->SelectObject(oldBrush); //원래의 브러쉬로 반환


5. Pen사용법

CPen pen(Pen Style,RGB(red,green,blue)); //브러쉬생성

//Style: PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_GEOMETRIC,PS_COSMETRIC

- 펜종류

PS_ENDCAP_ROUND,PS_ENDCAP_SQUARE - 펜끝을 둥글게,각지게 설정

CPen *oldPen=pDC->SelectObject(&pen); //이전Pen저장, 새로운 Pen설정

pDC->SelectObject(oldPen); //펜반환


6. 화면다시그리기

View Class에서 - Invalidate(TRUE) : 화면을 지우고다시그린다

Invalidate(FALSE) : 화면을 덮어씌운다

UpdateAllViews(NULL); // Doc Class에서 View 의 OnDraw 호출

RedrawWindow();


7. 메시지,함수 수동으로 넣기 (EX)버튼클릭함수넣기

헤더파일의 AFX_MSG_MAP 부분에 함수를 정의

// .cpp파일의 AFX_MSG 부분에 메시지를 추가한다

EX) afx_msg void funcName();

// ID 등록: View 메뉴의 Resource Symbol 에 들어가서 메뉴 ID 를 등록해준다..

EX) ON_BN_CLICKED(ID_NAME,funcName).

// .cpp파일의 맨아래에서 함수를 정의한다

EX) void CClass::funcName() { ... }


8. 마우스커서 바꾸기

리소스탭에서 커서를 그리고 저장한뒤 ID값은 준다음

SetCapture(); //커서의입력을 클라이언트영역을 벗어나더라도 받아낸다

SetCursor(AfxGetApp()->LoadCursor(nIDResource));

//APP클래스의 LoadCursor View의 SetCursor 사용

ReleaseCapture(); //SetCursor()상태를 해제한다


9. 색상표 사용하기

CColorDialog dlg;

if(dlg.DoModal()==IDOK) //Dialog 를 띄운후 OK버튼을누르면 실행할부분

MemberFunc: GetColor() //선택된 색상을 받아온다 return 형은 COLORREF 형


10. 팝업메뉴 만들기

CMenu menu; //메뉴 객체생성

CMenu *pmenu; //메뉴 포인터생성

menu.LoadMenu(IDR_MAINFRAME); //메뉴를 불러온다

pmenu=menu.GetSubMenu(3); //메뉴의 3번째 메뉴를 가져온다

menu.CheckMenuItem(ID_MENU,m_kind==ID_MENU ? MF_CHECKED : MF_UNCHECKED); //메뉴 체크하기 (메뉴 ID, ID 체크조건)

pmenu->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this) //(TMP_Style,x좌표,y좌표,hWnd) 메뉴 띄우기


*주의사항*

[안내]태그제한으로등록되지않습니다-OnContextMenu(CWnd* pWnd, CPoint point) //여기서 point 는 스크린 기준이고,

OnRButtonDown(UINT nFlags, CPoint point) //여기서 point 는 클라이언트 기준이다!


11. 클라이언트 포인터를 스크린 포인터로 변경

ClientToScreen(&point);


12. 그림판기능

if(m_flag==FALSE) return; //m_falg=그리기 기능 참,거짓설정 그리기 아니면 빠져나간다

CClientDC dc(this);

CPen myPen(PS_SOLID,m_width,m_color);

CPen *pOldPen=dc.SelectObject(&myPen);

switch(m_shape)

{

case ID_FREELINE: //자유선그리기

dc.MoveTo(m_oldpt.x,m_oldpt.y); //지난포인터부터

dc.LineTo(point.x,point.y); //새포인터까지 그린다

break;

case ID_RECT: //사각형그리기

dc.SetROP2(R2_NOTXORPEN);

dc.Rectangle(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y); //지워지는 효과

dc.Rectangle(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

break;

case ID_ELLIPSE: //원그리기

dc.SetROP2(R2_NOTXORPEN);

dc.Ellipse(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y); //지워지는 효과

dc.Ellipse(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

break;

case ID_LINE: //선그리기

dc.SetROP2(R2_NOTXORPEN);

dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

dc.LineTo(m_oldpt.x,m_oldpt.y); //지난점까지 그은선을 지운다

dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

dc.LineTo(point.x,point.y); //새로운점까지 그린다

break;

}

m_oldpt=point; //바로이전값 보관

dc.SelectObject(pOldPen); //펜 반환


13. MessageBox

AfxMessageBox()->전역함수를 이용하영 메세지 박스를 출력한다. //어디서든지 사용할수 잇다

int CWnd::MessageBox("메세지","창제목","아이콘|버튼(상수값)"); //View클래스에서 사용한다

아이콘 상수값 MB_IC[안내]태그제한으로등록되지않습니다 - xxONERROR,

MB_ICONWARNING, MB_ICONQUESTION,MB_ICONINFOMATION

MB_SYSTEMMODAL //시스템모달 대화창 닫기전에 다른작업 못함

MB_APPLMODAL //응용모달

버튼 상수값 MB_OK, MB_OKCANCEL, MB_YESNO


14. OS 컨트롤

ExitWindowEx(EWX_SHUTDOWN,NULL); //Shut Down

ExitWindowsEx(EWX_FORCE,0); //강제종료

ExitWindowsEx(EWX_LOGOFF,0); //로그오프

ExitWindowsEx(EWX_POWEROFF,0); //Shut Down -> Turn Off

ExitWindowsEx(EWX_REBOOT); //Shut Down -> Reboot


15. DialogBox 메시지 교환

UpdateData(FALSE); // 컨트롤에 멤버변수의 내용을 표시해준다

UpdateData(TRUE); // 컨트롤 내용을 다이얼로그 클래스의 멤버변수로 저장


16. 자료변환

atoi,itoa - int <=> ASCII(char) 변환

str.Format(" %d %d",x,y); // int형을 문자열로 변환

atol,ltoa - ASCII <=> long 변환

atof - ACSII => float 변환

fcvt,gcvt - 실수를 text로 변환

LPtoDP, DPtoLP - 장치좌표 <=> 논리좌표 변환


17. CEdit Class 사용하기

CEdit e_str.SetSel(int StartChae, int EndChar); //처음문자부터 마지막까지 블록 지정

CEdit e_str.GetSel(int SChar,int EChar); //블럭 지정한 처음문자와 마지막문자 받기

CString str=m_str.Mid(SChar,EChar-SChar); //블럭지정한 부분을 가져온다


18. 컨트롤과 자료교환

SetDlgItemText(컨트롤 ID,문자열) //컨트롤에 문자열을 넣는다

GetDlgItemText(컨트롤 ID,문자열) //컨트롤의 내용을 문자열에 넣는다

GetDlgItem(컨트롤 ID); //컨트롤의 주소를 가져온다


19. 상태바조작

CMainFrame 생성자 위에

static UINT indicators[] = //이안에 새로운 ID를 넣고 그 ID의 갱신핸들러를 만든다음 코딩

pCmdUI->SetText("표시할내용“);


20. 수동으로 Bitmap 컨트롤 사용하기

CStatic bitmap; //bitmap 컨트롤변수

bitmap.SetBitmap(CBitmap m_bitmap); //컨트롤에 비트맵지정

GetDlgItem(IDC_BITMAP)->ShowWindow(SW_SHOW,HIDE); // 그림을 보이거나 숨긴다.

21. 응용프로그램 실행하기

WinExec("프로그램경로“,SW_SHOW,HIDE); //응용프로그램실행,경로는 \\로 구분한다


22. Bitmap 사용하기

CBitmap bitmap.LoadBitmap(IDC_BITMAP); //비트맵객체에 비트맵지정

CDC memDC; //그림그릴 메모리DC생성

MemDC.CreateCompatibleDC(pDC); //화면 DC와 메모리 DC 호환 생성

CBitmap *pOldBitmap=MemDC.SelectObject(&m_bitmap); //메모리에 그림을그린다.

pDC->BitBlt(int x, int y,int Width, int Height, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop);

//BitBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림시작x좌표,그림시작y좌표,스타일);

pDC->StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop )

//StretchBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림x좌표,그림y좌표,메모리그림넓이,메모리그림높이,스타일);

MemDC.SelectObject(pOldBitmap); // 메모리DC반환


23. Font 바꾸기

CFontDialog dlg;//폰트다이얼로그 생성

LOGFONT m_logFont; //폰트받을변수선언

if(dlg.DoModal()==IDOK) //폰트다이얼로그표시

{dlg.GetCurrentFont(&m_logFont)} //선택된 폰트받기

OnDraw()

CFont newFont,*pOldFont; //폰트 객체 만들기

newFont.CreateFontIndirect(&m_logFont); //폰트 생성

pOldFont=(CFont *)pDC->SelectObject(&newFont); //폰트 선택

OnCreate()

CClientDC dc(this); //DC 생성

CFont *pFont=dc.GetCurrentFont(); //클라이언트 영역의 폰트를

pFont->GetLogFont(&m_logFont); //로그폰트 멤버값으로 지정


24. Font 만들기

LOGFONT logfont; //폰트를 만든다

logfont.lfHeight=50; //문자열 높이

logfont.lfWidth=0; //너비

logfont.lfEscapement=0; //문자열기울기

logfont.lfOrientation=0; //문자개별각도

logfont.lfWeight=FW_NORMAL; //굵기

logfont.lfItalic=TRUE; //이탤릭

logfont.lfUnderline=TRUE; //밑줄

logfont.lfStrikeOut=FALSE; //취소선

logfont.lfCharSet=HANGUL_CHARSET; //필수

logfont.lfOutPrecision=OUT_DEFAULT_PRECIS;

logfont.lfClipPrecision=CLIP_DEFAULT_PRECIS; //가변폭폰트 고정폭폰트

logfont.lfPitchAndFamily=DEFAULT_PITCH|FF_SWISS; //글꼴이름

strcpy(logfont.lfFaceName,"궁서체");

CClientDC dc(this);

CFont newFont; //폰트객체생성

newFont.CreateFontIndirect(&logfont); //폰트지정

CFont *pOldFont=dc.SelectObject(&newFont); //폰트선택

dc.TextOut(100,100,m_text);

dc.SelectObject(pOldFont); //폰트반환


25. Font 만들기 2

CFont newFont;

newFont.CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename );

CFont *pOldFont=dc.SelectObject(&newFont);


26. ComboBox 사용하기

CComboBox combo; //콤보박스 선언

combo.Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

//Style - WS_CHILD|WS_VISIBLE

int n=combo.GetCurSel(); //선택된 아이템의 index를 가져온다

combo.AddString("문자열“); //문자열을 추가한다

combo.GetLBText(n,str); //n번째 아이템을 str에 저장


27. Spin 사용하기

Spin은 바로앞의 Tab Order에 따라 붙는다

m_spinr.SetRange(1900,3000); //스핀 범위 지정

m_spinr.SetPos(m_nYear); //스핀 위치 지정


28. CTime사용하기

CTime time; //시간객체생성

time=CTime::GetCurrentTime(); //현재시간을 저장

time.GetYear(),time.GetMonth();,time.GetDay(),time.GetHour(),time.GetMinute(),time.GetSecond()


29. CListBox 메소드

AddString("문자열"); //리스트에 문자열 추가

DeleteString(index); //리스트에서 항목 삭제

GetCount() //전체 항목 갯수를 얻는다.

GetSelcount() //선택된 항목 갯수 리턴

GetSel() //선택된 것인지 아닌지를 리턴한다 -> 양수 = TRUE , 음수 => FALSE

GetText(int index,문자열변수) //index 번째 문자열을 문자열 변수에 넣는다

FindStringExact(문자열) //지정 문자열의 index 값 리턴 -> 없으면 리턴값 LB_ERR 반환

FindString("a") //"a"로 시작하는 항목을 모두 찾는다.

ResetCountent() //모든 내용을 지운다.


30. 파일입출력

프로젝트생성시 Step4 => Advanced => 저장파일확장자지정

//이 클래스를 저장,로드가능한 클래스로 쓰겟다는 선언

.h 파일에 DECLARE_SERIAL(CSawon)

.cpp 파일에 IMPLEMENT_SERIAL(CSawon,CObject,1) //이거를 해야 저장이 가능하다

void CFileioDoc::Serialize(CArchive& ar)

if (ar.IsStoring())

{

ar< < //저장하기

}

else //열기

{

ar>>m_shape; //불러올걸 쓴다. 읽을때도순서대로읽어야한다

}


31. MicroSoft FlexGrid 사용하기!

CMSFlexGrid m_Grid; //FlexGrid 컨트롤 변수

CString strTitle[]={"고객코드","고객성명","고객포인트","신장","몸무게","고객등급","BMT지수","판정결과"}; // Grid 의 제목에 넣을문자배열

int Width[]={900,900,1100,800,800,900,1000,900}; // Grid 의 열넓이 지정할 배열

m_Grid.SetRows(m_cnt+2); //전체행수 지정

m_Grid.SetCols(8);//전체열수 지정

m_Grid.Clear(); //지우기

m_Grid.SetFixedCols(0); //고정열은 없다.

m_Grid.SetRow(0); // 행선택

for(int i=0;i<=7;i++)

{

m_Grid.SetColWidth(i,Width[i]); //열 넓이 설정

m_Grid.SetCol(i); //열 선택

m_Grid.SetText(strTitle[i]); // 선택된행, 선택된열에 Text 를 넣는다

}


32. 4대 Class간 참조

//각각 헤더파일 include

#include "MainFrm.h" //메인프레임 헤더파일

#include "ClassDoc.h" //Doc클래스 헤더파일

#include "ClassView.h" //View를 include 할때는 반드시 Doc 헤더파일이 위에잇어야한다

#include "Class.h" //APP Class 의 헤더파일


void CClassView::OnMenuView() //뷰클래스

CClassApp *pApp=(CClassApp *)AfxGetApp(); //View -> App
CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //View -> MainFrm

//View -> MainFrm -> Doc

CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument();

CClassDoc *pDoc=(CClassDoc *)GetDocument(); //View -> Doc


//MainFrame 클래스

CClassView *pView=(CClassView *)GetActiveView(); //MainFrm -> View

CClassDoc *pDoc=(CClassDoc *)GetActiveDocument(); //MainFrm -> Doc

CClassApp *pApp=(CClassApp *)AfxGetApp(); //MainFrm -> App


//Doc 클래스

CClassApp *pApp=(CClassApp *)AfxGetApp(); //Doc -> App

CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //Doc -> MainFrm

// Doc -> MainFrm -> View

CClassView *pView=(CClassView *)pMain->GetActiveView();

CClassView *pView=(CClassView *)m_viewList.GetHead(); // Doc -> View


//App 클래스

CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //App -> MainFrm

//App -> MainFrm -> View

CClassView *pView=(CClassView *)pMain->GetActiveView();

//App -> MainFrm -> Doc

CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument();


33. ToolBar 추가하기

CMainFrame 으로 가서 멤버변수 추가

CToolBar m_wndToolBar1;

OnCreate 로 가서 다음 내용을 추가해준다

(위의 toolbar 부분을 복사하고 이름만 바꾸면 된다.3군데..)

if (!m_wndToolBar1.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE |

CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |

CBRS_SIZE_DYNAMIC) || !m_wndToolBar1.LoadToolBar(IDR_TOOLBAR1))

{

TRACE0("Failed to create toolbar\n");

return -1; // fail to create

}


그 함수내에서 //TODO 아래에 내용추가..역시..복사해서 이름만 바꾸면 된다.

m_wndToolBar1.EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM);

//DockControlBar(&m_wndToolBar1); <= 이부분 대신..

이거를 넣는다..

CRect toolRect; //툴바 영역을 얻을 사각형

this->RecalcLayout(); //현상태의 Client 영역을 구해서 저장한다

m_wndToolBar.GetWindowRect(&toolRect); //툴바영역을 저장한다

toolRect.left+=1; //사각형의 왼쪽을 1Pixel 줄인다

//ToolRect에 툴바를 붙인다

DockControlBar(&m_wndToolBar1,AFX_IDW_DOCKBAR_TOP,&toolRect);

return 0;


34. ToolBar에 ComboBox붙이기

CComboBox m_combo; //객체생성

ID 등록 => view 메뉴 => resource symbol => new => ID_COMBO

oncreate 에 내용 추가 (콤보를 만들고 표시하는 내용)

m_wndToolBar.SetButtonInfo(10,IDC_COMBO,TBBS_SEPARATOR,150);

//툴바의 10번째버튼을 편집한다

CRect itemRect; //콤보를넣을 사각형을 만든다

m_wndToolBar.GetItemRect(10,&itemRect); //툴바의 10번째 버튼을 사각형에 넣는다
itemRect.left+=5; //앞여백

itemRect.right+=5; //뒤여백

itemRect.bottom+=100; //콤보가열릴 공간확보

m_combo.Create(WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,itemRect,&m_wndToolBar,IDC_COMBO);

//콤보박스를 툴바에 붙여준다

m_combo.AddString("이름"); //내용추가

m_combo.SetCurSel(0); //셀 선택


35. Toolbar에 수동으로넣은 ComboBox 사용하기

afx_msg void [안내]태그제한으로등록되지않습니다-xxOnSelectCombo(); //원형

ON_CBN_SELCHANGE(IDC_COMBO,[안내]태그제한으로등록되지않습니다-xxOnSelectCombo) //메세지맵에 추가

CMainFrame *pMain=(CMainFrame *)GetParent(); //메인프레임 주소참조

CComboBox *pCom=(CComboBox *)(pMain->m_wndToolBar.GetDlgItem(IDC_COMBO));

//콤보박스의 주소를 가져온다, 접근할 때 메인프레임 -> 툴바 -> 콤보박스 의 순서로 가야한다

int n=pCom->GetCurSel(); //현재선택된 셀의 인덱스를 가져온다

if(n==CB_ERR) return; //선택된셀이 없으면 중지한다

CString str;

pMain->m_combo.GetLBText(n,str); //선택된셀의 Text를 가져온다


36. UPDATE_COMMAND 사용하기

pCmdUI->Enable(TRUE); //버튼 활성화

pCmdUI->SetText((bAdd)?"취소":"신규"); //버튼의 text 설정

pCmdUI->SetCheck(TRUE);//버튼 체크


37. 프로그램정보저장

CWinApp::GetProfileString(섹션명,항목명,기본값); // 함수를 사용한다. (문자열)

CWinApp::GetProfileInt(섹션명,항목명,기본값); //불러올때사용 (숫자)

CWinApp::WriteProfileString(섹션명,항목명,값); //저장할때 사용 (문자열)

CWinApp::WriteProfileInt(섹션명,항목명,값); //저장할때 사용 (숫자)

//불러올때 사용할함수

void CMainFrame::ActivateFrame(int nCmdShow) //프로그램 실행후 프레임생성될때 실행

//저장할 때 WM_DESTROY 메시지 사용


38. 컨트롤바 표시하기

CMainFrame *pMain=(CMainFrame *)GetParent();//MainFrame 주소가져오기

//툴바를 bTool2 에따라 보이고 감춘다

pMain->ShowControlBar(&pMain->m_wndToolBar,bTool1,FALSE);


39. Window 창크기,위치정보 저장하기

MainFrame 의 WM_DESTROY 에

WINDOWPLACEMENT w;

this->GetWindowPlacement(&w); //윈도우의 정보를 저장한다.

CString strRect;

strRect.Format("%04d,%04d,%04d,%04d", //04d 는 4자리 확보하고 남은건 0으로 채워라

w.rcNormalPosition.left,w.rcNormalPosition.top,

w.rcNormalPosition.right,w.rcNormalPosition.bottom); //윈도우의 위치,크기 확보..

BOOL bMax,bMin; //윈도우의 상태를 저장하기위한 변수

//w.falg 는 이전상태의 정보를 가지고 잇다!!

if(w.showCmd==SW_SHOWMINIMIZED) //최소화 상태

{

bMin=TRUE;

if(w.flags==0) //falg 값이 0 이면 이전 상태가 보통상태이다!!

bMax=FALSE;

else //이전상태가 최대화 상태

bMax=TRUE;

}

else

{

if(w.showCmd==SW_SHOWMAXIMIZED) //최대화상태

{

bMax=TRUE;

bMin=FALSE;

}

else //보통 상태

{

bMax=FALSE;

bMin=FALSE;

}

}

AfxGetApp()->WriteProfileString("WinStatus","Rect",strRect);

AfxGetApp()->WriteProfileInt("WinStatus","Max",bMax);

AfxGetApp()->WriteProfileInt("WinStatus","Min",bMin);


//읽어올차례..

ActivateFrame 함수로 가서

WINDOWPLACEMENT w; //윈도우의 상태를 저장하는 구조체..

BOOL bMax,bMin; //최대,최소상태를 저장할 변수

CString strRect; //창크기를 받아올 변수

strRect=AfxGetApp()->GetProfileString("WinStatus","Rect","0000,0000,0500,0700");

bMin=AfxGetApp()->GetProfileInt("WinStatus","Min",FALSE);

bMax=AfxGetApp()->GetProfileInt("WinStatus","Max",FALSE);

int a=atoi(strRect.Left(4)); //문자열을 int 로 바꿔준다.

int b=atoi(strRect.Mid(5,4)); //atoi 아스키 값을 int형으로 바꿔준다..

int c=atoi(strRect.Mid(10,4));

int d=atoi(strRect.Mid(15,4));

w.rcNormalPosition=CRect(a,b,c,d);

if(bMin)

{

w.showCmd=SW_SHOWMINIMIZED;

if(bMax)

{

w.flags=WPF_RESTORETOMAXIMIZED ;

}

else

{

w.flags=0;

}

}

else

{

if(bMax)

{

w.showCmd=SW_SHOWMAXIMIZED;

}

else

{

w.showCmd=SW_SHOWNORMAL;

}

}

this->SetWindowPlacement(&w); //설정된 값으로 윈도우를 그리게 한다..

//CFrameWnd::ActivateFrame(nCmdShow); //이건 반드시 주석처리한다..


40. progress Bar 쓰기

m_progress.SetRange(m_first,m_last); //Progress 범위설정하기

m_progress.SetStep(m_step); //Progress Step설정하기

//m_progress.StepIt(); //스텝만큼 움직이기

//또는 다음을 사용한다

for(int a=m_first;a<=m_last;a+=m_step) //a가 처음부터 끝까지

{

m_progress.SetPos(a); // 위치를 a에 맞춘다

Sleep(50); //천천히 움직이게한다

}


41. 파일대화상자 FileDialog 사용하기

void CConDlg1::OnFileopen() //파일열기 버튼

{

CFileDialog *fdlg; //파일대화상자 객체 생성 // 포인터로 만든다..

static char BASED_CODE szFilter[] = "Animate Video Files (*.avi)|*.avi|All Files

(*.*)|*.*||";

//필터를 만들어 준다..이건 할줄 모름..

fdlg =new CFileDialog(TRUE, ".avi", NULL, OFN_HIDEREADONLY |

OFN_OVERWRITEPROMPT,szFilter);

//대화상자 만들기..이렇게 해야댄다..

if(fdlg->DoModal()==IDOK) //이제..대화상자를 띠우고..

{ //OK 누르면 실행될 부분..

m_filename=fdlg->GetPathName(); //대화상자에서 경로를 받아서 저장.

UpdateData(FALSE);

}

}

//파일 다이얼로그 만들기

CFileDialog fdlg(TRUE,"avi",".avi",OFN_OEVRWRITEPROMPT,"Vidoe Files(*.avi)

|*.avi|All Files(*.*)|*.*||");

42. Animate Control 사용하기

m_animate.Open(m_filename); //파일을 연다

m_animate.Play(0,-1,1); //(처음프레임,마지막프레임,반복횟수)

m_animate.Stop(); //정지시키기

m_ani.SetAutoStart(TRUE);//자동으로 시작한다

43. Control 의 Style 바꿔주기

Control.ModyfyStyle(제거할스타일,추가할스타일); //스타일은 MSDN내용 참조


44. 시스템 날자바꾸기 버튼

SetSystemTime(),GetSystemTime() //GMT 표준시를 가져온다.

GetLocalTime(),SetLocalTime() //현재 지역시간을 가져온다.


SYSTEMTIME st;

GetLocalTime(&st); //현재 시간, 날자를 넣는다.

st.wYear=m_date2.GetYear();

st.wMonth=m_date2.GetMonth();

st.wDay=m_date2.GetDay();

SetSystemTime(&st);


45. 시스템 시간 바꾸기 버튼

UpdateData(TRUE);

SYSTEMTIME st;

GetLocalTime(&st);

st.wHour=m_time.GetHour();

st.wMinute=m_time.GetMinute();

st.wSecond=m_time.GetSecond();

SetLocalTime(&st);


46.시스템의 드라이브 문자 얻기

char temp[50];

GetLogicalDriveStrings(sizeof(temp),temp);

CString str,str1;

int n=0;

while(*(temp+n)!=NULL)

{

str=temp+n;

str1+= " "+str.Left(2);

n+=4;

}


47. 현재 작업경로 얻기

char temp[MAX_PATH]; //MAX_PATH 는 경로길이의 최대를 define 해놓은것.

GetCurrentDirectory(sizeof(temp),temp); // 현작업하는 경로(경로 길이,문자형);


48. Tree Control 사용하기

HTREEITEM hmov,hmus; //핸들을받을 변수 이게 잇어야 하위 디렉토리 생성가능

hmov=m_tree.InsertItem("영화",TVI_ROOT,TVI_LAST); //,TVI_ROOT,TVI_LAST는 default

hm1=m_tree.InsertItem("외화",hmov); //hmov 아래 “외화”트리 생성

CImageList m_image; //그림을 사용하기 위한 클래스다!! 알아두자..

m_tree.SetImageList(&m_image,TVSIL_NORMAL); //Tree View Style Image List => TVSIL

hmov=m_tree.InsertItem("영화",0,1,TVI_ROOT,TVI_LAST);//TVI_ROOT,TVI_LAST는 default

hmus=m_tree.InsertItem("가요",1,2);//("문자열",처음그림번호,선택시그림)

hm1=m_tree.InsertItem("외화",2,3,hmov); //그림 번호는 default 로 0이 들어간다..


49. List Control 사용하기

m_list.ModifyStyle(LVS_TYPEMASK, LVS_ICON); //리스트를 큰아이콘형태로 보인다

m_list.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);//리스트를 작은아이콘형태

m_list.ModifyStyle(LVS_TYPEMASK, LVS_LIST); //리스트를 리스트형태로 보인다

m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT); //리스트를 자세히형태로 보인다

CImageList m_treeimage; //이미지리스트

CImageList m_small, m_large;

m_large.Create(IDB_LARGE,32,0,RGB(255,255,255)); //이거는 클래스에서 추가해준거다

m_small.Create(IDB_SMALL,16,0,RGB(255,255,255));// (bmp ID값,

m_list.SetImageList(&m_large,LVSIL_NORMAL);

m_list.SetImageList(&m_small,LVSIL_SMALL);

CString name[]={"홍길동","진달래","한국남","개나리"};

CString tel[]={"400-3759","304-7714","505-9058","700-9898"};

CString born[]={"1980-1-1","1981-12-20","1980-05-15","1981-08-31"};

CString sex[]={"남자","여자","남자","여자"};

m_list.InsertColumn(0,"이름",LVCFMT_LEFT,70);

m_list.InsertColumn(1,"전화번호",LVCFMT_LEFT,80);

m_list.InsertColumn(2,"생일",LVCFMT_LEFT,90);

m_list.InsertColumn(3,"성별",LVCFMT_LEFT,50);

LVITEM it; //리스트 구조체

char temp[100];

for(int a=0;a<4;a++)

{

int n=(sex[a]=="남자")?0:1;

m_list.InsertItem(a,name[a],n); //insert item 은 행을 만들고..

it.mask=LVIF_TEXT|LVIF_IMAGE; //마스크 설정

it.iItem=a;

it.iSubItem=1;//열 설정

strcpy(temp,tel[a]); //이거 모하는거냐..

it.pszText=temp;

m_list.SetItem(&it); // setitem 열에 정보를 넣는다.


it.iSubItem=2; //열 설정

strcpy(temp,born[a]); //이거 모하는거냐..

it.pszText=temp;

m_list.SetItem(&it); // setitem 열에 정보를 넣는다.


it.iSubItem=3; //열 설정

strcpy(temp,sex[a]); //이거 모하는거냐..

it.pszText=temp;

m_list.SetItem(&it); // setitem 열에 정보를 넣는다.

50. Bitmap Button 사용하기

CBitmapButton 을 사용한다! CButton 에서 상속 받는클래스임..

m_button1.Create(NULL, WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,

CRect(310,20,370,50), this,IDC_MYBUTTON); //버튼만들기

m_button1.LoadBitmapsIDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE);//버튼의 그림설정

m_button1.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!


//그냥 버튼을 비트맵버튼으로 바꾸기 -> 버튼을 만든다 속성에서 OWNERDRA 속성에 체크!!

m_button2.LoadBitmaps(IDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE); //버튼 그림설정

m_button2.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!


51. 중복없는 난수발생하기

int su; //발생된 난수저장

int a,b;

BOOL bDasi; //숫자가중복될경우 다시하기위한 변수

for(a=0;a<9;a++) //난수 9개 발생

{

bDasi=TRUE;

while(bDasi)

{

bDasi=FALSE;

su=rand()%10; //난수발생

for(b=0;b

{

if(temp[b]==su) //중복이면

{

bDasi=TRUE; //중복이 잇으면 다시while 문을 실행한다

break;

}//if

}//for

}//while

temp[a]=su; //중복이 아니면 대입한다

52. 메뉴 범위로 사용하기

ON_COMMAND_RANGE(ID_LEVEL3,ID_LEVEL9,OnLevel); //범위메세지 발생

//메뉴 ID의 값이 연속된 숫자일 경우 범위로 지정해서 사용할수잇다


53. 한,영 전환함수

void CCustView::SetHangul(BOOL bCheck) //T:한글 F:영문 이건 외우자..

{

//뷰클래스의 윈도우 핸들포인터를 얻는다.

HIMC hm=ImmGetContext(this->GetSafeHwnd());

if(bCheck)

{

::ImmSetConversionStatus(hm,1,0);//1은 한글 0은 영문

}

else

{

::ImmSetConversionStatus(hm,0,0); //영문으로 바꿔준다

}

::ImmReleaseContext(this->GetSafeHwnd(),hm); //장치를 풀어준다

}

#include "imm.h" //헤더 반드시 추가하고

imm32.lib (라이브러리 파일)를 반드시 링크해주어야 한다!

**** 라이브러리 추가하기

프로젝트메뉴 -> 셋팅 -> 링크탭


54. DLL함수정의하기

임포트함수 : extern "C" __declspec(dllimport) 리터형 함수명(매개변수,...) ;

- 메인프로그램에서 DLL에 있는 함수를 호출할때 사용한다.


엑스포트함수 : extern "C" __declspec(dllexport) 리터형 함수명(매개변수,...)

{

내용;

}

출처: http://weezzle.net/1882
감사합니다.

Posted by SB패밀리
[개발/컬럼] 프로그래밍, 얼마나 배워야 하나요?

개발자에게 도움이 될만한 컬럼 같아서 올려봅니다.


 얼마나 배워야 하나요? | Codeway  2003.12.18  

류(ryujt)   http://cafe.naver.com/codeway/6


얼마나 배워야 하나요?


가끔 프로그래밍을 시작하려고 하거나 입문과정에 있는 분들에게 받는 질문이다. 그리고, 때로는 자신의 위치가 어느 정도가 되는지 항상 의문을 가지는 사람들에게로부터 같은 질문을 받는다.

본인이 그러한 것에 대한 권위적인 기준을 남에게 이야기할 만한 자격은 없지만, 나름대로 생각한 기준을 말해보고자 한다. 지금부터 이야기하는 것들은 본인 자신의 개인적인 기준일 뿐, 그 어떠한 권위적인 해석이나 정보를 바탕으로 하고 있지 않음을 미리 알려둔다.

우선 본인은 개발자의 등급분류를 준비과정, 입문과정, 초급, 중급, 고급, 특급으로 나누고자 한다. 여기서 입문과정과 준비과정이 다소 애매하다. 프로그래밍을 시작하면 무조건 입문과정이라고 분류할 수 있을 것이다. 하지만, 그 입문과정을 준비과정과 입문과정 둘로 나누고 싶은 데에는 이유가 있다.

준비과정은 공부를 시작한 지 최소 1개월 정도를 생각한다. 이 과정 중에는 개발에 대한 전반적인 이해와 개념을 공부하는 과정이다. 이러한 공부는 상당히 지겨운 편이다. 공부하는 동안 그 결과가 겉으로 들어나지 않기 때문이다. 하지만, 본인은 조금은 여유를 가지고 살펴보기를 강력히 권장한다.

대부분의 사람들은 무엇을 배울 때, 상당히 조급한 경향이 있다. 그리고, 그 조급함으로 인해 무엇인가 빨리 터득했다고 믿는다. 또한, 항상 자신이 빠르게 배웠음을 자랑으로 여긴다. 하지만, 이러한 사람은 임계점에서 실력이 향상되지 않는 슬럼프를 꼭 겪게 된다.

준비과정에서는 프로그래밍 자체보다는 자신이 선택한 개발영역에 대한 포괄적인 이해에 대하여 집중해야 한다. 이것을 공부해 나가는 동안 여러분들은 개발에 관한 전체적인 안목을 얻을 수 있는 좋은 기회가 된다. 편협한 지식은 가끔 문제해결을 하고자 할 때, 상당한 방해가 된다. 마땅히 공부할 자료를 제시하기는 어렵지만, 전산학개론 정도가 될 것이다.

입문과정은 6개월 정도를 생각한다. 이 과정 중에는 선택한 프로그래밍 언어 자체에 대한 수련을 하는 과정이다. 우선은 문법체계와 프로그래밍에 대한 절차에 대해서 공부를 하고, 이후 다양한 문제해결에 대한 실전적인 연습이 필요하다. 굳이 책에 집착하지 말고 자신이 하고자 하는 새로운 프로젝트 또는 문제를 설정하여, 이를 선택한 언어를 통해 해결해 나가는 동안 그 언어에 대한 감각을 길러 가는 것이다.

프로그래밍은 지식보다는 감각적인 요소가 상당히 중요하다. 그것은 프로그래밍을 실무에 접했을 때, 그 문제의 해결을 위한 그 어떠한 정석도 존재하지 않기 때문이다. 비슷한 업무에 대한 개발을 계속적으로 진행하더라도, 결국 구현단계에서는 언제나 새로운 문제가 기다리고 있다. 이러한 것들을 단순히 암기된 지식의 결합 및 수정으로는 해결할 수가 없다. 특히 알고리즘에 관한 공부를 게을리하지 않기를 바란다.

이제, 위에서 거론된 두 과정을 지나면 여러분들은 초급 프로그래머라고 할 수 있을 것이다. 그리고 이 초급은 최소 1년에서 2년 이상 거쳐야 한다고 생각한다. 이후 중급은 4년 이상 그리고 고급과 특급은 기간자체가 중요한 요소라고 생각하지 않는다.

입문과정이 전반적이 이해와 감각을 키우는 과정이라면, 초급과정에서는 스킬을 중점으로 공부하는 과정이라고 생각한다. 입문과정은 다소 포괄적인 공부를 하는 단계라고 한다면, 자신의 개발영역을 조금씩 넓혀가면서 심도 깊게 연구하는 단계이다.

초급, 중급, 고급, 특급 개발자들의 기준은 다음과 같이 정리해봤다.


초급
     - 제한된 범위의 단위 모듈에 대한 코딩 능력
     - 작성된 모듈에 대한 수정 및 변경 능력
     - 프로그래밍에 대하여 상급 개발자의 조언에 의존하는 단계

중급
     - 설계서를 토대로 스스로 프로그래밍의 문제를 해결할 수 있는 능력
     - 개발절차와 흐름에 대한 완벽한 이해

고급
     - 프로젝트의 위험요소를 미리 판별하고 대응하는 능력
     - 설계능력
     - 리더십
     - 풍부한 개발 경험

특급
     - 기술적인 사회적 흐름을 예측하는 능력
     - 대인관계와 대응에 대한 능력
     - 풍부한 사회 경험
     - 회사내의 개발전략을 작성하고 영업과 경영에 대한 조언 및 보조 능력
Posted by SB패밀리

1. DC얻기

  CClientDC dc(this);


2. Client 영역 구하기

  GetClientRect(&rect);

  WM_SIZE 메시지발생후 cx,cy 사용


3. 문자열 사각형안에 그리기

  pDC->DrawText(문자열,사각형,Style);

  Style : DT_BOTTOM - 문자열을 사각형 맨아래줄에배열 반드시DT_SINGLELINE과 함께사용

            DT_CENTER - 문자열을 가로중앙에 배치

            DT_VCENTER - 문자열을 세로중앙에 배치

            DT_LEFT,RIGHT - 문자열을 좌,우로 배치

            DT_SINGLELINE - 문자열을 한줄로만 쓴다


4. Brush 사용법

  CBrush brushname(RGB(red,green,blue)); //브러쉬 생성

  //이전Brush 저장, 새로운 Brush 선택

  CBrush *oldBrush=pDC->SelectObject(&brushname); 

  pDC->SelectObject(oldBrush); //원래의 브러쉬로 반환


5. Pen사용법

  CPen pen(Pen Style,RGB(red,green,blue)); //브러쉬생성

  //Style: PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_GEOMETRIC,PS_COSMETRIC

                - 펜종류 

              PS_ENDCAP_ROUND,PS_ENDCAP_SQUARE - 펜끝을 둥글게,각지게 설정

  CPen *oldPen=pDC->SelectObject(&pen); //이전Pen저장, 새로운 Pen설정

  pDC->SelectObject(oldPen); //펜반환


6. 화면다시그리기

  View Class에서 - Invalidate(TRUE) : 화면을 지우고다시그린다

                            Invalidate(FALSE) : 화면을 덮어씌운다

  UpdateAllViews(NULL);  // Doc Class에서 View 의 OnDraw 호출

  RedrawWindow();


7. 메시지,함수 수동으로 넣기 (EX)버튼클릭함수넣기

  헤더파일의 AFX_MSG_MAP 부분에 함수를 정의

  // .cpp파일의 AFX_MSG 부분에 메시지를 추가한다

  EX) afx_msg void funcName();

  // ID 등록:  View 메뉴의 Resource Symbol 에 들어가서 메뉴 ID 를 등록해준다..

  EX) ON_BN_CLICKED(ID_NAME,funcName).

  // .cpp파일의 맨아래에서 함수를 정의한다

  EX) void CClass::funcName() { ... }


8. 마우스커서 바꾸기

  리소스탭에서 커서를 그리고 저장한뒤 ID값은 준다음

  SetCapture(); //커서의입력을 클라이언트영역을 벗어나더라도 받아낸다

  SetCursor(AfxGetApp()->LoadCursor(nIDResource));

  //APP클래스의 LoadCursor View의 SetCursor 사용

  ReleaseCapture(); //SetCursor()상태를 해제한다


9. 색상표 사용하기

  CColorDialog dlg;

  if(dlg.DoModal()==IDOK) //Dialog 를 띄운후 OK버튼을누르면 실행할부분

  MemberFunc: GetColor() //선택된 색상을 받아온다 return 형은 COLORREF 형


10. 팝업메뉴 만들기

  CMenu menu; //메뉴 객체생성

  CMenu *pmenu; //메뉴 포인터생성

  menu.LoadMenu(IDR_MAINFRAME); //메뉴를 불러온다

  pmenu=menu.GetSubMenu(3); //메뉴의 3번째 메뉴를 가져온다

  menu.CheckMenuItem(ID_MENU,m_kind==ID_MENU ? MF_CHECKED : MF_UNCHECKED); //메뉴 체크하기 (메뉴 ID, ID 체크조건)

  pmenu->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this) //(TMP_Style,x좌표,y좌표,hWnd) 메뉴 띄우기


  *주의사항*

  [안내]태그제한으로등록되지않습니다-OnContextMenu(CWnd* pWnd, CPoint point)  //여기서 point 는 스크린 기준이고,

  OnRButtonDown(UINT nFlags, CPoint point)  //여기서 point 는 클라이언트 기준이다!


11. 클라이언트 포인터를 스크린 포인터로 변경

  ClientToScreen(&point);


12. 그림판기능

  if(m_flag==FALSE)  return;   //m_falg=그리기 기능 참,거짓설정  그리기 아니면 빠져나간다

        CClientDC dc(this);

        CPen myPen(PS_SOLID,m_width,m_color);

        CPen *pOldPen=dc.SelectObject(&myPen);

        switch(m_shape)

        {

        case ID_FREELINE: //자유선그리기

                dc.MoveTo(m_oldpt.x,m_oldpt.y); //지난포인터부터

                dc.LineTo(point.x,point.y); //새포인터까지 그린다

                break;

        case ID_RECT: //사각형그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.Rectangle(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y);  //지워지는 효과

                dc.Rectangle(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

                break;

        case ID_ELLIPSE: //원그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.Ellipse(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y);  //지워지는 효과

                dc.Ellipse(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

                break;

        case ID_LINE: //선그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

                dc.LineTo(m_oldpt.x,m_oldpt.y); //지난점까지 그은선을 지운다

                dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

                dc.LineTo(point.x,point.y); //새로운점까지 그린다

                break;

        }

        m_oldpt=point;  //바로이전값 보관

        dc.SelectObject(pOldPen); //펜 반환


13. MessageBox

  AfxMessageBox()->전역함수를 이용하영 메세지 박스를 출력한다.   //어디서든지 사용할수 잇다

  int CWnd::MessageBox("메세지","창제목","아이콘|버튼(상수값)");   //View클래스에서 사용한다

  아이콘 상수값  MB_IC[안내]태그제한으로등록되지않습니다 - xxONERROR,  

                MB_ICONWARNING, MB_ICONQUESTION,MB_ICONINFOMATION

                MB_SYSTEMMODAL //시스템모달 대화창 닫기전에 다른작업 못함

                MB_APPLMODAL //응용모달

  버튼 상수값    MB_OK, MB_OKCANCEL, MB_YESNO


14. OS 컨트롤

  ExitWindowEx(EWX_SHUTDOWN,NULL); //Shut Down

  ExitWindowsEx(EWX_FORCE,0); //강제종료

  ExitWindowsEx(EWX_LOGOFF,0); //로그오프

  ExitWindowsEx(EWX_POWEROFF,0); //Shut Down -> Turn Off

  ExitWindowsEx(EWX_REBOOT); //Shut Down -> Reboot


15. DialogBox 메시지 교환

  UpdateData(FALSE); // 컨트롤에 멤버변수의 내용을 표시해준다

  UpdateData(TRUE);  // 컨트롤 내용을 다이얼로그 클래스의 멤버변수로 저장


16. 자료변환

  atoi,itoa - int <=> ASCII(char) 변환

  str.Format(" %d %d",x,y); // int형을 문자열로 변환

  atol,ltoa - ASCII <=> long 변환

  atof - ACSII => float 변환

  fcvt,gcvt  - 실수를 text로 변환

  LPtoDP, DPtoLP - 장치좌표 <=> 논리좌표 변환


17. CEdit Class 사용하기

  CEdit e_str.SetSel(int StartChae, int EndChar); //처음문자부터 마지막까지 블록 지정

  CEdit e_str.GetSel(int SChar,int EChar); //블럭 지정한 처음문자와 마지막문자 받기

  CString str=m_str.Mid(SChar,EChar-SChar); //블럭지정한 부분을 가져온다


18. 컨트롤과 자료교환

  SetDlgItemText(컨트롤 ID,문자열) //컨트롤에 문자열을 넣는다

  GetDlgItemText(컨트롤 ID,문자열) //컨트롤의 내용을 문자열에 넣는다

  GetDlgItem(컨트롤 ID); //컨트롤의 주소를 가져온다


19. 상태바조작

  CMainFrame 생성자 위에

  static UINT indicators[] = //이안에 새로운 ID를 넣고 그 ID의 갱신핸들러를 만든다음 코딩

  pCmdUI->SetText("표시할내용“);


20. 수동으로 Bitmap 컨트롤 사용하기

  CStatic bitmap; //bitmap 컨트롤변수

  bitmap.SetBitmap(CBitmap m_bitmap); //컨트롤에 비트맵지정

  GetDlgItem(IDC_BITMAP)->ShowWindow(SW_SHOW,HIDE);  // 그림을 보이거나 숨긴다.

  

21. 응용프로그램 실행하기

  WinExec("프로그램경로“,SW_SHOW,HIDE); //응용프로그램실행,경로는 \\로 구분한다


22. Bitmap 사용하기

  CBitmap bitmap.LoadBitmap(IDC_BITMAP); //비트맵객체에 비트맵지정

  CDC memDC; //그림그릴 메모리DC생성

  MemDC.CreateCompatibleDC(pDC); //화면 DC와 메모리 DC 호환 생성

  CBitmap *pOldBitmap=MemDC.SelectObject(&m_bitmap); //메모리에 그림을그린다.

  pDC->BitBlt(int x, int y,int Width, int Height, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop);

//BitBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림시작x좌표,그림시작y좌표,스타일);

  pDC->StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop )

//StretchBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림x좌표,그림y좌표,메모리그림넓이,메모리그림높이,스타일);

MemDC.SelectObject(pOldBitmap); // 메모리DC반환


23. Font 바꾸기

  CFontDialog dlg;//폰트다이얼로그 생성

  LOGFONT m_logFont; //폰트받을변수선언

  if(dlg.DoModal()==IDOK) //폰트다이얼로그표시

  {dlg.GetCurrentFont(&m_logFont)} //선택된 폰트받기

  OnDraw()

   CFont newFont,*pOldFont; //폰트 객체 만들기

   newFont.CreateFontIndirect(&m_logFont); //폰트 생성

   pOldFont=(CFont *)pDC->SelectObject(&newFont); //폰트 선택

   OnCreate()

   CClientDC dc(this); //DC 생성

   CFont *pFont=dc.GetCurrentFont();        //클라이언트 영역의 폰트를

   pFont->GetLogFont(&m_logFont); //로그폰트 멤버값으로 지정


24. Font 만들기

         LOGFONT logfont; //폰트를 만든다

        logfont.lfHeight=50;               //문자열 높이

        logfont.lfWidth=0;                 //너비

        logfont.lfEscapement=0;            //문자열기울기

        logfont.lfOrientation=0;             //문자개별각도

        logfont.lfWeight=FW_NORMAL;     //굵기

        logfont.lfItalic=TRUE;             //이탤릭

        logfont.lfUnderline=TRUE; //밑줄

        logfont.lfStrikeOut=FALSE; //취소선

        logfont.lfCharSet=HANGUL_CHARSET; //필수

        logfont.lfOutPrecision=OUT_DEFAULT_PRECIS;               

        logfont.lfClipPrecision=CLIP_DEFAULT_PRECIS;     //가변폭폰트 고정폭폰트

        logfont.lfPitchAndFamily=DEFAULT_PITCH|FF_SWISS; //글꼴이름

        strcpy(logfont.lfFaceName,"궁서체");

        CClientDC dc(this);

        CFont newFont; //폰트객체생성

        newFont.CreateFontIndirect(&logfont); //폰트지정

        CFont *pOldFont=dc.SelectObject(&newFont); //폰트선택

        dc.TextOut(100,100,m_text);

        dc.SelectObject(pOldFont); //폰트반환


25. Font 만들기 2

  CFont newFont;

  newFont.CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename );

 CFont *pOldFont=dc.SelectObject(&newFont);


26. ComboBox 사용하기

  CComboBox combo; //콤보박스 선언

  combo.Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

  //Style - WS_CHILD|WS_VISIBLE

  int n=combo.GetCurSel(); //선택된 아이템의 index를 가져온다

  combo.AddString("문자열“); //문자열을 추가한다

  combo.GetLBText(n,str); //n번째 아이템을 str에 저장


27. Spin 사용하기

  Spin은 바로앞의 Tab Order에 따라 붙는다

  m_spinr.SetRange(1900,3000); //스핀 범위 지정

  m_spinr.SetPos(m_nYear); //스핀 위치 지정


28. CTime사용하기

  CTime time; //시간객체생성

  time=CTime::GetCurrentTime(); //현재시간을 저장

  time.GetYear(),time.GetMonth();,time.GetDay(),time.GetHour(),time.GetMinute(),time.GetSecond()


29. CListBox 메소드

  AddString("문자열");             //리스트에 문자열 추가

  DeleteString(index);             //리스트에서 항목 삭제

  GetCount()                     //전체 항목 갯수를 얻는다.

  GetSelcount()                   //선택된 항목 갯수 리턴

  GetSel()                //선택된 것인지 아닌지를 리턴한다 -> 양수 = TRUE , 음수 => FALSE

  GetText(int index,문자열변수)     //index 번째 문자열을 문자열 변수에 넣는다

  FindStringExact(문자열)    //지정 문자열의 index 값 리턴 -> 없으면 리턴값 LB_ERR 반환

  FindString("a")               //"a"로 시작하는 항목을 모두 찾는다.

  ResetCountent()            //모든 내용을 지운다.


30. 파일입출력

  프로젝트생성시 Step4 => Advanced => 저장파일확장자지정

  //이 클래스를 저장,로드가능한 클래스로 쓰겟다는 선언

  .h 파일에       DECLARE_SERIAL(CSawon)  

  .cpp 파일에     IMPLEMENT_SERIAL(CSawon,CObject,1) //이거를 해야 저장이 가능하다

  void CFileioDoc::Serialize(CArchive& ar)

        if (ar.IsStoring()) 

        {

            ar< <             //저장하기

        }

        else    //열기

        {

             ar>>m_shape; //불러올걸 쓴다. 읽을때도순서대로읽어야한다

        }

 


31. MicroSoft FlexGrid 사용하기!

  CMSFlexGrid m_Grid; //FlexGrid 컨트롤 변수

  CString strTitle[]={"고객코드","고객성명","고객포인트","신장","몸무게","고객등급","BMT지수","판정결과"}; // Grid 의 제목에 넣을문자배열

  int Width[]={900,900,1100,800,800,900,1000,900}; // Grid 의 열넓이 지정할 배열

  m_Grid.SetRows(m_cnt+2); //전체행수 지정

  m_Grid.SetCols(8);//전체열수 지정

  m_Grid.Clear(); //지우기

  m_Grid.SetFixedCols(0); //고정열은 없다.

  m_Grid.SetRow(0); // 행선택

  for(int i=0;i<=7;i++)

  {

     m_Grid.SetColWidth(i,Width[i]); //열 넓이 설정

     m_Grid.SetCol(i); //열 선택

     m_Grid.SetText(strTitle[i]); // 선택된행, 선택된열에 Text 를 넣는다

  }


32. 4대 Class간 참조

  //각각 헤더파일 include

  #include "MainFrm.h" //메인프레임 헤더파일

  #include "ClassDoc.h"   //Doc클래스 헤더파일

  #include "ClassView.h" //View를 include 할때는 반드시 Doc 헤더파일이 위에잇어야한다

  #include "Class.h" //APP Class 의 헤더파일


  void CClassView::OnMenuView() //뷰클래스

    CClassApp *pApp=(CClassApp *)AfxGetApp();   //View -> App
    CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd();  //View -> MainFrm

    //View -> MainFrm -> Doc

    CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument();

    CClassDoc *pDoc=(CClassDoc *)GetDocument();        //View -> Doc


    //MainFrame 클래스

    CClassView *pView=(CClassView *)GetActiveView();  //MainFrm -> View

    CClassDoc *pDoc=(CClassDoc *)GetActiveDocument();  //MainFrm -> Doc

    CClassApp *pApp=(CClassApp *)AfxGetApp(); //MainFrm -> App


    //Doc 클래스

    CClassApp *pApp=(CClassApp *)AfxGetApp(); //Doc -> App

    CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //Doc -> MainFrm

    // Doc -> MainFrm -> View

    CClassView *pView=(CClassView *)pMain->GetActiveView();

    CClassView *pView=(CClassView *)m_viewList.GetHead();      // Doc -> View


    //App 클래스

    CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //App -> MainFrm

    //App -> MainFrm -> View

    CClassView *pView=(CClassView *)pMain->GetActiveView();

    //App -> MainFrm -> Doc

    CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument();


33. ToolBar 추가하기

  CMainFrame 으로 가서 멤버변수 추가

  CToolBar m_wndToolBar1;

  OnCreate 로 가서 다음 내용을 추가해준다

  (위의 toolbar 부분을 복사하고 이름만 바꾸면 된다.3군데..)

  if (!m_wndToolBar1.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE |

         CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |

         CBRS_SIZE_DYNAMIC) || !m_wndToolBar1.LoadToolBar(IDR_TOOLBAR1))

        {

                TRACE0("Failed to create toolbar\n");

                return -1;      // fail to create

        }


  그 함수내에서 //TODO 아래에 내용추가..역시..복사해서 이름만 바꾸면 된다.

        m_wndToolBar1.EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM);

        //DockControlBar(&m_wndToolBar1);   <= 이부분 대신..

        이거를 넣는다..

        CRect toolRect; //툴바 영역을 얻을 사각형

        this->RecalcLayout(); //현상태의 Client 영역을 구해서 저장한다

        m_wndToolBar.GetWindowRect(&toolRect); //툴바영역을 저장한다

        toolRect.left+=1; //사각형의 왼쪽을 1Pixel 줄인다

        //ToolRect에 툴바를 붙인다

        DockControlBar(&m_wndToolBar1,AFX_IDW_DOCKBAR_TOP,&toolRect);

        return 0;


34. ToolBar에 ComboBox붙이기

  CComboBox m_combo; //객체생성

  ID 등록 => view 메뉴 => resource symbol => new => ID_COMBO

  oncreate 에 내용 추가 (콤보를 만들고 표시하는 내용)

        m_wndToolBar.SetButtonInfo(10,IDC_COMBO,TBBS_SEPARATOR,150); 

        //툴바의 10번째버튼을 편집한다

        CRect itemRect; //콤보를넣을 사각형을 만든다

        m_wndToolBar.GetItemRect(10,&itemRect); //툴바의 10번째 버튼을 사각형에 넣는다
        itemRect.left+=5; //앞여백

        itemRect.right+=5; //뒤여백

        itemRect.bottom+=100; //콤보가열릴 공간확보

        m_combo.Create(WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,itemRect,&m_wndToolBar,IDC_COMBO);

        //콤보박스를 툴바에 붙여준다

        m_combo.AddString("이름"); //내용추가

        m_combo.SetCurSel(0); //셀 선택


35.  Toolbar에 수동으로넣은 ComboBox 사용하기

  afx_msg void [안내]태그제한으로등록되지않습니다-xxOnSelectCombo(); //원형

  ON_CBN_SELCHANGE(IDC_COMBO,[안내]태그제한으로등록되지않습니다-xxOnSelectCombo) //메세지맵에 추가

  CMainFrame *pMain=(CMainFrame *)GetParent(); //메인프레임 주소참조

  CComboBox *pCom=(CComboBox *)(pMain->m_wndToolBar.GetDlgItem(IDC_COMBO));

  //콤보박스의 주소를 가져온다, 접근할 때 메인프레임 -> 툴바 -> 콤보박스 의 순서로 가야한다

  int n=pCom->GetCurSel(); //현재선택된 셀의 인덱스를 가져온다

  if(n==CB_ERR) return; //선택된셀이 없으면 중지한다

  CString str;

  pMain->m_combo.GetLBText(n,str); //선택된셀의 Text를 가져온다


36. UPDATE_COMMAND 사용하기

  pCmdUI->Enable(TRUE); //버튼 활성화

  pCmdUI->SetText((bAdd)?"취소":"신규"); //버튼의 text 설정

  pCmdUI->SetCheck(TRUE);//버튼 체크


37. 프로그램정보저장

  CWinApp::GetProfileString(섹션명,항목명,기본값); // 함수를 사용한다. (문자열)

  CWinApp::GetProfileInt(섹션명,항목명,기본값); //불러올때사용 (숫자) 

  CWinApp::WriteProfileString(섹션명,항목명,값); //저장할때 사용 (문자열)

  CWinApp::WriteProfileInt(섹션명,항목명,값); //저장할때 사용 (숫자)

 //불러올때 사용할함수

  void CMainFrame::ActivateFrame(int nCmdShow) //프로그램 실행후 프레임생성될때 실행

  //저장할 때 WM_DESTROY 메시지 사용


38. 컨트롤바 표시하기

  CMainFrame *pMain=(CMainFrame *)GetParent();//MainFrame 주소가져오기

  //툴바를 bTool2 에따라 보이고 감춘다

  pMain->ShowControlBar(&pMain->m_wndToolBar,bTool1,FALSE);


39. Window 창크기,위치정보 저장하기

  MainFrame 의 WM_DESTROY 에

    WINDOWPLACEMENT w;

    this->GetWindowPlacement(&w); //윈도우의 정보를 저장한다.

    CString strRect;

    strRect.Format("%04d,%04d,%04d,%04d", //04d 는 4자리 확보하고 남은건 0으로 채워라

         w.rcNormalPosition.left,w.rcNormalPosition.top,

         w.rcNormalPosition.right,w.rcNormalPosition.bottom); //윈도우의 위치,크기 확보..

        

        BOOL bMax,bMin; //윈도우의 상태를 저장하기위한 변수

        //w.falg 는 이전상태의 정보를 가지고 잇다!!

        if(w.showCmd==SW_SHOWMINIMIZED)           //최소화 상태

        {

                bMin=TRUE;

                if(w.flags==0) //falg 값이 0 이면 이전 상태가 보통상태이다!!

                        bMax=FALSE;

                else   //이전상태가 최대화 상태

                        bMax=TRUE;

        }

        else                            

        {

                if(w.showCmd==SW_SHOWMAXIMIZED) //최대화상태

                {

                        bMax=TRUE;

                        bMin=FALSE;

                }

                else //보통 상태

                {

                        bMax=FALSE;

                        bMin=FALSE;

                }

        }

        AfxGetApp()->WriteProfileString("WinStatus","Rect",strRect);

        AfxGetApp()->WriteProfileInt("WinStatus","Max",bMax);

        AfxGetApp()->WriteProfileInt("WinStatus","Min",bMin);

 


//읽어올차례..

ActivateFrame 함수로 가서

        WINDOWPLACEMENT w;  //윈도우의 상태를 저장하는 구조체..

        BOOL bMax,bMin;               //최대,최소상태를 저장할 변수

        CString strRect; //창크기를 받아올 변수

        strRect=AfxGetApp()->GetProfileString("WinStatus","Rect","0000,0000,0500,0700");

        bMin=AfxGetApp()->GetProfileInt("WinStatus","Min",FALSE);

        bMax=AfxGetApp()->GetProfileInt("WinStatus","Max",FALSE);

        int a=atoi(strRect.Left(4)); //문자열을 int 로 바꿔준다.

        int b=atoi(strRect.Mid(5,4));     //atoi 아스키 값을 int형으로 바꿔준다..

        int c=atoi(strRect.Mid(10,4));

        int d=atoi(strRect.Mid(15,4));

        w.rcNormalPosition=CRect(a,b,c,d);

        if(bMin)

        {

                w.showCmd=SW_SHOWMINIMIZED;

                if(bMax)

                {

                        w.flags=WPF_RESTORETOMAXIMIZED  ;

                }

                else

                {

                        w.flags=0;

                }

        }

        else

        {

                if(bMax)

                {

                        w.showCmd=SW_SHOWMAXIMIZED;

                }

                else

                {

                        w.showCmd=SW_SHOWNORMAL;

                }

        }

        this->SetWindowPlacement(&w); //설정된 값으로 윈도우를 그리게 한다..

        

        //CFrameWnd::ActivateFrame(nCmdShow); //이건 반드시 주석처리한다..


40. progress Bar 쓰기

  m_progress.SetRange(m_first,m_last); //Progress 범위설정하기

  m_progress.SetStep(m_step); //Progress Step설정하기

  //m_progress.StepIt(); //스텝만큼 움직이기

  //또는 다음을 사용한다

  for(int a=m_first;a<=m_last;a+=m_step) //a가 처음부터 끝까지

  {

    m_progress.SetPos(a); // 위치를 a에 맞춘다

    Sleep(50); //천천히 움직이게한다

  }


41. 파일대화상자 FileDialog 사용하기

  void CConDlg1::OnFileopen()  //파일열기 버튼

  {

        CFileDialog *fdlg; //파일대화상자 객체 생성 // 포인터로 만든다..

        static char BASED_CODE szFilter[] = "Animate Video Files (*.avi)|*.avi|All Files

                                                                (*.*)|*.*||";

        //필터를 만들어 준다..이건 할줄 모름..

        fdlg =new CFileDialog(TRUE, ".avi", NULL, OFN_HIDEREADONLY |

                                               OFN_OVERWRITEPROMPT,szFilter);

        //대화상자 만들기..이렇게 해야댄다..

        if(fdlg->DoModal()==IDOK) //이제..대화상자를 띠우고..    

        {                               //OK 누르면 실행될 부분..

                m_filename=fdlg->GetPathName();        //대화상자에서 경로를 받아서 저장.

                UpdateData(FALSE);    

        }

  }

  //파일 다이얼로그 만들기

  CFileDialog fdlg(TRUE,"avi",".avi",OFN_OEVRWRITEPROMPT,"Vidoe Files(*.avi)

                                 |*.avi|All Files(*.*)|*.*||");

 

42. Animate Control 사용하기

  m_animate.Open(m_filename); //파일을 연다

  m_animate.Play(0,-1,1);  //(처음프레임,마지막프레임,반복횟수)

  m_animate.Stop(); //정지시키기

  m_ani.SetAutoStart(TRUE);//자동으로 시작한다

 

43. Control 의 Style 바꿔주기

  Control.ModyfyStyle(제거할스타일,추가할스타일); //스타일은 MSDN내용 참조


44. 시스템 날자바꾸기 버튼

  SetSystemTime(),GetSystemTime() //GMT 표준시를 가져온다.

  GetLocalTime(),SetLocalTime()  //현재 지역시간을 가져온다.


        SYSTEMTIME st;

        GetLocalTime(&st); //현재 시간, 날자를 넣는다.

        st.wYear=m_date2.GetYear();

        st.wMonth=m_date2.GetMonth();

        st.wDay=m_date2.GetDay();

        SetSystemTime(&st);


45. 시스템 시간 바꾸기 버튼

        UpdateData(TRUE);

        SYSTEMTIME st;

        GetLocalTime(&st);

        st.wHour=m_time.GetHour();

        st.wMinute=m_time.GetMinute();

        st.wSecond=m_time.GetSecond();

        SetLocalTime(&st);


46.시스템의 드라이브 문자 얻기

        char temp[50];

        GetLogicalDriveStrings(sizeof(temp),temp);

        CString str,str1;

        int n=0;

        while(*(temp+n)!=NULL)

        {

                str=temp+n;

                str1+= " "+str.Left(2);

                n+=4;

        }


47. 현재 작업경로 얻기

  char temp[MAX_PATH]; //MAX_PATH 는 경로길이의 최대를 define 해놓은것.

  GetCurrentDirectory(sizeof(temp),temp);  // 현작업하는 경로(경로 길이,문자형);


48. Tree Control 사용하기

  HTREEITEM hmov,hmus; //핸들을받을 변수 이게 잇어야 하위 디렉토리 생성가능

  hmov=m_tree.InsertItem("영화",TVI_ROOT,TVI_LAST); //,TVI_ROOT,TVI_LAST는 default

  hm1=m_tree.InsertItem("외화",hmov);  //hmov 아래 “외화”트리 생성

  CImageList m_image; //그림을 사용하기 위한 클래스다!! 알아두자..

  m_tree.SetImageList(&m_image,TVSIL_NORMAL); //Tree View Style Image List => TVSIL

  hmov=m_tree.InsertItem("영화",0,1,TVI_ROOT,TVI_LAST);//TVI_ROOT,TVI_LAST는 default

  hmus=m_tree.InsertItem("가요",1,2);//("문자열",처음그림번호,선택시그림)

  hm1=m_tree.InsertItem("외화",2,3,hmov); //그림 번호는 default 로 0이 들어간다..


49. List Control 사용하기

  m_list.ModifyStyle(LVS_TYPEMASK, LVS_ICON); //리스트를 큰아이콘형태로 보인다

  m_list.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);//리스트를 작은아이콘형태

  m_list.ModifyStyle(LVS_TYPEMASK, LVS_LIST); //리스트를 리스트형태로 보인다

  m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT); //리스트를 자세히형태로 보인다

 

  CImageList m_treeimage; //이미지리스트

  CImageList m_small, m_large;

  m_large.Create(IDB_LARGE,32,0,RGB(255,255,255)); //이거는 클래스에서 추가해준거다

  m_small.Create(IDB_SMALL,16,0,RGB(255,255,255));// (bmp ID값,

  m_list.SetImageList(&m_large,LVSIL_NORMAL);

  m_list.SetImageList(&m_small,LVSIL_SMALL);

  CString name[]={"홍길동","진달래","한국남","개나리"};

  CString tel[]={"400-3759","304-7714","505-9058","700-9898"};

  CString born[]={"1980-1-1","1981-12-20","1980-05-15","1981-08-31"};

  CString sex[]={"남자","여자","남자","여자"};

       

  m_list.InsertColumn(0,"이름",LVCFMT_LEFT,70);

  m_list.InsertColumn(1,"전화번호",LVCFMT_LEFT,80);

  m_list.InsertColumn(2,"생일",LVCFMT_LEFT,90);

  m_list.InsertColumn(3,"성별",LVCFMT_LEFT,50);

        LVITEM it; //리스트 구조체

        char temp[100];

        for(int a=0;a<4;a++)

        {       

                int n=(sex[a]=="남자")?0:1;

                m_list.InsertItem(a,name[a],n); //insert item 은 행을 만들고..

                it.mask=LVIF_TEXT|LVIF_IMAGE; //마스크 설정

                it.iItem=a;

                it.iSubItem=1;//열 설정

                strcpy(temp,tel[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                     // setitem 열에 정보를 넣는다.


                it.iSubItem=2; //열 설정

                strcpy(temp,born[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                      // setitem 열에 정보를 넣는다.


                it.iSubItem=3; //열 설정

                strcpy(temp,sex[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                      // setitem 열에 정보를 넣는다.

 

50. Bitmap Button 사용하기

  CBitmapButton 을 사용한다! CButton 에서 상속 받는클래스임..

  m_button1.Create(NULL, WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,

                                 CRect(310,20,370,50), this,IDC_MYBUTTON); //버튼만들기

  m_button1.LoadBitmapsIDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE);//버튼의 그림설정

  m_button1.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!


 //그냥 버튼을 비트맵버튼으로 바꾸기 -> 버튼을 만든다 속성에서 OWNERDRA 속성에 체크!!

  m_button2.LoadBitmaps(IDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE); //버튼 그림설정

  m_button2.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!


51. 중복없는 난수발생하기

        int su; //발생된 난수저장

        int a,b;

        BOOL bDasi; //숫자가중복될경우 다시하기위한 변수

        for(a=0;a<9;a++)  //난수 9개 발생

        {

                bDasi=TRUE;

                while(bDasi)

                {

                        bDasi=FALSE;

                        su=rand()%10; //난수발생

                        for(b=0;b

                        {

                                if(temp[b]==su)  //중복이면

                                {

                                        bDasi=TRUE; //중복이 잇으면 다시while 문을 실행한다

                                        break;

                                }//if

                        }//for

                }//while

                temp[a]=su; //중복이 아니면 대입한다

 

52. 메뉴 범위로 사용하기

  ON_COMMAND_RANGE(ID_LEVEL3,ID_LEVEL9,OnLevel); //범위메세지 발생

  //메뉴 ID의 값이 연속된 숫자일 경우 범위로 지정해서 사용할수잇다


53. 한,영 전환함수

  void CCustView::SetHangul(BOOL bCheck) //T:한글 F:영문 이건 외우자..

  {

      //뷰클래스의 윈도우 핸들포인터를 얻는다.

      HIMC hm=ImmGetContext(this->GetSafeHwnd());

      if(bCheck)

      {

          ::ImmSetConversionStatus(hm,1,0);//1은 한글 0은 영문

      }

      else

      {

          ::ImmSetConversionStatus(hm,0,0); //영문으로 바꿔준다

      }

      ::ImmReleaseContext(this->GetSafeHwnd(),hm); //장치를 풀어준다

  }

  #include "imm.h" //헤더 반드시 추가하고

  imm32.lib (라이브러리 파일)를 반드시 링크해주어야 한다!

  **** 라이브러리 추가하기

  프로젝트메뉴 -> 셋팅 -> 링크탭


54. DLL함수정의하기

  임포트함수 :  extern "C"  __declspec(dllimport)   리터형  함수명(매개변수,...) ;

  - 메인프로그램에서 DLL에 있는 함수를 호출할때 사용한다.


  엑스포트함수 :  extern "C"  __declspec(dllexport)   리터형  함수명(매개변수,...)

                      {

                             내용;

                      }

출처: http://weezzle.net/1882
감사합니다.

Posted by SB패밀리

The Joel Test: 나은 코딩을 위한 12단계


글 : Joel Spolsky
번역 : B.K. Chung 정봉겸
감수 : Jang Han Goo 구장한
2000년 8월 9일

SEMA에 대해서 들어보신 적이 있습니까? 소프트웨어 팀이 얼마나 잘하는지를 재는 나름대로 복잡한 시스템입니다. 앗, 아니! 그 링크를 누르지 마세요. SEMA를 "이해"만 하는데 아마 6년정도가 걸릴것입니다. 그래서 소프트웨어 팀이 얼마나 좋은지 등급을 매길 수 있는 - 좀 무책임하고 되는대로의 - 자체적인 버젼의 테스트를 만들었습니다. 이 테스트의 장점은 3분정도밖에 걸리지 않는다는 것입니다. 절약되는 시간으로 의대에 가서 공부할 수도 있을 것입니다.

The Joel Test

  1. Source Control(소스 컨트롤)을 사용하십니까?
  2. 한번에 빌드를 만들어낼 수 있습니까?
  3. daily build(일별 빌드)를 만드십니까?
  4. 버그 데이타베이스를 가지고 있습니까?
  5. 새로운 코드를 작성하기 전에 버그들을 잡습니까?
  6. up-to-date(최신) 스케줄을 가지고 있습니까?
  7. spec(설계서)를 가지고 있습니까?
  8. 프로그래머들이 조용한 작업환경을 가지고 있습니까?
  9. 돈이 허락하는 한도내의 최고의 툴들을 사용하고 있습니까?
  10. 테스터들을 고용하고 있습니까?
  11. 신입사원들은 면접때 코드를 직접 짜는 실기시험을 봅니까?
  12. hallway usability testing(무작위 사용성 테스팅)을 하십니까?

Joel Test이 특별한 점은 각 직문에 예/아니오로 바로 대답할 수 있다는 것이다. lines-of-code-per-day(하루동안 산출되는 코드의 줄수)나 average-bugs-per-inflection-point(산출 시점의 평균 버그수) 같은 것은 알 필요가 없습니다. "예"에 해당 하는 질문에 1점씬 가산됩니다. 하지만 이 테스트는 핵 원자로에 사용하는 소프트웨어가 안전한지를 검사하는등 에는 사용하지 말아주십시오.

12점은 완벽, 11은 충분한 점수이지만 10점이나 그 이하는 심각한 문제가 있다는 신호입니다. 사실은 대개의 소프트웨어 회사 들이 2~3점을 받고 있고, 심각한 도움을 필요로 하고 있습니다. Microsoft같은 회사는 12점 만점을 받고 있습니다.

당연한 이야기지만 이것들만으로 성공과 실패를 가를 수는 없습니다. 특히, 아무도 필요없는 제품을 굉장히 훌륭한 소프트웨어 팀이 만들고 있다면, 역시나 아무도 원하지 않을 것입니다. 반대로 이런 방식을 따르지 않는 명인들이 세상을 바꾸는 소프트웨어 를 만드는 경우로 생각할 수 있겠습니다. 그러나, 이 12가지 이외의 요소를 모두 동등하게 놓고 본다면, 이들만 제대로 한다면 지속적으로 좋은 결과를 내는 잘 훈련된 팀이 될 것입니다.

1. Source Control(소스 컨트롤)을 사용하십니까?

상용 소스 컨트롤 패키지들도 사용해보았고, 무료로 사용할 수 있는 CVS도 사용해보았습니다. CVS는 무료이기는 하지만 충분합니다. 그렇지만 소스 컨트롤이 없다면 프로그래머들을 조율하는 일이 상당히 피곤할 것입니다. 프로그래머들은 다른 사람들이 어떤 것을 했는지 알 수 있는 방법이 없습니다. 이를 사용하면 실수를 쉽게 롤백할 수 있습니다. 소스 컨트롤의 다른 장점은 소스코드 자체가 모든 프로그래머의 하드디스크에 체크아웃(check out)되어 있다는 것입니다. 소스 컨트롤을 사용하는 프로젝트에서 코드를 날렸다는 이야기를 들어본 적이 없습니다.

2. 한번에 빌드를 만들어낼 수 있습니까?

"최신의 소스로부터 몇단계를 거쳐서 완제품(shipping build)을 만들 수 있습니까?"라는 의미의 질문입니다. 잘 되어있는 팀인 경우라면 하나의 스크립트로 checkout부터 시작하여 각 소스를 리빌드(rebuild)하고 각 버젼, 언어, #ifdef같은 조건별로 실행파일을 만들어내어 마지막 CDROM 레이아웃, 다운로드할 수 있는 웹사이트를 만들어 내는 정도까지 되어 있을 수 있겠습니다.

만일 이 과정이 하나의 단계 이상을 거친다면, 여기서부터 에러가 발생할 확률이 생깁니다. 정해진 기일이 가까워질수록 "마지막" 버그를 수정하고 실행파일을 만드는 등을 위해 빠른 사이클을 필요로 할 것입니다. 코드를 컴파일하고 설치파일을 구성하는데에 20단계가 필요하다면 급박한 시간때문에 사소한 실수를 저지르게 될 것입니다.

필자가 마지막으로 근무했던 회사에서는 이런 이유로 WISE를 InstallShield(역자주 : 두 제품 모두 설치본을 만들기위한 도구 입니다.)로 교체하였습니다. 설치 과정을 스크립트를 통해서 NT 스케줄러로 밤새에 자동으로 실행하도록 하고자 하였는데, WISE는 스케줄러로 실행할 수 없던 이유입니다. (WISE의 친절한 분들이 최신 버젼에는 이것이 가능하다고 알려왔습니다.)

3. daily build(일별 빌드)를 만드십니까?

소스 컨트롤을 사용하다 보면 누군가가 빌드를 실패하게 만드는 코드를 체크인 할 수 있습니다. 예를 들면, 새로운 소스파일을 추가해서 그 사람의 컴퓨터에서는 잘 컴파일되지만, 이를 코드 레파지토리(repository)에는 추가를 하지 않았을 수 있습니다. 이 사람은 이를 잊고 만족한 상태에서 컴퓨터를 잠그고 집에 돌아갑니다. 그렇지만 이로 인해 다른사람들은 작업을 할 수 없게 되고 결국 찝찝하지만 결과없이 집으로 돌아갈 수 밖에 없습니다.

모르는 사이에 빌드를 실패하는 이런 컴파일 오류가 나지 않도록 daily build를 만들게 됩니다. 큰 팀에서는 이런 경우를 위해서 daily build를 매일 오후 - 점심시간등 - 에 합니다. 사람들은 점심시간 이전에 될 수 있는 한 많이 체크인을 합니다. 점심을 먹으로 갔다가 다시 돌아오면 빌드는 이루어져 있습니다. 빌드가 실패하면, 사람들은 빌드가 성공한 이전 소스로 작업을 하면 됩니다.

엑셀팀에서는 누군가 빌드를 깨면 벌칙으로 다른 사람이 다시 깰때까지 빌드를 관리하도록 벌칙을 주었습니다. 이는 빌드를 깨면 받는 벌칙으로써 뿐만 아니라 모든 이들이 돌아가면서 빌드를 관리할 수 있게하여, 어떻게 돌아가는 지를 익히게 하는 방법으로써도 좋았습니다.

daily build에 관해 더 자세히 아시려면 저의 기사 daily builds are your friend를 읽으십시오.

4. 버그 데이타베이스를 가지고 있습니까?

뭐라고 반박하셔도 확신합니다. 코드를 짜고 있다면 설령 혼자 짜더라도 정리된 버그 명세 데이타베이스를 가지고 있지 않다면 낮은 질의 코드로 제품을 출시할 것입니다. 많은 프로그래머들이 머리로 버그들을 모두 기억할 것이라고 생각합니다. 말도 안되는 이야기입니다. 제 경우에는 한번에 2~3개의 버그밖에 기억을 못하고, 다음날이 되거나 출시를 위해 급해지면 전부 잊어버리게 됩니다. 버그를 제대로 트래킹해야합니다.

버그 데이타베이스는 복잡할 수도 있고, 간단할 수도 있습니다. 최소한으로 갖추어야할 요소는 다음과 같습니다:

  • 버그를 완벽하게 재현할 수 있는 과정
  • 버그가 없었다면 이루어졌어야할 결과(동작)
  • 버그로 인하여 생긴 결과(동작)
  • 누가 이 버그에 할당되어 있는지
  • 고쳐진 버그인지 아닌지

버그 데이타베이스를 사용하지 않는 이유가 제품들이 너무 복잡해서라면, 이것들을 포함한 5컬럼의 테이블을 만들어서 사용하기 시작하세요.

버그 트래킹에 관해 더 읽으려면, Painless Bug Tracking을 읽으세요.

5. 새로운 코드를 작성하기 전에 버그들을 잡습니까?

마이크로소프트 Windows용 Word의 첫 버젼은 죽음의 프로젝트였습니다. 끝이 없었습니다. 계속해서 스케줄을 펑크냈습니다. 팀 전체는 말도 안되는 시간동안 일했고, 계속해서 연기되고 또 연기되었습니다. 그 스트레스는 엄청났습니다. 빌어먹을 제품이 몇년 후에 출시되었을때, 마이크로소프트는 팀 전원을 Cancun으로 휴가보내고, 이 원인을 분석하기 시작했습니다.

그들이 깨닫게 된 것은 프로젝트 매니저들이 스케줄을 너무 강요하였기 때문에 프로그래머들은 코딩을 빨리 할 수 밖에 없었습니다. 게다가 버그를 고치는 단계는 스케줄에 아예 존재하지 않았습니다. 결과적으로 질이 아주 나쁜 코드를 만들게 되었습니다. 버그 갯수를 줄이려는 노력은 전혀 하지 않았습니다. 한 프로그래머는 텍스트의 높이를 계산하는 루틴 대신에 "return 12;"로 대체하여 버그 리포트로부터 이 값이 어떤 영향을 주었는지를 알고자 했습니다. 스케줄은 단지 버그일 수 밖에 없는 기능들을 모아 놓은 체크리스트였습니다. 나중에 이 상황을 "무한 결함 방식(infinite defects methodology)"이라고 이름지었습니다.

문제를 해결하기 위해서 마이크로소프트는 반대의 "무결함 방식(zero defects methodology)"라는 방식을 체택했습니다. 많은 프로그래머들은 경영진들의 명령에 의해서 버그 갯수를 줄일 수 있다고 생각했음직한 이 방식의 이름 탓에 이를 비웃었습니다. 하지만 실제로는 "무결함(zero defects)"이라는 이름은 주어진 시간에 가장 우선순위가 높은 것은 코딩하기전에 버그를 잡는 것이란 사실을 지칭하는 말이었습니다. 이유는 다음과 같습니다.

일반적으로 버그를 고치지 않고 방치하는 시간이 길어지면 길어질수록 고치는데 더 많은 시간과 금전이 요구된다는 것입니다.

예를 들면, 오타나 문법오류등은 컴파일러가 쉽게 잡아서 고치는데도 별로 문제가 되지 않습니다.

만일 버그가 처음 실행시에 발생하여 보이게 되면, 모든 코드가 머릿속게 생생하게 존재하기에 바로 고칠 수 있을 것입니다.

며칠전에 작성한 코드에서 버그를 찾게 되면 이를 고치기 위해 조금 시간이 더 걸릴 것입니다. 아마도 코드를 다시 보게 되면 대부분의 내용이 기억나고 적정한 시간내에 버그를 고칠 수 있을 것입니다.

하지만 몇달전에 작성한 코드에서 버그가 발견된다면 이미 그 코드에 관해서 많은 것이 이미 생각나지 않을 것이고, 고치기도 상대적으로 힘들 것입니다. 그때쯤 되면 다른 사람의 코드를 수정하고 있는 와중일지도 모르고, 그사람은 Aruba로 휴가를 떠나있을지도 모릅니다. 이렇게 된다면 버그를 고치는 것은 기술을 익히는 것같이 되어버릴 것입니다. 천천히 꼼꼼하게 그리고 주의 깊게 코드를 살펴봐야 하고, 물론 문제를 해결하는데에 얼마나 걸릴지 정확하게 판단하기 힘든 상황이 될 것입니다.

게다가 이미 출하된 코드에서 버그를 발견한다면, 이를 고치는데에 큰 대가를 치뤄야할지도 모를 것입니다.

이렇게 시간이 적게 들기 때문이라는 이유가 하나의 이유가 됩니다. 또다른 이유는 버그를 수정하는데 걸리는 시간을 예상하는 것보다는 새로운 코드를 작성하는데 걸리는 시간을 예상하기가 훨씬 쉽기 때문입니다. 예를 들면, 내가 당신에게 리스트를 소트하는 코드를 만드는데 얼마나 걸리냐고 물어본다면, 꽤 정확한 대답을 할 수 있을 것입니다. 그렇지만, 질문을 바꿔서 당신의 코드가 Internet Explorer 5.5만 설치되어있으면 동작하지 않는 버그를 고치는데 걸리는 시간을 묻는다면, 문제가 무엇인지도 모르는 상황이기 때문에 얼마나 걸릴지 추측하지도 못할 것입니다. 3일이 걸릴 수도 있을 것이고, 운좋으면 2분이 걸릴 수도 있을 것입니다.

이것이 의미하는 바는 고쳐야할 버그가 많이 존재하는 상태의 스케줄이라면 그 스케줄은 정확할 수가 없다는 것입니다. 그렇지만 이미 알고있는 버그들은 모두 고친 상태라면 그 스케줄은 상대적으로 상당히 정확하게 지킬 수 있는 스케줄일 것입니다.

버그 갯수를 0에 가깝게 하는 또하나의 좋은 점은 경쟁에서 훨씬 빠르게 대응할 수 있다는 것입니다. 어떤 프로그래머들은 이를 두고 제품을 바로 출하할 수 있는 항상 유지하는 것이라고 이야기합니다. 경쟁자가 고객들을 가로채갈만한 굉장히 좋은 기능을 새로 만들었다면 축척된 많은 버그를 수정할 필요없이 바로 이 기능을 추가할 수 있을 것입니다.

6. up-to-date(최신) 스케줄을 가지고 있습니까?

비즈니스에 당신의 코드가 조금이라도 중요한 부분이라면, 코드가 언제쯤 완성될 수 있는지를 아는 것 또한 중요하다는 것은 당연할 것입니다. 프로그래머들은 엉터리 스케줄을 만드는데 악명이 높습니다. "언젠가는 될꺼야!"하고 외칩니다.

불행하게도 그런 식으로는 해결할 수 있는것은 없습니다. 비즈니스에는 코드를 출하하기 전에 데모, 전시회, 광고등등 미리 많은 것들을 판단하여 결정해야합니다. 이를 할 수 있는 단 한가지 방법은 스케줄을 가지고 이를 계속해서 현실적으로 최신내용으로 유지하는 것입니다.

스케줄을 가져야하는 또다른 중요한 이유는 이를 통해서 어떤 기능이 필요한지를 결정하게끔 만들어준다는 것입니다. 때문에 어떤 기능이 덜 중요한지 결정해야하고 featuris 가 되기 전에 이들을 포기하도록 합니다.

스케줄을 관리하는 것이 어려울 필요는 없습니다. 제 글Painless Software Schedules 에 좋은 스케줄을 만드는 간단한 방법을 설명하였습니다.

7. spec(설계서)를 가지고 있습니까?

스펙을 만드는 것은 이빨을 쑤시는것과 같습니다: 모든 사람들이 좋다고 인정하지만, 아무도 하지 않습니다.

왜 그런 현상이 일어나는지는 정확히 모르겠습니다만, 아마도 프로그래머들이 문서를 만드는 것을 굉장히 싫어하는데에 기인하는 것 같습니다. 그 결과로, 프로그래머밖에 없는 집단에서 한가지 문제를 해결하고자 하면, 이들은 문서를 만들기 보다는 코드로 자신들의 의견을 표명하려 합니다. 스펙을 먼저 만들기보다는 차라리 코드를 짜서 보여주는 것을 택한다는 것입니다.

설계 단계에서 문제를 발견하면 몇줄을 고쳐서 이를 수정할 수 있습니다. 그렇지만 코드가 짜여진 상황이라면 이 문제를 수정하는 댓가는 감정적으로나(코드를 그냥 버리는 것을 좋아하는 사람은 없습니다) 시간적으로나 훨씬 높게 되고 더 힘든 작업이 되어버립니다. 스펙을 통해서 만들어지지 않은 소프트웨어는 대개 설계가 잘못되어 스케줄을 엉망으로 만들어놓습니다. Netscape에서도 이런 문제로 인해 브라우저의 초기 네개의 버젼이 너무 엉망이 되어 결국 관리자들이 멍청하게도 코드를 전부 버리고 다시 짜도록한 결정을 내려버리게 되는 상황이 벌어졌습니다. 거기다 한술 더 떠서 Mozilla에서 이런 실수를 다시 반복하여 겨우 Alpha 단계에 가는데 몇년이라는 시간이 걸리게 되었습니다.

필자의 지론은 이 문제는 프로그래머들이 문서를 작성하는데 거부감이 없도록 작문 강의를 듣도록 보내는 것으로 해결할 수 있다는 것입니다. 다른 해결책이라면 스펙같은 문서 작성에 능숙한 관리자를 두는 것입니다. 두 경우 모두 "스펙없는 코드는 금물"이라는 간단한 규칙을 따라야 할 것입니다.

저의 4부짜리 글에 스펙 작성하는 요령에 대해 이야기했습니다.

8. 프로그래머들이 조용한 작업환경을 가지고 있습니까?

지식 근로자에게 공간, 조용함, 프라이버시를 줌으로해서 많은 생산성 향상을 얻는다는 것은 이미 증명된 사실입니다. 소프트웨어 관리의 고전인 Peopleware에서는 이 생산성 향상에 대해 자세히 기술합니다.

문제는 여기에 있습니다. 지식 근로자는 "in the zone"상태라고도 하는 "flow"상태에 들어섬으로써 가장 최상의 상태가 되어 일에 완벽히 집중하고 외부에 개의치 않게 됩니다. 완벽한 집중으로 시간 가는 것을 잊고 좋은 결과를 내게 됩니다. 이때에 바로 대부분의 생산적인 일들을 처리하게 됩니다. 작가, 프로그래머, 과학자 그리고 심지어 농구선수들까지도 "in the zone"상태가 있음을 이야기할 것입니다.

문제는 "zone"으로 들어가는 것이 쉽지 않다는 것입니다. 측정해보면, 최상의 생산성으로 일을 하기 위해서는 평균 15분이 걸립니다. 하지만 어떤 경우에는 피곤하고 이미 많은 일을 한 상태에서 "zone"상태에 들어가지 못하고 다른 일을 하거나 웹서핑이나 테트리스로 시간을 허비하게 될 수도 있습니다.

또다른 문제는 "zone"상태에서 빠져나가는 것이 매우 쉽다는 것입니다. 잡음, 전화소리, 점심식사, 잠시 스타벅스에 5분간 갔다오는 것 그리고 특히 동료에 의한 방해등에 의해 바로 "zone"에서 빠져나가게 됩니다. 동료가 1분이라는 짧은 시간 동안이라도 질문을 하여 "zone"상태에서 빠져나간다면 다시 되돌아가기 위해서 30분이 넘는 시간이 걸려 전체 효율에 치명적인 영향을 미칠 수 있습니다. 카페인 가득한 닷컴 회사들이 좋아하는 합숙소같은 곳에 옆의 마케팅 부서에서 계속해서 오는 전화에 대고 소리지르는 그런 시끄러운 환경이라면 계속된 방해로 지식 근로자들의 생산성은 추락하여 "zone"상태에 절대 이르지 못할 수도 있습니다.

프로그래머들에게 있어서 특히 어렵습니다. 생산성은 단기적인 기억력으로 한번에 얼마나 많은 작은 세부사항들을 다루느냐에 달려있습니다. 어떠한 방해도 이런 세부사항들을 잊어버리게 할 수 있습니다. 일을 다시 재개하면 그것들을 다시 기억하지 못하여 (사용하던 지역변수나 검색 알고리즘을 만들던 중에 어디에서 멈줬었는지등) 다시 찾아보게 되고, 이로 인해 다시 속도가 붙을때까지 느려지게 됩니다.

직관적으로 계산해보면 다음과 같습니다. 만일 프로그래머가 단 1분이라도 방해를 받아서(명백한 근거에 의해) 15분의 생산성을 날려버린다고 합시다. 철수와 영희 두 프로그래머가 낮은 칸막이로 주욱 늘어선(a standard Dilbert veal-fattening farm) 열린 사각 파티션 옆자리에 앉아 있다고 합시다. 영희가 strcpy함수의 유니코드 버젼 이름을 잊었습니다. 30초면 찾아볼 수 있겠지만, 철수한테 물어보면 15초가 걸립니다. 그래서 바로 옆에 앉아 있는 철수에게 묻습니다. 철수는 산만해지고 - 영희의 15초를 아끼기 위해 - 15분을 낭비하게 됩니다.

이번에는 벽과 문으로 나뉘어진 별도의 사무실로 가정을 합시다. 여전히 영희는 함수를 기억하지 못합니다. 다시 찾아보는 것으로 30초를 보낼 수 있을 것이고 옆 방에 있는 철수에게 물어보기 위해서 (일반적인 프로그래머의 평균 물리적인 건강상태를 봐서는 쉽지 않은) 일어나서 걷는 것을 포함한 45초를 보낼 수 있을 것입니다. 결국 찾아보는 것을 선택하여 30초를 보내게 되지만 철수의 15분을 벌어주게 됩니다. 대단하죠!

9. 돈이 허락하는 한도내의 최고의 툴들을 사용하고 있습니까?

컴파일 되는 언어로 코드를 작성하는 것은 여전히 아무 PC에서 할 수 없는 것 중의 하나입니다. 컴파일을 하는데 몇초 이상 걸린다면 최상의 기종을 사용함으로써 시간을 절약할 수 있을 것입니다. 15초 이상 걸린다면 지루해서 그 시간동안 The Onion을 읽게 될 것이고 너무 재미있는 관계로 거기에 빠져 수시간의 생산성을 날려버릴 것입니다.

모니터 하나로 GUI코드를 디버깅한 것은 불가능하지는 않지만 고통스러운 작업입니다. GUI코드를 작성하고 있다면, 2대의 모니터로 훨씬 쉬운 작업을 할 수 있을 것입니다.

대개의 프로그래머들은 아이콘이나 툴바를 위해 비트맵을 수정해야하고 대부분의 프로그래머 역시 좋은 비트맵 에디터를 가지고 있지 않습니다. 마이크로소프트에서 기본적으로 제공하는 Paint 프로그램으로 비트맵을 수정하는 것은 웃긴 일이지만 대부분 이렇게 하고 있습니다.

필자의 가장 최근 직장에서 시스템 관리자가 계속해서 자동적으로 스팸을 보냈습니다. 이유인 즉슨 220MB이상의 하드드라이브를 사용하고 있다는 것이었습니다. 필자는 요즘 HD가격을 본다면 이 공간의 환산된 가격은 내가 이용하는 화장실 휴지보다 싸다는 것을 지적했습니다. 디렉토리를 정리하기 위해 10분을 허비하는 정도로도 큰 생산성 저하일 것입니다.

"최고의 개발팀은 절대 그들의 프로그래머들을 고문하지 않습니다!" 후진 제품으로 인한 작은 불편함이 쌓여서 프로그래머들이 불만에 찰 수도 있습니다. 그리고 그로 인한 불만에 찬 프로그래머는 비생산적인 프로그래머이기 쉬울 것입니다.

이런 것들을 모두 종합하면 프로그래머들은 최고/최신의 것들로 쉽게 매수된다는 뜻이 됩니다. 이는 높은 연봉을 주는 것보다는 훨씬 싼 방법일 것입니다!

10. 테스터들을 고용하고 있습니까?

팀이 최소한 2~3명의 프로그래머에게 테스팅만 전담하는 테스터가 할당되어 있지 않다면, 버그가 많은 제품을 출하하고 있거나 시간당 $100짜리 프로그래머에게 시간당 $30의 일을 시키는 낭비를 하고 있는 것입니다. 테스터를 고용하는 것이 낭비로 생각하는 것은 정말 잘못된 계산을 하고 있는 것이며, 많은 사람들이 이를 깨닫지 못하고 있는데에 놀랍니다.

이에 관해 더 자세히 알고자 한다면 Top Five (Wrong) Reasons You Don't Have Testers 를 읽으십시오.

11. 신입사원들은 면접때 코드를 직접 짜는 실기시험을 봅니까?

마법사를 고용하는데 그의 마법을 보지 않고 고용하시겠습니까? 당연히 그렇지 않겠죠.

결혼식에 요리사를 고용하는데 요리사가 만든 요리의 맛도 모르고 고용하시겠습니까? 그렇지 않을것입니다.(역자주: 실제로 결혼식장 요리사의 맛을 보는 비유는 우리나라에 맞지 않을 것 같네요. 이 문구의 뜻만 이해하세요)

하지만 현실에서는 매일 인상적인 이력서나 면접에서 맘에 든 이유로 고용하는 일들이 일어납니다. 혹은 ("CreateDialog()와 DialogBox()의 차이점은 무엇입니까")등의 문서만 보면 알 수 있는 사소한 질문으로 채용하기도 합니다. 프로그래머를 채용하는데 있어서 중요한 것은 그런 사소한 것들을 얼마나 많이 외웠느냐가 아니고 코드를 잘 작성할 수 있느냐입니다. 혹은 "아하!"류의 질문으로 채용하기도 합니다. "아하!"류의 질문이란 답을 알면 간단하지만 모르는 경우에는 절대 맞출 수 없는 질문을 이야기합니다.

제발 이런 방식을 그만 두십시오. 면접때 무얼해도 상관없지만 반드시 코드를 작성하도록 해야합니다.(더 많은 것을 알고 싶다면 Guerrilla Guide to Interviewing를 읽으십시오)

12. hallway usability testing(무작위 사용성 테스팅)을 하십니까?

무작위 사용성 테스트(hallway usability test)는 복도를 지나가는 다음 사람을 붙잡고 방금 짠 코드를 사용하게 하는 방식입니다. 5명에게 이 테스트를 한다면 95%의 사용성 문제에 대해 배울 수 있을 것입니다.

좋은 사용자 인터페이스 설계는 생각처럼 어려운 것이 아니고 사용자들이 당신의 제품을 구입하고 사용하게 하는데 있어서 절대적으로 중요합니다. 짧은 프로그래머 입문서로 UI 설계에 관해 필자가 쓴 무료 온라인 책을 읽어보실 수 있습니다.

하지만 사용자 인터페이스에서 제일 중요한 것은 많은 사람들에게 당신의 프로그램을 보여주면(5~6명이면 충분합니다) 제일 큰 문제점을 빠른 시간에 발견할 수 있다는 것입니다. Jakob Nielsen의 글에서 그 이유에 대한 설명을 찾을 수 있습니다. UI 설계 경험이 별로 없다고 하더라도 - 비용이 전혀 들지 않는 - 무작위 사용성 테스트를 한다면 당신의 UI는 훨씬 좋아질 것입니다.

Joel Test를 사용하는 4가지 방식

  1. 자신이 속한 소프트웨어 팀의 점수를 매기고 그것에 대해 언급할 수 있도록 결과에 대한 이유를 필자에게 알려주십시오.
  2. 프로그래머 팀의 관리자라면, 당신의 팀이 최대한 잘 운영될 수 있는지 확인할 수 있는 지표로 사용하십시오. 12점을 받기 시작하면 프로그래머들을 간섭없이 그냥 두고 비즈니스쪽 사람들이 그들을 간섭하지 못하게 하는데에 모든 시간을 할 수 있습니다.
  3. 프로그래머 일을 맡을지를 결정해야하는 상황이라면 그 팀의 친한 사람에게 이 테스트 결과가 어떤지를 물어보십시오. 결과 점수가 너무 낮다면 이를 고칠 수 있는 권한을 받을 것인지를 확인하십시오. 그렇지 않으면 불만과 스트레스에 빠질 것입니다.
  4. 프로그래밍 팀을 평가하여야 하는 투자자이거나 당신의 회사가 다른 소프트웨어 회사와 합병을 한다면 이 평가가 급한대로 괜찮은 지표가 될 것입니다.


이 기사는 영어로 The Joel Test: 12 Steps to Better Code 라는 이름의 기사가 원본입니다.
Posted by SB패밀리

1. 꾸준히 한다.

.프로그래밍언어도 언어(?)라서, 하루에 몰아서 하는 것보다 매일 꾸준히 하는 것이 중요하다.

 경력이 많은 프로그래머들도 몇달만 코딩을 안해도 감이 많이 떨어지는 것을 느낀다.

 

.특히 프로그래밍을 처음 배우는 사람이라면, 꼭 컴퓨터 앞에 앉지 않더라도 책을 항상 가까이해서

문법 및 표현에 익숙해지도록 하는 것이 중요하다. 자주보는 것이 중요하다.

 

 

2. 반복해서 한다.

.단지 태권도교본을 잘이해했다고 해서 멋진 발차기를 기대할수 없는 것처럼, 책의 내용을

 잘 이해했다고 해서 하루아침에 프로그래밍을 잘할수 있는 것은 아니다.

 

.이해한 내용을 바탕으로 수많은 반복연습을 통해서만 지식을 진정한 자신의 것으로 만들 수 있다.

 (같은 예제를 공부하더라도 이리저리 조금씩 변경해서 공부하는 것이 좋다.)

 

.처음 2~3번은 자세히 보고, 그 다음 부터는 하루에 10분간 10페이지를 훑어보는 식으로 반복하자.

 몇달안에 책에 있는 모든 목차와 예제의 위치와 주요내용을 모두 파악할수 있을 것이다.

 (적어도 언어책 한권, 데이터베이스책한권 정도는 이렇게 할 필요가 있다.)

 

 

3. 좋은코드를 많이 보고 따라한다.

 .이미 수많은 선배들이 여러문제들에 대한 코딩을 다 작성해 놓았다. 새로운 방법으로 문제를

  풀겠다고 도전하는 것은 별 의미가 없다. "이럴때는 이렇게 하는 구나..."라는 것을 배우고

  유사한 상황에서 활용하면 되는 것이다. 여러분들이 해야할일은 이러한 경험들을 많이 쌓아

  나가는 일이지, 기존과는 다른 새로운 코딩방식을 만들어 내는 것이 아니다.

 

 .좋은 코드는 보기에도 좋다. 잘정리되어 있고, 별로 특별한 것이 없다. 프로그래밍의 각요소들을

  잘이해하고, 각 요소들을 적재적소에 바르게 사용하면 되는 것이다. 

  단지 소스의 라인수를 줄인다고해서 좋은 코딩이 아닌것이다. 로직이 소스코드에 잘드러날수있게

  쉽고 평범하게 작성하는 것이 좋은 코드인 것이다. 이창호의 바둑이 평범하듯이...

 

4. 기본에 충실한다.

 .빨리 프로그래밍을 배워서 뭔가 해보고 싶은 여러분들의 마음을 이해하지 못하는 것은 아니다.

  그러나, 프로그래밍 하루이틀 할 것도 아니고... 처음에 기본을 잘배워놓지 않으면, 그 이후에는

  기회가 잘 없다. 실무에서는 매일 개발하기 바쁘고, 새로운 기술 배우기 바쁘고...

 

 .배울것이 많다고 생각할지 모르나, 실제로 원리는 모두 같다고 해도 과언이 아니다.

  하나를 깊이있게 파고들면 나머지는 다 여러분 손에 있을 것이다.

 

5. 코드를 작성하기전에 순서도를 그린다.

 ."프로그래밍 = 코딩"이 아니라 "프로그래밍 = 로직설계 + 코딩"이다. 필자가 생각하는 로직설계

   와 코딩간의 비율은 8:2정도이다.

 

 .포토샵만 잘한다고 디자이너가 아니라는것은 여러분들도 잘알고 있을 것이다. 새로운 기술이나

  프로그램을 공부하는 것도 중요하지만,  어떤 과제가 주어졌을때 이를 잘 분석하고 설계하는

  능력을 장기적으로 키워나가도록 노력해야할 것이다.(다양한 주제에 대해서 문제를 풀어보고

  다양한 종류의 책을 읽자.)

 

 .문제를 구성하고 있는 주인공들을 찾아서 나열해보라. 그리고 이들간의 관계는 무엇이고, 규칙은

  무엇인지 적어보라.(머릿속으로만 생각하지말고!!!)

 

6. 주석을 가능한한 많이 적는다.

 .주석은 매우 유용하고도 중요한 요소이다. 그럼에도 불구하고 많은 사람들이 이를 소홀히 한다.

  자신이 작성한 코드도 몇일만 지나면 이해가 안되는 경우가 많다. 적어도 이해하는데 시간이

  걸린다. 주석은 이러한 시간들을 절약해줄것이며, 보다 에러가 적은 코드를 작성하는데 도움을

  줄 것이다. 특히 여러사람이 공동작업을 하는 경우에는 더욱 더 중요하다. 서로를 위해서...

 

 .작업과 관련된 가능한한 많은 정보를 주석에 담도록 하자.

 

7. 작업일지를 작성한다.

 .과학자들이 매일 연구한 내용을 일지로 적듯이 여러분들도 일지를 적어보자. 오늘은 이렇게

  저렇게 해봤는데 잘안되었다... xxx.java의 코드를 이렇게 바꾸었다. 몇시몇분에 xx로 백업받아

  놓았다... 라는 식으로 가능한한 자세히 적도록 한다. 이렇게 함으로써 여러분들의 경험을 기록

  으로 쉽게 보관할수 있으며, 문제해결에 많은 도움이 된다.

 

8. 자신의 소스를 가꾼다.

 .보통 코딩을 마치고 나면, 모든 것을 덮어두곤 한다. 원하는 결과를 얻었다고 거기서 그치지말고

  이제 로직과 코드를 보다 효율적으로 개선할 방법이 없는지 고민해보자. 글을 써놓고 좋은 글로

  만들기 위해 읽고 또 읽고 다듬듯이 코드를 다듬어보자. 여러분들의 코드를 구사하는 능력이 보다

  향상되어가는 것을 느낄 수 있을 것이다.

 

 .여러분들을 위한 제안은 작은 프로그램을 만들어서 오랜기간동안 점차 발전시켜 나가는 것이다.

  새로운 기능들을 하나씩 추가해가고, 기능을 발전시켜나가보자. 이과정을 통해서 여러분들의 실력

  은 몰라보게 향상될 것이다.

 

9. 생각하라.

 .항상 머릿속에 한 가지 문제를 준비하라. 지하철을 기다리거나, 화장실에서 볼일 볼때 문제를 풀어

  보자. 유레카를 외치고 뛰어나올지도...^^;

 

10. 좋은 책을 선택한다.

 .공부를 시작할때 제일 먼저 하는 일은 아마도 책을 고르는 일일 것이다. 보통 책하나에 수십시간을

  학습하게 되는데, 책을 잘못선택한 경우 수십시간과 노력을 허비하는 셈이다.

  바른 책을 고르는 일은 쉬운일이 아니지만, 최소한 몇시간을 투자해서 최선의 선택을 하도록 노력

  해야 수십시간을 허비하는 일이 없을 것이다.

 

 .책을 고르는 법은 여러가지가 있겠으나, 가장 중요한 것은 본인이다. 서점에서 같은 종류의 몇가지

  책을 놓고 서로 비교해보면, 시간을 들인 만큼 보다 나은 선택을 할 가능성이 높아진다.

 

 .많은 컴퓨터 서적이 독자들의 선택을 어렵게 하고, 컴퓨터 업계 특성상 좋은책을 만들기 보다

  빨리찍어서 파는 것이 더 중요해진 요즘. 독자들의 바른 선택이 보다 나은 책이 출판되는 것을

  가능하게 한다는 것을 알았으면 한다.

 

 

11. 맺음말

  열손가락을 못채우는 미천한 필자의 경력으로 이런 글을 적는 다는 것이 부끄럽기도 했지만,
  본인이 그동안의 경험과 학습을 통해 얻은 것들이 후배들에게 도움이 되고자 하는 마음에서
  적어보았습니다.
  너무나도 많은 지식과 정보속에서 바른 길을 찾지못하고 방황하는 많은 후배들을 보았습니다.
  많은 후배들을 잘못된 방향으로 이끌고 가는 많은 사람들을 보았습니다.
  이건 아닙니다... 정말 아니지요.
  지금이 어느땐데, 후배들이 선배들이 했던 삽질을 반복한단 말입니까...
  누구를 위해서 말입니까...
 
  조금이라도 도움이 되고자 제가 책을 썼습니다.
  최고의 책을 쓰기 위해 많은 것을 잃었습니다.
 
  지금은 쳐다보는 사람도 없고, 알아주는 사람도 없습니다.
  하지만, 단언컨데 현시점에서 제 책보다 자바기본을 잘 정리하고 설명한 책은 없는걸로
  알고 있습니다.
 
  화려한 표지도 아니고, 유명한 출판사도 아니지만
  언젠가는 모든 독자가 선택하는 최고의 책이 되리라 믿습니다.
 
  카페의 필수강좌에 책의 내용을 올려놓았습니다.
  열심히 보시기 바랍니다.  
  부디 여러분들의 열정과 노력이 조금도 헛되임 없이 실력향상으로 직결되기를 바랍니다.
 
  2004. 6. 20  
 
 플래시로 배우는 Java의 저자
 남궁성

Posted by SB패밀리

코딩 기술에는 소프트웨어 개발의 여러 가지 측면이 통합되어 있습니다. 일반적으로 코딩 기술은 응용 프로그램의 기능에 영향을 미치지는 않지만, 소스 코드의 가독성을 향상시키는 데 기여합니다. 여기서는 프로그래밍, 스크립팅, 태그 및 쿼리 언어와 같은 모든 형식의 언어가 고려됩니다.

여기에서 정의된 코딩 기술은 고정된 집합의 코딩 표준을 구성하는 데는 사용하지 않는 것이 좋습니다. 그보다는 특정 소프트웨어 프로젝트에 사용되는 코딩 표준을 개발하기 위한 지침으로 사용하는 것이 좋습니다.

코딩 기술은 다음 세 단원으로 구분됩니다.

이름

명명 스키마는 응용 프로그램의 논리적 흐름을 이해할 수 있는 가장 강력한 수단 중의 하나입니다. 이름에는 "어떻게"보다는 "무엇을"이 나타나야 합니다. 내부 구현을 노출시키는 이름을 사용하지 않으면 복잡성이 줄어든 추상화 계층을 유지할 수 있습니다. 예를 들어, GetNextArrayElement() 대신 GetNextStudent()를 사용할 수 있습니다.

올바른 이름을 선택하는 것이 어렵다는 것은 사용자가 항목의 목적을 더 정확하게 분석하고 정의할 필요가 있다는 것을 의미할 수 있습니다. 이름의 길이는 정확한 의미를 전달할 수 있을 정도로 길어야 하지만 너무 길어져서 의미가 모호해지면 안됩니다. 프로그램 상에서 고유 이름은 특정 항목과 다른 항목을 구분하는 용도로만 사용되며, 표현 이름의 기능은 사용자의 이해를 돕는 것입니다. 따라서 사용자가 이해할 수 있는 이름을 제공하는 것이 좋습니다. 하지만 선택된 이름은 해당 언어의 규칙과 표준을 준수해야 합니다.

다음은 명명 기술에서 권장되는 사항입니다.

루틴

  • 루틴 이름 AnalyzeThis()와 변수 이름 xxK8과 같이 주관적으로 해석될 수 있는 모호한 이름은 사용하지 않습니다. 이와 같은 이름은 추상화에 기여하기 보다 모호함의 원인이 됩니다.
  • 개체 지향 언어에서 클래스 속성 이름에 Book.BookTitle과 같은 클래스 이름을 포함시키는 것은 불필요합니다. 그 대신 Book.Title을 사용합니다.
  • 지정 개체에서 특정 동작을 수행하는 루틴을 명명하기 위해서는 CalculateInvoiceTotal()와 같은 동사-명사 방식을 사용합니다.
  • 함수 오버로딩이 허용되는 언어에서 모든 오버로드는 유사한 동작을 수행해야 합니다. 함수 오버로딩이 허용되지 않는 언어에서는 유사한 기능을 관련시켜 주는 명명 표준을 설정합니다.

변수

  • 해당하는 변수 이름의 끝에 Avg, Sum, Min, Max, Index와 같은 계산 한정자를 추가합니다.
  • 변수 이름에 min/max, begin/end 및 open/close와 같은 보완적인 쌍을 사용합니다.
  • 대부분의 이름은 연속되는 몇 개의 단어로 구성되므로, 대소문자를 혼용하여 쉽게 읽을 수 있도록 합니다. 또한 변수와 루틴을 쉽게 구별하기 위해 루틴 이름에는 각 단어의 첫 글자가 대문자로 시작되는 파스칼 대/소문자(CalculateInvoiceTotal)를 사용합니다. 변수 이름에는 첫 단어를 제외한 각 단어의 첫 글자가 대문자로 시작하는 카멜 대/소문자(documentFormatType)를 사용합니다.
  • 부울 변수 이름에는 fileIsFound와 같이 Is가 포함되어야 합니다. Is는 Yes/No 또는 True/False 값을 내포합니다.
  • 상태 변수를 명명하는 경우에는 Flag와 같은 구문을 사용하지 않습니다. 이 구문은 두 개 이상의 값을 가질 수 있다는 점에서 부울 변수와 다릅니다. documentFlag를 사용하는 대신 documentFormatType와 같이 더 정확한 이름을 사용합니다.
  • 단 몇 줄의 코드에서만 사용되는 임시 변수에 대해서도 의미 있는 변수를 사용하십시오. i 또는 j와 같은 단일 글자 변수 이름은 단기 루프 인덱스에만 사용합니다.
  • For i = 1 To 7과 같은 리터럴 숫자나 리터럴 문자열은 사용하지 않습니다. 그 대신 For i = 1 To NUM_DAYS_IN_WEEK와 같은 명명된 상수를 사용하여 관리와 이해를 쉽게 합니다.

테이블

  • 테이블을 명명하는 경우에는 이름을 단수로 표현합니다. 예를 들어, Employees 대신 Employee를 사용합니다.
  • 테이블 열을 명명하는 경우에는 테이블 이름을 반복하지 않습니다. 예를 들어, Employee라는 테이블에서 EmployeeLastName이라는 필드를 사용하지 않습니다.
  • 열 이름에 데이터 형식을 포함시키지 않습니다. 이렇게 하면 나중에 데이터 형식을 변경할 때 필요한 작업의 양이 줄어들게 됩니다.

Microsoft SQL Server

  • 저장 프로시저 앞에 sp를 붙이지 마십시오. sp는 시스템 저장 프로시저를 식별하기 위해 예약된 접두사입니다.
  • 사용자 정의 함수 앞에 fn_을 붙이지 마십시오. fn_은 기본 제공 함수를 식별하기 위해 예약된 접두사입니다.
  • 확장 저장 프로시저 앞에 xp_를 붙이지 마십시오. xp_는 시스템 확장 저장 프로시저를 식별하기 위해 예약된 접두사입니다.

기타

  • 가능하면 약어를 사용하지 않습니다. 다만 사용자가 일관성 있게 만든 약어는 사용합니다. 약어에는 단 한 가지 의미만 존재해야 하며, 각 약자에는 단 하나의 약어가 존재해야 합니다. 예를 들어, minimum의 약어로 min을 사용하는 경우 다른 곳에서 minute의 약자로 min을 사용해서는 안됩니다.
  • 함수를 명명하는 경우 GetCurrentWindowName()과 같이 반환되는 값에 대한 설명을 포함시킵니다.
  • 프로시저 이름과 마찬가지로 파일과 폴더 이름도 그 용도를 정확하게 나타내야 합니다.
  • ProcessSales()라는 루틴과 iProcessSales라는 변수와 같이 서로 다른 요소를 위해 이름을 반복 사용하지 않습니다.
  • 코드 검사 시 혼동을 방지하려면 요소를 명명할 때 write 및 right와 같은 동음 이의어를 사용하지 않습니다.
  • 요소를 명명하는 경우 철자가 자주 틀리는 단어를 사용하지 않습니다. 또한 color/colour 및 check/cheque와 같이 지역마다 철자가 다르다는 점도 알아 둡니다.
  • 데이터 형식을 식별하기 위해 입력 마크(예: 문자의 $, 정수의 %)를 사용하지 않습니다.

주석

소프트웨어 설명서는 외부와 내부 설명서의 두 가지 형태로 존재합니다. 사양, 도움말 파일 및 디자인 문서와 같은 외부 설명서는 소스 코드의 외부에서 관리되며, 내부 설명서는 개발자가 개발 시에 소스 코드 내부에서 작성한 주석들로 구성됩니다.

외부 설명서를 사용할 수도 있지만, 하드 카피 설명서를 잊어 버릴 수 있으므로 소스 코드 목록을 독자적으로 유지해야 합니다. 외부 설명서는 사양, 디자인 문서, 변경 요청, 버그 목록 및 사용되는 코딩 표준으로 구성되어야 합니다.

내부 소프트웨어 설명서의 한 가지 문제는 소스 코드와 일치하도록 주석을 관리하고 업데이트하는 것입니다. 적절한 주석이 추가된 소스 코드는 실행 시에 특별한 역할을 하지 않지만, 상당히 복잡하고 귀찮은 소프트웨어를 관리해야 하는 개발자에게 있어서는 상당히 중요한 역할을 합니다.

다음은 주석 추가 시 권장되는 사항입니다.

  • C#에서 개발을 수행하는 경우 XML 문서 기능을 사용합니다. 자세한 내용은 XML 문서를 참조하십시오.
  • 코드를 수정하는 경우에는 반드시 최신 주석을 코드에 추가하십시오.
  • 모든 루틴의 시작부에는 이 루틴의 목적, 전제, 제한 등을 나타내는 정확한 표준 주석을 추가하는 것이 도움이 됩니다. 정확한 주석은 이 루틴의 용도와 목적을 간략하게 설명할 수 있어야 합니다.
  • 코드 줄의 끝에 주석을 추가하지 않습니다. 끝부분에 주석이 있으면 코드를 읽기가 더 어렵습니다. 하지만 변수 선언에 대해 주석을 추가하는 경우에는 줄 끝에 주석을 추가하는 것이 좋습니다. 이 때 탭을 사용하여 모든 주석을 일렬로 정렬합니다.
  • 모든 줄에 별표가 사용된 혼란스러운 주석을 사용하지 않습니다. 그 대신 공백을 사용하여 주석과 코드를 구분합니다.
  • 입력 프레임을 사용하여 블록 주석을 둘러싸지 않습니다. 이렇게 하면 멋지게 보일 수는 있지만 관리가 어려워집니다.
  • 나중에 관리 작업을 수행하는 동안 혼동을 방지하려면 임시 주석이나 관련이 없는 주석을 완전히 제거한 후에 배포합니다.
  • 복잡한 코드 부분을 설명하는 주석이 필요한 경우 그 코드를 검사하여 이 주석을 다시 작성해야 하는지를 결정합니다. 가능하면 오류 코드를 제공하지 말고 다시 작성합니다. 일반적으로 코드를 단순하게 만든다고 해서 성능이 저하되는 것은 아니지만, 성능과 유지 관리성 사이에 균형을 유지해야 합니다.
  • 주석을 작성하는 경우 완벽한 구문을 사용하십시오. 주석은 코드를 모호하게 하는 것이 아니라 명확하게 하는 것입니다.
  • 나중에 따로 주석을 추가할 시간이 없으므로 코딩과 주석 작업을 동시에 수행하십시오. 또한 자신이 작성했던 코드를 다시 살펴보아야 합니다. 지금은 코드가 명확하게 이해되지만 6주 후에는 명확하지 않을 수도 있습니다.
  • 유머스러운 말과 같은 불필요하고 부적절한 주석을 사용하지 않습니다.
  • 주석을 사용하여 코드의 목적을 설명하십시오. 주석은 코드를 그대로 옮겨 놓은 것이 아닙니다.
  • 코드에서 쉽게 알아볼 수 없는 모든 내용을 주석에 추가합니다.
  • 반복되는 문제를 해결하려면 버그 수정과 작업 코드에 대해 주석을 사용합니다.
  • 루프와 논리 분기로 이루어진 코드에 대해 주석을 사용합니다. 이 주석은 소스 코드 사용자에게 도움이 되는 중요한 부분입니다.
  • 응용 프로그램 전체에서 일관적인 문장 부호와 구조로 일정한 스타일을 사용하여 주석을 작성합니다.
  • 공백을 사용하여 주석 구분 기호와 주석을 분리합니다. 이렇게 하면 색 표시를 사용하지 않고 코드를 보는 경우에도 쉽고 명확하게 주석을 볼 수 있습니다.

서식

서식은 코드의 논리적 구조를 명확하게 합니다. 소스 코드의 서식을 일관적이고 논리적인 방식으로 적용하면 자신이나 다른 개발자가 이 소스 코드를 해독하는 데 도움이 됩니다.

다음은 서식 기술에서 권장되는 사항입니다.

  • 들여쓰기의 표준 크기(예: 공백 4개)를 설정하고 이 크기를 일관적으로 사용합니다. 지정된 들여쓰기를 사용하여 코드부를 정렬합니다.
  • 하드 카피 형태로 소스 코드를 출판하려면 한 가지 글꼴을 사용합니다.
  • 중괄호 쌍이 함께 나타나는 경우에는 다음과 같이 여는 중괄호와 닫는 중괄호를 수직으로 정렬합니다.
    for (i = 0; i < 100; i++)
    {
       ...
    }

    여는 중괄호가 줄의 끝에 나타나고 닫는 중괄호가 줄의 앞에 나타나는 경우에는 다음과 같은 스타일을 사용할 수 있습니다.

    for (i = 0; i < 100; i++){
       ...
    }

    선택된 스타일을 소스 코드 전체에서 일관적으로 사용합니다.

  • 논리적 구조에 따라 코드를 들여쓰기합니다. 들여쓰기를 사용하지 않으면 다음과 같이 코드를 이해하기가 어렵습니다.
    If ... Then
    If ... Then
    ...
    Else
    End If
    Else
    ...
    End If

    코드를 들여쓰면 다음과 같이 코드를 쉽게 이해할 수 있습니다.

    If ... Then
       If ... Then
       ...
       Else
       ...
       End If
    Else
    ...
    End If
  • 소스 코드 편집기의 스크롤을 방지하고 하드 카피가 명확하게 표시되도록 하기 위해 주석과 코드의 최대 줄 길이를 설정합니다.
  • 코드의 목적이 변경되지 않는 범위 내에서 모든 연산자의 전후에 공백을 사용합니다. 예를 들어, 한 가지 예외로는 C++에서 사용되는 포인터 표기법이 있습니다.
  • 공백을 사용하여 소스 코드의 구조를 체계적으로 표시할 수 있습니다. 이렇게 하면 코드 "단락"을 만들 수 있으므로, 사용자는 소프트웨어의 논리적 구조를 쉽게 이해할 수 있습니다.
  • 하나의 코드가 여러 줄에 걸쳐 나타나는 경우, 다음 줄이 없으면 코드가 불완전하다는 사실을 명확히 하기 위해 연결 연산자를 각 줄의 끝에 추가합니다.
  • 한 줄에 두 개 이상의 문을 사용하지 않습니다. 한 가지 예외로는 C, C++, C# 또는 JScript 에서 사용되는 for (i = 0; i < 100; i++)와 같은 루프가 있습니다.
  • HTML을 작성하는 경우 태그와 특성의 표준 서식을 설정합니다. 예를 들어 태그는 모두 대문자로 설정하고 특성은 모두 소문자로 설정할 수 있습니다. 다른 방법으로는 XHTML 사양을 사용하여 모든 HTML 문서가 유효하도록 보장할 수 있습니다. 웹 페이지를 만드는 경우 파일의 크기가 문제될 수 있지만, 따옴표 붙은 특성값과 닫는 태그를 사용하면 쉽게 관리할 수 있습니다.
  • SQL 문을 작성하는 경우 모든 키워드에는 대문자를 사용하고 테이블, 열 및 뷰와 같은 데이터베이스 요소에는 대소문자를 혼용합니다.
  • 실제 파일 사이에서 소스 코드를 논리적으로 분할합니다.
  • 다음과 같이 각각의 주 SQL 절을 별도의 줄에서 작성하면 쉽게 코드를 읽고 편집할 수 있습니다.
    SELECT FirstName, LastName
    FROM Customers
    WHERE State = 'WA'
  • 길고 복잡한 코드부는 쉽게 이해할 수 있는 작은 모듈로 분할합니다
Posted by SB패밀리

버그 없는 깨끗한 프로그램 만들기
2007/02/28 01:18 출처 : http://www.winapiprogramming.com/

 필자에게 프로그래밍이 무엇이냐고 묻는다면 프로그래밍이란 "버그와의 끝없는 싸움"이라고 대답하고 싶습니다. 필자가 처음 프로그래밍을 접해본 것은 중학교 3학년 때인 1984년이었습니다. 친구 집에 놀러갔다가 접한 SPC-1000에서 베이직으로 간단하게 계산기를 만들어본 것이 처음이었습니다. 그 컴퓨터란 물건이 얼마나 부럽던지 반년 동안 아버지를 졸라서 고등학교 1학년 때 애플 II 컴퓨터를 샀고 몇몇 컴퓨터 잡지를 사서 소스를 아무 생각없이 입력한 것 말고는 입시 준비(?)에 시달리느라 제대로 프로그래밍을 해본 적은 없었습니다. 그러다가 대학에 들어와서 포트란, 파스칼, C등을 배우면서 좀더 본격적인 프로그래밍을 시작하게 되었습니다. 그 시절을 돌이켜보면 참 어떻게 그렇게 무식하게 (?) 프로그래밍을 할 수 있었는지 대단하다는 생각이 간혹 들기도 합니다.

그 시절을 잠깐 회상하자면 일단 무턱대고 컴퓨터 앞에 앉아서 프로그래밍을 시작하지만 넘치는 의욕에 비해 실력이 따르질 않아서 쉬운 숙제에도 쩔쩔매며 간신히 코드를 만들었습니다. 컴파일하면 에러가 잔뜩 !!. 간신히 에러를 잡고 나서 실행하면 프로그램이 죽던지 엉뚱한 결과가 나오고... 그러면 이제부터 디버깅이 시작됩니다. 일단 의심이 가는 곳을 대충 고친 다음에 먼저 돌려보고 안 되면 다시 의심가는 곳을 고치고... 코드가 누더기가 된 다음에야 간신히 코드는 돌아가고 원하는 결과가 나오기는 하는데 왜 동작하는지 본인도 알 수 없고...

아마 대부분의 프로그래머들이 비슷한 과정을 거쳤거나 앞으로 거치리라 생각됩니다. 100줄 이상의 프로그램이라면 프로그램에 버그가 없을 수는 없습니다 (아니 적어도 컴파일 에러라도 있습니다). 그렇기 때문에 좋은 프로그래머가 되려면 버그가 적은 프로그램을 작성할 수 있거나 버그를 빨리 발견할 줄 알아야 합니다. 이 글에서는 여러분들이 이러한 목표를 달성하는데 도움이 될 수 있도록 먼저 좋은 코딩 습관에 대해 살펴보겠습니다. 그리고나서 다음으로 실례를 중심으로 자주 접하게 되는 버그 상황과 디버깅 팁에 대해 알아보도록 하겠습니다. 참고로 여기서는 비주얼 C++을 바탕으로 설명을 진행하겠습니다.

 

1. 바람직한 코딩 습관

버그 없는 프로그램을 작성하는 가장 좋은 방법은 바람직한 코딩 습관을 몸에 배게 하는 것입니다. 저도 지금 생각해보면 처음 프로그래밍을 배울 때 누군가 옆에서 이러한 것에 대해 이야기해주는 사람이 있었다면 고생을 좀 덜하지 않았을까 하는 생각을 간혹 하곤 합니다. 물론 자신이 직접 깨닫는 것만큼 확실히 배우는 것은 아니고 그 당시 그런 조언을 이해할만한 수준이 아니었기 때문에 그런 것들을 들었다 해도 얼마나 습득할 수 있었을지 의문이긴 합니다만. 참 이런 이야기를 하다보니 제가 이제는 무슨 완벽한 프로그래머가 된 것처럼 되어버렸는데 저도 이런 코딩 습관을 확립하기 위해 지금도 노력하고 있는 중입니다.

자 그럼 제가 생각하는 바람직한 코딩 습관에 대해 하나씩 알아보도록 하겠습니다. 이제 읽어보면 알겠지만 여기에 무슨 눈이 번쩍 뜨이는 그런 이야기들이 있는 것이 아닙니다. 항상 그렇지만 모든 성공은 작은 습관과 집중력으로부터 비롯합니다.

 

1> 사용하는 프로그래밍 언어를 제대로 이해하자.

이 것은 초보 프로그래머에게는 아주 중요한 사항입니다. 프로그래밍을 처음 배우는 사람이라면 그 개념을 제대로 이해하지 못하는 경우가 허다합니다. 특히 C 언어를 배우는 사람이라면 포인터의 개념을 제대로 이해하지 못하는 경우가 많고 C++/자바 등의 언어를 배우면 클래스의 상속(Inheritance)이나 가상 함수(Virtual function)와 같은 개념을 이해하지 못하는 경우가 허다합니다. 이런 것을 이해하지 못하고 일단 프로그래밍에 뛰어들게 되면 당연히 많은 시간을 소비하기 마련이고 결과물을 만들어내지 못하는 경우도 많습니다. 아무리 시간이 없어도 한걸음 한걸음 배워나가는 것이 결국에는 시간을 단축하기 마련입니다.

프로그래밍 서적을 보고 공부할 때도 모니터 앞에 앉아서 지금 보고 있는 부분에 대해 실습을 해보며 공부하는 것이 최선의 방법입니다. 그렇게 하면 보다 더 이해하기가 쉽고 개발환경의 사용법에 대해서도 배워볼 수 있기 때문입니다. 그렇기 때문에 초보 프로그래머라면 프로그래밍 언어 관련 서적을 고를 때 예제 코드가 많이 있는지 또한 따라하기식의 구성이 잘 되어있는지를 살펴보는 것이 중요합니다. 즉 프로그래밍 언어는 머리로 이해하는 것도 중요하지만 손으로도 익혀야 한다는 것입니다.

top

2> 프로그래밍 중에는 프로그래밍에만 집중하자.

이 것은 초보 프로그래머 뿐만 아니라 이 시대의 모든 프로그래머에게 해당되는 이야기라고 할 수 있는데 한마디로 자신의 일에 대한 집중력을 키우라는 이야기입니다. 요즘 웬만한 컴퓨터는 인터넷과 연결되어 있습니다. 인터넷 매체와 메일, 메신저 등이 워낙 발달해 있어서 한 시간이라도 중단 없이 일한다는 것이 쉬운 일이 아닙니다. 이런 상황은 사실 프로그래머 뿐만 아니라 컴퓨터를 사용하는 모든 사람들에게 해당된다고 할 수 있을 것 같습니다. 다음과 같은 비슷한 경험이 있는지 한번 생각해봅시다.

코딩이 좀 막히면 머리를 식히기 위해 인터넷의 바다에서 뛰어들고 코딩이 잘 되어도 기분이 좋아서 인터넷의 바다에 뛰어들고... 또 요즘 메일 클라이언트는 모두 알림 기능이 있어서 메일이 도착하면 이를 알려주게 되어있습니다. 메신저는 어떠한가요 ? 툭하면 친구로부터 채팅 요청이 들어오고... 수다 떠느라 시간가는 줄 모르고...

의식적으로 노력을 해서 이러한 중단이 없도록 해야 합니다. 인터넷의 바다에 뛰어드는 시간대를 정해두는 것이 제일 좋은 것 같습니다. 점심 시간 직후라든가 하는 식으로 말입니다. 메일의 경우에는 새 메일이 도착했다는 알림이 와도 바쁜 경우라면 일단 그 일을 끝낸 후에 보는 식으로 한번 바꿔보기 바랍니다. 메일의 경우 같이 일하는 사람들간 의사소통 수단이기도 하기 때문에 알림 기능 자체를 꺼버리는 것은 좀 무리가 있는 듯합니다. 메신저 같은 경우는 집중이 필요한 시간에는 아예 로그아웃해버리는 것입니다. 아니면 약간 치사(?)하지만 상태를 "자리 비움" 혹은 "다른 용무 중" 같은 걸로 해두세요.

약간 이야기가 옆길로 새는 것 같긴 하지만 집중력에 관해 얼마 전에 제가 읽은 글이 있어서 참고로 여러분들께 소개해 드리고자 합니다. ("고도원의 아침편지"란 사이트에서 읽은 글입니다.)

밥을 먹을 때에는 밥먹는 일에 집중하고
청소할 때에는 온전히 청소하는 행위만 있어야 합니다.
그렇게 생각하고 말하고 행동하는 것을
달리 말하면, 집중력 또는 통일성이라고 합니다.
이 집중하는 태도와 노력을 통해
우리는 스스로 정화되기도 하고
안정되기도 하며
또 문제의 본질을 통찰하는
힘을 얻기도 합니다.


- 도법스님의 <<내가 본 부처>> 중에서

프로그래밍을 할 때는 프로그래밍에만 집중합시다 !! 인터넷으로 인한 이러한 중단이외에도 회사 생활을 막 시작한 초보 프로그래머의 경우에는 다음과 같은 것을 조심해야 합니다. 긴 업무 시간으로 인한 집중력 상실입니다. 이건 사실 개인의 힘만으로 해결할 수 있는 문제는 아닙니다만 아주 한국적인 근무 환경 하에서는 늦게 퇴근하는 것이 하나의 미덕입니다. 따라서 오랜 시간을 회사에서 보내려니 업무의 강도가 느슨해지기 마련입니다. 하루 근무시간을 8시간으로 생각하지 않고 10시간에서 12시간으로 생각하니까 할 일이 있어도 "저녁에 하지 뭐!" 이런 생각을 갖게 되고 커피마시고 담배피우며 잡담하느라 보내는 시간이 더 많아지게 되는 것입니다. 사실 이는 매니저 역할을 맡고 있는 사람이 해결해야할 문제입니다만 이런 상황으로 인해 집중력을 잃는 일이 발생할 수도 있다는 점을 알아두고 자신이 이런 증상을 보이거든 회사를 옮기던가(?) 자신을 채찍질하여 다시 집중력을 되찾기 바랍니다.

 

3> 주석을 많이 달자

주석을 많이 다는 것도 굉장히 중요합니다. 아주 복잡하고 거창하게 코드 흐름도를 그리라는 이야기가 아닙니다. 소스 코드 중간 중간에 설명을 자세히 달아놓으라는 이야기입니다. 그것만으로도 다른 사람이나 코드의 원저자 자신이 나중에 소스를 볼 때 큰 도움을 얻을 수 있습니다. 제 아무리 자기가 작성한 코드라고 해도 복잡하고 사연 많은 코드의 세부적인 내용은 몇 개월만 지나도 잊어버리기 십상이기 때문입니다.

귀찮아서 주석을 안 다는 사람들도 많습니다. 심지어 주석이 코드의 미관을 해친다는 이색적인 주장(?)을 펼치며 주석달기를 거부하는 사람도 본 적 있었습니다. 오 마이 갓 !! 주석을 다는 일은 습관이 되면 그리 어려운 일이 아닙니다. 또 주석을 달면서 작성하는 소스의 구성이나 흐름이 보다 더 명확해질 수도 있습니다. 예를 들어 C/C++이나 자바로 코딩을 하는 중이라면 먼저 //부터 입력하고 밑에서 할 일을 간단히 적고 시작하면 자신이 해야할 일이 좀더 명확해지고 나중에 주석을 보고 코드를 기억하거나 이해하기도 쉽기 때문입니다.

소스 파일을 하나 새로 만들면 그 앞에 다음과 같은 식으로 주석을 달기 바랍니다.

// -------------------------------------------------------

// 파일이름 : NewModule.cpp

// 설명 : 전체 프로그램에서의 이 소스 파일의 역할을 기술합니다.

// 노트 : 기억할만한 점이 있으면 기록합니다.

// 히스토리 : 생성 - 한기용, 2002.03.31

// Abc 함수추가 - 두기용, 2002.04.01

// -------------------------------------------------------

함수의 경우에는 함수마다 앞에 다음과 같은 함수 주석을 답니다.

// -------------------------------------------------------

// 함수이름 : Abc

// 설명 : 이 함수의 역할에 대해 기록합니다.

// 노트 : 기억할만한 점이 있으면 기록합니다.

// 인자 : 인자를 하나씩 설명합니다. 인자의 값을 누가 채워주는지도 명시합니다.

// [IN] int nCount

// [IN] char *pString

// [OUT] int *pResultCount

// 리턴값 : 리턴값의 타입과 의미에 대해 설명합니다.

// -------------------------------------------------------

번거롭게 느껴질지 모르지만 한번 몸에 배면 아주 좋은 특히 같이 일하는 사람들이 좋아하는 습관이란 점을 분명히 기억해두기 바랍니다. 문서화를 잘 하는 사람들은 어디를 가든 사랑(?)받습니다.

 

4> 새로 작성한 코드는 항상 디버거로 따라가 보자

처음 작성했거나 잘 동작하던 코드를 수정한 경우라면 컴파일이 제대로 된 후에 한번 디버거로 흐름을 따라가 보면서 생각하는 대로 동작하는지 살펴보는 것이 아주아주 좋습니다. 좀 번거롭게 생각되어서 이런 이야기를 그냥 듣고 넘길 수도 있는데 이를 습관으로 만들면 여러모로 오히려 편리합니다.

그냥 일단 돌려보고 제대로 동작하지 않으면 디버거로 따라가 보는 전략을 택할 수도 있는데 그것보다는 처음 한번은 일단 디버거로 따라가 보는 것이 여러모로 좋습니다. 디버거로 따라가 보면 결과에 나타나지 않는 에러들도 찾아낼 가능성이 있고 다른 아이디어가 나올 가능성도 더 높기 때문입니다.

>

top

5> 테스트 코드를 만들자

훌륭한 프로그래머라면 누구나 버그없는 빠르고 깔끔한 프로그램을 만드는 사람이라고 생각할 것입니다. 하지만 앞서 제가 언급한 것처럼 복잡한 프로그램을 작성하다보면 버그 없는 프로그램을 작성하는 것은 거의 불가능한 일입니다. 그렇기 때문에 제가 생각하는 좋은 프로그래머의 요건은 버그를 어떻게 빨리 발견해서 없애느냐에 달려있다고 생각합니다. 참고문헌 3을 보면 "Debugging the Development Process"라는 책이 언급되어 있습니다. 이 책은 제가 아주 감명(?)깊게 읽은 책으로 제대로 된 개발을 하는 방법론에 대해 다루고 있습니다. 시간이 된다면 한번 꼭 읽어보기를 권합니다. 이 책에서는 버그를 발견하고 이를 없앨 때마다 항상 다음 두 가지를 생각해보기를 권하고 있습니다.

 어떻게 했으면 이 버그를 내가 미연에 방지할 수 있었을까 ?

 어떻게 했으면 이 버그를 내가 아주 쉽게 찾아낼 수 있었을까 ?

이를 좀더 확장해서 생각하면 코드를 작성할 때부터 이걸 어떻게 테스트를 할 것인지 염두에 항상 두고 있어야 한다는 말입니다. 그래서 저 같은 경우는 프로그래머의 수준을 측정할 때 한 가지 척도로 그 사람이 테스트 프로그램을 작성하는지를 일단 봅니다. 자신의 코드를 테스트할 프로그램을 별도로 작성할 정도의 사람이면 일단 기본기가 아주 잘 되어 있는 사람이라고 볼 수 있기 때문입니다.

코드에 따라서는 테스트하는 것이 아주 힘든 경우도 있을 수 있습니다. 하지만 그걸 핑계로 테스트를 건너뛰지는 말기 바랍니다. 테스트할 방법을 계속 생각해보면 결국 어떻게든 그 방법이 떠오르게 되어 있습니다. 테스트하기 힘든 상황에도 그 방법을 생각해낼 정도의 사람이라면 아주 긍정적인 사람으로 인식될 것입니다.

결론적으로 무슨 코드를 만들던지 테스트할 방법을 생각하기 바랍니다. 프로그램 수행의 결과로 파일을 만들어내는 프로그램이라면 그 파일의 내용이 맞는지 체크하는 프로그램을 따로 만들어 볼 수도 있을 것이고 뭔가를 수행해서 화면에 출력하는 간단한 프로그램이라면 그 부분을 별도의 함수로 만들어서 다양한 입력을 주어서 올바른 출력이 나오는지 확인해볼 수 있을 것입니다.

또 실행이 오래 걸리는 프로그램이라면 중간 중간마다 실행 상태를 파일등에 기록해두도록 하는 것도 아주 좋습니다. 이런 용도로 사용되는 파일을 흔히 로그 (log) 파일이라고 합니다. 이때 그 시각도 같이 기록해두면 도움이 많이 될 것입니다.

6> 생각하는 프로그래머가 되자

무슨 코드를 작성하던지 어떻게 할 것인지 먼저 생각하는 습관을 갖기 바랍니다. 그리고 프로그램이 정상적으로 동작하고 원하는 결과를 낸다면 이에 만족하지 말고 더 빠르고 간단하게 처리하도록 개선할 방법이 없는지 자꾸 생각해봐야 합니다. 그 당시에야 일단 실력이 모자라고 개선하는데 시간이 걸리니까 답답하게 여겨질지 모르지만 결국에는 프로그래밍 실력의 발전에 가속이 붙을 것입니다.

사용자 인터페이스 프로그램을 만들고 있는 중이라면 사용자 입장에서 더 편리하게 사용할 수 있는 방법을 자꾸 생각해봐야 합니다. 물론 이 과정은 끝이 없는 과정이 될 수도 있기 때문에 뭔가 데드라인이 있는 일을 작업 중이라면 적정선에서 타협을 봐야 합니다. 이러한 타협은 회사의 운명이 걸린 상용 프로그램을 만들 때는 아주 중요합니다. 자꾸 개선하다가 오히려 에러를 더 낼 수도 있도 그로 인해 결과적으로 주어진 시간 내에 작업을 못 끝낼 수도 있기 때문입니다.

요약하자면 무슨 프로그래밍을 하건 간에 먼저 생각하는 습관을 들이라는 것입니다. 그리고 결과로 만들어지는 코드의 질을 높이기 위해 노력하라는 것입니다. 이런 자세가 습관이 된다면 자신이 만들어낸 코드에 대한 안정성과 높은 성능을 보장해줄 것입니다. 이는 프로 프로그래머가 가져야 할 의식이며 이것이 바탕이 된다면 다른 사람들도 모두 여러분을 믿을만한 사람이라고 높게 평가해줄 것임에 틀림없습니다. 사실 필자도 프로그래밍을 배울 때 이렇게 하지 못했습니다. 프로그래밍을 오래 하다 보니까 그런 자세를 처음부터 갖는 것이 중요하다는 생각이 든 것입니다.

 

2. 예로 살펴보는 버그와 디버깅 팁

이제부터 실질적으로 코드를 통해 자주 접하게 되는 버그의 유형에 대해 예를 들어 알아보기로 하겠습니다.

 

1> 부호 숫자 타입과 무부호 숫자 타입

C/C++에는 부호 숫자 타입과 무부호 숫자 타입이 다음과 같이 존재합니다.

타입 크기

부호 숫자 타입 크기

무부호 숫자 타입 크기

8비트

char (-128 ~ 127)

unsigned char (0 ~ 255)

16비트

short (-32,768 ~ 32,767)

unsigned short (0 ~ 65,535)

32비트

int (-2,147,483,648 ~ 2,147,483,647)

unsigned int (0 ~ 4,294,967,295)

32비트

long (-2,147,483,648 ~ 2,147,483,647)

unsigned long (0 ~ 4,294,967,295)

비주얼 C++에서 사실 int와 long은 모두 32비트라는 점에 유의하기 바랍니다. 초보 프로그래머들이 많이 실수하는 분야 중의 하나가 바로 부적절한 정수 타입을 사용하는 것입니다.

 먼저 사용되는 데이터 값의 범위를 확인하고 그에 맞는 변수를 선택해야 합니다. 위의 표를 보고 적절한 변수를 선택하면 됩니다. 위의 표에는 없지만 사실 __int64라고 해서 64비트 짜리 정수 타입도 있습니다. 사실 대부분의 경우 int로 충분합니다.

 만일 사용되는 데이터의 값이 음수가 될 수 없는 것이 분명하면 무부호 정수 타입을 사용하기 바랍니다. 예를 들어 int 대신에 unsigned int를 사용하란 이야기입니다.

 그런데 무부호 정수 타입을 사용할 경우에는 비교 연산시에 아주 주의해야 합니다. 예를 들어 다음과 같은 코드를 보면

unsigned int dwNumber1 = 100, dwNumber2 = 200;

if (dwNumber1 - dwNumber2 > 0)

AfxMessageBox("dwNumber1 is larger");

else

AfxMessageBox("dwNumber2 is larger");

당연히 if 연산이 거짓이 되어 두 번째 메시지 박스가 출력될 것 같지만 그렇지 않습니다. 이 if 연산은 참이 됩니다. 무부호 정수간의 연산 결과는 다시 무부호 정수가 됩니다. 무부호 정수에는 말그대로 음수가 없습니다. 따라서 이 연산은 항상 참이 됩니다. 위의 코드는 다음과 같은 식으로 변경해야 합니다.

if (dwNumber1 > dwNumber2)

AfxMessageBox("dwNumber1 is larger");

else

AfxMessageBox("dwNumber2 is larger");

즉 무부호 정수끼리 뺄셈을 하는 경우에는 아주 조심해야 한다는 것입니다.

 

2> 포인터 사용하기

C/C++에 처음 입문한 프로그래머들이 가장 어려움을 느끼는 영역 중의 하나가 바로 포인터라는 개념을 익히는 것입니다. 포인터는 메모리 주소를 가리키는 변수입니다. 여기에 메모리를 할당해서 유효한 메모리 주소를 갖게 하기도 하고 다른 변수의 주소를 가리키게 하기도 합니다. 즉 포인터 변수는 선언한 후에 바로 사용하면 안 되고 무엇인가가 유효한 메모리 영역을 가리키게 만들어야만 한다는 것입니다. 전산을 4년 전공하고 회사에 막 들어온 사람들 중 상당수가 이를 제대로 이해하지 못한 상태임을 여러 번 보았습니다. 그 중의 한 예는 다음과 같습니다.

void DoSomething(char *lpstrSource)

{

char *lpstrString;

strcpy(lpstrString, lpstrSource);

...

}

위의 코드를 보면 pString이란 포인터 변수를 선언한 후에 그걸 그대로 strcpy라는 함수에 사용하고 있습니다. 이 상태에서 pString이란 변수는 메모리의 아무 영역이나 가리키고 있습니다. 여기에다가 인자로 넘어온 pSource가 가리키는 문자열을 복사하려고 하면 당연히 프로그램이 뻗어버립니다. 이런 코드가 나오는 이유는 포인터라는 것의 개념을 이해하지 못했기 때문입니다. 그런 상태에서 strcpy 함수의 원형을 보고 그대로 변수를 선언하고 인자로 지정한 것입니다. 올바른 코드는 다음과 같습니다. 포인터에 메모리를 할당한 다음에 이 것이 제대로 할당된 경우에만 복사를 시도하는 식으로 변경해야 합니다.

void DoSomething(char *lpstrSource)

{

char *lpstrString;

lpstrString = (char *)malloc(strlen(lpstrSource)+1);

if (lpstrString)

strcpy(lpstrString, lpstrSource);

...

}

다시 정리하자면 포인터란 메모리 영역을 가리키는 변수이기 때문에 무엇인가 유효한 영역을 가리키도록 초기화해야만 사용할 수 있습니다.

참고 1. 메모리 할당

컴퓨터 세계의 함수 중에는 쌍으로 사용되는 것이 무지하게 많습니다. 대표적인 것이 바로 메모리 할당을 할 때 사용되는 malloc 함수와 더 이상 사용할 일이 없을 때 이를 운영체제에 반환해주는 free 함수입니다. C++에는 new와 delete가 있지요. 이것들의 쌍이 제대로 맞지 않으면 메모리가 조금씩 조금씩 부족해지다가 결국에는 바닥나게 되어 있습니다.

그런데 화장실 들어갈 때와 나갈 때 마음이 다르다고 처음엔 메모리가 필요하니까 할당해서 사용하지만 사후처리를 잊기 십상입니다. 뭐 간단한 프로그램에서야 메모리할당과 반환이 그다지 복잡하지 않지만 정말로 복잡한 프로그램에서는 이게 쉽지 않습니다. 서버 프로그램의 경우 아주 작은 양의 메모리가 반환되지 않는 경우에는 프로그램이 한두달 돌아야 그게 밝혀지는 경우도 있습니다.

그래서 자바(Java)와 닷넷(.NET)에서는 이런 메모리 반환의 책임을 프로그래머에게 넘기지 않고 시스템 레벨에서 처리합니다. 즉 사용되지 않는 메모리가 있으면 알아서 반환시켜버리는 것입니다. 이를 가비지 컬렉션(Garbage Collection)이라고 부릅니다. 프로그래머 입장에서 두손 들고 환영할 일이지요. 하지만 이 가비지 컬렉션을 해주는 프로세스가 주기적으로 동작해야 하고 메모리가 바로 반환되는 것이 아니라 시간이 걸리기 때문에 프로그램의 실행 속도가 전체적으로 좀 느려지게 됩니다.

 

3> 함수의 리턴값 체크

초보 프로그래머이건 노련한 프로그래머이건 간에 또 많이 하는 실수가 바로 함수의 리턴값을 체크하지 않고 그대로 코드를 작성하는 경우입니다. 리턴 값을 체크하려면 if 문이 들어가야 하고 이게 좀 귀찮은 일입니다. 또한 이게 많아지면 코드를 한눈에 보기도 힘들어집니다. 그래서 많은 프로그래머들이 별일 있겠냐 하는 마음에 함수 리턴값 체크를 하지 않습니다. 예를 들어 파일 I/O를 한다고 하면 다음의 코드처럼 리턴 값을 체크하지 않는 경우가 허다합니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

file.Open(strFilePath, CFile::modeRead);

file.Read(strHeader, 255);

이런 코드는 strFilePath가 가리키는 파일이 없거나 파일은 있지만 파일의 크기가 부족한 경우에 에러를 내게 됩니다. 사용된 함수의 리턴 값을 모두 체크하도록 코드를 수정하면 다음과 같습니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

bool bError = FALSE;

// CFile::Open 함수는 오픈이 성공하면 TRUE를 리턴한다.

if (file.Open(strFilePath, CFile::modeRead))

{

// CFile::Read 함수는 읽어들인 바이트수를 리턴한다.

if (file.Read(strHeader, 255) != 255)

bError = TRUE;

}

else

bError = TRUE;

if (bError) // 에러가 있으면

{

CString strError;

strError.Format("에러가 발생했습니다 - %d", GetLastError());

AfxMessageBox(strError);

}

수정된 코드를 수정전의 코드와 비교하면 아마도 조금 더 코드의 흐름을 이해하기 힘들다는 느낌이 들 것입니다. 하지만 대신에 훨씬 더 코드가 안정적으로 동작하게 됩니다. 참고로 수정된 코드를 보면 에러가 발생했을 때 GetLastError 함수를 부르는데 이 함수는 윈도우 운영체제가 제공해주는 함수로 방금 발생한 에러에 해당하는 에러코드를 리턴해주는 역할을 수행합니다. 이 값에 해당하는 설명 문자열을 보고 싶다면 비주얼 C++의 도구(Tools) 메뉴의 오류 조회(Error Lookup) 명령을 실행한 다음에 거기에 에러코드를 입력해보면 됩니다. 다음은 "오류 조회" 다이얼로그의 실행 화면입니다.


< 그림 1. "오류 조회(Error Lookup)" 다이얼로그의 실행 화면 >

가끔 테스트 프로그램을 작성하거나 시간이 없을 경우 방금 코드처럼 리턴 값을 일일이 체크하는 것을 생략하는 사람들을 많이 봤습니다. 코드를 작성하는 당시에는 나중에 제대로 검사하게 고쳐야지 하지만 그럴 만큼 부지런한 사람은 드뭅니다. 수정할 기회가 언제 올지 알 수 없기 때문에 무슨 프로그램을 짜던지 항상 최선을 다하는 것이 좋습니다. 즉 되도록이면 "나중에 고치지"라는 생각은 접어 두시기 바랍니다.

참고 2. try/catch

함수의 리턴값 체크가 번거롭다면 그의 대안으로 사용할 수 있는 것이 바로 C++의 try/catch 문법입니다. 비주얼 C++에서는 MFC 클래스에 대해서만 적용가능하다는 단점을 갖고 있긴 하지만 이를 이용하면 if 문을 이용해 함수의 리턴 값을 검사할 필요가 없기 때문에 코딩도 간단해지고 코드를 읽기도 좀더 쉬워집니다. "3. 함수의 리턴값 체크"에서 본 코드를 try/catch를 이용하도록 변경해보면 다음과 같습니다.

// strFilePath가 읽어들이고자 하는 파일의 경로를 가리킨다고 하자.

CFile file;

char strHeader[256];

try

{

file.Open(strFilePath, CFile::modeRead);

file.Read(strHeader, 255);

}

catch(CException e)

{

e.ReportError();

e.Delete();

}

try로 둘러싸여진 블록 내에서 에러가 발생하면 실행이 바로 catch 블록으로 넘어갑니다. 단 모든 에러가 다 try 블록에 의해 감지되는 것은 아닙니다. 에러가 발생한 경우 그것을 throw 키워드를 이용해 리턴하는 함수들에서만 에러가 감지됩니다. MFC 클래스들은 대부분 에러가 발생하면 CException이란 클래스로부터 계승된 각자의 에러 클래스의 개체를 throw하도록 되어있습니다. CFile 클래스의 경우에는 CFileException이란 클래스가 에러 클래스에 해당하며 CException으로 계승된 클래스입니다. 또 클래스는 아니지만 new로 메모리를 할당하는 경우에도 메모리가 부족하면 예외를 발생시킵니다. 즉 ,new를 이용해서 메모리를 할당하는 경우에는 try/catch를 이용한다면 굳이 메모리 할당이 제대로 되었는지 일일이 검사할 필요가 없다는 이야기입니다.

사실 try/catch/throw는 그 자체만으로 상당한 지면을 통해 설명해야 하기 때문에 여기서는 이런 것이 있다는 것을 알리는 정도로 끝을 맺겠습니다. 참고로 비주얼 베이직이나 자바, C# 등의 대부분의 현대적인 프로그래밍 언어들은 이런 방식의 에러처리를 지원합니다.

>

top

4> 컴파일러 경고 메시지에 신경쓰자

대개의 프로그래머들은 컴파일러가 내주는 에러 메시지에만 신경을 쓰고 경고(Warning) 메시지에는 둔감합니다. 그런데 이 경고 메시지를 눈여겨 보면 간혹 모르고 지나쳤을 버그를 잡는 경우가 있습니다. 조사에 의하면 버그를 발견하는데 드는 시간이 90%이고 이를 수정하는데 걸리는 시간은 10%에 불과하다고 합니다. 경고 메시지를 눈여겨 보면 적은 노력으로 버그를 발견할 수 있는 셈입니다. 다양한 경고 메시지 중에서 네 가지를 살펴보도록 하겠습니다.

 비초기화 변수 사용 경고

그 중의 대표적인 것이 바로 변수를 선언은 했지만 그 변수를 초기화하지 않고 사용하는 경우입니다. 예를 들면 다음과 같은 경우가 있습니다.

{

int nNumber1, nNumber2;

nNumber1 = 100;

nNumber1 += nNumber2;

nNumber2에는 어떤 값이 들어가 있을지 아무도 모릅니다. nNumber2에는 임의의 값이 들어가 있을 수 있고 이는 nNumber1의 값에도 영향을 주게 됩니다. 위의 코드를 컴파일하면 다음과 같은 컴파일러 경고 메시지가 나타납니다.

warning C4700: local variable 'nNumber2' used without having been initialized

컴파일러의 경고 메시지도 유의해서 보는 사람이라면 쉽게 nNumber2가 초기화되지 않고 사용된 사실을 알아차릴 수 있습니다. 사실 이런 종류의 에러는 컴파일러의 경고 메시지가 아니면 찾기가 그리 쉽지 않을 수도 있습니다. 그 이유는 위와 같은 결과가 프로그램의 실행 결과에 큰 영향을 끼치지 않을 수도 있기 때문입니다. 즉 모르고 넘어갈 수도 있다는 것입니다.

 함수 내에 값을 리턴하지 않는 플로우 존재

함수의 코드가 길어지고 그 안의 다양한 부분에서 값을 리턴해야 한다면 실수로 return문을 빼먹을 수가 있습니다. 이 경우에도 앞서 초기화 안 된 변수를 쓸 때와 마찬가지로 그 당시 스택에 있던 아무 값이나 리턴이 되고 또한 마찬가지로 프로그램이 실행되는데 별 지장이 없을 수 있습니다. 그래서 이 역시 좀 찾기 어려운 에러가 될 수 있습니다. 예를 들어 다음과 함수를 보겠습니다.

int ReturnCheckFunction(int i)

{

switch(iValue)

{

case 1: return 0;

case 0: return 1;

}

}

위의 함수를 보면 인자 iValue로 1,0이외의 값이 들어오는 경우 return 문이 존재하지 않습니다. 이 경우 스택에 있던 아무 값이나 리턴됩니다. 위의 코드를 컴파일하면 다음과 같은 경고 메시지가 발생합니다.

warning C4715: 'ReturnCheckFunction' : not all control paths return a value

 논리 판단문안에서의 치환문 사용

가끔들 많이 하는 실수 중의 하나는 if문이나 while 문과 같은 논리 판단문 안에서 == 대신에 실수로 =를 사용하는 것입니다. 이 에러도 경우에 따라서는 아주 찾기 힘든데 컴파일러의 경고 메시지를 눈여겨 보면 아주 쉽게 찾을 수 있습니다. 예를 들어 다음과 같은 코드가 있다고 합시다.

int iRet; iRet = CallSomeFunction(); if (iRet = 0) { ... }

위의 코드에서 if문을 보면 iRet의 값과 0을 비교한다는 것이 잘못 되어서 iRet에 0을 대입하고 있습니다. 이렇게 되면 이 if 문은 항상 거짓이 됩니다. 이 코드를 컴파일하면 다음과 같은 에러가 발생합니다. WARNING 4706: assignment withing conditional expression 반대의 경우로 =를 써서 변수에 어떤 값을 대입하는 경우에 잘못해서 등호 연산자인 ==를 사용하는 경우도 있습니다.

i == 0;

이 것도 컴파일이 됩니다. 단 컴파일러가 다음과 같은 경고 메시지를 내줍니다.

WARNING 4553: '==' : operator has no effect; did you intend '='?

항상 컴파일러가 내주는 경고 메시지에 꼭 신경쓰기 바랍니다. 몇십초 동안 잠깐 살펴보는 것으로 여러분이 몇 시간 혹은 며칠동안 고생하는 것을 방지해줄 수 있습니다.

 

5> BOOLEAN 타입

비주얼 C++에는 두 종류의 Boolean 타입이 존재합니다. 하나는 BOOL이고 다른 하나는 bool입니다. 이 두 타입은 모두 다른 타입으로부터 typedef를 이용해 재정의된 것인데 다음과 같이 정의되어 있습니다.

typedef int BOOL;
typedef byte bool;

이 두 가지 중에서 bool 타입을 항상 사용하기를 권합니다. 이 타입의 장점은 다음과 같습니다.

 크기가 작습니다. 위에서 볼 수 있듯이 BOOL 타입은 크기가 4바이트이고 bool 타입은 크기가 1바이트입니다. 즉, 커다란 배열을 사용할 일이 있으면 bool이 절대적으로 메모리의 낭비를 줄일 수 있습니다.

 안전합니다. BOOL은 사실 TRUE, FALSE이외에도 다른 값이 대입 가능합니다. 예를 들어 다음과 같은 코드를 보고 실행 결과를 예측해보기 바랍니다.

BOOL bRet = 100;

if (bRet == TRUE)

AfxMessageBox("bRet가 참입니다.");

else

AfxMessageBox("bRet가 거짓입니다.");

BOOL 타입은 원래 TRUE와 FALSE 둘 중의 한 값을 갖도록 되어 있지만 불행히도 위와 같이 bRet에 100을 대입하는 코드는 전혀 문제를 일으키지 않습니다. 그리고 FALSE는 0으로 정의되어 있고 TRUE는 -1로 정의되어 있습니다. 따라서 위의 if문에서 bRet의 값(여기서는 100이 됩니다)과 TRUE는 서로 다른 값이 되어버리기 때문에 두 번째 메시지 박스가 실행됩니다. 즉 BOOL 타입의 문제는 TRUE, FALSE 이외의 다른 값이 들어갈 수 있는 가능성이 있기 때문에 if 문을 어떻게 사용하느냐에 따라서 전혀 다른 실행 결과를 낳는다는 것입니다. 만일 위의 코드에서도 if (bRet)와 같이 사용했다면 if 문이 참이 될 것입니다.

반면에 bool 타입(혹은 BOOLEAN)은 true와 false라는 단 두 가지의 값만을 가질 수 있습니다. 만일 0이외의 값을 이 타입의 변수에 대입하여도 이 값은 true라는 값으로 바뀌어서 저장됩니다. 다음과 같은 코드를 보기 바랍니다.

bool bRet = 100;

if (bRet == true)

AfxMessageBox("bRet가 참입니다.");

else

AfxMessageBox("bRet가 거짓입니다.");

위의 if 문은 참이 됩니다. 앞서 설명한 것처럼 bool 타입에서는 0이외의 값은 true로 바뀌어 저장되기 때문입니다. 항상 bool 타입을 사용하기 바랍니다.

 

6> 함수 인자 유효성 체크

이것은 버그를 방지하기 위한 일종의 방법인데 자신이 만드는 함수의 선두 부분에서 주어진 인자가 맞는 범위에 있는지 항상 먼저 체크하는 것입니다. 이 방법은 특히 다른 사람과 사용할 함수를 만들거나 다른 사람이 만든 데이터를 사용하는 함수를 만들 때 아주 효율적인 방법입니다. 즉 자기가 예상하고 있는 데이터들이 들어오는지 사용에 앞서 한번 검사하는 것입니다.

방금 이야기한 것처럼 이 방법은 공동작업에 아주 쓸모가 많습니다. 사람마다 나름대로의 독특한 이해방식을 갖고 있기 때문에 한참을 이야기해서 함수 인자 및 데이터의 형식에 대해 토론을 하고 결론을 내려도 실제로 구현을 해놓고 보면 서로의 이해가 다른 경우가 많습니다. 이로인한 혼란을 막기 위한 방법 중의 하나가 바로 이 방법입니다.

예를 들어 어떤 소팅함수가 있는데 소팅 대상이 되는 것이 0부터 2048 사이의 정수라고 합시다. 그러면 그 함수의 선두 부분에 다음과 같은 검사 코드를 넣어두는 것입니다.

BOOL Sort(int *pnNumbers, int nCount)

{

// 검사 코드를 넣어둡니다.

if (nCount <= 0) // 소팅할 대상이 0보다 작으면 그냥 리턴합니다.

return FALSE;

for(int iIndex = 0;iIndex < nCount;iIndex++)

{

if (pnNumbers[iIndex] < 0||pnNumbers[iIndex] > 2048)

{

printf("%d 번째의 값이 이상합니다. - %d\n", iIndex, pnNumbers[iIndex]);

return FALSE;

}

}

// 실제 소팅 코드가 나온다.

....

}

위와 같은 식으로 코딩을 하면 많은 버그를 잡을 수 있습니다. 그런데 위의 코드를 보면서 속도가 느려지지 않을까 하는 걱정을 하는 사람도 있을 것입니다. 그런 걱정이 든다면 위의 코드를 디버그 모드에서 동작하도록 하면 됩니다. 원래 디버그 모드에서 개발을 하고 디버깅이 끝나고 나면 릴리스 모드로 컴파일해서 사용하는 것이 정석이니까요. 그렇게 할 수 없는 경우도 가끔 있습니다. 디버그 모드에서는 잘 동작하는 프로그램이 릴리스 모드에서는 잘 동작하지 않는 경우가 간혹 있습니다. 다시 본론으로 돌아와서 디버그 모드에서 위의 체크 코드를 활성화시키고 싶다면 #ifdef 조건부 컴파일 지시자와 _DEBUG 상수를 이용하면 됩니다. 이 상수는 디버그 모드로 컴파일되는 경우에 비주얼 C++ 컴파일러에 의해 정의가 됩니다.

bool Sort(int *pnNumbers, int nCount)

{

// 검사 코드를 넣어둡니다.

if (nCount <= 0) // 소팅할 대상이 0보다 작으면 그냥 리턴합니다.

return FALSE;

#ifdef _DEBUG

for(int iIndex = 0;iIndex < nCount;iIndex++)

{

if (pnNumbers[iIndex] < 0||pnNumbers[iIndex] > 2048)

{

printf("%d 번째의 값이 이상합니다. - %d\n", iIndex, pnNumbers[iIndex]);

return FALSE;

}

}

#endif

// 실제 소팅 코드가 나온다.

....

}

#ifdef 다음에 오는 상수가 #define문으로 정의된 것이면 여기서부터 #else 혹은 #endif까지의 코드는 포함되고 결과적으로 컴파일됩니다. 즉 디버그 모드인 경우에만 위의 검사 코드가 포함되는 것입니다. 릴리스 모드에서는 _DEBUG라는 상수가 정의되지 않기 때문에 위의 코드는 포함되지 않습니다. 참고로 #ifndef라는 것도 있습니다. 그 다음에 오는 상수가 정의되어 있지 않으면 참이 되는 것입니다.

다시 한번 말하지만 디버그 모드는 말그대로 개발시에 디버깅을 위한 컴파일 모드입니다. 이 모드에서는 컴파일러가 코드 최적화를 시도하지 않습니다. 그렇기 때문에 덩치도 크고 속도도 릴리스 모드에 비해 20-40% 가량 늦습니다. 따라서 상품화되거나 외부에 서비스를 하는 프로그램이라면 릴리스 모드로 컴파일되는 것이 필수적입니다. 참고로 비주얼 C++ 닷넷에서는 그림 2처럼 빌드(Build) 메뉴의 구성 관리자(Configuration Manager) 명령을 선택하면 이를 선택할 수 있습니다. 비주얼 C++ 6.0에서는 이 컴파일 모드를 결정하는 것은 Build 메뉴의 Set Active Configuration 명령에서 가능합니다.


그림 2. 디버그 모드와 릴리스 모드의 선택

7> 통일된 함수 실행 성공 리턴 값과 시작 인덱스 값

리턴 값이 있는 함수의 경우 성공을 나타내기 위해 사용하는 값을 통일하기 바랍니다. 예를 들어 어떤 함수에서는 성공을 나타내기 위해 0을 사용하고 다른 함수에서는 1을 사용하고 또 다른 함수에서는 -1을 사용한다고 하면 자신이 만든 함수라 해도 항상 코드를 다시 살펴보아야 하는 문제가 발생하며 이로 인해 버그가 발생할 수도 있습니다. 저는 항상 성공을 나타내는 리턴코드로 0을 사용합니다. 성공을 나타내는 함수의 리턴 값을 통일해서 사용하기 바랍니다.

시작 인덱스 값도 마찬가지입니다. 예를 들어 열명의 사람에 대한 정보가 있는데 이름이 주어졌을 때 그 사람에 해당하는 번호를 리턴하는 함수를 만든다고 합시다. 자 그럼 이 번호를 0부터 셀 것인지 아니면 1부터 셀 것인지 혼란이 오는 경우가 있습니다. 저는 이런 경우 항상 0부터 세며 꼭 주석이나 문서에 0부터 센다는 것을 명시합니다. C/C++의 배열 인덱스가 0부터 시작하기 때문에 C/C++에서는 무엇인가 숫자 정보의 시작을 0부터 하는 것이 자연스러운 경우가 많습니다. 자신만의 인덱스 시작값 시작 규칙을 만들어 두기 바랍니다. 만일 여러 사람이 같이 일을 한다면 다같이 통일하면 더욱 좋겠지요.


참고 3. 변수 표기법

프로그래머의 개인 취향에 따라 변수 이름을 만드는 방법은 참 다양합니다. 아무 생각없이 하는 사람도 많긴 합니다만. 윈도우 운영체제에서는 찰스 시모니(Charles Simony)란 사람이 만든 헝가리안 표기법이란 것을 사용합니다. 이런 이름이 붙은 이유는 이 사람이 헝가리 출신이었기 때문이었고 윈도우에서 이 사람의 표기법을 사용하게 된 것은 이 사람이 마이크로소프트의 초창기 멤버로 많은 일을 했기 때문입니다. 이 표기법은 변수 이름 자체에 그 변수의 타입에 대한 정보를 같이 주는 것이지요. 예를 들면 다음과 같습니다.

int nCount; // 정수 변수 앞에는 i나 n을 붙입니다.

DWORD dwCount; // DWORD 타입 앞에는 dw를 붙입니다.

bool bReturn; // Boolean 타입 앞에는 b를 붙입니다.

char *lpstrMessage; // 문자에 대한 포인터앞에는 lpstr 혹은 pstr을 붙입니다.

여기에다가 윈도우에서는 클래스 멤버 변수의 경우, 그 이름 앞에 m_를 붙이는 것이 일반적입니다. 저 같은 경우는 전역 변수의 경우 그 이름 앞에 g_를 붙입니다. 이렇게 하면 좋은 점은 변수를 보는 순간이 이게 어디 정의되었고 타입이 무엇인지를 알 수 있게 된다는 것입니다. 예를 들어 다음과 같은 코드를 보기 바랍니다.

m_nCount = nIndex +g_nMinimum;

위의 코드를 보면 m_nCount는 이 코드가 실행되고 있는 클래스내의 멤버 변수이고 nIndex는 로컬 변수 (흔히 말하는 auto 변수)이고 g_nMinimum은 전역 변수라는 것을 알 수 있습니다. 거기다가 모든 변수가 정수 타입이라는 것도 알 수 있습니다.


결론

지금까지 버그를 방지하거나 버그를 잡기 위한 여러 가지 방법들에 대해 알아보았습니다. 사실 제일 중요한 것은 빠르고 간결하며 정확한 코드를 만들겠다는 마음 자세가 아닌가 싶습니다. 이러한 자세가 바탕이 된다면 위의 방법들이 습관으로 만드는 것이 그리 어렵지 않을 것입니다. 사실 방법 하나하나는 대단한 것이 없습니다. 하지만 이것들이 합쳐지면 시너지 효과가 대단합니다. 이것들을 습관으로 만드는 것이 바로 버그를 없애는 프로그래밍의 시작이며 프로그래밍을 처음 배우는 단계에서 이를 습관으로 만들 수 있다면 여러분은 아주 뛰어난 프로그래머가 될 수 있을 것입니다.

참고문헌

  1. Steve Maguire, Writing Solid Code : Microsoft's Techniques for Developing Bug-Free C Programs, Microsoft Press, 1993
  2. Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison-Wesley, 1999
  3. Steve Maguire, Debugging the Development Process : Practical Strategies for Staying Focused, Hitting Ship Dates, and Building Solid Teams, Microsoft Press, 1994
  4. Steve C McConnell, Code Complete : A Practical Handbook of Software Construction, Microsoft Press, 1993
Posted by keegan
Posted by SB패밀리

닷넷 프레임워크 - 프로그래밍 방식으로 윈도우즈 서비스 작성
C#.NET 기준

Windows 서비스 프로젝트 템플릿을 사용하지 않고 상속 및 다른 인프라 요소를 직접 설정하여 독자적인 서비스를 작성할 수도 있습니다. 서비스를 프로그래밍 방식으로 만들 경우 템플릿을 통해 처리할 수 있는 몇 가지 단계를 직접 수행해야 합니다.

  • ServiceBase 클래스에서 상속하는 서비스 클래스를 설정해야 합니다.

  • 서비스 프로젝트에 Main 메서드를 만들어 서비스가 Run 메서드를 실행하고 호출하도록 정의해야 합니다.

  • OnStartOnStop 프로시저를 재정의하고 프로시저에서 실행할 모든 코드를 입력해야 합니다.

    Note참고

    Visual Studio Standard Edition에서는 Windows 서비스 템플릿과 관련 기능을 사용할 수 없습니다. 자세한 내용은 Visual Studio Edition을 참조하십시오.

프로그래밍 방식으로 서비스를 작성하려면

  1. 다음 단계에 따라 빈 프로젝트를 만들고 필요한 네임스페이스에 대한 참조를 만듭니다.

    1. 솔루션 탐색기에서 참조 노드를 마우스 오른쪽 단추로 클릭한 다음 참조 추가를 클릭합니다.

    2. .NET Framework 탭에서 System.dll로 스크롤한 다음 선택을 클릭합니다.

    3. System.ServiceProcess.dll로 스크롤한 다음 선택을 클릭합니다.

    4. 확인을 클릭합니다.

  2. 클래스를 추가하고 ServiceBase에서 상속하도록 구성합니다.

     
    public class UserService1 : System.ServiceProcess.ServiceBase 
    {
    }

    public class UserService1 extends System.ServiceProcess.ServiceBase
    {
    }

  3. 다음 코드를 추가하여 서비스 클래스를 구성합니다.

     
    public UserService1()
    {
        this.ServiceName = "MyService2";
        this.CanStop = true;
        this.CanPauseAndContinue = true;
        this.AutoLog = true;
    }

    public UserService1()
    {
        this.set_ServiceName("MyService2");
        this.set_CanStop(true);
        this.set_CanPauseAndContinue(true);
        this.set_AutoLog(true);
    }

  4. 클래스에 대한 Main 메서드를 만들고 이를 사용하여 클래스에 포함될 서비스를 정의합니다. 클래스의 이름은 userService1입니다.

     
    public static void Main()
    {
        System.ServiceProcess.ServiceBase.Run(new UserService1());
    }

    public static void main()
    {
        System.ServiceProcess.ServiceBase.Run(new UserService1());
    }

  5. OnStart 메서드를 재정의한 다음 서비스를 시작할 때 발생시킬 모든 처리를 정의합니다.

     
    protected override void OnStart(string[] args)
    {
        // Insert code here to define processing.
    }

    J#
    protected void OnStart(System.String[] args)
    {
        // Insert code here to define processing.
    }

  6. 사용자 지정 처리로 정의할 다른 모든 메서드를 재정의한 다음 코드를 작성하여 각 경우에 서비스가 수행할 동작을 지정합니다.

  7. 서비스 응용 프로그램에 필요한 설치 관리자를 추가합니다. 자세한 내용은 방법: 서비스 응용 프로그램에 설치 관리자 추가를 참조하십시오.

  8. 빌드 메뉴에서 솔루션 빌드를 선택하여 프로젝트를 빌드합니다.

    Note참고

    F5 키를 눌러 프로젝트를 실행하지 마십시오. 이 방법으로는 서비스 프로젝트를 실행할 수 없습니다.

  9. 서비스를 설치할 사용자 지정 동작과 설치 프로젝트를 만듭니다. 예제를 보려면 연습: 구성 요소 디자이너에서 Windows 서비스 응용 프로그램 만들기를 참조하십시오.

  10. 서비스를 설치합니다. 자세한 내용은 방법: 서비스 설치 및 제거를 참조하십시오.

    출처: http://msdn.microsoft.com/ko-kr/library/76477d2t(v=VS.80).aspx

Posted by SB패밀리