[Golang + MongoDB] 몽고+고랭으로 CRUD 구현하기
이번 포스팅은 Golang언어를 사용하여,
MongoDB의 CRUD를 구현 하는 포스팅입니다:)
MongoDB 설치
MongoDB 설치는 아래 포스팅을 참고하면 됩니다.
soyoung-new-challenge.tistory.com/95?category=826576
MongoDB 접속/연결
접속 정보 읽기
작성 된 코드를 깃에 업로드 할 때, DB 접속 정보가 코드에 있으면, 보안 상의 위험이 있으므로
DB접속 정보는 따로 파일에 저장 후 로딩해서 사용함 (보안정보가 있는 파일은 .gitignore에 포함)
💡 아래와 같은 DB접속 정보가 있는 json 파일 생성
// $ vi mongodb_auth.json
{
"username" : "[유저이름]",
"password" : "[비밀번호]",
"hostname" : "[접속IP]",
"port" : "[:접속포트]"
}
💡 json을 로딩하여 DB 접속 정보를 가져오는 코드
import (
"encoding/json"
"io/ioutil"
"os"
U "[현재_작업_디렉토리]/utils"
)
// get MongoDB Authorization info7
func getAuth() m.Auth {
data, err := os.Open("[DB 접속정보.json 위치]")
U.CheckErr(err)
var auth m.Auth
byteValue, _ := ioutil.ReadAll(data)
json.Unmarshal(byteValue, &auth)
return auth
}
💡 Error 체크 코드
// $ vi utils/utils.go
// CheckErr function
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
}
}
> 자주 혹은 반복적으로 사용하는 코드는 util에 넣어서 사용
DB에 접속하기
💡 DB 접속 및 확인
// ConnectDB to MongoDB
func ConnectDB() (client *mongo.Client, ctx context.Context, cancel context.CancelFunc) {
// Timeout 설정을 위한 Context생성
ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
// 위에서 작성한 함수 사용
Authrization := getAuth()
// Auth에러 처리를 위한 client option 구성
clientOptions := options.Client().ApplyURI("mongodb://" + Authrization.Hostname + Authrization.Port).SetAuth(options.Credential{
Username: Authrization.Username,
Password: Authrization.Password,
})
// MongoDB 연결
client, err := mongo.Connect(ctx, clientOptions)
// Util에 작성한 에러 확인 함수
U.CheckErr(err)
// MongoDB 연결 검증
U.CheckErr(client.Ping(ctx, readpref.Primary()))
return client, ctx, cancel
}
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> Timeout 설정을 위한 Context생성
2> DB 접속정보 로딩
3> Auth 처리를 위한 Client Option 구성
4> MongoDB 연결/접속 + 접속 에러 체크
5> MongoDB 연결 검증
💡 Collection 연결
dbName := "[DB 이름]"
colName := "[Collection 이름]"
func GetCollection(client *mongo.Client, colName string) *mongo.Collection {
return client.Database(dbName).Collection(colName)
}
> mongo.Client와 collection이름을 input으로 받아 Collection을 리턴하는 function
CRUD 구현
C : Create (생성)
💡 도큐먼트 존재 유무 확인 후 생성
// CreateUser func
func CreateUser(googleID, name string) string {
// DB 접속
client, ctx, cancel := ConnectDB()
// 함수 종료 뒤 연결을 끊어지도록 설정
defer client.Disconnect(ctx)
defer cancel()
// 필터 값 정의
filter := bson.M{"googleId": googleID, "name": name}
// DB에 값이 존재하는지 확인
num, err := GetCollection(client, "[collection이름]").CountDocuments(ctx, filter)
U.CheckErr(err)
// 새로 넣을 데이터 정의
newData := m.UserInfo{
GoogleID: googleID,
Name: name,
Email: email,
}
// DB값이 존재하지 않으면
if num == 0 {
_, err := GetCollection(client, "[collection이름]").InsertOne(ctx, newData)
U.CheckErr(err)
}
return "create!"
}
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> DB 접속 : 위에서 생성한 ConectDB() func
2> 함수 종료 뒤 DB 접속이 종료되도록 설정 : defer (함수가 종료 된 후에 실행)
3> 검색 할 필터 값 정의
4> 위에서 정의한 필터 값에 맞는 도큐먼트가 존재하는지 확인
5> 새로 넣을 데이터 정의
6> DB에 동일한 데이터가 없을 경우 데이터를 넣는다.
📌 다큐먼트 갯수 확인 : CountDocuments
[ CountDocuments ]
func(ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error)
> *Collection에 존재하는 func : Input은 Context와 필터 값이고, Output은 document수와 error가 나온다
📌 한개의 데이터 넣기/생성 : InsertOne
[ InsertOne ]
func(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*InsertOneResult, error)
> *Collection에 존재하는 func : Input은 Context와 입력할 도큐먼트이고, Output은 *InserOneResult와 error가 나온다
R : Read(읽기)
💡 DB 모든 데이터 읽기
// AllData func
func AllData(collection string, filter bson.M, sort bson.M) string {
// DB 연결하기
client, ctx, cancel := ConnectDB()
// 함수 종료 후 DB 연결 종료하기
defer client.Disconnect(ctx)
defer cancel()
// 데이터를 담을 변수 선언
var datas []bson.M
// 데이터 읽기
res, err := GetCollection(client, collection).Find(ctx, bson.M{})
U.CheckErr(err)
// 결과를 변수에 담기
if err = res.All(ctx, &datas); err != nil {
fmt.Println(err)
}
// []byte를 String타입으로 변환
data := U.JSONMarshalString(datas)
return data
}
> 모든 데이터를 가져올 경우 filter := bson.M{}
💡 DB 특정 조건에 해당하는 데이터 읽기
// AllData func
func AllData(collection string, filter bson.M, sort bson.M) string {
// DB 연결하기
client, ctx, cancel := ConnectDB()
// 함수 종료 후 DB 연결 종료하기
defer client.Disconnect(ctx)
defer cancel()
// 데이터를 담을 변수 선언
var datas []bson.M
// 필터 조건 선언하기 : onLive 필드의 값이 true인 document만 읽는다
filter := bson.M{"onLive": true}
// 데이터 읽기
res, err := GetCollection(client, collection).Find(ctx, filter)
U.CheckErr(err)
// 결과를 변수에 담기
if err = res.All(ctx, &datas); err != nil {
fmt.Println(err)
}
// []byte를 String타입으로 변환
data := U.JSONMarshalString(datas)
return data
}
> 특정 조건에 해당하는 데이터를 가져올 경우 filter := bson.M{ "필드" : "값" }
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> DB 접속 : 위에서 생성한 ConectDB() func
2> 함수 종료 뒤 DB 접속이 종료되도록 설정 : defer (함수가 종료 된 후에 실행)
3> 결과로 나온 데이터를 담을 변수 선언
4> Find함수를 이용해 DB데이터 읽기
5> 결과를 변수에 담는다.
6> []bson.M 타입의 데이터를 string으로 변환 후 리턴 ( 각자 원하는 데이터 타입으로 변경하면 됨)
📌 데이터 읽기 : Find
[ Find ]
func(ctx context.Context, filter interface{},opts ...*options.FindOptions) (*Cursor, error)
> *Collection에 존재하는 func : Input은 Context와 원하는 필터값이고, Output은 *Cursor와 error
> 모든 데이터를 가져오고 싶을 때는 filter에 bson.M{}을 넣어준다 (필터값 없이 모든 결과를 가져오겠다는 의미)
> 조건을 추가할 때는 bson.M안에 키, 값 형태로 조건을 준다
R : Read (읽기) + 심화버전
심화버전에 대한 포스팅은 추후 업데이트 예정
U : Update(갱신)
💡 기존에 있는 데이터 값을 변경할 때
// Update func
func Update(collection string, filter bson.M, update bson.M) string {
client, ctx, cancel := ConnectDB()
defer client.Disconnect(ctx)
defer cancel()
filter := bson.M{"googleId": googleID}
update := bson.M{
"$set": bson.M{
"token": token,
},
}
_, err := GetCollection(client, collection).UpdateOne(ctx, filter, update)
U.CheckErr(err)
return "Update!"
}
> $set : 기존에 존재하는 값을 새로운 값으로 대체 할 경우
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> DB 접속 : 위에서 생성한 ConectDB() func
2> 함수 종료 뒤 DB 접속이 종료되도록 설정 : defer (함수가 종료 된 후에 실행)
3> 업데이트 할 도큐먼트를 찾기위한 필터 설정, 설정을 안해주면 모든 데이터가 업데이트 된다
4> 변경할 데이터에 대해 bson.M 타입으로 작성 ($set : 특정 key의 value를 재설정)
5> UpdateOne 함수를 사용하여 데이터 업데이트
6> 에러 확인 후, 문제가 없을 경우 업데이트 완료
💡리스트 값을 추가하고 제거하는 업데이트 요청일 경우
// 리스트 요소를 추가
func Update(collection string, filter bson.M, update bson.M) string {
client, ctx, cancel := ConnectDB()
defer client.Disconnect(ctx)
defer cancel()
filter := bson.M{"googleId": googleID}
update := bson.M{
"$push": bson.M{
"following": following,
},
}
_, err := GetCollection(client, collection).UpdateOne(ctx, filter, update)
U.CheckErr(err)
return "Update!"
}
// 리스트 요소를 제거
func Update(collection string, filter bson.M, update bson.M) string {
client, ctx, cancel := ConnectDB()
defer client.Disconnect(ctx)
defer cancel()
filter := bson.M{"googleId": googleID}
update := bson.M{
"$pull": bson.M{
"following": following,
},
}
_, err := GetCollection(client, collection).UpdateOne(ctx, filter, update)
U.CheckErr(err)
return "Update!"
}
> $push : 리스트안에 요소를 추가할 때
> $pull : 리스트안의 요소를 제거할 때
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> DB 접속 : 위에서 생성한 ConectDB() func
2> 함수 종료 뒤 DB 접속이 종료되도록 설정 : defer (함수가 종료 된 후에 실행)
3> 업데이트 할 도큐먼트를 찾기위한 필터 설정, 설정을 안해주면 모든 데이터가 업데이트 된다
4> 변경할 데이터에 대해 bson.M 타입으로 작성 ($push : 리스트에 값 추가, $pull : 리스트에 값 제거)
5> UpdateOne 함수를 사용하여 데이터 업데이트
6> 에러 확인 후, 문제가 없을 경우 업데이트 완료
🚩 하나의 데이터 변경 : UpdateOne
[ UpdateOne ]
func(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*UpdateResult, error)
> *Collection에 존재하는 func : Input은 Context와 원하는 필터(조건)과, 업데이트 내용, Output은 error이다
> 하나의 데이터를 업데이트 하는 func으로, 어떤 document를 업데이트 할 것인지 필터를 걸어준다
> 업데이트 데이터 : $set, $pull, $push 등 다양하게 원하는 기능에 맞게 업데이트 할 데이터와 형태를 지정
D : Delete(삭제)
💡 데이터를 삭제하는 경우
// Delete func
func Delete(collection string, filter bson.M) string {
client, ctx, cancel := ConnectDB()
defer client.Disconnect(ctx)
defer cancel()
filter := bson.M{"_id": U.ConvertID(id)}
_, err := GetCollection(client, collection).DeleteOne(ctx, filter)
U.CheckErr(err)
return "Delete!"
}
위에 작성한 코드는 아래와 같은 파이프 라인으로 작동 함
1> DB 접속 : 위에서 생성한 ConectDB() func
2> 함수 종료 뒤 DB 접속이 종료되도록 설정 : defer (함수가 종료 된 후에 실행)
3> 삭제 할 도큐먼트를 찾기위한 필터 설정
4> 필터에 해당하는 도큐먼트를 삭제하기 위한 DeleteOne func사용
5> 에러확인, 문제가 없을 경우 정상적으로 데이터 삭제
🚩 데이터 삭제 : DeleteOne
[ DeleteOne ]
func(ctx context.Context, filter interface{},opts ...*options.DeleteOptions) (*DeleteResult, error)
> *Collection에 존재하는 func : Input은 Context와 원하는 필터(조건), Output은 error이다
> 데이터를 삭제 하는 func으로, 어떤 document를 삭제 할 것인지 필터를 걸어준다