자습서: C# 콘솔 앱 확장 및 Visual Studio에서 디버그(2/2부)

이 자습서 시리즈의 2부에서는 날마다 수행하는 개발 작업에 필요한 Visual Studio 빌드 및 디버그 기능을 좀 더 자세히 살펴봅니다. 해당 기능에는 여러 프로젝트 관리, 디버깅, 타사 패키지 참조가 포함됩니다. 자습서 1부에서 만든 C# 콘솔 앱을 실행하고 Visual Studio IDE(통합 개발 환경)의 몇 가지 기능을 살펴봅니다. 이 자습서는 2부로 구성된 자습서 시리즈 중 제2부입니다.

이 자습서에서는 다음 작업을 완료합니다.

  • 두 번째 프로젝트를 추가합니다.
  • 라이브러리를 참조하고 패키지를 추가합니다.
  • 코드를 디버그합니다.
  • 완성된 코드를 검사합니다.

필수 조건

이 문서를 진행하려면 다음 계산기 앱 중 하나를 사용합니다.

다른 프로젝트 추가

실제 코드에는 한 솔루션에서 함께 작동하는 여러 프로젝트가 포함됩니다. 일부 계산기 함수를 제공하는 클래스 라이브러리 프로젝트를 계산기 앱에 추가할 수 있습니다.

