U-그루비

발행: (2026년 1월 2일 오전 12:58 GMT+9)
11 min read
원문: Dev.to

Source: Dev.to

죄송하지만, 외부 URL에 직접 접근하여 해당 글의 내용을 가져올 수 없습니다. 번역이 필요한 텍스트를 여기 채팅에 복사해 주시면, 요청하신 대로 한국어로 번역해 드리겠습니다.

개요

저는 Jenkins 파이프라인에서 Groovy를 오랫동안 사용해 왔습니다.
이제 Java 분야로 더 많이 이동하고 있어서, 이 언어가 할 수 있는 일에 대한 짧은 요약을 할 좋은 시점입니다.

제 생각에 Groovy는 U‑language입니다: unknown(알려지지 않음), underrated(과소평가됨), unnoticed(눈에 띄지 않음). 많은 사람들이 들어본 적은 있지만, 실제로 얼마나 편리하고 범용적인지 아는 사람은 적습니다.

일반적으로 Groovy는 Java와 비슷합니다 – JVM 위에서 실행되고 순수 Java 코드를 이해하므로 Java 개발자가 즉시 사용할 수 있습니다.
Java와 달리 Groovy는 거의 모든 프로그래밍 패러다임에서 사용할 수 있어 코드베이스를 매우 유연하게 구조화할 수 있습니다. 아래는 이러한 유연성을 보여주는 몇 가지 간단한 예시입니다.

A. 명령형 프로그래밍

명령형 프로그래밍은 프로그램이 어떻게 단계별로 수행되는지를 설명합니다. 이는 컴퓨터 프로그램을 작성하는 가장 일반적인 방식입니다.

1. 스크립팅 언어

Groovy는 Bash와 마찬가지로 일반 스크립팅 언어로 사용할 수 있습니다:

println "this is just plain scripting"
a = 1
b = 2
println a + b

2. 동적 타입을 활용한 객체지향 프로그래밍

동적 타이핑을 사용하면서 객체지향 스타일로 작업할 수 있습니다:

class Car {
    def regNumber = "123AB"
    def numberOfStarts = 0
    def weight = 2

    def start() {
        numberOfStarts++
        println "running"
    }

    def stop() {
        println "stopping"
    }

    def getRegNumber() {
        return regNumber
    }

    // Example of a dynamic getter (not required for the demo)
    def getNumberOfStarts() {
        return numberOfStarts
    }
}

def car = new Car()
car.start()
car.start()
assert car.getNumberOfStarts() == 2
println car.getRegNumber()

car = "this is car"
println car

정적 타이핑이 과도하게 느껴질 때는 코드를 단순화하고 정적 타이핑을 피할 수 있습니다.
동적 타이핑은 같은 변수(def car)에 서로 다른 타입의 값을 할당할 수 있음을 의미합니다. 메서드는 런타임 상황에 따라 서로 다른 타입을 반환할 수 있습니다.

3. 정적 타입을 활용한 객체지향 프로그래밍

Java와 더 비슷한 스타일을 선호한다면 정적 타입을 사용할 수 있습니다(세미콜론이 없다는 점에 주의하세요):

class Plane {
    String regNumber = "123AB"
    int weight = 20
    int numberOfFlights

    void fly() {
        numberOfFlights++
        println "flying"
    }

    void land() {
        println "landing"
    }

    String getRegNumber() {
        return regNumber
    }

    int getNumberOfFlights() {
        return numberOfFlights
    }
}

Plane plane = new Plane()
plane.fly()
plane.fly()
assert plane.getNumberOfFlights() == 2
println plane.getRegNumber()

B. 선언형 프로그래밍

선언형 프로그래밍은 무엇 결과가 되어야 하는지를 설명하고, 어떻게 달성할지는 설명하지 않습니다.

1. Groovy‑Based 선언형 구문

Groovy에서 사용자 정의 선언형 DSL(Domain‑Specific Languages)을 만들 수 있습니다:

println "declarative programming"

def surroundedWithDashes(closure) {
    println "-------------"
    closure.call()
    println "-------------"
}

def show(closure) {
    println closure.call()
}

def sumOfNumbers = { a, b -> a + b }

surroundedWithDashes {
    show { sumOfNumbers(3, 6) }
    show { sumOfNumbers(1, 2) }
    show { sumOfNumbers(5, 10) }
}

DSL이 정의된 후 surroundedWithDashes { … }를 호출하면 원하는 레이아웃이 자동으로 생성됩니다.

2. 함수형 프로그래밍

Groovy는 순수 함수, 함수 체이닝, 메모이제이션, 커링과 같은 함수형 프로그래밍 개념을 지원합니다. 대부분의 작업은 collect, findAll, inject 세 가지 메서드만으로 표현할 수 있습니다.

println "functional programming"

def data = [1, 5, 8, 3, 5]
def result = data
        .collect { it + 10 }          // add 10 to each element
        .findAll { it > 13 }          // keep only values > 13
        .toUnique()                   // remove duplicates

assert result == [15, 18]

이점은 체이닝뿐만 아니라 다음과 같은 함수형 기능에서도 얻을 수 있습니다:

  • Memoization(메모이제이션) – 주어진 입력에 대한 함수 결과를 캐시합니다.
  • Currying(커링) – 일반 함수의 일부 인자를 고정하여 새로운 함수를 생성합니다.

