Easy Understanding

Golang 찍먹해보기 본문

Dev

Golang 찍먹해보기

appleg1226 2021. 1. 16. 15:31

자바라는 언어를 어느 정도 사용하다보니 이제 다른 언어를 건드릴 때

어떤 것을 집중적으로 공부해야 하는지 알게 되는 것 같다.

 

그런 의미에서 일부 개발자들이 굉장히 극찬하는 go라는 언어를

한 번 경험이라도 해 보고 싶어서 한 번 '찍먹'을 해보았다.

 

로고는 귀여운데 막상 언어는 그렇게 귀엽지는 않다.

 


학습 과정

1. 인터넷 강의를 통해서 기초 학습하기

나는 보통 어떤 지식을 공부할 때 목표로 하는 깊이에 따라서 다음 방법으로 공부를 한다.

- 1단계: 가볍게 공부하기 = 인강 + google

- 2단계: 제대로 공부하기 = 인강 + 책 + google

- 3단계: 주력으로 사용하기 = 인강 + 책 + 레퍼런스 문서 정독 + google

 

이번 Go를 공부하는 방법으로는 1단계를 거쳤다.

 

강의는 Nomad Coder에 Go 무료 강의가 있어서 강의를 보면서 기초 문법을 확인했다.

 

nomadcoders.co/

 

노마드 코더 Nomad Coders

코딩은 진짜를 만들어보는거야!. 실제 구현되어 있는 서비스를 한땀 한땀 따라 만들면서 코딩을 배우세요!

nomadcoders.co

보통 새로운 프로그래밍 언어를 배우는 것은 처음보다 훨씬 빠르고, 해야할 것들이 적기 때문에

간단한 인터넷 강의만으로도, Go의 필수적인 문법과 개념은 얼추 이해를 할 수가 있었다.

 


2. 개발 환경 셋팅하기 및 프레임워크 선정하기

웹 개발을 주로 하면서 느낀건데, 보통 웹 관련 코드를 짜다보면 다양한 문법을 마주칠 수 밖에 없게 되며,

많은 코드를 짜야한다. 

 

이전에 kotlin, typescript를 공부할 때도 위와 같은 방법으로 언어를 익혔던 경험 때문에

이번에도 서비스를 하나 만들면서 연습하기로 결정하였다.

 

 그래서 데이터베이스 CRUD를 처리하는 http api 서버를 하나 만들기로 목표를 잡았다. 

 

go는 언어에서 기본적으로 제공되는 모듈만 이용해도 db 처리와 api는 만들 수 있지만, 

더 나은 생산성을 위한 프레임워크나 라이브러리를 조금 더 찾아보기로 했다.

 

아무래도 스프링으로 모든 것을 개발하던 습관 같은 것 때문에 찾아본 것 같다.

 

그런데, go에는 절대적인 우위를 갖고 있는 프레임워크가 없었다.

 

웹 프레임워크만 해도 gin, beego, echo, kit, fasthttp, revel....

 

하나의 절대적인 독주가 아니라,

"너가 필요한 프레임워크를 잘 골라서 갖다 써라." 는 느낌이었고,

"아니면 그냥 Go 기본 http 라이브러리로 짜라." 는 예제들도 많았다.

 

 

 웹 프레임워크는 Gin으로 결정했다. 

다른 프레임워크들의 문서도 읽어보기는 했지만, 다들 고만고만한 느낌이어서

가장 star 수가 많은 Gin을 나의 프레임워크로 결정했다.

 

현재 Go 프레임워크의 github star 순위는 다음 링크에서 확인할 수 있다.

(github.com/mingrammer/go-web-framework-stars)

 

 

 ORM(Object Relation Mapping) 프레임워크는 GORM으로 결정했다. 

이것도 다른 문서들을 봤지만,

가장 검증된 것이라는 판단으로 가장 많은 star 수를 가진 GORM을 선택했다.

 

현재 Go ORM 순위는 다음의 링크에서 확인할 수 있다.

(github.com/d-tsuji/awesome-go-orms)

 


