
안녕하세요 Walter입니다.
이번시간은 Golang을 이용해서 Graphql서버를 구현하고자 합니다.
개인적으로 Graphql 서버를 구현할때 자주 사용하는 라이브러리가 있어서 같이 소개하고자 합니다.
Golang 다운로드
맥사용자는 brew install golang도 가능하지만 개인적으로, 아래와같이 설치를 선호합니다.
Golang 다운로드 https://go.dev/dl/ 사이트에서 다운로드
Tools 설치
https://github.com/golang/tools
Graphql 라이브러리 고려사항
golang에서 graphql 라이브러리가 몇가지 있습니다. 크게는 2~3개가 유명하지만,
라이브러리를 사용하는데 있어서 선정기준을 정했습니다.
1. 스키마 우선접근의 제공
2. 스키마를 통한 코드 제너레이터 제공여부 - resovler, method 등등 일일이 만들기위해서는 많은 노동이 소요됩니다.
3. Apollo가 인정한 공식 라이브러리인가?
4. 별점과 지속적인 업데이트 라이브러리인가?
위의 고려사항을 통해서 https://github.com/99designs/gqlgen 위의 라이브러리를 사용하게 됐습니다.
https://gqlgen.com/ 위의 링크를 참조하여 코딩을 실행해보겠습니다.
코딩 시작
1. 서비스 모듈 시작
golang은 1.11버전이상부터 모듈시스템을 적용했습니다. 기존 gopath의 위치한 설정과 관계없이
아래와 같이 모듈을 생성하여 코드 작성 가능합니다.
# 컨테이너 서비스로 등록할 Go 프로젝트 폴더 생성합니다.
mkdir myservice
# myservice라는 모듈 초기화 합니다.
go mod init myservice
-> go: creating new go.mod: module myservice
total 8
drwxr-xr-x 3 user staff 96 1 20 00:11 .
drwxr-xr-x 15 user staff 480 1 20 00:10 ..
-rw-r--r-- 1 user staff 26 1 20 00:11 go.mod
2. Makefile 생성
간단하게 서비스를 실행할 makefile을 만들었습니다.
gen:
go get -d github.com/99designs/gqlgen
cd graphql/todo && gqlgen
start:
go mod tidy
go run main.go
3. todo의 간단하 조회 구현해보자
graphql 스키마 기본 구조 생성
# graphql 모움의 폴더에 todo 서비스 구현을 위해서 생성합니다.
mkdir -p graphql/todo && cd graphql/todo
# graphql schema generate를 위한 설정을 세팅합니다.
touch .gqlgen.yml
# 쿼리용 정의를 생성합니다.
touch query.graphql
# 데이타 enum, input용, 데이타 타입정의 폴더를 생성합니다.
mkdir -p schema/enum schema/input schema/type
gqlgen gernerate파일 - .gqlgen.yml 아래와 같이 입력합니다.
schema : graphql을 참조하는 폴더 & 적용합니다.
exec: 실행시 gernerate 폴더와 파일 이름을 설정합니다.
model : schema를 통해서 생성되는 모델 설정입니다.
resovler: 구현실체입니다. gernerate되어 생성되지만, 실제 비지니스 코드와 연결하여 수정할 곳입니다.
schema:
- schema/**.graphql
- ./*.graphql
exec:
filename: gen/generated.go
package: gen
federation:
filename: gen/federation.go
package: gen
model:
filename: gen/graphqlmodel/models_gen.go
package: graphqlmodel
resolver:
layout: follow-schema
dir: gen/resolver
package: resolver
models:
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Int:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Query 함수정의
query.graphql 이름으로 아래와 같이 생성합니다.
type Query {
# Todo
todo(where: TodoWhere!): Todo!
}
타입 정의
schema/type/todo.graphql - 데이타 타입
schema/input/tood.graphql - 입력 타입
schema/enum/tood.graphql - enum 타입
# Todo 할일정의
type Todo {
# 제목
title: String!
# 셜명
content: String!
# 타입
type: TodoType!
}
# 할일 타입
enum TodoType {
# 매일
DAYLY
# 매주
WEEKLY
}
# TodoWhere 조건
input TodoWhere {
todoType: TodoType!
}
4. grahql 코드 제너레이터 실행
go get -d github.com/99designs/gqlgen
cd graphql/todo && gqlgen
5. 메인 함수 & Graphql 서버 실행 구현
package main
import (
"context"
"flag"
"fmt"
"myservice/graphql/todo/gen"
"myservice/graphql/todo/gen/resolver"
"net/http"
"time"
graphqlHandler "github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/rs/cors"
"google.golang.org/grpc/metadata"
)
const servicePort = "9999"
func main() {
errc := make(chan error)
ctx := context.Background()
startHTTPServer(ctx, servicePort, errc)
fmt.Print("exit", <-errc)
}
// graphql server start
func startHTTPServer(
ctx context.Context,
port string,
errc chan error,
) {
var (
httpAddr = flag.String("http", ":"+port, "HTTP listen address")
readHeaderTimeout = 60 * time.Second
writeTimeout = 60 * time.Second
idleTimeout = 60 * time.Second
maxHeaderBytes = http.DefaultMaxHeaderBytes
queryPath = "/query"
graphqlPath = "/graphql"
)
// http 세팅
newResolver := resolver.NewResolver(
ctx,
)
middleware := authMiddleware(ctx, graphqlHandler.NewDefaultServer(
gen.NewExecutableSchema(gen.Config{Resolvers: &newResolver}),
))
mux := http.NewServeMux()
mux.Handle(queryPath, middleware)
mux.Handle(graphqlPath, playground.Handler("GraphQL playground", queryPath))
handler := cors.New(corsOption()).Handler(mux)
http := &http.Server{
Addr: *httpAddr,
Handler: handler,
ReadHeaderTimeout: readHeaderTimeout,
WriteTimeout: writeTimeout,
IdleTimeout: idleTimeout,
MaxHeaderBytes: maxHeaderBytes,
}
// 서버 스타트
go func() {
fmt.Printf(
"connect to http://localhost%s/graphql for GraphQL playground",
*httpAddr,
)
errc <- http.ListenAndServe()
}()
}
// auth token
func authMiddleware(ctx context.Context, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
md := metadata.Pairs("Authorization", r.Header.Get("Authorization"))
ctx = metadata.NewIncomingContext(ctx, md)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
}
// corsOption cors option
func corsOption() cors.Options {
return cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"},
AllowedHeaders: []string{
"Origin",
"Content-Type",
"Access-Control-Allow-Headers",
"DeviceInfo",
"Authorization",
"X-Requested-With",
},
AllowCredentials: false,
}
}
6. Todo 구현
todo/query.go
package todo
import (
"context"
"myservice/graphql/todo/gen/graphqlmodel"
"sync"
)
var (
instance *todo = nil
once sync.Once
)
type todo struct {
ctx context.Context
}
// Todo 인터페이스
type Todo interface {
Todo(ctx context.Context, where graphqlmodel.TodoWhere) (*graphqlmodel.Todo, error)
}
// NewTodo 서비스
func NewTodo(
ctx context.Context,
) Todo {
once.Do(func() {
instance = &todo{
ctx: ctx,
}
})
return instance
}
// Todo 조회
func (s *todo) Todo(
ctx context.Context,
where graphqlmodel.TodoWhere,
) (*graphqlmodel.Todo, error) {
return &graphqlmodel.Todo{
Title: "할일",
Content: "내일",
Type: graphqlmodel.TodoTypeDayly,
}, nil
}
Makefie 실행
make gen
make start
결과 화면

기타
소스에 대한 자세한 설명이 많이 빠져있어서 아쉽지만, 아래 다운로드를 통해서 실제 테스트해보며, graphql 데이타 스키마를 구현해 보는게 좋을듯 합니다.
DDD구현을 위한 Graphql 스키마를 설계합니다. 각각의 영역별로 폴더로 나누어서 구현합니다.
도메인 이벤트, 모델작성, 명령 command등등의 도메인 전문가와 함께 graphql 스키마를 구현하며 작업을 우선시 하면 좋을듯합니다.
generate과정을 통해서 실제 비즈니스를 구현하며, 특정 영역에 따른 MSA구현하면 좋을듯합니다.
다운로드 소스코드
'IT&Tech' 카테고리의 다른 글
Kubernetes환경의 Argo CD를 이용한 Gitops구현 적용 (0) | 2022.01.21 |
---|---|
API 인증프로세스 Istio Ingress Gateway, OAuth2-Proxy and Keycloak (0) | 2022.01.20 |
Kubernetes 소개 (2) | 2022.01.19 |
Istio service mesh 소개 & 설치방법 (0) | 2022.01.18 |
cert-manager를 이용한 ACME 무료인증 (0) | 2022.01.18 |