login register Sysop! about ME  
qrcode
    최초 작성일 :    2005년 05월 17일
  최종 수정일 :    2006년 10월 10일
  작성자 :    Loner (유경상)
  편집자 :    Loner(유 경상)
  읽음수 :    30,715

강좌 목록으로 돌아가기

필자의 잡담~

문자열 이야기 두 번째 입니다. 이번 글도 마찬가지로 딴지 스타일로 적어봤습니다. 머 완전한 딴지 스타일은 아니고... 세미 딴지 정도? 약간 얌전하게... 역시 기술 자료를 딱딱하지 않게 쓴다는 것이 힘들군요. 쫌더 딴지 스타일에 가깝게 쓰면 몰라도 세미 딴지 스타일은 좀 힘들다는...
현재 강좌의 원본 글의 링크는 http://www.simpleisbest.net/archive/2005/05/17/149.aspx 입니다.

앞서 문자열 이야기 포스트에서 StringBuilder에 대해 간략한 설명을 해보았다. 이번엔 좀더 꽁꼬 깊숙히 StringBuilder의 내부를 살펴보도록 하자.

Inside StringBuilder

StringBuilder가 생성되면 디폴트로 16문자를 담을 내부 문자열 버퍼를 생성한다. 요것이 오늘의 중요한 뽀인또가 되것다. 연속되는 Append 호출이 발생하여 문자열이 16문자를 초과하게 되면 2배인 32 문자를, 그 후에 또 버퍼가 초과되면 64, 128, 256, 512, ... 이런식으로 계속 새로운 버퍼를 할당한다. 새로운 버퍼를 할당하는 것 외에도 좆지(험험) 못한 것은 기존 버퍼의 내용을 새 버퍼로 복사해야 한다는 것이다. 그리고 나서 기존 문자열 버퍼는 어떻게 하냐고? 그걸 나한테 물으면 어떡하나? GC(Gargabe Collection)에게 물어봐야지.

오늘의 또 한가지 뽀인또는 StringBuilder.ToString() 메쏘드다. StringBuilder를 통해 기껏 문자열을 만들어 대면 뭐하나? 정작 필요한 것은 문자열, 즉 System.String 타입인걸... 그래서 항상 우린 StringBuilder.ToString()을 호출하여 문자열을 받아 낸다. StringBuilder.ToString 메쏘드는 새로운 문자열을 할당하고 내부 문자열 버퍼의 내용을 이 새로운 문자열에 복사하여 반환한다. StringBuilder.ToString이 새로운 문자열을 할당하는 것을 주목할 필요가 있겠다. 만약 새로운 문자열을 만들어 반환하지 않고 StringBuilder의 내부 문자열을 그대로 반환한다면, 하나의 문자열에 대해 2개 이상의 참조(reference)가 존재하게 된다. 만약 StringBuilder 가 재사용되어 변경이 가해지면 문제가 발생하게 될 것이다. ToString() 이 호출된 후, StringBuilder는 버퍼를 초기화 하여 StringBuilder가 재사용될 수 있도록 만드는 것 역시 알아두면 피가되고 살이되는 지식이다. 이쯤 되면 펜을 들고 메모할 필요를 느끼지 않는가?

StringBuilder의 내부를 까보면 생각보다 간단하지 않다는 것을 알 수 있다. 크기가 자동으로 증가되어야 하므로 StringBuilder는 내부 문자열 버퍼가 넘치는 것을 막기 위해 할당된 내부 문자열 버퍼의 크기를 유지하며 또한 이 내부 버퍼에 기록된 문자의 개수 역시 유지해야 하는 등의 오버헤드를 갖고 있다. StringBuilder는 빠르지 않다. StringBuilder에 대한 다양한 성능 테스트(난중에 성능 테스트 자료를 올리겠다. 지금 성능 테스트 자료까지 올리면 블로그 하나가 너무 빡세지기 때문에...)를 살펴보아도 StringBuilder의 오버헤드가 적지 않다는 것을 알 수 있다.

StringBuilder Usage

