다이나믹 예약어

황현중·2025년 12월 9일

C#

목록 보기
24/24

1. dynamic 한 줄 정의

보통 int, string 같은 정적 타입들은:

  • 컴파일 시점에 타입이 확정되고
  • 그 타입 기준으로 “이 멤버를 써도 되는지/안 되는지”를 컴파일러가 다 검사한다

그런데 dynamic은 이렇게 행동한다:

“지금 컴파일할 땐 타입 검사하지 말고,
실행할 때 실제 타입을 보고 알아서 동작해봐.”

그래서:

  • 멤버 접근
  • 메서드 호출
  • 연산

이런 것들이 전부 런타임에 결정된다.


2. 제일 중요한 특징: “컴파일러가 눈을 감는다”

dynamic x = 10;
x = "Hello";      // OK, 타입 바뀌어도 컴파일 에러 없음
x = DateTime.Now; // OK

Console.WriteLine(x.NonExistMethod()); // 컴파일은 통과하지만...

이 코드를 보면:

  • 컴파일할 때는 에러가 안 난다
    → 컴파일러가 “dynamic이면 일단 믿고 넘어갈게…” 모드라서
  • 실행 시점에 x.NonExistMethod()를 호출하려고 할 때, 실제 x 타입에 그런 메서드가 없으면
    RuntimeBinderException 예외가 터진다

반대로, 같은 걸 object로 쓰면:

object x = 10;
x.NonExistMethod(); // ❌ 컴파일 에러 (object에는 그런 메서드 없음)

object일 때는 컴파일러가 타입을 알고 있으니까:

“야, object에는 NonExistMethod 같은 멤버 없어.” → 컴파일 단계에서 바로 막아버린다.

정리하면:

  • object → 컴파일 시점에 타입이 object라서, object 범위 안에서만 사용 가능
  • dynamic → 컴파일러가 타입 체크를 포기하고, 실행 시점에 실제 타입 기준으로 멤버를 찾고 호출을 시도

3. dynamic vs var – 제일 많이 헷갈리는 포인트

거의 100% 한 번씩 헷갈리는 부분이라, 확실히 정리해 두는 게 좋다.

3-1. var

var s = "Hello";  // s는 컴파일 타임에 string으로 확정
s = 123;          // ❌ 컴파일 에러 (int 대입 불가)
  • var는 그냥 “형식 추론”일 뿐이다.
  • 컴파일러가 s를 보고 “아, 이건 string이구나”라고 딱 정해버린다.
  • 이후에는 string 변수처럼 완전히 작동한다.

3-2. dynamic

dynamic s = "Hello";  // s의 런타임 타입은 string, 컴파일러는 타입 검사 안 함
s = 123;              // OK, 이제 런타임 타입은 int로 바뀜
  • dynamic은 타입이 런타임에 바뀔 수 있다.
  • 컴파일러가 타입 체크를 거의 안 한다.
  • 어떤 메서드를 호출하더라도 일단 컴파일은 통과하고, 실행 중에 실제 타입에 맞는지 검사한다.

3-3. 표로 요약

키워드 의미 타입 결정 시점 타입 체크
var 형식 추론 (컴파일 시) 컴파일 타임 컴파일 타임에 엄격하게 검사
dynamic 런타임 바인딩 (동적 타입) 런타임 런타임에 검사

4. dynamic은 어디에 쓰는 게 좋을까?

대부분의 C# 코드는 정적 타입으로 깔끔하게 짜는 게 좋다.
그래도 dynamic이 유용한 상황이 분명 있다.

4-1. COM / Office 자동화 (Excel, Word 등)

기존 스타일:

using Excel = Microsoft.Office.Interop.Excel;

Excel.Application app = new Excel.Application();
Excel.Workbook wb = app.Workbooks.Open("test.xlsx");
Excel.Worksheet ws = (Excel.Worksheet)wb.Sheets[1];

인터페이스/캐스팅이 많아서 코드가 좀 번거롭다.

dynamic을 쓰면:

dynamic app = new Excel.Application();
dynamic wb = app.Workbooks.Open("test.xlsx");
dynamic ws = wb.Sheets[1];

