이 자습서에서는 콘솔 애플리케이션을 빌드하고 C# 언어의 일부인 기본 개체 지향 기능을 확인합니다.
필수 조건
- 최신 .NET SDK
- Visual Studio Code 편집기
- C# 개발 키트
설치 지침
Windows에서 이 WinGet 구성 파일을 사용하여 모든 필수 구성 요소를 설치합니다. 이미 설치된 항목이 있는 경우 WinGet은 해당 단계를 건너뜁니다.
- 파일을 다운로드하고 두 번 클릭하여 실행합니다.
- 사용권 계약을 읽고, y입력하고, 동의하라는 메시지가 표시되면 Enter 키를 선택합니다.
- 작업 표시줄에 깜박이는 UAC(사용자 계정 컨트롤) 프롬프트가 표시되면 설치를 계속하도록 허용합니다.
다른 플랫폼에서는 이러한 각 구성 요소를 별도로 설치해야 합니다.
- .NET SDK 다운로드 페이지권장 설치 관리자를 다운로드하고 두 번 클릭하여 실행합니다. 다운로드 페이지에서 플랫폼을 검색하고 플랫폼에 대한 최신 설치 관리자를 권장합니다.
- Visual Studio Code 홈페이지에서 최신 설치 관리자를 다운로드하고 두 번 클릭하여 실행합니다. 그 페이지는 또한 플랫폼을 탐지하며, 링크는 시스템에 맞게 정확할 것입니다.
- C# DevKit 확장 페이지에서 "설치" 단추를 클릭합니다. 그러면 Visual Studio 코드가 열리고 확장을 설치하거나 사용하도록 설정할지 묻습니다. "설치"를 선택합니다.
애플리케이션 만들기
터미널 창을 사용하여 클래스라는 디렉터리를 만듭니다. 거기에 애플리케이션을 빌드할 것입니다. 해당 디렉터리로 변경하고 콘솔 창에 dotnet new console
을 입력합니다. 이 명령은 애플리케이션을 만듭니다.
Program.cs를 엽니다. 다음과 같이 표시됩니다.
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
이 자습서에서는 은행 계좌를 나타내는 새로운 형식을 만듭니다. 일반적으로 개발자는 여러 텍스트 파일에 각 클래스를 정의합니다. 그러면 프로그램의 크기가 커질 때 쉽게 관리할 수 있습니다. 클래스 디렉터리에 BankAccount.cs라는 이름의 새 파일을 만듭니다.
이 파일에는 은행 계좌의 정의가 포함됩니다. 개체 지향 프로그래밍은 클래스 형태로 형식을 생성하여 코드를 구성합니다. 이러한 클래스에는 특정 엔티티를 나타내는 코드가 포함됩니다.
BankAccount
클래스는 은행 계좌를 나타냅니다. 코드는 메서드 및 속성을 통해 특정 작업을 구현합니다. 이 자습서에서 은행 계좌는 다음 동작을 지원합니다.
- 은행 계좌를 고유하게 식별하는 10자리 숫자가 있습니다.
- 소유자의 이름을 저장하는 문자열이 있습니다.
- 잔액을 검색할 수 있습니다.
- 예금을 허용합니다.
- 인출을 허용합니다.
- 초기 잔액은 양수여야 합니다.
- 인출로 인해 잔액이 음수가 될 수 없습니다.
은행 계좌 형식 정의
동작을 정의하는 클래스의 기본 사항을 만들어 시작할 수 있습니다. File:New 명령을 사용하여 새 파일을 만듭니다. 파일의 이름을 BankAccount.cs로 지정합니다. BankAccount.cs 파일에 다음 코드를 추가합니다.
namespace Classes;
public class BankAccount
{
public string Number { get; }
public string Owner { get; set; }
public decimal Balance { get; }
public void MakeDeposit(decimal amount, DateTime date, string note)
{
}
public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
}
}
계속하기 전에 빌드한 내용을 살펴보겠습니다.
namespace
선언은 코드를 논리적으로 구성하는 방법을 제공합니다. 이 자습서는 비교적 작으므로 하나의 네임스페이스에 모든 코드를 넣습니다.
public class BankAccount
(은)는 생성하는 클래스 또는 형식을 정의합니다. 클래스 선언 뒤에 오는 {
및 }
의 모든 항목은 클래스의 상태와 동작을 정의합니다.
클래스의 가 다섯 개 있습니다. 첫 번째 세 개는 속성입니다. 속성은 데이터 요소이며 유효성 검사 또는 기타 규칙을 적용하는 코드가 있을 수 있습니다. 마지막 두 개는 메서드입니다. 메서드는 단일 함수를 수행하는 코드 블록입니다. 각 멤버의 이름을 읽으면 사용자 또는 다른 개발자가 클래스가 수행하는 작업을 이해하기에 충분한 정보를 제공해야 합니다.
새 계좌 개설
구현할 첫 번째 기능은 은행 계좌 개설 기능입니다. 고객이 계좌를 개설할 때 초기 잔액과 해당 계좌 소유자에 대한 정보를 제공해야 합니다.
BankAccount
형식의 새 개체를 생성하는 것은 해당 값을 지정하는 생성자를 정의하는 것입니다.
생성자는 클래스와 이름이 같은 멤버입니다. 해당 클래스 형식의 개체를 초기화하는 데 사용됩니다.
BankAccount
형식에 다음 생성자를 추가합니다.
MakeDeposit
선언 위에 다음 코드를 배치합니다.
public BankAccount(string name, decimal initialBalance)
{
this.Owner = name;
this.Balance = initialBalance;
}
앞의 코드는 this
한정자를 포함하여 생성되는 개체의 속성을 식별합니다. 해당 한정자는 일반적으로 선택 사항이며 생략됩니다. 다음을 작성할 수도 있습니다.
public BankAccount(string name, decimal initialBalance)
{
Owner = name;
Balance = initialBalance;
}
this
한정자는 지역 변수 또는 매개 변수의 이름이 해당 필드 또는 속성과 같은 경우에만 필요합니다. 필요한 경우가 아니면 이 문서의 나머지 부분에서 this
한정자는 생략됩니다.
생성자는 new
를 사용하여 개체를 만들 때 호출됩니다.
Console.WriteLine("Hello World!");
의 줄을 다음 코드로 바꿉니다(<name>
을 사용자의 이름으로 바꿈).
using Classes;
var account = new BankAccount("<name>", 1000);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");
지금까지 빌드한 코드를 실행해 보겠습니다. Visual Studio를 사용하는 경우 디버그 메뉴에서 디버깅하지 않고 시작을 선택합니다. 명령줄을 사용하는 경우 프로젝트를 만든 디렉터리에 dotnet run
을 입력합니다.
계좌 번호가 공백인 것을 눈치채셨나요? 이를 수정해 보겠습니다. 개체가 생성될 때 계좌 번호를 지정해야 합니다. 그러나 그것을 만드는 것은 발신자의 책임이 아닙니다.
BankAccount
클래스 코드는 새 계좌 번호를 지정하는 방법을 알아야 합니다. 간단한 방법은 10자리 숫자로 시작하는 것입니다. 새 계좌가 생성될 때마다 숫자가 늘어납니다. 마지막으로, 개체가 생성될 때 현재 계좌 번호를 저장합니다.
BankAccount
클래스에 멤버 선언을 추가합니다.
{
클래스의 시작 부분에서 여는 중괄호 BankAccount
뒤에 다음 코드 줄을 배치합니다.
private static int s_accountNumberSeed = 1234567890;
accountNumberSeed
(은)는 데이터 멤버입니다. 이것은 private
입니다. 즉 BankAccount
클래스 내의 코드로만 액세스할 수 있습니다. 이는 전용 구현(계좌 번호가 생성되는 방법)과 공공 책임(계좌 번호를 가지는 것 등)을 구분하는 방법입니다. 또한 static
로서 모든 BankAccount
개체에서 공유됨을 의미합니다. 비정적 변수의 값은 BankAccount
개체의 각 인스턴스에 고유합니다.
accountNumberSeed
(은)는 private static
필드이므로 C# 명명 규칙에 따라 s_
접두사를 가집니다.
s
(은)는 static
(을)를 나타내고 _
(은)는 private
필드를 나타냅니다. 생성자에 다음 두 줄을 추가하여 계좌 번호를 지정합니다.
this.Balance = initialBalance
줄 뒤에 배치합니다.
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
dotnet run
을 입력하여 결과를 확인합니다.
예금 및 인출 만들기
은행 계좌 클래스는 제대로 작동하려면 예금과 인출을 허용해야 합니다. 계좌의 모든 트랜잭션에 대한 저널을 만들어 예금과 인출을 구현하겠습니다. 모든 트랜잭션을 추적하는 것은 단순히 각 트랜잭션의 잔액을 업데이트하는 것보다 몇 가지 이점이 있습니다. 기록을 사용하여 모든 트랜잭션을 감사하고 일별 잔액을 관리할 수 있습니다. 필요한 경우 모든 트랜잭션의 기록에서 잔액을 계산하면 고정된 단일 트랜잭션의 오류가 다음 계산의 잔액에 올바르게 반영되도록 할 수 있습니다.
트랜잭션을 나타내는 새 형식을 생성해 보겠습니다. 트랜잭션은 책임이 없는 간단한 형식입니다. 몇 가지 속성이 필요합니다. Transaction.cs라는 새 파일을 만듭니다. 파일에 다음 코드를 추가합니다.
namespace Classes;
public class Transaction
{
public decimal Amount { get; }
public DateTime Date { get; }
public string Notes { get; }
public Transaction(decimal amount, DateTime date, string note)
{
Amount = amount;
Date = date;
Notes = note;
}
}
이제 List<T> 개체의 Transaction
를 BankAccount
클래스에 추가하겠습니다.
BankAccount.cs 파일의 생성자 뒤에 다음 선언을 추가합니다.
private List<Transaction> _allTransactions = new List<Transaction>();
이제 Balance
를 올바르게 계산해 보겠습니다. 모든 트랜잭션의 값을 합하여 현재 잔액을 찾을 수 있습니다. 코드가 현재 상태이기 때문에 계정의 초기 잔액만 가져올 수 있으므로 Balance
속성을 업데이트해야 합니다.
public decimal Balance { get; }
의 줄을 다음 코드로 바꿉니다.
public decimal Balance
{
get
{
decimal balance = 0;
foreach (var item in _allTransactions)
{
balance += item.Amount;
}
return balance;
}
}
이 예제에서는 속성의 중요한 측면을 보여 줍니다. 이제 다른 프로그래머가 값을 요청할 때 잔액을 계산합니다. 계산은 모든 트랜잭션을 열거하고 합계를 현재 잔액으로 제공합니다.
다음으로 MakeDeposit
및 MakeWithdrawal
메서드를 구현합니다. 이러한 메서드는 마지막 두 규칙을 적용합니다. 초기 잔액은 양수여야 하며 인출은 음수 잔액을 생성하면 안 됩니다.
이러한 규칙은 예외의 개념을 소개합니다. 메서드가 작업을 성공적으로 완료할 수 없음을 나타내는 표준 방법은 예외를 throw하는 것입니다. 예외 형식 및 관련 메시지는 오류를 설명합니다. 여기서 MakeDeposit
메서드는 입금 금액이 0보다 크지 않으면 예외를 throw합니다.
MakeWithdrawal
메서드는 인출 금액이 0보다 크지 않거나 인출을 적용하면 음수 잔액이 발생하는 경우 예외를 throw합니다.
_allTransactions
목록의 선언 뒤에 다음 코드를 추가합니다.
public void MakeDeposit(decimal amount, DateTime date, string note)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount of deposit must be positive");
}
var deposit = new Transaction(amount, date, note);
_allTransactions.Add(deposit);
}
public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
}
if (Balance - amount < 0)
{
throw new InvalidOperationException("Not sufficient funds for this withdrawal");
}
var withdrawal = new Transaction(-amount, date, note);
_allTransactions.Add(withdrawal);
}
throw
문은 예외를 던집니다. 현재 블록의 실행이 종료되고 제어가 호출 스택에 있는 처음 일치하는 catch
블록으로 전달됩니다.
catch
블록을 추가하여 나중에 이 코드를 테스트합니다.
생성자는 잔액을 직접 업데이트하지 않고 초기 트랜잭션을 추가하도록 변경해야 합니다.
MakeDeposit
메서드를 이미 작성했으므로 생성자에서 호출합니다. 완성된 생성자는 다음과 같아야 합니다.
public BankAccount(string name, decimal initialBalance)
{
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
Owner = name;
MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}
DateTime.Now은 현재 날짜 및 시간을 반환하는 속성입니다. 새 Main
(을)를 만드는 코드에 따라 BankAccount
메서드에 몇 가지 입금 및 인출을 추가하여 이 코드를 테스트합니다.
account.MakeWithdrawal(500, DateTime.Now, "Rent payment");
Console.WriteLine(account.Balance);
account.MakeDeposit(100, DateTime.Now, "Friend paid me back");
Console.WriteLine(account.Balance);
다음으로, 계정을 잔액이 음수로 설정하여 오류 상태를 감지할 수 있는지 테스트합니다. 방금 추가한 이전 코드 뒤에 다음 코드를 추가합니다.
// Test that the initial balances must be positive.
BankAccount invalidAccount;
try
{
invalidAccount = new BankAccount("invalid", -55);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("Exception caught creating account with negative balance");
Console.WriteLine(e.ToString());
return;
}
try-catch
문을 사용하여 예외를 발생시킬 수 있는 코드 블록을 표시하고 예상한 오류를 잡습니다. 동일한 기술을 사용하여 음수 잔액에 대해 예외를 발생시키는 코드를 테스트할 수 있습니다.
invalidAccount
메서드에서 Main
(을)를 선언하기 전에 다음 코드를 추가합니다.
// Test for a negative balance.
try
{
account.MakeWithdrawal(750, DateTime.Now, "Attempt to overdraw");
}
catch (InvalidOperationException e)
{
Console.WriteLine("Exception caught trying to overdraw");
Console.WriteLine(e.ToString());
}
파일을 저장하고 dotnet run
을 입력하여 시도해 보세요.
과제 - 모든 트랜잭션 기록
이 자습서를 완료하기 위해 트랜잭션 기록에 대해 GetAccountHistory
을 생성하는 string
메서드를 작성할 수 있습니다.
BankAccount
형식에 이 메서드를 추가합니다.
public string GetAccountHistory()
{
var report = new System.Text.StringBuilder();
decimal balance = 0;
report.AppendLine("Date\t\tAmount\tBalance\tNote");
foreach (var item in _allTransactions)
{
balance += item.Amount;
report.AppendLine($"{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
}
return report.ToString();
}
기록은 StringBuilder 클래스를 사용하여 각 트랜잭션에 대해 한 줄이 포함된 문자열의 형식을 지정합니다. 이러한 자습서의 앞부분에서 문자열 형식 지정 코드를 살펴보았습니다. 새 문자는 \t
입니다. 해당 항목은 출력을 형식화하기 위해 탭을 삽입합니다.
Program.cs에서 테스트하려면 이 줄을 추가합니다.
Console.WriteLine(account.GetAccountHistory());
프로그램을 실행하여 결과를 확인합니다.
다음 단계
잘 알 수 없는 경우 GitHub 리포지토리에서 이 자습서의 소스를 확인할 수 있습니다.
개체 지향 프로그래밍 자습서를 계속 진행할 수 있습니다.
다음 문서에서 이러한 개념을 더 자세히 알아볼 수 있습니다.
.NET