3. 간단하게 개발해보기

내가 참고한 것은 다음과 같다.

<Go 공식 문서 + Gin 공식 문서 + GORM 공식 문서 + Gin을 사용한 프로젝트의 블로그와 깃헙>

 

나는 다음의 순서로 개발을 진행했다.

1. 가장 대표적인 예제인 User 테이블 만들기

2. 데이터베이스를 GORM과 연동하고 CRUD 함수들 만들기

3. Gin을 이용해서 http 요청을 routing하고 요청을 처리하는 함수 만들기

4. 위의 사항들을 테스트코드 작성하기

5. 배포해보기

 

그리고 개발의 결과물은 다음 Github에 업로드 해 놓았다.

github.com/appleg1226/golang-web-server

 

appleg1226/golang-web-server

golang web server(with gin, gorm). Contribute to appleg1226/golang-web-server development by creating an account on GitHub.

github.com

이외에도 테이블의 다대일 매핑도 건드려보고,

보안, 인증과 관련된 문서도 조금 읽어 보았는데 이건 그냥 나중에 읽어보고 구현하면 되는 데다가,

지금 이걸 다 보기엔 시간이 조금 걸릴 것 같아서 여기에서 멈추기로 했다.

 


Golang의 특징

1. Golang의 특징과 장점들

 Goroutine 

Reactor나 NodeJS의 Event-loop, Kotlin의 Coroutine과는 또 다른 Go만의 비동기 방법이다.

언어적 차원에서 이런 강력한 병렬 프로그래밍을 지원해준다는 것은 엄청난 장점이다.

어떤 블로그에서는 Go를 사용하는 굉장히 큰 이유가 이 Goroutine이라고 할 정도이다.

 

실제로 동작시켜본 결과 빠르고, 간단하다.

 

 간결한 문법들 

보통 다른 글들에서도 for문을 예로 들면서, 복잡한 문법이 적음을 강조한다.

try도 없고, while도 없고, 없는게 정말 많다.

그러나 그런 것이 없어도, Go 프로그래밍에서는 문제가 없다.

 

 빠른 컴파일 

Go도 역시 컴파일 언어라 보통 코드를 변경하고 빌드를 하면 시간이 많이 걸린다.

그러나 내부적으로 변경 상태를 추적하여 컴파일하기 때문에, 그 다음부터는 시간이 많이 줄어든다.

 

 쉬운 배포 

Java에서는 jar로 배포를 한다.

그러나 Go에서는 실행환경에 맞춘 binary 파일로 배포를 할 수 있다.

무슨 뜻이냐면 그냥 main.go를 빌드하면 ./main 명령어를 통해서 파일을 그냥 실행시킬 수 있다는 뜻.

자바에서는 JVM이 있어야 어플리케이션의 실행이 가능했었다.

 


2. Golang이 다른 언어와 다른 점들

 포인터 

요새 주류 언어들인 Java, Python, Javascript만 공부하면서 정말 포인터에 대해서는 까맣게 잊고 살았다.

Go의 위치가 왜 C와 Java 사이에 있는지를 알 수 있는 것이 바로 포인터이다.

 

포인터를 통해서 프로그래밍하는 것은 다른 언어들에서 참조값을 넘겨주던 방식과는 조금 다르다.

func GetUserById(user *User, key string) (err error){
    if err = config.DB.First(&user, key).Error; err != nil {
        return err
    }
    return nil
}

분명히 나는 데이터베이스에서 User를 조회하려고 했으나 return 형식은 error 형식이다.

'도대체 이 함수는 어떻게 user 객체를 반환하는거지'라는 생각을 잠깐했다. 

 

포인터를 이용한 프로그래밍 방식에서는 위처럼 매개변수에서 user의 포인터를 받아서,

config.DB.First() 함수에 그 주소값을 넘겨줌으로서 user 객체에 값이 채워진다.

 

이런 방식은 일반적인 방식이 아니라서 당황했다. 

 

 

 에러처리 

