앞서 우리는 if문에 대해 배워보았습니다.

[C#] 프로그래밍의 꽃 if문

if문에서는 어떤 상황을 가정하며 상황에 맞는 결과를 출력 혹은 행동할 수 있게끔 프로그램을 이끌어가는 하나의 흐름으로 볼 수 있습니다.

switch문 또한 if문과는 조금 다른 형태의 흐름을 제어하는 제어문이라 볼 수 있습니다. 

 


switch문의 기본 형태

switch (조건의 대상이 되는 변수)
{
	case 조건값1 :
    	조건값1이 참일 경우 실행;
        break;
    case 조건값2 :
    	조건값1이 참일 경우 실행;
        break;
    case 조건값3 :
    	조건값1이 참일 경우 실행;
        break;
    case 조건값4 :
    	조건값1이 참일 경우 실행;
        break;
    default :
    	위의 조건값에 해당하지 않는 경우에 실행;
        break;	
}

의 기본 형태로 진행하고 있습니다. 조건은 꼭 조건4까지만 있는 것이 아닌 원하는 만큼 넣어도 됩니다.

 

  1. switch문은 case 별로 나누어서
  2. case에 해당하는 조건을 찾아간 다음, 그에 해당하는 행동요건을 실행합니다. 
  3. 그리고 중지! (break) 하는 형태를 이루고 있습니다.
  4. 위에서 원하는 조건(case)를 찾지 못한다면, 기본값(default)을 찾아서 실행하는 것이지요.

 

예시를 들어보도록 하겠습니다.

[1부터 5까지의 숫자를 한 개 입력받아서 실행하면 그에 따른 결과값을 출력하는 예제입니다.]

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(String[] args)
        {
        	int input = 0;
            
            Console.Write("1~5까지의 숫자를 입력하세요 : ");
            input = Convert.ToInt32(Console.ReadLine());
            
            switch(input)
            {
            	case 1:
                Console.WriteLine("1을 입력하셨군요!");
                break;
                case 2:
                Console.WriteLine("2를 입력하셨군요!");
                break;
                case 3:
                Console.WriteLine("3을 입력하셨군요!");
                break;
                case 4:
                Console.WriteLine("4를 입력하셨군요!");
                break;
                case 5:
                Console.WriteLine("5를 입력하셨군요!");
                break;
                default:
                Console.WriteLine("1 ~ 5 외의 숫자를 입력하셨습니다!");
                break;
            }
        }
    }
}


 

실제 입력을 해서 확인해보면, 3을 입력하면 -> 3을 입력하셨군요! 라는 문장이 나오게 됩니다. 

 

여기서는 지금까지와 다른 한 문장이 추가되었는데, 바로 Convert.ToInt32(Console.ReadLine()); 입니다.

C#은 프로그래머가 쓰기 좋게 만들어져서 이러한 형태처럼 해석을 하기 편하게 만들어져 있습니다. 처음보면 당황할 수 있지만, 조금만 익숙해진다면 그 무엇보다 편하게 쓸 수 있습니다.

Convert.ToInt32(Console.ReadLine()) 하나씩 해석해보도록 하겠습니다.

 

  1. 괄호에서 제일 안쪽, 오른쪽부터 해석합니다. 
  2. Console.ReadLine() -> 한 줄을 읽겠다. (Console에 들어있는 기능 중 하나인 REadLine 기능을 쓰려고 한다.)
  3. 2번에서 읽어들인 것을 ToInt32 (Int 형으로, 즉 정수형으로), Convert (변환하겠다.) // Convert.ToInt32 를 한번에 해석해서 변환하려고 하는데, To Int32인 정수형으로 변환하고자 합니다. 정도로 해석할 수 있겠습니다.

앞으로 볼 내용들도 대부분 이러한 형태를 가지게 됩니다. 복잡한 형태라도 대부분이 이러한 형태를 가지고 있는데, 이 부분은 나중에 다시 설명하도록 하겠습니다. 지금은 입력받는 것은 문자열이지만, 입력받는 것은 정수형으로 받아야하므로 바뀐다는 사실만 알아두시면 됩니다. 

 


그렇다면 만약에 break문을 안쓰게 되면 어떻게 될까요?

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(String[] args)
        {
        	int input = 0;
            
            Console.Write("1~5까지의 숫자를 입력하세요 : ");
            input = Convert.ToInt32(Console.ReadLine());
            
            switch(input)
            {
            	case 1:
                Console.WriteLine("1을 입력하셨군요!");
                break;
                case 2:
                Console.WriteLine("2를 입력하셨군요!");
                
                case 3:
                Console.WriteLine("3을 입력하셨군요!");
                
                case 4:
                Console.WriteLine("4를 입력하셨군요!");
                
                case 5:
                Console.WriteLine("5를 입력하셨군요!");
                
                default:
                Console.WriteLine("1 ~ 5 외의 숫자를 입력하셨습니다!");
                break;
            }
        }
    }
}

이러한 형태로 말이죠.

여기에 3을 입력한다면, 결과는 

3을 입력하셨군요!

4를 입력하셨군요!

5를 입력하셨군요!

1 ~ 5 외의 숫자를 입력하셨습니다. 

가 나오게 될 것입니다.

 

여기서 우리는 break가 특정 case에 따른, 결과값을 break해준다는 사실을 볼 수 있습니다.

 


위와 같은 결과를 활용하면 이러한 형태를 만들 수도 있습니다.

 

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(String[] args)
        {
        	int input = 83;
            
            Console.WriteLine("당신의 성적은 ");
            switch(input)
            {
            	case 100: case 99: case 98: case 97: case 96: case 95: case 94: case 93:
                case 92: case 91:
                Console.WriteLine("A입니다.");
                break;
                case 90: case 89: case 88: case 87: case 86: case 85: case 84:
                case 83: case 82: case 81:
                Console.WriteLine("B입니다.");
                break;
                case 80: case 79: case 78: case 77: case 76: case 75: case 74: case 73:
                case 72: case 71:
                Console.WriteLine("C입니다.");
                break;
                case 70: case 69: case 68: case 67: case 66: case 65: case 64: case 63:
                case 62: case 61:
                Console.WriteLine("D입니다.");
                break;                
                default:
                Console.WriteLine("F입니다.");
                break;
            }
        }
    }
}

다양한 case를 활용해서 성적처리 프로그램을 만들었습니다. 단점은 너무 코드를 쓸 양이 많아진다는 점과 실수를 처리하기에는 부적절할 수도 있습니다. 그래서 위와 같은 코드는 if절로 처리하는 것이 더 효율적입니다.  <조금 더 정확하게는 간단한 상수값을 통한 비교라면 switch문이 효율적이지만, 복합적인 상황 혹은 여러 가지 조합을 이용한 비교라면 if문이 유리합니다.>

 

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(String[] args)
        {
        	int input = 83;
            
            Console.WriteLine("당신의 성적은 ");
            
            if ( input > 90 || input <= 100)
            {
                Console.WriteLine("A입니다.");
            }
            else if ( input > 80 || input <= 90)
            {
            	Console.WriteLine("B입니다.");
            }
            else if ( input > 70 || input <= 80 )
            {
            	Console.WriteLine("C입니다.");
            }
            else if ( input > 60 || input <= 70)
            {
            	Console.WriteLine("D입니다.");
            }
            else 
            {
            	Console.WriteLine("F입니다.");
            }
                        
        }
    }
}

switch문으로 일일히 쓰는 것보다 if문을 활용한 범위를 쓰는 것이 더 깔끔합니다. 물론 더 다양한 형태로 사용할 수도 있습니다. 위의 코드는 하나의 예시일 뿐입니다. 

if문과 switch문의 차이는 위와 같은 차이점이 있으며 보통 정수 혹은 딱딱 떨어지는 스위치를 누르는 것과 비슷하게 적용한다면 switch문이 유리하며, 특정 범위와 같은 범주라면 if문이 유리합니다. 

이는 프로그래머의 성향일 뿐 정해진 답은 없습니다. 

다만 좋은 프로그래밍은 누가보더라도 '가독성'이 좋은 코드입니다. 한 눈에 읽히는 것이 가장 좋습니다.

 

항상 중요한 것은 실습을 통해 둘 사이의 차이점을 이해하고 적절한 상황에 적절한 도구를 사용하는 것이겠죠?

우리가 프로그래밍을 구현함에 있어서 가장 중요한 점 중 하나는 특정 상황에 따라 맞는 행동을 해주는 것입니다. 그럴 때 사용하는 것이 바로 if문입니다.

if 문은 영어의 뜻 그대로 "만약 ~ 한다면" 이라는 의미를 가지고 있습니다.

하나를 예로 들어보도록 하죠.

- 만약에 인터넷을 더블클릭 한다면 -> 인터넷이 시작될 것이다.

- 만약에 키보드 엔터 버튼을 누른다면 -> 다음 줄로 넘어갈 것이다.

이러한 형태로 만약 ~한다면, ~할 것이다. 라는 형태를 이루고 있습니다. 

 

우리가 사용하고 있는 대부분의 프로그램 또한 무언가를 클릭하면 무언가가 선택이 되거나 그에 맞는 행동을 하기 마련입니다. 

 

if 문의 일반적인 사용 방법

if ( 우리가 원하는 조건, 10보다 큰 숫자라면 )
{
	10보다 큰 숫자가 왔을 때의 행동
	Console.WriteLine("나는 10보다 크다!");
}

의 형태처럼 사용할 수 있습니다. 

위의 코드를 조금 더 자세한 코드로 바꾸어서 이야기하자면, 

if ( num > 10 ) 
{
	Console.WriteLine("10보다 큰 숫자입니다!");
}

num이라는 숫자가 10보다 크다면, { } 안의 코드가 실행되는 형태입니다. 만약에 num > 10 이 거짓이라면 { } 안의 코드는 실행되지 않습니다. 

 

자 그래서 if 문의 기본 형태는 

if ( 참(true)인지 거짓(false)) 
{
	참이면 실행되는 명령
}

의 형태를 이루고 있습니다. 여기서 용어의 통일을 위해서 우리는 { } 괄호 안의 감싸져 있는

코드를 '코드 블럭(Code Block)'이라고 부릅니다.

 

그리고 if ( 이곳에 있는 문장을 '조건식' ) 이라 합니다. 

 

if문의 형태는 기본적으로 조건식을 검사한 뒤에, 나머지 코드 블럭에 있는 문장을 실행하는 형태로 코드를 진행하고 있습니다. 

 


간단한 실습

자 그럼 위에서 배운 대로 간단한 실습을 해보도록 하겠습니다.

 

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(string[] args)
        {
        	int num = 10;
            
            if ( num == 8 ) 
            {
            	Console.WriteLine("이곳은 출력되지 않는 공간입니다.");
            }
        }
    }
}

위의 코드는 if문을 활용하는 하나의 코드 형태입니다. if문 안에 있는 코드블럭은 실행되지 않습니다. 

조건식에 들어있는 문장이 false(거짓)이기 때문이죠.

 

위의 코드를 같은 의미이지만 다른 형태로 바꾸어보면 이렇게 바꾸어 볼 수도 있습니다.

