JHB의 프로그래밍 삽질기

경력있는 프로그래머는 무엇이 다른가? 본문

PROGRAMMING/Essay

경력있는 프로그래머는 무엇이 다른가?

roter 2011.09.16 16:32



초급과정을 뗀 프로그래머, 초보티를 벗어나면서..

 

C++ 초급 책으로 공부를 다 하면, 보통 포인터까지 배운다.
그러면 포인터를 배운 이후에는 뭘 해야하는가? 도대체 뭘 해야하는지 알 수가 없었다.
왜 '중급' 프로그래머 책은 없는게야!(지금은 있는지도 모르겠다. 여튼 나 공부할 시절에는 없었다. 있긴 있었나? 여튼..)

이제 중급으로 가려면 무엇을 해야하나 싶어서 무작정 시작한 것이 Windows API였다.
이제 이것도 다 뗐는데.. 그럼 난 중급자라고 불러도 되는 것인가?
그럼 난 실무에 바로 투입되도 되는것인가?

이렇게 프로그래밍 새내기가 무작정 실무에 투입되면.. 엄청나다..
그들에게는 프로그램이 돌아가는 것이 중요하지 가독성이지 유지보수 따위니 하는 것은 당연히 신경쓰지 않는다.
심지어 변수명조차 신경쓰지 않고 매직넘버가 남발하며 한달만 지나도 자기 코드를 알아 볼 수가 없다.

이제 나름 실무에서 3년간 일한 프로그래머가 된 이 시점즈음에서 경력이 쌓이면서 배운게 무엇인가 싶어 되짚어 보게 되었다.
그리고 코딩시 항상 염두에 두는 것도 함께 적어 보았다.


1. 가독성 있는 소스와, 유지 보수가 용이한 설계
2. Font의 일괄 처리.
3. 다국어 지원
4. 모듈별 지원(라이센스)
5. 중복 다 빼기.
6. 해상도에 따른 주의 사항.
7. 커스터마이징, 설정에 따른 주의 사항.
8. UI에 사용하는 변수와 실물 값을 일치 시키지 말것.(Worker의 분리)
9. 상속이 아주 제대로 된 설계
10. Const등의 사용
11. 의존적이지 않게 프로그래밍 하기
12. 게터세터 사용
13. 기타 등등
14. 로그 파일 남기기 (20111017 추가)
15. 메모리 누수 방지(20130406 추가)

 

1. 가독성 있는 소스와, 유지 보수가 용이한 설계

우선 경력이 쌓이면서 제일 많이 달라지는 부분이다. 1년차 때 짰떤 프로그램은 지금 보면 이게 뭔소리여 이런다.
그런 코드들은 뭔가 하나 바꾸고 싶으면 하루 내내 프로그램을 쳐다보다가 포기하는 경우도 있고 고친다해도 고치는데 한계가 있는 경우가 많다.
2년차 3년차 되면서 짠 코드들은 지금 와서 봐도 금방 이해가 된다.
특히 VS를 쓰는 경우, 콜스택 같은 디버깅 툴은 정말 최강이다.
그리고 덧, 요새 대세는 주석을 안달아도 이해가 가능하게 코딩하는 것이 대세라고는 하지만, 그래도 주석은 많이 써줘야 한다.


2. Font의 일괄 처리. Font 처리해 주는것. Font같이 프로그램 전체에 공용인 것은 싱글톤 패턴으로 하나 있는게 좋음.

처음에 안드로이드로 삽질할 시절, 원하는 부분에 원하는 폰트를 집어 넣었었다.
하지만 삽질도 이런 삽질이 있나. 나중에 폰트를 다 바꿔주기 위해서 일일히 다 바꿔줘야 했다.
사실 폰트 같은 부분은 프로그램 전체에 공통적으로 사용되는 부분인 만큼, 프로그램 설계부 최상위 부분에서 싱글톤으로 하나 만들어서
공용적으로 불러와서 쓸 수 있도록 설계했어야만 했다.
현재 회사에서는 GetXXXFont( BIG_BOLD ) GetXXXFont( SMALL_NORMAL ) 이런식으로 메소드를 하나 만들어서 사용한다.
포토샵 같은 프로그램 맨처음 킬 때 포토샵 로고 나오고 로딩창 띄우는 것들, 그런것들 다 이런 리소스들 로딩하는 과정이라고 생각하면 된다.
폰트 한번 정하고 바꿀일이 없을 것 같다고 느껴질려나? 갑자기 일본 고객사에서 우리프로그램을 쓰고 싶은데 일본어를 넣어달라고 요청을 한다. 근데 현재 폰트가 일본어를 지원하지 않는다면? 오호... 노가다로 바꿀것인가..?? 과연..?


