[궁금시리즈] 1-2. .NET과 CLR은 무엇일까?
C#을 처음 배우다 보면 .NET, CLR, JIT, BCL 같은 용어를 자주 접하게 된다. 하지만 이 개념들을 정확히 이해하지 못한 채 “그냥 C#을 실행해 주는 것” 정도로만 알고 넘어가는 경우가 많다.
사실 C#은 혼자서 동작하는 언어가 아니다. C#으로 작성한 코드는 .NET이라는 플랫폼 위에서 실행되며, 그 안에서 CLR(Common Language Runtime) 이 실제 프로그램의 실행을 담당한다.
이번 글에서는 C# 프로그램이 실행되는 전체 구조를 이해해 보자.
.NET이란?
많은 사람들이 C#과 .NET을 같은 의미로 생각한다. 하지만 둘은 전혀 다른 개념이다.
- C#은 프로그래밍 언어이다.
- .NET은 C# 프로그램을 실행하기 위한 개발 플랫폼이다.
예를 들어 자동차를 생각해 보자.
- 운전자 → 개발자
- 운전 방법 → C#
- 자동차 → .NET
운전 방법만 안다고 자동차가 움직이지는 않는다. 자동차라는 플랫폼이 있어야 운전이 가능하듯, C#도 .NET이라는 플랫폼 위에서 실행된다. 즉,
C#은 언어이고, .NET은 그 언어를 실행하기 위한 플랫폼이다.
.NET은 무엇을 제공할까?
.NET은 단순히 프로그램을 실행하는 기능만 제공하지 않는다. 개발에 필요한 수많은 기능을 함께 제공한다. 대표적으로
- 프로그램 실행 환경(CLR)
- 기본 라이브러리(BCL)
- 메모리 관리(GC)
- 파일 입출력
- 네트워크
- 컬렉션
- 멀티스레드
등을 제공한다. 덕분에 개발자는 운영체제의 API를 직접 호출하지 않고도 대부분의 기능을 사용할 수 있다. 예를 들어
File.ReadAllText("text.txt");
이 한 줄의 코드 뒤에는
- 운영체제 파일 API 호출
- 예외 처리
- 문자 인코딩 처리
등 많은 작업이 숨어 있다.
CLR(Common Language Runtime)
CLR은 .NET의 핵심 구성 요소이다. 쉽게 말하면
C# 프로그램을 실제로 실행하는 실행 엔진(Runtime)
이다.
CLR은 단순히 코드를 실행하는 것만이 아니라 프로그램 실행에 필요한 거의 모든 작업을 담당한다. 대표적으로
- 메모리 관리(GC)
- 예외 처리
- 타입 검사
- 스레드 관리
- 보안 검사
- JIT 컴파일
등을 수행한다.
개발자가 작성한 대부분의 C# 코드는 CLR의 관리 아래에서 실행된다. 그래서 C#을 Managed Language라고 부르기도 한다.
C# 프로그램은 어떻게 실행될까?
많은 사람들이
C# → 실행
이라고 생각하지만 실제 과정은 조금 더 복잡하다.
C# 코드
│
▼
C# 컴파일러(csc)
│
▼
IL(Intermediate Language)
│
▼
CLR
│
▼
JIT Compiler
│
▼
Native Code
│
▼
CPU 실행
즉, C# 코드는 바로 기계어로 변환되지 않는다. 먼저 IL(Intermediate Language)이라는 중간 언어로 컴파일된다. 그리고 프로그램이 실행되는 순간 CLR이 필요한 부분을 JIT 컴파일하여 CPU가 실행할 수 있는 Native Code로 변환한다.
이 구조 덕분에 같은 IL 코드를 Windows, Linux, macOS 등 다양한 운영체제에서 사용할 수 있다.
CTS(Common Type System)
.NET에는 여러 언어가 존재한다.
- C#
- F#
- VB.NET
그렇다면 서로 다른 언어끼리 객체를 주고받으려면 타입 체계가 같아야 한다. 이를 위해 만들어진 것이 CTS(Common Type System) 이다. CTS는
“.NET에서 사용하는 모든 타입의 규칙”
을 정의한다.
예를 들어
- int
- string
- class
- interface
- delegate
모두 CTS 규칙을 따른다. 덕분에 서로 다른 언어끼리도 같은 객체를 사용할 수 있다.
CLS(Common Language Specification)
CTS가 모든 타입 규칙을 정의한다면, CLS는
“.NET 언어들이 서로 호환되기 위해 반드시 지켜야 하는 최소 규칙”
이다.
예를 들어 C#에서는 사용할 수 있지만 다른 언어에서는 사용할 수 없는 기능이 존재할 수 있다. CLS는 이런 차이를 최소화하여 언어 간 호환성을 보장한다. 즉,
- CTS → 전체 규칙
- CLS → 공통 규칙
이라고 생각하면 된다.
BCL(Base Class Library)
C#에서 사용하는 대부분의 기능은 이미 .NET이 제공하고 있다.
예를 들어
List<int>
Dictionary<TKey, TValue>
String
File
Task
HttpClient
이 모든 클래스는 개발자가 만든 것이 아니라 BCL(Base Class Library) 에 포함되어 있다. 즉,
BCL은 .NET에서 기본 제공하는 클래스 라이브러리이다.
JIT(Just-In-Time Compiler)
C# 코드는 처음부터 기계어로 컴파일되지 않는다. 대신 IL로 컴파일된 후 실행 시점에 필요한 부분만 Native Code로 변환된다. 이 작업을 수행하는 것이 JIT Compiler이다.
예를 들어
Add(10, 20);
이 메서드를 처음 호출하면
- IL 코드 확인
- Native Code 생성
- 이후부터는 생성된 Native Code 재사용
이라는 과정을 거친다.
덕분에 같은 메서드는 한 번만 컴파일되고, 이후에는 빠르게 실행된다. JIT에 대해서는 이후 별도의 글에서 더 자세히 다룰 예정이다.
.NET의 구성 요소 정리
| 구성 | 요소역할 |
|---|---|
| .NET | C# 프로그램을 위한 개발 플랫폼 |
| CTS | 공통 타입 시스템 |
| CLS | 언어 간 공통 규칙 |
| BCL | 기본 클래스 라이브러리 |
| JIT | IL을 Native Code로 변환 |
마무리
C#은 단순히 컴파일만 하면 실행되는 언어가 아니다. C# 코드가 실행되기까지는 .NET 플랫폼, CLR, IL, JIT, BCL 등 여러 구성 요소가 함께 동작한다.
이 구조를 이해하면 이후 학습하게 될 GC, async/await, Reflection, Span
다음 글에서는 정적 타이핑이란 무엇이며, C#이 왜 정적 타이핑 언어인지 알아보겠다.
핵심 정리
- C#은 프로그래밍 언어이고, .NET은 실행 플랫폼이다.
- CLR은 프로그램 실행을 담당하는 런타임이다.
- C# 코드는 먼저 IL로 컴파일된 후 JIT가 Native Code로 변환한다.
- CTS는 .NET의 공통 타입 규칙을 정의한다.
- CLS는 여러 .NET 언어가 함께 사용할 수 있는 최소 규칙이다.
- BCL은 .NET이 기본 제공하는 클래스 라이브러리이다.
- JIT는 실행 시 필요한 코드를 기계어로 변환한다.