using System;

namespace MyFirstApp
{
	class Program
    {
    	static void Main(string[] args)
        {
        	int num = 10;
            
            if ( false ) 
            {
            	Console.WriteLine("이곳은 출력되지 않는 공간입니다.");
            }
        }
    }
}

조건식 안에 있는 문장 자체를 false로 바꾸어주었습니다. 

 

원래 num == 8 (여기서 == 는 두 개의 값이 같은지 틀린지를 확인한 뒤에, true와 false를 반환해줍니다.) 에서 false를 반환받아서 최종적으로는

if ( false )의 형태가 됩니다.  

 

if문은 중첩해서 사용할 수 있으므로 if문의 코드블럭 안에 다시 if문을 무한정으로 할 수 있다는 장점이 있습니다. 초반에 프로그래밍에 잘 모를 때는 여러가지 방법을 이용해서 나에게 주어진 문제를 풀어보면서 프로그래밍을 익혀나가야 하기에, 효율적인 프로그래밍 보다는 여러 방식으로 도전해보는 것을 추천드립니다.

 


if와 else if 그리고 else

자 그렇다면, 사람이 조건식을 판단할 때는, 단순하게 만약에 ~ 하다면 -> 하지만 그렇지 않다면 ~ 할거다.

라는 논리를 이야기합니다. 이러한 논리적 흐름을 C#에서도 마찬가지로 구현해두었습니다.

 

그것이 바로 if와 else if 그리고 else문 입니다.

 

먼저, if와 else

if와 else문은 조건식이 참이라면, if 문 안에 있는 코드블럭을 실행하고, 그렇지 않다면 else문을 실행하는 형태입니다. 둘 중에 하나는 무조건 실행되는 구조라 생각할 수 있습니다. 

 

if (조건식)
{
	실행할 코드
}
else 
{ 
	실행할 코드
}

이러한 기본 형태로 사용합니다.

 - if문이 실행안되면, else문이 실행되며

 - if문이 실행되면, else문은 실행이 되지 않는 구조입니다. 

 

if 가 참(true)이면 if문을 실행한다. / if문이 거짓이라면 else 문을 실행한다. 

의 형태로 진행되는 것이지요. 그래서 else문에는 조건식이 없습니다. if문이 아니라면 실행될 뿐인거죠.

 


if - else if - else

우리의 현실 세계는 if - else 문 만으로는 표현할 수 없습니다. 조금 더 복잡한 조건을 표현할 녀석이 필요한 것이지요. 그래서 존재하는 것이 if - else if - else 입니다.

 

먼저, 예제 코드부터 보도록 하겠습니다.

int number = 75;

if ( number > 90)
{
	Console.WriteLine("A입니다.");
}
else if ( number < 90 && number >= 80 )
{
	Console.WriteLine("B입니다.");
}
else if ( number < 80 && number >= 70 )
{
	Console.WriteLine("C입니다.");
}
else if ( number < 70 && number >= 60 )
{
	Console.WriteLine("D입니다.");
}
else 
{
	Console.WriteLine("F입니다.");
}

입력된 성적을 바탕으로 성적을 산출하는 프로그램입니다.  현재 성적은 75점으로 if문을 거쳐서 else if문을 계속해서 체크하며 C 성적까지 가고 있습니다.

 

여기서 조건식은 number < 80 && number >=70 는 80점 미만, 70점 이상을 의미하며, 

부등식으로 사용되었던, 70 <= number < 80을 의미합니다. 고등학교 1학년 때 배운, 부등식을 말로 표현한 형태입니다. 주로 사이에 있는 값은 && (그리고 혹은 and)값으로 표현해서 나타냅니다.

 

만약, number > 80 || number < 70 이면 (여기서 작대기처럼 보이는( || ) 것은 or(또는)을 의미합니다.

80보다 크고, 70보다 작다를 의미합니다. 

 

else if문은 if문이 실행안되면, else if문에 있는 조건식을 체크하고 -> 참이면 코드블럭을 실행, 거짓이면 다음 else if문의 조건식을 확인하는 형태로 진행하고 있습니다. 

모든 조건식이 거짓을 가리키고 있으면 기본으로 else문에 있는 코드블럭을 실행하는 형태인 것이죠.

 

 

 

이렇게 기본 형태를 가질 수 있는 것이죠. 여기서 if - else if - else 문은 당연히 중첩된 구조를 가질 수 있으며 다양한 형태로 활용이 가능합니다.

다음 시간에는 switch문의 활용에 대해 살펴보도록 하겠습니다. 감사합니다.

 

지난번에는 visual studio에서 설정하는 것만 확인했다.

[OpenCV]OpenCV란 무엇인가? 그리고 설치

 

[OpenCV]OpenCV란 무엇인가? 그리고 설치

1. OpenCV OpenCV는 Open Source Computer Vision의 약자로 영상 처리에 사용할 수 있는 오픈 소스 라이브러리 입니다. 컴퓨터가 사람의 눈처럼 인식할 수 있게 처리해주는 역할을 하기도 하며, 우리가 많이

studium-anywhere.tistory.com

이러한 방식은 간단하게 설정하고 하나의 프로젝트에서 써먹기 좋다. 하지만 여러 프로젝트를 진행해야 하는 상황이라면 지속적으로 lib를 복사해야 하는 귀찮음이 있을 수 있다. (물론 위에서 제시한 방법도 진행해보고 환경변수 설정을 활용한 방법도 진행해봄으로써 실제 상용 프로그램을 만들 때 필요한 프로그램이 무엇인가를 아는 것도 중요하다.)

 


시스템 환경변수 설정

  컴퓨터 시스템은 스스로 할 수 있는 일이 별로 없다. 그래서 우리가 하나하나 알려줘야 한다. "어? 지금까지는 컴퓨터가 알아서 해줬는데요?" 그것은 다른 프로그래머가 하나하나 설정해준 것이므로 이제 우리도 하나하나 할 때가 되었다. 

  무언가를 시작할 때, 어디에 있는지 알아야 시작하지 않겠는가? 그것이 바로 환경 변수이다. 이름이 환경변수인 이유는 영어를 직역해서 그렇다. (Environment variable)

 

윈도우10 기준으로 제어판 -> 시스템으로 들어가자.

그곳에서 시스템 정보를 클릭

 

이렇게 생긴 모습이 나올 것이며, 거기서 고급 시스템 설정을 눌러주자.

 

딱 대놓고 환경 변수라고 나와있다.

 

  하나의 계정에다가만 만들 것이 아니라 시스템 자체에 추가하려고 하니 '시스템 변수'에다가 추가하도록 하겠다. (위, 아래 칸 두 개가 있다고 당황하지 말자, 위에는 특정 사용자에 해당하는 변수 설정이며, 아래에는 시스템 전체에 해당하는 변수이다. 위에다가 해도 상관없지만, 윈도우 계정 로그아웃 시 환경변수 설정을 다시 해야 한다.)  새로 만들기를 눌러준다.

 

앞으로의 세 개를 연달아 입력해준다. 다만 필자는 C에다가 압축해제를 해두었으므로 C:\opencv를 놓은 것이니 만약에 D라면 D:\opencv로 진행하도록 하자. 혹은 폴더에 두는 사람도 있는데 

D:\opencv\opencv의 형태가 있을 수도 있다. 본인의 상황에 맞는 것으로 하자.

 

필자의 상황은 이러하다. C:\opencv 가 바로 있다.

각각의 변수에다가 폴더의 경로를 저장해주는 형태이다. 

 


그 다음으로 시스템 변수 - path를 더블 클릭하자.

 

그리고서 아까 저장한 OPENCV_DIR 를 아래 처럼 입력해도록 하자. 

아까 우리는 OPENCV_DIR = C:\opencv를 저장해두었으니, 아래에 경로명에는 

C:\opencv\build\x64\vc15\bin 의 경로가 생성되는 것이다.

우리는 컴퓨터에게 친절하게 알려줄 필요가 있다. 엄마! 내 OO 어딨어!?

 

확인을 누르고 나면 기본적인 환경설정은 마무리가 되었다.

 

이제 제대로 설정이 되었는가를 확인하기 위해 CMD (명령 프롬프트)를 켜도록 하자. 윈도우 10이라면 아래에 검색에서 cmd라고 치면 나온다.

 

제대로 설치가 되었다면 버전이 나온다. 그렇지 않다면 찾을 수 없다는 메시지가 나올 것이다.

 


Visual Studio 설정하기

프로젝트 -> [프로젝트 이름] 의 속성으로 가보자

*필자는 프로젝트 이름을 opencv_project라 지은 것이다.

 


1. 

창이 열리고 나면, C/C++ → 일반 탭에서 

추가 포함 디렉터리에다가 $(OPENCV_INC)

아까 시스템 변수에 저장된 값을 지정해주도록 해야 합니다.

 

 

2. 

그 다음으로 링커 → 일반에서 추가 라이브러리 디렉터리에

$(OPENCV_LIB)

를 입력하도록 합니다. 이 변수도 위에서 시스템 변수에서 적용한 값이죠.

 

3. 마지막으로 링커 → 입력에서 추가 종속성을 입력해주도록 합니다.

 

추가 종속성

lib를 연결해주는 역할을 합니다. 여기서 주의하실 점은 opencv의 버전에 따라 이름이 조금씩 다릅니다.

C:\opencv\build\x64\vc15\lib

저의 설치 경로는 이렇게 설치했으며, 직접 가셔서 확인해보는 것이 가장 좋습니다.

두 개가 있으며, d가 붙은 버전과 아닌 버전이 있는데, d가 붙어있으면 debug이며, 안 붙어있는 lib는 release 모드에서 사용됩니다. 둘 다 입력해주도록 합니다.

 

이렇게 확인을 누르면 완료가 됩니다.

 

 

 

 

컴퓨터는 문자와 숫자를 구분하지 못한다. 그렇다면 문자로 등록되어 있는 문자열을 숫자로 바꿀 수 있는가? 답은 그렇다 이다. 

 

string a = "12";

라는 값이 있다고 생각해보자. 이는 분명히 문자열입니다. 12라는 문자열이죠. 이를 다른 숫자와 더하려고 한다면 문제가 생깁니다.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "12";
            string b = "13";

            Console.WriteLine(a+b);
            
        }
    }
}

 

  위의 코드를 입력하고 실행해보시면, 출력 결과로 1213를 받을 수 있습니다. 문자열로 선언된 두 개의 문자열이기 때문에 우리가 원하는 결과인 12+13 = 25의 결과를 받지 못한 것 입니다. 그렇다면 어떻게 12와 13을 숫자로 바꿔야 할까요?

 

  여기에 대한 답은 Parse를 쓰거나 ToInt32 메소드를 사용하는 것 입니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "12";
            string b = "13";

            Console.WriteLine(a+b);

            int c = int.Parse(a);
            int d = int.Parse(b);

            Console.WriteLine(c + d);

            int e = Convert.ToInt32(a);
            int f = Convert.ToInt32(b);

            Console.WriteLine(e + f);
            
        }
    }
}

