← C# 강의 목록으로
🟣
기초
기초 · 선수: 이전 단원

02. 변수와 타입

C#은 정적 타입 언어로, 변수마다 어떤 값을 담을지 선언 시점에 정해야 합니다. 이 단원에서는 값 타입(int·double·bool·char 등)과 참조 타입(string·object), 형변환, var 키워드를 익힙니다.

C#.NET 8기초변수와 타입
소요 시간
약 1~1.5시간
난이도
📊 초급
선수 조건
🎯 이전 단원 또는 동등 지식
결과물
C#은 정적 타입 언어로, 변수마다 어떤 값을 담을지 선언 시점에 정해야 합니다. 이 단원에서는 값 타입(int·double·bool·char 등)과 참조 타입(string·object), 형변환, var 키워드를 익힙니다.

이 강의에서 배우는 것

  • 1기본 타입 `int`/`long`/`double`/`decimal`/`bool`/`char`/`string` 의 크기와 범위를 안다
  • 2**값 타입(value type)** 과 **참조 타입(reference type)** 의 차이를 이해한다
  • 3`var` / `const` / `readonly` 를 적재적소에 쓴다
  • 4문자열의 불변성(immutability)을 이해한다
  • 5**박싱(boxing)** 이 무엇이고 왜 느린지 안다

소개

프로그래밍은 결국 "값을 기억하고 가공하는 일"입니다. C#은 **정적 타입(statically typed)** 언어라 모든 변수는 컴파일 타임에 타입이 정해집니다. 이 단원에서 기본 타입과 값/참조 타입의 차이를 다집니다.

핵심 개념

1) 기본 타입표

타입크기범위/용도
`int`4 B약 ±21억, 일반 정수`int n = 100;`
`long`8 B약 ±9.2×10¹⁸, 큰 정수`long big = 10_000_000_000L;`
`double`8 B부동소수점, 일반 실수`double pi = 3.14;`
`decimal`16 B28자리 정밀 실수, 돈 계산용`decimal won = 1500m;`
`bool`1 B`true` / `false``bool ok = true;`
`char`2 B유니코드 한 글자`char c = '가';`
`string`가변문자열 (참조 타입)`string s = "hi";`

`long` 은 끝에 `L`, `decimal` 은 끝에 `m` 을 붙입니다.

2) 값 타입 vs 참조 타입

  • **값 타입(struct, int, double, bool, char, enum...)**: 값 자체가 변수에 저장. **복사 시 통째로 복제**.
  • **참조 타입(class, string, array...)**: 힙에 객체가 있고 변수는 그 주소만 가짐. **복사 시 주소만 공유**.
csharp
int a = 10;
int b = a;      // 값 복사
b = 20;
// a == 10, b == 20  (서로 독립)

int[] arr1 = [1, 2, 3];
int[] arr2 = arr1;   // 참조 복사
arr2[0] = 99;
// arr1[0] == 99    (같은 배열을 가리킴)

3) `var` : 타입 추론

`var` 는 컴파일러가 우변을 보고 타입을 추론해 줍니다. **타입이 사라지는 게 아니라 생략될 뿐**입니다.

csharp
var name = "Alice";   // string
var age = 30;         // int
var pi = 3.14;        // double

지역 변수에만 쓸 수 있고, 초기화가 없으면 못 씁니다(`var x;` 불가).

4) `const` 와 `readonly`

  • `const`: **컴파일 타임 상수**. 선언과 동시에 초기화, 이후 변경 불가. 기본 타입과 `string` 에만 가능.
  • `readonly`: **런타임에 한 번만 대입 가능**. 생성자에서 정해지는 값에 쓴다.
csharp
const double Pi = 3.14159;        // 절대 안 변하는 값
// Pi = 3.14;  // 컴파일 에러

5) 문자열은 불변(immutable)

`string` 은 참조 타입이지만 **내용을 바꿀 수 없습니다**. `+=` 나 `Replace` 는 새 문자열을 만들어 반환합니다.

csharp
string s = "Hello";
s += ", World!";   // 새 문자열 생성, s 는 새 객체를 가리킴

6) 박싱(boxing)

값 타입을 `object` 같은 참조 타입으로 다룰 때 **힙에 복사본을 만드는 작업**입니다. 느리고 GC 압박을 주므로 가급적 피합니다.

csharp
int n = 42;
object o = n;     // 박싱: 힙에 새 객체 생성
int m = (int)o;   // 언박싱

핵심 예제

예제 1 — `Primitives/Program.cs` : 기본 타입 살펴보기

csharp
int i = 100;
long l = 10_000_000_000L;
double d = 3.14;
decimal m = 1500.99m;
bool b = true;
char c = '가';
string s = "안녕";