3. 다국어 지원
우선, 이를 위해서도 모든 스트링은 String Table을 사용해야만 한다.. 괜히 UI에 출력되는 스트링을 코딩상에서 구현했다가는 나중에 눈물 흘린다.
그리고... 무조건 유니코드를 사용하자 -.-.... 괜히 멀티바이트로 했다가문자별 인코딩 생각하면 머리가 터진다.
또한 다국어 패키징을 자동화 시킬려면 이것 역시 머리 깨진다. 경험이 쌓이면 설계시부터 고려해서 하게 된다.
참고로 요새 웹에서는 이미지 버튼 안쓰고 CSS로 만든 버튼에 텍스트를 입해서 쓰는 경우가 많다고 한다.


4. 모듈별 지원(라이센스)
라이센스에 따라 보여지는 것이 다른 경우 이것 참 난감하다.
A회사에서는 '이 기능만 추가되면 살텐데'라고 하고 B회사에서는 '이 기능만 빼고 좀 싸게 팔면 살텐데'하면 어떻게 할텐가?
당연히 둘의 입장 모두 들어줘야되는 것이 우리나라 대한민국 소프트웨어 영업인들이 주로 택하는 방법일 것이다.
이에 대한 설명은 6 커스터마이징, 설정에 따른 주의 사항에서 다시 설명하겠다.


5. 중복 다 빼기
말그대로다. 중복 다 빼자. 특히 C++같은 경우는 똑같은 객체를 여러개 생성하는 바보짓을 하지 말자.. C++에는 만능의 포인터가 있단 말이다!!
그리고 역시나 자주 사용하는 함수들은 inline으로 만들던지 해서 설계부의 최 상단에 박아 놓자.


6. 해상도에 따른 주의 사항.

안드로이드 프로그래밍이건 윈도우 프로그래밍이건, 해상도에 따라서 프로그램이 변하는 것을 다 고려하려면 정말 한도 끝도 없다.
어느 부분은 동적으로 UI를 설계하고 어느 부분은 고정적으로 하다 보면 결국엔 엄청나게 깨지게 된다.
특히 윈7의 경우 제어판의 모양 및 개인 설정의 디스플레이에서 텍스트 크기를 100%, 125%, 150% 이렇게 셋중 하나로 선택 할 수 있는데, 이것 덕분에 기존에 고정+동적으로 설계했던 다이얼로그의 경우 크기가 와장창 깨진다. 기존에 이를 고려안하고 프로그래밍한 지금 당장은 고객님 100%로 사용해주세요, 라고 말 하는 수 밖에..
안드로이드는 해상도가 워낙 많아서.. 덕분에 많은 개발자들이 호환성이 좋은 아이폰으로 가는 경우도 많이 봤다..
매우 빡치긴 하지만, 글로벌하게 사용되게 하기 위해선 해상도를 고려해서 잘 짜야 한다.
이래서 갠적으로는 커널 프로그래머보다 UI프로그래머가 더 빡쎄보인다.. 난 지겨워서 못할듯..
그래서 요새 누군가가 안드로이드 프로그램 만들어달라고 그러면 해상도는 어떻게 맞춰야하는지 부터 물어본다.


7. 커스터마이징, 설정에 따른 주의 사항.

