푸터 콘텐츠로 바로가기
Iron Academy Logo
C# 배우기
C# 배우기

다른 카테고리

C# 주요 디자인 패턴 마스터하기

Tim Corey
35m 50s

디자인 패턴은 일반적인 소프트웨어 개발 문제에 대한 재사용 가능한 해결책으로, 객체 지향 코드를 보다 효율적이고 유지 관리하기 쉬운 방식으로 구조화하고 구현하기 위한 템플릿을 제공합니다. 이러한 도구는 개발자가 객체 생성, 구조 및 통신과 관련된 문제를 유연하고 확장 가능한 방식으로 해결할 수 있도록 지원합니다. 디자인 패턴은 개발자가 더 나은 코드를 작성하는 데 도움이 되는 모범 사례 개념입니다. 소프트웨어 설계의 기본 원칙 중 하나는 SOLID 원칙의 일부인 단일 책임 원칙(SRP)입니다.

팀 코리는 자신의 영상 " 디자인 패턴: C#에서 실용적으로 설명하는 단일 책임 원칙(SOLID의 S) "에서 단일 책임 원칙(SRP)을 탐구하며 소프트웨어 설계에서 그 중요성을 강조하고 이를 효과적으로 구현하는 방법에 대한 실질적인 통찰력을 제공합니다. 이 글은 그의 영상에서 얻은 핵심 내용을 간략하게 정리하여, 깔끔하고 유지보수 가능한 코드를 작성하는 데 있어 SRP(단일 책임 원칙)의 중요성을 강조합니다.

SRP 소개

소프트웨어 설계에서 SOLID 원칙은 유지보수성과 확장성이 뛰어난 코드를 작성하는 데 매우 중요합니다. 그들은 코드가 이해하기 쉽고, 테스트하기 쉽고, 수정하기 쉽도록 보장합니다. 단일 책임 원칙(SRP), 개방/폐쇄 원칙(OCP), 리스코프 치환 원칙(LSP), 인터페이스 분리 원칙(ISP), 의존성 역전 원칙(DIP)의 다섯 가지 원칙은 객체 지향 설계의 핵심 요소이며, 설계 패턴에 적용하여 더욱 견고한 솔루션을 만들 수 있습니다.

C#에서 디자인 패턴을 적용하면 개발자는 일반적인 문제를 더욱 효과적으로 해결할 수 있습니다. 객체 생성, 트리 구조 정의, 단일 인스턴스를 통한 재사용성 보장 등 디자인 패턴은 소프트웨어 아키텍처를 향상시키는 미리 정의된 솔루션을 제공합니다. 팩토리 메서드, 빌더, 싱글턴과 같은 패턴은 유연하고 재사용 가능한 솔루션을 제공하는 반면, 행동 및 구조적 패턴은 복잡성을 관리하고 시스템 내 의사소통을 개선하는 데 도움이 됩니다. 개발자들은 이러한 패턴을 학습하고 활용함으로써 유지 관리 및 확장이 더 쉬운 시스템을 구축할 수 있습니다.

팀은 SRP(단일 책임 원칙)의 개념에 대해 논의하며, 개발자들이 자신의 코드가 모범 사례를 준수하도록 하는 것이 매우 중요하다고 강조합니다. SRP는 학급이 변화해야 할 책임이나 이유가 단 하나뿐이어야 한다고 명시합니다. 이 원칙은 깔끔하고 유지보수하기 쉬우며 확장 가능한 코드를 유지하는 데 도움이 됩니다.

데모 코드 개요

팀은 사용자의 이름과 성을 입력받고, 해당 이름을 검증한 후 사용자 이름을 생성하는 간단한 콘솔 애플리케이션을 C#으로 작성합니다. 초기 구현은 단일 책임 원칙(SRP)을 위반하므로, 이 원칙을 준수하도록 코드를 리팩토링하는 방법을 보여줄 수 있는 좋은 기회입니다.

SRP 설명

팀은 초기 수업에 포함된 다양한 책임들을 강조하면서 SRP를 설명합니다.

  1. 사용자 상호작용 : 환영 메시지 및 안내 메시지 처리.
  2. 데이터 수집 : 사용자의 이름과 성을 수집합니다.
  3. 유효성 검사 : 입력된 이름의 유효성을 검사합니다.
  4. 사용자 이름 생성 : 입력된 이름들을 기반으로 사용자 이름을 생성합니다.