Visual Studio에서 파일>추가>새 프로젝트 메뉴 명령을 사용하여 새 프로젝트를 추가합니다. 솔루션 탐색기에서 솔루션을 마우스 오른쪽 단추로 클릭하고 상황에 맞는 메뉴에서 프로젝트를 추가할 수도 있습니다.

  1. 솔루션 탐색기에서 솔루션 노드를 마우스 오른쪽 단추로 클릭하고 추가>프로젝트를 선택합니다.

  2. 새 프로젝트 추가 창의 검색 상자에 ‘클래스 라이브러리’를 입력합니다. C# 클래스 라이브러리 프로젝트 템플릿을 선택하고 다음을 선택합니다.

    Screenshot of Class Library project template selection.

  3. 새 프로젝트 구성 화면에서 프로젝트 이름으로 CalculatorLibrary를 입력하고 다음을 선택합니다.

  4. 메시지가 표시되면 .NET 3.1을 선택합니다. Visual Studio에서 새 프로젝트를 만들어 솔루션에 추가합니다.

    Screenshot of Solution Explorer with the CalculatorLibrary class library project added.

  5. Class1.cs 파일의 이름을 CalculatorLibrary.cs로 바꿉니다. 파일 이름을 바꾸려면 솔루션 탐색기 이름을 마우스 오른쪽 단추로 클릭하고 이름 바꾸기를 선택하고 이름을 선택하고 F2 키를 누르거나 이름을 선택하고 다시 선택하여 입력할 수 있습니다.

    파일에서 Class1 참조의 이름을 바꿀지 여부를 묻는 메시지가 표시될 수도 있습니다. 이후 단계에서 코드를 바꿀 예정이므로 여기서는 어떤 선택을 해도 됩니다.

  6. 이제 첫 번째 프로젝트가 새 클래스 라이브러리를 통해 노출되는 API를 사용할 수 있도록 프로젝트 참조를 추가합니다. 계산기 프로젝트의 종속성 노드를 마우스 오른쪽 단추로 클릭하고 프로젝트 참조 추가를 선택합니다.

    Screenshot of the Add Project Reference menu item.

    참조 관리자 대화 상자가 나타납니다. 이 대화 상자에서 프로젝트에 필요한 다른 프로젝트, 어셈블리, COM DLL에 대한 참조를 추가할 수 있습니다.

  7. 참조 관리자 대화 상자에서 CalculatorLibrary 프로젝트의 확인란을 선택하고 확인을 선택합니다.

    Screenshot of the Reference Manager dialog box.

    프로젝트 참조는 솔루션 탐색기프로젝트 노드 아래에 나타납니다.

    Screenshot of Solution Explorer with project reference.

  8. Program.cs에서 Calculator 클래스와 해당 코드를 모두 선택하고 Ctrl+X를 눌러 잘라냅니다. 그런 다음, CalculatorLibrary.csCalculatorLibrary 네임스페이스에 코드를 붙여넣습니다.

    또한 Calculator 클래스 앞에 public을 추가하여 라이브러리 외부에 클래스를 노출합니다.

    이제 CalculatorLibrary.cs는 다음 코드와 유사합니다.

    using System;
    
     namespace CalculatorLibrary
     {
         public class Calculator
         {
             public static double DoOperation(double num1, double num2, string op)
             {
                 double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
    
                 // Use a switch statement to do the math.
                 switch (op)
                 {
                     case "a":
                         result = num1 + num2;
                         break;
                     case "s":
                         result = num1 - num2;
                         break;
                     case "m":
                         result = num1 * num2;
                         break;
                     case "d":
                         // Ask the user to enter a non-zero divisor.
                         if (num2 != 0)
                         {
                             result = num1 / num2;
                         }
                         break;
                     // Return text for an incorrect option entry.
                     default:
                         break;
                 }
                 return result;
             }
         }
     }
    
  9. Program.cs 참조도 있지만 호출이 Calculator.DoOperation 해결되지 않는다는 오류가 발생합니다. 오류는 다른 네임스페이스에 있기 때문 CalculatorLibrary 입니다. 정규화된 참조를 위해 다음과 같이 Calculator.DoOperation 호출에 CalculatorLibrary 네임스페이스를 추가할 수 있습니다.

    result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    또는 다음과 같이 파일 시작 부분에 using 지시문을 추가할 수 있습니다.

    using CalculatorLibrary;
    

    using 지시문을 추가하면 호출 사이트에서 CalculatorLibrary 네임스페이스를 제거할 수 있지만, 이제 모호성이 생깁니다. CalculatorCalculatorLibrary의 클래스인가요, 아니면 Calculator가 네임스페이스인가요?

    모호성을 해결하려면 Program.cs에서 네임스페이스의 이름을 Calculator에서 CalculatorProgram으로 바꿉니다.

    namespace CalculatorProgram
    
  1. 솔루션 탐색기에서 솔루션 노드를 마우스 오른쪽 단추로 클릭하고 추가>프로젝트를 선택합니다.

  2. 새 프로젝트 추가 창의 검색 상자에 ‘클래스 라이브러리’를 입력합니다. C# 클래스 라이브러리 프로젝트 템플릿을 선택하고 다음을 선택합니다.

    Screenshot of Class Library project template selection.

  3. 새 프로젝트 구성 화면에서 프로젝트 이름으로 CalculatorLibrary를 입력하고 다음을 선택합니다.

  4. 추가 정보 화면에서 .NET 8.0이 선택됩니다. 만들기를 실행합니다.

    Visual Studio에서 새 프로젝트를 만들어 솔루션에 추가합니다.

    Screenshot of Solution Explorer with the CalculatorLibrary class library project added.

  5. Class1.cs 파일의 이름을 CalculatorLibrary.cs로 바꿉니다. 파일 이름을 바꾸려면 솔루션 탐색기 이름을 마우스 오른쪽 단추로 클릭하고 이름 바꾸기를 선택하고 이름을 선택하고 F2 키를 누르거나 이름을 선택하고 다시 선택하여 입력할 수 있습니다.

    파일에서 모든 Class1 참조의 이름을 바꿀지 여부를 묻는 메시지가 표시될 수도 있습니다. 이후 단계에서 코드를 바꿀 예정이므로 여기서는 어떤 선택을 해도 됩니다.

  6. 이제 첫 번째 프로젝트가 새 클래스 라이브러리를 통해 노출되는 API를 사용할 수 있도록 프로젝트 참조를 추가합니다. 계산기 프로젝트의 종속성 노드를 마우스 오른쪽 단추로 클릭하고 프로젝트 참조 추가를 선택합니다.

    Screenshot of the Add Project Reference menu item.

    참조 관리자 대화 상자가 나타납니다. 이 대화 상자에서 프로젝트에 필요한 다른 프로젝트, 어셈블리, COM DLL에 대한 참조를 추가할 수 있습니다.

  7. 참조 관리자 대화 상자에서 CalculatorLibrary 프로젝트의 확인란을 선택하고 확인을 선택합니다.

    Screenshot of the Reference Manager dialog box.

    프로젝트 참조는 솔루션 탐색기프로젝트 노드 아래에 나타납니다.

    Screenshot of Solution Explorer with project reference.

  8. Program.cs에서 Calculator 클래스와 해당 코드를 모두 선택하고 Ctrl+X를 눌러 잘라냅니다. 그런 다음, CalculatorLibrary.csCalculatorLibrary 네임스페이스에 코드를 붙여넣습니다.

    또한 Calculator 클래스 앞에 public을 추가하여 라이브러리 외부에 클래스를 노출합니다.

    이제 CalculatorLibrary.cs는 다음 코드와 유사합니다.

     namespace CalculatorLibrary
     {
         public class Calculator
         {
             public static double DoOperation(double num1, double num2, string op)
             {
                 double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
    
                 // Use a switch statement to do the math.
                 switch (op)
                 {
                     case "a":
                         result = num1 + num2;
                         break;
                     case "s":
                         result = num1 - num2;
                         break;
                     case "m":
                         result = num1 * num2;
                         break;
                     case "d":
                         // Ask the user to enter a non-zero divisor.
                         if (num2 != 0)
                         {
                             result = num1 / num2;
                         }
                         break;
                     // Return text for an incorrect option entry.
                     default:
                         break;
                 }
                 return result;
             }
         }
     }
    
  9. Program.cs 참조도 있지만 호출이 Calculator.DoOperation 해결되지 않는다는 오류가 발생합니다. 오류는 다른 네임스페이스에 있기 때문 CalculatorLibrary 입니다. 정규화된 참조를 위해 다음과 같이 Calculator.DoOperation 호출에 CalculatorLibrary 네임스페이스를 추가할 수 있습니다.

    result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    또는 다음과 같이 파일 시작 부분에 using 지시문을 추가할 수 있습니다.

    using CalculatorLibrary;
    

    using 지시문을 추가하면 호출 사이트에서 CalculatorLibrary 네임스페이스를 제거할 수 있지만, 이제 모호성이 생깁니다. CalculatorCalculatorLibrary의 클래스인가요, 아니면 Calculator가 네임스페이스인가요?

    모호성을 해결하려면 Program.cs에서 네임스페이스의 이름을 Calculator에서 CalculatorProgram으로 바꿉니다.

    namespace CalculatorProgram
    