실행해보면 우리가 원하는 값인 25를 얻을 수 있습니다. 문자열을 숫자로 바꿀 수 있는 것처럼 숫자를 문자열로도 바꿀 수 있습니다. 

 

만약 a 문자열 변수에 12가 아닌 "안녕?" 을 넣으면 어떻게 될까요? 

답은 실행은 되지만 오류가 납니다.

 

 

이러한 형태로 말이죠!
(물론! 입력타입을 검사해서 적합한 타입이면 정수형으로 바꾸어주는 메소드가 존재합니다.)

 

추가로 현재 변수가 가지는 데이터형을 알아내는 메소드도 있습니다.

 

GetType()

GetType() 메소드를 활용하면 현재의 데이터 형이 무엇인지 바로 알 수 있습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10;
            float b = 10.32f;
            string c = "abc";

            Console.WriteLine("a의 데이터 형은 : {0}", a.GetType());
            Console.WriteLine("b의 데이터 형은 : {0}", b.GetType());
            Console.WriteLine("c의 데이터 형은 : {0}", c.GetType());

        }
    }
}

이렇게 메소드를 활용하면 각각의 형태를 하나씩 출력해보면서 확인해볼 수 있습니다. 

이 외에도 문자열을 다룰 수 있는 여러 메소드가 존재합니다. IndexOf(), LastIndexOf(), Contains(), ToLower(), ToUpper() 등이 있지만 이러한 부분도 뒤에 가서 다루도록 하겠습니다. 

# 추가 실습으로 우리가 배운 데이터 형을 선언해보고 각각의 형을 확인해보시길 바랍니다. 


Const는 상수

변수를 선언할 때, const를 붙여서 선언하게 되면, 한번 선언한 뒤에 변경이 안되는 상수가 됩니다.

const double pi = 3.14;

우리가 pi라는 형태는 3.14를 많이 사용합니다. 이러한 값을 사용할 때, 단순히 값으로만 입력하면 무엇을 의도했는지 잘 모르겠죠? 그래서 프로그램의 가독성을 위해서 그리고 실수로라도 값을 바꿀 것에 대비해서 const 상수형을 사용하는 것입니다. 여기에 추가로 전체 파일에 대한 수정을 해야 한다고 한다면, 위의 상수 하나만 교체해주면, 프로그램 내부에 있는 모든 상수값이 교체가 됩니다! 

 

실제로 상수 값을 변경하려고 한다면 컴파일에서 컴파일 오류가 납니다. 컴파일러가 상수를 변경할 수 있는 실수를 대신 막아주는 것이죠!

 


Enum 열거형

열거형은 말 그대로 값들을 나열해주는 역할을 합니다. 상수가 많아지거나 선택지가 생기게 될 수도 있습니다. 실제 상수를 너무 많이 사용하게 되면 실제로 실수하는 경우가 생깁니다. 이러한 실수는 컴파일러가 오류를 발생시켜 막아주지도 못합니다. 그렇기에 열거형을 통해 자동으로 값을 배정받도록 하는 것이지요.

 

enum 변수명 {상수1, 상수2, 상수3, ...};

의 형태로 선언합니다. 상수1은 0이라는 숫자를 입력받으며, 상수2는 1의 숫자를 받습니다. 하나씩 증가하면서 각각의 수를 매칭된다고 생각하면 됩니다.

 

어떻게 출력되는가에 대한 예제를 살펴보로독 하겠습니다.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        enum RequestResult { DONE, FAILED, REJECT, APPROVAL };  // 상수는 일반적으로 대문자로 선언합니다.

        static void Main(string[] args)
        {
            Console.WriteLine((int)RequestResult.DONE);
            Console.WriteLine((int)RequestResult.FAILED);
            Console.WriteLine((int)RequestResult.REJECT);
            Console.WriteLine((int)RequestResult.APPROVAL);

        }
    }

 

위의 코드를 실행시켜보면 0, 1, 2, 3의 값이 출력으로 나옵니다. 

 

그러면 여기서 (int)를 빼고 출력해보면 어떻게 될까요?

using System;

namespace MyFirstApp
{
    class Program
    {
        enum RequestResult { DONE, FAILED, REJECT, APPROVAL };  // 상수는 일반적으로 대문자로 선언합니다.

        static void Main(string[] args)
        {
            Console.WriteLine(RequestResult.DONE);
            Console.WriteLine(RequestResult.FAILED);
            Console.WriteLine(RequestResult.REJECT);
            Console.WriteLine(RequestResult.APPROVAL);

        }
    }
}

결과는 DONE, FAILED, REJECT, APPROVAL의 형태로 출력됩니다! 

 

그런데 항상 값이 0부터 시작해서 1씩 증가해야 하는 값이어야 하는가 하면 그렇지 않습니다. 원하는 값으로 배정할 수 있습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        enum RequestResult { DONE = 100, FAILED = 200, REJECT = 300, APPROVAL = 400 };  // 상수는 일반적으로 대문자로 선언합니다.

        static void Main(string[] args)
        {
            Console.WriteLine((int)RequestResult.DONE);
            Console.WriteLine((int)RequestResult.FAILED);
            Console.WriteLine((int)RequestResult.REJECT);
            Console.WriteLine((int)RequestResult.APPROVAL);

        }
    }
}

이러한 형태로 값을 따로따로 배정하면서 사용할 수도 있습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        enum RequestResult { DONE = 100, FAILED = 200, REJECT = 300, APPROVAL = 400 };  // 상수는 일반적으로 대문자로 선언합니다.

        static void Main(string[] args)
        {
            RequestResult myResult = RequestResult.DONE;

            Console.WriteLine(myResult == RequestResult.DONE);
            Console.WriteLine(myResult == RequestResult.FAILED);
            Console.WriteLine(myResult == RequestResult.REJECT);
            Console.WriteLine(myResult == RequestResult.APPROVAL);

        }
    }
}

실제 사용은 이러한 형태로 사용됩니다. 우리가 원하는 결과 값을 저장해두고 그러한 값이 온다면 실행시켜주는 형태로 진행하는 것이죠. 여기서 사용된 == 은 같은 값인지 아닌지를 확인시켜주는 비교 연산자입니다. 결과가 같으면 true를 아니면 false를 반환합니다. 

앞서 데이터 형과 변수 (상)편에 이은 포스팅입니다. 이전 글에 대한 정보는

[C# 입문] 데이터 형과 변수 (상) 에 있습니다.

 


문자를 다루는 방법에는 문자 자체를 다루는 문자 형식과 문자로 이루어진 문자열 형식으로 문자들을 다룰 수 있습니다. char과 string입니다. 

char

문자 형 char는 1개의 문자를 다루는 데이터 형입니다. 우리가 Hello, World를 입력한다고 생각합시다. 그러면 char형으로는 하나하나 입력해주어야 하는 것이죠.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            char a = 'H';
            char b = 'e';
            char c = 'l';
            char d = 'l';
            char e = 'o';
            char f = ',';
            char g = ' ';
            char h = 'W';
            char i = 'o';
            char j = 'r';
            char k = 'l';
            char l = 'd';

            Console.Write(a);
            Console.Write(b);
            Console.Write(c);
            Console.Write(d);
            Console.Write(e);
            Console.Write(f);
            Console.Write(g);
            Console.Write(h);
            Console.Write(i);
            Console.Write(j);
            Console.Write(k);
            Console.WriteLine(l);

        }
    }
}

 이렇게 하나의 문자를 담을 수 있는 것을 char형이라 합니다. 이러한 char형을 모아두면 string이라고 하는 문자열이 됩니다.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "Hello, World!";

            Console.WriteLine(a);

        }
    }
}

 

위의 문자로 이루어진 복잡한 형태가 문자열로 깔끔한 형태로 바꾸어주었습니다. 이러한 문자열 string은 정해진 데이터 범위가 없습니다. 데이터가 저장될 수 있는 양이 그때마다 다르기 때문이죠. 

 

그렇다면 여기서 또 생각해볼 수 있는 형태가 있습니다. 만약 문자열과 문자열을 더하게 되면 어떻게 될까요?

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "Hello, World!";
            string b = " 안녕 세상!";
            string c;

            c = a + b;

            Console.WriteLine(c);

        }
    }
}

출력 결과를 확인하면

 

두 개의 문자열이 더해져서 나오는 것을 확인할 수 있습니다. 이렇듯 문자열은 다양한 상태로 사용될 수 있습니다. 그렇다면 여기서 문자의 갯수를 세고 싶다면 어떻게 해야할까요? 직접 하나하나 세야 할까요? 그렇지 않습니다. 우리에게는 강력한 메소드가 있으니까요!

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "Hello, World!";
            string b = " 안녕 세상!";
            string c;

            c = a + b;

            Console.WriteLine(a.Length);
            Console.WriteLine(b.Length);
            Console.WriteLine(c.Length);

        }
    }
}

실제 실행을 시켜보면 13, 7, 20이 나옵니다.

실제로 하나씩 세어보도록 하겠습니다.

H e l l o ,   W o r
l d !              

총 13개의 문자가 나옴을 확인할 수 있습니다. 중간에 있는 공백을 포함해서 말이죠.

 

    !      

여기도 공백을 포함해서 7개임을 확인할 수 있습니다. 

c는 a와 b를 합친 것이므로 총 20개의 문자를 세어준 것이죠. 실제 Length의 역할을 string에 들어있는 char 유니코드 문자수가 아니라 인스턴스의 개체 수를 반환해서 수를 세어줍니다. 일반적으로 string에는 마지막에 널문자가 들어갑니다.

H e l l o null문자        

이러한 형태로 마지막 부분에 널문자를 넣음으로써 컴퓨터에게 문자열의 마지막임을 알립니다. 하지만 Length는 마지막 Null 문자를 제외하고 문자의 갯수를 공백을 포함해서 세어줍니다. 아주 편리한 기능이죠?

 


문자열에서는 사용하고 싶은 특수한 문자가 있을 수 있습니다. 예를 들어, ", \, /이러한 형태의 문자는 이대로 사용할 수 없습니다. 그래서 이러한 특수한 문자를 사용할 수 있게 하기 위해서 이스케이프 문자를 만들어두었습니다. 이스케이프 문자를 활용하면 언제든 특수한 문자를 사용할 수 있습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "Hello, \t this is tab \tand space";
            string b = "\" 큰 따움표를 사용하기 위해서는 \\를 입력해야 합니다.";

            //우리는 \를 이스케이프 문자라 합니다.

            Console.WriteLine(a);


        }
    }
}

 이스케이프 문자는 엔터 위에 있는 ' \ ' 표시가 이스케이프 문자입니다. 사용하는 곳에 따라 다르게 보이기 때문에 키보드 자판 상 돈 표시를 하고 있습니다.

 

이스케이프 형태 문자 이름 유니코드 인코딩
\' 작은 따옴표 0x0027
\" 큰 따옴표 0x0022
\\ 백 슬래시 0x005C
\0 NULL 0x0000
\a 경고 0x0007
\b 백스페이스 0x0008
\f 폼 피드 0x000C
\n 줄 바꿈 0x000A
\r 캐리지 리턴 0x000D
\t 탭 (시스템 설정에 따라 다름) 0x0009
\v 세로 탭 0x000B