이러한 각각의 책임은 SRP를 위반하여 클래스가 변경되는 서로 다른 이유를 나타냅니다.

SRP 준수를 위한 리팩토링

팀은 각 책임을 별도의 클래스로 분리하여 SRP(단일 책임 원칙)를 준수하도록 코드를 리팩토링하는 방법을 보여줍니다. 이러한 접근 방식은 각 클래스가 변경될 이유가 하나씩만 있도록 보장하여 코드를 더욱 모듈화하고 유지 관리하기 쉽게 만듭니다.

실제 사례

Tim은 리팩토링의 실용적인 예를 제시합니다.

using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to my application");

        Console.Write("Enter your first name: ");
        string firstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        string lastName = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
        {
            Console.WriteLine("You did not give us valid information!");
            Console.ReadLine();
            return;
        }

        var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
        Console.WriteLine($"Your username is {userName}");

        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
}
using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to my application");

        Console.Write("Enter your first name: ");
        string firstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        string lastName = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
        {
            Console.WriteLine("You did not give us valid information!");
            Console.ReadLine();
            return;
        }

        var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
        Console.WriteLine($"Your username is {userName}");

        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
}

단계별 리팩토링

1단계: StandardMessages 클래스 생성

먼저 Tim은 사용자에게 표시되는 표준 메시지를 처리하는 클래스를 만듭니다. 이 클래스는 환영 메시지와 종료 메시지를 관리합니다.

public class StandardMessages
{
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }

    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }

    public static void ShowValidationErrorMessage()
    {
        Console.WriteLine("You did not give us valid information!");
    }
}
public class StandardMessages
{
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }

    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }

    public static void ShowValidationErrorMessage()
    {
        Console.WriteLine("You did not give us valid information!");
    }
}

Program 클래스에서 Console.WriteLine 및 Console.ReadLine에 대한 직접 호출을 StandardMessages 클래스의 메서드 호출로 바꿉니다.

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        // Other code...

        StandardMessages.EndApplication();
    }
}

2단계: PersonDataCapture 클래스 생성

다음으로, Tim은 사람의 이름과 성을 캡처하는 것을 처리하는 클래스를 만듭니다. 이 클래스는 사용자 입력을 수집하고 Person 객체를 반환하는 역할을 담당합니다.

public class PersonDataCapture
{
    public static Person Capture()
    {
        Person output = new Person();

        Console.Write("Enter your first name: ");
        output.FirstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        output.LastName = Console.ReadLine();

        return output;
    }
}
public class PersonDataCapture
{
    public static Person Capture()
    {
        Person output = new Person();

        Console.Write("Enter your first name: ");
        output.FirstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        output.LastName = Console.ReadLine();

        return output;
    }
}

3단계: Person 클래스 생성

사용자의 이름과 성을 저장할 Person 클래스도 필요합니다.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Program 클래스에서 사용자 입력을 직접 처리하는 부분을 PersonDataCapture.Capture 호출로 대체하십시오.

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        // Other code...

        StandardMessages.EndApplication();
    }
}

4단계: PersonValidator 클래스 생성

다음으로, Tim은 사람의 이름과 성의 유효성을 검사하는 클래스를 만듭니다. 이 클래스는 이름이 null이거나 공백이 아닌지 확인하는 역할을 담당합니다.

public class PersonValidator
{
    public static bool Validate(Person person)
    {
        if (string.IsNullOrWhiteSpace(person.FirstName))
        {
            StandardMessages.ShowValidationErrorMessage("first name");
            return false;
        }

        if (string.IsNullOrWhiteSpace(person.LastName))
        {
            StandardMessages.ShowValidationErrorMessage("last name");
            return false;
        }

        return true;
    }
}
public class PersonValidator
{
    public static bool Validate(Person person)
    {
        if (string.IsNullOrWhiteSpace(person.FirstName))
        {
            StandardMessages.ShowValidationErrorMessage("first name");
            return false;
        }

        if (string.IsNullOrWhiteSpace(person.LastName))
        {
            StandardMessages.ShowValidationErrorMessage("last name");
            return false;
        }

        return true;
    }
}