.NET 라이브러리 참조: 로그에 쓰기

.NET Trace 클래스를 사용하여 모든 작업의 로그를 추가하고 텍스트 파일에 쓸 수 있습니다. Trace 클래스는 기본 출력 디버깅 기술에도 유용합니다. Trace 클래스는 System.Diagnostics에 있으며, StreamWriter와 같은 System.IO 클래스를 사용합니다.

  1. 먼저 CalculatorLibrary.cs의 맨 위에 using 지시문을 추가합니다.

    using System.IO;
    using System.Diagnostics;
    
  2. Trace 클래스를 이렇게 사용하려면 파일 스트림과 연결된 클래스 참조를 유지해야 합니다. 이 요구 사항에 따라 계산기가 개체로 작동하는 것이 더 효율적이므로 CalculatorLibrary.cs에서 Calculator 클래스의 시작 부분에 생성자를 추가합니다.

    또한 static 키워드를 제거하여 정적 DoOperation 메서드를 멤버 메서드로 변경합니다.

    public Calculator()
       {
           StreamWriter logFile = File.CreateText("calculator.log");
           Trace.Listeners.Add(new TextWriterTraceListener(logFile));
           Trace.AutoFlush = true;
           Trace.WriteLine("Starting Calculator Log");
           Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
       }
    
    public double DoOperation(double num1, double num2, string op)
       {
    
  3. 각 계산에 로그 출력을 추가합니다. 이제 DoOperation은 다음 코드와 유사합니다.

    public double DoOperation(double num1, double num2, string op)
    {
         double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
    
         // Use a switch statement to do the math.
         switch (op)
         {
             case "a":
                 result = num1 + num2;
                 Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
                 break;
             case "s":
                 result = num1 - num2;
                 Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
                 break;
             case "m":
                 result = num1 * num2;
                 Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
                 break;
             case "d":
                 // Ask the user to enter a non-zero divisor.
                 if (num2 != 0)
                 {
                     result = num1 / num2;
                     Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
                 }
                     break;
             // Return text for an incorrect option entry.
             default:
                 break;
         }
         return result;
     }
    
  4. Program.cs로 돌아가면 빨간색 물결선 밑줄이 정적 호출에 플래그를 지정합니다. 오류를 수정하려면 while (!endApp) 루프 바로 앞에 다음 코드 줄을 추가하여 calculator 변수를 만듭니다.

    Calculator calculator = new Calculator();
    

    또한 DoOperation 호출 사이트를 수정하여 소문자로 명명된 calculator 개체를 참조합니다. 이제 코드는 정적 메서드 호출이 아닌 멤버 호출입니다.

    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. 앱을 다시 실행합니다. 작업을 마쳤으면 계산기 프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 파일 탐색기에서 폴더 열기를 선택합니다.

  6. 파일 탐색기에서 bin/Debug/ 아래의 출력 폴더로 이동한 다음, calculator.log 파일을 엽니다. 출력은 다음과 비슷합니다.

    Starting Calculator Log
    Started 7/9/2020 1:58:19 PM
    1 + 2 = 3
    3 * 3 = 9
    

이제 CalculatorLibrary.cs는 다음 코드와 유사합니다.

using System;
using System.IO;
using System.Diagnostics;

namespace CalculatorLibrary
{
    public class Calculator
    {

        public Calculator()
        {
            StreamWriter logFile = File.CreateText("calculator.log");
            Trace.Listeners.Add(new TextWriterTraceListener(logFile));
            Trace.AutoFlush = true;
            Trace.WriteLine("Starting Calculator Log");
            Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
        }

        public double DoOperation(double num1, double num2, string op)
        {
            double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.

            // Use a switch statement to do the math.
            switch (op)
            {
                case "a":
                    result = num1 + num2;
                    Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
                    break;
                case "s":
                    result = num1 - num2;
                    Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
                    break;
                case "m":
                    result = num1 * num2;
                    Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
                    break;
                case "d":
                    // Ask the user to enter a non-zero divisor.
                    if (num2 != 0)
                    {
                        result = num1 / num2;
                        Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
                    }
                    break;
                // Return text for an incorrect option entry.
                default:
                    break;
            }
            return result;
        }
    }
}

Program.cs는 다음 코드와 유사합니다.

using System;
using CalculatorLibrary;

namespace CalculatorProgram
{

    class Program
    {
        static void Main(string[] args)
        {
            bool endApp = false;
            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            Calculator calculator = new Calculator();
            while (!endApp)
            {
                // Declare variables and set to empty.
                string numInput1 = "";
                string numInput2 = "";
                double result = 0;

                // Ask the user to type the first number.
                Console.Write("Type a number, and then press Enter: ");
                numInput1 = Console.ReadLine();

                double cleanNum1 = 0;
                while (!double.TryParse(numInput1, out cleanNum1))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput1 = Console.ReadLine();
                }

                // Ask the user to type the second number.
                Console.Write("Type another number, and then press Enter: ");
                numInput2 = Console.ReadLine();

                double cleanNum2 = 0;
                while (!double.TryParse(numInput2, out cleanNum2))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput2 = Console.ReadLine();
                }

                // Ask the user to choose an operator.
                Console.WriteLine("Choose an operator from the following list:");
                Console.WriteLine("\ta - Add");
                Console.WriteLine("\ts - Subtract");
                Console.WriteLine("\tm - Multiply");
                Console.WriteLine("\td - Divide");
                Console.Write("Your option? ");

                string op = Console.ReadLine();

                try
                {
                    result = calculator.DoOperation(cleanNum1, cleanNum2, op); 
                    if (double.IsNaN(result))
                    {
                        Console.WriteLine("This operation will result in a mathematical error.\n");
                    }
                    else Console.WriteLine("Your result: {0:0.##}\n", result);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
                }

                Console.WriteLine("------------------------\n");

                // Wait for the user to respond before closing.
                Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
                if (Console.ReadLine() == "n") endApp = true;

                Console.WriteLine("\n"); // Friendly linespacing.
            }
            return;
        }
    }
}

