C++ 코드 구조화 제대로 이해하기 - 네임스페이스부터 헤더/소스 분리까지
C++ 코드 구조화 제대로 이해하기
유지보수성, 가독성, 빌드 속도까지 개선하는 C++ 프로젝트 설계법
C++로 프로젝트를 진행하다 보면 점점 파일이 많아지고, 클래스 간 의존성도 복잡해진다.
이럴수록 코드를 체계적으로 나누고 정리하는 구조화 전략이 중요해진다.
이번 글에서는 실무에서 자주 사용되는 C++ 코드 구조화 방법을 예제와 함께 정리해보았다.
네임스페이스로 이름 충돌 방지하기
C++에서 여러 라이브러리나 클래스가 같은 이름의 함수나 변수를 가지면 충돌이 발생할 수 있다.
이를 방지하기 위해 namespace
를 사용하면 코드의 모듈화도 동시에 이룰 수 있다.
namespace MyApp {
int add(int a, int b) {
return a + b;
}
}
int result = MyApp::add(3, 4);
네임스페이스는 중첩도 가능하고, using 키워드로 일부 또는 전체를 가져올 수도 있다.
인클루드 가드로 헤더 중복 막기
같은 헤더 파일이 여러 소스에서 참조될 경우, 중복 정의 오류가 날 수 있다.
이를 방지하기 위한 방법이 include guard다.
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 클래스나 함수 선언
#endif
#pragma once를 사용하는 것도 가능하지만, 호환성과 명확성을 위해 전통적인 include guard도 여전히 널리 사용된다.
헤더(.h) 파일과 소스(.cpp) 파일 분리하기
C++에서는 선언과 구현을 분리하는 것이 일반적인 패턴이다.
- .h 파일: 클래스와 함수의 선언
- .cpp 파일: 실제 구현
이렇게 구조를 나누면 컴파일 시간도 줄고, 유지보수 시에도 한눈에 파악하기 쉬워진다.
// Example.h
class Example {
public:
void print();
};
// Example.cpp
#include "Example.h"
#include <iostream>
void Example::print() {
std::cout << "Hello, world!" << std::endl;
}
전방 선언으로 의존성 최소화
클래스나 함수가 다른 파일에서 참조될 때, 정의 없이 먼저 선언해두는 것이 전방 선언이다.
이는 컴파일 속도 향상과 순환 참조 방지에 큰 도움이 된다.
class MyClass; // 정의 없이 선언만
void doSomething(MyClass* ptr); // 포인터 또는 참조로만 사용 가능
함수도 마찬가지로 먼저 선언하고 나중에 정의하는 방식이 가능하다.
int add(int, int); // 선언
int main() {
return add(2, 3);
}
int add(int a, int b) {
return a + b;
}
코드 분할과 개별 컴파일
코드를 기능 단위로 분할하고 각 소스 파일을 독립적으로 컴파일하면 효율적인 빌드가 가능하다.
예를 들어, 클래스마다 .cpp 파일을 만들고 별도로 오브젝트 파일을 생성한 후 링크하면 된다.
g++ -c main.cpp -o main.o
g++ -c utils.cpp -o utils.o
g++ main.o utils.o -o my_program
빌드 속도 개선과 함께 유지보수 시에도 수정 범위가 줄어든다.
스코프를 명확히 구분하자
변수나 함수가 어디에서 유효한지를 결정하는 범위를 스코프(scope)라고 한다.
C++에서는 다음과 같은 스코프가 존재한다.
- 전역 스코프: 파일 전체에서 접근 가능
- 지역 스코프: 함수나 블록 내에서만 유효
- 클래스 스코프: 클래스 내부 멤버 전용
- 네임스페이스 스코프: 특정 네임스페이스 내에서만 사용
int globalValue = 10;
void func() {
int localValue = 5;
std::cout << globalValue + localValue;
}
스코프를 정확히 구분하면 변수 충돌과 메모리 관리에서 불필요한 혼란을 줄일 수 있다.
마무리
프로젝트가 커질수록 코드의 구조는 곧 유지보수성과 직결된다.
네임스페이스, include guard, 전방 선언, 헤더/소스 분리, 코드 분할 등의 전략은 C++ 실무 개발의 필수 요소다.
이제부터는 기능을 구현하는 것뿐 아니라, 구조를 어떻게 잡을지도 함께 고민하며 코드를 작성해보자.