Program 클래스에서 유효성 검사 코드를 PersonValidator.Validate 호출로 바꿉니다.

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        if (!PersonValidator.Validate(user))
        {
            StandardMessages.EndApplication();
            return;
        }

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        if (!PersonValidator.Validate(user))
        {
            StandardMessages.EndApplication();
            return;
        }

        // Other code...

        StandardMessages.EndApplication();
    }
}

7단계: AccountGenerator 클래스 생성

Tim은 사용자 이름 생성 및 계정 생성 로직을 새로운 AccountGenerator 클래스로 옮겼습니다.

  1. AccountGenerator 클래스 생성 :

    • 해당 클래스에는 사용자 이름을 생성하고 계정 생성을 시뮬레이션하는 로직이 포함되어 있습니다.
    public class AccountGenerator
    {
    public static void CreateAccount(Person user)
    {
        string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower();
        Console.WriteLine($"Your username is: {username}");
    }
    }
    public class AccountGenerator
    {
    public static void CreateAccount(Person user)
    {
        string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower();
        Console.WriteLine($"Your username is: {username}");
    }
    }
  2. 메인 클래스 업데이트 :

    • 메인 클래스에서 AccountGenerator를 호출하여 계정을 생성합니다.
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        bool isUserValid = PersonValidator.Validate(user);
        if (!isUserValid)
        {
            StandardMessages.EndApplication();
            return;
        }

        AccountGenerator.CreateAccount(user);

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        bool isUserValid = PersonValidator.Validate(user);
        if (!isUserValid)
        {
            StandardMessages.EndApplication();
            return;
        }

        AccountGenerator.CreateAccount(user);

        StandardMessages.EndApplication();
    }
}

Csharp Top Design Patterns 1 related to 7단계: AccountGenerator 클래스 생성

요약 및 결론

이 마지막 부분에서 팀 코리는 데모 코드의 리팩토링 과정을 통해 단일 책임 원칙(SRP)의 이점과 구현 방법을 요약합니다. 그는 지원서를 더 작고 집중적인 과정으로 나누는 것의 장점을 강조합니다.

SRP의 주요 이점

  1. 간소화된 코드 유지 관리 :

    • 각 클래스는 하나의 책임만 가지므로 변경이 필요한 부분을 더 쉽게 찾을 수 있습니다. 예를 들어, 사용자 데이터 캡처 로직은 PersonDataCapture 아래에 명확하게 배치되어 있습니다.

    • 이 구조는 이해를 단순화합니다. 사용자 유효성 검사를 수정하려는 사람은 누구나 PersonValidator를 확인하면 된다는 것을 알 수 있기 때문입니다.
  2. 향상된 가독성 :

    • 책임이 명확하게 정의되면 주요 프로그램 흐름을 더 쉽게 이해할 수 있습니다. 이제 코드는 명확하고 순차적인 일련의 동작처럼 보입니다.
    StandardMessages.WelcomeMessage();
    Person user = PersonDataCapture.Capture();
    bool isUserValid = PersonValidator.Validate(user);
    if (!isUserValid)
    {
        StandardMessages.EndApplication();
        return;
    }
    AccountGenerator.CreateAccount(user);
    StandardMessages.EndApplication();
    StandardMessages.WelcomeMessage();
    Person user = PersonDataCapture.Capture();
    bool isUserValid = PersonValidator.Validate(user);
    if (!isUserValid)
    {
        StandardMessages.EndApplication();
        return;
    }
    AccountGenerator.CreateAccount(user);
    StandardMessages.EndApplication();
  3. 복잡성 감소 :

    • 특정 책임 영역에 집중된 소규모 클래스는 코드 줄 수가 적어 이해하고 유지 관리하기가 더 쉽습니다.

    • 예시: StandardMessages 클래스의 메서드는 간결하며 환영 메시지를 표시하거나 애플리케이션을 종료하는 등 단일 목적만을 수행합니다.
    public class StandardMessages
    {
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }
    
    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
    
    public static void ShowValidationErrorMessage(string fieldName)
    {
        Console.WriteLine($"You did not give us a valid {fieldName}!");
    }
    }
    public class StandardMessages
    {
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }
    
    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
    
    public static void ShowValidationErrorMessage(string fieldName)
    {
        Console.WriteLine($"You did not give us a valid {fieldName}!");
    }
    }

    Csharp Top Design Patterns 2 related to SRP의 주요 이점

  4. 코드 변경의 용이성 :

    • 각 클래스마다 변경해야 할 이유가 하나씩만 존재하므로, 새로운 요구 사항에 맞춰 코드를 수정하는 것이 간단해집니다.

    • 예: 종료 메시지를 변경해야 하는 경우, 변경은 StandardMessages.EndApplication 메서드에서만 수행됩니다.
  5. 향상된 디버깅 및 협업 :

    클래스가 작고 잘 정의되어 있으면 문제 발생 위치를 쉽게 파악할 수 있으므로 디버깅이 더 간단해집니다.

    • 신규 개발자는 각 클래스의 명확한 구조와 책임 사항을 이해함으로써 더 빠르게 업무에 적응할 수 있습니다.