.NET Trace 클래스를 사용하여 모든 작업의 로그를 추가하고 텍스트 파일에 쓸 수 있습니다. Trace 클래스는 기본 출력 디버깅 기술에도 유용합니다. Trace 클래스는 System.Diagnostics에 있으며, StreamWriter와 같은 System.IO 클래스를 사용합니다.

  1. 먼저 CalculatorLibrary.cs의 맨 위에 using 지시문을 추가합니다.

    using System.Diagnostics;
    
  2. Trace 클래스를 이렇게 사용하려면 파일 스트림과 연결된 클래스 참조를 유지해야 합니다. 이 요구 사항에 따라 계산기가 개체로 작동하는 것이 더 효율적이므로 CalculatorLibrary.cs에서 Calculator 클래스의 시작 부분에 생성자를 추가합니다.

    또한 static 키워드를 제거하여 정적 DoOperation 메서드를 멤버 메서드로 변경합니다.

    public Calculator()
       {
           StreamWriter logFile = File.CreateText("calculator.log");
           Trace.Listeners.Add(new TextWriterTraceListener(logFile));
           Trace.AutoFlush = true;
           Trace.WriteLine("Starting Calculator Log");
           Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
       }
    
    public double DoOperation(double num1, double num2, string op)
       {
    
  3. 각 계산에 로그 출력을 추가합니다. 이제 DoOperation은 다음 코드와 유사합니다.

    public double DoOperation(double num1, double num2, string op)
    {
         double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
    
         // Use a switch statement to do the math.
         switch (op)
         {
             case "a":
                 result = num1 + num2;
                 Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
                 break;
             case "s":
                 result = num1 - num2;
                 Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
                 break;
             case "m":
                 result = num1 * num2;
                 Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
                 break;
             case "d":
                 // Ask the user to enter a non-zero divisor.
                 if (num2 != 0)
                 {
                     result = num1 / num2;
                     Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
                 }
                     break;
             // Return text for an incorrect option entry.
             default:
                 break;
         }
         return result;
     }
    
  4. Program.cs로 돌아가면 빨간색 물결선 밑줄이 정적 호출에 플래그를 지정합니다. 오류를 수정하려면 while (!endApp) 루프 바로 앞에 다음 코드 줄을 추가하여 calculator 변수를 만듭니다.

    Calculator calculator = new Calculator();
    

    또한 DoOperation 호출 사이트를 수정하여 소문자로 명명된 calculator 개체를 참조합니다. 이제 코드는 정적 메서드 호출이 아닌 멤버 호출입니다.

    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. 앱을 다시 실행합니다. 작업을 마쳤으면 계산기 프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 파일 탐색기에서 폴더 열기를 선택합니다.

  6. 파일 탐색기에서 bin/Debug/ 아래의 출력 폴더로 이동한 다음, calculator.log 파일을 엽니다. 출력은 다음과 비슷합니다.

    Starting Calculator Log
    Started 7/9/2020 1:58:19 PM
    1 + 2 = 3
    3 * 3 = 9
    

이제 CalculatorLibrary.cs는 다음 코드와 유사합니다.

using System.Diagnostics;

namespace CalculatorLibrary
{
    public class Calculator
    {

        public Calculator()
        {
            StreamWriter logFile = File.CreateText("calculator.log");
            Trace.Listeners.Add(new TextWriterTraceListener(logFile));
            Trace.AutoFlush = true;
            Trace.WriteLine("Starting Calculator Log");
            Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
        }

        public double DoOperation(double num1, double num2, string op)
        {
            double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.

            // Use a switch statement to do the math.
            switch (op)
            {
                case "a":
                    result = num1 + num2;
                    Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
                    break;
                case "s":
                    result = num1 - num2;
                    Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
                    break;
                case "m":
                    result = num1 * num2;
                    Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
                    break;
                case "d":
                    // Ask the user to enter a non-zero divisor.
                    if (num2 != 0)
                    {
                        result = num1 / num2;
                        Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
                    }
                    break;
                // Return text for an incorrect option entry.
                default:
                    break;
            }
            return result;
        }
    }
}

Program.cs는 다음 코드와 유사합니다.

using CalculatorLibrary;

namespace CalculatorProgram
{

    class Program
    {
        static void Main(string[] args)
        {
            bool endApp = false;
            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            Calculator calculator = new Calculator();
            while (!endApp)
            {
                // Declare variables and set to empty.
                string numInput1 = "";
                string numInput2 = "";
                double result = 0;

                // Ask the user to type the first number.
                Console.Write("Type a number, and then press Enter: ");
                numInput1 = Console.ReadLine();

                double cleanNum1 = 0;
                while (!double.TryParse(numInput1, out cleanNum1))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput1 = Console.ReadLine();
                }

                // Ask the user to type the second number.
                Console.Write("Type another number, and then press Enter: ");
                numInput2 = Console.ReadLine();

                double cleanNum2 = 0;
                while (!double.TryParse(numInput2, out cleanNum2))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput2 = Console.ReadLine();
                }

                // Ask the user to choose an operator.
                Console.WriteLine("Choose an operator from the following list:");
                Console.WriteLine("\ta - Add");
                Console.WriteLine("\ts - Subtract");
                Console.WriteLine("\tm - Multiply");
                Console.WriteLine("\td - Divide");
                Console.Write("Your option? ");

                string op = Console.ReadLine();

                try
                {
                    result = calculator.DoOperation(cleanNum1, cleanNum2, op); 
                    if (double.IsNaN(result))
                    {
                        Console.WriteLine("This operation will result in a mathematical error.\n");
                    }
                    else Console.WriteLine("Your result: {0:0.##}\n", result);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
                }

                Console.WriteLine("------------------------\n");

                // Wait for the user to respond before closing.
                Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
                if (Console.ReadLine() == "n") endApp = true;

                Console.WriteLine("\n"); // Friendly linespacing.
            }
            return;
        }
    }
}