Source:

C. 메타프로그래밍

메타프로그래밍은 아마도 Groovy의 특징이라고 할 수 있습니다. 컴파일 시점이나 런타임에 코드를 수정하거나 생성할 수 있게 해줍니다.

1. 컴파일‑시점 메타프로그래밍

AST(추상 구문 트리) 변환을 사용하여 컴파일 중에 코드를 생성할 수 있습니다:

println "compile-time metaprogramming"

@Log
class Car2 {
    def regNumber
    def numberOfStarts = 0
    def weight = 2

    @NullCheck
    Car2(regNumber) {
        this.regNumber = regNumber
    }

    def start() {
        numberOfStarts++
        log.info "log: running"
    }

    def stop() {
        log.info "log: stopping"
    }

    String getRegNumber() {
        return regNumber
    }
}

try {
    new Car2().start()
} catch (error) {
    println "error was caught: ${error}"
}

new Car2("12345").start()

@Log는 로거를 주입하고, @NullCheck는 생성자에 널‑체크를 추가합니다. 소스 코드에 명시적인 널‑체크 코드를 작성할 필요가 없습니다.

2. 런타임 메타프로그래밍

런타임에 메서드와 프로퍼티를 가로채거나 주입, 심지어 합성까지 할 수 있습니다. 이 기능 덕분에 Groovy는 DSL을 만들거나, 테스트에서 객체를 모킹하거나, 기존 API를 즉석에서 적응시키는 데 매우 유연합니다.

예시

@Log
class Car3 {

    def regNumber
    def numberOfStarts = 0
    def weight = 2

    @NullCheck
    Car3(regNumber) {
        this.regNumber = regNumber
    }

    def start() {
        numberOfStarts++
        log.info "log: running"
    }

    def stop() {
        log.info "log: stopping"
    }

    def getRegNumber() {
        return regNumber
    }

    def methodMissing(String name, args) {
        log.info "log: called missing method: ${name} with arguments: ${args}"
    }

}

def car3 = new Car3("12345")
car3.someUnexistingMethod("with parameter")
car3.metaClass.additionalMethod = { param ->
    log.info "log: called added method: additionalMethod with parameter: ${param}"
}
car3.additionalMethod("paramValue")

내장 메서드 methodMissing을 구현하면 클래스에 정의되지 않은 메서드가 호출될 때마다 이 메서드가 실행됩니다.
additionalMethod는 런타임에 클래스에 추가될 수 있으며 정상적으로 사용할 수 있습니다.

이 기능을 사용하는 이유

다음과 같은 경우에 매우 유용합니다:

  • 테스트 – 특히 모킹이 필요할 때.
    관련 기사 보기: Mocking with Groovy

  • 고급 작업 – 예를 들어 테스트 프레임워크를 작성할 때.
    예시: GitHub에 있는 간소화된 Jenkins 테스트 프레임워크 – Jenkinson

Take‑away

  • Groovy는 JVM 위에서 실행되며 순수 Java 코드를 이해합니다.
  • 명령형, 선언형, 함수형, 메타프로그래밍 스타일을 지원합니다.
  • 동적 또는 정적 타입(또는 두 가지를 혼합) 을 선택할 수 있습니다.
  • DSL 구축AST 변환 기능을 통해 도메인에 맞게 언어를 맞춤화할 수 있습니다.

Jenkins 파이프라인용 빠른 스크립트를 작성하든, 대규모 타입‑안전 애플리케이션을 구축하든, Groovy는 많은 Java 개발자들이 간과하는 다재다능한 도구 상자를 제공합니다. 즐거운 코딩 되세요!

요약

이것은 Groovy 코드를 작성할 수 있는 다양한 방법 중 간단한 예시들입니다. 저는 표면적인 부분만 다루었지만, 예시들이 언어에 대한 호기심과 흥미를 불러일으키길 바랍니다.

저 역시 Groovy를 계속 배우고 있지만, 여기서 보여준 모든 기능을 어느 정도 사용해 보았습니다. 포괄적인 문서는 다음을 참고하세요:

  • Groovy Documentation:

아직 문서를 살펴보지 않으셨다면, 꼭 읽어보시길 권합니다—추가적인 언어 기능이 많이 있습니다.

제 생각에 이러한 유연성은 작업이나 프로젝트에 언어를 선택할 때 Groovy의 가장 큰 장점이며, 훌륭한 교육 도구이기도 합니다.

이 짧은 글이 도움이 되길 바랍니다.

Back to Blog

관련 글

더 보기 »

Java 소개

인간이 작성한 Java 코드는 어떻게 기계에서 실행될까요? 프로그래밍 언어를 기계어로 변환하기 위해 JDK → JRE → JVM → JIT 라는 번역 시스템을 사용합니다.

Ruby 파일 처리와 시스템 명령

파일 열기와 읽기 ```ruby File.open 'p014constructs.rb', 'r' do |f1| while line = f1.gets puts line end end ``` 파일 쓰기 ```ruby File.open 'test.rb', 'w' do |f2| f2.puts 'Created by...' end ```