C# 콘솔 입출력, ReadLine과 출력 서식
목차
콘솔 프로그램이 사용자와 글자를 주고받는 방법을 정리합니다. Write와 WriteLine으로 출력하고, 서식 지정자와 자리 맞춤으로 숫자와 표를 보기 좋게 맞춥니다. 그다음 ReadLine으로 입력을 받고, 잘못된 입력이 들어오면 다시 묻는 방법을 익힙니다. 글자색과 Windows 콘솔의 한글 표시도 짚고, 마지막에는 입력을 받는 성적 평균 계산기를 만듭니다.
이 글은 Visual Studio 콘솔앱 만들기, 파일 나누기와 디버깅에서 이어집니다. 지난 글의 예제는 정해진 데이터로만 동작했습니다. 이번 글부터는 사용자가 입력한 값으로 동작하는 프로그램을 만듭니다. 예제는 지금까지처럼 콘솔 앱 프로젝트의 Program.cs를 바꾸고 Ctrl+F5로 실행합니다.
실행 결과는 .NET 10 SDK로 직접 빌드하고, 한국어 문화권(ko-KR)으로 실행해서 얻은 것입니다. 입력을 받는 예제는 입력을 한 줄씩 넣으며 화면에 보이는 그대로 기록했습니다. 그래서 입력한 글자도 질문 옆에 함께 보입니다. 한글과 기호가 보이는 모양은 콘솔 글꼴과 설정에 따라 다를 수 있어서, 해당하는 곳마다 따로 적었습니다.
1. Write와 WriteLine

