go를 알아보자(1)
목차
새해가 되고 뭔가 해야하지 않을까… 싶은 불안감이 커지던 중 평소에 node.js의 다음은 go 아니겠나 하고 생각하던 게 떠올랐습니다. 물론 node.js는 대체 불가입니다. 풀스택 개발자에게는 내 고향같은 편안함이 있죠. DB 부터 백엔드, 프론트까지 모든 스택을 하나로 다룰 수 있고 Typescript를 사용하면 정적 타입의 편안함까지 느낄 수 있죠.(물론 타입이 너무 복잡해서 아직도 잘 모릅니다… 하하하하…)
그런데 아무래도 만드는 제품을 on-premiss 솔루션화 하려다 보면, node.js 는 약간 어색함이 있습니다. 어쨌든 스크립트니까 코드가 그대로 노출된다는 단점이 있죠. 난독화를 하긴 하지만, 여전히 불안한 느낌은 있습니다. 물론 바이너리 역시도 100% 안전한 건 없지만, 뭐 그래도 말이죠.
하여간 이제 아주 천천히, 하던대로 go를 알아볼까 합니다. 평소에 많이 하던 코드를 go로 간단하게 작성해보면서 말이죠!
mongodb야 반갑다!⌗
예, mongodb를 이용할 겁니다. 그리고 아주 간단하게 도큐먼트를 하나 집어넣고, 집어 넣은 도큐먼트를 다시 꺼내오는 작업을 할 겁니다.
go를 설치하자.⌗
현재 go 최신버전은 1.25.5 입니다. 저는 맥에서 작성중이기 때문에 homebrew로 그냥 설치하면 됩니다.
> brew install go
> go version
go version go1.25.5 darwin/arm64
프로젝트 셋업⌗
그리고 적당한 곳에 폴더를 생성합니다.
> mkdir mongodb-example
> cd mongodb-example
# go 모듈 초기화
> go mod init mongodb-example
그리고 가장 좋아하는 ide를 띄웁니다. 그러면 go.mod 파일이 하나 보일겁니다.
module test
go 1.25.5
모듈 이름과 go 버전이 보이는데요, 이 파일은 node.js의 package.json 같은 역할을 합니다. 일단 이번에 우리가 사용할 모듈을 설치해볼까요?
> go get go.mongodb.org/mongo-driver/v2/mongo
go: added github.com/golang/snappy v1.0.0
go: added github.com/klauspost/compress v1.16.7
go: added github.com/xdg-go/pbkdf2 v1.0.0
go: added github.com/xdg-go/scram v1.1.2
go: added github.com/xdg-go/stringprep v1.0.4
go: added github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
go: added go.mongodb.org/mongo-driver/v2 v2.4.1
go: added golang.org/x/crypto v0.33.0
go: added golang.org/x/sync v0.11.0
go: added golang.org/x/text v0.22.0
자 그리고 다시 go.mod 파일을 보면!
module test
go 1.25.5
require (
github.com/golang/snappy v1.0.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver/v2 v2.4.1 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
)
설치된 패키지 목록이 보입니다. package.json 이랑 진짜 비슷하죠? 근데 저 많은 // indirect 는 뭘까요?
그러니까 직접적으로 사용하는 모듈(go.mongodb.org/mongo-driver/v2)가 간접적으로 사용하는 모듈이라는 뜻입니다. 근데 왜 go.mongodb.org/mongo-driver/v2 도 // indirect 냐고요? 아직 이 모듈을 사용하는 코드가 없기 때문입니다!
그럼 코드를 작성해보자.⌗
main.go를 작성할 차례입니다.
// 이 파일은 실행가능한 패키지라는 의미
package main
// 사용할 모듈을 임포트
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// 프로그램의 진입점
func main() {
/**
* 컨텍스트는 일종의 프로그램의 실행 환경을 제어하는 객체
* 컨텍스트에 10초 동안 아무런 신호가 전달되지 않으면 컨텍스트를 취소하고 프로그램을 종료
* 특정 연산이 너무 오래걸리는 경우 무작정 대기하지 않도록 함
*
* := 는 한 줄로 변수를 선언하고 초기화 한다
*/
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// defer는 프로그램이 종료될 때 실행되도록 지연시킨다
defer cancel()
// mongodb에 연결
client, err := mongo.Connect(options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
// log.Fatal은 오류가 발생하면 프로그램을 종료하고 오류 메시지를 출력
log.Fatal(err)
}
// 프로그램이 종료될 때 연결을 끊기
defer client.Disconnect(ctx)
// mongodb 서버에 연결 확인, ping 성공시 컨텍스트가 취소되지 않고 계속 실행
if err := client.Ping(ctx, nil); err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB")
// users 컬렉션을 가져옴
collection := client.Database("testdb").Collection("users")
// bson.M은 자바스크립트 객체와 유사한 형태의 데이터를 저장하는 Binary JSON 형식
user := bson.M{
"name": "John Doe",
"email": "john.doe@example.com",
"age": 30,
}
// 사용자를 컬렉션에 삽입
insertResult, err := collection.InsertOne(ctx, user)
if err != nil {
log.Fatal(err)
}
fmt.Println("User inserted with ID:", insertResult.InsertedID)
// 이메일로 사용자를 찾음
filter := bson.M{"email": "john.doe@example.com"}
var result bson.M
err = collection.FindOne(ctx, filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
// 찾은 사용자 정보 출력
fmt.Println("User found:", result)
}
코드를 보면 Go의 특징적인 에러 처리 패턴을 볼 수 있습니다.
if err != nil { log.Fatal(err) }다른 언어의
try-catch와는 다르게, Go는 에러를 명시적으로 반환값으로 처리합니다. 이게 처음엔 좀 번거로워 보이지만, 에러가 발생할 수 있는 지점을 명확하게 알 수 있다는 장점이 있죠. 예외가 여러개 중첩 된 경우 예외가 어디서 발생한 건지 어렵게 찾을 필요가 없습니다!
그리고 코드를 실행할 때마다 같은 데이터가 계속 쌓이는 게 싫다면, 삽입 전에 기존 데이터를 삭제하는 코드를 추가할 수도 있습니다.
// 삽입 전에 기존 데이터 삭제 (선택사항) _, err = collection.DeleteMany(ctx, bson.M{"email": "john.doe@example.com"}) if err != nil { log.Fatal(err) }
그리고 go mod tidy 를 실행한 뒤 다시 go.mod 파일을 보면 다음과 같습니다.
module test
go 1.25.5
require go.mongodb.org/mongo-driver/v2 v2.4.1
require (
github.com/golang/snappy v1.0.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
)
직접 임포트해서 사용하는 go.mongodb.org/mongo-driver/v2 모듈이 // indirect 에서 독립했습니다!
mongodb를 띄우자.⌗
간단한 docker-compose.yml 을 사용해서 mongodb를 띄울 겁니다. 프로젝트 루트 폴더(mongodb-example)에 docker-compose.yml 파일을 생성해주세요.
services:
mongodb:
image: mongo:latest
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
volumes:
mongodb_data:
이걸 이렇게 띄우죠.
> docker-compose up -d
이제 결과를 보자⌗
이제 결과를 확인해볼까요?
> go run main.go
Connected to MongoDB
User inserted with ID: ObjectID("6961a3ea4f62d4037b2611bb")
User found: {"_id":{"$oid":"6961a3ea4f62d4037b2611bb"},"name":"John Doe","email":"john.doe@example.com","age":{"$numberInt":"30"}}
예… 이게 끝입니다. 처음이니까요. 하하하하하하.
실습 환경 정리하기⌗
실습이 끝났으면 깔끔하게 정리해줍시다. MongoDB 컨테이너를 종료하고 데이터까지 삭제하려면 다음 명령어를 실행하면 됩니다.
> docker-compose down -v
-v 옵션은 볼륨까지 삭제한다는 의미입니다. 실습용 데이터니까 깔끔하게 지워버리는 게 속이 시원하죠!
참고자료⌗
- MongoDB Go Driver
- Perplexity와 Claude