포스트

전처리기 지시문과 Debug.Log를 위한 WrapperClass

전처리기 지시문 (Preprocessor Directives)이란?

  • 컴파일 과정에서 소스 코드의 일부를 조건부로 포함하거나 제외하도록 지시하는 명령어.

전처리기 지시문은 컴파일러에 의해 처리되기 전, 이 시점을 전처리 단계라고 하는데, 이 단계에서 실행되는 문장이다.

소스 코드가 어떻게 컴파일될 지를 동적으로 제어할 수 있다고 한다.

주로 C, C++, C#에서 사용된다고 한다.

사용할 때에는 # 으로 시작하며, 한 줄에 한 명령을 쓰고, 끝에 ;을 쓰지 않는다는 특징이 있다.

주요 기능

조건부 컴파일

  • 코드의 특정 부분을 조건에 따라 포함시키거나 제외할 수 있다.
  • 이를 통해 다양한 플랫폼이나 컴파일 옵션에 맞게 코드를 작성할 수 있겠다.

    매크로 정의와 사용

  • 코드 내에서 반복적으로 사용되는 값, 혹은 코드 조각을 매크로로 지정하고 사용할 수 있다.
  • 코드의 가독성을 높이고, 유지보수가 쉬워질 수 있다.

    파일 포함

  • 다른 파일의 내용을 현재 파일에 포함시키는 기능
  • 코드의 모듈화와 재사용성을 높이는 데 유용

    오류 및 경고 메세지

  • 컴파일 시점에 특정 조건을 확인하고, 조건에 따라 컴파일 오류나 경고를 발생시킬 수 있다.

주요 전처리기 지시문

#deifne / #undef

  • 매크로 정의와 해제 이 지시문은 C언어를 처음 접할 때, 그리고 학과에서 C++로 Graphics를 배울 때 정말 지루할정도로 자주 만났었다. 주로 소스코드의 제일 위에서 특별하게 쓰이는 상수들, OpenGL 프로그램을 짤 때면 가로세로 해상도와 같은 숫자를 매크로로 지정하기 위해 자주 사용했던걸로 기억한다.

#if / #else / #elif / #endif

  • 조건에 따라 코드를 포함 혹은 제외.

이 지시문은 특히, 다른 사람들이 만든 프로젝트의 소스를 볼 때면 심심치 않게 볼 수 있었다. 조건문을 활용해 특정 코드가 동작하게 할 수도 있고, 반대로도 할 수 있기 때문에, 플랫폼 별 빌드나 디버깅/릴리즈 빌드를 따로 구별하지 않고도 코드가 다르게 동작할 수 있게하는 유용한 녀석이다. 당연히 #if 뒤에는 && 혹은 || 와 같은 부울 연산자가 사용될 수 있다.

#include

  • 다른 파일의 내용을 현재 파일에 포함.

이 지시문은 처음 C언어에 발을 들인 중학생때부터 정말 질리도록 많이 본 지시문이다. C++를 공부하는 최근에도 어김없이 많이 보고있다. 스킵한다.

#error / #warning

  • 컴파일 시 오류 혹은 경고 메세지를 포함시킴.

이 지시문은 개발자에게 중요한 정보를 알리는데 사용된다고 한다. 개발자라면 당연히 혼자 일하는 경우보다 다른 개발자와 함께 협업하는 경우가 더 많을 것이다. 수 많은 소스코드가 포함된 프로젝트에서, 실수로라도 포함시키면 안될 코드를 빌드에 포함시키면 당연히 큰일이 날 것이다. 그러한 사태를 방지하기 위해 만든 지시문인 것 같다. 앞으로의 협업에 용이하게 사용할 수 있을것 같다. 미완성인 클래스나 소스코드에 포함하여 혹시모를 오류를 방지할 수 있도록 해보자.

#region / #endregion

  • 코드를 논리적인 영역으로 구분하는 지시문.
  • 소스코드의 가독성을 위해 주로 사용된다.