회사별로 커스터마이징을 다르게 하면, 초기에 실 수 할 수 있는게 "어느 회사의 경우 어떻게 보여지도록 하라"라고 특정 부분만 대충 수정 하는 것이다.
차라리 아예 해당 부분을 유저가 설정에서 커스터마이징 할 수 있게 만들어 버리는게 깔끔성 면에서나 유지 보수를 위해서나 훨씬 좋다.
물론 들어가는 노력은 배가 될 것이지만.
이것 말고도 설계상에 정말 중요한 것이 있는데... 아 필력이 딸려서 설명하기가 너무 힘들다.
예를 들어 'Exam'버튼을 누를 때 A회사에서는 1이라는 프로세스를 실행하길 원하고 B라는 회사에서는 2라는 프로세스를 실행하길 원할 때
처음에 아무 생각 없이 이런 식으로 코딩을 했더랬다.
if( btnExam.onClick )
{
 if( company == A )
   process1();
 else if( companny = B )
   process2();
}
이상이 없어 보이는 이 곳에.. 사실은 엄청난 문제가 있다. 갑자기 C회사가 나타나서 자기들은 process1이 되길 원한다고 하면 어떻게 할려는가?
if( company == A || company == C ) 이런식으로 계속 추가하다보면 거래처가 많아지면 많아 질수록 나중에는 소스에 손을 댈 수 없는 상황이 와버린다.
이렇게 커스터마이징 해준 기능이 대충 30개라고 치고 거래처가 20개라고 치면 무언가 유지보수 할 때마다 600군데씩 건드려야 된다는 말이다. 우와! 이건 못해!
차라리 처음부터 이렇게 갔어야 했다.
if( btnExam.onClick )
{
 CSetting setting = Preference.GetSetting( COMPANY /*= NULL*/, /USER /*= NULL*/ );
 UINT nFlag = setting.GetPermissionBtnExam();
 if( nFlag == process1 )
   process1();
 else if( bFlag == process2 )
   process2();
}
뭐 대충 이런식으로 말이다. 나중에 거래처가 늘어나면 Preference만 건드려 주면 된다. 저렇게 해서 COMPANY뿐만 아니라 걍 유저가 설정한 값으로도 분기 할 수 있게..
예를 들어 A회사에서 설정한 Exam 버튼의 Default값은 process1이었지만, user가 프로그램 내에 있는 option에서 해당 버튼의 기능을 process2로 바꿨다면, process2가 되도록 flag를 리턴해주는 것이다.
으으 설명이 너무 어렵다.. 뭐 우선은 나만 알아봐도 만족.....ㅋㅋㅋㅋㅋ


8. UI에 사용하는 변수와 실물 값을 일치 시키지 말것.(가능하다면 UI와 워커 부분을 완전 분리할 것. 의존성 노노)

수천개의 샘플 데이터를 그래프로 표시하는 컨트롤을 만들었었는데, 이 때, 클릭한 부분을 값으로 추출하도록 하였다.
별 생각 없이 UI에서 사용하는 m_nX, m_nY값과 실제 그래프에서 외부로 보내는 값을 일치 시켜서 프로그래밍했는데, 후에 요구 사항이 다음과 같이 변경되었다.
'그래프를 확대 축소 해서 볼 수 있게 해주세요.'
이에 따라서 실제 클릭한 영역과 실제 값이 다르게 되었다.
예를 들어 UI에서 400px 부분을 클릭해서 400이라는 값을 얻어 오고 싶었지만, 그래프가 축소된 이후에 400px자리를 찍으면 800이란 값을 얻어와야 하는 것이다.
기존에 UI에서 사용한 값을 그대로 리턴하도록 짠 덕분에, 요구사항이 변경된 이후 대폭 수정해야만 했다.
꼭 이런 예 뿐만 아니더라도, 사실 UI와 실제 처리 부분은 완전히 별개로 두는게 가장 좋다. 근데 그게 힘들긴 힘들다 -.-...
Worker 를 따로 두는게 좋은데, 예를 들어 MFC 초보일 때 가장 하기 쉬운 설계 중에 하나는, 다이얼로그에서 바로 계산시켜서 결과를 보여 주는 것이다.
예를 들어 다이얼로그에 텍스트박스 2개가 있고 버튼이 하나 있다. 버튼을 누르면 두 텍스트박스에 써진 숫자를 더해서 AfxMessageBox로 값을 띄워준다고 해보자.
초보일 땐 어떻게 짰을까?
대충 이렇게 짰었다.(함수명이나 형 같은건 무시)
int a = atoi(TextBox1.GetText()) + aoit(TextBox2.GetText());
CString strTmp;
strTmp.format(_T("%d"),a);
AfxMessageBox(strTmp);

