Refactoring - Encryption Hash Helper

2023. 10. 16. 00:00CSharp/Advance

반응형

일반적으로 특정 언어를 이용한 프로그램 개발을 위해서는 몇가지 과정이 있다. 

1. 해당 언어를 습득하는 단계

2. 해당 언어로 특정 기능들을 구현하는 단계

3. 구현된 프로그램을 유지 보수 하는 단계

 

위에서 3번째 단계에 들어서게 되면 refactoring 이라는 과정이 필요하게 된다. 

왜냐하면 처음 생각했던 설계들은 많이 변경되었을 것이고

우리고 구현했던 구현물 자체도 몇몇 bug 에 의해서

중구난방 적으로 코드 땜빵(??)이 되었을 것이기 때문이다.

 

그래서 이번에는 Refactoring (이라고 쓰고 정리 라고 읽는다) 을 간단하게 해보겠다.

 

사실 martin fowler 라는 guru 가 이미 refactoring 에 관해서는 bible 을 만들어 놓았다. 

그래서 그 기법들을 알고 싶다면 관련 책을 읽어보는 것도 한 방법이다. 

이곳은 그의 blog 다. https://martinfowler.com/

 

martinfowler.com

A website on building software effectively

martinfowler.com

 

이번에 하려는 것은 일반적인 코딩에서 하드 코딩 되어있는 코드를 refactoring 해보겠다. 

static class 를 생성하고 helper 를 만들 것이다. 

(static class 같은 것을 만들면 습관적으로 발작을 일으키는 부류들이 있는데...

흠 뭐 대충 이유는 알겠다만.. 일단 refactoring 이라는 것이 무언인지 좀더 근원적인 부분으로 접근해보자

그렇다면 goto 를 쓰면 된다 안된다와 같은 절대적인 답이 없다는걸 알게 될 것이다...)

 

어쨌든 우리는 일단 아주가볍게 특정 string 을 hash 처리 해볼 것이다. 

 

다음과 같이 코드를 작성할 수 있을 것이다. 

public static string Encrypt(string text, bool isUppercase = false)
{
    string result = string.Empty;
    using (var algo = System.Security.Cryptography.SHA256.Create())
    {
        result = GenerateHashString(algo, text, isUppercase);
    }

    return result;
}

private static string GenerateHashString(HashAlgorithm algo, dynamic input, bool isUppercase)
{
    if (input is string)
        input = Encoding.UTF8.GetBytes(input);

    if (!(input is Stream) && !(input is byte[]))
        throw new Exception($"can not GenerateHashString type  = {input}");

    // Compute hash from text parameter
    algo.ComputeHash(input);

    // Get has value in array of bytes
    var result = algo.Hash;

    if (result is null) throw new Exception($"can not ComputeHash");

    // Return as hexadecimal string
    return string.Join(
        string.Empty,
        result.Select(x => x.ToString(isUppercase ? "X2" : "x2")));
}

그리고 call 을 아래와 같이 할 수 있다. 

var source = "Hello, World!";
Console.WriteLine(source);

var hash = Encrypt(source);
Console.WriteLine(hash);

그러면 sha256 형태의 hash string 이 다음과 같이 표시된다. 

dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

그런데 이런식으로는 call 하는 코드와 encryption hash 를 하게 되는 코드가 뒤죽 박죽이 된다. 

solid 에서 srp 를 위반하게 되는 것이다. 

그래서 최소한의 srp 를 적용하기 위해서 helper 를 하나 만들어 method 를 extract 해보자

public static class EncryptionHashHelper
{
    public static string Encrypt(string text, bool isUppercase = false)
    {
        string result = string.Empty;
        using (var algo = System.Security.Cryptography.SHA256.Create())
        {
            result = GenerateHashString(algo, text, isUppercase);
        }

        return result;
    }
    private static string GenerateHashString(HashAlgorithm algo, dynamic input, bool isUppercase)
    {
        if (input is string)
            input = Encoding.UTF8.GetBytes(input);

        if (!(input is Stream) && !(input is byte[]))
            throw new Exception($"can not GenerateHashString type  = {input}");

        // Compute hash from text parameter
        algo.ComputeHash(input);

        // Get has value in array of bytes
        var result = algo.Hash;

        if (result is null) throw new Exception($"can not ComputeHash");

        // Return as hexadecimal string
        return string.Join(
            string.Empty,
            result.Select(x => x.ToString(isUppercase ? "X2" : "x2")));
    }
}

