|

자바 String, StringBuilder, StringBuffer 차이: 문자열 연결에서 무엇을 써야 할까

자바 String, StringBuilder, StringBuffer 차이 대표 이미지
값인지, 반복 조립인지, 공유 버퍼인지 기준으로 구분한다

자바 String, StringBuilder, StringBuffer 차이는 자바 입문자도 자주 만나고, 실무에서도 계속 다시 나오게 되는 주제입니다. 실전에서는 문자열 값 자체를 다루는지, 반복해서 조립하는지, 같은 버퍼를 여러 스레드가 공유하는지를 먼저 보면 판단이 훨씬 쉬워집니다.

이번 글에서는 immutable, 반복 연결, 성능 감각, thread safety, 그리고 StringBuffer가 정말 필요한 순간까지 쉬운 코드 예시로 정리하겠습니다. 짧은 규칙만 외우는 대신 실제 문자열 연결 상황에서 무엇을 써야 할지 감을 잡는 데 초점을 맞추겠습니다.


자바 String, StringBuilder, StringBuffer 차이

  • 기본 문자열 값은 <code>String</code>으로 두는 것이 가장 자연스럽다
  • 반복해서 문자열을 이어 붙일 때는 <code>StringBuilder</code>가 잘 맞는다
  • 같은 mutable 버퍼를 여러 스레드가 함께 바꿔야 할 때만 <code>StringBuffer</code>를 검토하면 된다

한 줄로 줄이면 값은 String, 조립은 StringBuilder, 공유 버퍼 동기화는 StringBuffer입니다.

자바 String과 StringBuilder 차이를 설명한 카드 이미지
비교 카드 1/2: String과 StringBuilder의 역할 차이
자바 StringBuffer와 선택 기준을 설명한 카드 이미지
비교 카드 2/2: StringBuffer와 실전 선택 기준

String

String은 자바의 기본 문자열 타입입니다. 가장 중요한 성질은 immutable이라는 점입니다. 문자열을 바꾸는 것처럼 보여도 기존 값을 직접 고치는 것이 아니라 새 문자열 결과를 만든다고 이해하면 됩니다.

String name = "java";
name = name + " blog";

System.out.println(name); // java blog

이 특성 덕분에 String은 값처럼 다루기 편합니다. 메서드 인자, 반환값, 필드 타입, 로그 메시지 같은 대부분의 평범한 문자열은 String이면 충분합니다.


StringBuilder

StringBuilder는 mutable 문자열 조립 도구입니다. append()로 같은 버퍼를 재사용하면서 문자열을 이어 갈 수 있어서, 반복 연결에 잘 맞습니다.

StringBuilder sb = new StringBuilder();
sb.append("java");
sb.append(" ");
sb.append("blog");

String result = sb.toString();
System.out.println(result); // java blog
  • 반복문 안에서 문자열을 계속 붙일 때
  • CSV, SQL, 로그 한 줄, HTML 조각처럼 결과를 조립할 때
  • 조건에 따라 문장을 조금씩 이어 붙일 때

실무에서는 반복 연결이 보이면 먼저 StringBuilder를 떠올리는 습관이 가장 실용적입니다.


StringBuffer

StringBuffer도 mutable이라서 역할 자체는 StringBuilder와 비슷합니다. 차이는 thread-safe라는 점입니다. Oracle 문서 기준으로 필요한 메서드들이 synchronized 되어 있어서, 같은 버퍼를 여러 스레드가 다룰 때 안전성을 제공하도록 설계되었습니다.

StringBuffer buffer = new StringBuffer();
buffer.append("A");
buffer.append("B");

String result = buffer.toString();

그래서 일반적인 새 코드에서는 단일 스레드 조립용으로 StringBuilder를 더 자주 씁니다. StringBuffer는 공유 mutable buffer가 실제로 있을 때 의미가 더 분명합니다.


성능 감각

이 주제는 과장해서 외우기 쉽습니다. 하지만 String은 무조건 느리고 StringBuilder는 무조건 빠르다고 단정하면 오히려 실전 감각이 흐려집니다.

한두 번 연결

String message = "Hello, " + userName + "!";