정말 간단하다!
다이얼로그의 버튼 이벤트에서 덧셈도 수행하고 메세지 박스도 띄워준다!
지금은 예를 들려고 간단한걸 해서 그렇지만 나중에 계산해야 될 양이 많아졌는데도 저런식으로 짜면 곤란하다.
요새는 아무래도 이런 식으로 짠다.
int nVal1 = atoi(TextBox1.GetText());
int nVal2 = atoi(TextBox2.GetText());

CProcessVal valPro;
valPro.setVal1( nVal1 );
valPro.setVal2( nVal2 );
CProcess process( valPro );
process.process();
int nRet = process.GetResult();
CString strTmp;
strTmp.format(_T("%d"),nRet);
AfxMessageBox(strTmp);

뭐 대충 이런 식으로..... 위에 예제는 과장이 심했지만 보통은 대충 그냥 저런식이다. 다이얼로그에서 바로 처리 안하고 다른 곳에서 처리해주도록!!
나중에 똑같은 기능이 또 쓰일 줄 어떻게 알고 그걸 걍 다이얼로그에서 처리하게 하는가 ㅋㅋㅋ;; 분리 분리 분리! 하도록!


9. 상속이 아주 제대로 된 설계

RPG만들기 씨리즈로 게임을 만들 때, NPC를 만들면 NPC 오브젝트를 불러다가 그 안에 있는것들을 설정만 해주면 됐다.
NPC를 상속받은 동물인지, 해적인지, 산적인지에 따라 공통되는 부분은 그대로 남고, 각자의 특수성에 따라 다른 변수들은 달라졌었다.
프로그래밍이란 것도 사실 크게 다를 바 없다. 거기서 GUI로 보던게 걍 다 CUI로 바뀌었다고 보면 된다.
진짜 웬만한 오브젝트들은 상속 하나 해주는 것 만으로도 플밍이 된다면, 클래스 설계한 사람에게 매우 감사해하자!!
그리고 P를 상속받은 C1과 C2와 C3에 계속 중복되는 코딩하지 않게 하는건 정말 P를 잘 짜야지 된다.
그리고 짜다가 바꾸는 것 보다 처음부터 설계를 잘해서 코딩하는게 훨씬 좋다.


10. Const등의 사용

프로그래머 왕초보 시절에 진짜 진짜 궁금했던 것이다. Const 도대체 왜쓰지? 그냥 값 내가 안바꾸면 되는거 아닌가?
하하하하하하.. 초보 스러운 생각이었다. const 붙여주면 쓰는 것이 금지 된다. 그래서 나중에 해당 소스를 다시 보면 아 이녀석은 절대 바뀔 일이 없는 놈이겠군.
아, 이 메소드에서는 멤버 변수를 바꾸면 안되는군.(즉 뭔가 계산해서 멤버변수에 할당하는 놈이 아니군) 쯤으로 생각 할 수 있는 것이다.
반대로 const가 없는 놈은 무언가 처리하는 놈이겠지요?
비슷한 맥락으로, private에 변수 왜 넣지? 걍 public에 넣어놓고 접근 안하면 되는거 아닌가? 랑 마찬가지려나.


11. 의존적이지 않게 프로그래밍 하기

정보처리기사던가 시험볼 때 나왔던 거다. 결합도에 대한 부분. 물론 결합이 아예 없을 순 없다. 그러나 떨어 뜨릴 수 있는건 최대한 떨어뜨리는게 좋다.
MFC의 경우, 모듈로 작성하는 프로그램일 때 DLL로 나누어 놓으면 자동적으로 결합도가 낮아지므로, 이 좋지 아니한가?
경력이 쌓이고 쌓이면서 늘게 되는건 아무래도 의존성을 줄인 설계방법과 유지 보수가 용이한 설계 방법에 관련된 부분인것 같다.
이 부분은 정말 경험하지 않고는 깨닳기 힘든 부분인 듯 싶다. 아무리 창조적이고 획기적인 아이디어가 있어도, 구현 및 설계 부분은 경력 짬이 최고인듯..
그래서 아이디어만으로 사업이 성공하지 않는거임. 논외지만, 20대에 성공하기 힘들다는 것은 경험이 많이 없기 때문인 이유도 있는데, 코딩 실력 또한 한몫 할 듯.
아 참고로, 결합도가 높으면.. 컴파일 하는데 시간이 어머어머해진다 -.-;;; 지금 회사 같은 경우는 결합도가 높은 모듈 하나 건드렸다가는 컴파일 시간이 20~30분씩 걸리는 경우도 많다.