이 코드는 일전에 우연히 어디서 주워들은 후, 지금까지도 용이하게 사용하고 있다. 중요한 클래스가 길어질때, 그 클래스안에는 변수가 산더미, 그리고 메서드가 산더미일 것이다. 변수와 메서드를 용도별로, 혹은 비슷한 계열 별로 묶어서 코드를 열람할 때 좀 더 찾기 편하게 할 수 있으니, 혹시라도 이제알았다면, 당장 사용해보자.

#pragma

  • 컴파일러에 특정 동작을 지시. 컴파일러마다 지원하는 #pragma 지시문이 다를 수 있음.

이 지시어는 위의 다른 지시어와는 달리, 컴파일러에 종속적이라고 한다. 그래서 컴파일러마다 지원되는 #pragma가 다르다고 한다. 이에 대해서는 추가적으로 더 지식이 보충되었을 때, 포스팅하도록 하겠다.

Debug.Log를 위한 WrapperClass

최근들어 최적화에 관심이 부쩍 늘었다. 일전에 만들었던 프로젝트들이 빌드 후에는 에디터와 큰 성능차이를 보여주었던 탓일까. 그래서 이번에 알아본 전처리기 지시문을 활용해 최적화를 돕는 클래스를 하나 만들어보려고 한다.

Debug.Log() 함수는 유니티를 시작해본 사람이라면 모두가 안다. 콘솔에 무언가를 출력하는 함수로 그 클래스와 메서드의 이름과 같이, 많은 개발자들이 개발의 중간단계에 이를 정말 많이 활용한다.

그러나, 이 메서드가 상당히 비용이 높은 메서드라는 것은 적지않은 사람들이 간과하고 있는 것 같다. 나도 비교적 최근에 알았다.

이 함수를 소스코드에 작성하고 빌드하면, 당연하게도 빌드에 포함될 수 있다. 만약, 일반 유저들의 로컬에서 작동할 클라이언트에서 이 소스가 그대로 동작한다면, 어디에도 출력되지 않는, 아무도 그것을 보는 걸 원치않는 기능을 위해 컴퓨터 성능을 사용해야 한다. 아주아주 작다고 한들 이는 손해가 아닌가? 그런데도 불구하고 릴리즈 빌드에서도 이를 깜박하고 그대로 빌드해버렸던 나 자신은 반성할 필요가 있다.

하지만 이제는 너무 손에 익어버렸다. 애자일 방법론인지, 꼼꼼한 성격탓인지, 기능을 만들어도 하나만들고 디버그, 하나만들고 디버그를 반복하는 나에게는 이제 너무 손에 익어버렸다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using UnityEngine;  
  
public static class DebugEx  
{  
    public enum LogLevel  
    {  
        None,  
        Error,  
        Warning,  
        All  
    }  
  
    public static LogLevel CurrentLogLevel = LogLevel.All;  
  
    public static void Log(object message, Object context = null)  
    {
#if UNITY_EDITOR  
        if (CurrentLogLevel >= LogLevel.All)  
        {
	        Debug.Log(message, context);  
        }
#endif  
    }  
  
    public static void LogWarning(object message, Object context = null)  
    {
#if UNITY_EDITOR  
        if (CurrentLogLevel >= LogLevel.Warning)  
        {
			Debug.LogWarning(message, context);  
        }
#endif  
    }  
  
    public static void LogError(object message, Object context = null)  
    {
#if UNITY_EDITOR 
        if (CurrentLogLevel >= LogLevel.Error)  
        {
			Debug.LogError(message, context);  
        }
#endif 
    }  
}

그런 나를 위해, 혹은 나와 비슷하게 고민할 누군가를 위해 간단하지만 DebugEx라는 이름의 WrapperClass를 만들어보았다.

DebugEx.CurrentLogLevel 을 조절하여 콘솔에 어떤 로그까지만 출력시킬지도 조절할 수 있다.

Debug.Log() 말고도 Debug.LogWarning()Debug.LogError()를 간간히 사용하는 나를 위해 위와 같이 만들었다.

이제 최적화를 걱정하는 나의 마음과 함께, 빌드 파일도 한결 가벼워 질 것이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.