SimpleIsBest.NET

유경상의 닷넷 블로그
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
첫 기술 블로그로 문자열 이야기를 좀 해볼까 합니다. 문자열은 가장 기본적이면서 가장 중요한 데이터 타입 중 하나입니다. 하지만 대개 개발자들이 그다지 중요하게 생각하지 않는 데이터 타입들 중 하나이기도 하지요. 대수롭지 않게 작성하는 코드가 전체적인 프로그램 성능에 영향을 미칠 수도 있다는 점을 알리기 위해 키보드를 잡았습니다.

편의상 기술 블로그는 존칭을 생략하겠습니다. 그리고... 제가 예전부터 해보고 싶었던 '딴지일보' 스타일의 글을 써보고자 합니다. 대개 기술문서는 딱딱하기 그지 없고 재미 없는 글입니다. 게다가 잘 이해가 안가는 내용이라면 지하철에서 서서 읽을 때도 졸음을 유발하곤 하지요. (적어도 제 개인 경험상은...) 그래서 평소에 해보고 싶었던 것은 딴지일보 스타일로 글을 쓰는 것이였습니다. 이런 글이라면 기술 문서라도 킥킥거리면서 재미있게 읽을 수 있지 않을까 생각했던 거죠.

그래서 질러보기로 했습니다. 아직은 글 쓰는 실력이 서툴르고 처음 시도인 만큼, 완전한 딴지 스타일의 글을 쓰진 않겠습니다. 첨부터 '독자제위', 'X까' 등등의 용어를 구사하다가 욕을 먹을 수 있으므로 처음에는 약간의 딴지 스타일로만 글을 쓰고, 좀더 딴지 스타일의 글을 연구하고, 여러분들의 피드백을 받아 본 후에 추후 글 스타일을 바꾸도록 하겠습니다. 많은 피드백 부탁 드립니다.

Story about StringBuilder - (I)

요번 글의 핵심은 StringBuilder내부를 알자이다. StringBuilder의 내부를 함 까보자는 얘기이다. 닷넷 프레임워크의 내부를 까보는데 도움이 되는 것은 뭐니 뭐니 해도 소스 코드이다. 닷넷 프레임워크의 StringBuilder 클래스의 내부를  Reflector를 통해 살짝 엿보면 므흣한 내용을 많이 알 수 있다. 문자열 이야기의 첫번째인 StringBuilder 에 대한 썰을 풀어 보자...

Immutable String

결혼 생활에서 항상 주의할 것이 있다. 연애질 할 때는 가끔 만나기 땀시 (뭐 주구장창 붙어 사는 커플도 있다. -_-) 이것 저것 신경도 쓰지만, 일단 결혼에 골인하고 나면 눈 벌어지면 보는 사람이기 때문에 서로에 대해 소홀해 질 수 있다는 거다 (무난한 결혼 생활을 유지하기 위해 특히 남성 분들은 신경을 써야 할 것이다). 닷넷 프로그래밍에도 매우, 극히, 엄청나게 자주 대하는 데이터 타입이 문자열(string)일 것이다. 매우 흔하기 때문에 코드 작성시 문자열 타입을 크게 고려하지 않는 경우가 많다. 하지만 문자열 타입도 세심한 주의가 필요한 데이터 타입이다. 어떤 주의가 필요한가를 알아보기 위해 '문자열 이야기' 씨리즈를 만들었다. 모두 몇 차례의 문자열 이야기가 나갈지는 나도 모른다. 하지만 생각나는 대로 글을 올릴 터이니 시간 나는 대로 탐독하시길...

독자들이 처음 닷넷을 배운 때를 눈을 감고 생각해 봐라. 읽었던 책에서 닷넷의 문자열은 모 Jxxx 언어와 같이 문자열이 immutable 이란 이야기가 생각날 것이다. 왜 닷넷에서 문자열이 immutable 인가에 대해서는 여기서 시시콜콜 논하지 않겠다. 뭐 좋으니까 immutable 이겠지 하고 걍 넘어가자. (나중에 시간나면 문자열이 왜 immutable이 되었나 함 다루어볼까 한다. 믿거나 말거나... -_-)

