개요
이 튜토리얼에서는 MongoDB 고 (Go) 드라이버 와 Gin 웹 프레임워크 사용하여 웹 애플리케이션 빌드 방법을 보여줍니다.
Gin은 고 (Go) 위한 빠르고 가벼운 웹 프레임워크 입니다. 최소한의 모듈식 설계로 웹 서버, API 및 마이크로서비스 구축에 이상적입니다.
이 튜토리얼에서는 MongoDB database 에 연결하는 다음 엔드포인트를 사용하여 웹 애플리케이션 만드는 방법을 학습 수 있습니다.
컬렉션 에서 모든 영화를 조회 하는 엔드포인트
ID 로 단일 영화를 조회 하는 엔드포인트
movies컬렉션 에서 집계 작업을 실행 위한 엔드포인트입니다.
샘플 데이터
이 튜토리얼에서는 Atlas 샘플 데이터 세트의 데이터베이스 movies 에 있는 컬렉션 sample_mflix 사용합니다. 무료 MongoDB Atlas cluster 생성하고 샘플 데이터 세트를 로드하는 방법을 학습 Atlas 시작하기 가이드 참조하세요.
전제 조건
이 튜토리얼을 시작하기 전에 다음 사항이 있는지 확인하세요.
개발 머신에 고(Go) 설치
MongoDB deployment. 이 튜토리얼에서는 무료 MongoDB Atlas cluster 사용할 수 있습니다. 자세한 내용은 Atlas 시작하기 가이드 참조하세요.
프로젝트 설정
애플리케이션 구조 만들기
기본 애플리케이션 파일 만들기
프로젝트 디렉토리 에 main.go 파일 만들고 다음 코드를 추가하여 기본 Gin 애플리케이션 설정하다 합니다.
package main import ( "log" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
MongoDB에 연결
main.go 파일 업데이트하여 MongoDB 에 연결합니다. 기존 코드를 다음 코드로 대체하여 MongoDB deployment 에 연결합니다.
package main import ( "context" "log" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // Replace with your MongoDB connection string const uri = "<connection-string>" func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
자리 <connection-string> 표시자를 실제 MongoDB 연결 문자열 로 바꿉니다. 연결 문자열 얻는 방법에 대해 자세히 학습 클러스터에 연결 가이드 를 참조하세요.
API 엔드포인트 구현
이제 엔드포인트 3개를 추가하여 영화 데이터와 상호 작용 수 있습니다. 필요한 가져오기 및 엔드포인트 구현을 포함하도록 main.go 파일 업데이트합니다.
경로 핸들러 추가
connectToMongoDB 함수 뒤에 다음 경로 핸들러를 추가합니다.
// GET /movies - Retrieves all movies func getMovies(c *gin.Context, client *mongo.Client) { // Find all movies in the collection cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes all results var movies []bson.D if err = cursor.All(c.Request.Context(), &movies); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movies c.JSON(http.StatusOK, movies) } // GET /movies/:id - Retrieves a movie by ID func getMovieByID(c *gin.Context, client *mongo.Client) { // Gets the movie ID from the URL parameter as a string idStr := c.Param("id") // Converts string ID to MongoDB ObjectID id, err := bson.ObjectIDFromHex(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"}) return } // Finds the movie by ObjectID var movie bson.D err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movie c.JSON(http.StatusOK, movie) } // POST /movies/aggregations - Runs aggregation pipeline func aggregateMovies(c *gin.Context, client *mongo.Client) { // Gets aggregation pipeline from request body var pipeline interface{} if err := c.ShouldBindJSON(&pipeline); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"}) return } // Executes the aggregation pipeline cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes the results var result []bson.D if err = cursor.All(c.Request.Context(), &result); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the aggregation result c.JSON(http.StatusOK, result) }
경로 등록
main 함수를 업데이트하여 새 경로를 등록합니다.
func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) // Registers movie endpoints r.GET("/movies", func(c *gin.Context) { getMovies(c, client) }) r.GET("/movies/:id", func(c *gin.Context) { getMovieByID(c, client) }) r.POST("/movies/aggregations", func(c *gin.Context) { aggregateMovies(c, client) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
애플리케이션 테스트
생성한 엔드포인트를 테스트하여 예상대로 작동하는지 확인할 수 있습니다.
모든 동영상 가져오기 테스트
서버 다시 시작하고 웹 브라우저에서 http://localhost:8080/movies (으)로 이동합니다. 샘플 데이터 세트의 영화 배열 포함된 JSON 응답이 표시되어야 합니다.
ID 로 영화 가져오기 테스트
단일 영화 엔드포인트를 테스트하려면 영화 응답에서 _id 값을 복사하고 http://localhost:8080/movies/{id}로 이동합니다.
예시 들어 http://localhost:8080/movies/573a1390f29313caabcd42e8(으)로 이동하면 브라우저의 응답은 다음과 유사합니다.
{ "_id": "573a1390f29313caabcd42e8", "awards": { "wins": 1, "nominations": 0, "text": "1 win." }, "cast": [ "A.C. Abadie", "Gilbert M. 'Broncho Billy' Anderson", "George Barnes", "Justus D. Barnes" ], "countries": [ "USA" ], "directors": [ "Edwin S. Porter" ], "fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.", "genres": [ "Short", "Western" ]," .... }
집계 엔드포인트 테스트
집계 엔드포인트를 테스트하려면 POST 요청 해야 합니다. curl 또는 Postman과 같은 도구를 사용하여 요청 본문의 집계 파이프라인 사용하여 요청 보낼 수 있습니다.
다음 집계 예시 연도별 희극 영화 수를 계산합니다.
[ {"$match": {"genres": "Comedy"}}, {"$group": { "_id": "$year", "count": {"$sum": 1} }}, {"$sort": {"count": -1}} ]
터미널에서 다음 코드를 실행하여 curl로 이 엔드포인트를 테스트합니다.
curl -X POST http://localhost:8080/movies/aggregations \ -H "Content-Type: application/json" \ -d '[ {"$match": {"genres": "Comedy"}}, {"$group": {"_id": "$year", "count": {"$sum": 1}}}, {"$sort": {"count": -1}} ]'
[ { "_id": 2014, "count": 287 }, { "_id": 2013, "count": 286 }, { "_id": 2009, "count": 268 }, { "_id": 2011, "count": 263 }, { "_id": 2006, "count": 260 }, ... ]
경고
이 튜토리얼의 집계 엔드포인트를 통해 사용자는 모든 집계 파이프라인 실행 수 있습니다. 프로덕션 환경에서는 잠재적인 보안 문제나 성능 문제를 방지하기 위해 작업 유형을 제한하고 입력의 유효성을 검사해야 합니다.
전체 코드
다음 파일 웹 애플리케이션 의 전체 코드를 보여줍니다.
package main import ( "context" "log" "net/http" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // Replace with your MongoDB connection string const uri = "YOUR-CONNECTION-STRING-HERE" func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) // Registers movie endpoints r.GET("/movies", func(c *gin.Context) { getMovies(c, client) }) r.GET("/movies/:id", func(c *gin.Context) { getMovieByID(c, client) }) r.POST("/movies/aggregations", func(c *gin.Context) { aggregateMovies(c, client) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } } // GET /movies - Retrieves all movies func getMovies(c *gin.Context, client *mongo.Client) { // Find all movies in the collection cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes all results var movies []bson.D if err = cursor.All(c.Request.Context(), &movies); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movies c.JSON(http.StatusOK, movies) } // GET /movies/:id - Retrieves a movie by ID func getMovieByID(c *gin.Context, client *mongo.Client) { // Gets the movie ID from the URL parameter as a string idStr := c.Param("id") // Converts string ID to MongoDB ObjectID id, err := bson.ObjectIDFromHex(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"}) return } // Finds the movie by ObjectID var movie bson.D err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movie c.JSON(http.StatusOK, movie) } // POST /movies/aggregations - Runs aggregation pipeline func aggregateMovies(c *gin.Context, client *mongo.Client) { // Gets aggregation pipeline from request body var pipeline interface{} if err := c.ShouldBindJSON(&pipeline); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"}) return } // Executes the aggregation pipeline cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes the results var result []bson.D if err = cursor.All(c.Request.Context(), &result); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the aggregation result c.JSON(http.StatusOK, result) }
요약
이제 다음을 보여주는 기본 애플리케이션 생겼습니다.
Gin 웹 프레임워크 와 고 (Go) 운전자 사용하여 고 (Go) 프로젝트 설정합니다.
고 (Go) 운전자 사용하여 MongoDB database 에 연결합니다.
MongoDB 작업을 수행하여 문서를 찾고, 단일 문서 찾고, 집계 파이프라인을 실행 REST API 엔드포인트를 구현합니다.
문자열 ID를 ObjectID로 변환하는 등의 일반적인 시나리오를 처리합니다.
데이터베이스 작업에 대한 적절한 오류 처리를 구현합니다.
다음 단계
애플리케이션 추가로 개발하려면 다음 단계를 고려하세요.
집계 엔드포인트에 대한 입력 유효성 검사 및 요청 제한 추가
인덱스를 추가하여 쿼리 성능 개선
추가 리소스
이 튜토리얼의 개념에 대해 자세히 학습 다음 리소스를 참조하세요.