diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml
new file mode 100644
index 00000000..34f01d1f
--- /dev/null
+++ b/.github/workflows/deploy-dev.yml
@@ -0,0 +1,36 @@
+name: Deploy to Dev Environment
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - develop
+ paths:
+ - "server/**"
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ submodules: 'recursive'
+ - name: Deploy
+ uses: dagger/dagger-for-github@v5
+ env:
+ SSH_DEST: ${{ secrets.DEV_SSH_DEST }}
+ SSH_KEY: ${{ secrets.DEV_SSH_KEY }}
+ with:
+ version: 0.12.0
+ verb: call
+ module: ./cicd
+ args: >-
+ deploy
+ --source-dir=server
+ --profile=dev
+ --ssh-dest=env:SSH_DEST
+ --ssh-key=env:SSH_KEY
+ --target-path=~/repo/makevook/vook-deployment/dev
+ --version=${{ github.sha }}
+ --command="make deploy-api"
diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml
new file mode 100644
index 00000000..a0c3547b
--- /dev/null
+++ b/.github/workflows/deploy-prod.yml
@@ -0,0 +1,36 @@
+name: Deploy to Prod Environment
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ paths:
+ - "server/**"
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ submodules: 'recursive'
+ - name: Deploy
+ uses: dagger/dagger-for-github@v5
+ env:
+ SSH_DEST: ${{ secrets.PROD_SSH_DEST }}
+ SSH_KEY: ${{ secrets.PROD_SSH_KEY }}
+ with:
+ version: 0.12.0
+ verb: call
+ module: ./cicd
+ args: >-
+ deploy
+ --source-dir=server
+ --profile=prod
+ --ssh-dest=env:SSH_DEST
+ --ssh-key=env:SSH_KEY
+ --target-path=~/repo/makevook/vook-deployment/prod
+ --version=${{ github.sha }}
+ --command="make deploy-api"
diff --git a/.github/workflows/deploy-stag.yml b/.github/workflows/deploy-stag.yml
new file mode 100644
index 00000000..3b7fbbca
--- /dev/null
+++ b/.github/workflows/deploy-stag.yml
@@ -0,0 +1,36 @@
+name: Deploy to Stag Environment
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - release
+ paths:
+ - "server/**"
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ submodules: 'recursive'
+ - name: Deploy
+ uses: dagger/dagger-for-github@v5
+ env:
+ SSH_DEST: ${{ secrets.STAG_SSH_DEST }}
+ SSH_KEY: ${{ secrets.STAG_SSH_KEY }}
+ with:
+ version: 0.12.0
+ verb: call
+ module: ./cicd
+ args: >-
+ deploy
+ --source-dir=server
+ --profile=stag
+ --ssh-dest=env:SSH_DEST
+ --ssh-key=env:SSH_KEY
+ --target-path=~/repo/makevook/vook-deployment/stag
+ --version=${{ github.sha }}
+ --command="make deploy-api"
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..04b28663
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "devenv/sql"]
+ path = devenv/sql
+ url = git@github.com:makevook/vook-sql.git
+[submodule "api/src/test/resources/migrate/sql"]
+ path = api/src/test/resources/migrate/sql
+ url = git@github.com:makevook/vook-sql.git
+[submodule "server/api/src/test/resources/migrate/sql"]
+ path = server/api/src/test/resources/migrate/sql
+ url = git@github.com:makevook/vook-sql.git
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..b5cbba11
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+.PHONY:sql-update
+sql-update:
+ git submodule update --remote --merge
diff --git a/README.md b/README.md
index daf55198..648df155 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,28 @@
-# vook-server
\ No newline at end of file
+# Vook Server
+
+
+
+
+
+
+
+
+
+
+
+드래그앤드롭으로 용어의 의미를 가장 쉽고 빠르게 찾는 방법
+
+
+
+
+
+
+ Diagram •
+ Schema
+
+
+
diff --git a/cicd/.gitignore b/cicd/.gitignore
new file mode 100644
index 00000000..123d4997
--- /dev/null
+++ b/cicd/.gitignore
@@ -0,0 +1,2 @@
+/secrets
+/out
diff --git a/cicd/Makefile b/cicd/Makefile
new file mode 100644
index 00000000..5963b1b1
--- /dev/null
+++ b/cicd/Makefile
@@ -0,0 +1,46 @@
+VERSION := $(shell git describe --tags --always --dirty)
+
+.PHONY:deploy-dev
+deploy-dev:
+ dagger call -v deploy \
+ --source-dir=../server \
+ --profile=dev \
+ --ssh-dest=file:./secrets/dev/dest.txt \
+ --ssh-key=file:./secrets/dev/ssh.key \
+ --target-path=~/repo/makevook/vook-deployment/dev \
+ --version=${VERSION} \
+ --command="make deploy-api"
+
+.PHONY:deploy-stag
+deploy-stag:
+ dagger call -v deploy \
+ --source-dir=../server \
+ --profile=stag \
+ --ssh-dest=file:./secrets/stag/dest.txt \
+ --ssh-key=file:./secrets/stag/ssh.key \
+ --target-path=~/repo/makevook/vook-deployment/stag \
+ --version=${VERSION} \
+ --command="make deploy-api"
+
+.PHONY:deploy-prod
+deploy-prod:
+ dagger call -v deploy \
+ --source-dir=../server \
+ --profile=prod \
+ --ssh-dest=file:./secrets/prod/dest.txt \
+ --ssh-key=file:./secrets/prod/ssh.key \
+ --target-path=~/repo/makevook/vook-deployment/prod \
+ --version=${VERSION} \
+ --command="make deploy-api"
+
+.PHONY:build-jar
+build-jar:
+ dagger call -v build-api-jar --dir=../server --test --sub-module=api export --path out/api.jar
+
+.PHONY:build-image
+build-image: build-jar
+ dagger call -v build-api-image --jar-file=out/api.jar export --path out/api_linux_arm64.tar
+
+.PHONY:run-jar
+run-jar:
+ (cd out && java -jar api.jar)
diff --git a/cicd/dagger.json b/cicd/dagger.json
new file mode 100644
index 00000000..8cac4153
--- /dev/null
+++ b/cicd/dagger.json
@@ -0,0 +1,28 @@
+{
+ "name": "vook-server",
+ "sdk": "go",
+ "dependencies": [
+ {
+ "name": "docker",
+ "source": "github.com/purpleclay/daggerverse/docker@43c1c55dadf15afc9ba401dc59e04baaa3802cca"
+ },
+ {
+ "name": "dockerService",
+ "source": "github.com/aweris/daggerverse/docker@980888a31696001fe024dc38ff104368f4a1931d"
+ },
+ {
+ "name": "java",
+ "source": "github.com/seungyeop-lee/daggerverse/java@a5e511cb5adec1bf0b67ef8856d4191e61ab4711"
+ },
+ {
+ "name": "scp",
+ "source": "github.com/seungyeop-lee/daggerverse/scp@63f0f2d385768aa435474a9eec552750500899f2"
+ },
+ {
+ "name": "ssh",
+ "source": "github.com/seungyeop-lee/daggerverse/ssh@63f0f2d385768aa435474a9eec552750500899f2"
+ }
+ ],
+ "source": "dagger",
+ "engineVersion": "v0.12.0"
+}
diff --git a/cicd/dagger/.gitattributes b/cicd/dagger/.gitattributes
new file mode 100644
index 00000000..3a454933
--- /dev/null
+++ b/cicd/dagger/.gitattributes
@@ -0,0 +1,4 @@
+/dagger.gen.go linguist-generated
+/internal/dagger/** linguist-generated
+/internal/querybuilder/** linguist-generated
+/internal/telemetry/** linguist-generated
diff --git a/cicd/dagger/.gitignore b/cicd/dagger/.gitignore
new file mode 100644
index 00000000..7ebabcc1
--- /dev/null
+++ b/cicd/dagger/.gitignore
@@ -0,0 +1,4 @@
+/dagger.gen.go
+/internal/dagger
+/internal/querybuilder
+/internal/telemetry
diff --git a/cicd/dagger/go.mod b/cicd/dagger/go.mod
new file mode 100644
index 00000000..f3bc6359
--- /dev/null
+++ b/cicd/dagger/go.mod
@@ -0,0 +1,40 @@
+module dagger/vook-server
+
+go 1.22.2
+
+require (
+ github.com/99designs/gqlgen v0.17.49
+ github.com/Khan/genqlient v0.7.0
+ github.com/vektah/gqlparser/v2 v2.5.16
+ go.opentelemetry.io/otel v1.27.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
+ go.opentelemetry.io/otel/sdk v1.27.0
+ go.opentelemetry.io/otel/trace v1.27.0
+ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
+ golang.org/x/sync v0.7.0
+ google.golang.org/grpc v1.64.0
+)
+
+require (
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/go-logr/logr v1.4.1 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
+ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
+ github.com/sosodev/duration v1.3.1 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88
+ go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
+ go.opentelemetry.io/otel/log v0.3.0
+ go.opentelemetry.io/otel/metric v1.27.0 // indirect
+ go.opentelemetry.io/otel/sdk/log v0.3.0
+ go.opentelemetry.io/proto/otlp v1.3.1
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
+ google.golang.org/protobuf v1.34.1 // indirect
+)
diff --git a/cicd/dagger/go.sum b/cicd/dagger/go.sum
new file mode 100644
index 00000000..6fea81b9
--- /dev/null
+++ b/cicd/dagger/go.sum
@@ -0,0 +1,87 @@
+github.com/99designs/gqlgen v0.17.49 h1:b3hNGexHd33fBSAd4NDT/c3NCcQzcAVkknhN9ym36YQ=
+github.com/99designs/gqlgen v0.17.49/go.mod h1:tC8YFVZMed81x7UJ7ORUwXF4Kn6SXuucFqQBhN8+BU0=
+github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w=
+github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
+github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
+github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
+go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
+go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY=
+go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs=
+go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys=
+go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
+go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
+go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
+go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
+go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U=
+go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g=
+go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
+go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/cicd/dagger/main.go b/cicd/dagger/main.go
new file mode 100644
index 00000000..e1652111
--- /dev/null
+++ b/cicd/dagger/main.go
@@ -0,0 +1,183 @@
+package main
+
+import (
+ "context"
+ "dagger/vook-server/internal/dagger"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+type VookServer struct{}
+
+func (v *VookServer) BuildApiJar(
+ ctx context.Context,
+ // 빌드 대상의 디렉토리
+ dir *dagger.Directory,
+ // +optional
+ test bool,
+ // +optional
+ subModule string,
+) (*dagger.File, error) {
+ c := dag.Java().
+ Init().
+ WithGradleCache().
+ WithDir(dir).
+ Container()
+
+ if test {
+ var testCommand []string
+ if subModule == "" {
+ testCommand = []string{"./gradlew", "test"}
+ } else {
+ testCommand = []string{"./gradlew", fmt.Sprintf(":%s:test", subModule)}
+ }
+
+ _, err := c.
+ With(dag.DockerService().WithCacheVolume("docker-var/lib/docker").BindAsService).
+ WithExec(testCommand).
+ Sync(ctx)
+ if err != nil {
+ return nil, errors.New("test fail:" + err.Error())
+ }
+ }
+
+ var bootJarCommand []string
+ if subModule == "" {
+ bootJarCommand = []string{"./gradlew", "bootJar"}
+ } else {
+ bootJarCommand = []string{"./gradlew", fmt.Sprintf(":%s:bootJar", subModule)}
+ }
+
+ jarFile := c.
+ WithExec(bootJarCommand).
+ File("jar/api.jar")
+
+ return jarFile, nil
+}
+
+func (v *VookServer) BuildApiImage(
+ // jar 파일
+ jarFile *dagger.File,
+ // profile
+ // +optional
+ profile []string,
+) *dagger.File {
+ if profile == nil {
+ profile = []string{"default"}
+ }
+
+ dockerfile := `
+FROM eclipse-temurin:21-jre
+
+WORKDIR /app
+
+COPY app.jar app.jar
+
+ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=` + strings.Join(profile, ",") + `", "app.jar"]
+`
+ sourceDir := dag.Directory().
+ WithFile("app.jar", jarFile).
+ WithNewFile("Dockerfile", dockerfile)
+
+ return dag.Docker().
+ Build(sourceDir, dagger.DockerBuildOpts{
+ Platform: []dagger.Platform{"linux/arm64"},
+ }).
+ Save(dagger.DockerBuildSaveOpts{
+ Name: "api",
+ }).
+ File("api_linux_arm64.tar")
+}
+
+func (v *VookServer) SendImage(
+ ctx context.Context,
+ sshDest *dagger.Secret,
+ sshKey *dagger.Secret,
+ path string,
+ imageTar *dagger.File,
+) error {
+ sshDestText, err := sshDest.Plaintext(ctx)
+ if err != nil {
+ return err
+ }
+
+ _, err = dag.Scp().
+ Config(strings.TrimSpace(sshDestText)).
+ WithIdentityFile(sshKey).
+ FileToRemote(imageTar, dagger.ScpCommanderFileToRemoteOpts{
+ Target: path,
+ }).
+ Sync(ctx)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (v *VookServer) Apply(
+ ctx context.Context,
+ destination *dagger.Secret,
+ sshKey *dagger.Secret,
+ imageTar *dagger.File,
+ path string,
+ version string,
+ command string,
+) error {
+ destinationText, err := destination.Plaintext(ctx)
+ if err != nil {
+ return err
+ }
+
+ filename, err := imageTar.Name(ctx)
+ if err != nil {
+ return err
+ }
+
+ _, err = dag.SSH().
+ Config(strings.TrimSpace(destinationText)).
+ WithIdentityFile(sshKey).
+ Command(
+ fmt.Sprintf(`
+cd %s
+API_FILENAME=%s API_VERSION=%s %s
+`, path, filename, version, command),
+ ).
+ Sync(ctx)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (v *VookServer) Deploy(
+ ctx context.Context,
+ sourceDir *dagger.Directory,
+ profile string,
+ sshDest *dagger.Secret,
+ sshKey *dagger.Secret,
+ targetPath string,
+ version string,
+ command string,
+) error {
+ jarFile, err := v.BuildApiJar(ctx, sourceDir, true, "api")
+ if err != nil {
+ return err
+ }
+
+ imageTar := v.BuildApiImage(jarFile, []string{"default", profile})
+
+ err = v.SendImage(ctx, sshDest, sshKey, targetPath, imageTar)
+ if err != nil {
+ return err
+ }
+
+ err = v.Apply(ctx, sshDest, sshKey, imageTar, targetPath, version, command)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/devenv/Makefile b/devenv/Makefile
new file mode 100644
index 00000000..1481badb
--- /dev/null
+++ b/devenv/Makefile
@@ -0,0 +1,15 @@
+.PHONY:up
+up:
+ docker compose up -d --build
+
+.PHONY:down
+down:
+ docker compose down
+
+.PHONY:log
+log:
+ docker compose logs -f
+
+.PHONY:clean
+clean:
+ docker compose down -v
diff --git a/devenv/compose.yml b/devenv/compose.yml
new file mode 100644
index 00000000..52409e56
--- /dev/null
+++ b/devenv/compose.yml
@@ -0,0 +1,58 @@
+services:
+ db:
+ build:
+ context: .
+ dockerfile: mariadb.Dockerfile
+ volumes:
+ - ./db/initdb.d:/docker-entrypoint-initdb.d
+ - db-data:/var/lib/mysql
+ environment:
+ MYSQL_PORT: 3306
+ MYSQL_ROOT_PASSWORD: rootPw
+ MYSQL_DATABASE: vook
+ MYSQL_USER: user
+ MYSQL_PASSWORD: userPw
+ TZ: Asia/Seoul
+ ports:
+ - "3307:3306"
+ mem_limit: 300m
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "healthcheck.sh", "--connect", "--innodb_initialized" ]
+ start_period: 3s
+ start_interval: 2s
+ interval: 1m
+ timeout: 5s
+ retries: 3
+ db-init:
+ image: migrate/migrate:v4.17.1
+ volumes:
+ - ./sql:/sql
+ command:
+ - -source=file:///sql
+ - -database=mysql://user:userPw@tcp(db:3306)/vook
+ - up
+ depends_on:
+ db:
+ condition: service_healthy
+ meilisearch:
+ image: getmeili/meilisearch:v1.9.0
+ volumes:
+ - meili_data:/meili_data
+ ports:
+ - "7700:7700"
+ environment:
+ - MEILI_ENV=development
+ - MEILI_MASTER_KEY=aSampleMasterKey
+ mem_limit: 1000m
+ restart: unless-stopped
+ redis:
+ image: redis:7.2.5
+ ports:
+ - "6379:6379"
+ mem_limit: 100m
+ restart: unless-stopped
+
+volumes:
+ db-data: { }
+ meili_data: { }
diff --git a/devenv/db/conf.d/my.cnf b/devenv/db/conf.d/my.cnf
new file mode 100644
index 00000000..c6314654
--- /dev/null
+++ b/devenv/db/conf.d/my.cnf
@@ -0,0 +1,11 @@
+[client]
+default-character-set = utf8mb4
+
+[mysql]
+default-character-set = utf8mb4
+
+[mysqld]
+character-set-client-handshake = FALSE
+character-set-server = utf8mb4
+collation-server = utf8mb4_unicode_ci
+lower_case_table_names = 1
diff --git a/devenv/db/initdb.d/.gitkeep b/devenv/db/initdb.d/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/devenv/mariadb.Dockerfile b/devenv/mariadb.Dockerfile
new file mode 100644
index 00000000..0c53909c
--- /dev/null
+++ b/devenv/mariadb.Dockerfile
@@ -0,0 +1,5 @@
+FROM mariadb:10.11.8
+
+# windows에서 volume mount 할 경우, 파일 권한이 777로 변경되는 문제가 있어서 아래와 같은 작업을 추가 함
+COPY db/conf.d/my.cnf /etc/mysql/conf.d/my.cnf
+RUN chmod 644 /etc/mysql/conf.d/my.cnf
diff --git a/devenv/sql b/devenv/sql
new file mode 160000
index 00000000..4704fdcc
--- /dev/null
+++ b/devenv/sql
@@ -0,0 +1 @@
+Subproject commit 4704fdcc9389d9a790e692b7a82d579e3aa4d243
diff --git a/docs/.tbls.yml b/docs/.tbls.yml
new file mode 100644
index 00000000..1aec44b5
--- /dev/null
+++ b/docs/.tbls.yml
@@ -0,0 +1,6 @@
+dsn: mariadb://user:userPw@localhost:3307/vook
+
+docPath: schema
+
+er:
+ format: mermaid
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..56f7150d
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,3 @@
+.PHONY:tbls
+tbls:
+ tbls doc --rm-dist
diff --git a/docs/diagram/README.md b/docs/diagram/README.md
new file mode 100644
index 00000000..ca0fdd59
--- /dev/null
+++ b/docs/diagram/README.md
@@ -0,0 +1,23 @@
+# Diagram
+
+> Generated by [Spring Modulith](https://spring.io/projects/spring-modulith)
+
+## Overview
+
+![Overview](components.png)
+
+## User Web
+
+![User Web](module-vook.server.api.web.user.png)
+
+## Term Web
+
+![Term Web](module-vook.server.api.web.term.png)
+
+## Vocabulary Web
+
+![Vocabulary Web](module-vook.server.api.web.vocabulary.png)
+
+## Demo Web
+
+![Demo Web](module-vook.server.api.web.demo.png)
diff --git a/docs/diagram/components.png b/docs/diagram/components.png
new file mode 100644
index 00000000..3f4bfcdc
Binary files /dev/null and b/docs/diagram/components.png differ
diff --git a/docs/diagram/module-vook.server.api.web.demo.png b/docs/diagram/module-vook.server.api.web.demo.png
new file mode 100644
index 00000000..85a8099e
Binary files /dev/null and b/docs/diagram/module-vook.server.api.web.demo.png differ
diff --git a/docs/diagram/module-vook.server.api.web.term.png b/docs/diagram/module-vook.server.api.web.term.png
new file mode 100644
index 00000000..7c5750da
Binary files /dev/null and b/docs/diagram/module-vook.server.api.web.term.png differ
diff --git a/docs/diagram/module-vook.server.api.web.user.png b/docs/diagram/module-vook.server.api.web.user.png
new file mode 100644
index 00000000..038b6bf4
Binary files /dev/null and b/docs/diagram/module-vook.server.api.web.user.png differ
diff --git a/docs/diagram/module-vook.server.api.web.vocabulary.png b/docs/diagram/module-vook.server.api.web.vocabulary.png
new file mode 100644
index 00000000..84494228
Binary files /dev/null and b/docs/diagram/module-vook.server.api.web.vocabulary.png differ
diff --git a/docs/introduction/240622/README.md b/docs/introduction/240622/README.md
new file mode 100644
index 00000000..49790bf6
--- /dev/null
+++ b/docs/introduction/240622/README.md
@@ -0,0 +1,207 @@
+---
+theme: uncover
+class:
+ - lead
+marp: true
+style: |
+ section {
+ font-size: 25px;
+ }
+ section strong {
+ background-color: yellow;
+ padding: 2px;
+ }
+ section h2 {
+ margin: 15px 0 15px;
+ }
+---
+
+![bg center:50% 50%](./assets/logo.svg)
+
+---
+
+# 용어의 의미를 가장 쉽고 빠르게 찾는 방법
+
+---
+
+### Target
+
+용어집을 만들고 사용하길 원하는
+개인을 위한 Utility 서비스
+
+### Main Problem
+
+원하는 결과를 찾기 어려움
+
+### How to Solve
+
+**드래그앤드롭**으로 쉽게 찾기
+
+![bg right:58% 90%](./assets/drag-and-drop.png)
+
+---
+
+# 프로젝트 구조 - 레이아웃
+
+![w:850](./assets/layout.png)
+
+---
+
+# 프로젝트 구조 - root (tree)
+
+```
+.
+├── api # Spring API 서버
+├── cicd # dagger 모듈
+├── devenv # 개발에 필요한 외부 의존성 (docker)
+└── docs # 관련 문서
+```
+
+---
+
+# 프로젝트 구조 - api main package (tree)
+
+```
+.
+└── vook
+ └── server
+ └── api
+ ├── app # 앱 파트
+ │ ├── common # 앱 파트 공통
+ │ ├── contexts # 도메인 별 비즈니스 로직
+ │ ├── crosscontext # 2개 이상의 도메인이 협력하는 비즈니스 로직
+ │ └── infra # 외부 연동 어뎁터
+ ├── config # 서버 설정 (@Configuration)
+ ├── devhelper # 개발 편의성을 위한 컴포넌트 모음 (운영에서는 미사용 될 로직)
+ ├── helper # 헬퍼 함수
+ └── web # 웹 파트
+ ├── auth # 인증, 인가
+ ├── common # 웹 파트 공통
+ ├── routes # 웹 라우터 (@RestController)
+ └── swagger # Swagger 공통 설정
+```
+
+---
+
+# 프로젝트 구조 - api main package (layout)
+
+![w:700](./assets/dep.png)
+
+---
+
+## 도입 기술 - Swagger Schema 재사용
+
+```java
+// api/src/main/java/vook/server/api/web/swagger/GlobalOpenApiCustomizerImpl.java
+public class GlobalOpenApiCustomizerImpl implements GlobalOpenApiCustomizer {
+ @Override
+ public void customise(OpenAPI openApi) {
+ openApi.getComponents()
+ .addSchemas(getKey(ComponentRefConsts.Schema.COMMON_API_RESPONSE), new Schema