|
| 1 | +# 사용하는 것만 포함하기 (Include What You Use) |
| 2 | + |
| 3 | +소스 파일이나 헤더 파일이 다른 곳에 정의된 심볼을 참조한다면, 해당 심볼의 선언 또는 정의를 제대로 제공하는 헤더 파일을 **직접** 포함해야 합니다. 다른 이유로 헤더 파일을 포함해서는 안 됩니다. |
| 4 | + |
| 5 | +간접 포함(transitive include)에 의존하지 마세요. 이렇게 하면 더 이상 필요하지 않은 `#include`를 헤더에서 제거하더라도 클라이언트 코드가 깨지지 않습니다. 이는 관련된 헤더에도 동일하게 적용됩니다. 예를 들어, `foo.cc`가 `bar.h`에 정의된 심볼을 사용한다면, `foo.h`가 `bar.h`를 포함하고 있더라도 `foo.cc`는 `bar.h`를 직접 포함해야 합니다. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 이해하기 쉽게 설명하기 |
| 10 | + |
| 11 | +### 기본 원칙: "내가 쓰는 건 내가 포함한다" |
| 12 | + |
| 13 | +- 어떤 파일이 특정 타입, 함수, 상수 등을 **사용**한다면, 그 파일은 해당 심볼을 선언/정의하는 헤더를 **직접 포함**해야 합니다. |
| 14 | + - 다른 이유로 헤더 파일을 포함해서는 안된다는 말은, 관성, 습관, 혹시 몰라서 등으로 헤더를 넣으면 안된다는 의미입니다. |
| 15 | +- "다른 헤더가 이미 포함하고 있으니까 괜찮겠지"라는 생각은 금지입니다. |
| 16 | + |
| 17 | +### 왜 간접 포함(transitive include)에 의존하면 안 될까? |
| 18 | + |
| 19 | +간접 포함에 의존하면 다음과 같은 문제가 생깁니다: |
| 20 | + |
| 21 | +- A 헤더가 B 헤더를 포함하고, B 헤더가 C 헤더를 포함한다고 해봅시다. |
| 22 | +- 어떤 `.cc` 파일이 C에 있는 심볼을 쓰면서 A만 포함하고, C를 직접 포함하지 않는다면, 구조가 바뀌었을 때 문제가 됩니다. |
| 23 | +- 나중에 B에서 C에 대한 `#include`를 삭제하면, 해당 `.cc` 파일은 갑자기 컴파일이 깨집니다. |
| 24 | + |
| 25 | +즉, **내 파일이 사용하는 심볼이 어디에서 오는지 명확히 하기 위해** 직접 포함이 필요합니다. |
| 26 | + |
| 27 | +### 예시: 나쁜 코드 |
| 28 | + |
| 29 | +```cpp |
| 30 | +// bar.h |
| 31 | +struct Bar { |
| 32 | + int value; |
| 33 | +}; |
| 34 | + |
| 35 | +// foo.h |
| 36 | +#include "bar.h" |
| 37 | + |
| 38 | +void ProcessBar(const Bar& bar); |
| 39 | + |
| 40 | +// foo.cc |
| 41 | +#include "foo.h" // 여기서는 bar.h를 직접 포함하지 않음 |
| 42 | + |
| 43 | +void ProcessBar(const Bar& bar) { |
| 44 | + // Bar를 사용하지만, foo.cc는 bar.h를 직접 포함하지 않음 |
| 45 | +} |
| 46 | +``` |
| 47 | +
|
| 48 | +위 코드에서 `foo.cc`는 `Bar` 타입을 사용하지만, `bar.h`를 직접 포함하지 않습니다. 만약 `foo.h`에서 `#include "bar.h"`를 제거하면, `foo.cc`는 컴파일 오류가 발생합니다. |
| 49 | +
|
| 50 | +### 예시: 좋은 코드 |
| 51 | +
|
| 52 | +```cpp |
| 53 | +// bar.h |
| 54 | +struct Bar { |
| 55 | + int value; |
| 56 | +}; |
| 57 | +
|
| 58 | +// foo.h |
| 59 | +#include "bar.h" |
| 60 | +
|
| 61 | +void ProcessBar(const Bar& bar); |
| 62 | +
|
| 63 | +// foo.cc |
| 64 | +#include "foo.h" |
| 65 | +#include "bar.h" // foo.cc가 직접 bar.h를 포함 |
| 66 | +
|
| 67 | +void ProcessBar(const Bar& bar) { |
| 68 | + // Bar를 안전하게 사용 가능 |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +이렇게 하면 `foo.h`에서 `bar.h`를 포함하는 방식이 바뀌더라도, `foo.cc`는 여전히 `bar.h`를 직접 포함하고 있으므로 안전합니다. |
| 73 | + |
| 74 | +### 관련 헤더에도 동일한 규칙 적용 |
| 75 | + |
| 76 | +- `foo.cc`가 `bar.h`의 심볼을 사용한다면, `foo.h`가 `bar.h`를 포함하고 있더라도 `foo.cc`는 **반드시** `bar.h`를 직접 포함해야 합니다. |
| 77 | +- 이렇게 하면 각 파일이 자신이 필요로 하는 의존성을 명확히 표현하게 되어, 의존 관계가 더 투명해지고 리팩토링도 쉬워집니다. |
| 78 | + |
| 79 | +### 정리 |
| 80 | + |
| 81 | +- **원칙**: 사용하는 것은 직접 포함한다. (Include What You Use) |
| 82 | +- **하지 말 것**: "다른 헤더가 대신 포함해 주겠지"라고 기대하지 말 것. |
| 83 | +- **효과**: |
| 84 | + - 불필요한 `#include`를 제거해도 안전 |
| 85 | + - 의존 관계가 명확해짐 |
| 86 | + - 리팩토링과 빌드 속도 개선에 도움이 됨 |
0 commit comments