ws.Cells[1, 1].Value = "Hello";
  • 멤버를 전부 dynamic에 넣고 쓰면, 캐스팅/구체 타입을 덜 신경 써도 된다.
  • 대신, 멤버 이름을 잘못 쓰면 런타임 에러가 난다.

4-2. 동적 언어(.NET 상의 Python, JS 등)와 연동

예를 들어 IronPython, ClearScript 같은 스크립트 엔진과 연동할 때:

  • 스크립트에서 넘어오는 객체의 타입을 C# 컴파일러가 미리 모른다.
  • dynamic으로 받아서 “그냥 메서드 있는 것처럼” 호출할 수 있다.
dynamic pyObj = GetPythonObject(); // 예시
pyObj.Run();   // 런타임에 Run이 있는지 확인 후 호출

4-3. JSON / ExpandoObject를 가볍게 다룰 때

예를 들어 Newtonsoft.Json을 사용할 때:

dynamic obj = JsonConvert.DeserializeObject(json);
Console.WriteLine(obj.name);
Console.WriteLine(obj.address.city);
  • 프로퍼티명을 문자열로 꺼내는 대신, 점(.)으로 바로 접근하는 코드를 작성할 수 있다.
  • 다만, 정석적으로는 DTO 클래스를 만들어서 Person 같은 타입으로 매핑하는 게 더 타입 안정성이 좋다.

5. 예제로 보는 dynamic 동작

5-1. Duck Typing 비슷하게 사용하는 예

static void PrintLength(dynamic x)
{
    Console.WriteLine(x.Length);
}

PrintLength("Hello");            // string.Length → 5
PrintLength(new int[] {1,2,3});  // 배열.Length → 3
PrintLength(123);                // int에는 Length 없음 → 런타임 예외

컴파일러 입장:

  • dynamic이면 일단 Length가 있다고 믿자…” → 컴파일 OK
  • 실행 중에 실제 타입을 보고 Length를 찾는다.
  • 있으면 호출, 없으면 예외.

6. dynamic의 단점 / 주의할 점

6-1. 타입 안전성이 떨어진다

dynamic x = "Hello";
int n = x;  // 컴파일 OK, 실행 시 예외 (string → int 변환 불가)
  • var나 일반 정적 타입이라면 컴파일 단계에서 잡아줄 수 있는 오류도,
  • dynamic실행해 봐야만 알 수 있다.
  • 프로젝트가 커지고 팀원이 늘어나면, 이런 런타임 에러는 디버깅이 더 까다로워질 수 있다.

6-2. 성능 오버헤드

  • 호출할 때마다 “어떤 멤버를 호출해야 할지”를 런타임에 찾아야 한다.
  • 즉, 내부적으로 항상 런타임 바인딩 작업이 수행된다.
  • 단순 정적 호출보다 느릴 수 있고, 특히 대량 루프 안에서 쓰면 부담이 될 수 있다.

7. 언제 쓰고, 언제 피해야 할까?

써도 괜찮은/유리한 경우

  • COM / Office 자동화 코드 간단히 정리하고 싶을 때
  • 동적 언어(파이썬, JS 등)에서 넘어오는 객체를 편하게 다루고 싶을 때
  • 빠르게 프로토타입을 만들고, 나중에 정적 타입으로 정리할 생각일 때
  • JSON을 잠깐 파싱해서 몇 개 필드만 임시로 보고 싶을 때

가능하면 피해야 하는 경우

  • 핵심 도메인 로직, 중요한 계산 로직
  • 팀원이 많은 프로젝트의 공용 라이브러리/핵심 인프라 코드
  • 성능이 중요한 반복 루프 내부

대부분의 일반적인 C# 개발에서는:

  • 정적 타입 + 제네릭으로 설계하는 것이 기본 원칙이고,
  • dynamic은 진짜 필요한 구석(interop, 스크립팅, 테스트용)에서
    “도구 상자에서 잠깐 꺼내 쓰는 공구” 정도로 쓰는 게 좋다.

0개의 댓글