구조 개선 : 유지보수 가능한 코드로 리팩토링하기
구조 개선 과정은 선택이 아니라 필수입니다. 메시지 기반 구조 특성상 모든 로직을 WndProc에 넣으면 코드가 빠르게 복잡해지고 유지보수가 어려워집니다. 핵심은 이벤트 분기, 상태 관리, 기능 분리를 명확히 나누는 것입니다.
버튼과 입력창까지 구현했다면 이제 코드가 길어지고 수정이 점점 어려워졌을 것입니다. 이 단계에서는 기능을 확장하기 전에 구조를 정리해야 이후 작업이 훨씬 수월해집니다.
왜 구조 개선 이 필수 단계가 되는가
처음에는 WndProc 하나에 모든 코드를 넣어도 문제가 없어 보입니다. 하지만 기능이 늘어나면 다음 문제가 바로 나타납니다.
- 메시지 처리 코드가 계속 증가
- 전역 변수 관리가 어려워짐
- UI와 로직이 뒤섞임
WinAPI는 메시지 기반 구조이기 때문에 한 곳에 로직이 몰리면 수정 시 영향 범위를 파악하기 어려워집니다.
1 – WndProc은 이벤트 분기만 담당하기
WndProc은 “무슨 일이 발생했는지 판단하는 역할”까지만 담당해야 합니다. 실제 동작은 별도의 함수로 분리하는 것이 핵심입니다.
case WM_LBUTTONDOWN:
OnClick(hwnd, lParam);
return 0;
void OnClick(HWND hwnd, LPARAM lParam)
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
// 실제 처리 로직
}
이렇게 하면 WndProc은 이벤트 라우터 역할만 하게 되어 코드가 훨씬 읽기 쉬워집니다.
2 – 전역 변수 대신 상태 구조체 사용하기
전역 변수는 초기에는 편하지만 규모가 커질수록 문제를 일으킵니다. 상태를 하나의 구조로 묶는 것이 훨씬 안전합니다.
struct AppState
{
int x;
int y;
std::wstring text;
};
| 방식 | 특징 |
|---|---|
| 전역 변수 | 간단하지만 확장성 낮음 |
| 구조체 | 상태 관리 명확, 유지보수 용이 |
상태를 구조체로 묶으면 코드 흐름을 이해하기 쉬워지고, 기능 추가 시 충돌을 줄일 수 있습니다.
3 – 기능별 함수 분리하기
WinAPI 프로그램은 다음 세 역할로 나눌 수 있습니다.
- 입력 처리
- 화면 그리기
- UI 이벤트 처리
void Render(AppState& state, HDC hdc)
{
TextOut(hdc, 100, 100, state.text.c_str(), state.text.length());
}
void HandleInput(AppState& state, LPARAM lParam)
{
state.x = LOWORD(lParam);
state.y = HIWORD(lParam);
}
상태를 참조로 전달해야 변경 내용이 유지됩니다. 값으로 전달하면 수정이 반영되지 않는 문제가 발생합니다.
4 – 클래스 기반 구조로 확장하기
규모가 커지면 구조체만으로는 한계가 생깁니다. 이때는 클래스 기반으로 확장하는 것이 좋습니다.
class App
{
public:
void OnClick(LPARAM lParam);
void Render(HDC hdc);
private:
int x;
int y;
std::wstring text;
};
| 구조 | 특징 |
|---|---|
| 구조체 | 상태 관리 중심 |
| 클래스 | 상태 + 기능 통합 |
이 방식은 상태와 로직을 함께 관리하기 때문에 프로젝트 규모가 커질수록 효과가 커집니다.
구조를 개선하지 않은 상태에서 기능을 계속 추가하면, 코드 수정 하나가 전체 동작에 영향을 주는 상황이 발생합니다. 반대로 이 구조를 적용하면 기능을 독립적으로 확장할 수 있습니다.
이 단계까지 오면 WinAPI를 단순 학습이 아니라 실제 프로젝트 수준으로 확장할 수 있는 기반을 갖추게 됩니다.