다양한 수업에 대한 우려 사항 해결

Tim은 SRP를 적용하면 클래스가 너무 많아져 프로젝트가 번거로워진다는 일반적인 우려에 대해 언급합니다.

  1. 탐색 및 이해 :

    • Visual Studio의 IntelliSense와 같은 도구를 사용하면 여러 클래스를 쉽게 탐색할 수 있습니다. 예를 들어, F12 키를 누르면 메서드 또는 클래스 정의로 바로 이동합니다.

    • 작고 관리하기 쉬운 여러 부분으로 구성하면 하나의 거대한 클래스에 비해 전체 애플리케이션을 더 쉽게 이해할 수 있습니다.
  2. 성능 및 저장 용량 :

    • 최신 스토리지 및 컴퓨팅 기능을 고려할 때, 추가 클래스는 디스크 공간이나 성능에 큰 영향을 미치지 않습니다.
  3. 균형과 초과분 :

    • 팀은 균형을 찾는 것이 중요하다고 조언합니다. 클래스의 책임이 너무 커져서 클래스 자체가 너무 커진 경우, 변경해야 할 이유가 여러 가지인지 고려해 보고, 필요하다면 추가 분할이 필요할 수 있음을 판단하십시오.

    • 그는 Visual Studio에서 클래스를 광범위하게 스크롤해야 하는 경우 클래스가 너무 커서 분할해야 할 수도 있다고 제안합니다.

실제 구현

팀은 개발자들이 특히 기존 코드베이스에서 SRP를 점진적으로 적용하도록 권장합니다. SRP 원칙에 맞추기 위해 작은 변화와 새로운 코드 작성부터 시작하세요. 이러한 점진적 접근 방식은 더욱 원활한 전환과 지속적인 개선을 보장합니다.

결론

팀 코리의 리팩토링 예시는 단일 책임 원칙(SRP)을 준수하면 코드가 더 깔끔하고 유지보수하기 쉬워진다는 것을 보여줍니다. 개발자는 책임을 더 작고 집중적인 클래스로 분해함으로써 코드베이스 내의 가독성, 디버깅 및 협업을 개선할 수 있습니다. SOLID 디자인 패턴의 이러한 기본 원칙은 소프트웨어 개발에서 더욱 고급 원칙과 모범 사례를 위한 토대를 마련합니다.

더 자세한 정보와 코드 예제는 그의 영상을 시청하고, 그의 채널을 방문하여 더 많은 디자인 패턴 영상을 확인하세요.

Hero Worlddot related to C# 주요 디자인 패턴 마스터하기
Hero Affiliate related to C# 주요 디자인 패턴 마스터하기

사랑하는 것을 공유하여 더 많은 수익을 얻으세요

당신은 .NET, C#, Java, Python, 또는 Node.js를 다루는 개발자를 위한 콘텐츠를 만드나요? 당신의 전문성을 추가 수입으로 전환하세요!

아이언 서포트 팀

저희는 주 5일, 24시간 온라인으로 운영합니다.
채팅
이메일
전화해