C# GZipStream 문자열 압축과 해제



많은 문자열을 네트워크를 통해 보낼때 CPU 비용 보다는 네트워크 비용을 줄이기 위해 사용할 수 있을것이다. 하지만 압축과 해제는 CPU뿐만 아니라 메모리에서도 제약 사항을 많이 받는다. 최대 4GB까지 지원하지만 압축된 데이터를 저장할 수 있는 메모리 공간과 압축 해제된 데이타를 저장할 수 있는 공간이 필요 하기에 예상보다 많은 메모리 사용량이 필요 할 수 있다. 그러르모 언제든지 OutOfMemoryException(OOM)이 발생할 수 있으며, InSufficientMemoryException 또한 발생할 수 있다. 그래서 실 사용에서는 사용환경에 맞게 최대 허용가능한 용량을 산정하여 사용할 필요가 있다.


실예로 모 사이트에서 수만 row의 값을 압축하여 클라이언트에 내려 보냈으나 압축해제시 OutofMemoryException이 발생하여 고생을 했던적이 있다. 그리고 서버상에서는 여러 사용자가 요청할 수도 있는 환경이기에 더욱더 주의해서 사용해야 한다.


아래는 실제로 메모리 상에서 문자열을 압축하고 해제하는 코드다.


압축 메소드

/// <summary>
/// 중복된 많은 문자열을 용량을 줄일 때 사용
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </summary>
/// <param name="str">압축할 문자열</param>
/// <returns>압축된 문자열</returns>
/// <remarks>
/// UTF-8기반 문자열 압축 - 최대 4Gb 이하만 사용
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </remarks>
/// <example> 문자열 압축
/// <code>
/// string str = "compress string";
/// string compressed = Compression(str);
/// </code>
/// </example>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InsufficientMemoryException">사용 가능한 메모리가 없을때 발생</exception>
public static string Compression(string str)
{
    var rowData = Encoding.UTF8.GetBytes(str);
    byte[] compressed = null;
    using (var outStream = new MemoryStream())
    {
        using (var hgs = new GZipStream(outStreamCompressionMode.Compress))
        {
            //outStream에 압축을 시킨다.
            hgs.Write(rowData0rowData.Length);
        }
        compressed = outStream.ToArray();
    }
 
    return Convert.ToBase64String(compressed);
}


압추해제 메소드

/// <summary>
/// 문자열 압축 해제
/// , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </summary>
/// <param name="compressedStr">압축된 문자열</param>
/// <returns>평문 문자열</returns>
/// <remarks>
///  UTF-8기반 문자열 압축 해제 - 최대 4Gb 이하만 사용
///  , 메모리가 많이 사용되기 때문에 충분히 테스트가 되어야 하며 사전에 사용할 수 있는 최대값을 결정하고 허용된 범위 안에서만 되도록 한다.
/// </remarks>
/// <example> 문자열 압축 해제
/// <code>
/// string deCompressed = DeCompression(compressedStr); //압축 해제된 문자열
/// </code>
/// </example>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InsufficientMemoryException">사용 가능한 메모리가 없을때 발생</exception>
public static string DeCompression(string compressedStr)
{
    string output = null;
    byte[] cmpData = Convert.FromBase64String(compressedStr);
    using (var decomStream = new MemoryStream(cmpData))
    {
        using (var hgs = new GZipStream(decomStreamCompressionMode.Decompress))
        {
            //decomStream에 압축 헤제된 데이타를 저장한다.
            using (var reader = new StreamReader(hgs))
            {
                output = reader.ReadToEnd();
            }
        }
    }
 
    return output;
}


(소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략)
※ 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 포스팅을 하는제 주안점을 두도록 하겠습니다.
  진짜로 필요한 소스는 단순히 Copy & Paste만드르도 사용할 수 있도록 노력할 것이며 주석을 nDoc이나 별도의 자동 DOC 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다.
DOC Util link



다음은 위 메소드가 정상적으로 동작하는지 테스트를 해 보도록 하겠다.

[TestMethod]
public void Compress_DeCompress_TestMethod()
{
    string str = "양복 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press 양복 press";
    string compressed = Compression(str);
    int fooLength = compressed.Length;      // 60   - 약 90% 정도가 압축이 되었다.
    int barLength = str.Length;             // 704
    string deCompressed = DeCompression(compressed);
    int bazLength = deCompressed.Length;    // 704
 
    //System.IO.BinaryWriter
    //System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
 
    Assert.AreEqual(strdeCompressed);
}

(위 테스트는 중복된 문자열이 많게 하여 테스트를 진행하였다.)


압축과 해제를 적절한 곳에 제한적으로 사용한다면 훌륭한 모듈이지만 과도하게 적용하여 사용하다가 보면 CPU나 메모리 제한으로 언제 어떠한 일이 발생할지 모른다. 그러므로 적절히 제한적으로 사용하여 시스템의 전체적인 퍼포먼스를 제대로 사용할 수 있는 개발자가 되었으면 한다.


Tip!

1. 압축에 관계된 오픈 소스가 여러가지 존재 하고 있다.

.Net Framework에서 기본제공 하는 속도나 메모리 제약 사항을 뛰어 넘는 훌륭한 솔루션들이 많이 있지만 제한에 대한 높이만 다를뿐 언제든지 이와 같은 문제에 직면할 수 있으므로 주의 해서 사용 해야 한다.


2. 다음에는 문자열만 압축하는 것이 아닌 Object 자체 까지도 압축할 수 있는 방법을 알아 보도록 하겠다.


+ Recent posts