간단 하다.

 

그런데 이제 우리가 md5 형태의 hash 를 표시하려고 한다고 해보자

public static string Encrypt(string text, bool isUppercase = false)
{
    string result = string.Empty;
    using (var algo = System.Security.Cryptography.MD5.Create()) // 이곳이 변경되어야 한다. 
    {
        result = GenerateHashString(algo, text, isUppercase);
    }

    return result;
}

hash algorithm 이 바뀔때 마다 Encrypt 함수를 수정 할 수 없으니 외부로 부터 알고리즘을 주입 받으면 가장 좋을 것이다. 

그러나 여기서는 간단히 enum 을 만들어서 type 만 넘겨주고 내부에서 알고리즘을 생성하겠다. 

주입 받는 것이 가장 유연하나 simple 하게 만든다는 생각을 하면 어차피 system 에서 지원하는 알고리즘이므로 변경이 많지 않을 것이기 때문에 이 방법도 나쁘지 않을 것이다. 

 

public enum AlgorithmType
{
    Md5 = 0,
    Sha1,
    Sha256,
    Sha384,
    Sha512
}
public static class EncryptionHashHelper
{
	// AlgorithmType 을 parameter 로 받는다. 
    public static string Encrypt(string text, AlgorithmType algorithmType, bool isUppercase = false)
    {
        string result = string.Empty;
        using (var algo = CreateHashAlgorithm(algorithmType)) // 이곳에서 algorithm 을 생성한다. 
        {
            result = GenerateHashString(algo, text, isUppercase);
        }

        return result;
    }

	// HashAlgorithm 을 생성하는 method
    private static HashAlgorithm CreateHashAlgorithm(AlgorithmType algorithmType)
    {
        switch (algorithmType)
        {
            case AlgorithmType.Md5: return CreateMD5();
            case AlgorithmType.Sha1: return CreateSha1();
            case AlgorithmType.Sha256: return CreateSha256();
            case AlgorithmType.Sha384:return CreateSha384();
            case AlgorithmType.Sha512:return CreateSha512();
            default: return CreateSha256();
        }
    }
    
    // 각 hashAlgorithm 생성 method
    private static HashAlgorithm? CreateMD5() => MD5.Create();
    private static HashAlgorithm? CreateSha1() => SHA1.Create();
    private static HashAlgorithm? CreateSha256() => SHA256.Create();
    private static HashAlgorithm? CreateSha384() => SHA384.Create();
    private static HashAlgorithm? CreateSha512() => SHA512.Create();


    private static string GenerateHashString(HashAlgorithm algo, dynamic input, bool isUppercase)
    {
        if (input is string)
            input = Encoding.UTF8.GetBytes(input);

        if (!(input is Stream) && !(input is byte[]))
            throw new Exception($"can not GenerateHashString type  = {input}");

        // Compute hash from text parameter
        algo.ComputeHash(input);

        // Get has value in array of bytes
        var result = algo.Hash;

        if (result is null) throw new Exception($"can not ComputeHash");

        // Return as hexadecimal string
        return string.Join(
            string.Empty,
            result.Select(x => x.ToString(isUppercase ? "X2" : "x2")));
    }
}

자 이제 완성 되었다. 

 

위와 같이 하게 되면 hashalgorithm 에 따라 helper 에서 원하는 hash 를 이용해 encryption 을 호출 할 수 있게 된다. 

Refactoring 이 가장 간단한 예를 보였다고 생각하면 되겠다. 

 

관련영상

https://youtu.be/Rlcd_QXJ1jg

반응형

'CSharp > Advance' 카테고리의 다른 글

Refactroing - HttpClient(EP02)  (1) 2023.10.30
Refactroing - HttpClient  (1) 2023.10.23
C# deadlock prevent  (0) 2023.06.23
Singleton  (0) 2022.02.25
Prototype  (0) 2022.02.24