지금까지 출력은 거의 Console.WriteLine으로 했습니다. 출력 메서드에는 Write도 있습니다. 둘의 차이는 출력한 뒤 줄을 바꾸느냐입니다.
// Console.Write와 Console.WriteLine
Console.Write("가");
Console.Write("나");
Console.WriteLine("다"); // 출력한 뒤 줄을 바꿉니다
Console.WriteLine(); // 빈 줄 하나
// 값을 넣는 방법 1: 자리 표시자 {0}, {1} (복합 서식)
Console.WriteLine("이름: {0}, 나이: {1}", "홍길동", 20);
// 값을 넣는 방법 2: 문자열 보간 $"..."
string name = "김철수";
int age = 21;
Console.WriteLine($"이름: {name}, 나이: {age}");
Console.WriteLine($"내년 나이: {age + 1}");
Console.WriteLine($"중괄호를 글자로 쓰려면 두 번: {{name}}");
// 숫자, bool도 그대로 출력됩니다
Console.WriteLine(3.5);
Console.WriteLine(true);
// 입력을 같은 줄에서 받을 때는 Write로 질문을 씁니다 (입력을 받는 예제에서 계속 씁니다)
Console.Write("나이를 입력하세요: ");
Console.WriteLine("← 커서가 여기 남습니다");
실행 결과입니다.
가나다
이름: 홍길동, 나이: 20
이름: 김철수, 나이: 21
내년 나이: 22
중괄호를 글자로 쓰려면 두 번: {name}
3.5
True
나이를 입력하세요: ← 커서가 여기 남습니다
줄을 바꾸느냐, 안 바꾸느냐
Write는 글자를 출력하고 커서를 그 자리에 둡니다. 그래서Write("가"),Write("나")다음의WriteLine("다")까지 한 줄에가나다로 이어집니다.WriteLine은 출력한 뒤 줄을 바꿉니다. 괄호 안을 비운Console.WriteLine();은 빈 줄 하나를 출력합니다.- 마지막 두 줄처럼 질문을
Write로 쓰면 커서가 질문 바로 뒤에 남습니다. 입력을 받을 때 이렇게 쓰면 답이 질문 옆에 입력됩니다. 이 글의 입력 예제는 모두 이 방법을 씁니다.
값을 넣는 두 가지 방법
- 방법 1은 자리 표시자
{0},{1}입니다. 공식 문서에서는 복합 서식(composite formatting)이라고 부릅니다. 문자열 뒤에 쓴 값이 0번, 1번 순서로 들어갑니다. 옛 코드나 문서에서 자주 볼 수 있습니다. - 방법 2는 지금까지 써 온 문자열 보간
$"..."입니다. 중괄호 안에 변수 이름이나{age + 1}같은 식을 바로 씁니다. 이 시리즈는 이 방법을 기본으로 씁니다. - 중괄호 자체를 글자로 출력하려면
{{,}}처럼 두 번 씁니다. true는True로 출력됩니다. 첫 글자가 대문자입니다.
헷갈리기 쉬운 점
- Write 다음에 출력할 것이 없으면 줄이 안 바뀐 채로 끝납니다. 마지막 출력을
Write로 하면 다음 출력이 그 줄 뒤에 붙습니다. 줄을 끝내려면WriteLine을 씁니다. - 문자열 보간은 맨 앞의 $를 빼먹으면 동작하지 않습니다.
"이름: {name}"처럼$가 없으면 중괄호가 글자 그대로 출력됩니다. - 자리 표시자 번호는 0부터 셉니다. 값이 두 개면
{0},{1}입니다. 값보다 큰 번호를 쓰면 실행 중에 예외가 납니다.
2. 출력 서식과 자리 맞춤
![출력 서식과 자리 맞춤 그림({값,폭:서식}으로 숫자 모양 정하기). 맨 위 {값,폭:서식} 해부: int price = 1234567; Console.WriteLine($'[{price,12:N0}]');에서 price = 값, 12 = 폭(12칸), N0 = 서식(천 단위 쉼표)으로 나누고, 12칸 모눈에 1,234,567이 오른쪽 정렬된 그림(12칸 안에서 오른쪽 정렬)과 [{no,-5}] → [7 ] 폭이 음수면 왼쪽. '자주 쓰는 서식' 목록: {price:N0} 천 단위 쉼표 1,234,567, {price:N2} 쉼표 + 소수 2자리 1,234,567.00, {pi:F2} 소수 2자리(쉼표 없음) 3.14, {no:D3} 정수를 3자리로(정수만) 007, {rate:P1} 백분율, 소수 1자리 85.7%, {price:C} 통화, 기호는 문화권 따라 ₩1,234,567, 메모 '결과는 문화권 ko-KR 기준, ₩ 표시는 콘솔에 따라 다를 수 있어서 {price:N0}원 모양을 권함'. '한글은 화면에서 두 칸': {names[i],-10}은 글자 수로 맞추는데 한글 한 글자는 화면에서 두 칸이라, 모눈 위 Tom 줄과 김철수 줄을 비교하면 김철수 줄의 90/85 칸이 3칸 밀림(주황 칸 = 한글 한 글자, 터미널·글꼴에 따라 다를 수 있음). 쉬운 해결은 길이가 제각각인 이름은 맨 뒤에: 실행 결과 일부 1번 90점 Tom, 2번 85점 김철수, 3번 77점 Alexander. 아래 '반올림 함정': {2.5:F0} {3.5:F0} {1.005:F2} → 2 4 1.00(딱 가운데면 끝자리가 짝수인 쪽으로, 1.005는 실제로는 1.00499…로 저장됨), MidpointRounding.AwayFromZero → 3(학교식 반올림). 초보가 2.5를 반올림하면 당연히 3이라고 하고, 선배가 F0으로 찍으면 2가 나온다고 답함](https://blog.kakaocdn.net/dna/HeVRS/dJMcacE0z6v/AAAAAAAAAAAAAAAAAAAAAGC5RDvrpY-fYLRt1x8zKZItVpYqvFtn4uiL93qd9fP2/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1793458799&allow_ip=&allow_referer=&signature=H2UgjZQavmwcoyvaIq4a7dFL8cI%3D)
앞선 글들에서 ToString("N0"), {average:F1} 같은 코드를 설명 없이 썼습니다. 이 장에서 정식으로 정리합니다. 중괄호 안에 {값:서식}으로 서식을, {값,폭}으로 자리 폭을 정합니다.
// 출력 서식: 숫자 서식 지정자와 자리 맞춤
using System.Globalization;
Console.WriteLine($"현재 문화권: {CultureInfo.CurrentCulture.Name}");
int price = 1234567;
double pi = Math.PI;
int no = 7;
double rate = 0.8567;
// 1) 서식 지정자 {값:서식}
Console.WriteLine($"N0 → {price:N0}"); // 천 단위 쉼표, 소수 0자리
Console.WriteLine($"N2 → {price:N2}"); // 천 단위 쉼표, 소수 2자리
Console.WriteLine($"F2 → {pi:F2}"); // 소수 2자리 (쉼표 없음)
Console.WriteLine($"F4 → {pi:F4}");
Console.WriteLine($"D3 → {no:D3}"); // 정수를 3자리로, 빈자리는 0
Console.WriteLine($"P1 → {rate:P1}"); // 백분율, 소수 1자리
Console.WriteLine($"C → {price:C}"); // 통화 (문화권에 따라 기호가 달라짐)
// 2) 자리 맞춤 {값,폭} 양수 = 오른쪽 정렬, 음수 = 왼쪽 정렬
Console.WriteLine($"[{no,5}]");
Console.WriteLine($"[{no,-5}]");
Console.WriteLine($"[{price,12:N0}]"); // 폭과 서식을 같이: {값,폭:서식}
// 3) 표 모양으로 출력
string[] items = { "Coffee", "Sandwich", "Cake" };
int[] prices = { 4500, 7800, 12000 };
Console.WriteLine($"{"Item",-10}{"Price",8}");
for (int i = 0; i < items.Length; i++)
{
Console.WriteLine($"{items[i],-10}{prices[i],8:N0}");
}
// 4) 날짜 서식 (DateTime.Now 대신 고정 날짜를 씁니다)
DateTime day = new DateTime(2026, 10, 6, 14, 5, 0);
Console.WriteLine($"{day:yyyy-MM-dd}");
Console.WriteLine($"{day:yyyy년 M월 d일 dddd}");
Console.WriteLine($"{day:HH:mm}");
Console.WriteLine($"{day:tt h:mm}");
// 5) 같은 서식을 string.Format, ToString으로
string a = string.Format("{0,10:N0}원", price);
string b = price.ToString("N0") + "원";
Console.WriteLine(a);
Console.WriteLine(b);
// 6) F 서식의 반올림 주의: 0.5가 항상 올라가지는 않습니다
Console.WriteLine($"{2.5:F0} {3.5:F0} {1.005:F2}");
Console.WriteLine($"{Math.Round(2.5, MidpointRounding.AwayFromZero):F0}");
실행 결과입니다(한국어 문화권 ko-KR).
현재 문화권: ko-KR
N0 → 1,234,567
N2 → 1,234,567.00
F2 → 3.14
F4 → 3.1416
D3 → 007
P1 → 85.7%
C → ₩1,234,567
[ 7]
[7 ]
[ 1,234,567]
Item Price
Coffee 4,500
Sandwich 7,800
Cake 12,000
2026-10-06
2026년 10월 6일 화요일
14:05
오후 2:05
1,234,567원
1,234,567원
2 4 1.00
3
자주 쓰는 서식 지정자
| 서식 | 뜻 | 예 | 결과 |
|---|---|---|---|
N0, N2 |
천 단위 쉼표, 소수 n자리 | {price:N0} |
1,234,567 |
F2, F4 |
소수 n자리(쉼표 없음) | {pi:F2} |
3.14 |
D3 |
정수를 n자리로, 빈자리는 0 | {no:D3} |
007 |
P1 |
100을 곱하고 % 붙임, 소수 n자리 | {rate:P1} |
85.7% |
C |
통화, 기호는 문화권을 따름 | {price:C} |
₩1,234,567 |
결과 칸의 값은 위 실행 결과에서 가져왔습니다. N0의 쉼표, C의 통화 기호, 날짜의 요일과 오후 같은 글자는 PC의 문화권 설정을 따릅니다. 그래서 예제 첫 줄에서 현재 문화권을 출력해 두었습니다. 한국어 Windows에서도 같은 결과가 나올 것으로 보이지만, 날짜 표기 같은 세부는 환경에 따라 다를 수 있습니다.
자리 맞춤 {값,폭:서식}
{no,5}처럼 폭이 양수면 오른쪽 정렬,{no,-5}처럼 음수면 왼쪽 정렬입니다.- 서식과 함께 쓰면
{price,12:N0}입니다. 값, 폭, 서식 순서입니다. - 표를 만들 때는 이름 칸을
-10으로 왼쪽 정렬하고 가격 칸을8로 오른쪽 정렬하면 줄이 맞습니다. - 공식 문서에 따르면 글자가 폭보다 길면 폭은 무시됩니다. 내용이 잘리지는 않습니다.
string.Format("{0,10:N0}원", price)와price.ToString("N0")도 같은 서식을 씁니다.
한글은 화면에서 두 칸
{name,-10}은 글자 수로 폭을 맞춥니다. 그런데 한글은 보통 화면에서 영문 두 칸 너비로 그려집니다. 그래서 한글 이름이 들어가면 줄이 어긋날 수 있습니다.
// 한글 자리 맞춤 문제: {값,-10}은 "글자 수"로 맞추는데, 한글은 화면에서 두 칸을 차지합니다
string[] names = { "Tom", "김철수", "Alexander", "이" };
int[] scores = { 90, 85, 77, 100 };
Console.WriteLine("[1] {이름,-10} 으로 맞춤");
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine($"|{names[i],-10}|{scores[i],5}|");
}
Console.WriteLine("[2] 화면 폭을 계산해서 맞춤");
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine($"|{PadRightWide(names[i], 10)}|{scores[i],5}|");
}
Console.WriteLine("[3] 길이가 제각각인 글자는 맨 뒤에 두기");
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine($"{i + 1,2}번 {scores[i],5}점 {names[i]}");
}
// 한글 음절·자모는 2칸, 나머지는 1칸으로 계산해서 공백을 채웁니다
static string PadRightWide(string text, int width)
{
int used = 0;
foreach (char c in text)
{
used += IsWide(c) ? 2 : 1;
}
return text + new string(' ', Math.Max(0, width - used));
}
static bool IsWide(char c)
{
return (c >= '\uAC00' && c <= '\uD7A3') // 한글 음절 가~힣
|| (c >= '\u1100' && c <= '\u115F') // 한글 자모(초성)
|| (c >= '\u3131' && c <= '\u318E'); // 한글 호환 자모 ㄱ~ㆎ
}
실행 결과입니다. 블로그 코드 블록의 글꼴에 따라 아래 정렬이 다르게 보일 수 있습니다.
[1] {이름,-10} 으로 맞춤
|Tom | 90|
|김철수 | 85|
|Alexander | 77|
|이 | 100|
[2] 화면 폭을 계산해서 맞춤
|Tom | 90|
|김철수 | 85|
|Alexander | 77|
|이 | 100|
[3] 길이가 제각각인 글자는 맨 뒤에 두기
1번 90점 Tom
2번 85점 김철수
3번 77점 Alexander
4번 100점 이
- [1]에서
김철수는 3글자라 공백 7개가 붙습니다. 한글이 두 칸으로 그려지는 화면에서는 6칸 + 7칸 = 13칸이 되어|가 3칸 밀립니다. - [2]는 한글을 두 칸으로 세어 공백을 채운 방법입니다. 이런 방법도 있다는 정도로만 보면 됩니다.
- [3]처럼 길이가 제각각인 글자(이름)를 줄의 맨 뒤에 두면 앞의 숫자 칸은 항상 맞습니다. 가장 간단해서 이 글의 종합 예제도 이 방법을 씁니다.
한글이 정확히 두 칸으로 그려지는지는 터미널과 글꼴에 따라 다를 수 있습니다(문자 자체는 코드 페이지 949에서 정상 출력됨). 줄이 어긋나 보이면 터미널 글꼴이 한글을 지원하는 고정폭 글꼴인지 확인하고, 그래도 어긋나면 [3]처럼 한글이 들어가는 칸을 맨 뒤로 옮깁니다.
헷갈리기 쉬운 점
- F 서식의 반올림은 학교에서 배운 반올림과 다를 수 있습니다. 위 결과에서
{2.5:F0}은 3이 아니라 2,{1.005:F2}는 1.01이 아니라 1.00이었습니다. 공식 문서에 따르면 .NET Core 2.1 이후에는 정확히 가운데인 값이면 끝자리가 짝수인 쪽을 고릅니다.1.005는 컴퓨터 안에서 정확히 저장되지 않아 실제로는 1.00499…에 가까운 값이라 1.00이 됩니다. 0.5를 항상 올리려면Math.Round(값, MidpointRounding.AwayFromZero)를 씁니다. - D 서식은 정수에만 씁니다. 실수에 쓰면 빌드는 되지만 실행 중에 예외가 납니다.
double score = 3.5;를{score:D3}로 출력하면 아래처럼 멈춥니다(경로는 줄여서 적었습니다).
Unhandled exception. System.FormatException: Format specifier was invalid.
at System.Number.GetFloatingPointMaxDigitsAndPrecision(Char fmt, Int32& precision, NumberFormatInfo info, Boolean& isSignificantDigits)
at System.Number.FormatFloat[TNumber,TChar](ValueListBuilder`1& vlb, TNumber value, ReadOnlySpan`1 format, NumberFormatInfo info)
at System.Number.TryFormatFloat[TNumber,TChar](TNumber value, ReadOnlySpan`1 format, NumberFormatInfo info, Span`1 destination, Int32& charsWritten)
at System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted[T](T value, String format)
at Program.<Main>$(String[] args) in Program.cs:line 3
- 첫 줄의
Format specifier was invalid.가 "서식 지정자가 잘못됐다"는 뜻입니다. 아래 줄들은 .NET 내부에서 어디를 거쳤는지 보여 주는 부분이라 지금은 맨 아래Program.cs:line 3만 보면 됩니다. - ₩ 표시는 콘솔에 따라 다를 수 있습니다.
C서식은 한국어 문화권에서₩기호를 붙입니다. 이 글자는 환경에 따라 깨질 수 있습니다. 그럴 때 확인할 점은 5장에서 정리합니다. 확실하게 하려면{price:N0}원처럼N0뒤에 "원"을 붙이는 방법을 권합니다. - 점수 평균을 F1로 보여 주는 정도는 문제없습니다. 반올림 차이가 중요한 돈 계산은
decimal타입을 쓰는 것이 좋습니다.
3. ReadLine과 string?
![ReadLine과 string? 그림(입력은 비어 있거나 null일 수도 있음). 위쪽 'ReadLine이 돌려주는 세 가지': Console.ReadLine()에서 세 갈래로 글자를 치고 Enter → ' 홍길동 ' 글자가 든 문자열, Enter만 → '' 빈 문자열, Ctrl+Z 누르고 Enter → null 입력 끝, 그 아래 string.IsNullOrWhiteSpace(name)의 결과가 각각 false, true, true(null·빈 문자열·공백만 있는 글자를 한 번에 검사). 가운데 '실제로 입력해 본 화면'(파랑 = 입력한 글자): 이름을 입력하세요: 홍길동 / 반갑습니다, 홍길동님! / 별명을 입력하세요(없으면 Enter): [Enter만] / 별명 없음 / 한 줄 더 입력하세요: [입력 끝] / 입력이 끝났습니다(ReadLine이 null을 돌려줌)., 앞뒤 공백은 Trim()으로 지움, Console.ReadLine() ?? ''는 왼쪽이 null이면 오른쪽 값('')을 씀. '? 를 무시하면 경고': string name = Console.ReadLine(); CS8600 null일 수 있는 값을 string에 넣음, name.Length CS8602 null일 수도 있는데 바로 씀, int.Parse(Console.ReadLine()) CS8604 null일 수 있는 값을 인수로 넘김, 경고만 나오고 빌드는 되지만 입력이 끝난 상태(null)로 실행하면 프로그램이 멈춤: int.Parse(Console.ReadLine())에 null이 오면 System.ArgumentNullException: Value cannot be null. (Parameter 's'), ! 를 붙여 경고만 끄면 NullReferenceException(null을 막아 주지 않음). 초보가 Enter만 누르면 null이냐고 묻고, 선배가 아니, 빈 문자열이고 null은 입력이 끝났을 때라고 답함](https://blog.kakaocdn.net/dna/bwTrNM/dJMcadDyaVi/AAAAAAAAAAAAAAAAAAAAAFf4q4tkPu1tNZLw_9TOTkOHbxblE9ZCmpqGRYu6rgxT/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1793458799&allow_ip=&allow_referer=&signature=x4QDKZ8T7R51KrVQX4p90oO4oxQ%3D)
Console.ReadLine()은 사용자가 Enter를 누를 때까지 입력한 한 줄을 문자열로 돌려줍니다. 공식 문서의 선언은 public static string? ReadLine();입니다. 돌려주는 타입이 string이 아니라 string?인 점이 중요합니다.
ReadLine이 돌려주는 세 가지
- 글자를 치고 Enter를 누르면: 입력한 글자가 든 문자열
- Enter만 누르면: 빈 문자열
""(null이 아님) - 입력이 끝나면:
null
공식 문서에 따르면 null은 더 읽을 줄이 없을 때 돌아옵니다. Windows 콘솔에서는 Ctrl+Z를 누르고 이어서 Enter를 누르면 입력이 끝납니다. 입력을 파일로 넣어 실행했는데 파일의 줄이 다 떨어졌을 때도 null입니다. 아래 화면 기록에서는 세 번째 질문에서 Ctrl+Z, Enter로 입력을 끝냈습니다. 입력을 끝낸 자리에 보이는 글자는 콘솔에 따라 다를 수 있습니다.
예제: 세 가지 경우 직접 보기
// Console.ReadLine은 string?(null일 수 있는 문자열)을 돌려줍니다
Console.Write("이름을 입력하세요: ");
string? name = Console.ReadLine();
if (string.IsNullOrWhiteSpace(name))
{
Console.WriteLine("이름이 입력되지 않았습니다.");
}
else
{
// 위에서 null이 아님을 확인했으므로 name.Trim()에 경고가 없습니다
Console.WriteLine($"반갑습니다, {name.Trim()}님!");
}
Console.Write("별명을 입력하세요(없으면 Enter): ");
string nickname = Console.ReadLine() ?? ""; // null이면 빈 문자열("")로
Console.WriteLine(nickname == "" ? "별명 없음" : $"별명: {nickname}");
Console.Write("한 줄 더 입력하세요: ");
string? more = Console.ReadLine();
if (more == null)
{
Console.WriteLine();
Console.WriteLine("입력이 끝났습니다(ReadLine이 null을 돌려줌).");
}
else
{
Console.WriteLine($"받은 값: {more}");
}
입력은 앞뒤에 공백이 있는 홍길동 , Enter만, 그리고 입력 끝 순서로 넣었습니다. 화면 기록입니다.
이름을 입력하세요: 홍길동
반갑습니다, 홍길동님!
별명을 입력하세요(없으면 Enter):
별명 없음
한 줄 더 입력하세요:
입력이 끝났습니다(ReadLine이 null을 돌려줌).
- 첫 입력의 앞뒤 공백은
Trim()이 없앴습니다. - 두 번째는 Enter만 눌렀기 때문에 빈 문자열이 들어와 "별명 없음"이 나왔습니다.
- 세 번째에서는 입력이 끝나서
ReadLine이 null을 돌려줬습니다.
null을 다루는 두 가지 도구
string.IsNullOrWhiteSpace(name)은 null, 빈 문자열, 공백만 있는 문자열을 한 번에 검사합니다. 이 검사를 통과한 다음에는 컴파일러도 null이 아니라는 것을 알아서name.Trim()에 경고를 내지 않습니다.Console.ReadLine() ?? ""의??는 왼쪽이 null이면 오른쪽 값을 쓰는 연산자입니다. 결과가 null일 수 없으니 물음표 없는string변수에 받을 수 있습니다.
string?를 무시하면 나오는 경고
물음표를 무시하고 코드를 쓰면 빌드할 때 경고가 나옵니다. 아래처럼 물음표를 무시한 줄을 세 군데 쓰고 빌드했습니다.
// string? 를 무시하면 나오는 경고 세 가지 (빌드 경고만 확인합니다)
string name = Console.ReadLine();
string? nickname = Console.ReadLine();
Console.WriteLine(nickname.Length);
int age = int.Parse(Console.ReadLine());
Console.WriteLine($"{name} {age}");
Program.cs(2,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
Program.cs(4,19): warning CS8602: Dereference of a possibly null reference.
Program.cs(5,21): warning CS8604: Possible null reference argument for parameter 's' in 'int int.Parse(string s)'.
| 경고 | 뜻 |
|---|---|
| CS8600 | null일 수 있는 값을 물음표 없는 string에 넣음 |
| CS8602 | null일 수 있는 변수의 멤버(Length)를 바로 씀 |
| CS8604 | null일 수 있는 값을 메서드의 인수로 넘김 |
경고만 나오고 빌드는 되기 때문에 무시하기 쉽습니다. 하지만 입력이 끝난 상태에서 실행하면 프로그램이 멈춥니다. int age = int.Parse(Console.ReadLine());로 나이를 받는 짧은 프로그램을 입력 없이(입력 끝 상태로) 실행한 결과입니다. 경로는 줄여서 적었습니다.
Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 's')
at System.Int32.Parse(String s)
at Program.<Main>$(String[] args) in Program.cs:line 2
한국어 Visual Studio의 오류 목록에서는 이 경고들이 한국어 메시지로 보일 수 있습니다.
헷갈리기 쉬운 점
- Enter만 누르면 null이 아니라 빈 문자열입니다. null은 입력이 끝났을 때만 옵니다. 그래서 빈 입력을 검사하려면
== null이 아니라string.IsNullOrWhiteSpace를 씁니다. - 느낌표(!)로 경고를 끄지 않습니다.
Console.ReadLine()!처럼 쓰면 "null이 아님을 내가 보장한다"는 뜻이 되어 경고가 사라집니다. 하지만 null을 막아 주지는 않아서, null이 오면NullReferenceException으로 멈춥니다. - 창이 바로 닫히는 것을 막으려고 ReadKey를 쓸 때는 주의합니다. 지난 글에서 exe를 더블클릭하면 창이 바로 닫힐 수 있다고 했습니다. 프로그램 끝에
Console.ReadKey();를 두면 키를 누를 때까지 기다립니다. 다만 입력을 파일로 넣어 실행하면 아래 예외가 납니다. 공식 문서도 입력이 리디렉션되면InvalidOperationException이 난다고 설명합니다. 가운데at줄은 이 PC에서at System.ConsolePal.ReadKey(Boolean intercept)형태로 나왔습니다(.NET 버전에 따라 내부 메서드 이름은 달라질 수 있음).
끝났습니다. 아무 키나 누르세요.
Unhandled exception. System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
at System.ConsolePal.ReadKey(Boolean intercept)
at Program.<Main>$(String[] args) in Program.cs:line 3
4. 숫자로 바꾸기와 입력 검사 반복

ReadLine은 항상 문자열을 돌려줍니다. 나이나 점수처럼 숫자가 필요하면 문자열을 숫자로 바꿔야 합니다. 방법은 int.Parse와 int.TryParse 두 가지입니다.
같은 입력, 두 가지 방법
같은 입력을 두 방법으로 바꿔 비교하는 예제입니다. 입력이 끝날 때까지 한 줄씩 읽습니다.
// 같은 입력을 int.Parse와 int.TryParse로 바꿔 보기 (입력이 끝날 때까지 반복)
while (true)
{
string? line = Console.ReadLine();
if (line == null)
{
break; // 입력 끝
}
Console.WriteLine($"입력: \"{line}\"");
// TryParse: 실패해도 프로그램이 멈추지 않고 false를 돌려줍니다
if (int.TryParse(line, out int number))
{
Console.WriteLine($" TryParse → 성공, 값 {number}");
}
else
{
Console.WriteLine($" TryParse → 실패 (number는 {number})");
}
// Parse: 실패하면 예외가 납니다 (try-catch가 없으면 프로그램이 멈춤)
try
{
int parsed = int.Parse(line);
Console.WriteLine($" Parse → 성공, 값 {parsed}");
}
catch (FormatException ex)
{
Console.WriteLine($" Parse → FormatException: {ex.Message}");
}
catch (OverflowException ex)
{
Console.WriteLine($" Parse → OverflowException: {ex.Message}");
}
}
25, 42 , -7, abc, Enter만, 3.5, 1,000, 99999999999를 차례로 입력한 화면 기록입니다. 입력한 줄이 먼저 보이고, 그 아래에 결과가 나옵니다.
25
입력: "25"
TryParse → 성공, 값 25
Parse → 성공, 값 25
42
입력: " 42 "
TryParse → 성공, 값 42
Parse → 성공, 값 42
-7
입력: "-7"
TryParse → 성공, 값 -7
Parse → 성공, 값 -7
abc
입력: "abc"
TryParse → 실패 (number는 0)
Parse → FormatException: The input string 'abc' was not in a correct format.
입력: ""
TryParse → 실패 (number는 0)
Parse → FormatException: The input string '' was not in a correct format.
3.5
입력: "3.5"
TryParse → 실패 (number는 0)
Parse → FormatException: The input string '3.5' was not in a correct format.
1,000
입력: "1,000"
TryParse → 실패 (number는 0)
Parse → FormatException: The input string '1,000' was not in a correct format.
99999999999
입력: "99999999999"
TryParse → 실패 (number는 0)
Parse → OverflowException: Value was either too large or too small for an Int32.
| 입력 | TryParse | Parse | 이유 |
|---|---|---|---|
25, -7 |
성공 | 성공 | |
앞뒤 공백이 있는 42 |
성공 | 성공 | 앞뒤 공백과 앞쪽 부호는 허용 |
abc, 3.5, 1,000 |
실패(0) | FormatException | 소수점과 쉼표는 정수 형식이 아님 |
| 빈 줄 | 실패(0) | FormatException | |
99999999999 |
실패(0) | OverflowException | int 범위(약 ±21억) 밖 |
| 입력 끝(null) | 실패 | ArgumentNullException | 앞 장의 예 |
int.TryParse(line, out int number)는 바꾸기에 성공하면true를 돌려주고, 바꾼 값을number에 넣습니다.out자리에서 변수를 바로 선언할 수 있습니다.- 실패하면
false를 돌려주고number에는 0이 들어갑니다. 공식 문서도 실패하면 0이라고 설명합니다. 그래서 실패했을 때number를 쓰면 안 됩니다. int.Parse는 실패하면 예외가 납니다. 지난 글에서 본try,catch가 없으면 프로그램이 멈춥니다.- 사용자가 친 글자는 언제든 틀릴 수 있으니
TryParse를 씁니다.Parse는 프로그램 안에서 만든, 숫자임이 확실한 문자열에만 씁니다. - 소수는
double.TryParse로 바꿉니다. 쉼표가 들어간 숫자를 받는 방법도 있지만 이 글에서는 다루지 않습니다.
올바른 값이 들어올 때까지 다시 묻기
잘못된 값이 들어오면 안내를 보여 주고 다시 묻는 메서드를 만듭니다. 한 번 만들어 두면 나이, 수량처럼 여러 곳에서 다시 씁니다.
// 올바른 숫자가 들어올 때까지 다시 묻는 입력 반복
int age = ReadInt("나이를 입력하세요(1~120): ", 1, 120);
Console.WriteLine($"입력한 나이: {age}");
int count = ReadInt("주문 수량(1~10): ", 1, 10);
Console.WriteLine($"{count}개 x 4,500원 = {count * 4500:N0}원");
// prompt를 보여 주고 min~max 사이 정수를 받을 때까지 반복합니다
static int ReadInt(string prompt, int min, int max)
{
while (true)
{
Console.Write(prompt);
string? text = Console.ReadLine();
if (text == null) // 입력 끝(Windows: Ctrl+Z 후 Enter)
{
Console.WriteLine();
Console.WriteLine("입력이 끝나서 프로그램을 종료합니다.");
Environment.Exit(0);
}
if (!int.TryParse(text, out int value))
{
Console.WriteLine(" 숫자로 입력해 주세요.");
continue;
}
if (value < min || value > max)
{
Console.WriteLine($" {min}~{max} 사이로 입력해 주세요.");
continue;
}
return value;
}
}
abc, Enter만, 200, 25, 0, 3을 차례로 입력한 화면 기록입니다.
나이를 입력하세요(1~120): abc
숫자로 입력해 주세요.
나이를 입력하세요(1~120):
숫자로 입력해 주세요.
나이를 입력하세요(1~120): 200
1~120 사이로 입력해 주세요.
나이를 입력하세요(1~120): 25
입력한 나이: 25
주문 수량(1~10): 0
1~10 사이로 입력해 주세요.
주문 수량(1~10): 3
3개 x 4,500원 = 13,500원
ReadInt의 while (true) 안은 이런 순서입니다.
- 질문을
Write로 출력하고ReadLine으로 한 줄 읽습니다. - null이면(입력 끝) 안내를 출력하고 프로그램을 끝냅니다.
Environment.Exit(0)은 프로그램을 바로 끝내는 메서드입니다. 이 줄이 없으면 입력이 끝난 뒤에도 질문을 영원히 반복합니다. - 숫자가 아니면 안내하고
continue로 반복의 처음으로 돌아갑니다. - 범위 밖이면 안내하고 역시
continue로 돌아갑니다. - 모두 통과하면
return value;로 값을 돌려줍니다.return은 반복과 메서드를 함께 빠져나옵니다.
헷갈리기 쉬운 점
- while (true)는 return이나 break가 있어야 끝납니다. 이 예제에서는 올바른 값이면
return, 입력이 끝나면Environment.Exit로 빠져나옵니다. - continue는 반복을 끝내는 것이 아니라 처음으로 돌아가는 것입니다. 반복을 끝내는 것은
break입니다. - TryParse의 결과(true/false)와 바꾼 값(out)은 다른 것입니다.
if (int.TryParse(...))로 성공했는지 먼저 확인한 뒤 값을 씁니다. - 한글 입력기의 전각 숫자는 숫자로 인식되지 않을 수 있습니다. 숫자를 넣었는데 계속 다시 묻는다면 입력기가 전각 모드인지 확인합니다.
5. 글자색과 한글 인코딩

글자색 바꾸고 되돌리기
Console.ForegroundColor에 색을 넣으면 그 뒤로 출력하는 글자의 색이 바뀝니다. 배경색은 Console.BackgroundColor입니다.
// 글자색과 배경색 바꾸기
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("합격");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("불합격");
Console.ResetColor(); // 원래 색으로
Console.WriteLine("기본 색으로 돌아왔습니다.");
Console.BackgroundColor = ConsoleColor.DarkBlue;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write(" 알림 ");
Console.ResetColor();
Console.WriteLine(" 배경색도 바꿀 수 있습니다.");
// ConsoleColor에 들어 있는 16가지 색 이름
ConsoleColor[] colors = Enum.GetValues<ConsoleColor>();
Console.WriteLine($"색 개수: {colors.Length}");
foreach (ConsoleColor color in colors)
{
Console.ForegroundColor = color;
Console.Write(color + " ");
}
Console.ResetColor();
Console.WriteLine();
실행한 화면 기록입니다. 블로그 코드 블록에는 색을 칠할 수 없어서, 색이 바뀐 지점에 «글자:Green», «기본색» 같은 표시를 넣었습니다.
«글자:Green»합격
«글자:Red»불합격
«기본색»기본 색으로 돌아왔습니다.
«글자:Yellow,배경:DarkBlue» 알림 «기본색» 배경색도 바꿀 수 있습니다.
색 개수: 16
«글자:Black»Black «글자:DarkBlue»DarkBlue «글자:DarkGreen»DarkGreen «글자:DarkCyan»DarkCyan «글자:DarkRed»DarkRed «글자:DarkMagenta»DarkMagenta «글자:DarkYellow»DarkYellow «글자:Gray»Gray «글자:DarkGray»DarkGray «글자:Blue»Blue «글자:Green»Green «글자:Cyan»Cyan «글자:Red»Red «글자:Magenta»Magenta «글자:Yellow»Yellow «글자:White»White «기본색»
- 한 번 바꾼 색은 다시 바꿀 때까지 계속됩니다. 그래서 색을 쓴 뒤에는
Console.ResetColor()로 원래 색으로 되돌립니다. ConsoleColor에는 16가지 색이 있습니다. 결과의색 개수: 16과 그 아래 이름 목록이 전부입니다.- 공식 문서에 따르면 기본 글자색은 Gray입니다. 실제 색은 터미널의 색 설정에 따라 달라 보일 수 있습니다.
- 출력을 파일로 보내면 색 정보는 남지 않습니다. 같은 예제의 출력을 파일로 받았을 때 글자만 남았습니다.
한글이 화면에 나오기까지
C# 문자열은 그대로 화면에 찍히는 것이 아닙니다. 공식 문서에 따르면 콘솔은 현재 콘솔 코드 페이지로 입력을 읽고 출력을 씁니다. 그리고 기본 코드 페이지는 시스템 로캘이 정합니다. 한국어 Windows의 시스템 로캘에 해당하는 코드 페이지는 949입니다. 즉 "C# 문자열 → 콘솔 코드 페이지로 변환 → 터미널과 글꼴이 화면에 그림" 순서입니다.
코드 페이지 949에는 현대 한글이 모두 들어 있어서, 한국어 Windows 기본 콘솔에서는 따로 설정하지 않아도 한글이 출력됩니다(예: 안녕하세요가 그대로 나옵니다). 코드 페이지에 없는 글자(이모지 등)는 제대로 표시되지 않습니다.
어떤 글자가 949로 바꿔도 남는지 확인하는 예제입니다. 실제 콘솔이 아니라 .NET의 949 변환기로 바꿨다가 되돌려 보는 흉내입니다.
// 콘솔 인코딩 확인 + 코드 페이지 949로 바꿀 때 살아남는 글자 (변환만 흉내 낸 것)
using System.Text;
Console.WriteLine($"출력 코드 페이지: {Console.OutputEncoding.CodePage}");
Console.WriteLine($"입력 코드 페이지: {Console.InputEncoding.CodePage}");
// .NET은 기본으로 코드 페이지 949 변환기를 갖고 있지 않아서 등록이 필요합니다
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding cp949 = Encoding.GetEncoding(949);
Console.WriteLine($"비교용 인코딩: {cp949.WebName} (코드 페이지 {cp949.CodePage})");
string[] samples = { "안녕하세요", "똠방각하 펲시 쀍", "★ ☆ ♥ ㄱ ㄴ", "₩1,234,567", "이모지 😀" };
foreach (string text in samples)
{
byte[] bytes = cp949.GetBytes(text); // 949로 바꾸고
string back = cp949.GetString(bytes); // 다시 문자열로
string result = back == text ? "그대로" : "바뀜";
Console.WriteLine($"{text} → {back} ({result})");
}
실행 결과입니다.
출력 코드 페이지: 949
입력 코드 페이지: 949
비교용 인코딩: ks_c_5601-1987 (코드 페이지 949)
안녕하세요 → 안녕하세요 (그대로)
똠방각하 펲시 쀍 → 똠방각하 펲시 쀍 (그대로)
★ ☆ ♥ ㄱ ㄴ → ★ ☆ ♥ ㄱ ㄴ (그대로)
₩1,234,567 → \1,234,567 (바뀜)
이모지 😀 → 이모지 ?? (바뀜)
- 첫 두 줄은 콘솔이 쓰는 코드 페이지입니다. 한국어 Windows 기본 설정(UTF-8 베타 꺼짐)에서는
출력 코드 페이지: 949,입력 코드 페이지: 949로 나옵니다. 설정의 "Beta: Use Unicode UTF-8 for worldwide language support"를 켠 PC에서는 65001이 나올 수 있습니다. - 한글, 자주 쓰지 않는 글자인
똠,펲,쀍(완성형 기본 2,350자에 없는 글자), 기호★, 자모ㄱ은 그대로 돌아왔습니다. ₩는\로 바뀌었고, 이모지는??로 바뀌었습니다.- Windows의 실제 콘솔은 Windows 자체 변환을 쓰므로 이 흉내와 결과가 다를 수 있습니다. 한국어 글꼴은
\를 ₩ 모양으로 그리는 경우가 많아서 겉보기에는 비슷할 수도 있습니다. Encoding.RegisterProvider(...)는 이 비교를 하려고 쓴 줄입니다. 보통 프로그램에는 필요 없습니다.
글자가 깨질 때 확인할 점
코드 페이지 949, 한글의 두 칸 너비, ₩와 이모지 표시는 모두 환경에 따라 깨질 수 있습니다. 그럴 때는 아래 순서로 확인합니다.
- 한글이
?나 이상한 글자로 나오면: 터미널 글꼴이 한글을 그릴 수 있는 글꼴인지 확인합니다. Console 클래스 공식 문서도 이런 문자를 표시하려면 콘솔 글꼴이 TrueType 글꼴이어야 한다고 설명합니다. - 이모지나 특수 기호만
?로 나오면: 그 글자가 콘솔 코드 페이지에 없는 것입니다. 출력에서 그 글자를 빼는 것이 가장 간단합니다. - 꼭 이모지를 써야 하면:
Program.cs맨 위에Console.OutputEncoding = System.Text.Encoding.UTF8;을 넣어 출력 인코딩을 UTF-8로 바꾸는 방법이 있습니다. Windows 문서도 새 콘솔 프로그램에는 코드 페이지 대신 유니코드를 권합니다.Console.OutputEncoding = Encoding.UTF8뒤 이모지 바이트는 출력됩니다. 콘솔 창에서 그림 문자로 보이는지는 터미널과 글꼴에 따라 다를 수 있습니다. ₩가\로 보이면: 2장에서 권한 대로{price:N0}원처럼 쓰면 이 문제를 피할 수 있습니다.- 표의 줄이 어긋나면: 2장의 [3]처럼 한글이 들어가는 칸을 줄의 맨 뒤로 옮깁니다.
헷갈리기 쉬운 점
- 색을 바꾼 뒤 ResetColor를 빼먹으면 그 뒤의 출력이 모두 그 색으로 나옵니다. 색을 쓰는 부분은 항상
ResetColor()로 끝냅니다. - 입력 인코딩(InputEncoding)은 바꾸지 않습니다. 출력 쪽을 바꾸는 팁을 보고 입력 쪽까지 바꾸면 한글 입력에서 다른 문제가 생긴 사례가 있었습니다. 이 시리즈의 예제는 입력 인코딩을 건드리지 않습니다.
- 글자가 깨지는 것은 코드 문제가 아닐 때가 많습니다. 같은 프로그램이라도 터미널, 글꼴, 시스템 설정에 따라 다르게 보입니다. 다른 터미널에서 실행해 보면 원인을 나누기 쉽습니다.
6. 종합 예제: 성적 평균 계산기
![종합 예제 성적 평균 계산기 그림(입력 검사 + 서식 + 색). 위쪽 '이번 글에서 배운 것 모두 쓰기' 체크 목록: ReadInt 범위 안 정수가 올 때까지 다시 묻기, ReadName 빈 이름이면 다시 묻기, {average:F1} 평균을 소수 1자리로, :P0 합격률을 백분율로(67%), ConsoleColor.Green 합격은 초록·불합격은 빨강. 가운데 '틀리게 입력해 본 실제 화면'(파랑 = 입력, 한글은 두 칸으로 그림): === 성적 평균 계산기 === / 학생 수(1~10): 세 [숫자 아님] 1~10 사이의 정수로 입력해 주세요. / 학생 수: 3 / 1번 학생 이름: 홍길동, 홍길동 점수(0~100): 95 / 2번 학생 이름: [빈 이름] 이름을 입력해 주세요. / 김영희, 점수 백점 [숫자 아님] 0~100 사이의 정수로 입력해 주세요., 88 / 3번 이철수, 점수 -5 [범위 밖], 120 [범위 밖], 59 / 결과 표 번호 점수 결과 이름: 1 95 초록 합격 홍길동, 2 88 초록 합격 김영희, 3 59 빨강 불합격 이철수 / 평균: 80.7점, 최고: 95점, 최저: 59점, 합격: 2명 / 3명 (67%), 메모 '틀린 입력 5번이 모두 다시 질문으로 돌아감, 이름을 맨 뒤에 두어 한글 폭 문제를 피함'. 아래 '결과 줄의 서식': Console.WriteLine($'평균: {average:F1}점');과 Console.Write('합격 ');('합격 '은 뒤에 공백 2칸을 붙여 '불합격'과 화면 폭을 맞춤), (double)passCount / count:P0 → 합격: 2명 / 3명 (67%)((double)로 정수 나눗셈을 피함). 초보가 틀리게 입력해도 다시 물어본다고 하고, 선배가 ReadInt 하나로 어디서든 검사한다고 답함](https://blog.kakaocdn.net/dna/lkjn6/dJMcadDyaVk/AAAAAAAAAAAAAAAAAAAAAEfYDqaHWfq88trTlEULTCywecfRHuammAncQF8VhbHt/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1793458799&allow_ip=&allow_referer=&signature=RVix4zgJXk%2FPib%2F%2FnbxP%2F70Er%2BM%3D)
앞선 글들에서 만든 성적 계산기는 이름과 점수가 코드에 정해져 있었습니다. 이번에는 학생 수, 이름, 점수를 입력받습니다. 이 글에서 배운 입력 검사 반복, 서식, 자리 맞춤, 글자색을 모두 씁니다.
// 종합 예제: 성적 평균 계산기 (입력 검사 + 서식 + 색)
Console.WriteLine("=== 성적 평균 계산기 ===");
int count = ReadInt("학생 수(1~10): ", 1, 10);
string[] names = new string[count];
int[] scores = new int[count];
for (int i = 0; i < count; i++)
{
names[i] = ReadName($"{i + 1}번 학생 이름: ");
scores[i] = ReadInt($"{names[i]} 점수(0~100): ", 0, 100);
}
Console.WriteLine();
Console.WriteLine("번호 점수 결과 이름");
Console.WriteLine("---------------------------");
int passCount = 0;
for (int i = 0; i < count; i++)
{
Console.Write($"{i + 1,3} {scores[i],4} ");
if (scores[i] >= 60)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("합격 ");
passCount++;
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("불합격");
}
Console.ResetColor();
Console.WriteLine($" {names[i]}");
}
Console.WriteLine("---------------------------");
double average = scores.Average();
Console.WriteLine($"평균: {average:F1}점");
Console.WriteLine($"최고: {scores.Max()}점, 최저: {scores.Min()}점");
Console.WriteLine($"합격: {passCount}명 / {count}명 ({(double)passCount / count:P0})");
// 정수 입력: 범위 안의 숫자가 들어올 때까지 반복
static int ReadInt(string prompt, int min, int max)
{
while (true)
{
Console.Write(prompt);
string? text = Console.ReadLine();
if (text == null)
{
Console.WriteLine();
Console.WriteLine("입력이 끝나서 프로그램을 종료합니다.");
Environment.Exit(0);
}
if (int.TryParse(text, out int value) && value >= min && value <= max)
{
return value;
}
Console.WriteLine($" {min}~{max} 사이의 정수로 입력해 주세요.");
}
}
// 이름 입력: 빈 줄이면 다시 묻기
static string ReadName(string prompt)
{
while (true)
{
Console.Write(prompt);
string? text = Console.ReadLine();
if (text == null)
{
Console.WriteLine();
Console.WriteLine("입력이 끝나서 프로그램을 종료합니다.");
Environment.Exit(0);
}
if (!string.IsNullOrWhiteSpace(text))
{
return text.Trim();
}
Console.WriteLine(" 이름을 입력해 주세요.");
}
}
틀린 입력을 일부러 섞어 넣은 화면 기록입니다. 학생 수에 세, 두 번째 이름에 Enter만, 점수에 백점, -5, 120을 넣었습니다. 색이 바뀐 지점에는 앞 장처럼 «글자:Green» 같은 표시가 들어 있습니다.
=== 성적 평균 계산기 ===
학생 수(1~10): 세
1~10 사이의 정수로 입력해 주세요.
학생 수(1~10): 3
1번 학생 이름: 홍길동
홍길동 점수(0~100): 95
2번 학생 이름:
이름을 입력해 주세요.
2번 학생 이름: 김영희
김영희 점수(0~100): 백점
0~100 사이의 정수로 입력해 주세요.
김영희 점수(0~100): 88
3번 학생 이름: 이철수
이철수 점수(0~100): -5
0~100 사이의 정수로 입력해 주세요.
이철수 점수(0~100): 120
0~100 사이의 정수로 입력해 주세요.
이철수 점수(0~100): 59
번호 점수 결과 이름
---------------------------
1 95 «글자:Green»합격 «기본색» 홍길동
2 88 «글자:Green»합격 «기본색» 김영희
3 59 «글자:Red»불합격«기본색» 이철수
---------------------------
평균: 80.7점
최고: 95점, 최저: 59점
합격: 2명 / 3명 (67%)
- 틀린 입력 다섯 번이 모두 다시 질문으로 돌아갔습니다.
ReadInt는 숫자 검사와 범위 검사를&&로 한 줄에 묶었습니다. 앞 장의 예제와 하는 일은 같습니다.ReadName은 빈 이름이면 다시 묻고, 앞뒤 공백을 없애서 돌려줍니다.- 결과 표는 길이가 일정한 번호와 점수를 앞에, 이름을 맨 뒤에 두어 한글 너비 문제를 피했습니다.
"합격 "은"불합격"과 칸을 맞추려고 뒤에 공백 2칸을 넣었습니다. 한글 두 칸 너비 기준으로 한글 2자와 공백 2칸이 한글 3자와 같은 너비입니다.scores.Average(),Max(),Min()은 지난 글들에서 본 LINQ 메서드입니다.- 합격률은
(double)passCount / count로 계산합니다. 정수끼리 나누면 소수점 아래가 버려지기 때문입니다. 지난 글의 평균 버그와 같은 이야기입니다.P0서식이라67%로 나옵니다.
더 해 보기: 할 일 프로그램에 메뉴 붙이기
지난 글의 할 일 프로그램에 메뉴를 붙이면 사용자와 주고받는 프로그램이 됩니다. TodoItem.cs와 TodoList.cs는 지난 글과 같고, 첫 줄만 namespace TodoMenu;로 바꿨습니다. Program.cs만 새로 씁니다.
// 지난 글의 할 일 프로그램에 메뉴를 붙여 사용자와 주고받기
using TodoMenu;
TodoList todos = new TodoList();
while (true)
{
Console.WriteLine();
Console.WriteLine("1) 추가 2) 완료 3) 목록 0) 종료");
int menu = ReadInt("메뉴 선택: ", 0, 3);
if (menu == 0)
{
break;
}
switch (menu)
{
case 1:
Console.Write("할 일: ");
string? title = Console.ReadLine();
if (string.IsNullOrWhiteSpace(title))
{
Console.WriteLine(" 빈 제목은 추가하지 않습니다.");
}
else
{
TodoItem item = todos.Add(title.Trim());
Console.WriteLine($" {item.Id}번으로 추가했습니다.");
}
break;
case 2:
int id = ReadInt("완료할 번호: ", 1, 999);
if (todos.Complete(id))
{
Console.WriteLine($" {id}번을 완료했습니다.");
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($" {id}번은 없습니다.");
Console.ResetColor();
}
break;
case 3:
todos.PrintAll();
break;
}
}
Console.WriteLine("프로그램을 종료합니다.");
static int ReadInt(string prompt, int min, int max)
{
while (true)
{
Console.Write(prompt);
string? text = Console.ReadLine();
if (text == null)
{
Console.WriteLine();
Console.WriteLine("입력이 끝나서 프로그램을 종료합니다.");
Environment.Exit(0);
}
if (int.TryParse(text, out int value) && value >= min && value <= max)
{
return value;
}
Console.WriteLine($" {min}~{max} 사이의 숫자로 입력해 주세요.");
}
}
메뉴를 골라 할 일을 추가하고, 빈 제목과 없는 메뉴 번호, 없는 할 일 번호를 넣어 본 화면 기록입니다.
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 1
할 일: 장보기
1번으로 추가했습니다.
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 1
할 일:
빈 제목은 추가하지 않습니다.
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 1
할 일: C# 입출력 정리
2번으로 추가했습니다.
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 5
0~3 사이의 숫자로 입력해 주세요.
메뉴 선택: 3
[ ] 1. 장보기
[ ] 2. C# 입출력 정리
완료 0 / 전체 2
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 2
완료할 번호: 7
«글자:Red» 7번은 없습니다.
«기본색»
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 2
완료할 번호: 1
1번을 완료했습니다.
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 3
[v] 1. 장보기
[ ] 2. C# 입출력 정리
완료 1 / 전체 2
1) 추가 2) 완료 3) 목록 0) 종료
메뉴 선택: 0
프로그램을 종료합니다.
클래스 파일은 그대로 두고 Program.cs만 바꿨는데 전혀 다른 프로그램이 됐습니다. 지난 글에서 클래스를 파일로 나눈 장점이 여기서 보입니다.
헷갈리기 쉬운 점
- 입력 검사 메서드는 프로그램마다 조금씩 다릅니다. 이 예제의
ReadInt는 안내 문구가 하나로 합쳐졌고, 메뉴 예제의ReadInt는 "숫자로"라고 안내합니다. 필요에 맞게 고쳐 씁니다. - switch의 case 안에서 선언한 변수는 switch 전체에서 이름이 겹치면 안 됩니다. 메뉴 예제의
title과id처럼 case마다 다른 이름을 씁니다. - 입력을 받는 프로그램은 Ctrl+F5로 실행합니다. 콘솔 창에서 직접 입력해야 하므로 지금까지처럼 Ctrl+F5로 실행하고, 끝낼 때는 메뉴의 0번이나 Windows 기준 Ctrl+Z, Enter로 입력을 끝냅니다.
마치며
이번 글에서는 콘솔 프로그램의 입력과 출력을 정리했습니다.
Write는 줄을 바꾸지 않고,WriteLine은 줄을 바꿉니다. 질문은Write로 씁니다.{값:서식}으로N0,F2,D3,P1,C서식을,{값,폭}으로 자리 맞춤을 합니다. 한글이 들어가는 칸은 맨 뒤에 둡니다.ReadLine은string?을 돌려줍니다. Enter만 누르면 빈 문자열, 입력이 끝나면 null입니다.- 사용자 입력은
TryParse로 바꾸고,while (true)와continue로 올바른 값이 들어올 때까지 다시 묻습니다. ForegroundColor로 글자색을 바꾸고ResetColor로 되돌립니다. 한글과 기호는 콘솔 코드 페이지와 글꼴에 따라 다르게 보일 수 있습니다.- 입력을 받는 성적 평균 계산기와 할 일 메뉴 프로그램을 만들었습니다.
다음 글은 「Windows Forms 시작하기, 폼 만들기와 도구 상자 컨트롤」입니다. 콘솔을 떠나 창과 버튼이 있는 화면 프로그램을 만듭니다. 화면 프로그램에서는 콘솔 대신 텍스트 상자가 입력창이 되지만, 이번 글의 TryParse 입력 검사와 서식 지정자는 그대로 씁니다.
참고 자료
- Console 클래스
- Console.Write 메서드
- 복합 서식(Composite formatting)
- 문자열 보간 $
- 표준 숫자 서식 문자열
- 표준 날짜 및 시간 서식 문자열
- Console.ReadLine 메서드
- nullable 참조 형식
- nullable 경고(CS8600, CS8602, CS8604 등)
- Int32.TryParse 메서드
- Int32.Parse 메서드
- Console.ReadKey 메서드
- Console.ForegroundColor 속성
- ConsoleColor 열거형
- Console.OutputEncoding 속성
- 콘솔 코드 페이지(Windows)
- 코드 페이지 식별자(949 포함)
'C#' 카테고리의 다른 글
| Windows Forms 통장 프로그램, 입금·출금·거래내역 만들기 (0) | 2026.10.06 |
|---|---|
| Windows Forms 시작하기, 폼 만들기와 도구 상자 컨트롤 (0) | 2026.10.06 |
| Visual Studio 콘솔앱 만들기, 파일 나누기와 디버깅 (0) | 2026.10.06 |
| C# 고급 문법, 클래스부터 LINQ까지 (0) | 2026.10.06 |
| C# 기본 문법, 변수부터 메서드까지 (0) | 2026.10.06 |