안녕하세요.
사용자정의 함수(Function) 가 재계산되는 시기를 인터넷에서 찾아보면
파라미터 인수가 변동되었을 때라고 나옵니다. 인공지능도 동일한 설명을 합니다.
예를 들어 ....
Public Function Test(a As Integer, b As Integer) As Integer
Test = a + b
End Function와 같이 Test 라는 이름의 사용자정의함수가 있는데(이하 Test), 이를 셀에서 보면 아래와 같이 A1, B1 셀값이 변동되면 C1 셀도 재계산됩니다.

그런데 제가 아래의 실험을 해보았습니다.
새로 하나의 버튼을 만들겠습니다. 아래 [단추1] 과 같습니다.

위 버튼은 [ 1 과 100 을 더하여 그 값 101 을 A3셀에 입력해라 ] 입니다.
버튼을 누르면 101 이 A3셀에 입력되겠죠. 소스는 아래와 같이 간단합니다.
Sub Btn_Click()
Dim i As Integer
i = 1 + 100
Range("A3") = i
End Sub중요한 점은, 이 소스는 가장 위에 설명드린 사용자정의함수Test 와 무관합니다. 글쵸. 그래서 이 버튼을 눌러서 A3셀값이 입력되든 말든, Test 에 영향을 끼치지않겠죠. 그런데 제가 어떤 실험을 했냐면.... 현재 이 상태에서 ..... VBA창을 열어서
Public Function Test(a As Integer, b As Integer) As Integer
Test = a - b ' <--- 여기 고침. 예전에 a + b 였는데, 지금은 a - b 로 수정됨
End Function...와 같이 수정하고 VBA창을 닫았습니다. 이렇게 수정한 이유는 [ Test 의 파라미터가 아닌 곳의 값이 변해도 Test 가 재계산이 되는 경우도 있지 않을까? ] 그런 의문이 들었고 재계산이 발생한 것을 알기 위해 일부러 고쳐본 겁니다. 저렇게 수정하고 VBA창을 닫는다하여
Test = a - b 로 바로적용되어 A3 셀값이 바로 변하지는 않습니다. 아직 셀값이 변동안되어 예전정보를 그대로 가지고있겠죠. 셀값변동 사건(이벤트)이 발생되지 않은 상태니까요. 그런데 이제 실험을 해보겠습니다.
제가 실험한 내용 (아래) :
현재 이상태에서.....제가 저 버튼을 눌러봤습니다. 1 + 100 = 101 을 계산하여 A3셀에 입력시키는 버튼이요. 그리고 이 사건은 Test 와 전혀 무관하죠? 그런데도 A3 셀이 변하더라구요. 제가 아까 수정한 a - b 가 적용된 새값으로 입력되더라구요. 이는 Test의 재계산이 이루어졌다는 겁니다. Test 파라미터 인수를 건들지 않았는데도요.
결론 : 사용자정의 함수는 자기 파라미터 a, b 값의 변동이 설령 없다해도 재계산을 한다. 인터넷설명, 인공지능 설명이 틀렸다는 결론에 도달함.
저의 질문 : 서로 무관한데 왜 재계산 된건지 그 이유를 알고 싶습니다. 이건 Test 함수만의 문제가 아니라 제가 엑셀이 돌아가는 근본원리를 모르고 있다는 자괴감이 듭니다. 엑셀의 기본원리에 대해 좀 설명해주시면 감사드리겠습니다.
첨부파일 : 제가 실험한 Test.xlsm
제가 실험했던 현상 몇가지 더 :
1. 저렇게 소스 수정후, 마우스로 셀을 이리저리 클릭하면 Test재계산 안함. 마우스동작 이벤트로는 재계산 안한다는 결론. 기타 엑셀 메뉴를 열었다 닫았다 상관없는거 다 해봄. 역시 재계산 안함. 이건 예상이 되어 놀랍지 않음.
2. 임의의 셀을 선택했다 해제했다 막~~~ 수없이 해봄. 역시 재계산안함. 저는 재계산할 것으로 예상했는데 예상이 빗나감. 그래서 임의의 셀 선택후 색깔도 빠꾸고 Font Size 도 변경해봄. 역시 재계산 안함.
3. 임의의 셀 어디라도 선택후 키보드로 아무런 숫자 또는 문자 입력해봄. 근데 이건 Test재계산 함. 셀입력 이벤트로는 재계산 한다는 결론.
4. 임의의 셀 어디라도 선택후 키보드로 아무런 숫자 또는 문자 입력하..... ㄹ 려고 하다가.... Enter 말고 Esc 키를 누르고 그냥 나옴. Test재계산 안함.
5. 위와 같이 버튼(매크로 수행)으로는 Test재계산 함. 이미 위에서 설명드림.
6. 버튼을 한번 누르고 ( 101 입력됨), 소스 수정후, 또 한번 버튼 누름 (또 101 입력됨. 그러나 화면상에서는 동일한 숫자라 화면 움직임없음) 이 경우, Test재계산 함.
udf 의 치명적인 문제 중 하나가.. 셀 참조 추적이 안돼서 시트 변경 시 항상 트리거 된다는 겁니다.
그래서 파라미터 변경과 무관하게 시트 변경이 있으면 함수가 재계산되는 것이고, 채우기변경, 선택 변경은 트리거에 영향을 주지 않기에 선택해제, 폰트변경시에는 계산되지 않습니다.
안녕하세요. 답글에 감사드립니다. 네. 그렇지 않아도 시트명 변경같은 트리거 발생시 함수재계산을 실행한다는 것을 확인하였습니다. 참 엑셀은 알다가도 모를 별스런 프로그램이네요. 무엇을 트리거로 인식하고 무엇을 트리거로 인식하지 않는 지 그걸 이해하는 것도 코딩에 필요할 것 같습니다. 감사합니다.
안녕하세요~
"엑셀 계산 엔진의 원리"를
정확히 점검해 보는 수준 높은 실험하셨네요.
흔히 말하는 "UDF는 파라미터가 변할 때만 재계산된다"는 말은 절반만 맞는 말이고,
실제 엑셀의 내부 계산 엔진은 그보다 훨씬 복잡하다고 볼 수 있습니다.
결론부터 말씀드리면,
물론 다양한 견해와 관점이 존재하고,
아래 답변은 기술적 오류가 있을 수 있기 때문에
참고하는 수준에서 이해해 주세요.
1. VBA 코드(버튼 클릭)를 통해 셀 값이 변경되면,
엑셀은 “이 변경이 다른 수식에도 영향을 줄 수 있다”고 판단하여
해당 셀과 그 셀을 참조하는 셀들을 dependency tree(의존성 트리) 기준으로 Dirty로 표시하고,
필요한 UDF들을 재계산하게 됩니다.
즉,
"UDF는 파라미터가 변하지 않아도 재계산될 수 있다"가
엑셀 계산 엔진 내부동작과 관련되어서 공식문서에는 언급이 없지만,
실제로 특허문서나 리서치 논문 등을 통해서 보면
일부 상황에서는 재계산될 수 있다고 보는 것이 타당합니다.
https://learn.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
그렇다면,
엑셀은 어떤 상황일 때
해당 시트의 계산 dependency tree(의존성 트리)를 확인하고 재계산을 하게 될까요?
셀이 변경되었다고 엑셀이 판단할 때,
VBA 코드가 실행되어 "dirty" 플래그가 켜질 때,
다른 함수들이 영향받을 가능성이 있을 때,
코드가 실수로 또는 의도적으로 Calculate를 트리거할 때,
다시 재계산하게 됩니다.
여기서,
해당 시트의 계산 dependency tree(의존성 트리) 개념을
좀 이해하고 설명을 진행하도록 하겠습니다.
먼저 dependency(의존성) tree(트리)가 없다면,
시트의 모든 셀을 다 읽어들여야 하기 때문에
엑셀은 엄청 속도가 느려지게 됩니다.
여기서 엑셀은
"어떤 셀의 계산이 어떤 다른 셀에 의존하는지 나타내는 구조"를 만들어
이를 내부적으로 트리 형태로 저장해 두게 됩니다.
예를 들면,
이런 수식이 있다면,
이러한 형태로
dependency(의존성) tree(트리)를 구성합니다.
이때,
A1의 값이 바뀌면,
엑셀은 Dependency Tree를 참조하여
값이 변경된 셀과 "그 셀을 참조하는 셀들만" 재계산하게 됩니다.
이러한 구조가
엑셀의 빠른 계산 성능의 핵심이라고 볼 수 있습니다.
그래서
행,열을 추가하거나 삭제라든지
테이블을 확장거나
이름 정의를 변경한다든지
수식 구조 자체를 변경하거나
UDF 코드를 수정한다든지
시트 복사,이동,삭제 등을 하게되면,
이런 event는 Excel에게,
"트리를 다시 만들어!"
라는 신호가 됩니다.
2. 왜 버튼을 누르면 UDF Test()가 재계산이 될까요?
Sub Btn_Click() Range("A3") = 101 End SubVBA에서 셀 값을 변경하면 Excel은 "해당 Sheet는 Dirty 상태"라고 표시합니다.
즉,
엑셀은
"시트의 값이 변경되었으니, 이 변경이 다른 수식에도 영향이 있을 수 있다.
Dependency Tree(의존성 트리)를 다시 확인해야겠다.” 하고
자동으로,
이 시트에 존재하는 모든 수식의 dependency를 다시 검사하고
그 중에서 UDF(Test)는 "휘발성(volatile) 여부"를 재평가하게 됩니다.
이때, 파라미터가 안 바뀌었더라도
엑셀은 UDF는 일반 수식보다 판단 기준을 더 엄격하게 적용하는데,
그 이유는
SUM, IF 같은 내장 함수는 내부동작을 완벽히 알고 있지만,
UDF는 사용자가 마음대로 작성할 수 있으므로,
내부에서 어떤 셀을 참조할지도 모르고
파일 읽을 수도 있고
전역 변수를 읽을 수도 있고
Static 변수도 있고
심지어 랜덤 API나 시간도 가져올 수 있어서
평상시(Normal Runtime)에는
UDF도 의존성 트리에 등록해서 코드를 수정하지 않은 상태에서는
A3를 바꾸는 버튼을 눌러도 Test(A1, B1)은 재계산되지 않는 것이 정상으로
UDF도 Smart Recalc의 대상이지만,
특수 상황(Code Edit)에서는
실험처럼 VBA 코드를 수정하고 닫았을 때,
엑셀은 "코드가 바뀌었으니 기존 의존성 트리가 유효한지 알 수 없다"고 판단하여
모든 UDF를 일시적으로 Dirty(재계산 필요) 상태로 만듭니다.
좀 쉽게 정리하자면,
원칙적으로, 코드를 수정하지 않은 일반적인 상황에서는
Test(A1,B1)와 무관한 셀(A3 등)을 변경해도
그 셀이 의존성 트리 상에서 연결되어 있지 않다면
Test는 재계산 대상에 포함되지 않는 것이 정상입니다.
다만, 코드 수정, 전체 재계산(Full Recalc), 이름 정의 변경, 일부 Add-in 등
다른 요인이 개입되면 예외적으로 재계산이 발생할 수 있다는 의미입니다.
3. 그래서
"셀 클릭, 포맷 변경"은 무효지만 "키보드 입력"은 유효한 이유가
마우스로 클릭하고, 셀 색, 폰트를 변경하고, ESC로 입력 취소를 하는 것은 값을 바꾸는 이벤트가 없어
dependency tree에 변화가 없지만,
입력 후 Enter나
버튼을 클릭하여 매크로를 실행함으로, Range("A3") 값의 변경이 발생하거나
버튼을 클릭하여 값이 동일한 값으로 덮어쓰기하여도 "write" 자체가 변경 이벤트로 처리되어
dependency tree에 변화를 주는 이벤트가 발생하게 됩니다.
물론
UDF는 휘발성(volatile)이 아니어도 "반은 휘발성(volatile)"처럼 동작하도록 하는 이유는
설계 자체가 "안전성 확보" 목적이라 조금만 위험해도 다시 계산합니다.
4. 추가적으로,
인터넷이나 AI에서 거의 설명이 없을텐데,
엑셀이 언제 무엇을 다시 계산해야 하는지 판단하는 데 사용하는 핵심 내부 원리 중 하나가
바로 Dirty Flag(공식문서에는 Dirty Cell, Dirty Region, Dirty Chain 등)입니다.
엑셀은 모든 셀을 매번 다 재계산하면 너무 느리기 때문에
Dirty 플래그 기반의 "부분 재계산(Smart Recalc)" 알고리즘을 사용합니다.
그럼 반대는
물론 Clean Flag입니다.
"현재 최신 상태이며 다시 계산할 필요 없다"는 의미입니다.
엑셀이 Dirty Flag로 인식하는 이벤트는
- 사용자가 A1에 값을 입력했다.
- VBA에서 Range("A1") = 10 입력했다.
- 외부 링크로부터 값 업데이트되었다.
- 행,열이 추가 또는 삭제되었다.
- 이름 정의 변경이 있었다.
즉, 해당 영역의 모든 셀의 dependency가 Dirty 상태입니다.
- UDF 코드가 수정되었다.
- VBA 창에서 함수 한 줄이 수정이 되었다.
엑셀은 "이 UDF가 어떤 셀에 쓰였는지 모르기 때문에 모두 Dirty"라고 판단합니다.
- Volatile 함수(NOW(), RAND(), INDIRECT(), OFFSET(), INFO(), TODAY())가 포함되어 있다
이 셀과 모든 의존 셀도 Dirty라고 판단합니다.
- CalculateFullRebuild 같은 전체 재계산 계열은
내부적으로 모든 셀을 Dirty처럼 취급하고 의존성 트리도 다시 만드는 효과가 있습니다.
엑셀은
위와 같은 Dirty 상태가 발생하면
이 순서를 따라 계산하게 되므로
엑셀이 엄청 빠른 이유입니다.
따라서
버튼을 클릭해 아래 코드가 실행되면,
Range("A3") = i이 문장 하나 때문에 Excel 내부에서는 다음이 발생합니다.
"A3의 값이 변경되었다"
→ A3는 Dirty
A3과 dependency 관계에 있는 모든 셀(포함해서, 같은 시트의 수식들)들은
잠재적으로 Dirty 후보가 됩니다.
엑셀은 UDF를 더 보수적으로 판단하게 되고,
UDF가 있는 셀에 Dirty 플래그를 설정하게 됩니다.
따라서
재계산 시 해당 UDF가 다시 실행하게 되므로
파라미터 변화가 없어도 재계산이 발생한 것입니다.
즉,
아무리 인수값이 변하지 않아도
Dirty 플래그 때문에 계산할 수밖에 없습니다.
참고적으로
엑셀 내부 구현은 공식적으로 모두 공개되어 있지 않지만,
특허/리서치 문서와 엔진 동작을 종합해 보면,
셀마다 아래와 같은 "셀 메타데이터 구조"를 내부적으로 보유하는 것으로 추론하는데,
Dirty 플래그(공식적으로는 Dirty)는 이 중 하나로 비공개이며
Dirty 상태는 Excel Calculation Engine이 관리하기 때문에
VBA로 직접 읽을 수 없습니다.
좀 어려운 내용이지만
엑셀 계산 구조를 이해하시는 데 도움이 되셨기를 바랍니다.
선생님 안녕하세요. 또 뵙게 되어 반갑습니다. 너무나 상세한 설명에 머리숙여 감사드립니다.
저는 처음에 저게 아주 단순한 문제라고 생각했었습니다. 셀값 변동시 파라미터가 아니어도 재계산될 수도 있지 뭐~ 원래 엑셀은 똑똑한 짓을 스스로 알아서 잘 하잖아. 이렇게 대수롭지 않게 생각했었는데 실험을 해보면 해볼수록 미궁속으로 빠지더라구요. 매크로를 실행하니(A3셀에 숫자입력) 관련 파라미터가 아니어도 재계산될수도 있다고 결론낸 이후에, 이걸 증명해볼려고 다른 매크로를 하나 더 만들어 보았었습니다.
Sub Btn_Click2() MsgBox (“Test”) End Sub이렇게 하고 버튼을 눌러봤습니다. 저는 당연히 재계산될 줄 예상했었습니다. 어쨌든 매크로가 실행됐잖아요. 근데 재계산을 안하더라구요. 도대체 이건 내가 모르는 어떤 비밀이 있는 것 같다는 생각이 들었습니다. 엑셀이 트리거로 인식하는 것은 무엇이고 무시하고 건너뛰는 것은 무엇인가? 도대체 어떤 질서가 있는 것일까? 실험을 해보면 해볼수록 미궁속으로......
방금 선생님께서 제시하신 것도 해보았습니다.
제가 이 문제를 심각하게 고민하게된 이유는, 저는 그동안 사용자정의함수를 사용하여 최종결과값을 도출하고 있었는데 자꾸 이상한 낌새가 느껴졌기 때문입니다. 모든 계산을 수행한 이후 마지막으로 제가 원하는 값이 나오면 [ 설정값에 도달하였습니다. ] 이렇게 알려주게 해놓았었거든요. 그런데 무슨 이유에선지는 잘 모르겠지만 엑셀이 지 기분따라 바로 알려줄 때도 있고, 잠잠할 때도 있다는 느낌을 받았었기 때문입니다. 그래서 사용자정의함수 재계산 시기에 대해서 공부해보니 파라미터변동이 없어도 재계산 실행될 수도 있지 않을까 의심하다가, 올려드린 것처럼 예제파일을 만들어 보았습니다. 충격적이게도 파라미터와 무관한 셀값이 변동되어도 재계산을 하더라구요. 제가 그동안 엑셀이 값을 잘 도출해주고 있었다고 오해하고 있었는데 실상은 원인이 그게 아니더라구요. [ 셀값이 자꾸 변동되므로 --> 그것이 결국 이벤트 트리거가 되어 --> 엑셀이 설정값도달을 알려주고 ---> 엑셀이 잘 작동하고 있다 ] 라고 착각을 하고 있었다는 겁니다. 만약에 설정값에 도달해도 셀값변동 같은 트리거가 없다면(예: 설정값 도달여부를 변수로만 저장하고 있다거나 등등) 엑셀은 잠잠모드가 되어 이를 알려주지 않게 되잖아요.
dependency tree 는 말하자면 윈도우에서 색인목차를 만드는 것과 같군요. 윈도우 파일찾기를 하다보면 가끔 색인(Index)파일을 만드시겠습니까라고 물어보는 경우가 있잖아요. 그리고 컴퓨터를 빠르게 하려면 상식적으로 [ 이 기능을 꼭 꺼야한다 ] 라고 알려져 있죠. CPU성능을 저하시키는 윈도우 쓸데없는 짓 정도로 알려져 있죠. 파일찾기를 시도할 땐 성능발휘를 하겠지만요. 엑셀이 바로 이 dependency tree 라는 색인 인덱스를 만들어 관리하므로 어디에서 재계산해야하고 안하는 지를 판단하여 계산효율성을 높이는 도구로 활용하고 있었다는 비밀을 이제야 알았습니다. 참 엑셀이 대단한 프로그램이라는 것을 다시 또 실감합니다. 똑똑한 줄만 알았더니 참 별걸 다 신경쓰면서 만든 프로그램이군요.
스마트 알고리즘을 사용한다니... 엑셀이 신뢰도+효율성 2가지를 모두 잡기 위해 내부 계산모드에서는 참 부단한 노력을 하고 있었다는 거네요. 효율성을 높인다고 이벤트를 무시하면 업데이트가 제때 안되 신뢰도가 떨어지고, 그렇다고 신뢰도를 높인다고 매번 업데이트를 하면 엑셀은 느림보가 되어 효율이 안생기고요. 근데 선생님 설명을 들을때마다 감탄스러움을 자주 느끼는데 도대체 이런 원리는 어떻게 알고 계시나요? 기술문서를 보시나요? 정말 엑셀개발팀에 계시는 분이신지요? 마징가제트를 만든 김박사가 아니면 도저히 모르는 마징가제트 비밀을 알고 계시는 분 같아요.
사용자장의 함수 실행시 이제야 의문이 풀리는 점은 제가 VBA창을 닫을 때 수정된 코드가 이 행위를 Dirty 로 인식하게 한 요인이 되었다는 점인데요. 그래서 엑셀은 dependency tree 를 재구성하느라 제가 셀값을 변동시키는 것을 트리거로 인식하여 함수재계산을 실행했다는 것으로 이해하였습니다. 엑셀은 용어가 참 고상한데 Dirty 라는 용어는 첨 봅니다. 메쏘드, 파라미터, 이벤트핸들러 등등 듣기만 해도 테크놀로지야~ 라는 감탄사가 절로 흘러나오는 용어랑은 거리가 머네요. 어정쩡하게 변동된 값들은 빨리 청소시켜버리라고 저렇게 이름지었을까요? 엑셀처럼 똑똑한 프로그램을 만드는 개발팀들조차도 어정쩡하게 변동된 지저분한 값들을 어떻게 처리해야할까 고민고민하다가 햐~ 이런것까지 우리가 감안하며 엑셀을 개발해야 하나... 개발팀들의 빡침이 느껴지는 용어네요. 토요일인데도 이렇게 빠르고 상세한 답변해주셔서 감사드립니다. 선생님 덕분에 뇌가 칠성사이다를 마신것처럼 개운해지는 느낌을 받습니다. 감사합니다. 즐거운 주말밤 되세요.