C#시작과 문법

StringBuilder

string이 불변이라 루프에서 += 연결이 비효율적인 이유, StringBuilder의 내부 버퍼 동작, Append/Insert/Remove/Replace 메서드 패턴을 정리합니다.

마지막 수정 2026년 3월 25일

기본 패턴

csharp
var sb = new StringBuilder(256);  // 초기 capacity 지정

sb.Append("이름: ").AppendLine("Mina");
sb.AppendFormat("점수: {0:N0}점", 1200);
sb.Insert(0, "=== 결과 ===\n");
sb.Replace("Mina", "민아");

string result = sb.ToString();

설명

string 불변성과 += 연결의 비용

C#에서 string불변(immutable) 타입입니다. +=로 문자열을 연결할 때마다 기존 문자열을 수정하는 것이 아니라 새로운 string 객체를 힙에 할당하고 두 문자열을 복사합니다. 루프 안에서 이 작업을 반복하면 복사량이 이전 단계의 길이만큼 누적되어 O(n²) 비용이 발생합니다.

csharp
// ❌ 루프마다 새 string 객체 생성 → O(n²) 복사 비용
string result = "";
for (int i = 0; i < 1000; i++)
    result += $"item{i},";

// ✅ StringBuilder — 내부 버퍼 재사용 → O(n)
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
    sb.Append("item").Append(i).Append(',');
string result = sb.ToString();

StringBuilder 내부 동작 — 가변 버퍼와 capacity

StringBuilder는 내부에 가변 char 배열(버퍼) 을 유지합니다. 기본 capacity는 16자이며, 버퍼가 가득 차면 두 배 크기로 자동 확장합니다. 예상 크기를 미리 알고 있다면 생성 시 capacity를 지정해 재할당 횟수를 최소화할 수 있습니다.

csharp
var sb1 = new StringBuilder();         // capacity = 16 (기본값)
var sb2 = new StringBuilder(512);      // capacity = 512 (재할당 최소화)
var sb3 = new StringBuilder("초기값", 128); // 초기 문자열 + capacity

Console.WriteLine(sb2.Capacity);   // 512
Console.WriteLine(sb2.Length);     // 0 (현재 문자 수)

주요 메서드

AppendAppendLine 은 가장 자주 쓰는 메서드로, 각각 문자열과 줄바꿈을 포함한 문자열을 버퍼 끝에 추가합니다. AppendFormatstring.Format과 동일한 서식 지정자를 지원합니다. Insert 는 지정 위치에, Remove 는 지정 범위를 삭제하며, Replace 는 버퍼 전체에서 특정 문자열을 치환합니다. Clear 는 버퍼를 비우면서 인스턴스를 재사용할 수 있게 합니다.

csharp
var sb = new StringBuilder();

sb.Append("Hello");                    // "Hello"
sb.AppendLine(", World!");             // "Hello, World!\n"
sb.AppendFormat("값: {0:N0}", 1200);   // 서식 지정 추가

sb.Insert(0, "▶ ");                    // 맨 앞에 삽입
sb.Remove(0, 2);                       // 인덱스 0부터 2자 삭제
sb.Replace("World", "C#");            // 전체 치환

sb.Clear();                            // 버퍼 초기화 (인스턴스 재사용)

메서드 대부분이 StringBuilder 자신을 반환하므로 체이닝이 가능합니다.

csharp
string result = new StringBuilder()
    .Append("이름: ")
    .AppendLine("민아")
    .AppendFormat("점수: {0}점", 1200)
    .ToString();

ToString() — 최종 string 변환

StringBuilder의 버퍼를 실제 string으로 변환하는 것은 ToString() 한 번만 호출합니다. 중간에 반복 호출하면 매번 새 string이 생성되어 StringBuilder를 쓰는 이점이 줄어듭니다.

csharp
var sb = new StringBuilder();
// ... 여러 Append 작업 ...

string final = sb.ToString();   // ✅ 마지막에 한 번만 호출

언제 StringBuilder를 써야 하나

루프 안에서 수십 개 이상의 문자열을 조립하거나, 크기가 예측하기 어려운 대용량 텍스트를 생성하거나, 동적으로 SQL·HTML 같은 쿼리/마크업을 빌드할 때 효과적입니다. 반대로 단순히 두세 개를 이어 붙이는 경우라면 **문자열 보간($"")**이 더 간결하고 컴파일러가 최적화해 줍니다.

빠른 정리

항목stringStringBuilder
가변성불변 (immutable)가변 (mutable)
+= 연결 비용매번 새 객체 할당 (O(n²))내부 버퍼 확장 (O(n))
메모리 할당연결마다 힙 할당재할당 최소화
적합한 상황소수 연결, 리터럴 값루프 조립, 대용량 텍스트
최종 변환이미 stringToString() 한 번 호출
재사용불가 (새 변수 필요)Clear() 후 재사용 가능

주의할 점

단순히 두세 개를 이어 붙이는 경우에는 **문자열 보간($"")**이 더 읽기 쉽고 컴파일러가 최적화합니다. StringBuilder가 진짜 효과를 발휘하는 것은 루프 안에서 수십 개 이상을 조립하거나 크기가 클 때입니다.

Clear() 후 인스턴스를 재사용하면 새 StringBuilder를 매번 생성하는 비용을 아낄 수 있습니다. 예상 크기를 알고 있다면 new StringBuilder(capacity)로 초기 용량을 지정해 내부 버퍼 재할당 횟수를 줄이세요.

참고 링크

1 sources