등이 있습니다. 

 


Bool

  참과 거짓으로 이루어진 논리형 입니다. 단순히 true와 false 두가지 밖에 없습니다. 간략한 형태지만 프로그래머들에게는 강력한 도구가 됩니다. bool형이 없는 프로그래밍 언어에서는 0과 1로써 참과 거짓을 표현했는데, 이는 코드를 읽을 때 가독성이 떨어지게 됩니다. 하루에도 수십 줄의 코드를 읽는 프로그래머 입장에서는 굉장히 힘든일이죠. 그래서 bool형은 프로그래머가 코드를 더 잘 읽을 수 있도록 만들어주는 큰 역할을 했습니다.

 

  간략하게 bool형에 대해 살펴보겠습니다.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            bool a = true;
            bool b = false;

            Console.WriteLine(a);
            Console.WriteLine(b);
        }
    }
}

이 코드를 실행하면 treu와 false가 출력됩니다. 

 


이렇게 기본적인 데이터형에 대해서는 마무리가 되었습니다. 아직 안한 object형도 있지만, 나중에 상속에 대한 이야기를 배운 뒤에 object를 더 자세히 다루도록 하겠습니다. 감사합니다!

 

Hello, World! 코드 분석해보기! 지난 강의에서 2개의 프로그래밍 문제를 드렸습니다. 이 문제에 대한 답부터 살펴보도록 하겠습니다. 

 

첫 번째 문제에 대한 해답은

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("안녕하세요. 저는 OO입니다.");
            Console.WriteLine("앞으로 우리의 프로그램을 재밋게 해보도록 하겠습니다.");
            Console.WriteLine("다음에 뵙겠습니다!");
        }
    }
}

 두번째 문제에 대한 해답은

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("이 줄은 첫번째 줄입니다.");
            Console.WriteLine("");
            Console.WriteLine("이 줄은 세번째 줄입니다.");
            Console.WriteLine("");
            Console.WriteLine("이 줄은 다섯번째 줄 입니다.");
        }
    }
}

 

로 볼 수 있겠습니다. 이 코드와 다르게 쓰셨다고 해도 틀린 것은 아닙니다. 다양한 방법이 있으며 여기에는 이러한 방법이 있다고 볼 수 있습니다. 출력된 결과가 같으면 정답이라 생각하시면 됩니다. 

 


 

 

  이번 포스팅에서는 C#에서 사용되는 데이터 종류에 대해 살펴보도록 할 예정입니다. C#에서 정의된 상태를 이야기하는 것이다 보니 글 자체가 지루해질 우려가 있지만, 반드시 필요한 부분이기도 하니 한 번쯤 쭈욱 읽어보신 다음에 하나하나 직접 쳐보시면서 익혀보는 시간을 갖도록 하시면 됩니다. 

 

  정말 단순한 내용이지만, 단순한 내용이기에 오히려 어려워질 수 있는 단원입니다. 꼭 한번은 끝까지 읽어보시기 바랍니다. 

 


  앞서 이야기햇듯이 컴퓨터는 데이터의 종류를 딱딱 알아맞히지 못합니다. (물론 어느 정도는 컴퓨터 스스로가 판단할 수 있도록 설계되어 있기는 합니다.) 예를 들어, 12와 "12"는 다른 형태로 받아들입니다. 그냥 12는 정수로 받아들여지며, "12"는 문자열로 쓰입니다. 인간은 이를 한 번에 알아들을 수 있도록 12라는 숫자로 자동으로 변환해서 생각합니다. 하지만 컴퓨터는 아니죠.

 

  그래서 필요한 것이 데이터형이 필요합니다. 컴퓨터가 어느정도 추측할 수도 있지만, 이러한 추측은 프로그래머 입장에서 설계의 혼란이 오기 마련입니다. 예를 들어, 숫자 12로 저장하려고 했는데, 컴퓨터의 추측으로 12라는 문자로 입력되면 이후에 오류가 나기 마련입니다. 

 

  이러한 수많은 데이터에 대한 통일을 하기 위해서는 반드시 명시적인 데이터형이 정의되어 있어야 합니다. 

 

  C#의 데이터 형식은 두 가지를 기본 근간으로 이루고 있습니다. 기본 데이터 형식복합 데이터 형식으로 크게 나눌 수 있습니다. 그리고 이러한 데이터 형식은 값 형식참조 형식으로 나누어 집니다.당장에는 기본 데이터 형식에 대해서 살펴보도록 하고 나중에 복합 데이터 형식에 대해 살펴보고자 합니다.

 


기본 데이터 형식

데이터 형식 .NET 형식(Type) 비트 범위
byte System.Byte 부호 없는 정수 8 0 ~ 255
sbyte System.SByte 부호 있는 정수 8 -128 ~ 127
short System.Int16 부호 있는 정수 16 -32,768 ~ 32,767
ushort System.UInt16 부호 없는 정수 16 0 ~ 65,535
int System.Int32 부호 있는 정수 32 -2,147,483,648 ~ 2,147,4283,647
uint System.UInt32 부호 없는 정수 32 0 ~ 4,294,967,295
char System.Char 유니코드 문자 16 -
string System.String 문자 시퀀스 - -
bool System.Boolean 논리 형식 8 true, false
object System.Ojbect 모든 형의 기본 형식 - -
long System.Int64 부호 있는 정수 64 -922,337,203,685,477,508 ~ 922,337,203,685,477,507
ulong System.UInt64 부호 없는 정수 64 0 ~ 18,446,744,073,709,551,615
float System.Single 단정밀도 부동 소수점 형식 32 -3.402823e38 ~ 3.402823e38
double System.Double 배정밀도 부동 소수점 형식 64 -1.79769313486232e308 ~ 1.79769313486232e308
decimal System.Decimal 10진수 부동 소수점 숫자 128 -3.402823e38 ~ 3.402823e38

  여기서 1.3e2 의 형태로 쓰인 수는 1.3 × 10의 제곱 형태를 의미합니다. 우리가 고등학교 때, 배운 로그에서와 비슷한 형태를 의미합니다. 수의 과학적 표기 기법에 따라 표시한 수를 의미합니다. 큰 수를 표시할 때는 e는 에러의 e가 아니라 지수의 Exponent의 e입니다. 

 

이러한 수의 범위를 직접 확인할 수 있는데, 그 방법은 int.MaxValue와 int.MinValue를 사용하는 것입니다.

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("byte의 최댓값 : " + byte.MaxValue);
            Console.WriteLine("byte의 최솟값 : " + byte.MinValue);

            Console.WriteLine("sbyte의 최댓값 : " + sbyte.MaxValue);
            Console.WriteLine("sbyte의 최솟값 : " + sbyte.MinValue);

            Console.WriteLine("char의 최댓값 : " + char.MaxValue);
            Console.WriteLine("char의 최솟값 : " + char.MinValue);
            
            Console.WriteLine("float의 최댓값 : " + float.MaxValue);
            Console.WriteLine("float의 최솟값 : " + float.MinValue);

            Console.WriteLine("double의 최댓값 : " + double.MaxValue);
            Console.WriteLine("double의 최솟값 : " + double.MinValue);

        }
    }
}

 알아보고자 하는 데이터형에다가 .MaxValue를 써주면 됩니다. 위에 코드는 몇 개만 살펴보았지만, 위에서 소개한 데이터형을 더 넣어봄으로써 각각의 값을 확인해보시기 바랍니다. 

 

  실제 실행하면

이러한 형태가 나오는데, char의 값은 ?로 나오는 것을 확인할 수 있습니다. 저장된 값에 따라 값이 변하기 때문에 이러한 형태가 됩니다. 지금은 알 수 없는 값이기에 값이 나오지 않고 있습니다. 심지어 문자열을 저장하는 데이터형인 string은 출력해보려고 하면 오류가 나옵니다. 

 

 


값 형식(Primitive Data Type)과 참조 형식 (Reference Type)

  기본 데이터와 복합 데이터 형에는 각각 값 형식과 참조 형식이 존재합니다. 값 형식은 실제 값을 변수를 통해 데이터를 저장하며, 참조 형식은 참조할 위치에 대한 주소를 저장하는 역할을 합니다. 조금 더 자세히 살펴보면 값 형식은 스택에 의해 저장되며, 참조 형식은 힙과 스택에 의해 할당됩니다. 스택에는 참조하고자 하는 주소지가 저장되어 있는 것이지요. (그리고 가비지 컬렉터에 의해서 할당된 값이 해제됩니다.) 이는 나중에 다시 이야기하도록 하겠습니다.  (이 부분에 대해 자세히 이야기하기 위해서는 스택과 힙이 필요한데, 자료구조에 대한 이야기이므로 나중에 자세히 살펴보도록 하겠습니다.) 

 

* 기본 데이터 형식에는 숫자 형식, 논리 형식, 문자열 형식, 오브젝트 형식으로 크게 나누어 집니다. 여기서 문자열 형식과 오브젝트 형식만 참조형식이며 숫자 형식, 논리 형식은 값 형식입니다.

 

* 스택은 책을 쌓아둔 것을 생각하시면 됩니다. 그래서 가장 먼저 들어간 것이 가장 나중에 나오는 선입 후출의 형태를 가집니다. 

(좌) 힙, (우) 스택

* 힙은 이진 트리라 불리는 형태를 가집니다. 가지가 2개 짜리인 나무 구조를 연상시키는 형태를 의미합니다. 이러한 트리 형태는 컴퓨터에서 자주 쓰이는 자료 구조입니다. 나중에 알아보도록 하겠습니다. 


변수

변수는 값을 저장할 수 있는 공간을 의미합니다. 

int a;
char b;
string c;

  이러한 a, b, c를 변수라 부릅니다. 컴퓨터에서 메모리에 '일정한 공간 만큼을 할당하라'라고 명령을 내리는 역할을 합니다. 그리고 위에서 본 것처럼, 각 데이터형에 따라 얼마만큼의 비트를 할당할지 결정하게 됩니다. a라는 변수는 32비트만큼의 메모리를 차지하고 있습니다. 마치 우리가 살고 있는 현실 세계에서 특정한 땅만큼을 배정해주고, a씨네 집이라고 이름 짓는 것과 같습니다. 

 

   실제 주소는 서울시 코딩서로 200번 길 32처럼 있지만, a씨네 집이라고 별칭을 지어주는 것이지요. 메모리에서도 같은 일이 일어납니다. a라는 변수에는 메모리 주소가 저장되며, a라는 별칭을 통해 이 메모리에 접근하자 라고 약속을 해두는 것입니다. C#에서는 메모리에 대해 크게 신경 쓰지 않아도 되도록 가비지 컬렉터가 존재합니다. 이 부분은 다시 나중에 이야기하도록 하겠습니다. 다만 변수를 선언하면 메모리에 올라가며, 메모리에 직접적으로 접근하는 것은 불가능하며, 변수라는 메모리 주소의 별칭을 통해 접근한다 정도로 이해하시면 되겠습니다.