이것도 조금 새롭다. 보통 error에 대한 처리는 try나 throw를 통해서 해주던 다른 언어들과는 다르다.

 

위의 예시를 다시 보면 다음과 같은 if 문이 있다.

if err = config.DB.First(&user, key).Error; err != nil {
    return err
}

자세히 살펴보면

err = config.DB.First(&user, key).Error

err != nil

 

이렇게 두 개로 나눌 수 있다.

윗 줄은 err에 대한 임시 변수를 설정하는 부분이고

아랫 줄은 if문에 대한 조건문이다.

 

다음으로 err != nil 을 좀 더 자세히 보면

err가 nil(null 같은 go의 예약어)인지를 체크하는 문장이다.

if문으로 null check를 통해 에러체크를 하다니 정말 Java에서는 혼날만한 문법이

go에는 당연하게 사용되고 있다.

 

대부분의 에러 처리가 이렇게 이루어진다는 점에서 조금은 충격을 먹었다.

저수준 언어라는 점에서 이런 것이 허용되는구나 라는 생각도 들었다.

 

 

 상속이 없고 interface도 느낌이 다르다. 

Go에서는 다른 언어처럼 명시적으로 implement나 extend를 하지 않는다.

 

먼저 인터페이스를 구현하고 그 안에 공통으로 사용할 메서드들을 정의한다.

그리고 다른 struct들(class)은 그 안에 있는 메서드들을 구현하기만 하면 알아서 관계가 묶인다.

 

다른 언어들과는 다르게 이름이 같은 메서드를 구현하기만 해도 묶여버린다.

그리고 그것을 Go 자체에서 알아서 찾아서 연결을 해버린다.

 

아래의 예시처럼 구현을 한다.

// 1. 인터페이스를 구현했다.
type Book interface{
    Published() int
    Price() int
}

// 2. 클래스를 구현했다. 아직까진 위와 상관이 없다.
type Comics struct{
    published  int 
    price      int
}

// 3-1. 코믹 클래스에 대하여 Published()라는 Book 인터페이스의 함수를 구현했다.
func (c Comics) Published() int {
    return c.pubilished
}

// 3-2. 코믹 클래스에 대하여 Price()라는 Book 인터페이스의 함수를 구현했다.
func (c Comics) Price() int {
    return c.price
}

// 4. 이제 Book을 이용하여 Comics을 참조가 가능하다.
var onepiece, naruto Book
onepiece = Comic(1000, 10000)
naruto = Comic(600, 9000)

 

 이외에도 다른점들은 많다. 

public, private도 없고 그것을 이름의 첫 글자를 대문자 소문자로 구분한다.

제네릭도 없다.

다양한 컬렉션 라이브러리도 없다.

 

흠... 이렇게 써보니 없는게 많은건가...

여튼 다른 언어들과 스타일이 많이들 다르다.

 


마무리

확실히 C++을 대체하고자 나온 언어라서 그런지 포인터를 다루는 데에서 스타일이 많이 달랐다.

자바만 하다가 이 언어를 사용하기에는 스타일적으로 적응이 되지 않는 것이 제일 컸다.

 

그러나 그렇다고 문법을 익히는 데에 몇 주가 걸릴 정도는 아니었고, 

하다보면 금방 익숙해질 것 같기는 하다.

 

이후로 성능적인 이슈를 고려할만한 어플리케이션을 만든다면 Go의 도입을 고려해볼 지도 모른다.

 

그러나 내가 이후에 Go를 이용해서 무엇인가를 만들어야 할 때는, 

관련 생태계가 많이 좀 발전되어 있었으면 좋겠다는 생각이 든다.

 

자료도 별로 없고, 그 얼마 없는 자료로 공부하기엔 실력이 부족하여 위험부담도 큰 것 같다.

 

사실 C++을 도전하기엔 부담스럽지만, 이 정도는 이후로도 할만한 듯 하다.

저수준 언어 경험이 없어서 개발자로서 부끄러움이 있었는데, 그럴 때 Go를 꺼내면 될 것 같다.