Console.WriteLine($"int    : {i} ({sizeof(int)} B)");
Console.WriteLine($"long   : {l} ({sizeof(long)} B)");
Console.WriteLine($"double : {d} ({sizeof(double)} B)");
Console.WriteLine($"decimal: {m}");
Console.WriteLine($"bool   : {b}, char: {c}, string: {s}");

**실행 결과**

text
int    : 100 (4 B)
long   : 10000000000 (8 B)
double : 3.14 (8 B)
decimal: 1500.99
bool   : True, char: 가, string: 안녕

**메모:** `sizeof(decimal)` 은 unsafe 컨텍스트가 필요해 생략. `_` 로 자릿수 구분이 가능합니다(`10_000_000_000`).

예제 2 — `ValueVsReference/Program.cs` : 복사 동작 비교

csharp
namespace CodingNow.Lecture.Basics02;

internal static class Program
{
    public static void Main()
    {
        Point p1 = new(1, 2);
        Point p2 = p1;          // 값 복사
        p2.X = 99;
        Console.WriteLine($"p1=({p1.X},{p1.Y})  p2=({p2.X},{p2.Y})");

        Box b1 = new() { Value = 10 };
        Box b2 = b1;            // 참조 복사
        b2.Value = 99;
        Console.WriteLine($"b1={b1.Value}  b2={b2.Value}");
    }
}

internal struct Point
{
    public int X;
    public int Y;
    public Point(int x, int y) { X = x; Y = y; }
}

internal class Box
{
    public int Value;
}

**실행 결과**

text
p1=(1,2)  p2=(99,2)
b1=99  b2=99

**메모:** `struct` 는 값 타입이라 독립적, `class` 는 참조 타입이라 같은 객체를 공유합니다.

예제 3 — `VarConst/Program.cs` : `var`, `const`, `readonly`

csharp
var name = "Alice";   // 추론: string
var age = 30;         // 추론: int

const double Pi = 3.14159;
const string Greet = "안녕!";

Console.WriteLine($"{name}, {age}세, {Greet} (Pi={Pi})");
// Pi = 3.0;   // 컴파일 에러

**실행 결과**

text
Alice, 30세, 안녕! (Pi=3.14159)

**메모:** `var` 는 가독성을 위해 사용하지만, 우변 타입이 명확하지 않으면 명시 타입이 낫습니다.

예제 4 — `StringBasics/Program.cs` : 문자열 불변성

csharp
string a = "Hello";
string b = a;
a += ", World!";          // 새 문자열 생성

Console.WriteLine($"a = {a}");
Console.WriteLine($"b = {b}");   // b 는 여전히 "Hello"

// 연결 방법 비교
string c1 = string.Concat("값:", 42);
string c2 = $"값:{42}";          // 문자열 보간 (권장)
Console.WriteLine(c1);
Console.WriteLine(c2);

**실행 결과**

text
a = Hello, World!
b = Hello
값:42
값:42

**메모:** `a += "..."` 이후에도 `b` 는 원래 문자열을 그대로 가리킵니다. 문자열은 불변이라 공유해도 안전합니다.

전체 예제 코드 (src/)

src/Primitives/Primitives.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/Primitives/Program.cs

csharp
// 기본 타입과 크기
int i = 100;
long l = 10_000_000_000L;
double d = 3.14;
decimal m = 1500.99m;
bool b = true;
char c = '가';
string s = "안녕";

Console.WriteLine($"int    : {i} ({sizeof(int)} B)");
Console.WriteLine($"long   : {l} ({sizeof(long)} B)");
Console.WriteLine($"double : {d} ({sizeof(double)} B)");
Console.WriteLine($"decimal: {m}");
Console.WriteLine($"bool   : {b}, char: {c}, string: {s}");

src/StringBasics/Program.cs

csharp
// 문자열은 불변(immutable)
string a = "Hello";
string b = a;
a += ", World!";          // a 만 새 객체를 가리킴

Console.WriteLine($"a = {a}");
Console.WriteLine($"b = {b}");

// 연결 방법 비교
string c1 = string.Concat("값:", 42);
string c2 = $"값:{42}";   // 문자열 보간 (권장)
Console.WriteLine(c1);
Console.WriteLine(c2);

src/StringBasics/StringBasics.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/ValueVsReference/Program.cs

csharp
namespace CodingNow.Lecture.Basics02;

internal static class Program
{
    public static void Main()
    {
        // 값 타입 (struct): 값을 통째로 복사
        Point p1 = new(1, 2);
        Point p2 = p1;
        p2.X = 99;
        Console.WriteLine($"p1=({p1.X},{p1.Y})  p2=({p2.X},{p2.Y})");

        // 참조 타입 (class): 주소를 공유
        Box b1 = new() { Value = 10 };
        Box b2 = b1;
        b2.Value = 99;
        Console.WriteLine($"b1={b1.Value}  b2={b2.Value}");
    }
}

