Unisquads
로그인
홈
이용약관·개인정보처리방침·콘텐츠정책·© 2026 Unisquads
이용약관·개인정보처리방침·콘텐츠정책
© 2026 Unisquads. All rights reserved.

CLR (r1)

이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.14 23:09

CLR

이름

CLR

전체 명칭

공용 언어 런타임(Common Language Runtime)

개발사

마이크로소프트(Microsoft)

주요 기능

메모리 관리, 가비지 컬렉션, 예외 처리, 보안, JIT 컴파일

주요 지원 언어

C#, VB.NET, F#, C++/CLI

플랫폼

.NET 프레임워크, .NET Core/.NET 5+

역할

관리 코드(Managed Code)의 실행 환경 제공

기술 상세 정보

출시 연도

2002년 (.NET Framework 1.0과 함께)

핵심 구성 요소

CTS(공용 타입 시스템), CLS(공용 언어 사양), 가상 실행 시스템(VES)

메모리 관리 방식

자동 가비지 컬렉션(Garbage Collection)

보안 모델

코드 액세스 보안(CAS), 역할 기반 보안(RBS)

코드 실행 방식

중간 언어(IL/MSIL)을 JIT 컴파일러로 네이티브 코드 변환

호환성

다양한 프로그래밍 언어 간 상호 운용성 지원

주요 관련 기술

어셈블리(.dll, .exe), 메타데이터, AppDomain

개발 도구

비주얼 스튜디오(Visual Studio)

현재 상태

.NET 5/.NET 6 이상의 CoreCLR로 진화

1. 개요

CLR(공용 언어 런타임)은 마이크로소프트의 .NET Framework 및 그 후속 플랫폼의 실행 엔진이다. CLR은 중간 언어(IL)로 컴파일된 코드를 관리하고 실행하며, 메모리 관리, 보안, 예외 처리, 스레드 관리 같은 핵심 서비스를 제공한다. 이로 인해 개발자는 플랫폼의 하위 수준 세부 사항보다는 애플리케이션 로직 자체에 더 집중할 수 있다.