문자열이 immutable 이란 얘기는 할당된 문자열을 바꿀 수 없다는 얘기 인데... 문자열을 바꾼다는 얘기는 기존 문자열에 추가 문자열을 붙이거나 기존 문자열의 문자(들)를 바꾸는 등의 문자열 연산을 존재하는 문자열에 대해서 수행할 수 없다는 얘기가 되겠다. 그렇다면 어떻게 문자열 연산을 수행할까? 간단하다. 문자열 연산의 결과로서 기존 문자열이 바뀌는 것이 아니라 새로 문자열을 만들면 된다. 예를 들어 "aaa" 라는 문자열에 "bbb"를 연결하는 문자열 연산을 생각해 보자. 요런 문자열 연산을 유식하게 문자열 연결(concatenate) 이라고 한다. C/C++ 스타일이라면 문자열 버퍼를 대략 큼지막하게 작성해 놓고, 이 문자열 버퍼에 "aaa"를 할당하고 그 문자열 버퍼 뒤에 "bbb"를 쑤셔 넣으면 된다. 하지만 닷넷은 "aaa" 문자열 뒤에 문자열 "bbb"를 연결하면 새로운 문자열 "aaabbb" 가 생성 된다. C/C++에서는 2개의 문자열이 있으면 되는 반면("aaa"와 "aaabbb"를 위한 덩치 큰 문자열과 "bbb" 문자열), 닷넷은 세 개의 문자열이 소요된다. 대략 낭비가 아닐 수 없다.

StringBuilder Introduced

이런 전차로... 문자열 연결과 같은 연산을 하라고 만들어 놓은 것이 있으니 그것이 System.Text.StringBuilder 클래스 이다. 대다수의 닷넷 서적과 온라인 문서들을 살펴보면 문자열 연결에 StringBuilder를 사용하는 것이 좋다고 되어 있다. 대략적인 논지는 닷넷의 문자열은 변경 불가능이고 이 때문에 문자열 연산을 수행하면 새로운 문자열 객체들이 계속 생성되어 비효율 적이니 문자열 연결 마다 문자열이 생성되지 않는(?) StringBuilder를 쓰라는 것이다. 게다가 어떤 글은 StringBuilder의 성능까지 운운하기도 한다.

그렇다면 StringBuilder는 대체 어떻게 문자열 연결을 수행할까? StringBuilder라고 용빼는 재주는 없다. StringBuilder 역시 내부에 문자열 버퍼를 가지고 있다. 그리고 문자열 연결이 수행될 때마다 즉, Append 혹은 AppendFormat 메쏘드가 호출될 때마다 이 내부 퍼버에 연결하고자 하는 문자열을 복사하는 것이다. 그런데 StringBuilder의 내부 문자열 버퍼가 바로 System.String 타입이다.

이런... 문자열은 immutable 이라 하지 않았나?  적어도 CLR 내부에서는 문자열이 immutable 이란 말은 완전히 구라가 되겠다. 말이 immutable 이지 CLR 내부와 mscorlib.dll 내부에서는 FillString 이란 static 메쏘드(FillStringEx 등등 문자열을 바꾸는 메쏘드 세트가 있다)를 호출하여 새로운 문자열을 할당하지 않고 기존 문자열을 바꿀 수 있다 (이런 치사한... -_-). 우리 같은 미천한 프로그래머는 FillString 메쏘드를 호출할 수 없다. 이 녀석이 private 메쏘드이기 때문이다. 대략 뷁 스럽다 아니할 수 없다.

* 불가능은 없다... 대략 귀찮을 뿐...
FillString 메쏘드 호출이 전혀 불가능한 건 아니다. Reflection을 이용하면 호출할 수 있다. 하지만 이렇게 까지 할 가치는 없다고 본다. 기존 문자열 변경을 위해 Reflection 까지 쓴다는 건 배보다 배꼽이 클 수 있기 때문이다. 구체적인 내용은 다른 문자열 이야기에서 썰을 풀겠다. 여기서 이야기 하면 글이 대략 난감하게 길어진다는...

