데이터로써의 코드 (또는 "클로저")

Groovy는 다른 대부분의 컴파일 언어와 달리 코드를 데이터처럼 다룰 수 있습니다. 즉, 코드 덩어리를 마치 정수나 문자열처럼 이리저리 전달할 수 있다는 뜻입니다. 다음 코드를 참고하세요:

square = { it * it }

"it * it"이라는 표현식을 감싸고 있는 괄호(curly braces)는 Groovy 컴파일러에게 괄호 안에 있는 표현식이 코드라는 사실을 알려줍니다. 소프트웨어 세상에서는 이를 클로저(closure)라고 부릅니다. 이 경우 "it"은 클로저에 주어진 값을 의미합니다. 이제 이 컴파일된 함수는 "square"라는 변수에 저장됩니다. 이제 아래와 같은 코드를 쓸 수 있습니다:

square(9)

그리고 결과는 81이 됩니다.

여기까지는 별로 흥미롭지 않습니다. 하지만 함수 "square"를 값으로써 여기저기 전달할 수 있다는 사실에 주목하세요. Groovy에는 클로저를 인자로 받는 몇몇 내장 함수가 존재합니다. 한가지 예제는 배열의 "collect" 메서드 입니다. 다음 코드를 시도해보세요:

[ 1, 2, 3, 4 ].collect(square)

위 표현식은 값이 1,2,3,4 인 배열을 만들고 "collect" 메서드를 호출하면서 우리가 위에서 정의한 클로저를 전달합니다. collect 메서드는 배열의 각 아이템을 순회하면서 클로저를 호출하면서 해당 아이템은 인자로 넘깁니다. 그리고 그 결과를 새로운 배열에 담습니다. 따라서 위 코드를 수행한 결과는 다음과 같습니다:

[ 1, 4, 9, 16 ]

클로저를 인자로 받는 더 많은 메서드들을 찾아보려면 Groovy GDK 문서 를 참고하세요.

기본적으로 클로저는 "it" 이라는 이름의 인자를 갖지만 여러 인자에 이름을 지정해서 클로저를 만들 수도 있습니다. 예를 들어 Map.each() 메서드는 두 개의 인자를 갖는 클로저를 인자로 받습니다:

printMapClosure = { key, value -> println key + "=" + value }
[ "yue" : "wu", "lane" : "burks", "sudha" : "saseethiaseeleethialeselan" ].each(printMapClosure)

위 코드의 결과는 다음과 같습니다:

yue=wu
lane=burks
sudha=saseethiaseeleethialeselan

더 많은 클로저 예제들

이번 섹션에서는 클로저 예제를 몇 가지 더 소개합니다. 아래에 나오는 예제는 두 가지를 보여줍니다. 첫째, 클로저는 클로저의 바깥쪽에 있는 변수에 접근할 수 있습니다. 즉 아래 클로저의 목적은 orderParts의 각 요소를 fullString 변수에 이어 붙이는 것입니다. 하지만 변수 fullString은 클로저 밖에 있지요. 두번째, 이 클로저는 이름이 없는 익명 클로저 입니다. 쓰이기 직전에 바로 정의되어서 한번만 쓰이고 있습니다:

fullString = ""
orderParts = ["BUY", 200, "Hot Dogs", "1"]
orderParts.each {
  fullString += it + " "
}

println fullString

위 예제의 결과가 무엇을 출력할지는 아마 짐작하실겁니다.

다음 예제는 또다른 익명 클로저를 보여줍니다. 이 예제는 Map에 저장된 값들을 모두 더하고 있습니다:

myMap = ["asdf": 1 , "qwer" : 2, "sdfg" : 10]

result = 0
myMap.keySet().each( { result+= myMap[it] } )
println result

파일 다루기

파일에서 데이터를 읽는 것은 상대적으로 쉽습니다. 우선 myfile.txt 라는 텍스트 파일을 생성합니다. 그 안에 뭐가 들어있는지는 중요하지 않습니다. 아무 글자나 넣은 다음에 C: 드라이브의 \temp 디랙토리에 저장하세요. 그리고 groovyConsole에 다음 코드를 입력하세요:

myFileDirectory = "C:\\temp\\"
myFileName = "myfile.txt"
myFile = new File(myFileDirectory + myFileName)

printFileLine = { println "File line: " + it }
 
myFile.eachLine( printFileLine )

이 예제는 파일의 각 줄을 출력하는데 줄 앞에 "File line: " 이라는 문자열을 붙여서 출력할 것입니다. 예제의 첫 두 줄에서는 파일의 위치를 담고 있는 변수를 선언하고 있습니다. 변수 이름 자체에는 어떠한 특별한 기능도 없습니다. 이 변수 두 개를 합쳐서 파일 위치를 만들어내고 있습니다. Groovy에서 역슬래시 문자는 특별한 의미를 갖기 때문에 역슬래시 자체를 표현하고 싶으면 역슬래시를 연속으로 두 개 써줘야 합니다.

"myFile = "로 시작하는 세번째 줄에서는 새 File 객체를 만들고 있습니다. 객체란 단순히 서로 관련된 메서드와 데이터의 묶음 입니다. 예를 들어 파일 객체는 파일의 위치(이 경우에는 "C:\temp\myfile.txt")를 알려주는 데이터와, 파일을 삭제하는 메서드 등을 가지고 있습니다. 위 예제에서 우리가 사용할 단 하나의 메서드는 파일의 각 줄에 대해 클로저를 수행해주는 eachLine 인데, 예제의 마지막 줄에서 사용하고 있습니다. 마지막 줄 직전에서는 클로저를 정의하고 있습니다. 여러번 보았기 때문에 이제 친숙할 것입니다.

문자열 다루기

Groovy의 문자열(String)은 자바 문자열과 동일한 기능을 수행합니다. 사실 Groovy 문자열은 자바 문자열 그 자체인데 몇 가지 기능을 추가로 제공하고 있습니다. 그렇기 떄문에 String으로 할 수 있는 흥미로운 일들을 살펴보기 위해 자바 문서 를 참고할 수 있습니다. 예를 들어 '''Method Summary''' 섹션에서 '''split''' 메서드를 찾을 수 있습니다. 이 메서드는 매우 유용한데, 정규식(regular expression)을 이용하여 문자열을 분리해주는 일을 합니다. 정규식에 대해서는 나중에 다룰 것입니다. 지금 당장 필요한 지식은, 가장 단순한 정규식은 단일 문자라는 점입니다. "2005-07-04" 라는 문자열을 나누고 2006년 7월 4일로 바꾸려면 아래와 같이 하면 됩니다:

stringDate = "2005-07-04"
dateArray = stringDate.split("-")
year = dateArray[0].toInteger()
year = year + 1
newDate = year + "-" + dateArray[1] + "-" + dateArray[2]

이 코드는 우리가 지금까지 배웠던 여러가지 기능들을 함께 쓰고 있습니다. 두 가지 새로운 기능은 String의 split 메서드와 toInteger() 메서드입니다. toInteger 메서드는 데이터를 문자열이 아닌 숫자로 다룰 수 있도록 바꿔줍니다. toInteger() 부분을 제거하면 어떤 일이 벌어지는지 살펴보면 이해하기 쉬울 것입니다.

또 한가지 주목할 점은, toInteger 메서드가 자바 String 클래스 문서에는 존재하지 않는다는 사실입니다. 그 이유는 toInteger가 Groovy에서 String에 추가한 부가 기능이기 때문입니다. 자바 객체에 대한 Groovy 확장 을 참고하시기 바랍니다.