NuGet 패키지 추가: JSON 파일에 쓰기

개체 데이터를 저장하기 위한 인기 있고 이식 가능한 형식인 JSON으로 작업을 출력하려면 Newtonsoft.Json NuGet 패키지를 참조합니다. NuGet 패키지는 .NET 클래스 라이브러리의 기본 배포 방법입니다.

  1. 솔루션 탐색기에서 CalculatorLibrary 프로젝트의 종속성 노드를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.

    Screenshot of Manage NuGet Packages on the shortcut menu.

    Screenshot of Manage NuGet Packages on the shortcut menu.

    NuGet 패키지 관리자가 열립니다.

    Screenshot of the NuGet Package Manager.

  2. Newtonsoft.Json 패키지를 검색하여 선택한 다음, 설치를 선택합니다.

    Screenshot of Newtonsoft J SON NuGet package information in the NuGet Package Manager.

    Visual Studio에서 패키지를 다운로드하여 프로젝트에 추가합니다. 솔루션 탐색기의 참조 노드에 새 항목이 나타납니다.

    Screenshot of Newtonsoft J SON NuGet package information in the NuGet Package Manager. 변경 내용을 수락할지 여부를 묻는 메시지가 표시되면 확인을 선택합니다.

    Visual Studio에서 패키지를 다운로드하여 프로젝트에 추가합니다. 솔루션 탐색기패키지 노드에 새 항목이 나타납니다.

    CalculatorLibrary.cs의 시작 부분에 Newtonsoft.Json에 대한 using 지시문을 추가합니다.

    using Newtonsoft.Json;
    
  3. JsonWriter 멤버 개체를 만들고 Calculator 생성자를 다음 코드로 바꿉니다.

         JsonWriter writer;
    
         public Calculator()
         {
             StreamWriter logFile = File.CreateText("calculatorlog.json");
             logFile.AutoFlush = true;
             writer = new JsonTextWriter(logFile);
             writer.Formatting = Formatting.Indented;
             writer.WriteStartObject();
             writer.WritePropertyName("Operations");
             writer.WriteStartArray();
         }
    
  4. DoOperation 메서드를 수정하여 JSON writer 코드를 추가합니다.

         public double DoOperation(double num1, double num2, string op)
         {
             double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
             writer.WriteStartObject();
             writer.WritePropertyName("Operand1");
             writer.WriteValue(num1);
             writer.WritePropertyName("Operand2");
             writer.WriteValue(num2);
             writer.WritePropertyName("Operation");
             // Use a switch statement to do the math.
             switch (op)
             {
                 case "a":
                     result = num1 + num2;
                     writer.WriteValue("Add");
                     break;
                 case "s":
                     result = num1 - num2;
                     writer.WriteValue("Subtract");
                     break;
                 case "m":
                     result = num1 * num2;
                     writer.WriteValue("Multiply");
                     break;
                 case "d":
                     // Ask the user to enter a non-zero divisor.
                     if (num2 != 0)
                     {
                         result = num1 / num2;
                     }
                     writer.WriteValue("Divide");
                     break;
                 // Return text for an incorrect option entry.
                 default:
                     break;
             }
             writer.WritePropertyName("Result");
             writer.WriteValue(result);
             writer.WriteEndObject();
    
             return result;
         }
    
  5. 사용자가 작업 데이터 입력을 완료하면 JSON 구문을 마치기 위해 메서드를 추가합니다.

     public void Finish()
     {
         writer.WriteEndArray();
         writer.WriteEndObject();
         writer.Close();
     }
    
  6. Program.cs의 끝에 있는 return; 앞에 Finish 호출을 추가합니다.

             // Add call to close the JSON writer before return
             calculator.Finish();
             return;
         }
    
  7. 앱을 빌드하고 실행합니다. 몇 개의 작업 입력을 완료한 후 n 명령을 입력하여 앱을 닫습니다.

  8. 파일 탐색기에서 calculatorlog.json 파일을 엽니다. 다음과 유사한 내용이 표시됩니다.

    {
     "Operations": [
         {
         "Operand1": 2.0,
         "Operand2": 3.0,
         "Operation": "Add",
         "Result": 5.0
         },
         {
         "Operand1": 3.0,
         "Operand2": 4.0,
         "Operation": "Multiply",
         "Result": 12.0
         }
     ]
    }
    