그림으로 그려보자면 이러한 느낌입니다. 

int a = 10; int라는 데이터 형을 가진 a라는 변수에는 10이 저장되어 있다. 그리고 그 주소는 1000이다. int는 32비트이지만 바이트로 표현하면 4바이트이다. 그래서 주소지도 4씩 증가하는 형태로 표현되어 있다! 

 

※ 변수는 메모리에서 특정한 자리를 차지하는 영역에 대한 별칭을 의미한다! 정도가 변수의 의미라 생각할 수 있겠습니다. 

 

초기 프로그래밍 언어에서는 변수를 선언한 뒤에 반드시 초기화가 필요했습니다. 초기화를 하지 않으면 '쓰레기 값'이라 불리는 임의의 아무 값이 들어가기 때문이죠. 이러한 쓰레기 값으로 인해 프로그램이 오류가 생길 가능성이 있었어서 초기에는 초기화가 필수가 되었습니다. C#에는 초기화를 하지 않으면 컴파일 수준에서 오류를 발생시킵니다. 초기화는 일반적으로 정수 값이라면 0을 넣습니다. 

 


리터럴(Literal)

리터럴이라 하면 문자 그대로의 값을 의미합니다. 실제 사전 상의 의미도 "문자 그대로의"라는 의미가 있습니다. 다음의 코드를 살펴보도록 하겠습니다.

int a = 100;

이라는 코드에서 100은 리터럴입니다. 100이라는 의미 그대로를 가지고 있기 때문입니다. 100이라는 숫자가 쓰이기 위해서는 어떤 부분에는 저장이 되어야 할 것입니다. 이러한 형태로 100이라는 문자 그대로의 의미를 가지는 진짜 숫자가 저장된 형태가 리터럴이라 부릅니다. 고등학교 수학에서는 상수라 불리던 것이 리터럴과 같은 의미를 가집니다.

 


이제 간단한 예제를 실습해보면서 변수를 사용해보도록 하겠습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 1_000_000_000;      // 자릿수를 구분할 때 _를 사용합니다.
            float b = 3.1734F;          // F는 float를 의미하며 이러한 리터럴을 만들 때 사용
            double c = 3.1734;          // double 형태는 F를 넣지 않아도 에러가 생기지 않는다.
            string d = "안녕 나야";

            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
            Console.WriteLine(d);

        }
    }

숫자를 사용할 때는 구분자인 '_'를 사용함으로써 사람이 보기 편하게 만들 수 있습니다.

1000000000 = 1_000_000_000 과 같이 쓰는 것이죠. 구분자를 어디에 둘지는 프로그래머가 정할 수 있지만, 통상적으로 3개의 자릿수 마다 구분자를 둡니다.

 

만약에 데이터형의 범위를 넘어가는 값을 대입하려고 한다면 컴파일러가 오류를 발생시킵니다. 

위에 코드에서 추가로

short e = 500000000;

을 넣게 되면 오류가 발생합니다. 

 

컴파일러가 잡지 못하는 오류에 대해서, 데이터형을 넘쳐서 데이터가 변형된 형태를 오버플로우라 부르며, 데이터형이 부족해서 생긴 변형을 언더플로우라 부릅니다. 

 

우리는 둘 중에서 오버플로우에 대한 예제만 살펴보도록 하겠습니다.

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            short a = 32767;      // short의 최댓값은 32,767
            a++;                  // 1만큼 증가시킨다.

            Console.WriteLine(a);

        }
    }
}

실제 실행하게 되면, 32767이 나오는 것이 아닌 -32768이 나오게 됩니다. 데이터가 저장할 수 있는 한계선을 넘어감으로써 우리가 원하는 값이 아닌 다른 데이터 값을 표현하게 된 것입니다. 이 부분에 대해서도 나중에 저장 구조()에 대해 살펴보고 그 다음에 설명드리도록 하겠습니다. 

 

위의 구조를 조금 더 바꿔보도록 하겠습니다. 바로 전에 배웠던 short.MaxValue를 활용해보도록 하겠습니다. 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            short a = short.MaxValue;      // short의 최댓값은 32,767
            a++;                  // 1만큼 증가시킨다.

            Console.WriteLine(a);

        }
    }
}

언더플로우까지 나오게 한다면, 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            short a = short.MaxValue;      // short의 최댓값은 32,767
            a++;                  // 1만큼 증가시킨다.

            Console.WriteLine(a);

            a = short.MinValue;
            a--;
            Console.WriteLine(a);
        }
    }
}

로 바꿔 볼 수 있습니다. 

 


실수를 담은 데이터 형 float, double, decimal

  지금까지는 거의 정수형에 대한 이야기를 해왔습니다. 그렇다면 소수를 표현하는 수단은 어떻게 표현해야 하는가? 바로 float, double, decimal 형태로 표현합니다. 세 개의 데이터 형의 차이점은 데이터를 어디까지 표현할 수 있는가 입니다. 

 

  가장 많은 수를 표현할 수 있는 것은 float < double < decimal의 순으로 정밀도가 높습니다. 정밀도는 소수점을 표현할 수 있는 선을 이야기합니다. C#은 기본적으로 double형을 장려합니다. float보다 더 많은 데이터를 차지하지만, 데이터 손실을 안고 가는 것보다는 double형을 쓰면서 얻을 데이터를 확보하자는 것이죠. 하지만 더 높은 정밀도를 가진 decimal은 너무 과도한 정밀도 일수도 있기에 기본으로 쓰이는 것은 double형을 사용합니다. 

 

  실제로 예제를 통해 정밀도가 어디까지 표현되는지에 대해 살펴보도록 하겠습니다. 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            float a = 3.1415926535_8979323846_2643383279_5028841971f;   // float형을 사용하려면 숫자 뒤에 f가 필요하다.
            double b = 3.1415926535_8979323846_2643383279_5028841971;   // 
            decimal c = 3.1415926535_8979323846_2643383279_5028841971m; // decimal형을 사용하려면 숫자 뒤에 m이 필요하다.

            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
        }
    }
}

 

입력한 수는 파이를 입력해보았습니다. 10자리 마다 구분지어서 넣어보았습니다. 실제 실행 결과를 보면 float과 double, decimal에 따라 소수점 아래에 숫자를 표현하는 정도가 다름을 확인할 수 있습니다. (정밀도가 다른 것이죠)

 


글이 길어지는 관계로 다음 포스팅으로 넘어가서 글을 이어가도록 하겠습니다! 

감사합니다.

 

  지난 시간에는 Visual Studio 2019 설치와 더불어 Hello, World!를 출력해봄으로써 대략적인 구조를 확인했습니다. 이번 시간에는 지난 시간에 했던 Hello, World!의 소스코드를 하나씩 확인하며 어떠한 내용이 존재하는지 그리고 어떤 의미를 지니는지 간략하게 살펴보도록 하겠습니다. 기억이 안나시는 분은 지난 시간 포스트를 확인해보고 오시기 바랍니다.

 

  앞으로도 이러한 방식으로 하나하나 살펴보도록 할 예정입니다. 힘들수도 있지만, 머릿속으로 어떤 의미인지 확인이 안된다면 주먹구구식의 프로그래밍이 될 수밖에 없습니다. 그러니 하나씩 정확하게 배워가도록 하겠습니다. 

 

using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

 

  우리는 이러한 코드를 작성해보았습니다. C#과 같이 고수준 언어의 특징은 마치 사람이 쓰는 말을 간결하게 풀어놓은 듯한 느낌을 받습니다. 실제로도 컴퓨터에게 명령하듯이 코드를 작성합니다. 예를 들면, '사용해라', '만약 아니라면 ~를 해라' 이러한 형태로 컴퓨터에게 명령한다는 느낌으로 코드를 작성합니다. 

using System;

  Visual Studio IDE는 우리의 눈을 편하게 만들어주는 역할을 해줍니다. 보라색으로 보이는 것은 키워드(Keyword)입니다. 키워드는 프로그래밍을 하기 위해서 컴퓨터에게 내리는 명령어라 생각하시면 됩니다. 내가 정한 말인지 아니면 사전에 약속된 말인지 구분이 되어야 겠죠? 그러한 구분을 해놓은 단어들을 Keyword(키워드)라 합니다. 

 

  위에 코드 상에서 using, namespace, class, static, void, string은 모두 다 사전에 정의된 키워드가 되는 것이죠!

 

그렇다면 using 키워드의 의미는 무엇인지 생각해보면, '사용한다' 겠죠? 무엇을 사용한다? System을 사용하겠다는 의미입니다. 마지막으로 ; (세미콜론)은 컴퓨터에게 명령어 한 줄 한 줄을 구분시켜주는 역할을 하는 구분자 역할을 해주는 칸막이 같은 역할을 합니다. 컴퓨터가 봤을 때 사람의 언어는 구분이 안되는 외계어처럼 보이게 될 것입니다. (물론 세미콜론을 없애고자 하는 움직임이 없었던 것은 아닙니다. 그런데 세미콜론을 남겨둠으로써 얻는 이점이 많다고 생각해서 세미콜론을 남겨둔 형태로 지금도 사용되고 있습니다.)

 

실제로 우리가 컴퓨터의 기계어를 보면 010001010100111101 의 형태로 보이는데, 우리는 이 말이 무슨 말인지 모릅니다. 그리고 컴퓨터는 알 수 있는 것이죠. 

 

뒤에 나오는 System은 클래스(class)를 담고 있는 네임스페이스(namespace)가 됩니다. 

 


 

네임스페이스

  네임스페이스는 특정 클래스라 하는 것이 어디에 속해 있는지 알려주는 역할을 합니다. A라는 네임스페이스에 add 클래스가 있고, B라는 네임스페이스에 add라는 같은 이름의 클래스가 있으면 이를 구분지어주기 위한 역할을 하는 것이 네임스페이스가 됩니다. 

 

  예를 들어, A네 자전거, B네 자전거, C네 자전거라 하면 이를 구분지어주는 역할을 하는 것이 네임스페이스가 되는 것이죠. A씨 네 자전거입니다! B씨 네 자전거입니다. C씨 네 자전거입니다. 이렇게 소속을 구분지어주는 역할을 합니다.

 

  왜 이렇게 하냐? 그 이유는 변수이름을 짓기 어렵기 때문이죠. 그래서 프로그래머들은 비슷한 이름을 쓰게 되는데, 문제는 이름들끼리 충돌하기 시작한다는 것입니다. 위에 예에서도 " 자전거다!" 라고 지칭하면 누구네 자전거? 라는 의문이 따라오는 것처럼 어디에 속해 있는지가 중요한 문제이기 때문이죠. (물론 더 다양한 문제를 해결하기 위해서 도입했지만, 대략적인 이유는 이렇습니다.)

 

그렇다면 다시 돌아와서

using System;

기존에 보았던 이 코드에서 System은 System으로 되어 있는 네이스페이스에 있는 것들을 쓰겠다는 소리가 되는 거겟죠?

 

