호출자 정보 특성은 호출한 위치(메서드명, 파일 경로, 줄 번호, 인수 표현식)를 컴파일 타임에 문자열/숫자로 주입해 주는 기능입니다. 로깅, 디버깅, 가드(검증) 코드에서 특히 유용합니다.
네임스페이스: System.Runtime.CompilerServices
1. 기본 개념과 사용 형태
특성은 선택적 매개변수(optional parameter)에 적용하며, 호출 시 값을 생략하면 컴파일러가 정보를 채워 줍니다.
using System;
using System.Runtime.CompilerServices;
static class Trace
{
public static void Log(
string message,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine($"[{System.IO.Path.GetFileName(file)}:{line} - {member}] {message}");
}
}
class Program
{
static void Main()
{
Trace.Log("시작합니다"); // 파일명, 줄번호, 메서드명이 자동 주입됩니다.
DoWork();
}
static void DoWork()
{
Trace.Log("작업 처리 중");
}
}포인트
- 런타임 반사가 아닌 컴파일 타임 주입이므로 오버헤드가 거의 없습니다.
- 파일 경로는 전체 경로가 들어오므로 민감 정보 노출이 걱정되면 Path.GetFileName 등으로 줄여 출력합니다.
2. INotifyPropertyChanged에서 [CallerMemberName]
속성 변경 알림에 속성명을 직접 문자열로 쓰면 오타에 취약합니다. CallerMemberName으로 안전하게 대체합니다.
using System.ComponentModel;
using System.Runtime.CompilerServices;
class Person : INotifyPropertyChanged
{
private string _name = "";
public string Name
{
get => _name;
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged(); // "Name"을 자동으로 채워줍니다.
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}3. [CallerArgumentExpression]으로 가드 강화
C# 10부터 인수로 전달한 표현식 문자열을 받아올 수 있습니다. 검증 실패 시 어떤 식이 실패했는지 명확히 기록/예외에 담을 수 있습니다.
using System;
using System.Runtime.CompilerServices;
public static class Guard
{
public static T NotNull<T>(T? value,
[CallerArgumentExpression("value")] string? expr = null)
where T : class
{
if (value is null)
throw new ArgumentNullException(paramName: expr);
return value;
}
public static void Require(bool condition,
[CallerArgumentExpression("condition")] string? expr = null)
{
if (!condition)
throw new ArgumentException($"조건 실패: {expr}");
}
}
class Demo
{
static void Run(string? path, int count)
{
path = Guard.NotNull(path); // paramName에 "path"가 들어갑니다.
Guard.Require(count > 0); // 메시지에 "count > 0"이 들어갑니다.
}
}4. 실전 로깅/디버깅 패턴
메서드 진입/종료 추적, 예외 로깅, 성능 측정에서 호출자 정보를 함께 남기면 문제 지점을 빠르게 찾을 수 있습니다.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
static class PerfLog
{
public static IDisposable Scope(
string message,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
=> new ScopeImpl(message, member, file, line);
private sealed class ScopeImpl : IDisposable
{
private readonly string _prefix;
private readonly Stopwatch _sw = Stopwatch.StartNew();
public ScopeImpl(string message, string member, string file, int line)
{
_prefix = $"[{System.IO.Path.GetFileName(file)}:{line} - {member}] ";
Console.WriteLine(_prefix + "ENTER: " + message);
}
public void Dispose()
{
_sw.Stop();
Console.WriteLine(_prefix + $"LEAVE: {_sw.ElapsedMilliseconds} ms");
}
}
}
class Work
{
void Process()
{
using var _ = PerfLog.Scope("데이터 처리");
// 작업...
}
}5. 제약 사항과 주의점
- 적용 대상: 선택적 매개변수에만 의미가 있습니다. 호출 시 해당 인수를 생략해야 컴파일러가 값을 넣어줍니다.
- 타입 제약: MemberName/FilePath/ArgumentExpression은 string, LineNumber는 int 매개변수에 적용합니다.
- 보안/크기: FilePath는 전체 경로가 포함됩니다. 로그에 그대로 남기면 민감 정보가 노출될 수 있으니 가공하세요.
- IL에 상수로 주입: 값이 메서드 호출부마다 문자열 상수로 박히므로 바이너리 크기에 영향을 줄 수 있습니다(보통 미미함).
- 명시적으로 값을 넘기면 특성은 무시됩니다. 필요 시 항상 생략해 자동 주입을 받으세요.
- [CallerArgumentExpression]은 C# 10 이상에서 사용 가능합니다.
6. 요약
Caller Info 특성은 코드 변경에 강하고(리팩터링 친화적), 성능 오버헤드가 거의 없는 로깅/디버깅 도구입니다. 최소한의 코드로 더 많은 컨텍스트를 로그와 예외에 담아 문제를 빠르게 추적해 보세요.
'C#' 카테고리의 다른 글
| C# BenchmarkDotNet으로 성능 측정하기 (0) | 2026.04.22 |
|---|---|
| C# BackgroundWorker로 백그라운드 작업 처리 (0) | 2026.04.22 |
| C# unsafe 코드와 포인터 사용하기 (0) | 2026.04.21 |
| C# 구조체(Struct) 심층 분석 (1) | 2026.04.21 |
| C# 메모리 누수 방지를 위한 약한 참조(WeakReference) 사용 (1) | 2026.04.20 |