디버그: 중단점 설정 및 적중

Visual Studio 디버거는 강력한 도구입니다. 디버거는 코드를 단계별로 실행하여 프로그래밍 오류가 있는 정확한 지점을 찾을 수 있습니다. 그러면 수정해야 하는 사항을 파악하고 앱을 계속 실행할 수 있도록 일시적으로 변경할 수 있습니다.

  1. Program.cs에서 다음 코드 줄 왼쪽 여백을 클릭합니다. 줄을 클릭하고 F9 키를 선택하거나 줄을 마우스 오른쪽 단추로 클릭하고 중단점>중단점 삽입을 선택할 수도 있습니다.

    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    표시되는 빨간색 점이 중단점을 나타냅니다. 중단점을 사용하여 앱을 일시 중지하고 코드를 검사할 수 있습니다. 코드에서 어떤 실행 줄에도 중단점을 설정할 수 있습니다.

    Screenshot that shows setting a breakpoint.

  2. 앱을 빌드하고 실행합니다. 계산에 사용할 값을 다음과 같이 입력합니다.

    • 첫 번째 숫자로 8을 입력합니다.
    • 두 번째 숫자로 0을 입력합니다.
    • 약간의 즐거움을 위해 연산자로 d를 입력합니다.

    앱 중단점을 만든 위치에서 일시 중지됩니다. 그러면 코드가 강조 표시되고 왼쪽에 노란색 포인터가 표시됩니다. 강조 표시된 코드는 아직 실행되지 않은 코드입니다.

    Screenshot of hitting a breakpoint

    이제 앱이 일시 중단된 상태로 애플리케이션 상태를 검사할 수 있습니다.

디버그: 변수 보기

  1. 강조 표시된 코드에서 cleanNum1op 같은 변수 위로 마우스를 가져갑니다. 두 변수의 현재 값(각각 8, d)이 DataTips에 표시됩니다.

    Screenshot that shows viewing a DataTip.

    디버그할 때 변수에 필요한 값이 들어 있는지 확인하는 것이 문제를 해결하는 데 중요한 경우가 많습니다.

  2. 아래쪽 창에서 지역 창을 확인합니다. 창이 닫혀 있는 경우 디버그>>지역을 선택하여 엽니다.

    지역 창에는 현재 범위에 있는 각 변수가 해당 값 및 형식과 함께 표시됩니다.

    Screenshot of the Locals window.

    Screenshot of the Locals window.

  3. 자동 창을 확인합니다.

    자동 창은 지역 창과 유사하지만 앱이 일시 중지된 현재 코드 줄 바로 앞과 뒤에 있는 변수를 보여 줍니다.

    참고 항목

    자동 창이 표시되지 않으면 Windows>자동 디버그>를 선택하여 엽니다.

그런 다음, 디버거에서 코드를 문 단위로 실행합니다. 이 동작을 ‘단계별 실행’이라고 합니다.

