프날 오토핫키 강좌  v2

⚠ 이 강좌는 오토핫키 v2를 다룹니다

지금 보시는 강좌는 과거 오랜 시간동안 알려진 오토핫키(v1.1)의 차세대 버전인 오토핫키 v2를 다루고 있습니다.
만약 구버전인 '오토핫키 v1.1'의 강좌를 찾으신다면 프날 오토핫키 강좌(https://pnal.kr)를 봐주시면 되지만, 새로 오토핫키를 배우신다면 v2 버전을 배우시는 것을 강력히 추천드립니다.

24. 변수의 유효 범위 (상)


이제 다소 이론적인 부분으로 돌아와봅시다. 핫키를 배우면서 우리는 블록(Block) 개념을 접하게 되었는데, 이로 인한 혼동을 방지하기 위해서는 스크립트의 구조를 조금 더 구체화시켜 이해할 필요가 있습니다.

블록과 지역

블록은 스크립트에서 중괄호({})로 둘러싸인 부분을 의미합니다. 이는 주로 앞에 있는 구문의 실행 범위를 지정해주는데 사용됩니다.

예를 들어서 아래와 같은 핫키 구문에서도 블록(중괄호)을 이용해서 '핫키의 실행 범위'를 지정해주었죠.

1F1::
2{
3 ;F1을 눌렀을 때 실행되는 내용
4}

이렇게 앞으로 우리는 블록을 이용해서 각 코드의 실행 범위를 지정해주곤 할 것입니다.

지역은 함수나 핫키의 내부 전체를 의미합니다. 블록 안에 블록이 더 있더라도, 가장 바깥의 함수 혹은 핫키의 본문 전체가 지역이 됩니다.

1F1::
2{
3 ;F1을 눌렀을 때 실행되는 내용
4 (어떤 내부 블록의 시작 1)
5 {
6 }
7 (어떤 내부 블록의 시작 2)
8 {
9 }
10}
11
12F2::
13{
14 ;F2를 눌렀을 때 실행되는 내용
15}

위와 예시에서 지역은 'F1에 의한 지역'과 'F2에 의한 지역'이 있네요. 편하게 'F1 지역', 'F2 지역'이라고 부르겠습니다. 그리고 F1 지역 안에 있는 두 개의 블록의 안쪽은 또다른 지역이 아닙니다. 위 코드 조각에는 총 두 개의 '지역'이 있는 셈이죠. 각각의 지역이 구분되시나요?

지역이란 함수나 핫키의 내부 전체를 의미한다고 했습니다. 사실 함수도 직접 만들면 지역이 생기거든요. 그런데 우리는 오토핫키에 기본으로 있는 함수만 사용하고 있습니다. 따라서 우리는 지금까지 함수에 의한 지역은 써본 적이 없으며, 핫키에 의한 지역만 써본 셈입니다.

자동 실행 스레드

또, 자동 실행 스레드(Auto-execute Thread)라는 개념을 알아야합니다. 이는 프로그램을 실행하면 자동으로 진행되는 부분을 의미합니다. 스크립트를 실행하면 어느 지역에도 포함되지 않는 부분을 자동으로 실행하는데, 이 부분을 자동 실행 스레드라고 부릅니다.

아래와 같은 코드는 표시한 부분이 자동 실행 스레드 영역입니다.

1;자동 실행 스레드 영역
2F1::
3{
5}
6;자동 실행 스레드 영역

위의 코드에서는 자동 실행 스레드 영역이 두 군데로 나뉘어져있습니다. 핫키 구문의 위, 그리고 아래이죠. 프로그램을 실행하면 자동으로 이 영역에 있는 구문이 위에서부터 순차적으로 실행됩니다. 중간에 핫키처럼 어떤 지역이 있다고 해도, 자동 실행 스레드 전체가 우선 실행됩니다.

따라서, 우리가 핫키 구문을 배우기 전에 했던 실습은 모두 자동 실행 스레드에서 한 셈입니다. 지역 안에 코드를 적지 않고, 어느 지역에도 포함되지 않은 부분에서 스크립트를 작성했으니까요.

Tip: 스크립트의 자동 종료

핫키 지역이 없다면, 스크립트는 자동 실행 스레드의 모든 구문을 실행했을 때 자동으로 종료됩니다. 핫키를 사용하면 스크립트가 자동으로 종료되지 않아 ExitApp을 사용했던 이유가 그 때문입니다.

변수의 유효 범위

이제 지겹고도 어려운 이야기를 안할 수 없겠군요. 대부분의 현대 프로그래밍 언어에서는 구조적인 프로그래밍을 위해 변수의 생존 및 사용 범위를 제한해둡니다.

"변수가 유효하다"라는 것은, 해당 변수가 값을 가져 사용할 수 있는 상태를 의미합니다. 아래와 같은 코드에서, 변수 var1은 2번 줄에서 유효한 상태이기 때문에 사용할 수 있는 것입니다.

1var1 := 1
2MsgBox(var1)

오토핫키의 변수는 그 유효 범위에 따라 아래와 같이 세 종류로 구분됩니다.

종류사용 가능 범위생존 기한
지역(Local) 변수해당 지역 내해당 지역이 끝나면 소멸
전역(Global) 변수스크립트 전체프로그램이 종료될 때까지 소멸 안함
정적(Static) 변수해당 지역 내프로그램이 종료될 때까지 소멸 안함

1. 지역 변수

어떠한 함수 내에서 만들어진 모든 변수는 지역 변수입니다. 지역 변수는 해당 지역 내에서만 사용이 가능합니다. 즉, 여러분이 핫키 구문 내에서 만든 변수는 다른 함수에서 사용할 수 없습니다.

말이 어렵나요? 쉽게 예를 들면, 아래 스크립트를 실행하면 경고를 제시합니다.

1F1::
2{
3 var := 100
4}
5
6F2::
7{
8 MsgBox(var)
9}
경고가 발생하는 스크립트 이 변수는 아무 값이 대입되지 않았습니다. 라고 영어로 작성된 알림 상자의 모습 사진 1. 실행 결과

경고의 내용은 F2 핫키 구문 내에서 var 변수의 값이 할당되지 않았다는 것입니다. 즉, F2 지역 내에서는 F1 지역에서 만든 var 변수를 인식하지 못하는 것을 알 수 있습니다. 왜냐하면 지역 변수는 그 지역 안에서만 유효하기 때문입니다!

또한, 지역 변수는 지역이 끝나면 소멸됩니다.

2. 전역 변수

어떠한 지역 내에서 만들어지지 않은 변수, 즉 자동 실행 스레드에서 만들어진 변수는 모두 전역 변수입니다. 전역 변수는 어디에서나 접근할 수 있으며, 프로그램이 종료되기 전엔 사라지지 않습니다.

전역 변수를 읽기만 하는 경우엔 지역 내에서도 조건 없이 읽을 수 있습니다. 예를 들어서, 아래 예제는 전역 변수의 값을 읽는 상황을 잘 드러냅니다.

1var := 100
2
3F1::
4{
5 MsgBox(var)
6}
7
8F2::
9{
10 MsgBox(var)
11}
전역 변수의 사용

코드 조각을 실행해보면 각각의 핫키 지역 안에서 var 변수가 잘 표현되는 것을 볼 수 있습니다. 어느 핫키를 눌러도 전역 변수를 읽을 수 있죠.

global 키워드

전역 변수를 지역에서 읽기만 하지 않고, 전역 변수의 값을 지역에서 수정하거나 연산을 하려면 "이 지역에서 사용하려는 변수가 전역 변수다" 라고 명시해주어야합니다.

간단히 해당 변수 앞에 global 키워드를 붙여주면 됩니다. 아래 예시는 지역 내에서 global 키워드를 사용하여 그 지역에서 var는 전역 변수로 사용한다고 지정해주었습니다.

1var := 100
2
3F1::
4{
5 global var ;이 지역에서 var는 전역 변수로 사용한다.
6 var := 200 ;전역 변수 var의 값을 수정
7 MsgBox(var)
8}
9
10F2::
11{
12 MsgBox(var) ;읽기만 하기 때문에 global 구문이 없어도 값이 표시됨
13 ExitApp
14}
var 변수는 전역 변수입니다.

3번 줄에 global 키워드가 보이나요? 이와 같이 사용하면 해당 지역에서 var 변수의 값을 수정할 수 있기 때문에 12번 줄에서 바뀐 전역 변수의 값을 확인할 수 있습니다.

그리고 아래와 같이 global 지정과 값 대입을 동시에 할 수 있습니다.

5global var := 200
예제 3의 5-6번줄을 위와 같이 한 줄로 쓸 수 있습니다.

Tip: 전역 변수가 없을 때 global을 쓰면

자동 실행 스레드에 전역 변수를 만들지 않았어도, global 키워드를 사용하면 자동으로 그 이름의 전역 변수를 만듭니다. 실제로 위의 '예제 3'에서 1번 줄이 없다고 해도, F1 블록의 global 키워드를 만나면 자동으로 var 전역 변수를 만들기 때문에 F1을 누른 후 F2를 누르면 값을 잘 보여줍니다.

그러나, 이런 경우 var 변수가 만들어지기 전(=F1을 누르기 전)에 F2를 누르면 값을 지정해주지 않은 var 변수를 이용했다며 오류가 날 것입니다. 때에 따라 다르지만, 전역 변수로 쓸 변수는 자동 실행 스레드에 모두 적어주시는 편을 추천드립니다.

1var := 0 ;숫자가 들어갈 전역 변수의 생성
2var := "" ;문자열이 들어갈 전역 변수의 생성

지역 변수와 전역 변수의 우선 순위

지역 변수와 전역 변수가 같이 써져 있으면 어떻게 될까요? 예를 들어 아래와 같은 경우 말입니다.

1var := 100
2
3F1::
4{
5 var := 200
6 MsgBox(var)
7}
6번 줄의 var 변수는 100? 200?

global 키워드를 쓰지 않았으므로, 5번 줄의 var는 1번 줄의 var와 다른 새 지역 변수입니다. 따라서 6번 줄에서 사용하는 var 변수는 5번 줄에서 대입한 지역 변수입니다. 따라서 1번 줄에서 대입한 전역 변수의 값은 전혀 바뀌지 않았고, 스크립트는 200을 출력합니다.

다만, 아래와 같이 동명의 전역 변수가 있는 상황에서 지역 변수가 쓰임에도 불구하고 해당 변수를 대입하기 전에 사용하면, 오토핫키는 이를 지역 변수로 해석하여 '할당된 값이 없다'며 경고를 나타냅니다.

1var := 100
2
3F1::
4{
5 MsgBox(var) <<< 경고 발생!
6 var := 200
7 MsgBox(var)
8}
5번 줄의 var 변수는 지역 변수(빈 값)로 취급됩니다.

해당 지역 내에서 var라는 지역 변수가 존재하는데(6번 줄), 이 변수가 값을 가지기 전에 사용(5번 줄)해버리면 스크립트가 전역 변수(100) 대신 지역 변수(빈 값)로 해석합니다.

따라서 지역 변수와 전역 변수의 이름은 같을 수 있지만, 그 경우 같은 함수 내에서 전역 변수로서 해당 변수를 사용할 순 없습니다. 우리는 이러한 오류를 줄이기 위해 아래와 같은 스크립트 작성 원칙을 정해보겠습니다.

[전역 변수와 이름이 같은 지역 변수가 있다면...]
1. 그 변수명은 지역 변수로만 사용하거나,
2. global 키워드를 이용하여 전역 변수로만 사용한다.

⚠ 모호한 스크립트 진행

동명의 지역 변수와 전역 변수가 있다면, 해당 스크립트 내에 지역 변수에 값이 할당되기 전엔 당연히 전역 변수로 해석하도록 하는 것이 일반적입니다. 타 언어에서는 주로 위 예제와 같은 경우엔 5번 줄에서 '100'을 출력하며, 7번 줄에서는 '200'을 출력합니다. 5번 줄의 var를 전역 변수로 해석하기 때문입니다.

그래서 개인적으로는 이 부분이 '모호하다'고 생각합니다. 저 말고 많은 분들이 그렇게 생각할 것입니다. 더 아래에 있는 지역 변수의 여부에 따라 전역 변수를 읽지 않는 것은 직관적이지 않기 때문입니다.

업데이트를 통해 이 부분이 개선되길 바라며, 만약 개선된다면 강좌를 수정하도록 하겠습니다.

분량이 길어지기에 정적 변수는 다음 장에서 배우겠습니다. 더불어 어떤 변수를 어떻게 써야하는지 이야기해드리도록 하겠습니다.

질문하러 가기