internal struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

internal class Box
{
    public int Value;
}

src/ValueVsReference/ValueVsReference.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

src/VarConst/Program.cs

csharp
// var: 컴파일러가 우변을 보고 타입 추론
var name = "Alice";   // string
var age = 30;         // int

// const: 컴파일 타임 상수 (이후 변경 불가)
const double Pi = 3.14159;
const string Greet = "안녕!";

Console.WriteLine($"{name}, {age}세, {Greet} (Pi={Pi})");
// Pi = 3.0;   // 컴파일 에러

src/VarConst/VarConst.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

자주 하는 실수

  1. `int` 범위(약 ±21억)를 넘기는데 `int` 를 써서 오버플로우 — 큰 수는 `long`.
  2. 돈 계산에 `double` 사용 — 부동소수점 오차. **반드시 `decimal`**.
  3. `var x;` 처럼 초기화 없이 `var` 사용 — 컴파일 에러.
  4. `string` 을 루프에서 `+=` 로 누적 — 매번 새 객체. 많을 땐 `StringBuilder` (16편 참고).
  5. `const` 와 `readonly` 혼동 — `const` 는 컴파일 타임 상수, `readonly` 는 런타임 1회 대입.

정리

  • 정수는 `int`/`long`, 실수는 `double`(과학) / `decimal`(돈)
  • 값 타입은 복사 시 통째 복제, 참조 타입은 주소만 공유
  • `var` 는 타입 추론 편의, `const` 는 절대 불변
  • `string` 은 참조 타입이지만 불변 — 안전하게 공유 가능
  • `object` 로 값 타입을 담으면 박싱 발생 → 가급적 피하자

과제

**과제 - 02. 변수와 타입**

문제 1 — 프로필 카드

  • 프로젝트 폴더: `Homework01/`
  • 핵심 개념: 변수 선언, 적절한 타입 선택, 문자열 보간

요구사항

  • 다음 정보를 적절한 타입의 변수에 담는다
  • 이름 (`string`)
  • 나이 (`int`)
  • 키 cm (`double`)
  • 학생 여부 (`bool`)
  • 모두 한 줄씩 출력한다

예상 출력

text
=== 프로필 ===
이름: 지수
나이: 25
키: 172.5cm
학생: True

힌트

  • 키처럼 소수가 있는 값은 `double` 을 쓴다
  • `bool` 값은 그대로 출력하면 `True` / `False` 가 나온다

문제 2 — 두 정수의 사칙연산

  • 프로젝트 폴더: `Homework02/`
  • 핵심 개념: `Console.ReadLine`, `int.Parse`, 산술 연산자

요구사항

  • 두 정수를 사용자에게 입력받는다
  • 합·차·곱·몫·나머지를 출력한다

예상 출력

text
첫 번째 정수: 10
두 번째 정수: 3
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 / 3 = 3
10 % 3 = 1

힌트

  • `int a = int.Parse(Console.ReadLine()!);` 형태로 변환
  • `!` 는 "null 이 아님" 을 컴파일러에 알려 주는 연산자 (자세한 건 3단원에서)
  • 정수끼리의 `/` 는 몫만, `%` 는 나머지

정답 확인

직접 풀어 본 후 [`answer/`](./answer/) 폴더의 정답과 비교해 보세요.

정답 (answer/)

homework/answer/Homework01/Homework01.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

homework/answer/Homework01/Program.cs

csharp
// 프로필 카드
string name = "지수";
int age = 25;
double height = 172.5;
bool isStudent = true;

Console.WriteLine("=== 프로필 ===");
Console.WriteLine($"이름: {name}");
Console.WriteLine($"나이: {age}");
Console.WriteLine($"키: {height}cm");
Console.WriteLine($"학생: {isStudent}");

homework/answer/Homework02/Homework02.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>CodingNow.Lecture.Basics02</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

homework/answer/Homework02/Program.cs

csharp
// 두 정수의 사칙연산
Console.Write("첫 번째 정수: ");
int a = int.Parse(Console.ReadLine()!);

Console.Write("두 번째 정수: ");
int b = int.Parse(Console.ReadLine()!);

Console.WriteLine($"{a} + {b} = {a + b}");
Console.WriteLine($"{a} - {b} = {a - b}");
Console.WriteLine($"{a} * {b} = {a * b}");
Console.WriteLine($"{a} / {b} = {a / b}");
Console.WriteLine($"{a} % {b} = {a % b}");

직접 해 보기

bash
cd src/Primitives && dotnet run
cd ../ValueVsReference && dotnet run
cd ../VarConst && dotnet run
cd ../StringBasics && dotnet run

다음 단원

[03_연산자와_표현식](../03_연산자와_표현식/) — 변수로 계산하고 비교하는 도구를 배웁니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