디버그: 단계별 코드 실행

  1. F11 키를 누르거나 디버그>한 단계씩 코드 실행을 선택합니다.

    한 단계씩 코드 실행 명령을 사용하면 앱은 현재 문을 실행하고 다음 실행 가능한 문(일반적으로 다음 코드 줄)으로 이동합니다. 왼쪽의 노란색 포인터는 항상 현재 문을 나타냅니다.

    Screenshot of step into command

    방금 Calculator 클래스의 DoOperation 메서드를 한 단계씩 실행했습니다.

  2. 프로그램 흐름을 계층적으로 보려면 호출 스택 창을 확인합니다. 창이 닫혀 있는 경우 디버그>>호출 스택을 선택하여 엽니다.

    Screenshot of the call stack

    이 뷰에는 노란색 포인터로 지정된 현재 Calculator.DoOperation 메서드가 표시됩니다. 두 번째 행에는 Program.csMain 메서드에서 해당 메서드를 호출한 함수가 표시됩니다.

    호출 스택 창에는 메서드와 함수가 호출되는 순서가 표시됩니다. 이 창의 바로 가기 메뉴에서 소스 코드로 이동 등의 많은 디버거 기능에 액세스할 수도 있습니다.

  3. 앱이 switch 문에서 일시 중지될 때까지 여러 번 F10 키를 누르거나 디버그>프로시저 단위 실행을 선택합니다.

    switch (op)
    {
    

    프로시저 단위 실행 명령은 현재 문이 함수를 호출하는 경우 디버거가 함수의 코드를 실행하고 함수가 반환될 때까지 실행을 일시 중단하지 않는다는 점을 제외하고 한 단계씩 코드 실행 명령과 비슷합니다. 특정 함수에 관심이 없는 경우 프로시저 단위 실행이 한 단계씩 코드 실행보다 더 빠릅니다.

  4. 앱이 다음 코드 줄에서 일시 중지되도록 F10 키를 한 번 더 누릅니다.

    if (num2 != 0)
    {
    

    이 코드는 0으로 나누기 사례를 검사합니다. 앱이 계속되면 일반적인 예외(오류)가 throw되지만 콘솔에서 실제 반환된 값을 보는 것과 같은 다른 작업을 시도할 수 있습니다. 한 가지 옵션은 ‘편집하며 계속하기’라는 디버거 기능을 사용하여 코드를 변경한 후 디버깅을 계속하는 것입니다. 그러나 실행 흐름을 일시적으로 수정하는 다른 트릭이 있습니다.

디버그: 일시적인 변경 내용 테스트

  1. 현재 if (num2 != 0) 문에서 일시 중지된 노란색 포인터를 선택하고 다음 문으로 끌어옵니다.

    result = num1 / num2;
    

    포인터를 여기로 끌면 앱이 if 문을 완전히 건너뛰므로 0으로 나눌 때 발생하는 결과를 확인할 수 있습니다.

  2. F10 키를 눌러 코드 줄을 실행합니다.

  3. result 변수를 마우스로 가리키면 무한대 값이 표시됩니다. C#에서 무한대는 0으로 나눌 때의 결과입니다.

  4. F5 키를 누르거나 디버그>디버깅 계속을 선택합니다.

    수학 연산의 결과로 무한대 기호가 콘솔에 표시됩니다.

  5. n 명령을 입력하여 앱을 정상적으로 닫습니다.

코드 완료

모든 단계를 완료한 후 CalculatorLibrary.cs 파일의 전체 코드는 다음과 같습니다.

using System;
using System.IO;
using Newtonsoft.Json;

namespace CalculatorLibrary
{
    public class Calculator
    {

        JsonWriter writer;

        public Calculator()
        {
            StreamWriter logFile = File.CreateText("calculatorlog.json");
            logFile.AutoFlush = true;
            writer = new JsonTextWriter(logFile);
            writer.Formatting = Formatting.Indented;
            writer.WriteStartObject();
            writer.WritePropertyName("Operations");
            writer.WriteStartArray();
        }

        public double DoOperation(double num1, double num2, string op)
        {
            double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
            writer.WriteStartObject();
            writer.WritePropertyName("Operand1");
            writer.WriteValue(num1);
            writer.WritePropertyName("Operand2");
            writer.WriteValue(num2);
            writer.WritePropertyName("Operation");
            // Use a switch statement to do the math.
            switch (op)
            {
                case "a":
                    result = num1 + num2;
                    writer.WriteValue("Add");
                    break;
                case "s":
                    result = num1 - num2;
                    writer.WriteValue("Subtract");
                    break;
                case "m":
                    result = num1 * num2;
                    writer.WriteValue("Multiply");
                    break;
                case "d":
                    // Ask the user to enter a non-zero divisor.
                    if (num2 != 0)
                    {
                        result = num1 / num2;
                    }
                    writer.WriteValue("Divide");
                    break;
                // Return text for an incorrect option entry.
                default:
                    break;
            }
            writer.WritePropertyName("Result");
            writer.WriteValue(result);
            writer.WriteEndObject();

            return result;
        }

        public void Finish()
        {
            writer.WriteEndArray();
            writer.WriteEndObject();
            writer.Close();
        }
    }
}

그리고 Program.cs의 코드는 다음과 같습니다.

using System;
using CalculatorLibrary;

namespace CalculatorProgram
{

    class Program
    {
        static void Main(string[] args)
        {
            bool endApp = false;
            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            Calculator calculator = new Calculator();
            while (!endApp)
            {
                // Declare variables and set to empty.
                string numInput1 = "";
                string numInput2 = "";
                double result = 0;

                // Ask the user to type the first number.
                Console.Write("Type a number, and then press Enter: ");
                numInput1 = Console.ReadLine();

                double cleanNum1 = 0;
                while (!double.TryParse(numInput1, out cleanNum1))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput1 = Console.ReadLine();
                }

                // Ask the user to type the second number.
                Console.Write("Type another number, and then press Enter: ");
                numInput2 = Console.ReadLine();

                double cleanNum2 = 0;
                while (!double.TryParse(numInput2, out cleanNum2))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput2 = Console.ReadLine();
                }

                // Ask the user to choose an operator.
                Console.WriteLine("Choose an operator from the following list:");
                Console.WriteLine("\ta - Add");
                Console.WriteLine("\ts - Subtract");
                Console.WriteLine("\tm - Multiply");
                Console.WriteLine("\td - Divide");
                Console.Write("Your option? ");

                string op = Console.ReadLine();

                try
                {
                    result = calculator.DoOperation(cleanNum1, cleanNum2, op); 
                    if (double.IsNaN(result))
                    {
                        Console.WriteLine("This operation will result in a mathematical error.\n");
                    }
                    else Console.WriteLine("Your result: {0:0.##}\n", result);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
                }

                Console.WriteLine("------------------------\n");

                // Wait for the user to respond before closing.
                Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
                if (Console.ReadLine() == "n") endApp = true;

                Console.WriteLine("\n"); // Friendly linespacing.
            }
            calculator.Finish();
            return;
        }
    }
}