12. 게터세터 사용

게터 세터 무조건 쓰는게 좋다!
심지어 같은 클래스 내에서도 사용하는 것이 좋다. 그래야 나중에 브레이크 포인트 걸기도 엄청 편하고 유지보수도 용이해진다.
진짜 이건 아무리 귀찮아도 게터 세터는 강추 초초 강추 하는 항목이다.
아 더욱이 세터의 경우 void로 하느냐 bool로 하느냐 고민이 많이 되는데, 난 주로 bool로 처리 한다. 그래서 에러값이 들어오면 false리턴!
이건 걍 자기 하고 싶은대로 하면 될 듯.
이클립스에서는 커맨드로 쉽게 만들 수 있는 반면 VS에서는 자동으로 만들어주는 기능이 없다..
있으면 참 좋을텐데... VAX에는 있으려나?

13. 기타 등등

그 밖에 너무 많다. 근데 걍 정리해 보자면, 경력이 늘고 중급자가 될 수록 발전하는 것은 유지 보수가 용이하게 프로그램을 짠다는 것이다.
나중에 확장도 용이하고 말이다.
심심하면 가끔씩 android 소스 같은거 보고 그러는데, 소스가 정말 아름답다. 그거대로만 잘 따라해도 엄청난 수확!

14. 로그 파일 남기기

내가 사용하는 기기에서는 문제가 안생기는데, 고객사에서는 문제가 생긴다. 도저히 내 기기에서 문제를 재연시킬 수가 없다. 고객 피씨에 소스를 깔고 디버깅 해볼 수도 없는 노릇이다 -.-;; 이럴 때 정말 필요한게 로그를 확인 하는 것이다. 적재 적소에 로그를 잘 뿌려주면 나중에 문제 해결이 상대적으로 용이해 진다. if문이나 switch문마다 로그를 찍어주도록 하자. 물론, 파일로 남겨서 내가 받아오기 쉽게 하는 것은 당연지사 ~_~

15. 메모리 누수 방지

우선, 프로그램에서 무언가 리소스를 로딩하고 사용 할 때는 반드시 스레드를 사용해서 프로그레스 다이얼로그를 띄우는 식으로 처리하는게 좋고, 미리 로딩해 놓고 사용하는 것도 좋다.
무엇보다 중요한 것은 메모리 누수를 방지 하는 것인데, 특히 비트맵 등을 로드하여 사용하는 경우 가장 먼저 설계해야 하는 것이 로딩과 메모리 해제 하는 부분이다.

 

글이 너무 길고 재미가 없다.
근데 걍 나만 볼려고 쓴거라고 생각하니 맘이 편해진다.
나중에 또 읽어 봐야지.
그럼 모두 즐거운 하루 보내세요!
--------------------------------------------------------------------------------------------
120224 추가
역시 시간이 지나면 바뀐다.
게터세터 많이 쓰는거 안좋은것 같다.
예를 들어 3+4 를 계산하고 저장하고 싶은데
a.setValue(b.getValue() + c.getValue())
식으로 하게 된다.
이렇게 나누면 안좋을듯 하다.
a.Plus(b,c)
차라리 뭐 이런식?으로 갔어야 할듯.. 그러니까.. 다양한 로직이 처리 가능하게 메소드를 만드는게 게터세터를 여러개 만드는거보다 나은듯.

--------------------------------------------------------------------------------------------

2012년 7월 21일 추가
c/c++ 프로그램의 경우 프로그램을 설계할 때 객체가 어디까지 유효한지 체크하고 어느 시점에 delete를 해주는지 까지 고려해야 할 듯 하다.

--------------------------------------------------------------------------------------------

2015년 9월 5일 추가
7번 '커스터마이징' 같은 경우는, 현재 CSC로 처리하는 방법을 기본으로 한다고 본다.
또한 Git의 Branch 기능이 워낙 막강하기 때문에, Feature Branch를 따로 관리 하면서 Release 시점에 Customize Release Branch 에 요구 사항과 관련된 Feature Branch를 Merge하여 사용하는 방법도 좋은 것 같다. 이 경우 C++이 아닌 C Program 이라 하더라도 define의 남용을 자제할 수 있다. 결론 : GIT 짱!

0 Comments
댓글쓰기 폼