그렇다면 이렇게 다양한 네임스페이스를 외워야 하냐? 그건 아닙니다. 기본적으로 우리에게는 찾아볼 수 있도록 준비된 사전이 있습니다.

 

docs.microsoft.com/ko-kr/dotnet/api/system?view=net-5.0

 

System 네임스페이스

일반적으로 사용되는 값과 참조 데이터 형식, 이벤트와 이벤트 처리기, 인터페이스, 특성, 예외 처리 등을 정의하는 핵심 클래스 및 기본 클래스가 포함되어 있습니다. Contains fundamental classes and b

docs.microsoft.com

우리가 궁금한 내용이 있을 때마다 Docs를 찾아보면서 쓰면 됩니다. 일단은 이러한 것이 있구나 정도로 생각하면 됩니다. 

 

위의 코드에서 using System;을 빼면

 

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Console.WriteLine("Hello World!");
        }
    }
}

 코드가 조금 더 길어집니다. 네임스페이스를 사용하면 길어지는 현상도 줄일 수 있는 장점이 있죠.


using System;

namespace MyFirstApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

 자 다시 코드로 돌아와서 생각해보도록 하겠습니다.

이제 다음은

namespace MyFirstApp

이 부분은 이제 이해하기 쉽습니다. namespace는 소속을 정해주는 것이죠. 마찬가지로 앞으로 만들 클래스는 MyFirstApp 이라는 네임스페이스에 속하게 만들겠다는 것입니다. 

 

using System;

namespace MyFirstApp
{ 이 부분부터 들어가는 곳은 MyFirstApp의 네임스페이스에 속해있는 것입니다.
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

 

그 다음으로 

 

Class

class Program

  class란 기본적인 객체를 정의하는 하나의 틀을 의미합니다. 객체는 나중에 이야기하겠지만, 우리가 세상을 바라보는 물체를 객체라 부릅니다. 물론 물체 뿐 아니라 무형의 형태도 객체로 바라볼 수 있습니다. 객체지향에 대한 이야기는 이야기하면 이야기할 수록 많은 이야기가 필요하니 이후에 다시 이야기하도록 하겠습니다. 

 

  클래스는 이러한 기본이 되는 형태를 class라 부릅니다. 여기에는 변수메소드라 불리는 것이 정의되는 공간입니다. 클래스는 한 개만 존재할 수도 있고, 여러개가 존재할 수도 있습니다. 

 

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }

class Program 안에는 Main 이 있습니다. Main은 모든 프로그램이 흘러가는 중심이 되는 곳이라고 생각하시면 됩니다. 그래서 Main은 한 개만 있어야 하며, 두 개 이상이 되면 프로그램에서 오류가 발생합니다. 

 

Main은 실제로 프로그램이 실행되는 공간이며, 그 외에 메소드(함수)는 Main에서 불러주어야 실행이 됩니다.

 

static(정적) : static이 선언되어 있으면, 프로그램 시작시에 해당 속성이 붙어있는 클래스가 메모리에 올라가게 됩니다. 프로그램 시작과 함께 초기화되며, 메모리에 처음부터 올라가있는 상태가 됩니다. 더 자세한 이야기는 나중에 하도록 하겠습니다. 

 

  void는 자료형을 의미합니다. 컴퓨터는 자료형을 지정해줘야 합니다. 예를 들어, 12와 "12"를 보고 사람은 같은 숫자 12를 생각하지만, 컴퓨터는 이를 보고 다르게 생각합니다. 12는 숫자 12이며, "12"는 문자 12로 인식합니다. 그래서 프로그래머는 명시적으로 컴퓨터에게 어떤 자료형임을 알려주어야 합니다. 그래서 여러 자료형이 주어져 있습니다. 예를 들어, int, char, string등등 이 존재합니다. 

 

  class는 일반적으로 돌려주는 return 값이 있을 수 있습니다. 메인에서 프로그램이 실행될 때, 다양한 class를 지속적으로 호출하게 됩니다. 그러면 다른 class에 갔다가 다시 메인으로 돌와서 프로그램을 실행해야 합니다. 이러한 상황에서 다시 돌아올 때, 값을 가지고 오는 class가 있는 반면, 가지고 오는 값이 없는 class가 있습니다. 이 중에서 가지고 돌아오는 값이 없는 class에서 사용되는 것을 void라 합니다. 가지고 돌려주는 값이 정수형이면 int를 붙여서 사용합니다. (이러한 부분은 뒤에 다시 설명하도록 하겠습니다.)

 

대략적으로 설명하자면 이러한 형태가 됩니다.

  우리가 아직 많은 부분을 배우지 않아서 정확한 설명은 아니지만 대략적으로 이러한 느낌으로 흘러갑니다. Main이라는 프로그램의 큰 흐름에서 필요한 Class를 호출하고 흐름이 잠시간 넘어가는 형태로 프로그램이 진행됩니다. 이럴 때 void는 넘겨주는 값이 없는 형태이며, int는 정수를 넘겨주며, float은 실수값을 넘겨주는 것이죠. 무엇을 넘겨주는 지에 대해 알려주는 역할을 한다고 보시면 됩니다.


마지막으로 

Console.WriteLine("Hello, World!");

앞서 이야기한 Class에 대해 흐름이 넘어간다고 했죠? 여기서도 System이라는 네임스페이스에 있는 Console Class에 있는 WriteLine이라 불리는 메소드를 통해서 우리는 "Hello, World"의 문자열을 출력해줍니다. 

 

그리고 WriteLine은  출력이 끝난 후에 다음줄로 넘어갑니다. (Enter를 누르는 효과가 나옵니다.)

만약 줄 바꿈을 하고 싶지 않다면, Write라고 사용하면 됩니다.

Console.Write("Hello, World");

한번 써보신다음에 출력을 해보세요!

 


아직 static void Main(string[] args) 에서 string[] args는 이야기하지 않았습니다. 하지만 이는 나중에 이야기하도록 하겠습니다. 뒤에서 배울 내용을 먼저 배운 다음에야 이해가 됩니다. 일단은 이러한 구조를 갖는구나 생각을 하시면 됩니다. 

 

마지막 과제를 드리면서 이번 장을 마무리 짓도록 하겠습니다.

 

#1. 

안녕하세요. 저는 * * 입니다.
앞으로 우리의 프로그랭을 재밋게 해보도록 하겠습니다.
다음에 뵙겠습니다!

 

#2. 

이 줄은 첫번째 줄 입니다.

이 줄은 세번째 줄 입니다.

이 줄은 다섯번째 줄 입니다.

이 문제에 대한 답은 다음 시간에 올리도록 하겠습니다. 감사합니다.

 

 

  우리가 사용할 IDE는 Visual Studio를 활용해서 C#을 배워보도록 할 예정입니다. IDE를 없이도 충분히 사용할 수도 있지만, IDE가 가지는 편안함을 이용하기 위해서 IDE를 사용할 예정입니다. 아직은 어떤 것도 모르르는 상태이기 때문에 하나하나 따라오시면서 익혀보는 것에 관점을 두고 시작하시기 바랍니다. 

 

  Visual Studio는 마이크로소프트에서 만든 IDE로 전문가용(Professinal)을 사용하고자 한다면, 일정 금액을 내고 사용해야 합니다. 하지만 우리는 엄청난 기능까지는 필요없으므로 Community(커뮤니티)버전이면 충분합니다. 

 

링크를 타고 들어가서 다운로드를 받아보도록 합시다. visualstudio.microsoft.com/ko/downloads/

 

Windows 및 Mac용 Visual Studio 2019 다운로드

Visual Studio Community, Professional, Enterprise를 다운로드하세요. 지금 Visual Studio IDE, Code 또는 Mac을 무료로 사용해 보세요.

visualstudio.microsoft.com

* 링크가 안나올 경우, 구글에 'Visual studio 다운로드'를 타이핑 하면 다운로드 받을 수 있는 링크가 준비되어 있습니다. [잘 모르겠다 싶으시면 물어보세요!]

 

다운로드를 해주도록 합시다.

혹은 아래로 들어가서 다운로드 -> Community 2019로 다운로드 하셔도 됩니다.

visualstudio.microsoft.com/ko/vs/

 

Visual Studio 2019 | 무료 다운로드

Visual Studio에서 코드 완성, 디버깅, 테스트, Git 관리, 클라우드 배포를 사용하여 코드를 작성할 수 있습니다. 지금 Community를 무료로 다운로드하세요.

visualstudio.microsoft.com

 

  다운로드를 하는 동안 아래쪽으로 내려가면 Visual Studio Code가 있습니다. Visual Studio Code도 강력한 IDE 중 하나입니다. 필자는 Code를 더 많이 사용합니다. 하지만 Code는 완전 초보자가 처음부터 쓰기에는 너무 힘듭니다. 하나부터 열까지 자신이 커스터마이징을 할 수 있다는 점이 장점이지만, 초보자에게 너무 많은 것이 주어지면 오히려 힘들어지죠? 그래서 일단은 다른 셋팅 없이도 할 수 있는 Visual Studio Community 2019로 시작하도록 합시다! IDE는 우리의 프로그래밍을 도와주는 '수단'임을 잊으시면 안됩니다. 

 


다운로드가 완료되면 바로 실행시켜 주도록 합시다. 

실행시키면 어떤 용도로 작업할지에 대해 묻습니다. 그러면 우리가 .NET 데스크톱 개발이 우리가 하려는 C#을 만들 수 있는 것이 들어있는 패키지입니다. 

 .NET 데스크톱 개발을 체크표시하고 '설치'를 눌러주시면 됩니다. 생각보다 시간이 필요합니다. 설치 완료 이후에는 딱히 필요한 부분이 없습니다. 사용하는데는 로그인도 필요없지만, 차후 로그인하지 않고는 사용기간이 정해짐니다. 그래서 로그인을 해주고 넘어가면 편합니다. (나중에 하셔도 상관없습니다.)

 

완료하고 나면 이러한 화면이 나옵니다. 

 

새 프로젝트 만들기를 눌러줍니다. 

 

먼저, 프로그래밍 언어를 C#으로 바꾼다음 -> Console Application -> 다음 을 눌러줍니다. 

 

이 화면에서의 구성은 총 3가지 입니다. 

  • 하나는 프로젝트 이름
  • 두번째는 위치, 위치는 이 프로젝트를 저장할 위치를 선정해주는 것 입니다. 저장하고 싶은 위치를 선정해주시면 됩니다. 저는 C:\Users\one-math\Desktop\Test\ 에 저장을 하기 위해 지정해주었습니다. (저장하고자 하는 폴더에 넣으면 됩니다.)
  • 솔루션 이름

마이크로소프트의 비주얼 스튜디오는 프로젝트와 솔루션이라는 특별한 구조를 갖습니다. 이는 프로젝트 시에 관리를 용이하게 해줍니다. 

  • 솔루션 : 솔루션은 여러 프로젝트를 관리하기 위해 있는 하나의 큰 컨테이너입니다. 프로젝트와 프로젝트를 연결할 수 있는 연결 고리를 만들어주는 역할을 하는 것이지요.
  • 프로젝트 : 현재는 1개의 파일만을 가지고 실습을 진행하겠지만, 개발의 규모가 커지게 되면 파일의 갯수도 늘어나게 됩니다. 그래서 여러 소스 파일과 파생되는 여러 파일을 묶어서 관리하기 위한 것이 프로젝트입니다.