다시 본론으로 돌아와서, StringBuilder는 내부 문자열 버퍼를 만들어 놓고 Append 류의 메쏘드가 호출될 때마다 내부 문자열 버퍼를 채워 가는 것이다. 예상 했던 바다. 하지만 문자열 버퍼가 다 차버린다면? 마이크로소프트 CLR 개발팀이 짱구가 아닌 다음에야 이러한 경우를 대비하지 않았을리 없다. StringBuilder는 내부 버퍼가 부족할 때마다 새로운 내부 버퍼를 생성하며 그 크기는 기존 버퍼의 2배가 되도록 한다. 이때 중요한 것은 앞서 말한 것처럼 내부 버퍼가 System.String 이라는 점이며 이는 곧 새로운 문자열을 만드는 것이 되겠다.

눈치빠른 독자라면 조금씩 냄새를 맡았을 것이다. 글타. 다시 강조하지만 StringBuilder라고 용빼는 재주가 있는 것이 아니다. StringBuilder를 사용하더라도 새로운 문자열의 할당은 발생할 수 있는 것이며 StringBuilder를 사용할 때의 핵심은 이러한 문자열 할당이 발생하지 않도록 사용해야 하며, StringBuilder를 사용할 것인가 말 것인가를 결정하는 기준으로도 사용해야 한다.

뭔 말인가 하면... 다음 문자열 이야기 포스트에 상세히 나온다. -_-



Comments (read-only)
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / 경아 / 2005-05-17 오전 2:53:00
여러번 읽어야 할만큼 어렵당.. 세번쯤 읽으니 겨우 이해가 되었는데 초보들을 대상으로 한건 아니건 같네...그치?
맞게 이해를 한건지 모르겠네.
닷넷에서는 문자열을 바꿀 수가 없어서 연산을 할 경우 새로운 문자열이 추가로 생성된다는 말인거지?
그래서 System.Text.StringBuilder클래스를 사용한다. 하지만 이 클래스도 내부적으로 보면 내부 버퍼에 채우고...
내부 버퍼는 새로운 문자열을 추가하기도 한다... 뭐 그런거 맞어?
그래서 내부 버퍼에서 추가 할당이 발생하지 않게 잘 써야 한다고 설명한건가...
아휴..이해가 된건지.. 모르겠지만 다음 글이 궁금하당.... 헤헤.... 이 밤에 이런 걸 쓰고 있다니....
진짜 궁금한건 그렇게 쓰면 성능이 얼마나 향상되는건지...그게 젤 궁금하네..

<<오타 수정 해요. 읽다가 거슬려서...시리...ㅋㅋ>>
Append 혹은 AppendFormat 메쏘드가 호출될 때마다 이 내부 <<퍼버>>에 연결하고자 하는 문자열을 복사하는

잘 읽었어. 멋지게 잘 만들어 가길......
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / 선우아빠 / 2005-05-19 오후 5:56:00
잘보고 갑니다...
많은 글 올려 주심 내공 쌓는 데 도움 되겠사옵니다.
"눈 벌어지면 보는 사람" 넘 감동적입니다....ㅋㅋㅋ
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / 이석원 / 2005-06-16 오전 11:39:00
MS에서도 stringbuilder를 사용해라라고 공공연하게 recommand하는데 우찌....
나도 여러곳에서 거짓말 한꼴이 되버렸당......
java에서도 stringbuilder를 많이 사용하던데...거기도 같은 방식인지는 모르지만...stringbuilder에 대한 잘못된 개념이 확...깨져 버렸네요.
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / jinos / 2006-11-09 오후 2:15:00
열시미 배우겠습니다. 원숭이 프로그래머에서 개념있는 프로그래머가 될때까징...
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / 열혈코더 / 2007-11-13 오전 9:03:00
그동안 당연히 사용해야 하는걸로만 알고 있던 StringBuilder에 대해 사실을 알게된 것 같습니다.
좋은 글 감사드립니다.
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / 조승태 / 2008-02-22 오후 5:21:00
저는 그냥 string을 막 쓰는데.. ^^;;;;

어쨌든 감사합니다.
#re: 문자열 이야기 (1) - StringBuilder에 대한 진실 혹은 거짓말 (I) / huzii / 2008-06-25 오후 10:45:00
한 수 배워갑니다.