. 발상의 전환

ApiEdit를 만들면서 가장 많이 애를 먹었던 부분이 바로 방금 작업을 마친 마진영역이었다. 작업영역에 무엇인가 다른 정보가 같이 출력되어야 한다는 점이 상당히 힘들었는데 특히 수평스크롤될 때 마진영역이 같이 스크롤되지 않도록 하는 것이 어려웠다. 이 증세는 이미 앞에서 확인해보았고 OnHScroll에서 클리핑영역을 지정함으로써 간단하게 해결되었다. 그러나 문제가 계속 터져 나왔다.

최초 마진의 출력코드는 OnPaint에 있었고 DrawLineOnPaint가 그려준 마진영역에 줄번호나 북마크 표시만 할 뿐이었다. 왜 이렇게 했는가 하면 마진영역은 문서에 없는 부분이고 DrawLine은 문서의 한 줄만 출력하는 함수이기 때문에 OnPaint가 직접 마진을 그려야 한다고 생각했기 때문이다. 이 방식대로 코드를 작성해도 전혀 문제가 없는 것처럼 보였는데 예상 외의 문제가 있었다. 스크롤바를 드래그해서 스크롤하는 것은 클리핑영역이 지정되지만 키보드로 수평스크롤을 하면 마진이 깨지는 것이다. 다음 그림처럼 말이다.

왜냐하면 SetCaret이 스크롤을 할 때는 클리핑영역 지정없이 화면 전체를 무효화시켜 다시 그리도록 되어 있었고 DrawSegment는 현재 xPos 위치에 아주 정직하게 출력을 하기 때문이다. 이 문제를 해결하기 위해 DrawSegment TextOut 출력문을 ExtTextOut으로 교체했다.

 

void DrawSegment(HDC hdc, int &x, int y, int SegOff, int len, BOOL ignoreX, COLORREF fore, COLORREF back)

{

     RECT cliprt;

 

     if (MarginWidth != 0) {

          SetRect(&cliprt,MarginWidth,y,frt.right,y+FontHeight);

     } else {

          SetRect(&cliprt,0,y,frt.right,y+FontHeight);

     }

          ....

          ExtTextOut(hdc,x,y,ETO_CLIPPED,&cliprt,buf+SegOff,len,NULL);

 

TextOut 함수는 클리핑 기능이 없기 때문에 스크롤된 상태에서는 마진영역을 침범할 수밖에 없는 것이다. 이렇게 수정한 후 한 동안은 괜찮았다. 그런데 제어코드 보기가 또 말썽을 일으키기 시작했다. 탭이나 엔터코드는 선택된 상태일 때 FillRect로 직접 영역을 그리는데 이 함수가 클리핑영역을 계산하지 못하는 것이다. 그래서 DisplayTab, DisplayEnter 함수에 클리핑영역을 인수로 전달하고 이 영역을 넘어서지 않도록 모든 출력문에 조건을 달아 주었다.

코드는 급격하게 늘어나고 지역변수들도 덩달아 늘어났지만 어쨌든 문제는 해결되는 것처럼 보였다. 그러나 실상은 더 골치아픈 문제들이 기다리고 있었다. 제어코드를 좀 멋지게 출력해보려고 그래픽으로 바꾸었는데 LineTo, Rectangle 이런 함수들이 클리핑영역 바깥을 마구 침범하였으며 그 외에도 설명하기 힘든 몇 가지 문제들이 계속 발생했고 나는 이 문제들을 해결하기 위해 많은 양의 코드를 작성했다. 하지만 그 코드들은 근본적인 해결책이 되지 못했다.

결국 이 문제는 OnPaint에 있는 마진 출력문을 DrawLine으로 옮기고 나서야 해결을 보았다. 애초에 마진으로 나가는 출력을 클리핑하겠다고 생각한 것부터가 잘못이었던 것 같다. 모든 텍스트를 다 출력한 후에 마진을 그렸으면 될 것을 마진 먼저 그려 놓고 텍스트가 그 영역을 침범하지 못하도록 애써 막으려 한 것이 실수의 시작이었다. 순서를 바꾸면 텍스트나 개행코드가 마진을 침범하든 말든 그대로 내버려 두어도 DrawLine이 리턴하기 전에 마진을 깨끗하게 다시 그려 준다. 어차피 더블 버퍼링을 하고 있으므로 중간 결과는 보이지도 않는다.

어처구니 없는 실수담을 하고 있는 중인데 해결책을 잘못 설계해서 시간을 낭비하는 경우는 누구에게나 있다. 미국이 처음 우주인을 내 보내 우주를 탐사하게 한 후, NASA는 우주에서 볼펜을 쓸 수 없다는 사실을 알게 되었다. 볼펜의 액은 중력의 힘으로 밑으로 내려오고 그래서 필기가 가능한데 우주에서는 중력이 없으니 볼펜이 제 기능을 발휘하지 못하는 것이다. 그래서 NASA는 수억 달러와 상당한 시간을 들여 중력과 상관없이 쓸 수 있고 심지어 물속에서도 필기가 가능한 멋진 볼펜을 개발했다.

무엇이든 필요가 있으면 어떤 대가를 치르고라도 문제를 해결하는 미국의 적극적인 탐험 정신을 보여주는 한 예라 할 수 있다. 그런데 똑같은 시기에 우주 탐사를 한 소련의 해결책은 이와는 완전히 달랐다. 소련의 우주인은 우주에서 연필로 필기를 한다. 연필은 중력이 없어도 되고 물속이나 극한 환경에서도 필기가 잘 되는 우수한 필기구인 것이다.

코드를 작성할 때도 이런 발상의 전환이 필요하다. 문제가 발생했는데 그 문제를 어떻게 해결할 것인가에 급급해서 마구 코드를 작성하다 보면 더 문제가 커지기만 하는 경우를 종종 경험하게 된다. 아무리 경험이 많은 개발자라도 이런 경우가 항상 있으며 더구나 지금 코딩을 열심히 배우고 있는 사람들에게는 더 말할 나위도 없다.

이럴 때는 계속 코드를 작성하지 말고 한 발짝 떨어져서 과연 애초의 문제점과 지금의 해결책이 적합한가를 한 번쯤 살펴볼 필요가 있다. 지금 작성하고 있는 코드의 방향이 문제 해결을 위한 곳으로 가고 있는지, 왜 이렇게 멀리 가야만 하는지, 얼마나 더 복잡해져야 문제가 해결될 수 있을지 잠시 여유를 가지고 생각해보도록 하자. 정말 문제가 복잡해서 많은 양의 코드가 필요하다고 판단이 되면 부지런히 코딩을 해야겠지만 그렇지 않다면 방향 전환이 필요하다.

문제가 사소한 것 같지만 실제로 풀어보면 아주 복잡한 경우가 있다. 그런데 이런 경우보다 더 흔한 경우는 도저히 풀지 못할 정도로 복잡해보이는 데 막상 해결해놓고 보니 시시하게 해결되는 경우가 훨씬 더 많다. 문제를 만났을 때 당장 떠오르는 생각만으로 성급하게 코드 작성을 시작하지 말고 좀 더 연구해 본 후 작업을 시작하도록 하자. 출력순서만 바꾸면 될 것을 바보같이 왜 저런 고생을 했던가 말이다.