콘텐츠로 이동

C# 문자열 보간과 FormattableString

string name = "Alice";
int age = 30;
string msg = $"이름: {name}, 나이: {age}";
// → "이름: Alice, 나이: 30"

중괄호 안에 임의의 C# 표현식을 사용할 수 있습니다.

double price = 9.9;
string s = $"가격: {price:C2}"; // 통화 형식: ₩9.90
string t = $"날짜: {DateTime.Now:yyyy-MM-dd}";
string u = $"결과: {(price > 5 ? "비쌈" : "저렴")}";

$"" 리터럴을 FormattableString으로 받으면 형식 문자열과 인자를 분리해 처리할 수 있습니다.

FormattableString fs = $"Hello, {name}! Age: {age}";
Console.WriteLine(fs.Format); // "Hello, {0}! Age: {1}"
Console.WriteLine(fs.ArgumentCount); // 2
Console.WriteLine(fs.GetArgument(0)); // "Alice"
Console.WriteLine(fs.ToString()); // "Hello, Alice! Age: 30"
// 로케일 지정 출력
string invariant = fs.ToString(System.Globalization.CultureInfo.InvariantCulture);
// 위험: 직접 문자열 연결 → SQL 인젝션
string sql = $"SELECT * FROM users WHERE name = '{userInput}'";
// 안전: FormattableString으로 파라미터화
FormattableString query = $"SELECT * FROM users WHERE name = {userInput}";
DbCommand cmd = CreateSafeCommand(query);
static DbCommand CreateSafeCommand(FormattableString fs) {
var cmd = connection.CreateCommand();
cmd.CommandText = Regex.Replace(fs.Format, @"\{(\d+)\}", "@p$1");
for (int i = 0; i < fs.ArgumentCount; i++)
cmd.Parameters.AddWithValue($"@p{i}", fs.GetArgument(i));
return cmd;
}

.NET 6부터 컴파일러가 보간 문자열을 InterpolatedStringHandler로 변환해 불필요한 중간 문자열 생성을 방지합니다.

// .NET 6+: 조건이 false면 보간 표현식 자체를 평가하지 않음
logger.LogDebug($"계산 결과: {ExpensiveComputation()}");
// Debug 레벨이 비활성화되어 있으면 ExpensiveComputation() 호출 안 됨

커스텀 핸들러 구현:

[InterpolatedStringHandler]
public ref struct LogHandler {
private StringBuilder _sb;
private readonly bool _enabled;
public LogHandler(int literalLength, int formattedCount, bool enabled, out bool shouldAppend) {
_enabled = enabled;
shouldAppend = enabled;
_sb = enabled ? new StringBuilder(literalLength) : default!;
}
public void AppendLiteral(string s) { if (_enabled) _sb.Append(s); }
public void AppendFormatted<T>(T value) { if (_enabled) _sb.Append(value); }
public override string ToString() => _sb?.ToString() ?? "";
}
// C# 11 raw string literal + 보간
var json = $"""
{{
"name": "{name}",
"age": {age}
}}
""";

""" 안에서는 {{, }}이 필요 없고 {, }은 그대로 사용됩니다.

방식특징
string.Format전통적, 박싱 발생
$"" (문자열 보간)컴파일러가 string.Format으로 변환
StringBuilder대량 연결에 유리
InterpolatedStringHandler.NET 6+, 조건부 평가 생략 가능
string.Create할당 최소화, 저수준 제어
  • $"" 보간은 가독성이 좋고 컴파일 타임에 검증됨
  • FormattableString으로 받으면 형식 문자열과 인자를 분리 처리 가능 → SQL/쿼리 파라미터화에 유용
  • .NET 6+ InterpolatedStringHandler로 로깅 등에서 불필요한 문자열 생성 방지
  • C# 11 raw string literal과 결합하면 JSON/XML 템플릿 작성이 편리