|
메서드 | 설명 |
Console.Beep() | 경고음 출력 |
Console.Clear() | 화면 지움 |
Console.SetCursorPosition( int left, int top) | 커서의 위치 설정 |
Console.SetWindowSize( int width, int height) | 콘솔 창의 높이와 너비를 설정 |
Thread.Sleep(int millisecondsTimeout) | 지정된 시간 동안 현재 스레드를 일시 중단함. using System.Threading 선언 필요 |
[표 7-1] 콘솔 메서드와 스레드 메서드
위 [표 7-1]은 Console 클래스에서 제공하는 메서드이며 Sleep() 메서드는 Threading 클래스에서 제공하는 메서드이다.
위의 메서드는 모두 static 메서드이므로 클래스를 생성하지 않고 사용할 수 있다.
다음은 Console 클래스에서 제공하는 속성으로 일반 변수의 형식을 가지고 있지만 이 속성을 통해 목적하는 임의의 값을
설정하거나 가져올 수 있다.
즉 변수의 형태를 가지고 있지만 함수적인 역할을 하는 것이 속성이라 할 수 있다.
속성 | 설명 |
Console.BackgroundColor | 콘솔의 배경색을 가져오거나 설정 |
Console.ForegroundColor | 콘솔의 전경색을 가져오거나 설정 |
[표 7-2] 콘솔 속성
다음 소스는 콘솔 창의 크기를 100, 40으로 하고 “Hello World” 라는 문자열을 임의의 좌표로 30개 출력하면서 문자열의 색상을
랜덤하게 출력하는 소스이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | static void Main(string[] args) { Console.SetWindowSize(100, 40); Random rand = new Random(); ConsoleColor[] Color = { ConsoleColor.Blue, ConsoleColor.Cyan, ConsoleColor.Red, ConsoleColor.Yellow, ConsoleColor.Green, ConsoleColor.Magenta, ConsoleColor.Gray }; while (true) { Console.Clear(); for (int i = 0; i < 30; i++) { Console.ForegroundColor = Color[rand.Next(6)]; Console.SetCursorPosition(rand.Next(100), rand.Next(40)); Console.Write("Hell World!"); } Thread.Sleep(200); } } |
[소스 7-1] 30개의 “Hello World”와 임의의 색상으로 출력
[실행 결과]
7.3 기획서 작성
프로젝트를 할 때는 생각에서부터 시작하여 구현에 이르기까지 전체적인 사항과 세부적인 사항이 필요하다.
시작은 생각에서 시작했지만 그것이 보다 분명한 형태와 기능을 가지게 되는 시점은 문서로써 작성되고 정리가 되었을 때이다.
그러므로 프로젝트를 할 때는 물론 혼자서 프로그래밍을 하더라도 프로젝트에 대한 기획서를 작성해 볼 필요가 있으며 오히려
이 부분이 프로젝트에 들어갈 코드를 더욱 간결하게 하고 명확하게 한다.
프로젝트 기획서를 작성해 보는 시간은 실제 프로젝트에 필요한 여러 가지 사항을 미리 생각해 보는 것이므로 기획서의 내용이
100% 구현되지는 않지만 계속 보완되면서 프로그램의 완성도는 높아지게 된다.
또한 프로젝트 기획서라고 해서 거창한 내용을 요구하는 것이 아니라 생각한 아이디어를 글로 나열하는 정도로 구성하면 된다.
완성도가 있는 프로그램을 한 번에 완성할 수 있는 것도 중요하지만 앞으로 개선되어야 할 사항에 대해서 나열하는 것도
중요하므로 이런 부분들을 프로젝트 기획서 안에 나열하면 된다.
기획서를 작성하는 것에 너무 많은 시간을 투자하면 실제 코딩해야할 시간이 줄어들므로 적절하게 분배를 잘 해야 한다.
실제로 기획한 내용은 코딩을 하면서 부분적으로 수정되므로 한번 작성한 기획서가 프로젝트 끝까지 가는 경우는 상당히 드물다.
그러므로 기획서는 항상 수정되고 보완될 수 있다는 것을 늘 생각해야 한다.
스마트 밥솥 프로젝트는 크게 개요, 기능, 개선 사항으로 나눴으며 주제에 맞춰 글로 나열하면 된다. 실제로 기획서를 작성해 보면
구현하려는 내용이 정리가 되며 수정되거나 보완될 때는 처음 생각한 부분의 잘못을 다시 살펴보게 됨으로 설계하는 능력이
점차적으로 유연하게 된다.
본 스마트 밥솥 프로젝트에 대한 기획서는 나우캠퍼스(www.nowcampus.co.kr)의 프로젝트 소식란에 있으므로 이 내용을
참고하기 바란다.
7.4 프로그램 화면
스마트 밥솥이라는 유형적인 기기를 코딩으로 구현할 때는 화면 디자인이 상당히 중요하다.
화면 디자인에 따라 시각적인 요소와 사용자 인터페이스가 결정되며 이 부분에 의해 코드의 양과 형태가 결정된다.
만약 화면 디자인이 바뀐다면 코드가 여기에 따라 바뀌게 되므로 이를 잘 고려해서 화면을 여러 번 설계해 볼 필요가 있다.
그리고 복잡한 화면보다는 단순하지만 구현하려는 기능을 전부 적용할 수 있는 현재는 중요하므로 여기에 맞춰 화면을 디자인해야
한다.
1) 전체 화면
[그림 7-1] 전체 화면
2) 전원 On /Off
[그림 7-2] 밥솥 전원 On
3) 뚜껑 열기, 닫기
[그림 7-3] 밥솥 뚜껑 열기
4) 쌀통에 쌀 보충
[그림 7-4] 쌀통에 쌀 보충 메뉴 출력
5) 밥솥 취사 전 동작
[그림 7-5] 취사 전 과정 출력
6) 취사와 보온 동작 화면
[그림 7-6] 취사와 보온 출력
7.5 메뉴
메뉴는 사용자 인터페이스에 해당하며 유동적인 데이터를 외부로부터 입력 받아 프로그램에 적용할 수 있는 부분이다.
그러므로 쉽게 사용할 수 있도록 디자인해야 한다.
메뉴는 두 가지 형태로 생각해 볼 수 있다.
첫 번째는 메뉴를 번호로 선택하는 형태이다.
두 번째는 상하키를 이용하여 메뉴를 선택하는 형태이다.
[그림 7-7] 번호와 커서를 이용한 메뉴
첫 번째 방법은 사용자가 번호를 잘못 입력할 수 있지만 두 번째 방법은 사용자의 입력 실수 없이 메뉴를 선택할 수 있게 한다.
본 프로젝트에서는 두 번째 방법을 사용하여 이를 위해 Console.BackgroundColor과 Console.ForegroundColor을 사용하면 된다.
단순히 콘솔의 배경과 전경 색상을 바꾸는 것이지만 위 [그림 7-7]과같이 메뉴를 선택할 수 있는 바를 출력할 수 있게 한다.
위와 같이 메뉴에 바를 출력하는 코드는 상당히 단순하며 간단히 커서 키의 입력에 따라 메뉴에 해당되는 문자열의 배경색과
전경색을 바꾸어 주는 형태로 하면 된다.
다음의 소스는 메뉴에 해당되는 문자열을 입력받고 상하키에 의해 문자열의 배경색과 전경색을 반전시키는 코드이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static void Menu(int x, int y, string[] MenuItem) { ConsoleKeyInfo InputKey;
while(true) { for(int i = 0; i < MenuItem.Length; i++) { if(MainMenuIndex == i) { Console.BackgroundColor = ConsoleColor.Yellow; Console.ForegroundColor = ConsoleColor.Black; Console.SetCursorPosition(x, y + i); Console.Write(MenuItem[i]); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; } else { Console.SetCursorPosition(x, y + i); Console.Write(MenuItem[i]); } } |
[소스 7-2] 메뉴 바 출력
위의 7행부터 23행까지는 현재 커서의 인덱스에 따라 메뉴 바를 출력해 주는 부분이다.
11행과 12행은 선택된 메뉴 바를 출력해 주는 부분이며 15행과 16행은 다음 메뉴를 정상적으로 출력해 주기 위해서 복원해 주는
부분이다.
Console.BackgroundColor과 Console.ForegroundColor는 다음에 다시 설정될 때까지 현재 설정한 색상을 모든 화면에
적용하므로 15, 16행과 같이 원래 상태로 복원해 주는 부분이 필요하다.
다음 소스는 연결되는 코드로써 커서 키에 따라 처리되는 부분으로 입력된 커서 키에 따라 메뉴의 인덱스를 실제로 가감하는
부분이다.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | InputKey = Console.ReadKey(true); if (InputKey.Key == ConsoleKey.Enter) break; else if(InputKey.Key == ConsoleKey.UpArrow) { MainMenuIndex--; if (MainMenuIndex < 0) MainMenuIndex = 0; }else if(InputKey.Key == ConsoleKey.DownArrow) { MainMenuIndex++; if (MainMenuIndex == MenuItem.Length) MainMenuIndex = MenuItem.Length - 1; } } } |
[소스 7-3] 커서 키 처리
7.6 전체 구조
■ 화면 디자인
화면 디자인에 관한 프로그래밍은 특별히 알고리즘을 요구하는 것이 아니므로 다음과 같이 단순 출력하는 메서드를 작성하여
출력한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | static void RiceBox(int x, int y) { int height = 7; Console.SetCursorPosition(x, y); Console.Write("┌──────────┐"); for (int i = 1; i < height; i++) { Console.SetCursorPosition(x, y + i); Console.Write("│ │"); }
Console.SetCursorPosition(x, y + height); Console.Write("└──────────┘"); Console.SetCursorPosition(x + 10, y + 2); Console.Write("밥솥"); Console.SetCursorPosition(x, y + 6); Console.Write("┤"); // 전원 부분 } |
[소스 7-4] 밥솥 출력 함수
■ 메뉴에 따른 전체 처리 구조
본 프로그램은 메뉴에 따라 개별 동작하는 프로그램이므로 메뉴의 선택에 따라 각 처리 과정이 나눠지게 된다.
메뉴를 선택하게 되면 메뉴 인덱스에 따라 선택된 메뉴를 알 수 있으므로 다음과 같은 분기 구조가 메인 프로그램의 큰 구조를
구성한다.
while(true) { MenuIndex = Menu(); // 메뉴 출력 및 선택
switch( MenuIndex ) { case 0 : // 전원 처리 break; case 1 : // 뚜껑 처리 break; case 2 : // 취사 처리 break; case 3: // 생략......... break; } } |
7.7 코드 보기
■ 밥솥 데이터 구조
밥솥이라는 객체는 여러 데이터를 기반으로 밥솥의 역할을 하게 되며 밥솥을 하나의 단위로 다루기 위해서는 다음과 같이
구조체를 통해 밥솥에 필요한 데이터를 정의할 수 있다.
enum CookerProcess { None, Riceing, Watering, Washing, Droping, Cooking, Completion, Keeping }; struct RiceCookerInfo { public bool CoverOpenClose; // 뚜껑 열기 닫기 public bool Power; // 전원 On/Off public CookerProcess State; // 밥솥 진행 상태 public int Rice; // 쌀의 양 g 기준, 출력 때 환산 public int Water; // 물의 양 미리 리터 기준, 출력때 환산 public int Number; // 인원수
public RiceCookerInfo(int _Rice, int _Water) { Rice = _Rice; Water = _Water; State = CookerProcess.None; CoverOpenClose = false; Power = false; Number = 0; } } |
[소스 7-5] 밥솥 구조체
C#에서 구조체의 생성자를 선언할 때는 반드시 매개변수가 있어야 하므로 위와 같이 생성자를 선언하였다.
위의 변수들은 밥솥의 동작을 결정짓기 위한 것으로 특별한 연산이 필요 없음을 알 수 있다.
■ 사운드 출력
C#에서 사운드는 SoundPlayer 클래스를 사용하며 using System.Media를 선언해야 한다.
이 클래스를 통해 사용할 수 있는 사운드 파일 형식은 wav 파일만 사용할 수 있으며 사용 방법은 다음과 같다.
SoundPlayer Sound = new SoundPlayer(); Sound.SoundLocation = “출력할 wav 파일명”; Sound.Load(); Sound.Play(); |
[소스 7-6] 사운드 출력
SondPlayer 객체를 생성한 후에 SoundLocation 멤버에 출력할 파일명만 지정하고 Load()와 Play()를 호출하기만 하면
사운드 출력이 된다.
이때 사운드가 다 출력될 때까지 Play()가 대기하지 않으므로 필요에 따라 약간 대기할 수 있도록 Thread.Sleep()을 사용하여
사운드가 다 출력될 때까지 대기 상태로 만들거나 사운드 출력이 완전히 끝날 때까지 정지하도록 하는 PlaySync()를 호출하면 된다.
■ 취사 코드
취사는 전원이 들어오는 상태와 밥솥 뚜껑이 닫히고 식사할 인원수에 따라 쌀과 물이 모두 준비되어야 취사 상태로 들어 간다.
만약 인원수에 따라 씻을 물과 밥할 물이 모자라면 보충해 달라는 메시지를 미리 띄워 보충이 다 되었다면 전원과 밥솥 뚜껑의
상태를 최종 점검하고 다음과 같은 과정을 반복한다.
[그림 7-8]
위의 코드 흐름이 순차적이며 2회 반복하는 부분만 for문으로 2회 반복하면 된다.
- 쌀 넣기 전에 조사하는 코드
위 [그림 7-8]에서 쌀 넣기를 하기 전에 인원수, 쌀, 물의 상태를 조사하여 메시지를 띄우는 코드를 살펴보면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | if (!RCInfo.Power) { // 전원 경고 메시지 박스 MessageBox(51, 27, "전원이 꺼져 있습니다"); Console.ReadKey(true); break; }
if (RCInfo.CoverOpenClose) { // 뚜껑 경고 메시지 박스 MessageBox(51, 27, "뚜껑이 열려져 있습니다"); Console.ReadKey(true); break; }
if (RCInfo.Number == 0) { // 인원수 메시지 박스 MessageBox(51, 27, "인원수를 입력해 주세요"); Console.ReadKey(true); break; }
// 일정한 시간 간격으로 쌀 넣기 -> 물 넣기 -> 쌀 씻기 -> 배수 -> 2 번 반복 // 취사 -> 완료 -> 보온 // 필요한 쌀과 물의 공급이 되어 있는지를 체크한다. int Rice = RCInfo.Rice - (RCInfo.Number * 160); // 쌀 일인분 160g if (Rice < 0) { MessageBox(51, 27, " ??? 쌀 부족 ???"); Sound.SoundLocation = "쌀을보충해주세요.WAV"; Sound.Load(); Sound.Play(); Console.ReadKey(true); break; }
// 물통에서 물 빼기 (대략 인원수 x 170 ml ) * 5; //물로 씻는 거 2번 취사 // 씻을 때는 1인분 물 170의 두 배 사용, 필요한 물은 씻기 2번(170*4 ml) 취 int Water; Water = RCInfo.Water - (RCInfo.Number * 170)*5; if (Water < 0) { MessageBox(51, 27, " ??? 물 부족 ???"); Sound.SoundLocation = "물보충.WAV"; Sound.Load(); Sound.Play(); Console.ReadKey(true); break; } |
[소스 7-7]
위 소스 42행을 보면 쌀을 씻기 위한 물의 양을 대략적으로 계산하는 부분으로 1인분에 대한 물이 170 ml이므로 씻을 때는
그 두배로 씻는 것으로 했을 때 42행과 같이 계산하여 물통의 물의 양을 조사한다.
위의 소스를 보면 별다른 알고리즘 보다는 기본적인 문법만을 이용하여 단순하게 처리하고 있는 것을 알 수 있다.
- 쌀 넣기 부분
쌀 넣기 코드는 단순히 쌀을 출력해 주는 정도로만 구현하며 코드는 다음과 같다.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | RCInfo.State = CookerProcess.Riceing; RiceInfo(RCInfo);
Sound.SoundLocation = "쌀넣기.WAV"; Sound.Load(); Sound.Play();
Console.SetCursorPosition(24, 12); Console.Write("쌀 넣기"); Console.SetCursorPosition(18, 13); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 14); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 15); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 16); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 17); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); RCInfo.Rice = RCInfo.Rice - (RCInfo.Number * 160); // 1인분 160g RiceHeight(50, 2, RCInfo.Rice); Thread.Sleep(3000); // 3초 정도 |
[소스 7-8]
- 물 넣기와 쌀 씻기, 배수
이 부분은 위 [그림 7-8]과 같이 2회 반복하는 부분으로 단순히 일정시간동안 출력해 주기만 하면 된다.
배수 할 때는 물이 실제로 밥솥에서 빠지는 것이 출력되면 좋으므로 일정간격으로 배경색인 파란색과 검정색의 높이 수를 가감해
줘서 마치 물이 빠지는 것과 같이 출력해 보자.
이를 위해서는 타이머를 사용하여 점차적으로 진행되도록 하며 소스 코드는 다음과 같다.
1) 물 넣기 부분
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | for (int i = 0; i < 2; i++) { // Note: 물 넣기 --> 파란 색 보여 주기 RCInfo.State = CookerProcess.Watering; RCInfo.Water = RCInfo.Water - (RCInfo.Number * 170 * 2); RiceInfo(RCInfo);
Sound.SoundLocation = "water_in.WAV"; Sound.Load(); Sound.Play();
Console.BackgroundColor = ConsoleColor.Black; Console.SetCursorPosition(24, 12); Console.Write("물 넣기"); Console.BackgroundColor = ConsoleColor.Blue; Console.SetCursorPosition(18, 13); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 14); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 15); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 16); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 17); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); WaterHeight(74, 2, RCInfo.Water); Thread.Sleep(3000); // 3초 정도 |
[소스 7-9]
2) 쌀 씻기 부분
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | // Note: 쌀 씻기 Sound.SoundLocation = "쌀씻기.wav"; Sound.Load(); Sound.Play(); RCInfo.State = CookerProcess.Washing; RiceInfo(RCInfo);
Console.BackgroundColor = ConsoleColor.Black; Console.SetCursorPosition(24, 12); Console.Write("쌀 씻기"); Console.BackgroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.White; Console.SetCursorPosition(18, 13); Console.Write("~ ~ ~ ~ ~ ~ ~ ~ ~ ~"); Console.SetCursorPosition(18, 14); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 15); Console.Write("~ ~ ~ ~ ~ ~ ~ ~ ~ ~"); Console.SetCursorPosition(18, 16); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); Console.SetCursorPosition(18, 17); Console.Write("~ ~ ~ ~ ~ ~ ~ ~ ~ ~"); Thread.Sleep(3000); // 3초 정도 |
[소스 7-10]
3) 배수 부분
배수는 시각적으로 물이 빠지는 것과 같이 밥솥의 배경을 파란색에서 검은색으로 점차적으로 적용하면 된다.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | // Note: 물 배수 RCInfo.State = CookerProcess.Droping; RiceInfo(RCInfo);
Sound.SoundLocation = "water_out.WAV"; Sound.Load(); Sound.Play();
Console.BackgroundColor = ConsoleColor.Black; Console.SetCursorPosition(24, 12); Console.Write(" 배수 "); for (int j = 0; j < 5; j++) { // 지우기 Console.BackgroundColor = ConsoleColor.Black; for (int k = 0; k < j; k++) { Console.SetCursorPosition(18, 13 + k); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); }
// 물 출력 Console.BackgroundColor = ConsoleColor.Blue; for (int m = j; m < 5; m++) { Console.SetCursorPosition(18, 13 + m); Console.Write("⊙ ⊙ ⊙ ⊙ ⊙ ⊙ ⊙"); } Thread.Sleep(700); } } |
[소스 7-11]
135행부터 154행까지는 물의 배수를 시각적으로 보여주는 부분으로 검은색을 배경으로 출력하는 부분(138행~143행)과
푸른색으로 출력하는 부분(146행~ 151행)으로 구분되어 있는 것을 알 수 있다.
밥솥의 물의 총 높이는 5이므로 135행과 같이 5번 반복과 배경색을 전환하여 배수를 표현하고 있다.
7.8 정리
본 프로젝트에서 제외된 내용은 예약에 대한 부분이다.
예약은 현재 시각과 예약된 시각을 비교하여 자동으로
취사를 진행하는 과정이지만 이를 위해서는 현재 밥솥의 상태를 관리하는 프로세스와 예약된 시각을 조사하는 프로세스가 동시에
동작해야 하므로 스레드가 필수적이라고 볼 수 있다.
이런 부분은 기초 문법의 활용에서 많이 벗어난 내용이므로 다음에 스레드를 다룰 때 다시 적용해 볼 내용이다.
기초 문법만으로도 현재의 같은 프로젝트는 얼마든지 가능하며 특수문자를 이용하여 게임 제작도 가능하다. 그리고 전기밥솥 외에
전기장판, 전기청소기, 스마트 침대, 건강관리 프로그램 등 다양한 프로그램을 작성할 수 있다.
이번 프로젝트는 기초 문법을 활용한 프로그래밍의 예에 불과하지만 C# 기초 문법을 활용하여 프로그래밍으로 풀어 내는데는
충분하리라 생각한다.
중요한 것은 학습자 스스로가 기획하고 프로그래밍으로 실제 해보는 것이 중요하다.
그리고 필요에 따라 MSDN의 검색을 통해 필요한 클래스를 찾아 적용해 보는 것도 상당히 도움이 될 것이다. C#에 제공되는
클래스 라이브러리(BCL)이 워낙 방대하므로 일일이 학습하기 보다는 필요에 따라 찾아보고 적용해 보는 것이 중요하다.
C# 프로그래밍은 잘 만들어진 클래스 라이브러리를 잘 찾아 적용하는 프로그래밍이라고 해도 과언이 아니다.
첫댓글 파일 다운이 안되요 ㅠㅠ