프로젝트는 여러 소스파일과 파생되는 파일을 묶어서 관리하는 개념, 이러한 프로젝트가 많아졌을 때, 이를 묶어서 관리하는 솔루션이라는 개념이라 생각하시면 됩니다. 

이러한 형태가 나오게 됩니다. 실제로 저장된 폴더에 들어가서 확인해보면,

이러한 형태로 생성이 됩니다. 확장자가 sln은 솔루션 파일, 확장자가 csproj이면 C-sharp-Project 파일이 됩니다.

 

우리가 사용하는 프레임워크는 .NET 5.0을 사용할 예정이니 .NET 5.0인지 확인하고 만들기를 누르면 됩니다. 

 

성공했다면, 이러한 화면이 나오게 됩니다. 

 


 

화면구성

기본적인 화면 구성은 코드 편집기, 솔루션 탐색기, 콘솔로 이루어져 있으며, 처음에는 3가지만 기억하고 계시면 됩니다. 

  • 코드 편집기 : 실제로 코드를 작업하는 공간이며, 코드를 작성할 공간입니다. 
  • 솔루션 탐색기 : 우리가 작업하고 있는 소스 파일과 그에 따른 종속성 프로젝트에 대해 전반적으로 보여주는 공간입니다. 기본적인 프로그램 흐름을 배울 때는 필요없지만, 나중에 규모가 큰 프로그램을 만들게 되면 자주 사용하는 공간이기도 합니다.
  • 콘솔 : 시스템 메시지가 나오는 공간으로 에러가 생기면 알려줍니다. 

이제 설치에서 프로젝트 생성까지 완료했으니, 실행을 한번 해보도록 하겠습니다. 

실행에는 두 가지 방법이 있습니다. 빌드에 의한 실행, 혹은 디버그에 의한 실행으로 두 가지가 있습니다. 빌드에 의한 방법은 실행 시키기 위해서는 조금 더 복잡하기 때문에 당분간은 디버그에 의한 실행을 사용하도록 하겠습니다.

 

 

실행

상단 메뉴에서 디버그 -> 디버그하지 않고 시작을 눌러줍니다. 혹은 Ctrl + F5를 누르시면 됩니다. 

그러면 

 

이러한 화면이 나옵니다. "Hello World!"를 확인할 수 있습니다. 

 


Hello, World의 유래

  문득 모든 프로그래밍의 시작은 Hello, World로 시작하는데, 그 이유가 궁금하지 않나요? Hello, World는 Brian W.Kernighan(브라이언 커닝헨)과 Dennis M.Richie(데니스 리치)가 쓴 저서인 "The C Programming Language"의 교재에서 첫 번째 예제가 Hello, World를 출력하는 것이었습니다. C언와 함께 유명해지면서 대부분의 프로그래밍 언어에서 첫 번째 예제로 Hello, World!를 사용하는 것이 관행처럼 굳어진 것이죠.

 

  단순히 Hello, World자체가 중요한 것은 아니고, 첫 예제를 실제로 하나하나 따라하다 보면 전반적인 프로그래밍 코드를 실행시키는 구조가 눈에 들어오기 때문에 이러한 코드를 직접 쳐보는 것이지요. 여기서 우리가 얻어야 하는 것은 한번을 직접 실행시켜보는 것이 여러번 책을 들여다 보는 것보다 중요한 점입니다. 

 

  꼭 프로그래밍을 배우실 때는 힘들더라도 직접 하나씩 만져보는 것을 추천드립니다. 

* 28가지의 다른 Hello, World!를 아래에서 확인할 수 있습니다.

excelwithbusiness.com/blog/say-hello-world-in-28-different-programming-languages/


  설치부터 시작해서 Hello World를 실행해보면서 기본적인 실행의 흐름을 따라가 보았습니다. 우리가 생각하는 것보다 프로그래밍은 복잡할순 있지만, 생각보다 그리 복잡하진 않습니다. 우리가 항상 하는 게임과 배우는 수준은 같습니다. 처음에는 하나하나 해보다 보면 어느덧 성장해 있는 우리의 모습을 확인할 수 있습니다. 하루에 하나만 배우자는 마음으로 하나씩 배워가길 바라겠습니다. 

 

  저도 항상 한번에 많은 것을 하려고 했습니다. 그러다보니 마음은 조급한데 생각보다 늘어나는 실력은 너무 작다보니 포기하는 일이 많았습니다. 우리는 분명 오늘을 달리고 있습니다. 어제보다 나은 내가 되었으며, 앞으로 조금씩 나아가면 6개월, 1년 이란 시간이 지나면 멀리 나아가 있는 우리의 모습을 볼 수 있습니다. 그러니 너무 조급해하지 마시고 '하루에 한개는 내가 꼭 알아내겠다.' 라는 목표로 꾸준히 나아가시기 바랍니다. 

 

  저는 프로그래밍을 어렵게하는 이유는 '꾸준함'이라 생각합니다. 프로그래밍은 언제나 거대한 문제를 조그만한 문제로 쪼개서 내가 먹을 수 있게 만드는데 의의가 있습니다. 하지만 하루 이틀로는 이렇게 거대한 문제를 여러번 베어먹는다고 해서 의미가 없죠. 하지만 이러한 날이 하루, 이틀이 모이면 거대한 산 같은 덩어리를 쪼개서 내가 베어 먹을 수 있게 됩니다. 

  C#(씨샵이라 말합니다.) C#은 C라고 이름붙어진 형제에서 마지막으로 나온 언어 중 하나입니다. C# 외에도 C언어, C++(씨플플이라 읽습니다.)가 앞에 존재하지만 업그레이드의 버전은 아닙니다. (어떤 의미로는 업그레이드라고도 볼 수 있지만 엄밀히 다른 부류의 언어이지 업그레이드 된 언어라 보기는 힘듭니다.) 

 

 이러한 C#은 객체지향적 언어이며, 개발자가 사용하기 쉽게끔 나왔습니다. 여러가지 특성이 존재하지만 초보자의 관점에서는 대략 이러한 느낌입니다. 프로그래밍을 접하는데 C#은 어떤가요? 라고 했을 때, C#은 충분히 처음에 배우기 편한 언어임은 분명합니다. (Python과 마찬가지로 강력한 언어이죠.) 문법적 특성은 C언어가 더 적고 사용하기 편할지 몰라도 실제로 메모리 관리나 프로그래머에게 많은 자유를 준 만큼 C언어는 프로그래머가 고려해야할 사항이 많아지는 단점이 있죠. 게다가 C언어로 100줄을 작성해야할 코드가 C#이나 파이썬으로 넘어가면 작성해야할 코드가 20줄로 끝날 정도로 간편해지기도 했습니다. (물론 상황에 따라 다르겠지만, C언어 보다 C#, 파이썬이 사람의 언어(자연어)에 가깝기 때문에 작성하기가 더욱 편리합니다.)

 

  C#을 사용하는 IDE는 여러 가지가 있지만, 대표적으로 Visual Studio와 MonoDevelop 등이 있습니다. 물론 이외에도 다양한 C#을 지원하는 IDE가 존재합니다. 앞으로의 포스팅에서는 Visual Studio를 통해 진행할 예정이지만, 설치가 힘든 분들은 웹에서 제공하는 컴파일러를 사용하셔도 괜찮습니다. 충분히 우리의 실습을 커버할 수 있습니다. OnlineGDB

 

Online C# Compiler - online editor

OnlineGDB is online IDE with C# compiler. Quick and easy way to run C# program online.

www.onlinegdb.com

 

C#에 대략적인 이야기는 이 정도만 알고 넘아가셔도 충분합니다. 다만 밑에 적혀질 이야기는 C#에 대해 조금 더 생각해보자란 의미에서 더 적어두도록 하겠습니다. 

 

1. C#


 C#이라는 이름의 유래는 C++에서 C++++ 네 개의 +를 2개씩 쌓아 올리면 # 모양이 되기 때문에 C#으로 만들어졌다.  Java는 JVM(자바 가상머신)을 활용해 어떤 플랫폼에 상관없이 사용할 수 있는 장점을 필두로 생산성 향상을 도모했다. 한번 작성하고 어디서든 사용할 수 있도록 하는 것이 큰 장점으로 다가왔다. 그래서 C# 또한 이러한 양상을 따라가기 위해 만들어졌다. 그래서 Java에서 JVM(자바 가상머신)이 필요했듯, C#은 닷넷 프레임 워크가 필요하다. (정확히는 Java를 MS에서 만들어낼 때, J#이라 만들어냈는데, 저작권 문제로 C/C++, Java의 장점을 모아서 C#으로 다시 출시하게 되었다.)

 

  그렇다면 C#, Java가 무조건 더 좋은가? 라는 질문에는 그렇다고 볼 수도 있고 아니라고 볼 수도 있다. 초기 C#, Java는 성능면에서 불리한 점이 너무나도 많았다. 물론 이는 지금도 비슷하다. C/C++ 이 더 빠른건 설계상 차이점이 생길 수 밖에 없다. 태생적 한계가 분명히 존재한다. 가상 머신을 넣는 순간 느리지는건 어쩔 수 없다. 가상머신이 없는 C/C++은 C#, Java보다는 당연히 빠르다. 하지만 가상머신이 없으므로 윈도우에서 만들어진 것을 다른 운영체제에서 사용할 수 없다. 그래서 이를 바꿔주는 작업을 해야 한다. Java는 이럴 필요가 없는 것이다. 또한 메모리 관리 측면과 자연어에 가까운 언어가 사람이 쓰기 편한 언어이다. Java와 C#은 사람이 쓰기 조금 더 편하다.

 

  그럼에도 불구하고 프로그램 언어의 인기 순에는 항상 C/C++를 빼놓고 이야기할 수 없다. 이는 하드웨어를 컨트롤할 수 있는 언어는 결국 C언어이며, C++은 메모리 관리를 직접할 수 있다는 장점이 있다. 그래서 여전히 사라지지 않고 큰 인기를 누리고 있다. (리눅스의 기반 언어는 C로 이루어져 있으므로 이를 다루기 위해서는 반드시 C를 할 수 밖에 없다.)

 

  초기에는 마이크로소프트 플랫폼에 귀속되어 있어서 사용도가 많이 떨어지기도 했지만, 이후에 개선을 거쳐서 업데이트를 통해 개선을 거쳐가는 중이다. 그래서 C#의 인기도 업데이트와 더불어 그 인기가 늘어가는 중이다. 유니티의 기본 개발 언어이기도 하다.

 

 