StringBuilder를 쓰지 말라고 ? 전혀 쓰지 말라는 얘기가 아니다. StringBuilder를 쓸때와 그렇지 않을 때를 명확히 구분하는 것이 좋다는 얘기이다. 이쯤 되면 독자제위들은 어떤 때 StringBuilder를 쓰지 말아야 하는가를 말할 것이라고 예상할 것이다. 반대로 StringBuilder를 사용해야 하는 때는 필자 생각에는 그다지 많지 않다. 대부분의 경우 String.Concat 이나 C#, VB.NET의 + 연산자를 쓰는 것이 더 효율적이며, 반복적으로 다수(수십 ~ 수백회) 문자열을 연결하는 경우에나 StringBuilder를 사용하는 것이 좋다. StringBuilder를 사용하지 않을 때 사용할 문자열 연산 방법은 쪼금 있다 설명하기로 하고 여기서는 StringBuilder의 올바른 용법에 대해서 살펴보자.

StringBuilder를 쓸 때는 가급적 버퍼 크기를 명시하는 것이 좋다. 즉 달랑 디폴트 생성자로 StringBuilder를 생성하지 말고 생성자에 버퍼 크기를 주라는 것이다. 버퍼 크기는 대략적으로 예상되는 크기를 적어주면 되겠다. 그렇다고 무조건 겐또로 찍는 것도 곤란하지만 대략적으로 크기를 알 수 있을 것이다. 전혀 모르겠으면 넉넉하게 크게 잡아줘라. 잘모르겠다고 ? 다음 코드를 보자.

// 좋지 못한 StringBuilder 사용법
StringBuilder sb = new StringBuilder();    // 16 문자 버퍼(문자열)를 생성
sb.Append("1234567890");
sb.Append("1234567890");    // 버퍼가 부족하므로 32 문자를 담을 새로운 버퍼를 생성하고 기존 버퍼 내용을 복사한다.
sb.Append("1234567890");
sb.Append("1234567890");    // 버퍼가 또 부족하므로 64 문자를 담을 새로운 버퍼를 생성하고 기존 버퍼 내용을 복사한다.
string s = sb.ToString();   // 새로운 문자열을 만들어서 내부 버퍼의 내용을 복사하고 반환한다.

위 코드는 4개의 문자열이 StringBuilder와 관련되어 생성되고 사용되며 이중 2개는 아주 짧은 시간 동안만 사용되고 버려진다. 반면 다음 코드는 2개의 문자열만이 사용된다.

// 그나마 나은 StringBuilder 사용법
StringBuilder sb = new StringBuilder(64);    // 64 문자 버퍼(문자열)를 생성
sb.Append("1234567890");
sb.Append("1234567890");
sb.Append("1234567890");
sb.Append("1234567890");    // 버퍼가 충분하므로 새로운 내부 버퍼를 요구하지 않는다.
string s = sb.ToString();   // 새로운 문자열을 만들어서 내부 버퍼의 내용을 복사하고 반환한다.

이 예제에서는 최종적인 문자열 크기를 알기 때문에 64라는 capacity 값을 줄 수 있었지만 최종적인 크기를 전혀 알 수 없다면 넉넉하게 capacity 값을 주는 것이 좋다. 항상 이런 말을 하면 걱정되는 것이 무식하게 딥따 큰 capacity를 주는 인간들이다. 이런 단순 무식은 명랑한 프로그래밍 문화에 아무런 도움이 못 된다. 적당히 넉넉히 주면 StringBuilder가 알아서 그 크기를 2배씩 키워 줄 것이다. 초기 값이 작으면 작을 수록 반복적으로 임시 문자열이 만들어졌다가 사라지므로 이것을 방지해 보자는 것이다.

잠시 후 알게 되겠지만 위의 두 예제 코드는 모두 삽질이 되겠다.... -_- 위와 같이 간단한 문자열 연결(concatenate)은 StringBuilder를 쓰는 것이 더 비효율적일 뿐더러, 위 예제와 같이 문자열 상수(유식하게는 문자열 리터럴 이라구도 한다)를 연결할 때는 더더욱 그렇다. StringBuilder는 커다란 문자열 버퍼에 반복적으로 수십, 수백 회의 문자열 연결하는 때가 아니라면 사용할 일이 별로 없다고 보문 되긋다. 특히 for, while, foreach 류의 반복문 안에서는 StringBuilder를 쓰는 것이 좋다. 그런 때가 언제냐고? ASP.NET에서 HTML을 말 그대로 '만들어'낼 때는 반복적으로 문자열은 연결해야 하는 경우가 마니 생긴다. 안 해봤다고? 그럼 지금이라도 메뉴 웹 컨트롤을 만들어 보라...


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})