프날 오토핫키 강좌
누르면 강좌 리스트가 나와요
프날 오토핫키 강좌

⚠ 이 강좌는 오토핫키 v1을 다룹니다

지금 보시는 강좌는 구버전 오토핫키(v1.1)를 다루고 있습니다. 따라서 본 강좌의 내용은 현재 최신 오토핫키 버전 (v2.0)과 호환되지 않습니다. 구버전의 정보가 필요한 것이 아니라면, 가능한 한 새로운 사이트에 작성한 v2 강좌(https://ahkv2.pnal.dev)를 봐주시길 바랍니다.

[프날 오토핫키] 코드 작성 요령 - 누구보다 예쁜 코드 작성하기

오토핫키 관련 강좌 활동을 해오면서, 그동안 많은 양의 질문을 받아보았습니다. 그러면서 수 많은 사람들이 작성한 서로 다른 스타일의 코드를 보았고, 보면서 감탄이 나오는 코드가 있는 반면에 읽지도 못할 정도로 난해한 코드도 보았습니다.

 

오토핫키는 스크립트 언어입니다. 프로그래밍 언어의 일종이라는 뜻입니다. 그렇기 때문에 좋은 코딩 습관이 중요합니다. 한 번 만들고 끝이 아닌, 어떠한 형태로든 유지보수를 해야하기 때문입니다. (디버깅, 기능 추가/수정 등..) 그렇기 때문에 언제 한 번 좋은 코딩 습관에 관해 글을 쓰고싶었습니다.

 

본 글은 지극히 주관적인 내용을 담고 있습니다. 코딩 스타일은 개개인의 특성이며, 누군가가 강제할 수 없기 때문입니다. 다만 권장되는 코딩 스타일은 있으며, 이 글은 그러한 "권장 코딩 스타일"에 제 주관적인 의견을 덧붙여서 쓰는 것임을 알려드립니다. 

 


1. 들여쓰기는 기본입니다.

들여쓰기는 꼭 하셔야합니다. 들여쓰기에 어려움을 겪으시는 분들이 아주 가끔 계신데, 그냥 "단락 구분"만 해주신다고 생각해주세요. 오토핫키는 단락 구분이 중괄호로 이루어지기 때문에, 중괄호가 들어가는 부분은 모두 들여쓰기를 해주시면 됩니다.

들여쓰기 유무에 따른 가독성 차이는 생각보다 심합니다. 중괄호로 Loop의 포함 관계를 나타냈기 때문에 Loop 안 쪽의 내용은 들여쓰기 한 번을 해주었고요, 조건문의 실행부인 break는 한 줄이기 때문에 중괄호를 생략했지만, 역시 if문에 포함되었다는 것을 보여주기 위해 들여쓰기를 해주었습니다.

 

들여쓰기에 관한 취향은 사람마다 다르지만, 보통 공백 4개로 통일되고 있습니다. Tab키를 누르면 되는 기본 들여쓰기 양식이 4 Space이기 때문입니다.


2. 띄어쓰기를 적극 활용하세요.

띄어쓰기 또한 가독성을 높이는 중요한 요소입니다. 연산자와 피연산자 사이는 (++나 --같은)단항 연산자를 제외하곤 모두 띄어주시는 것이 좋습니다.

var := "Space"
var:="Space"

if (var = "Space")
if (var="Space")

그러나, 단항 연산자는 붙여 주는 것이 깔끔합니다.

var++
var--

3. 변수 이름을 신중하게 지어주세요.

물론, 간단한 테스트용 프로그램을 만들 땐 중요하지 않습니다. 10~20줄 되는 프로그램 만드는데 몇 개 되지도 않는 변수명이 뭐가 중요하겠어요?

 

다만 "본격적으로" 내가 프로그래밍을 해보겠다 하면 변수 이름은 굉장히 중요해집니다. a, b, var와 같은 변수명은 긴 코드를 작성할 땐 분명 헷갈릴 수 있는 이름입니다. 변수명이 너무 많이 길지만 않는다면, 해당 변수의 "역할"에 맞게 이름을 지어주세요. (저 또한 잘 지켜지지 않는 부분입니다.)

 

변수 이름은 원래 영어로 짓는 것이 권장됩니다. 오토핫키에선 한글로 쓰셔도 잘 해석하기 때문에, 의미가 없는 영문자보다는 차라리 의미 있는 한글명으로 지어주세요. (다만 표준적으로 의미 있는 영문자가 제일 권장됩니다.)

Gui, Add, Edit, x10 y10 w100 h20 vStatus, 현재 상태
Gui, Add, Edit, x10 y10 w100 h20 vEdit, 현재 상태

→ 현재 프로그램 상태를 나타내기 위한 Edit를 생성할 때, 위의 형태가 아래의 형태보다 권장됩니다.

4. 연산자 우선 순위를 명확히 표기해주세요.

오토핫키를 포함한 프로그래밍 언어는 연산자의 우선 순위를 매깁니다. 여러 연산자가 한 줄에 있을 때, 어떤 연산자를 먼저 계산하냐에 관한 순위입니다. 그래서 아래와 같은 코드에서, "더하기"연산자가 "대입"연산자보다 우선적으로 계산되는 것입니다.

var := 2 + 3

그러나 이러한 연산 표시는 피연산자가 많아질수록 복잡해집니다. 이럴 땐 괄호를 이용하여 적절히 연산 순위를 "사람이 볼 수 있도록" 표시해주세요.

if (++a >= 3 && b < 2 + 3 * ++c)
if ((++a >= 3) && (b < (2 + (3 * ++c))

다만, 아래의 5번 항목 때문에 위 예제의 아래 표기도 좋은 표기는 아닙니다.


5. 줄 수를 아끼지 마세요.

코드 줄 수를 줄이는 일은 중요합니다. 그렇지만 그것을 가독성을 포기하면서까지 행하는 것은 멍청한 짓입니다.

4번 항목의 예제가 좋지 못한 예제인 이유가 그렇습니다. 무리하게 전위 표기식을 사용하면서 if문 안쪽을 복잡하게 만들었습니다. 차라리 줄 수를 늘리면서 가독성을 올리는 것이 더욱 생산적일 것 같습니다.

a++
c++
var := c * 3 + 2
if (a >= 3 && b < var)

6. goto를 쓰지 마세요.

오토핫키는 위에서 아래로 진행하는 것이 자연스러운 언어입니다. 부득이하게 다시 윗 줄로 올라가면, 올라가는 지점을 표시해주게 됩니다. (Loop나 While은 스크립트의 진행을 다시 위로 올려주는 역할을 하지만, 중괄호가 곧 반복 범위를 뜻하기 때문에 문제될 것이 없습니다.)

 

그런데 goto는 해당 레이블이 가리키는 서브루틴이 정말 "아무 곳에나" 있을 수 있습니다.

저 위에 있을수도, 밑에 있을수도 있습니다. 당연히 생산성이 크게 떨어지게 됩니다. 여러분이 책을 읽을 때, "22페이지로 가세요" "다시 15페이지로 가세요" "이번엔 73페이지로 가세요" 와 같은식으로 페이지 구성이 되어있다면 분명 읽기 힘들 것입니다. goto도 마찬가지입니다.

 

오토핫키는 구조적 프로그래밍이 권장됩니다. 한번 진입한 서브루틴은 반드시 그 서브루틴의 return을 만나야하며, goto는 이 행위를 방해하게 합니다.

 

위 코드는 그러한 예의 대표적 예시입니다. F1 서브루틴은 반드시 1이라고 표시해 둔 return을 만나야합니다. 그러나 goto에 의해 두 번째 return을 만났고, 이는 너무나도 부자연스러운 상황입니다. 또한 return을 만나는 것이 보장되지 않은 채로 다시 위쪽 서브루틴으로 가는 것은 더욱 위험한 상황이 되고, 이렇게 코드의 흐름이 꼬여서 정상적인 "위에서 아래로 내려가는 흐름"을 보여주지 않는 경우를 "스파게티 코드"라고합니다.

 

유지보수적인 측면에서 "스파게티 코드"는 피해야할 상황입니다. 만약 goto없이 짤 수 없는 코드처럼 보인다면, 그건 그냥 본인의 능력 부족입니다. 모든 오토핫키 코드는 goto 없이 짤 수 있습니다. 반복문과 break, continue개념만 알아도 대부분의 goto를 사용하는 상황은 없앨 수 있습니다.

 

실제로, 최근 나오는 언어 중에선 goto를 지원하지 않는 언어가 많습니다. 관련된 내용으로, 위키백과의 "구조적 프로그래밍" 문서가 도움이 될 것 같습니다.

 

구조적 프로그래밍 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기 구조적 프로그래밍(structured programming)은 구조화 프로그래밍으로도 불리며 프로그래밍 패러다임의 일종인 절차적 프로그래밍의 하위 개념으로 볼 수 있다. GOTO문을 없애거나 GOTO문에 대한 의존성을 줄여주는 것으로 가장 유명하다. 역사적으로 구조적 프로그램을 작성하기 위하여 몇가지 다른 구조화 기법과 방법론이 개발되어왔다. 가장 일반적인 3가지는 다음과 같다. 잭슨의 구조

ko.wikipedia.org


7. 함수화를 해주세요.

이 말은 곧 최대한 같은 기능을 하는 부분을 함수로 묶어서 "실행부"에서는 함수 호출을 위주로 소스코드 구현을 하라는 뜻입니다.

 

오토핫키를 처음 접하게 되면 한 서브루틴에 쭉 스크립트를 적어넣는 방식을 주로 사용하게됩니다. 물론 동작은 되겠지만, 스크립트를 수정하고자 하면 일일이 해당 부분을 찾아다녀야합니다.

 

그렇기 때문에, 기능에 따라 함수로 그 기능을 쪼갠 후, 해당 서브루틴에선 함수를 호출하는 방식을 채택해주세요. gdip을 이용할 때 여러분은 주로 이러한 방식을 채택하게 됩니다. (gdip 자체가 함수의 모음집, 라이브러리기 때문입니다.)

 

함수 디자인을 하실 때, 주의하실 점이 있습니다. 각각의 함수의 기능을 최대한 쪼개서, 하나의 함수가 하나의 기능만 하도록 만들어주세요. 예를 들어서, "캡처"를 하는 경우와 "캡처 후 저장"을 하는 두 가지 기능을 만들어야한다면,

Capture()
{
}

CaptureAndSave()
{
}

위와 같은 경우엔 두 함수의 대부분의 영역이 겹치기 때문에 좋지 못한 함수 디자인입니다.

Capture()
{
}

Save()
{
}

위와 같이 기능에 따라 구현해주셔서, Capture()함수에서 Save()함수의 호출 유무를 변경하는 식으로 구현하는 것이 적합합니다.

 

또한, 아예 다른 역할에 사용되는 기능이라면 Class개념을 사용하는 것도 좋은 방법입니다.

Class Capture
{
    Capture()
    ImageSave()
}

Class ActionSelect
{
    LoadImage()
    CreateNewImage()
    DeleteImage()
}

[사용]
Capture.Capture()
ActionSelect.DeleteImage()
등..

읽기 쉬운 코드가 좋은 코드입니다. 줄 수에 목숨을 걸지 말아주시기를 부탁하며

프로그램은 컴퓨터가 돌리지만 코드는 인간이 봅니다. 여러분이 보기에 편한 코드를 작성해주세요.

 

사실 저도 좋은 코딩 습관을 가지고 있는 것은 아니지만, 가지기 위해 노력중입니다.

혹시 관련된 내용 더 읽고 싶으시면 

좋은 코딩, 나쁜 코딩 (박진수, 한빛미디어)

유지보수하기 어렵게 코딩하는 방법 : 평생 개발자로 먹고 살 수 있다. (로에디 그린 (역: 우정은), 한빛 미디어)

이렇게 두 가지 서적을 읽어보시면 좋겠습니다. 특히 두 번째 책 같은 경우엔 0원으로 배포중인 e-book입니다. (링크)

해당 책에서는 제목대로 "유지보수하기 어렵게 코딩하는 방법"을 알려주며, 당연히 책에서 알려주는 것과 "반대로" 코딩을 하셔야합니다. 내용이 내용인지라 재밌게 봤었고, pdf 형식으로도 제공하니 휴대폰으로라도 쓱 봐주셨으면 좋겠습니다.