주간 팁 #103: 플래그는 전역 변수입니다
작성자: Matt Armstrong
.cc
파일에서 전역 범위로 플래그를 정의하세요. 해당 .h
파일에서는 최대 한 번만 선언하세요.
왜 헤더 파일에 선언할까요?
헤더 파일 사용은 대부분 우리에게 자연스러운 습관이지만, 그 이유를 잊었을 수도 있습니다. 헤더 파일을 사용하는 이유는 다음과 같습니다:
- 헤더 파일에 무언가를 선언하면 다른 곳에서 쉽게
#include
할 수 있습니다. 프로그램 전체가 동일한 선언을 참조합니다. - 동일한 엔티티를 정의하는
.cc
파일에서 해당 헤더 파일을 포함하면, 정의가 선언과 일치하는지 보장할 수 있습니다. - 헤더 파일은 패키지의 공개 API를 문서화하는 역할을 합니다. 패키지의 공개 API 외부의 것을 사용하는 것은 좋지 않습니다.
- 엔티티를 재선언하는 대신 헤더 파일을 포함하면, 도구와 사람이 종속성 분석을 더 쉽게 수행할 수 있습니다.
Abseil 플래그도 다른 전역 변수처럼 취약합니다
링크 타임 에러 없이 잘못된 방법으로 플래그를 정의할 수 있습니다. 먼저, 다음과 같은 코드를 .cc
파일에 작성합니다:
// .cc 파일에서 --my_flag를 정의
ABSL_FLAG(std::string, my_flag, "", "My flag is a string.");
그리고 잘못된 플래그 선언을 다른 .cc
파일(예: 테스트 파일)에 작성합니다:
// 잘못된 선언: 타입이 std::string이어야 함
extern absl::Flag<int64> FLAGS_my_flag;
이 프로그램은 잘못된 형식이며, 발생하는 모든 결과는 정의되지 않은 동작의 결과입니다. 테스트 프로그램에서는 컴파일과 링크는 되었지만, 플래그에 접근할 때 크래시가 발생했습니다.
권장사항
명령줄 플래그를 전역 변수처럼 설계하세요.
- 플래그 사용을 피할 수 있다면 피하세요. 관련 팁을 참조하세요.
- 테스트를 작성하기 쉽게 하려고 플래그를 사용하는 경우, 프로덕션에서 이를 설정할 의도가 없다면 테스트 전용 API를 클래스에 추가하는 것을 고려하세요.
- 플래그를 비공개 정적 변수로 취급하는 것을 고려하세요. 다른 패키지가 플래그에 접근해야 한다면, 이를 함수로 감싸세요.
- 플래그를 네임스페이스 내부가 아닌 전역 범위에서 정의하세요. 이렇게 하면 플래그 이름이 충돌할 경우 링크 에러를 발생시킬 수 있습니다.
- 플래그가 여러 파일에서 참조된다면, 정의된 플래그에 대응하는
.h
파일에 한 번 선언하세요. ABSL_FLAG(type, ...)
매크로를 사용해 플래그를 정의하세요.
결론
플래그는 전역 변수입니다. 신중하게 사용하세요. 플래그를 사용할 때도 다른 전역 변수처럼 정의하고 선언하세요.