C#에서 문자열은 불변(Immutable)입니다
C#에서 string 타입은 불변(immutable)입니다. 즉, 문자열이 한 번 생성되면 내용을 직접 수정할 수 없습니다. 문자열에 어떤 변경을 가하면 항상 변경된 내용을 담은 새로운 문자열 객체가 반환되며, 원본 문자열은 그대로 유지됩니다.
string word = "aaabbbccc";
string newWord = word.Replace('b', 'd');
Console.WriteLine(word); // aaabbbccc 출력
Console.WriteLine(newWord); // aaadddccc 출력위 예제에서 Replace 메서드는 원본인 word를 전혀 건드리지 않고, 'b'를 'd'로 바꾼 새 문자열만 반환합니다.
StringBuilder는 가변(Mutable) 문자열입니다
StringBuilder 클래스는 내용을 자유롭게 수정할 수 있는, 즉 가변(mutable) 문자열을 표현합니다. 불변 문자열을 다루는 string 타입과 달리 내부적으로 완전히 다른 방식으로 구현되어 있습니다.
문자열 객체를 수정할 때마다 새 복사본이 만들어지기 때문에, 반복문 안에서 문자열을 계속 수정하면 성능 저하가 발생할 수 있습니다. 몇 번 정도의 반복이라면 차이가 미미하지만, 대규모 루프에서는 상당한 부담이 됩니다. StringBuilder는 문자를 추가(Append), 삭제(Remove), 교체(Replace), 삽입(Insert)하는 작업을 효율적으로 처리할 수 있는 대안을 제공합니다.
내부 버퍼 동작 원리
StringBuilder는 문자를 담아두기 위한 내부 버퍼(internal buffer)를 유지합니다. 버퍼에 여유 공간이 있으면 새 데이터를 그대로 추가하고, 공간이 부족하면 더 큰 새 버퍼를 할당한 뒤 기존 데이터를 복사하고 새 데이터를 덧붙입니다. 이런 방식 덕분에 매번 새 객체를 생성하는 string보다 훨씬 효율적입니다.
var sb = new StringBuilder();
for (int i = 0; i < 10; i++)
{
sb.Append("a");
}
Console.WriteLine(sb.ToString()); // aaaaaaaaaa 출력StringBuilder 객체 생성 방법
StringBuilder는 다양한 생성자 오버로드를 제공합니다.
// 기본 생성 var sb1 = new StringBuilder(); // 초기 용량(capacity)을 지정하여 생성 var sb2 = new StringBuilder(capacity: 10); // 초기 문자열을 지정하여 생성 var sb3 = new StringBuilder(value: "Hello World"); // 초기 용량과 최대 용량(maxCapacity)을 지정하여 생성 var sb4 = new StringBuilder(capacity: 20, maxCapacity: 50); // 초기 문자열과 용량을 함께 지정하여 생성 var sb5 = new StringBuilder(value: "Hello", capacity: 20); // 부분 문자열(substring)과 용량을 지정하여 생성 var sb6 = new StringBuilder(value: "Hello World", startIndex: 0, length: 5, capacity: 20);
Length, Capacity, MaxCapacity의 이해
StringBuilder에는 현재 저장된 문자 수를 나타내는 Length 속성이 있습니다. 문자를 계속 추가하면 Length가 늘어나다가, 현재 담을 수 있는 최대 문자 수를 의미하는 Capacity(용량)에 도달하게 됩니다.
추가된 문자로 인해 길이가 현재 용량을 초과하면, 클래스는 새 메모리를 할당해 용량을 두 배로 늘립니다. 이후 새 문자가 객체에 추가되고 Length 속성도 갱신됩니다.
StringBuilder는 MaxCapacity 속성 값에 도달할 때까지 메모리를 동적으로 계속 확장합니다. MaxCapacity에 도달한 후에는 더 이상 메모리를 할당할 수 없으며, 그 이상 데이터를 추가하려 하면 ArgumentOutOfRangeException 또는 OutOfMemoryException 예외가 발생합니다.
주요 메서드 정리
StringBuilder는 문자열 수정 작업을 손쉽게 해주는 다음과 같은 메서드들을 제공합니다.
- Append — 지정된 객체의 문자열 표현을 현재 인스턴스 끝에 추가합니다.
- AppendFormat — 복합 형식 문자열(composite format string)을 처리한 결과를 현재 인스턴스에 추가합니다. 각 형식 항목은 해당 개체 인수의 문자열 표현으로 대체됩니다.
- AppendJoin — 제공된 객체 배열 요소들의 문자열 표현을 지정된 구분자로 연결한 뒤, 그 결과를 현재 인스턴스에 추가합니다.
- AppendLine — 현재 StringBuilder 객체 끝에 기본 줄 종결자(line terminator)를 추가합니다.
- Clear — 현재 StringBuilder 인스턴스에서 모든 문자를 제거합니다.
- CopyTo — 문자들을 대상 char 범위(span) 또는 char 배열로 복사합니다.
- EnsureCapacity — 이 인스턴스의 용량이 최소한 지정된 값 이상이 되도록 보장합니다.
- Equals — 이 인스턴스와 제공된 인스턴스의 문자열 값, Capacity, MaxCapacity가 모두 같으면 true를 반환합니다.
- GetChunks — 문자 청크(chunk)들을 순회(iterate)할 수 있는 객체를 반환합니다.
- Insert — 지정된 객체의 문자열 표현을 이 인스턴스의 지정된 문자 위치에 삽입합니다.
- Remove — 이 인스턴스에서 지정된 범위의 문자를 제거합니다.
- Replace — 이 인스턴스에서 지정된 문자 또는 문자열이 나타나는 모든 부분을 다른 문자나 문자열로 교체합니다.
- ToString — 현재 인스턴스를 string으로 변환합니다.
종합 예제
using System;
using System.Text;
class Program
{
static void Main(string[] args)
{
// string은 불변임을 확인
string word = "aaabbbccc";
string newWord = word.Replace('b', 'd');
Console.WriteLine(word); // aaabbbccc
Console.WriteLine(newWord); // aaadddccc
// StringBuilder로 문자 반복 추가
var sb = new StringBuilder();
for (int i = 0; i < 10; i++)
{
sb.Append("a");
}
Console.WriteLine(sb.ToString()); // aaaaaaaaaa
// 다양한 생성자 활용
var sb1 = new StringBuilder();
var sb2 = new StringBuilder(capacity: 10);
var sb3 = new StringBuilder(value: "Hello World");
var sb4 = new StringBuilder(capacity: 20, maxCapacity: 50);
var sb5 = new StringBuilder(value: "Hello", capacity: 20);
var sb6 = new StringBuilder(value: "Hello World", startIndex: 0, length: 5, capacity: 20);
}
}실행 결과
aaabbbccc aaadddccc aaaaaaaaaa
마무리
정리하자면, C#의 string은 불변이므로 반복적인 문자열 수정 작업에서는 매번 새 객체가 생성되어 성능이 떨어질 수 있습니다. 반면 StringBuilder는 내부 버퍼를 활용해 문자열을 효율적으로 추가·수정할 수 있는 가변 문자열 클래스입니다. 특히 루프 안에서 문자열을 조립하거나 대량의 텍스트를 처리할 때 StringBuilder를 사용하면 눈에 띄는 성능 향상을 기대할 수 있습니다.