체인의정석

MongoDB+Go 에서의 Cursor를 활용한 다중 데이터 조회 (select all) 본문

개발/backend(go)

MongoDB+Go 에서의 Cursor를 활용한 다중 데이터 조회 (select all)

체인의정석 2025. 4. 8. 15:33
728x90
반응형

MongoDB와 Go를 사용하여서 데이터를 조회할 때는

Mongo driver를 사용하면된다.

https://github.com/mongodb/mongo-go-driver

 

GitHub - mongodb/mongo-go-driver: The Official Golang driver for MongoDB

The Official Golang driver for MongoDB. Contribute to mongodb/mongo-go-driver development by creating an account on GitHub.

github.com

이 중 cursor에 대해서 사용해보았다.

먼저 위의 공식 문서 상 cursor는 다음과 같이 사용할 수 있게 되어있다. 커서의 경우 스트림 형태로 DB의 접근 1번만 통해서 계속해서 작업을 처리하기 때문에 select all 과 같은 다중 조회를 해야할 때는 일일이 Find를 하는 것은 DB와의 I/O가 늘어나기 때문에 실행의 효율성을 위하여 cursor를 사용할 수 있는 것이 장점이다.

ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

cur, err := collection.Find(ctx, bson.D{})
if err != nil {
  log.Fatal(err)
}

defer cur.Close(ctx)
for cur.Next(ctx) {
    var result bson.D
    if err := cur.Decode(&result); err != nil {
      log.Fatal(err)
    }

    // do something with result....
}

if err := cur.Err(); err != nil {
    log.Fatal(err)
}

cursor는 사실 일반적인 Find를 실행할 때 사용이 되게 되며, 이에 따라서 사용방법 또한 Find 부터 시작된다.

참고로 context는 go에서 자주 사용된다고 하는데 특히 gin 프레임워크나 외부 프레임워크에서 자주 사용하다 보니 이해를 해야한다고 한다. 여기서 쓰인 context.Background()는 현재 컨텍스트를 불러오는 행위이므로 Find를 하기 위해 필요하며, 만약 context가 꼭 들어가야하지만 넣을것이 마땅히 없다면 context.TODO()를 사용한다고 한다.

 

unc (a *AccountDB) VerifyApiKey(rawKey string) (*entity.ApiKey, error) {
	ctx := context.Background()

	// status가 active인 모든 API 키 조회
	cursor, err := a.collectionApiKey.Find(ctx, bson.M{"status": "active"})
	if err != nil {
		return nil, errors.New("internal error while fetching API keys")
	}

위와 같이 Find 와 같이 ORM 비슷한 문법을 사용하면 가능하다. 이 때 에러가 리턴되는지 체크를 해준 후에 뒤에서 받은 cursor를 통하여서 추가적인 작업을 진행할 수 있다.

	defer cursor.Close(ctx)

	// 모든 후보에 대해 salt를 조합한 해시값을 비교
	for cursor.Next(ctx) {
		var candidate entity.ApiKey
		if err := cursor.Decode(&candidate); err != nil {
			continue
		}

		// rawKey + salt 조합 → SHA256 해시
		combined := rawKey + candidate.Salt
		hashedInput := sha256.Sum256([]byte(combined))
		if hex.EncodeToString(hashedInput[:]) == candidate.HashedKey {
			return &candidate, nil
		}
	}

	// 커서 반복 중 오류가 발생했는지 확인
	if err := cursor.Err(); err != nil {
		return nil, errors.New("error while iterating API keys")
	}

	return nil, errors.New("invalid or inactive API key")

먼저 defer는 함수가 모두 실행되고 난 후 마지막에 실행되기 때문에 커서가 생성될 때 미리 만들어 두는 식을 취하면 좋다고 한다.

이후 cursor.Next를 통해서 이후의 작업을 진행할 수 있는데 이 Cursor라는 것은 어차피 조회할 때 나오는 것이기 때문에 여기서 조합한 해시 값을 비교하는등의 로직을 진행할 수 있다.

 

728x90
반응형
Comments