Python Web Framework/Django, Flask

[Golang + MongoDB] 몽고+고랭으로 CRUD 구현하기

new_challenge 2020. 6. 10. 00:19
반응형

이번 포스팅은 Golang언어를 사용하여,

MongoDB의 CRUD를 구현 하는 포스팅입니다:)

 

 

 

 

MongoDB 설치

MongoDB 설치는 아래 포스팅을 참고하면 됩니다.

soyoung-new-challenge.tistory.com/95?category=826576

 

[MongoDB] macOS에 몽고DB + MongoDB Compass 설치

이번 포스팅은 맥os에 몽고 db와 관리툴인 MongoDB compass 설치에 관한 포스팅 입니다 NoSQL 란 NoSQL : 비관계형 데이터베이스 >> 정해진 스키마 또는 관계가 없다. >> 안에 들어있는 데이터를 Documents라��

soyoung-new-challenge.tistory.com

 

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가 나온다

 

CountDocuments 상세 설명

 

📌 한개의 데이터 넣기/생성 : InsertOne

[ InsertOne ]

func(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*InsertOneResult, error)

> *Collection에 존재하는 func :  Input은 Context와 입력할 도큐먼트이고, Output은 *InserOneResult와 error가 나온다

 

InsertOne 상세설명

 

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를 삭제 할 것인지 필터를 걸어준다

 

반응형