TECH/웹개발

[Java] String vs StringBuilder vs StringBuffer: 아직도 헷갈린다면 이 글 하나로 끝내세요

런코리치 2026. 3. 9. 08:00

자바에서 문자열을 다루는 클래스는 세 가지나 됩니다. "셋 다 문자열을 다루는 건데 왜 굳이 나눠놨을까?"라는 의문이 드신다면, 여러분은 이미 좋은 개발자가 될 자질이 충분합니다.

오늘은 이 세 클래스의 결정적인 차이점을 가변성(Mutability)과 동기화(Synchronization)라는 키워드로 완벽히 정리해 드립니다.


1. 한눈에 보는 비교표

특성 String StringBuffer StringBuilder
가변성 불변(Immutable) 가변(Mutable) 가변(Mutable)
동기화 해당 없음 지원 (Thread-Safe) 미지원 (Fast)
속도 느림 (잦은 수정 시) 보통 매우 빠름
메모리 상수 풀 활용 힙(Heap) 영역 활용 힙(Heap) 영역 활용

2. String: 왜 '불변(Immutable)'인가?

String은 한 번 생성되면 그 값을 변경할 수 없습니다. 우리가 문자열을 더할 때(+) 일어나는 일은 기존 문자열이 바뀌는 게 아니라, 새로운 String 객체가 생성되는 것입니다.

Java
String str = "Run";
str += "Code"; // "Run"이 바뀌는 게 아니라, "RunCode"라는 새 객체가 생성됨
str += "Rich"; // 또 "RunCodeRich"라는 새 객체가 생성됨
  • 문제점: 반복문 안에서 문자열을 더하면, 사용되지 않는 쓰레기 객체(Garbage)가 메모리에 계속 쌓여 성능이 급격히 저하됩니다.
  • 장점: 값이 변하지 않기 때문에 멀티 스레드 환경에서 안전하며, String Constant Pool을 통해 동일한 문자열을 재사용함으로써 메모리를 절약합니다.

3. StringBuilder vs StringBuffer: '가변(Mutable)'의 힘

문자열을 빈번하게 수정해야 한다면 StringBuilder나 StringBuffer를 써야 합니다. 이들은 내부에 버퍼(Buffer)라는 메모리 공간을 가지고 있어, 새로운 객체를 만들지 않고 그 안에서 문자를 추가하거나 삭제합니다.

💡 여기서 갈리는 결정적 차이: 동기화(Thread-Safety)

  • StringBuffer: 메서드마다 synchronized 키워드가 붙어 있습니다. 여러 스레드가 동시에 이 객체에 접근해도 안전하게 처리됩니다. (멀티 스레드 환경용)
  • StringBuilder: 동기화를 고려하지 않습니다. 덕분에 StringBuffer보다 속도가 훨씬 빠릅니다. (단일 스레드 환경용)
Java
// 실무 권장 패턴: 문자열 가공은 StringBuilder로!
StringBuilder sb = new StringBuilder();
sb.append("Run");
sb.append("Code");
sb.append("Rich");
System.out.println(sb.toString()); // 최종 결과만 String으로 변환

 


4. JVM 메모리 관점에서 보기 (면접 필살기)

면접관이 "왜 String 연산이 느린가요?"라고 묻는다면, 힙(Heap) 영역의 메모리 할당 관점에서 답변해 보세요.

  1. String: 연산 시마다 새로운 인스턴스가 Heap 영역에 할당됩니다. 이전 객체들은 GC(Garbage Collector)의 대상이 되어 시스템 부하를 줍니다.
  2. Builder/Buffer: 처음 할당된 버퍼의 크기를 초과하지 않는 한, 기존 메모리를 그대로 재사용합니다. GC가 열일할 필요가 없으니 당연히 빠릅니다.

5. 실무에서는 무엇을 써야 할까? (결론)

현대적인 자바 개발 환경(Java 8 이상)에서는 컴파일러가 단순한 + 연산을 어느 정도 StringBuilder로 자동 변환해 주기도 합니다. 하지만 반복문 내부에서의 연산은 여전히 개발자가 직접 선택해야 합니다.

  1. String: 문자열 수정이 거의 없고, 조회 위주일 때. (대부분의 변수 선언)
  2. StringBuilder: 단일 스레드 환경에서 문자열 가공(문자 더하기, 자르기 등)이 많을 때. (성능이 최우선일 때)
  3. StringBuffer: 웹 서버의 공용 리소스처럼 멀티 스레드 환경에서 안전하게 문자열을 공유해야 할 때.

🏁 마무리하며: "의도가 담긴 코드"

단순히 "속도가 빠르니까 쓴다"를 넘어, "이 데이터는 불변이어야 하니까 String을 쓴다" 혹은 "이 로직은 빈번한 수정이 일어나므로 StringBuilder를 쓴다"는 의도가 코드에 담겨야 합니다.

오늘 정리한 내용을 바탕으로 상황에 맞는 최적의 클래스를 선택해 보세요. 여러분의 프로그램은 훨씬 더 가볍고 빨라질 것입니다!