이 정도는 가독성이 좋고 충분히 자연스럽습니다. Oracle String 문서도 + 구현이 JDK 버전에 따라 StringBuilder, StringBuffer, StringConcatFactory 등으로 달라질 수 있다고 설명합니다. 즉, +를 봤다고 바로 비효율이라고 단정하면 안 됩니다.

반복 연결

String result = "";
for (int i = 0; i < 5; i++) {
    result += i;
}

이 코드는 반복할 때마다 새 문자열 결과를 만들어야 할 수 있어서, 반복 횟수가 늘어날수록 비용이 쌓이기 쉽습니다.

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
    sb.append(i);
}
String result = sb.toString();

그래서 짧고 단순한 연결은 String+도 괜찮고, 반복해서 이어 붙이면 StringBuilder가 보통 더 적합하다고 기억하면 실전에서 충분합니다.


스레드 안전성

StringStringBuffer는 둘 다 멀티스레드 문맥에서 자주 언급되지만, 안전한 이유가 다릅니다.

  • <code>String</code>은 immutable이라 값이 중간에 바뀌지 않는다
  • <code>StringBuffer</code>는 mutable이지만 synchronized 메서드로 접근을 제어한다
  • <code>StringBuilder</code>는 mutable이지만 synchronization 보장이 없다

즉, 바뀌지 않아서 공유하기 편한 것바뀌지만 동기화해서 다루는 것은 완전히 다른 이야기입니다.


언제 무엇을 쓸까

제목, 사용자 이름, URL, 상태 메시지처럼 완성된 문자열 값이라면 대부분 String이면 충분합니다.

조립

반복문, 조건문, 템플릿 조립처럼 문자열을 조금씩 만들어 가는 중이라면 StringBuilder가 더 자연스럽습니다.

공유 버퍼

같은 mutable 문자열 버퍼 인스턴스를 여러 스레드가 함께 바꿔야 한다면 StringBuffer를 검토할 수 있습니다. 하지만 이 장면은 생각보다 흔하지 않습니다.


StringBuffer가 필요한 순간

여기서 가장 흔한 오해는 멀티스레드 프로그램이면 무조건 StringBuffer라는 생각입니다. 중요한 질문은 프로그램 전체가 멀티스레드인지가 아니라, 같은 버퍼 인스턴스를 여러 스레드가 동시에 바꾸는지입니다.

예를 들어 웹 서버가 멀티스레드로 동작하더라도, 요청 하나를 처리하는 메서드 안에서 로컬 변수로 StringBuilder를 쓰는 것은 보통 문제되지 않습니다. 그 버퍼를 여러 스레드가 공유하지 않기 때문입니다.

즉, StringBuffer가 필요한 것은 멀티스레드 앱 자체가 아니라, 공유 mutable buffer가 실제로 존재하는 장면입니다.


자주 하는 오해

  • <code>+</code>는 항상 나쁘다 → 아니다. 짧은 한 줄 연결은 가독성이 좋다
  • <code>StringBuilder</code>가 무조건 최고다 → 아니다. 완성된 문자열 값의 기본 타입은 여전히 <code>String</code>이다
  • 멀티스레드면 무조건 <code>StringBuffer</code>다 → 아니다. 공유 버퍼가 없으면 굳이 고를 이유가 약하다
  • <code>StringBuffer</code>는 완전히 쓸모없다 → 아니다. 레거시 코드와 실제 공유 버퍼 상황에서는 여전히 의미가 있다

정리

자바 문자열 선택 기준은 생각보다 단순합니다. 기본 문자열 값은 String, 반복 연결은 StringBuilder, 공유 mutable buffer의 동기화가 꼭 필요하면 StringBuffer입니다.

그리고 가장 중요한 한 줄은 이것입니다. 문자열 연결에서 중요한 것은 클래스 이름 암기가 아니라, 값인지 조립 과정인지, 그리고 공유되는지 보는 판단 기준입니다.

관련해서 자바 문자열 비교가 헷갈린다면 자바 ==와 equals 차이 글도 같이 보면 좋습니다. 메서드 동작을 구분하는 감각까지 함께 보고 싶다면 자바 오버로딩과 오버라이딩 차이 글도 이어서 읽어보면 도움이 됩니다.

공식 문서는 Oracle Java SE 21 String API, StringBuilder API, StringBuffer API를 참고했습니다.

함께보면 좋은 글