Golang에서 Lombok을 느껴보자

Golang에서 Lombok을 느껴보자

October 24, 2023

Lombok?

LombokJava에서 getter, setter와 같은 반복적인 코드를 줄여주는 라이브러리이다.

Go 언어는 특성 상 Getter, Setter, 생성자와 같은 작업이 매우 귀찮다.

자바와 같은 경우 이런 작업을 자동으로 해주는 라이브러리가 존재한다.

예를 들어 아래와 같은 구조체에 생성자를 만든다고 가정해보자

type User struct {
    Name string
    Age int
}

아마 대부분 다음과 같이 생성자를 만들어야 할 것이다.

func NewUser(name string, age int) User {
    return User{
        Name: name,
        Age: age,
    }
}

자바에서는 생성자 생성을 보통 이렇게 하는데

public class User {
    private String name;
    private int age;
    
    public User(String name, int age){
        this.name = name;
        this.age = age;
    } 
}

이런 작업을 자동으로 해주는 라이브러리가 Lombok이다. Lombok을 사용하면 다음과 같이 사용할 수 있다.

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

이렇게 개발자로 하여금 생성자 생성과 같은 반복작업, 휴먼 에러등을 줄여주는 라이브러리이다.

Gombok

Go 언어 특성상 워낙 보일러 플레이트가 많고, 이런 코드를 계속 쓰다보면 실제 비즈니스 코드를 써야하는 부분에 대해 집중하기가 어렵다. (그냥 내가 게으른거겠지만..) 어떻게 자동화할까를 생각하다 회사에서 잘 활용할 수 있게 lombok과 go를 합쳐서 gombok이라는 Code Generator 라이브러리를 만들어보았다.

설치

간단하게 install 명령어로 설치가 가능하다

 $ go install github.com/YangTaeyoung/gombok@v1.1.0

사용법

사용하고자 하는 구조체에 주석과 함께 Lombok에서 사용하던 어노테이션을 그대로 적어주면 된다.

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
    Name string
    Age int
}

그리고 다음과 같이 실행하면 된다. 별도의 옵션을 지정할 필요가 없으며 해당 명령 실행시 {파일명}_gombok.go 파일이 생성된다.

$ gombok

> main.go
> 2023/10/24 23:25:39 Found @AllArgsConstructor in User
> 2023/10/24 23:25:39 Found @Builder in User
> 2023/10/24 23:25:39 Found @Getter in User
> 2023/10/24 23:25:39 Found @Setter in User

해당 파일을 열어보면 다음과 같이 생성자, 빌더, Getter, Setter가 생성된 것을 확인할 수 있다.

// Code generated by gombok. DO NOT EDIT.
package main

// NewUserWithAllArgs
func NewUserWithAllArgs(name string, age int) User {
	return User{
		Name: name,
		Age:  age,
	}
}

// UserBuilder
// a builder for User
type UserBuilder struct {
	target *User
}

// WithName
// sets the Name field of the target User
func (ub UserBuilder) WithName(name string) UserBuilder {
	ub.target.Name = name

	return ub
}

// WithAge
// sets the Age field of the target User
func (ub UserBuilder) WithAge(age int) UserBuilder {
	ub.target.Age = age

	return ub
}

// Build
// constructs a User from the builder
func (ub UserBuilder) Build() User {
	return *ub.target
}

// NewUserBuilder
// creates a new builder instance for User
func NewUserBuilder() UserBuilder {
	return UserBuilder{target: &User{}}
}

// GetName
func (u *User) GetName() string {
	return u.Name
}

// GetAge
func (u *User) GetAge() int {
	return u.Age
}

// SetName
func (u *User) SetName(name string) {
	u.Name = name
}

// SetAge
func (u *User) SetAge(age int) {
	u.Age = age
}

이제 단순히 생성된 리시버 함수를 사용하고자 하는 곳에서 사용하면 된다.

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
	Name string
	Age  int
}

func main() {
	user := NewUserBuilder().WithAge(10).WithName("John").Build()

	fmt.Println(user.GetName(), user.GetAge())

	user.SetName("Jane")
	user.SetAge(20)

	fmt.Println(user.GetName(), user.GetAge())

	user2 := NewUserWithAllArgs("giraffe", 27)

	fmt.Println(user2.GetName(), user2.GetAge())
}

Result

John 10
Jane 20
giraffe 27