본문 바로가기
#4 VBA/#4.2 코딩문법

Variant형의 특징과 주의점

by 이세계의엑셀 2025. 8. 8.
반응형

엑셀 VBA에서 Variant 형은 “무엇이든 담을 수 있는 그릇”이다. 이 글에서는 Variant형의 동작 원리, 메모리 구조, 속도·안정성에 미치는 영향, 주의해야 할 함정을 실무 위주로 정리하여, 잘못 사용했을 때 발생할 수 있는 오류와 성능 저하를 예방하고자 한다.

1. Variant형이란 무엇인가?

Variant는 VBA의 기본(암시적) 데이터형으로, 숫자·문자·Object·배열·Null·Error까지 모두 보관할 수 있다. 내부적으로는 16바이트 Fixed Header + 가변 데이터 구조로 동작하며, SubType(Tag) 필드를 통해 현재 담긴 실제 형식을 결정한다.

구성 요소 크기(바이트) 설명
Type Descriptor 2 현재 저장된 서브타입 코드(예: 2 = Integer, 8 = String)
Flag 2 ByRef 여부, Array 여부 등 메타 정보
Data(포인터 또는 값) 8 정수·Long·Double 등은 값 그대로, String·Object·배열은 포인터
Reserved 4 런타임용 예약 영역

Tip Variant 배열(Dim v())은 Variant Header가 다시 요소마다 중첩되므로, 고정형 배열보다 메모리 부담이 크다.

2. 장점

  1. 유연성 : 데이터형을 미리 모를 때(예: 유저 입력, ADO 레코드셋) 즉시 저장·처리 가능하다.
  2. 쉬운 문자열 결합 : 숫자 + 문자 등을 & 연산으로 간단히 이어 붙일 수 있다.
  3. Null·Empty·Error 상태 전달 : 데이터베이스 Null, 미입력(Empty), 오류(Error)를 그대로 보존해 로직에서 분기 처리할 수 있다.
반응형

3. 주의점 및 성능 이슈

3-1) 암시적 형 변환 오버헤드

Variant는 연산 시마다 타입 해석 → 필요 시 변환 → 계산 → 재포장 과정을 거친다. 반복 루프 내부에서 숫자 덧셈처럼 자주 호출되는 부분에 Variant를 쓰면, 정수형(Long) 대비 3 ~ 15배까지 느려진다.

'느린 예시
Dim v1 As Variant, v2 As Variant, i As Long
v1 = 1: v2 = 2
For i = 1 To 1E6
    v1 = v1 + v2   '타입 해석 반복
Next i

3-2) 메모리 사용량 증가

정수 한 개(Long)는 4바이트지만, Variant는 최소 16바이트 헤더 + 4바이트 값 = 20바이트를 차지한다. 대용량 배열·딕셔너리에서 Variant를 쓰면 예상보다 메모리가 급증한다.

3-3) Null, Empty, ""(빈 문자열) 혼동

상태 판별 함수 의미 테스트 예시
Empty IsEmpty 초기화되지 않음 Dim v: IsEmpty(v)=True
Null IsNull “값 없음” (DB Null) v = Null
"" Len(v)=0 길이 0 문자열 v = ""

Null을 산술 연산에 사용하면 결과가 Null이 되므로, 합계 계산 시 Nz 함수 또는 Null 검사가 필수이다.

3-4) Option Explicit 의무화

변수를 선언하지 않으면 VBA는 Variant로 간주한다. Option Explicit를 통해 암시적 Variant 사용을 막아야 오타·런타임 오류를 줄일 수 있다.

3-5) Variant + Date 서브타입 주의

날짜 계산에서 서브타입이 Date인지 Double인지 헷갈리면, 국가 설정(로캘) 교차 시 서식 오류가 발생한다. CDate, CLng, Format으로 명시 변환 후 저장·출력한다.

4. 안전하게 사용하는 6가지 실전 팁

  • 초기값을 명시 : Dim v As Variant: v = Empty 처럼 초기 상태를 결정한다.
  • 계산용 변수는 고정형 : 합계·카운트는 Long, Double을 사용 후 결과만 Variant에 저장한다.
  • 배열에는 고정형 요소 지정 : Dim arr() As Double 로 선언해 변환비용을 없애고, 필요 시 Variant 배열로 ReDim Preserve 하지 않는다.
  • IsEmpty / IsNull / VarType 로 검사 : 상태별로 다른 함수를 사용해 의도치 않은 분기 오류를 막는다.
  • ByRef 매개변수는 형 따로 선언 : 프로시저 인수는 ByVal v As Variant 로 받더라도 내부에서 Dim d As Double: d = CDbl(v) 로 고정.
  • Debug.Print VarType(v) : 디버깅 단계에서 서브타입(예: 8=String, 7=Date)을 출력해 실시간 확인한다.

5. 실무 예제: 폼 입력값 자동 형 변환

사용자가 폼에 입력한 데이터를 검증 후 적절한 형으로 저장하는 예시이다.

Function NormalizeValue(raw As Variant) As Variant
    If IsNull(raw) Or IsEmpty(raw) Then
        NormalizeValue = Null           'DB Null 처리
    ElseIf IsNumeric(raw) Then
        NormalizeValue = CDbl(raw)      '숫자는 Double
    ElseIf IsDate(raw) Then
        NormalizeValue = CDate(raw)     '날짜는 Date
    Else
        NormalizeValue = CStr(raw)      '기타는 문자열
    End If
End Function

이처럼 초기에 서브타입을 고정해 두면, 이후 쿼리·계산 시 조건 분기가 단순화된다.

FAQ

Q. Variant를 완전히 쓰지 않을 수 있나요?

A. 레거시 코드·폼·데이터베이스 Null 처리에는 Variant가 편리하다. 다만 루프·대용량 배열·수치 연산에서는 반드시 고정형으로 캐스팅해 성능을 확보한다.

Q. Variant 메모리 관리가 어려운데, 종료 후 자동 해제되나요?

A. 프로시저 종료 시 Variant Header는 스택에서 사라지지만, 내부 포인터(String, Object, 배열)는 참조 카운트 0이 되어야 해제된다. Set Nothing, Erase 등을 적시에 호출하라.

Q. Variant 배열에서 Null 과 Empty 를 구분하려면?

A. 루프 돌면서 If IsEmpty(arr(i)) Then … ElseIf IsNull(arr(i)) Then … 형태로 별도 분기해야 한다. WorksheetFunction.CountA는 Null을 무시하지 않으므로 주의한다.

반응형