C# 어트리뷰트(Attribute) 마스터하기: 메타데이터로 코드 강화하기
어트리뷰트(Attribute)는 클래스, 메서드, 구조체, 열거자, 어셈블리 등 프로그램 요소의 동작에 대한 정보를 런타임에 전달하는 선언적 태그(Declarative Tag)입니다. 대괄호 [ ] 안에 요소 위에 배치하여 사용합니다.
어트리뷰트는 컴파일러 지시사항, 설명, 메서드/클래스 정보 등 메타데이터(Metadata)를 프로그램에 추가하는 데 사용됩니다. .NET Framework는 미리 정의된(Predefined) 어트리뷰트와 사용자 정의(Custom) 어트리뷰트 두 가지를 제공합니다.
어트리뷰트 지정 구문
[어트리뷰트(위치_매개변수, 이름_매개변수 = 값, ...)] 대상_요소
- 어트리뷰트 이름과 값은 대상 요소 앞 대괄호 안에 지정
- 위치 매개변수: 필수 정보
- 이름 매개변수: 선택적 정보
미리 정의된 어트리뷰트 (Predefined Attributes)
.NET Framework가 제공하는 세 가지 핵심 미리 정의 어트리뷰트:
AttributeUsage
Conditional
Obsolete
1. AttributeUsage
사용자 정의 어트리뷰트 클래스가 어떻게 사용될 수 있는지 설명합니다. 적용 가능한 언어 요소 타입 지정.
[AttributeUsage(
validOn, // 적용 대상 (AttributeTargets 열거자 조합)
AllowMultiple = bool, // 다중 적용 허용 여부 (기본 false)
Inherited = bool // 파생 클래스에 상속 여부 (기본 false)
)]
validOn의 주요 AttributeTargets 값: All, Class, Struct, Method, Property, Field, Constructor, Parameter, Assembly, Module, Interface, Enum, Delegate, ReturnValue, GenericParameter 등.
예제
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true
)]
public class MyCustomAttribute : Attribute { }
2. Conditional
지정된 전처리 심볼에 따라 메서드 호출을 조건부 컴파일합니다. 주로 DEBUG나 TRACE 심볼과 함께 디버깅용 메서드에 사용.
[Conditional("심볼_이름")]
예제
#define DEBUG
using System;
using System.Diagnostics;
public class MyClass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void Function1()
{
MyClass.Message("In Function 1.");
Function2();
}
static void Function2()
{
MyClass.Message("In Function 2.");
}
public static void Main()
{
MyClass.Message("In Main function.");
Function1();
Console.ReadKey();
}
}
출력 (DEBUG 정의 시): In Main function / In Function 1 / In Function 2
출력 (DEBUG 미정의 시): (아무 출력 없음 - 메서드 호출 자체가 컴파일에서 제거됨)
핵심: Conditional 메서드는 void 반환 타입만 가능하며, 인터페이스 메서드나 오버라이드 메서드에는 적용 불가.
3. Obsolete
사용하지 말아야 할 프로그램 요소에 표시. 컴파일러가 경고 또는 에러 발생.
[Obsolete("메시지")]
[Obsolete("메시지", isError)]
message: 사용 중단 이유와 대체할 것 설명
isError: true면 컴파일 에러, false면 경고 (기본값)
예제
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod(); // 컴파일 에러 발생
}
}
컴파일 에러: Don't use OldMethod, use NewMethod instead
사용자 정의 어트리뷰트 (Custom Attributes) 만들기
.NET Framework는 선언적 정보를 저장하고 런타임에 검색할 수 있는 사용자 정의 어트리뷰트 생성을 허용합니다.
4단계 과정
- 사용자 정의 어트리뷰트 선언
- 사용자 정의 어트리뷰트 생성자 구현
- 대상 프로그램 요소에 어트리뷰트 적용
- 리플렉션(Reflection)으로 런타임에 어트리뷰트 접근
1단계: 선언
System.Attribute 클래스에서 파생.
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true
)]
public class DeBugInfo : System.Attribute { }
2단계: 생성자 및 속성 구현
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string Message { get; set; } // 명명된 매개변수
// 위치 매개변수 (생성자 필수)
public DeBugInfo(int bg, string dev, string d)
{
bugNo = bg;
developer = dev;
lastReview = d;
}
public int BugNo => bugNo;
public string Developer => developer;
public string LastReview => lastReview;
}
3단계: 적용
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
protected double length, width;
public Rectangle(double l, double w) { length = l; width = w; }
[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
public double GetArea() { return length * width; }
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
4단계: 리플렉션으로 읽기 (다음 장에서 상세)
Type type = typeof(Rectangle);
foreach (object attr in type.GetCustomAttributes(false))
{
DeBugInfo dbi = (DeBugInfo)attr;
Console.WriteLine($"Bug: {dbi.BugNo}, Dev: {dbi.Developer}, Review: {dbi.LastReview}, Msg: {dbi.Message}");
}
자주 쓰는 내장 어트리뷰트
| 어트리뷰트 | 용도 |
[Serializable] | 직렬화 가능 표시 |
[NonSerialized] | 필드 직렬화 제외 |
[DllImport] | 비관리 DLL 함수 임포트 |
[AttributeUsage] | 커스텀 어트리뷰트 사용 규칙 정의 |
[Conditional] | 조건부 메서드 컴파일 |
[Obsolete] | 사용 중단 경고/에러 |
[CallerMemberName] | 호출자 멤버 이름 자동 전달 (C# 5.0+) |
[CallerFilePath] | 호출자 파일 경로 자동 전달 |
[CallerLineNumber] | 호출자 줄 번호 자동 전달 |
모범 사례: 어트리뷰트는 메타데이터용으로만 사용하고, 비즈니스 로직에는 사용하지 마세요. 리플렉션으로 읽을 때 성능 비용이 있으므로 자주 접근하는 경로에서는 캐싱을 고려하세요.