CLR의 주요 목표는 언어 간 상호 운용성과 플랫폼 독립성을 제공하는 것이다. 서로 다른 프로그래밍 언어(예: C#, VB.NET, F#)로 작성된 코드는 모두 공통의 중간 언어로 컴파일되고, CLR에 의해 동일한 환경에서 실행된다. 이 설계는 "한 번 작성, 어디서나 실행"이라는 개념을 구현하며, 특정 하드웨어나 운영 체제에 대한 종속성을 줄인다.

CLR이 코드를 실행하는 과정은 일반적으로 다음과 같은 단계를 거친다. 먼저, 소스 코드는 언어별 컴파일러에 의해 IL 코드와 메타데이터를 포함하는 어셈블리로 컴파일된다. 실행 시점에 CLR의 JIT 컴파일러는 이 IL 코드를 현재 플랫폼의 네이티브 기계어 코드로 실시간 변환한다. 이 과정에서 코드 검증과 최적화가 수행된다.

CLR의 도입은 마이크로소프트의 소프트웨어 개발 패러다임을 크게 변화시켰다. 이전의 COM 기반 모델에 비해 향상된 보안, 견고한 메모리 관리(가비지 컬렉션), 그리고 단순화된 배포 모델을 제공한다. CLR은 .NET Framework의 초기 버전부터 핵심이었으며, 이후 .NET Core 및 통합된 .NET 5 이상의 플랫폼에서도 진화된 형태인 CoreCLR로 그 역할을 이어가고 있다.

2. CLR의 핵심 구성 요소

CLR은 .NET Framework의 실행 엔진으로, 여러 핵심 구성 요소를 통해 다양한 프로그래밍 언어로 작성된 코드를 통일된 환경에서 실행할 수 있게 한다. 이 구성 요소들은 공통 언어 기반(CLI) 사양을 구현하며, 언어 간 상호 운용성과 일관된 실행 환경을 보장한다.

주요 구성 요소로는 공통 타입 시스템(CTS), 공용 언어 사양(CLS), 그리고 가상 실행 시스템(VES)이 있다. CTS는 모든 .NET 언어가 공유하는 형식 시스템을 정의하여, 서로 다른 언어 간에 데이터 타입을 일관되게 정의하고 사용할 수 있는 기반을 마련한다. 예를 들어, C#의 int와 VB.NET의 Integer는 CTS에서 모두 System.Int32로 매핑된다. CLS는 CTS의 하위 집합으로, 언어 간 상호 운용성을 위해 준수해야 하는 규칙과 표준을 정의한다. 개발자가 CLS 규격의 구성 요소만 사용하면, 해당 라이브러리는 다른 .NET 언어에서도 문제없이 참조 및 사용될 수 있다.

실제 코드 실행을 담당하는 핵심은 가상 실행 시스템(VES)이며, 이는 흔히 CLR 자체를 가리킨다. VES는 중간 언어(IL)로 컴파일된 코드를 로드하고, JIT 컴파일(Just-In-Time Compilation)을 통해 실행 시점에 네이티브 머신 코드로 변환하여 실행한다. 이 과정에서 메모리 관리, 보안 검사, 예외 처리 등의 서비스를 제공한다. JIT 컴파일은 필요한 메서드가 호출될 때마다 실시간으로 컴파일을 수행하는 방식으로, 초기 실행 속도는 느릴 수 있지만 캐싱을 통해 반복 호출 시 성능을 최적화한다.

구성 요소

약어

주요 역할

공통 타입 시스템

CTS

모든 .NET 언어에 공통된 데이터 타입 체계 정의

공용 언어 사양

CLS

언어 간 상호 운용성을 보장하는 규칙 집합 정의

가상 실행 시스템

VES

IL 코드 실행, 메모리 관리, 보안 등 런타임 서비스 제공[1]

이 세 요소는 상호 보완적으로 작동한다. CTS가 공통의 '단어'(타입)를 제공하면, CLS는 이 단어들을 사용해 모든 언어가 이해할 수 있는 '문법'(규칙)을 정한다. 마지막으로 VES는 이 규칙에 따라 작성된 '문장'(IL 코드)을 실제로 읽고 실행하는 '해석자' 역할을 한다. 이 구조 덕분에 개발자는 선호하는 언어로 개발하면서도, 다른 .NET 언어로 만들어진 라이브러리를 자유롭게 활용할 수 있다.

2.1. 공통 타입 시스템(CTS)

공통 타입 시스템(Common Type System, CTS)은 CLR이 지원하는 모든 데이터 타입과 관련 연산을 정의하는 형식 시스템의 핵심 규격이다. 이는 .NET 언어들이 서로 호환될 수 있는 기반을 제공하며, 서로 다른 프로그래밍 언어로 작성된 코드 간의 원활한 상호 운용성을 보장한다. CTS는 값 타입과 참조 타입이라는 두 가지 주요 범주로 모든 타입을 구분한다.

값 타입은 스택에 직접 저장되며 변수에 값 자체가 포함된다. 기본 제공 타입(예: int, float, bool), 열거형(enum), 사용자 정의 구조체(struct)가 이에 해당한다. 참조 타입은 힙에 할당되며, 변수는 힙에 있는 객체의 참조(주소)를 보유한다. 클래스, 인터페이스, 배열, 대리자(delegate)가 대표적인 참조 타입이다. 이 구분은 변수 할당, 매개변수 전달 방식, 메모리 관리 방식에 근본적인 차이를 만든다.

CTS는 모든 타입이 반드시 System.Object 클래스에서 최종적으로 상속받도록 규정한다. 이는 모든 타입이 ToString(), GetType(), Equals()와 같은 공통 메서드를 공유함을 의미한다. 또한, 타입의 가시성(public, private 등), 멤버(메서드, 속성, 필드, 이벤트)의 정의 방식, 상속 규칙 등에 대한 표준 모델을 제공하여 언어 간의 일관성을 유지한다.

타입 범주

저장 위치

할당 방식

예시

기본값

값 타입

주로 스택

값 복사

int, char, struct

각 타입의 기본값 (예: 0, false)

참조 타입

관리 힙

참조 복사

class, interface, array, string

null

이 체계 덕분에 C#에서 정의한 클래스를 Visual Basic .NET에서 인스턴스화하고 사용하는 것이 가능해진다. CTS는 공용 언어 사양(CLS)의 상위 집합으로, CLS는 언어 간 상호 운용성을 위해 CTS의 기능 중 모든 언어가 준수해야 하는 공통 부분을 규정한다.

2.2. 공용 언어 사양(CLS)

공용 언어 사양(CLS)은 공통 타입 시스템(CTS)의 하위 집합으로, 서로 다른 프로그래밍 언어로 작성된 코드가 .NET Framework 환경에서 원활하게 상호 운용되도록 보장하기 위해 정의된 규칙과 제약 조건의 집합이다. 주된 목적은 언어 간 상호 운용성을 촉진하는 것이다. CLS는 주로 컴포넌트와 라이브러리의 개발자를 대상으로 하며, 이러한 라이브러리가 CLS를 준수하여 작성되면 다른 모든 CLS 호환 언어에서 안전하게 사용될 수 있다.

CLS 규칙은 공개적으로 노출되는 API(예: public, protected 멤버)에만 적용된다. 내부 구현(private 또는 internal 멤버)은 어떠한 언어 특성도 사용할 수 있다. 주요 규칙으로는 특정 데이터 타입의 사용 제한, 메서드 오버로딩 규칙, 예외 처리 방식의 통일 등이 포함된다. 예를 들어, CLS 규격 코드는 부호 없는 정수 타입(uint)을 공개 API의 일부로 노출해서는 안 되며, 오버로드된 메서드는 매개변수의 개수나 타입만으로 구별되어야 한다[2].

규칙 범주

예시

비CLS 규격 예시

명명 규칙

식별자는 대소문자를 구분하여 구별되어야 함.

Calculate와 calculate를 동일한 범위에서 구별 불가한 이름으로 사용.

타입 규칙

공개 멤버의 노출된 타입은 CLS 규격 타입이어야 함.

공개 메서드의 매개변수 타입으로 UInt32(uint) 사용.

메타데이터 규칙

어셈블리는 CLS 규격임을 명시해야 함.

어셈블리 매니페스트에 [assembly: CLSCompliant(true)] 특성이 없음.

개발자는 [assembly: CLSCompliant(true)] 특성을 프로젝트에 적용하여 컴파일러가 CLS 규격을 검사하도록 할 수 있다. 이는 C#, Visual Basic .NET, F# 등 다양한 언어가 공통의 중간 언어(IL)로 컴파일된 후 하나의 애플리케이션에서 함께 작동할 수 있는 기반을 제공한다. 따라서 CLS는 .NET의 "언어 중립성"이라는 핵심 가치를 실현하는 데 필수적인 구성 요소이다.

2.3. 가상 실행 시스템(VES)과 JIT 컴파일

가상 실행 시스템(VES)은 CLR의 실행 엔진으로, 중간 언어(IL)로 컴파일된 코드를 실제 컴퓨터가 이해할 수 있는 기계어로 변환하고 실행하는 역할을 담당한다. VES의 가장 핵심적인 기능은 JIT 컴파일(Just-In-Time Compilation)이다. JIT 컴파일러는 어셈블리가 로드되고 메서드가 처음 호출될 때, 해당 메서드의 IL 코드를 실시간으로 네이티브 코드로 컴파일한다. 이렇게 컴파일된 네이티브 코드는 메모리에 캐시되어, 동일한 메서드가 다시 호출될 때는 재컴파일 없이 바로 실행될 수 있다.

JIT 컴파일 과정은 단순한 번역을 넘어서 다양한 최적화를 수행한다. 실행 환경의 실제 CPU 아키텍처를 고려한 최적화[3]와 애플리케이션의 런타임 동작을 기반으로 한 최적화[4]가 대표적이다. 이는 정적 컴파일 언어가 가질 수 없는 상황별 최적화의 이점을 제공한다.

VES의 작동 모드는 크게 두 가지로 구분된다. 표준 JIT 컴파일 외에, 애플리케이션 설치 시 또는 실행 전에 모든 IL 코드를 네이티브 이미지로 미리 컴파일하는 NGEN(Native Image Generator) 도구를 사용할 수 있다. NGEN으로 생성된 네이티브 이미지는 시작 속도를 개선하고 메모리 사용을 최적화할 수 있지만, 런타임 정보를 활용한 동적 최적화는 적용되지 않는다.

컴파일 방식

시점

최적화 특징

장점

JIT 컴파일

메서드 첫 실행 시

런타임 정보 기반 동적 최적화 가능

상황에 맞는 고도 최적화, 플랫폼 호환성

NGEN (미리 컴파일)

설치 시 또는 실행 전

정적 최적화만 적용

애플리케이션 시작 속도 향상, 메모리 공유 가능

이러한 VES와 JIT 컴파일의 구조는 "한 번 작성하면 어디서나 실행된다"는 관리 코드의 이식성 원칙을 실현하는 기술적 기반이 된다. 개발자는 특정 CPU를 대상으로 코드를 작성할 필요 없이, CLR이 제공하는 가상 머신을 대상으로 IL 코드를 생성하면 된다. VES가 해당 플랫폼에 맞는 네이티브 코드로 최종 변환을 담당하기 때문이다.

3. 메모리 관리와 가비지 컬렉션(GC)

CLR은 개발자가 명시적으로 메모리를 할당하고 해제하는 작업에서 벗어나게 하는 가비지 컬렉션 기반의 자동 메모리 관리 모델을 제공한다. 이 모델의 핵심은 관리 힙이다. 개발자가 new 연산자를 사용하여 객체를 생성하면, CLR은 관리 힙의 다음 사용 가능한 주소에 해당 객체를 할당한다. 객체는 더 이상 참조되지 않게 되면 가비지 컬렉션의 대상이 되며, 개발자는 이를 명시적으로 해제할 필요가 없다. 이는 메모리 누수와 댕글링 포인터와 같은 문제를 상당 부분 방지한다.

가비지 컬렉터는 세대별 수집 방식을 사용하여 효율성을 극대화한다. 객체는 생성 시점에 따라 0세대, 1세대, 2세대로 구분된다. 가장 최근에 생성된 객체는 0세대에 속하며, 가장 빈번하게 수집이 발생한다. 한 번의 가비지 컬렉션에서 살아남은 객체는 다음 세대로 승격된다. 이 방식은 '신생 객체는 일찍 사라지고, 오래된 객체는 더 오래 참조된다'는 경험적 관찰에 기반한다. 따라서 수집기는 대부분의 경우 메모리 상의 작은 영역(0세대 힙)만을 빠르게 스캔하여 처리함으로써 애플리케이션의 일시 정지 시간을 최소화한다.

세대

설명

수집 빈도

0세대

가장 최근에 할당된 객체. 수명이 짧은 객체가 대부분이다.

매우 빈번함

1세대

0세대 수집에서 살아남아 승격된 객체. 0세대와 2세대 사이의 버퍼 역할을 한다.

중간 정도

2세대

수명이 매우 긴 객체. 정적 데이터나 애플리케이션이 시작될 때 로드된 객체 등이 여기에 속한다.

드물게

가비지 컬렉션은 일반적으로 관리 힙의 여유 공간이 부족해지거나 시스템 메모리 압박이 있을 때 발생한다. 수집 과정에서는 살아 있는 객체를 식별하고, 그 객체들을 힙의 한쪽으로 압축하여 단편화를 방지한다. 이때 객체의 메모리 주소가 변경되므로, CLR은 모든 객체 참조를 올바른 새 주소로 업데이트한다. 대용량 객체는 별도의 대용량 객체 힙에 할당되어 2세대로 간주되지만, 공간 효율성을 위해 압축되지 않을 수 있다[5].

3.1. 관리 힙과 수명 주기

관리되는 모든 참조 타입 객체와 박싱된 값 타입은 관리 힙이라는 CLR이 관리하는 메모리 영역에 할당됩니다. 이 영역은 운영 체제로부터 예약되어 있으며, 객체 생성 요청이 들어올 때마다 힙의 다음 사용 가능한 주소에 메모리를 할당하는 연속적인 방식으로 작동합니다. 이 할당 과정은 매우 빠르며, 단순히 포인터를 이동시키는 것에 가깝습니다.

객체의 수명 주기는 루트 참조에 의해 결정됩니다. 루트 참조는 전역 변수, 정적 필드, 지역 변수, CPU 레지스터의 참조, 파이널라이저 큐의 참조 등 애플리케이션에서 객체에 접근할 수 있는 모든 경로를 의미합니다. 가비지 컬렉터는 정기적으로 또는 메모리 부족 시에 실행되어, 모든 루트 참조부터 시작하여 도달 가능한 객체 그래프를 추적합니다. 이 과정에서 도달할 수 없는 객체는 더 이상 사용되지 않는 것으로 판단되어 가비지로 표시됩니다.

가비지 컬렉션 사이클은 크게 표시(Mark), 계획(Plan), 휩쓸기(Sweep), 조각 모음(Compact) 단계로 나눌 수 있습니다. 표시 단계에서는 모든 활성 객체를 식별합니다. 계획 단계에서는 죽은 객체가 차지한 공간을 계산하고, 조각 모음이 유익한지 판단합니다. 휩쓸기 단계에서는 죽은 객체의 메모리 공간을 재사용 가능한 것으로 표시합니다. 마지막으로 조각 모음 단계에서는 살아남은 객체들을 힙의 한쪽으로 이동시켜 단편화를 줄이고, 할당 포인터를 새로운 연속된 공간의 시작점으로 재설정합니다.

객체의 최종 정리에는 파이널라이저가 관여할 수 있습니다. 파이널라이저가 정의된 객체는 가비지 컬렉션에서 즉시 회수되지 않고, 파이널라이저 스레드가 해당 메서드를 실행한 후 다음 사이클에서 회수됩니다. 이로 인해 메모리 해제가 지연되고 성능에 부정적 영향을 미칠 수 있으므로, IDisposable 인터페이스를 통한 명시적 자원 해제 패턴이 권장됩니다[6].

3.2. 세대별 가비지 컬렉션

가비지 컬렉션은 관리 힙에서 더 이상 사용되지 않는 객체를 식별하고 회수하여 메모리를 확보하는 과정이다. CLR의 가비지 컬렉터는 성능을 최적화하기 위해 객체를 수명에 따라 0세대, 1세대, 2세대의 세 가지 세대로 구분하여 관리한다. 이는 '대부분의 객체는 짧은 시간 동안만 사용된다'는 경험적 관찰에 기반한 세대별 가비지 수집이라는 전략이다.

새로 할당된 객체는 0세대에 위치한다. 가비지 컬렉션이 발생할 때, 0세대에서 살아남은(아직 참조되고 있는) 객체는 1세대로 승격된다. 마찬가지로, 1세대에서 가비지 컬렉션이 발생한 후 살아남은 객체는 2세대로 이동한다. 2세대는 가장 오래 살아남은 객체들을 담는다. 각 세대별 컬렉션의 빈도와 방식은 다르게 설계되어 있다. 0세대 컬렉션은 가장 빈번하게 발생하며 속도가 빠르다. 2세대 컬렉션은 전체 힙을 대상으로 할 수 있어 가장 부담이 크지만, 상대적으로 드물게 수행된다.

이 세대별 접근법의 주요 이점은 성능이다. 작은 0세대 힙 영역만을 빈번히 검사함으로써 전체 힙을 스캔하는 데 드는 비용을 줄인다. 또한, 수명이 짧은 임시 객체는 0세대에서 빠르게 회수되고, 장수하는 객체는 2세대로 이동하여 계속해서 검사 대상에서 벗어나게 되어 효율성이 높아진다. 가비지 컬렉터는 추가적으로 대형 객체 힙을 별도로 관리하여 큰 객체(일반적으로 85,000바이트 이상)가 세대 간 이동으로 인한 복사 비용을 발생시키지 않도록 한다.

세대

설명

컬렉션 빈도

대상 영역

0세대

가장 최근에 할당된, 수명이 짧은 객체

매우 빈번

0세대 힙 세그먼트

1세대

0세대 컬렉션에서 살아남아 승격된 객체

중간 정도

0세대 및 1세대 힙 세그먼트

2세대

모든 세대 컬렉션에서 살아남은 장수 객체

드물게

전체 관리 힙(0,1,2세대 및 LOH)

이 구조는 애플리케이션의 메모리 할당 패턴에 동적으로 적응한다. 예를 들어, 많은 양의 객체가 0세대 컬렉션에서 살아남아 1세대로 승격되면, 가비지 컬렉터는 다음 컬렉션 시 1세대의 임계값을 조정하거나 1세대 컬렉션을 트리거할 수 있다[7].

4. 어셈블리와 메타데이터

어셈블리는 CLR에서 배포, 버전 관리, 보안 및 재사용의 기본 단위가 되는 논리적 실행 파일이다. 하나 이상의 모듈과 리소스 파일로 구성되며, 반드시 하나의 매니페스트를 포함한다. 이 매니페스트는 어셈블리에 대한 메타데이터를 담고 있어 어셈블리의 정체성(이름, 버전, 문화권, 공개 키), 구성 파일 목록, 그리고 이 어셈블리가 의존하는 다른 어셈블리에 대한 참조 정보를 기술한다. 어셈블리는 전용 어셈블리로 응용 프로그램 디렉터리에 배치되거나, 강력한 이름으로 서명되어 전역 어셈블리 캐시(GAC)에 설치되어 여러 응용 프로그램에서 공유될 수 있다.

모든 관리 코드는 실행되기 전에 반드시 어셈블리로 패키징된다. 어셈블리의 물리적 형태는 단일 파일(.exe 또는 .dll)일 수도 있고, 여러 네트모듈과 리소스 파일로 구성된 다중 파일 어셈블리일 수도 있다. CLR의 클래스 로더는 매니페스트를 읽어 필요한 타입을 찾고 로드하는 방법을 알게 된다. 이 구조는 DLL 지옥과 같은 버전 충돌 문제를 완화하는 데 기여한다.

어셈블리 내에는 코드와 함께 상세한 메타데이터가 풍부하게 포함된다. 이 메타데이터는 어셈블리에 정의된 모든 타입(클래스, 인터페이스, 구조체 등), 해당 타입의 멤버(메서드, 속성, 필드, 이벤트), 그리고 이러한 요소들의 시그니처와 특성에 대한 정보를 이진 형태로 기술한다. 메타데이터는 IL 코드와 항상 함께 생성되며, 런타임에 JIT 컴파일러, 가비지 컬렉터, 보안 시스템 등이 정확하게 코드를 실행하고 관리하는 데 필수적인 정보를 제공한다.

메타데이터의 강력한 활용 사례는 리플렉션이다. 리플렉션 API(System.Reflection 네임스페이스)를 사용하면 실행 중인 프로그램이 자신이나 다른 어셈블리의 메타데이터를 검사하여 타입 정보를 동적으로 조회하고, 인스턴스를 생성하며, 메서드를 호출할 수 있다. 이 기능은 객체 직렬화/역직렬화, 데이터 바인딩, 의존성 주입 컨테이너, IDE의 인텔리센스와 같은 도구의 기반이 된다. 메타데이터는 또한 언어 간 상호 운용성을 가능하게 하는 근간이 되며, 컴파일 타임에 타입 안정성을 보장하는 데 결정적인 역할을 한다.

4.1. 어셈블리 구조와 매니페스트

어셈블리는 CLR에서 배포, 버전 관리, 보안 및 재사용의 기본 단위가 되는 논리적 단위이다. 하나의 물리적 파일(.exe 또는 .dll)로 구성될 수도 있고, 여러 모듈과 리소스 파일로 구성될 수도 있다. 어셈블리의 핵심은 매니페스트(Manifest)라고 불리는 메타데이터 테이블을 포함한다는 점이다. 매니페스트는 어셈블리에 대한 자체 설명 정보를 담고 있어, CLR이 어셈블리를 실행하는 데 필요한 모든 정보를 제공한다.

매니페스트에는 다음과 같은 주요 정보가 포함되어 있다.

정보 항목

설명

어셈블리 식별 정보

어셈블리의 이름, 버전, 문화권, 공개 키 토큰을 포함하는 강력한 이름(Strong Name)[8]이다.

파일 목록

어셈블리를 구성하는 모든 파일(모듈, 리소스 파일 등)의 이름과 해시 값을 나열한다.

참조된 어셈블리

이 어셈블리가 정적으로 참조하는 다른 모든 어셈블리의 이름과 버전 정보를 기록한다.

내보낸 형식

어셈블리 내에서 다른 어셈블리에 공개적으로 노출되는 클래스, 인터페이스 등의 정보를 기술한다.

이러한 구조는 의존성 지옥(DLL Hell) 문제를 완화하는 데 기여한다. CLR은 매니페스트에 기록된 버전 정보를 바탕으로 정확한 버전의 어셈블리를 로드할 수 있다. 또한 어셈블리는 전용(private)으로 응용 프로그램 디렉터리에 배치되거나, 전역 어셈블리 캐시(GAC)에 공유 어셈블리로 설치될 수 있다. 매니페스트는 어셈블리가 자체적으로 완결된(self-describing) 단위가 되어, 시스템 레지스트리와 같은 외부 정보에 의존하지 않고도 독립적으로 실행될 수 있게 한다.

4.2. 리플렉션과 메타데이터 활용

리플렉션은 실행 중인 어셈블리의 메타데이터를 검사하고, 그 안에 정의된 타입, 멤버, 모듈 등을 동적으로 탐색하거나 인스턴스를 생성하고 메서드를 호출하는 기능을 제공합니다. 이는 런타임에 타입 정보에 접근할 수 있게 해주는 강력한 메커니즘입니다. 리플렉션의 핵심 클래스는 System.Type이며, typeof 연산자나 Object.GetType() 메서드를 통해 특정 타입의 Type 객체를 얻을 수 있습니다.

리플렉션을 활용하면 프로그램에서 알려지지 않은 타입을 조사하고 조작할 수 있습니다. 일반적인 활용 사례로는 타입의 필드, 속성, 메서드 목록을 열거하거나, 특정 애트리뷰트가 적용된 멤버를 찾는 것이 있습니다. 또한 Activator.CreateInstance 메서드를 사용하여 타입의 인스턴스를 동적으로 생성하거나, MethodInfo.Invoke를 통해 메서드를 호출할 수 있습니다. 이 기능은 객체 직렬화/역직렬화, 의존성 주입 컨테이너, 데이터 바인딩, 플러그인 아키텍처 구현 등 다양한 고급 시나리오의 기반이 됩니다.

활용 분야

설명

주요 클래스/메서드 예시

타입 정보 검사

어셈블리 내 타입의 이름, 네임스페이스, 기본 클래스, 구현 인터페이스 등을 확인합니다.

Assembly.GetTypes(), Type.Name, Type.BaseType

멤버 탐색 및 호출

특정 타입의 생성자, 메서드, 속성, 필드를 찾아내고 실행합니다.

Type.GetMethod(), MethodInfo.Invoke, PropertyInfo.GetValue

애트리뷰트 처리

타입이나 멤버에 적용된 사용자 정의 또는 시스템 애트리뷰트를 읽어 로직을 변경합니다.

Attribute.GetCustomAttribute, MemberInfo.GetCustomAttributes

동적 타입 생성

런타임에 새로운 타입과 어셈블리를 생성합니다[9].

AssemblyBuilder, TypeBuilder, ILGenerator

리플렉션은 유연성을 제공하지만, 성능 오버헤드와 보안 고려 사항이 따릅니다. 정적으로 알 수 있는 타입 작업에는 리플렉션 사용을 피하는 것이 좋습니다. 또한 완전 신뢰 환경이 아닐 경우 필요한 권한이 제한될 수 있습니다. C#의 dynamic 키워드나 ExpandoObject 같은 후속 기능들은 일부 동적 시나리오에서 리플렉션 사용을 간소화하는 대안을 제공하기도 합니다.

5. 보안 모델

CLR의 보안 모델은 관리 코드의 실행을 안전하게 제어하기 위한 체계를 제공한다. 이 모델은 주로 코드 액세스 보안(CAS)과 역할 기반 보안(RBS)이라는 두 가지 핵심 메커니즘으로 구성된다. CAS는 코드 자체의 출처나 신원, 서명 상태와 같은 증거를 바탕으로 코드에 부여된 권한을 결정한다. 예를 들어, 로컬 시스템에서 실행되는 코드는 파일 시스템에 대한 무제한 접근 권한을 가질 수 있지만, 인터넷에서 다운로드된 코드는 매우 제한된 권한 집합만을 받는다. 이 권한 검사는 스택 워크(Stack Walk) 과정을 통해 이루어지며, 호출 체인의 모든 어셈블리가 요청된 권한을 가졌는지 확인한다.

역할 기반 보안은 코드를 실행하는 사용자의 신원과 역할에 기반하여 접근을 제어한다. 주로 PrincipalPermission 객체나 선언적 특성을 사용하여 구현된다. 이는 애플리케이션 수준에서 특정 메서드나 클래스에 대한 접근을 특정 사용자나 관리자 역할을 가진 사용자로만 제한하는 데 사용된다. CAS와 RBS는 서로 보완적으로 작동하여, 코드가 무엇을 할 수 있는지(권한)와 누가 코드를 실행하는지(역할)를 모두 고려한 다층적인 보안을 제공한다.

.NET Framework 4.0 이후로 CAS 정책의 기본 설정이 크게 변경되었다. 이전에는 관리자가 컴퓨터 전체에 걸쳐 세분화된 CAS 정책을 구성할 수 있었지만, 기본적으로 코드는 완전 신뢰로 실행되도록 전환되었다[10]. 이는 복잡성과 관리 부담을 줄이기 위한 조치였다. 보다 세밀한 샌드박싱이 필요한 경우, 새로운 호스트나 애플리케이션 도메인을 생성하여 제한된 권한 집합을 명시적으로 할당하는 방식으로 대체되었다.

보안 메커니즘

기준

주요 목적

구현 예시

코드 액세스 보안(CAS)

코드의 출처, 서명 등 증거

악성 코드로부터 시스템 리소스 보호

FileIOPermission, SecurityPermission

역할 기반 보안(RBS)

실행 사용자의 신원 및 역할

비즈니스 로직 수준의 접근 제어

PrincipalPermission, [PrincipalPermission(SecurityAction.Demand, Role="Admin")]

이러한 보안 인프라는 관리 코드가 메모리 안전성과 타입 안전성을 유지하면서도 외부 리소스에 대한 접근을 통제할 수 있는 기반을 마련한다.

5.1. 코드 액세스 보안(CAS)

코드 액세스 보안은 CLR이 제공하는 보안 메커니즘으로, 실행 중인 코드의 신원(예: 출처, 발행자, 암호화 서명)에 기반하여 해당 코드가 보호된 리소스나 작업에 접근할 수 있는 권한을 제어한다. 이는 전통적인 역할 기반 보안이 사용자 신원에 초점을 맞추는 것과 대비되는 개념이다. CAS 정책은 코드가 가진 증거(어셈블리가 로드된 위치, 디지털 서명, 강력한 이름 등)를 평가하여 권한 집합을 부여하는 방식으로 작동한다.

주요 구성 요소로는 권한, 증거, 정책 수준, 권한 집합이 있다. 권한은 파일 시스템 접근, 레지스트리 접근, 네트워크 연결 생성 등 보호된 작업을 나타낸다. 증거는 어셈블리와 함께 제공되어 출처를 증명하는 정보이다. CLR은 엔터프라이즈, 머신, 사용자, 애플리케이션 도메인 네 가지 정책 수준에서 증거를 평가하며, 각 수준에서 부여된 권한 집합의 교집합이 코드에 부여되는 최종 권한이 된다.

CAS의 동작은 런타임에 스택 워크를 수행하여 모든 호출자 체인에 필요한 권한이 있는지 확인하는 방식으로 이루어진다. 이는 한 코드 조각이 높은 권한을 가진 다른 코드를 호출하여 권한을 우회하는 "래더 공격"을 방지하기 위한 것이다. 또한, 코드는 특정 권한을 요구하거나, 특정 권한이 없어도 실행되도록 선언할 수 있으며, 이는 런타임이 어셈블리를 로드할 때 검사한다.

구성 요소

설명

권한(Permission)

파일 I/O, 환경 변수 읽기, 리플렉션 사용 등 보호된 작업에 대한 접근 권한을 정의하는 객체

증거(Evidence)

어셈블리의 출처(URL, 사이트, 영역)나 신원(강력한 이름, 발행자)을 증명하는 정보

정책 수준(Policy Level)

권한을 부여하는 정책이 정의되는 계층(머신, 사용자, 엔터프라이즈, 애플리케이션 도메인)

권한 집합(Permission Set)

하나 이상의 권한을 그룹으로 묶은 것(예: FullTrust, Internet, LocalIntranet)

이 모델은 부분적으로 신뢰하는 코드(예: 인터넷에서 다운로드한 컨트롤)가 시스템 리소스에 피해를 주지 않도록 샌드박스 환경에서 실행되도록 하는 데 유용했다. 그러나 실제 환경에서의 복잡한 정책 관리와 성능 오버헤드, 공격 경로의 다양화로 인해 .NET Framework 4.0 이후로는 기본적으로 사용되지 않으며, 보안 투명성 모델 등 다른 접근법이 강조되었다[11].

5.2. 역할 기반 보안

역할 기반 보안은 CLR이 제공하는 보안 메커니즘 중 하나로, 애플리케이션이 실행 중인 사용자나 프로세스의 역할과 권한을 기반으로 특정 작업이나 리소스에 대한 접근을 제어합니다. 이는 코드 액세스 보안이 코드 자체의 신원과 권한을 검사하는 것과는 차별화된 접근법입니다. 역할 기반 보안은 주로 사용자 인증 및 권한 부여와 관련된 비즈니스 로직 수준의 보안 요구사항을 구현하는 데 사용됩니다.

이 모델의 핵심 구성 요소는 주체 객체와 권한 객체입니다. 주체 객체는 실행 중인 코드의 사용자 신원과 그가 속한 역할(예: 관리자, 일반 사용자, 게스트)을 캡슐화합니다. 개발자는 코드 내에서 IsInRole 메서드를 사용하거나 PrincipalPermission 요구를 통해 현재 주체가 특정 역할을 갖고 있는지 명시적으로 확인할 수 있습니다. 이를 통해 특정 메서드나 기능에 대한 접근을 역할에 따라 허용하거나 거부할 수 있습니다.

역할 기반 보안의 작동 방식은 다음과 같이 요약할 수 있습니다.

단계

설명

1. 인증

운영 체제, 애플리케이션 또는 사용자 지정 메커니즘을 통해 사용자 신원을 확인합니다.

2. 주체 생성

인증된 사용자의 신원(Identity)과 그가 속한 역할(Role) 정보를 담은 주체(IPrincipal) 객체가 생성되어 현재 스레드(Thread.CurrentPrincipal)에 연결됩니다.

3. 권한 검사

애플리케이션 코드는 런타임에 주체 객체를 검사하여 특정 작업 수행 전 사용자의 역할 멤버십을 확인합니다.

4. 접근 제어

검사 결과에 따라 작업 수행을 허용하거나 보안 예외를 발생시켜 접근을 거부합니다.

이 모델은 .NET Framework의 System.Security.Principal 네임스페이스에 정의된 인터페이스와 클래스들을 통해 구현됩니다. Windows 애플리케이션의 경우 WindowsIdentity와 WindowsPrincipal 클래스를 사용하여 Active Directory 도메인이나 로컬 머신의 Windows 그룹과 통합하는 것이 일반적입니다. 또한, GenericIdentity와 GenericPrincipal을 사용하면 사용자 지정 인증 시스템에 기반한 역할 기반 보안을 쉽게 구축할 수 있습니다.

6. CLR 호스팅과 상호 운용성

CLR 호스팅은 .NET Framework나 .NET Core/.NET 5 이상의 런타임 환경을 프로세스 내에 로드하고 제어하는 메커니즘을 의미한다. 일반적인 .NET 응용 프로그램은 운영체제가 CLR을 시작하지만, SQL Server나 Internet Explorer와 같은 호스트 응용 프로그램은 자체 프로세스 내에서 CLR을 초기화하고 관리할 수 있다. 이를 통해 호스트는 AppDomain을 생성하여 코드를 격리하거나, 메모리 및 스레드 풀 같은 리소스를 관리하거나, 사용자 정의 로더를 제공하여 어셈블리 로딩 정책을 제어할 수 있다. 호스팅 API는 주로 ICLRRuntimeHost 및 ICLRMetaHost와 같은 인터페이스 집합으로 제공되어, 호스트가 런타임의 동작을 세밀하게 조정할 수 있게 한다.

상호 운용성 측면에서 CLR은 기존 COM 구성 요소와의 통합을 광범위하게 지원한다. .NET 어셈블리는 RCW를 통해 COM 객체를 관리 코드에서 마치 .NET 객체인 것처럼 사용할 수 있게 한다. 반대로, .NET 객체는 CCW를 통해 COM 클라이언트에 노출될 수 있다. 이 과정에서 TLBIMP나 TlbExp와 같은 도구가 형식 라이브러리 변환을 담당한다. 또한 P/Invoke 기술을 통해 관리 코드에서 Win32 API 같은 비관리 DLL의 네이티브 함수를 직접 호출할 수 있다. 개발자는 DllImport 특성을 사용하여 호출 규칙과 문자열 마샬링 방식을 지정한다.

상호 운용성 기술

주요 목적

핵심 구성 요소/도구

COM 상호 운용

.NET과 COM 구성 요소 간의 양방향 통합

RCW, CCW, 형식 라이브러리, TlbImp.exe

P/Invoke

관리 코드에서 네이티브 DLL 함수 호출

DllImport 특성, 마샬링, Marshal 클래스

이러한 호스팅과 상호 운용성 기능은 CLR을 단순한 실행 엔진을 넘어서, 레거시 시스템과의 통합이나 복잡한 응용 프로그램 내의 스크립팅 엔진으로서의 역할을 가능하게 하는 핵심 요소이다.

6.1. CLR 호스팅 API

CLR 호스팅은 관리되지 않는 애플리케이션이 CLR을 프로세스 내에 로드하고 실행 환경을 구성하며, 관리 코드를 실행할 수 있도록 하는 기능이다. 이를 가능하게 하는 인터페이스 집합이 CLR 호스팅 API이다. 대표적인 호스트로는 IIS, SQL Server, Microsoft Office 또는 사용자 정의 서비스 애플리케이션이 있다. 호스팅 API는 호스트가 CLR의 동작을 세밀하게 제어할 수 있게 하여, 애플리케이션 도메인 생성, 어셈블리 로드 정책 설정, 쓰레드 풀 관리, 가비지 컬렉션 동작 모니터링 등을 수행한다.

주요 인터페이스는 ICLRRuntimeHost와 ICLRMetaHost 등이 있다. 호스팅 프로세스는 일반적으로 CorBindToRuntimeEx 또는 CLRCreateInstance와 같은 함수를 사용하여 CLR 인스턴스를 초기화한다. 이후 호스트는 ICLRRuntimeHost::Start 메서드를 호출하여 CLR을 시작하고 관리 코드 실행을 준비한다. 호스트는 또한 애플리케이션 도메인 관리자(AppDomainManager)를 지정하여 특정 애플리케이션 도메인 내에서 보안 정책, 어셈블리 로드 해결 방식 등을 사용자 정의할 수 있다.

CLR 호스팅 API를 통해 호스트는 다음과 같은 고급 제어를 할 수 있다.

제어 영역

설명

메모리 관리

가비지 컬렉터가 사용하는 힙의 세그먼트 크기를 제한하거나, 수집 시점을 알림 받을 수 있다.

스레딩

CLR의 스레드 풀을 관리하거나, 관리 코드에서 생성된 스레드의 선점을 제어할 수 있다.

정책 확장

어셈블리 로드 실패 시 대체 경로를 제공하거나, 코드 액세스 보안 정책을 조정할 수 있다.

모니터링

프로파일링 API를 통해 관리 코드의 성능 및 메모리 사용량을 추적할 수 있다.

이러한 호스팅 기능은 서버 환경에서 여러 독립적인 애플리케이션을 단일 프로세스에서 안정적으로 격리 실행하거나, 기존 네이티브 애플리케이션에 관리 코드 컴포넌트를 점진적으로 통합하는 시나리오에서 필수적이다. 예를 들어, SQL Server는 CLR 호스팅을 통해 데이터베이스 내에서 CIL로 작성된 저장 프로시저를 실행한다.

6.2. COM 상호 운용성과 P/Invoke

COM 상호 운용성은 .NET 코드가 기존의 COM 구성 요소(예: ActiveX 컨트롤)를 사용하거나, 반대로 COM 클라이언트가 .NET 어셈블리를 COM 객체처럼 호출할 수 있게 합니다. CLR은 런타임 호출 가능 래퍼와 COM 호출 가능 래퍼를 자동으로 생성하여 양측 간의 데이터 형식, 메모리 관리, 오류 처리 방식 차이를 중개합니다. 이를 통해 개발자는 레거시 COM 라이브러리를 재사용하거나 .NET 컴포넌트를 Microsoft Office 같은 COM 기반 애플리케이션에 노출시킬 수 있습니다.

P/Invoke는 플랫폼 호출 서비스로, 관리 코드에서 Win32 API 같은 네이티브 DLL에 구현된 비관리 C/C++ 함수를 직접 호출하는 메커니즘입니다. 개발자는 DllImport 특성을 사용하여 대상 함수, DLL 이름, 호출 규칙, 문자열 마샬링 방식 등을 선언합니다. CLR은 이 선언을 바탕으로 필요한 마샬링 작업을 수행하여 관리 데이터 형식을 비관리 형식으로 변환하고, 함수 호출 후 결과를 다시 관리 형식으로 반환합니다.

상호 운용성 기술

주요 목적

사용 시나리오 예시

COM Interop

.NET과 COM 구성 요소 간 통신

.NET 애플리케이션에서 Windows Media Player ActiveX 컨트롤 사용

P/Invoke

관리 코드에서 네이티브 DLL 함수 호출

.NET 프로그램에서 User32.dll의 MessageBox 함수 호출

이 두 기술은 .NET이 기존 Windows 생태계와 원활하게 통합될 수 있는 기반을 제공합니다. 그러나 성능 오버헤드가 발생할 수 있으며, 특히 P/Invoke를 통한 잘못된 포인터나 메모리 관리는 애플리케이션의 안정성을 해칠 수 있습니다. 따라서 네이티브 리소스의 정확한 해제와 데이터 마샬링에 주의를 기울여야 합니다.

7. 성능 최적화와 프로파일링

성능 최적화는 CLR 기반 애플리케이션의 실행 효율성을 높이는 과정이다. 핵심적인 최적화 기법으로는 JIT 컴파일 시 적용되는 코드 최적화가 있다. CLR의 JIT 컴파일러는 중간 언어인 CIL을 네이티브 코드로 변환하면서 상수 접기, 데드 코드 제거, 인라인 확장, 루프 최적화 등의 다양한 기법을 적용하여 실행 속도를 향상시킨다. 또한, 가비지 컬렉션의 성능에 직접적인 영향을 미치는 객체 할당 패턴을 관리하는 것이 중요하다. 큰 객체 힙과 세대별 컬렉션의 특성을 이해하고, 불필요한 객체 생성을 최소화하며, 특히 단기 생존 객체를 0세대에 머물게 하는 설계는 GC의 부하를 줄이는 데 핵심적이다.

프로파일링은 애플리케이션의 성능 병목 현상을 식별하는 도구이다. CLR은 성능 카운터와 ETW를 통한 상세한 런타임 이벤트 추적을 제공한다. 프로파일링 도구는 일반적으로 다음과 같은 영역의 데이터를 수집하고 분석한다.

프로파일링 영역

주요 분석 내용

CPU 사용률

가장 많은 연산 시간을 소비하는 메서드(핫 패스) 식별

메모리 할당

객체의 생성 빈도, 크기, 생존 기간 및 가비지 컬렉션 발생 영향 분석

입출력 대기 시간

파일, 네트워크 작업에서의 블로킹 시간 측정

스레드 동시성

잠금 경합, 데드락, 스레드 풀 사용 효율성 분석

성능 튜닝은 측정에서 시작하여, 프로파일러로 병목 지점을 찾고, 최적화 기법을 적용한 후 다시 측정하는 반복적인 과정이다. 일반적인 최적화 방법으로는 알고리즘 복잡도 개선, 비싼 연산 결과 캐싱, 문자열 연결 대신 StringBuilder 사용, boxing/unboxing 최소화 등이 있다. 또한, 비동기 프로그래밍 패턴을 활용하여 입출력 바운드 작업에서의 스레드 블로킹을 방지하는 것도 중요한 최적화 전략이다.

8. .NET Framework와 .NET Core/5+에서의 CLR

.NET Framework의 초기 버전부터 핵심 실행 엔진 역할을 해온 CLR은 .NET Core 및 통합된 후속 플랫폼인 .NET 5 이상으로의 진화 과정에서 상당한 변화를 겪었다. .NET Framework의 CLR은 윈도우 운영체제와 긴밀하게 통합된 단일체 모놀리식 런타임이었다. 이는 강력한 기능을 제공했지만, 크로스 플랫폼 지원과 모듈화, 배포 유연성 측면에서 한계가 있었다.

이러한 한계를 극복하기 위해 등장한 .NET Core는 새로운 런타임 구현체인 CoreCLR을 도입했다. CoreCLR은 원본 CLR의 핵심 설계 개념(예: CTS, 가비지 컬렉션, JIT 컴파일러)을 계승하지만, 모듈화와 최적화에 중점을 두어 재구성되었다. 주요 변화는 다음과 같다.

변화 영역

.NET Framework CLR

.NET Core / .NET 5+의 CoreCLR

플랫폼 지원

주로 Windows

Windows, Linux, macOS를 공식 지원하는 크로스 플랫폼

배포 모델

시스템 전역 설치

애플리케이션과 함께 배포 가능한 자체 포함 배포 지원

구성 요소화

비교적 단일체

필요에 따라 트리밍 가능한 모듈식 설계 (예: ReadyToRun)

성능

지속적 개선

대규모 리팩토링과 새로운 최적화(예: Tiered Compilation) 도입

.NET 5부터는 .NET Framework, .NET Core, Xamarin 등의 런타임이 통합된 단일 .NET 플랫폼이 출시되었다. 이 플랫폼의 런타임은 여전히 CoreCLR을 기반으로 하며, 공식 명칭은 단순히 ".NET 런타임"이지만 내부적으로는 진화된 CLR 아키텍처를 유지한다. 이 통합된 런타임은 AOT 컴파일 시나리오를 더욱 강화한 네이티브 AOT 컴파일과 같은 혁신을 지속적으로 도입하고 있다. 결과적으로, 현대의 .NET 런타임은 원본 CLR의 호환성과 안정성을 유지하면서도 성능, 배포 유연성, 플랫폼 범위 측면에서 훨씬 더 발전된 모습을 보인다.

8.1. 역사적 변화와 CoreCLR

.NET Framework의 초기 버전부터 CLR은 윈도우 운영체제와 긴밀하게 통합된 단일 구현체로 발전해왔다. 그러나 크로스 플랫폼과 오픈 소스라는 현대적 요구사항은 CLR의 구조에 근본적인 변화를 가져왔다.

이 변화의 핵심 결과물은 .NET Core 프로젝트에서 개발된 CoreCLR이다. CoreCLR은 원본 CLR의 핵심 기능(가비지 컬렉터, JIT 컴파일러, 기본 타입 시스템)을 유지하면서도, 모듈화와 이식성을 최우선으로 재설계되었다. 주요 차이점은 다음과 같다.

특징

.NET Framework CLR

.NET Core의 CoreCLR

플랫폼 대상

주로 Microsoft Windows

Windows, Linux, macOS (크로스 플랫폼)

배포 모델

운영체제와 함께 배포되는 시스템 전역 런타임

애플리케이션과 함께 배포 가능한 자체 포함형 런타임

구성 요소화

비교적 단일화된 대형 라이브러리

필요에 따라 패키지를 추가할 수 있는 모듈화된 설계

개발 모델

독점적, 공개 소스 아님

오픈 소스 (.NET Foundation 관리)

이러한 재설계는 .NET 5와 그 이후의 통일된 .NET 플랫폼으로 이어졌다. .NET 5 이상에서는 'CoreCLR'이라는 명칭이 공식적으로 사라지고, 단순히 '.NET 런타임' 또는 'CLR'로 통칭되지만, 그 기반은 CoreCLR의 모듈화되고 크로스 플랫폼인 아키텍처이다. 이 변화는 런타임이 더 가볍고, 다양한 시나리오(클라우드, 컨테이너, 임베디드)에 최적화될 수 있도록 했으며, JIT 컴파일 외에 AOT 컴파일을 통한 성능 최적화 옵션도 확장되었다[12].

9. 관련 기술 및 개념

CLR은 .NET 생태계의 핵심 런타임으로, 여러 관련 기술 및 개념과 밀접하게 연관되어 작동한다. 공통 중간 언어는 CLR이 실행하는 CPU 중립적인 명령어 집합이다. 소스 코드는 C샵 또는 VB.NET 같은 고급 언어 컴파일러에 의해 CIL로 컴파일되며, 이는 플랫폼 간 이식성을 제공하는 핵심 요소이다.

CLR의 설계 철학은 관리 코드 환경을 구현하는 것이다. 이와 대조적으로, 플랫폼 고유 API를 직접 호출하는 비관리 코드는 CLR의 메모리 관리나 보안 서비스를 받지 않는다. 공통 언어 규격은 언어 간 상호 운용성을 보장하는 규칙의 하위 집합이며, 공통 타입 시스템은 모든 .NET 언어가 공유하는 형식 시스템의 기초이다.

CLR을 이해하기 위해서는 다음과 같은 핵심 개념들에 대한 지식이 필요하다.

개념

설명

공통 중간 언어

CLR이 실행하는 중간 언어로, JIT 컴파일에 의해 네이티브 코드로 변환된다.

관리 코드

CLR의 관리 하에 실행되어 가비지 컬렉션과 보안 서비스를 제공받는 코드이다.

어셈블리

.NET에서 배포, 버전 관리, 보안의 기본 단위가 되는 실행 파일 또는 라이브러리이다.

응용 프로그램 도메인

프로세스 내에서 실행되는 응용 프로그램을 격리하는 경계로, 프로세스보다 가벼운 격리 수단을 제공한다.

리플렉션

실행 시점에 어셈블리, 모듈, 형식의 메타데이터를 검사하거나 동적으로 생성 및 호출할 수 있는 기능이다.

가비지 컬렉션

관리 힙의 메모리를 자동으로 회수하여 메모리 누수를 방지하는 CLR의 핵심 메모리 관리 메커니즘이다.

또한, CLR은 JIT 컴파일 방식을 채택하여 CIL 코드를 실행 시점에 네이티브 머신 코드로 변환한다. .NET Framework의 초기 버전부터 .NET Core 및 통합된 .NET 5 이상의 런타임인 CoreCLR로의 진화 과정에서 CLR 구현체는 지속적으로 최적화되었다. 이러한 기술들은 모두 CLR이 제공하는 통합된, 안전한, 효율적인 실행 환경을 구성하는 데 기여한다.

리비전 정보

버전r1
수정일2026.02.14 23:09
편집자unisquads
편집 요약AI 자동 생성