Menu
Coddy logo textTech

Sealed 클래스

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 22번째.

때때로 상속 체인을 중단하고 싶을 때가 있습니다. sealed 키워드는 클래스가 상속되는 것을 방지하며, 다른 어떤 클래스도 이를 확장할 수 없도록 구현을 고정합니다.

public sealed class FinalDog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

// 이 코드는 컴파일 오류를 발생시킵니다:
// public class SuperDog : FinalDog { }

왜 클래스를 봉인(seal)할까요? 보안과 설계 무결성이 일반적인 이유입니다. 클래스를 신중하게 설계했고 상속을 통해 누군가가 그 동작을 변경하는 것을 원하지 않을 때, 클래스를 봉인하면 구현이 그대로 유지되도록 보장할 수 있습니다.

클래스 전체가 아닌 개별 메서드를 봉인할 수도 있습니다. 이렇게 하면 클래스 자체는 상속될 수 있도록 허용하면서도, 해당 메서드가 더 이상 오버라이딩되지 않도록 합니다:

public class Dog : Animal
{
    public sealed override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

public class Puppy : Dog
{
    // Speak()을 오버라이드할 수 없습니다 - sealed로 설정되어 있습니다
    // 하지만 새로운 메서드를 추가할 수 있습니다
    public void Whimper()
    {
        Console.WriteLine("Whimper...");
    }
}

메서드에서 sealed는 반드시 override와 결합되어야 한다는 점에 유의하세요. 부모 클래스에서 이미 가상(virtual)이었던 메서드만 봉인할 수 있습니다. 이를 통해 상속 계층 구조에서 일부 사용자 정의를 허용한 다음, 특정 지점에서 이를 고정하는 정밀한 제어가 가능합니다.

challenge icon

챌린지

쉬움

sealed를 사용하여 중요한 구현을 잠그는 방법을 보여주는 결제 처리 시스템을 구축해 보겠습니다. 특정 결제 수단은 확장할 수 있지만, 가장 보안이 중요한 수단은 수정을 방지하기 위해 봉인(sealed)되는 계층 구조를 만들 것입니다.

코드를 정리하기 위해 네 개의 파일을 생성합니다:

  • PaymentMethod.cs: Payments 네임스페이스에 PaymentMethod 클래스를 정의합니다. 이 기본 클래스는 Name 속성(string)과 이를 설정하는 생성자를 가집니다. "Processing ${amount} via {Name}"를 반환하는 virtual 메서드 ProcessPayment(decimal amount)를 포함하세요.
  • CreditCardPayment.cs: PaymentMethod를 상속받는 CreditCardPayment 클래스를 Payments 네임스페이스에 정의합니다. CardNumber 속성(string)을 추가합니다. 생성자는 이름과 카드 번호를 받으며, base를 사용하여 이름을 전달합니다. 더 이상의 파생 클래스가 이 동작을 변경할 수 없도록 sealed 키워드를 사용하여 ProcessPayment를 오버라이드합니다. 이 메서드는 "Charging ${amount} to card ending in {last 4 digits of CardNumber}"를 반환해야 합니다.
  • BankTransfer.cs: PaymentMethod를 상속받는 BankTransfer라는 sealed 클래스를 Payments 네임스페이스에 정의합니다. 이 클래스 전체가 봉인되어 아무도 이를 상속받을 수 없습니다. BankName 속호(string)를 추가합니다. 생성자는 이름과 은행 이름을 받습니다. ProcessPayment를 오버라이드하여 "Transferring ${amount} from {BankName}"를 반환하도록 합니다.
  • Program.cs: 메인 파일에서 입력 값을 사용하여 세 가지 결제 유형의 인스턴스를 모두 생성합니다. 이들을 PaymentMethod 참조로 저장하고 각각에 대해 ProcessPayment를 호출하여 봉인된 구현을 통한 다형성을 보여줍니다.

네 가지 입력을 받게 됩니다:

  • 신용카드 번호 (16자리)
  • 은행 이름
  • 결제 금액

각 메서드를 통해 결제를 처리한 결과를 출력하세요:

{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}

예를 들어, 입력값이 1234567890123456, First National, 150.00인 경우 출력은 다음과 같아야 합니다:

Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First National

기본 PaymentMethod 인스턴스의 이름으로는 "Generic"을, 신용카드 결제에는 "Credit Card"를, 은행 송금에는 "Bank Transfer"를 사용하세요.

CreditCardPayment의 봉인된 메서드는 누군가 하위 클래스를 만들더라도 신용카드 결제 처리 방식을 변경할 수 없도록 보장합니다. 봉인된 클래스 BankTransfer는 상속 자체를 방지합니다. 두 가지 모두 결제 시스템에서 중요한 보안 패턴입니다!

치트 시트

sealed 키워드는 클래스가 상속되는 것을 방지하거나, 상속 체인에서 메서드가 더 이상 오버라이드되지 않도록 합니다.

클래스 봉인하기:

public sealed class FinalDog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

// 이 코드는 컴파일 오류를 발생시킵니다:
// public class SuperDog : FinalDog { }

봉인된(Sealed) 클래스는 보안 및 설계 무결성을 위해 사용되며, 상속을 통해 구현이 수정되지 않도록 보장합니다.

메서드 봉인하기:

public class Dog : Animal
{
    public sealed override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

public class Puppy : Dog
{
    // Speak()를 오버라이드할 수 없습니다 - 봉인되어 있습니다
    // 하지만 새로운 메서드를 추가할 수는 있습니다
    public void Whimper()
    {
        Console.WriteLine("Whimper...");
    }
}

메서드에 사용하는 sealed 키워드는 반드시 override와 함께 사용해야 합니다. 부모 클래스에서 이미 virtual로 선언된 메서드만 봉인할 수 있습니다. 이를 통해 계층 구조의 특정 지점까지만 커스터마이징을 허용하고, 그 이후로는 잠글 수 있습니다.

직접 해보기

using System;
using Payments;

class Program
{
    public static void Main(string[] args)
    {
        // 입력 읽기
        string cardNumber = Console.ReadLine();
        string bankName = Console.ReadLine();
        decimal amount = Convert.ToDecimal(Console.ReadLine());
        
        // TODO: 이름이 "Generic"인 기본 PaymentMethod 인스턴스 생성
        // 이를 PaymentMethod 참조로 저장
        
        // TODO: 이름이 "Credit Card"이고 카드 번호를 포함하는 CreditCardPayment 인스턴스 생성
        // 이를 PaymentMethod 참조로 저장
        
        // TODO: 이름이 "Bank Transfer"이고 은행 이름을 포함하는 BankTransfer 인스턴스 생성
        // 이를 PaymentMethod 참조로 저장
        
        // TODO: 각각에 대해 ProcessPayment를 호출하고 결과를 출력
        // 각 결과는 별도의 줄에 출력되어야 함
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