모든 단계를 완료한 후 CalculatorLibrary.cs 파일의 전체 코드는 다음과 같습니다.

using Newtonsoft.Json;

namespace CalculatorLibrary
{
    public class Calculator
    {

        JsonWriter writer;

        public Calculator()
        {
            StreamWriter logFile = File.CreateText("calculatorlog.json");
            logFile.AutoFlush = true;
            writer = new JsonTextWriter(logFile);
            writer.Formatting = Formatting.Indented;
            writer.WriteStartObject();
            writer.WritePropertyName("Operations");
            writer.WriteStartArray();
        }

        public double DoOperation(double num1, double num2, string op)
        {
            double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
            writer.WriteStartObject();
            writer.WritePropertyName("Operand1");
            writer.WriteValue(num1);
            writer.WritePropertyName("Operand2");
            writer.WriteValue(num2);
            writer.WritePropertyName("Operation");
            // Use a switch statement to do the math.
            switch (op)
            {
                case "a":
                    result = num1 + num2;
                    writer.WriteValue("Add");
                    break;
                case "s":
                    result = num1 - num2;
                    writer.WriteValue("Subtract");
                    break;
                case "m":
                    result = num1 * num2;
                    writer.WriteValue("Multiply");
                    break;
                case "d":
                    // Ask the user to enter a non-zero divisor.
                    if (num2 != 0)
                    {
                        result = num1 / num2;
                    }
                    writer.WriteValue("Divide");
                    break;
                // Return text for an incorrect option entry.
                default:
                    break;
            }
            writer.WritePropertyName("Result");
            writer.WriteValue(result);
            writer.WriteEndObject();

            return result;
        }

        public void Finish()
        {
            writer.WriteEndArray();
            writer.WriteEndObject();
            writer.Close();
        }
    }
}

그리고 Program.cs의 코드는 다음과 같습니다.

using CalculatorLibrary;

namespace CalculatorProgram
{

    class Program
    {
        static void Main(string[] args)
        {
            bool endApp = false;
            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            Calculator calculator = new Calculator();
            while (!endApp)
            {
                // Declare variables and set to empty.
                string numInput1 = "";
                string numInput2 = "";
                double result = 0;

                // Ask the user to type the first number.
                Console.Write("Type a number, and then press Enter: ");
                numInput1 = Console.ReadLine();

                double cleanNum1 = 0;
                while (!double.TryParse(numInput1, out cleanNum1))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput1 = Console.ReadLine();
                }

                // Ask the user to type the second number.
                Console.Write("Type another number, and then press Enter: ");
                numInput2 = Console.ReadLine();

                double cleanNum2 = 0;
                while (!double.TryParse(numInput2, out cleanNum2))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput2 = Console.ReadLine();
                }

                // Ask the user to choose an operator.
                Console.WriteLine("Choose an operator from the following list:");
                Console.WriteLine("\ta - Add");
                Console.WriteLine("\ts - Subtract");
                Console.WriteLine("\tm - Multiply");
                Console.WriteLine("\td - Divide");
                Console.Write("Your option? ");

                string op = Console.ReadLine();

                try
                {
                    result = calculator.DoOperation(cleanNum1, cleanNum2, op); 
                    if (double.IsNaN(result))
                    {
                        Console.WriteLine("This operation will result in a mathematical error.\n");
                    }
                    else Console.WriteLine("Your result: {0:0.##}\n", result);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
                }

                Console.WriteLine("------------------------\n");

                // Wait for the user to respond before closing.
                Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
                if (Console.ReadLine() == "n") endApp = true;

                Console.WriteLine("\n"); // Friendly linespacing.
            }
            calculator.Finish();
            return;
        }
    }
}

다음 단계

축하합니다. 이 자습서를 마쳤습니다. 자세히 알아보려면 다음 콘텐츠를 살펴보세요.