2. .NET (닷넷)


  C#은 기본적으로 닷넷에서 실행된다. Java가 JVM이라는 자바 가상 머신 위에서 돌아가는 것과 같은 형태이다. CLR(Common Language Runtime) - 공용 언어 런타임이라 하는 가상 실행 시스템이다. CLI(Common Language Infrastructure) - 공용 언어 인프라를 MS(마이크로소프트)에서 구현한 것이다. 

 

  C#으로 작성된 코드는 IL(Intermediate Language) - 중간 언어 라 불리는 것으로 컴파일 된다. (마이크로소프트에서 개발한 CLI를 준수하는 IL 언어로 컴파일된다고 보는 것이 정확할 것이다.) 그리고 CLR을 통해 기계가 읽어들일 수 있는 형태로 로드 된다. 

 

  C# 프로그램이 실행되면 어셈블리가 CLR에 로드되며, 이러한 CLR은 Just - In - Time으로 컴파일을 수행한 다. (우리나라 말로 그 때 그 때 마다) 

  이러한 CLR에서 나온 코드는 기계어로 바꾸어준다. (기계어는 기계가 해석할 수 언어로써 01010의 형태를 갖는 코드를 의미한다.) 0은 꺼짐, 1은 켜짐의 형태로 반도체를 껏다가 켯다가 할 수 있는 것이다.

  

  정리하면, C# 코드 -> 컴파일 -> CIL -> CLR -> 기계어 형태로 변형해주는 것이다. 

 

 

이것 외에도 CLR은 가비지 컬렉션(Garbage Collection) 자동 실행, 예외 처리, 리소스 관리 등 다양한 서비스를 제공한다. CLR에서 실행되는 코드는 관리 코드(Managed code)라 불린다. 

 

  닷넷의 주요 기능 중 하나는 언어의 상호 호환 가능성이다. C# 컴파일러에서 생성된 CIL 코드는 CTS - 공용 형식 사양)을 따르기 때문에 [한 마디로 표준화가 되어 있다.], C#에서 생성된 CIL 코드 자체는 F#의 .NET 버전과 Visual Basic, C++ 등 CTS(공용 형식 사양)을 따르는 언어와 상호 호환이 가능하다. 그래서 다른 언어로 작서된 것들도 CTS를 따른다면 같은 언어로 작성된 것처럼 서로 참조하며 사용이 가능한 점이 강점이다! 물론 이것 외에도 다양한 라이브러리를 포함하는 것은 당연하다. 

 

.NET 코어에 대해 더 자세한 이야기가 궁금하다면 .NET GitHub을 방문하도록 하자!

.NET은 Winodw, macOS, Linux, RHEL, Tizen (대표적으로 갤럭시 워치 OS) 등을 지원한다. 더 많은 지원이 있지만 이 글에서는 대략 이 정도로 정리하겠다.

 

.NET 언어는 다양한 기능을 지원하는데 

 

  • Type Safety (타입 안정성)
  • Type inference - C#, F#, Visual Basic (
  • Generic Types (제네릭 형식)
  • Delegates (대리자 - C언어나 C++에서 포인터 같은 기능)
  • Lambdas (람다)
  • Events (이벤트)
  • Exceptions (예외)
  • Attributes (특성)
  • Asynchronous code ( 비동기 코드)
  • Parallel Programming (병렬 프로그래밍)
  • Code Analyzers (코드 분석기)

등이 있다. 더욱 자세한 내용은 C#을 배워보면서 하나씩 알아가도록 하겠다.

 

 

  지난 시간에는 오브젝트를 생성해보고 각 오브젝트의 요소와 더불어 컴포넌트란 것에 대해 살펴보았습니다. 그렇다면 이러한 오브젝트를 조금 더 잘 다룰 수 있도록 하나의 요소가 더 있습니다. 바로 기즈모죠! 물론 앞선 강의에서는 이미 기즈모를 사용하고 있었습니다. 

 

화면에서 요소를 사용하기 편하게 만들어주기 위한 이정표들을 우리는 기즈모라 부릅니다. 

여러 요소가 기즈모로 표현함으로써 유니티 사용을 조금 더 용이하게 만들어줍니다. 

 

1. 기즈모


화면을 움직여주는 도구 
'이동'도구 오브젝트를 이동시켜준다.
'회전'도구 오브젝트를 회전시켜준다.
'스케일'도구 오브젝트의 크기를 늘려주거나 줄여준다.

 

2. 씬 기즈모


Scene 뷰의 오른쪽 위에는 이러한 씬 기즈모가 존재한다. 원뿔의 정육면체에 붙어있는 모양새를 하고 있는데, 씬 기즈모를 활용해서 어떠한 시점을 보고 있는지 확인할 수 있다. 여기서 각각의 요소를 클릭함으로써 원하는 시점으로 이동할 수도 있다.

 

  지난 시간에 실습했던 것을 기준으로 한번 보도록 하겠다. [기존 실습한 파일이 없더라도 상관없다.]

 

실제로 실습을 해보면 이러한 모양새가 됨을 확인할 수 있다. 원하는 시점에 따라 기즈모를 선택함으로써 시점 관리를 편하게 할 수 있다.

 

3. Presp(퍼스펙티브), Iso(아이소메트릭) 모드


씬 기즈모에는 두가지 요소가 있다. 퍼스펙티브 모드와 아이소메트릭 요소이다. 씬 기즈모 아래에 모드를 선택할 수 있는 요소가 준비되어 있다.

Persp으로 되어 있다. 실제로 클릭하면 Iso로 바뀌는 것을 확인할 수 있다.

실제로 눌러보면 변화가 일어난다. 아래의 gif를 확인해보자.

 

누를 때마다 바뀌는 것을 볼 수 있다. 두 개의 차이점은

  • Iso : 사람의 눈은 보는 시점에 따라 사물이 다르게 보인다. 하지만 게임을 만드는 입장에서 이러한 변하는 시점은 도움이 되지 않는다. 그래서 멀리있는 사물이나 가까이 있는 사물이나 실제 크기로 보여주도록 하는 것이다. 
  • Persp : 관점에 따라 사물은 변하는데, 이러한 현실 세계의 시점을 반영해서 실제처럼 표현해준다. 즉 멀리있으면 작아지고 가까이 있으면 커지는 형태를 표현하는 것이다.

이 기능은 실제로 유용하게 사용된다. 멀리 있는 오브젝트를 작게 지정한줄 알았는데, 가까이서 보니 생각보다 커지는 경우가 있기 때문이다. 예를 들어, 사람에게 다가오는 공을 지정했는데, 가까이서 보니 사람보다 더 큰 공으로 보이는 경우도 있는 것이다. (물론 실제 오브젝트 생성시에 조심하겠지만, 비율에 대한 이야기는 중요하므로 이러한 기능도 유용하게 사용된다.)

 

 

  씬 기즈모의 관점을 바꾸는 기능은 기즈모를 오른쪽 클릭해서 바꾸기도 한다. 기즈모를 직접 만들어서 사용할 수도 있습니다. (이러한 커스텀 기즈모는 나중에 알아보도록 하겠습니다.)

 

 

4. 기즈모 핸들 포지션 토글


 

기즈모 핸들포지션에는 트랜스폼 도구 기즈모의 위치와 기즈모 자체를 조작하는데 사용하는 중심점을 어떻게 움직이는지에 대해 정의하는데 사용합니다.

 

  • 피벗(Pivot) / 센터(Center)
  • 로컬(Local) / 글로벌(Global)

으로 이루어져 있습니다

 

  • (피벗)Pivot : 피벗은 기즈모의 위치가 오브젝트의 위치를 기준으로 이러한 기즈모를 표사해줍니다.
  • 센터(Center) : 센터는 기즈모의 표시점의 위치가 오브젝트의 중앙점을 기준으로 표시해줍니다. 오브젝트의 생김새의 중앙점을 표시하게 되는 것이죠. [오브젝트의 형태를 기준으로 중앙점이라 생각하면 됩니다]

 

  • 로컬(Local) : 선택한 오브젝트를 기준으로 한 좌표계
  • 글로벌(Global) : 실제 선택한 오브젝트에 대한 좌표계는 무시한체로 글로벌 좌표계를 기준으로 봅니다. 글로벌 좌표계는 게임 세상의 절대 좌표로 사용합니다.

 

이러한 요소는 기본 오브젝트에서 나오지 않지만, 디자이너가 오브젝트의 중심점을 어디로 보냐에 따라 값이 다르게 나옵니다. 예를 들어, 사람을 모델링해서 유니티에 임포트 시켰을 경우, 일반적으로 디자이너는 발 쪽에다가 피벗을 두는 경우가 많습니다. 그래야 땅 속으로 들어가지 않으니까요. 이렇게 디자인하는 요소에 따라 기즈모 핸들 포지션은 중요합니다. 

 

일반적으로 기즈모 핸들 포지션은 기본적으로 피벗/글로벌 의 형태로 두고 게임 제작을 많이 합니다. 이는 취향 차이이지만 피벗을 기준으로 만들어진 경우가 많아서 피벗 / 상대좌표계는 좌표의 혼동이 있을 가능성이 있으므로 글로벌 좌표계를 많이 사용합니다. 

 


여기에 추가로 우리의 시점 관리가 힘든 경우가 많습니다. 그래서 시점 관리를 편하게 하는 몇 가지 단축키를 살펴보도록 하겠습니다. 

 

  • Alt를 누른체로 마우스를 클릭해서 이리저리 돌려보면 특정 축을 기점으로 시야가 회전함을 알 수 있습니다. 

Alt를 누른체로 화면 시야 이동


  • Move 도구나 Transform을 움직이는 동안 Ctrl키를 누르고 있으면 특정 단위 만큼씩 움직일 수 있습니다. 뒷자리가 소수가 나오는 것이 아닌 딱딱 떨어지는 값이 나옵니다. 

 


  • Move를 사용할 때, Shift와 Control키를 누르고 있으면, 두 오브젝트가 충돌하지 않게 이동시켜 줍니다. 만약 겹쳐지려고 하면 오브젝트를 조금 더 먼 곳으로 이동 시켜줍니다. (겹침 방지 해주는 것이죠.)


  • Rotate 회전 툴을 사용할 때, Shift와 Control 키를 누르고 방향을 이동시키면 특정 방향을 바라보게 만들 수 있습니다. 특히, 특정 오브젝트 방향으로 바라볼 수 있도록 해줍니다. 

오브젝트에서Collider 표면의 한 점을 향해 회전하는 것을 볼 수 있습니다.


  • 버텍스 스냅 : Move나 Transform도구를 사용하고 있을때, V를 누르면 버텍스 스냅 모드가 활성화 됩니다. 버텍스 스냅 모드는 오브젝트를 정렬할 때 유용하게 쓰이는데, 다른 오브젝트에 딱 붙이도록 도와줍니다. 

버텍스 스냅을 통해 오브젝트를 정리하면 빠르게 오브젝트 정리가 한결 쉬워짐니다. 

위에서 V를 누르는 순간 찰싹 달라붙는 것을 확인할 수 있습니다. 


이번 시간에는 기즈모를 사용하는 방법과 시점 그리고 유용한 단축키를 알아보았습니다. 

단순히 눈으로만 보시지 마시고 항상 실습을 하면서 진행하시기 바랍니다!

 

* 공감과 댓글은 글을 이어가는데 힘이 됩니다. 감사합니다.

 

 

+ Recent posts