Skip to content

๐ŸŒŸ A comment server of KHU community service

License

Notifications You must be signed in to change notification settings

khu-dev/khumu-comment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

KHUMU Comment API Server

khumu-comment๋Š” MSA๋กœ ๊ฐœ๋ฐœ์ค‘์ธ khumu์˜ comment ๊ด€๋ จ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ์„œ๋ฒ„์ด๋ฉฐ Echo ๋ผ๋Š” Golang์˜ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐœ๋ฐœ๋˜๊ณ  ์žˆ๋‹ค. ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก ์€ TDD์ด๋ฉฐ Clean architecture๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ ์ค‘์ด๋‹ค.

API Documentation: https://documenter.getpostman.com/view/13384984/TVsvfkxs

โš™๏ธ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•

์„ค์ • ํŒŒ์ผ ์ ์šฉ์€ Go์˜ ์œ ๋ช…ํ•œ ์„ค์ • ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ spf13/viper์„ ์ฟ ๋ฎค ์„œ๋น„์Šค์—์„œ ์ข€ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•œ ๋ฒˆ ๋ž˜ํ•‘ํ•œ umi0410/ezconfig๋ฅผ ์ด์šฉํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” config/default.yaml์„ ์ด์šฉํ•˜๋ฉฐ ํ•„์š”์— ๋”ฐ๋ผ KHUMU_ prefix๊ฐ€ ๋ถ™์€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด overrideํ•  ์ˆ˜๋„ ์žˆ๊ณ  config/{{KHUMU_ENVIRONMENT}}.yaml์˜ ์„ค์ •ํŒŒ์ผ๋กœ overrideํ•  ์ˆ˜ ์žˆ๋‹ค. ์ž์„ธํ•œ ์‚ฌํ•ญ์€ umi0410/ezconfig์—์„œ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

KHUMU_CONFIG_PATH ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด config ์ด์™ธ์˜ config file์ด ์œ„์น˜ํ•œ path๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹จ์ˆœํžˆ config๋ผ๋Š” ์ƒ๋Œ€ ๊ฒฝ๋กœ๋งŒ์„ ์ด์šฉํ•˜๋ฉด ๊ฐ„ํ˜น test code๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๊ณผ์ •์— test code๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์œ„์น˜์— ๋”ฐ๋ผ working directory๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด์„œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ config ํŒŒ์ผ๋“ค์„ ์ฐพ์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

(์„ค์ • ์˜ˆ์‹œ: KHUMU_CONFIG_PATH=/home/jinsu/khumu-comments/config)

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •

  • KHUMU_SECRET: ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด jwt๋ฅผ verifyํ•  secret์„ ์„ค์ •ํ•œ๋‹ค.
  • KHUMU_CONFIG_PATH : ์ƒ๋Œ€ ๊ฒฝ๋กœ config ์ด์™ธ์˜ ๊ฒฝ๋กœ์—์„œ config file์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
  • ์ถ”๊ฐ€์ ์œผ๋กœ KHUMU_ prefix๋ฅผ ํ†ตํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ Config์˜ ํ•„๋“œ๋“ค์„ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŽ ๋ฐฐํฌ (CI/CD)

cicd.png

  1. ํ˜„์žฌ๋Š” Github Action์—์„œ ๋งค ํ‘ธ์‹œ๋งˆ๋‹ค ์ „์ฒด unit test๋ฅผ ์ง„ํ–‰
  2. ํ…Œ์ŠคํŠธ ๋ชจ๋‘ ํ†ต๊ณผ ์‹œ docker image ๋นŒ๋“œ ํ›„ ์ƒˆ๋กœ ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋ฅผ private repository๋กœ ๊ด€๋ฆฌ๋˜๋Š” khu-dev/devops ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ๋ฐ˜์˜
  3. ArgoCD ๋ฅผ ํ†ตํ•ด ์ž๋™ ๋ฐฐํฌํ•˜๋ฉฐ ์ด๋•Œ kustomize๋ฅผ ํ†ตํ•ด ์ƒˆ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๊ฐ€ ์ ์šฉ๋จ

๐Ÿ’ฏ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•

ํ”„๋กœ์ ํŠธ ๋‚ด์˜ ๋ชจ๋“  ์œ ๋‹› ํ…Œ์ŠคํŠธ

# ํ”„๋กœ์ ํŠธ์˜ ๋ฃจํŠธ ๊ฒฝ๋กœ์—์„œ
$ go test ./...
# ํ˜น์€ ์ž์„ธํ•œ ๋กœ๊ทธ๋ฅผ ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด
$ go test ./... -v
# ํ˜น์€ ๊ฐ„๋‹จํžˆ
$ make test

TDD ์‹์˜ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ• - ์„  ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ. ํ›„ ๊ฐœ๋ฐœ/๋ฆฌํŒฉํ† ๋ง

test๋Š” MySQL์ด ์•„๋‹Œ SQLite3๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ด์šฉํ•œ๋‹ค.

TODO: Service layer ํ…Œ์ŠคํŠธ ์‹œ์—๋Š” Repository layer๋‚˜ ์™ธ๋ถ€ ๋ชจ๋“ˆ๋“ค์„ Mockingํ•˜์—ฌ ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ง„ํ–‰ํ•˜๊ธฐ. ๊ธฐ์กด์—๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ์—๋„ SQLite3๊ณผ DAO๋ฅผ ์ด์šฉํ•ด ์˜์†ํ™”๊นŒ์ง€ ์ง„ํ–‰ํ•˜๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์ง„ํ–‰ํ–ˆ์œผ๋‚˜ ํ…Œ์ŠคํŠธ๊ฐ€ DAO์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ๊ฐ–๋‹ค๋ณด๋‹ˆ ๋ถˆํ•„์š”ํ•œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋“ค์„ ์ƒ์„ฑํ•ด์ค˜์•ผํ•˜๋Š” ๋ถˆํŽธ์ด ์žˆ์—ˆ๊ณ  ์ด๋กœ ์ธํ•ด ๋งŽ์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ์— ๋ถˆํŽธ์„ ๋Š๋‚Œ

  • ๊ฐœ๋ฐœํ•  ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ตœ์†Œํ•œ์˜ ๊ธฐ๋Šฅ๊ณผ ์‚ฌ์šฉํ•˜๊ณ ์žํ•˜๋Š” ํƒ€์ž…, ๋„ค์ด๋ฐ๋“ฑ์„ ๋ฏธ๋ฆฌ xxx_test.go ํŒŒ์ผ์— ์ž‘์„ฑํ•œ๋‹ค.
  • ํ•ด๋‹น ๊ธฐ๋Šฅ์„ xxx.go ์—์„œ ๊ตฌํ˜„ํ•œ๋‹ค.
  • ๊ตฌํ˜„ํ•ด๋‚˜๊ฐ€๋ฉด์„œ ์•„๋ž˜์˜ ์ปค๋งจ๋“œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋‚ด์šฉ์— ๋Œ€ํ•ด์„œ๋งŒ ๊ฐ„๋‹จํžˆ ํ…Œ์ŠคํŠธ ํ•ด๋ณธ๋‹ค.
  • ๊ตฌํ˜„์ด ๋๋‚˜๋ฉด ์ „์ฒด ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๊ณ , ์ข€ ๋” ๋‚˜์€ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฆฌํŒฉํ† ๋งํ•œ๋‹ค.
  • ๋ฆฌํŒฉํ† ๋ง์ด ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ์ƒˆ๋กœ ๊ตฌํ˜„ํ•  ๋‚ด์šฉ์— ๋Œ€ํ•œ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋ฉด ์ „์ฒด ์„œ๋ฒ„๋ฅผ ์žฌ์‹คํ–‰ํ•˜๋ฉด์„œ ๊ฐ™์€ ์ž‘์—…์„ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ถˆํŽธ์„ ์—†์•จ ์ˆ˜ ์žˆ๊ณ , ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ „์ฒด ๊ณ„์ธต์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ ๊ฐ ๊ณ„์ธต๋ณ„๋กœ ๋ฏธ๋ฆฌ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

e.g. TDD ๋ฐฉ์‹์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  ๊ฐœ๋ฐœํ•˜๋ฉด ์šฐ์„  repository ๊ณ„์ธต๋งŒ ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์€๋ฐ, http ๊ณ„์ธต ๊นŒ์ง€ ๋‹ค ๊ตฌํ˜„ํ•œ ๋’ค ์„œ๋ฒ„๋ฅผ ๋„์šฐ๊ณ  ๊ทธ ์—”๋“œํฌ์ธํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๋กœ๊ทธ๋ฅผ ๊ด€์ฐฐํ•˜๋ฉฐ ๊ฐœ๋ฐœ์„ ํ•ด์•ผํ•˜์ง€๋งŒ, TDD์™€ Unit test๋ฅผ ์ด์šฉํ•ด ๊ฐœ๋ฐœํ•˜๋ฉด unit test์˜ ๊ฒฐ๊ณผ๋งŒ ๋ณด๊ณ ๋„ ๊ฐœ๋ฐœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

# ๊ฐœ๋ฐœ ์ค‘์ธ ํŒŒ์ผ์ด ์†ํ•œ ํŒจํ‚ค์ง€์˜ ๊ฒฝ๋กœ์— ๋Œ€ํ•ด ์‹คํ–‰ํ•˜๊ณ ์žํ•˜๋Š” ํ•จ์ˆ˜๋ช…์„ ์ „๋‹ฌํ•œ๋‹ค.
# ์ด๋•Œ TestSetUp์—์„œ Initialize ๊ด€๋ จํ•œ ๋‚ด์šฉ๋„ ํ…Œ์ŠคํŠธํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ๊ธฐ๋•Œ๋ฌธ์— TestSetUp๋„ ๊ฐ™์ด ์ „๋‹ฌํ•œ๋‹ค.
$ go test ./repository/ -run TestSetUp TestLikeCommentRepositoryGorm_Create -v

๐Ÿ“š ๊ฐœ๋ฐœ ํŒ ๋ฐ ๋ฉ”๋ชจ

  • go test ๋Œ€์‹  gotest๋ฅผ ์ด์šฉํ•˜๋ฉด ์ข€ ๋” ๊ฐ€์‹œ์„ฑ ์ข‹๊ฒŒ test๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Jetbrains์‚ฌ์˜ GoLand๋ฅผ ์ด์šฉํ•˜๋ฉด IDE์—์„œ ์ข€ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์›ํ•˜๋Š” unit test๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • {"message": "Not Found"} ์‘๋‹ต์„ ๋ฐ›๋Š” ๊ฒฝ์šฐ echo ๊ฐ€ ํ•ด๋‹น ๊ฒฝ๋กœ์— ๋Œ€ํ•ด route ํ•  ์ˆ˜ ์—†์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ์ฃผ๋กœ ์ฃผ์†Œ์˜ ๋งจ ๋ / ์˜ ์ฐจ์ด์ธ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ด ๋ ˆํฌ์˜ ์ปจ๋ฒค์…˜์€ ๋งจ ๋’ค์— / ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ๋ณธ์œผ๋กœํ•œ๋‹ค.

๐Ÿš€ ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ์„ฑ ๋ฐ ์›์น™

1. clean architecture์ ์ธ ์‚ฌ๊ณ ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ ์ ˆํžˆ ์ ์šฉํ•ด๋‚˜๊ฐ€์ž

dependencies.png

๊ฐ€์žฅ ์ƒ์œ„ ๊ณ„์ธต๋ถ€ํ„ฐ ๊ฐ€์žฅ ํ•˜์œ„ ๊ณ„์ธต, ๊ทธ๋ฆฌ๊ณ  ๊ณ„์ธต๊ณผ ๋…๋ฆฝ๋œ config๋‚˜ container ์ˆœ์œผ๋กœ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • model ์—ญํ• ์„ ํ•˜๋Š” ent/schema
    • khumu์˜ comment๋ผ๋Š” ๋„๋ฉ”์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    • ์•„๋ฌด๋Ÿฐ ๋‹ค๋ฅธ ๊ณ„์ธต๋„ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š” ์ตœ์ƒ์œ„ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.
    • DB Table์— ํ™œ์šฉ๋˜๊ฑฐ๋‚˜ API Response ํฌํ•จ๋˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด ๊ณ„์ธต์€ ํ•˜์œ„ ๊ณ„์ธต๋“ค์ด ์ž์‹ ์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ „ํ˜€ ์•Œ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • usecase ํ˜น์€ service
    • ๋Œ€๋ถ€๋ถ„์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ด๊ณณ์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.
      • e.g. ๋Œ“๊ธ€ ์ƒ์„ฑ ์‹œ message queue์— message๋ฅผ publish. ๋‹จ ์ด message queue๋Š” ์‚ฌ์‹ค์ƒ kafka๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ๊ณ , sqs, sns, redis ๋“ฑ์ด ๋  ์ˆ˜๋„ ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ตฌ์ฒดํ™”๋œ ๊ฒƒ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์ถ”์ƒ์ ์ธ ๊ฒƒ์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (ํ•˜์ง€๋งŒ ํ˜„์žฌ๋Š” ๋‹ค๋ฅธ message queue๋ฅผ ์‚ฌ์šฉํ•  ๊ณ„ํš์ด ์—†์–ด SNS(+SQS์˜ ์กฐํ•ฉ)์— ์ง์ ‘ ์˜์กดํ•ด ๊ฐœ๋ฐœ ์ค‘์ด๋‹ค.)
      • e.g. ์ต๋ช… ๋Œ“๊ธ€์˜ ์ž‘์„ฑ์ž๊ฐ€ ๋ณธ์ธ์ด๋ฉด is_author: true๋กœ ๋ณ€ํ™˜
      • e.g. ์ต๋ช… ๋Œ“๊ธ€์˜ ๊ฒฝ์šฐ ์ž‘์„ฑ์ž์˜ username์™€ nickname์„ ๊ฐ์ถฅ๋‹ˆ๋‹ค.
    • model๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ˆœ์ˆ˜ ์šฐ๋ฆฌ ๋„๋ฉ”์ธ์˜ ์ฝ”๋“œ๋กœ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ•˜์œ„ ๊ณ„์ธต์ธ repository์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜์กด์„ฑ ์—ญ์ „ ์›๋ฆฌ(DIP)์— ์˜ํ•ด ํ•˜์œ„ ๊ณ„์ธต์˜ ๊ตฌํ˜„์ฒด์— ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ถ”์ƒ์ ์ธ repository interface์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.
    • ํ•˜์œ„ ๊ณ„์ธต์ธ externalํ•œ message queueํ˜น์€ aws sns์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. repository์™€ ๊ฐ™์ด ์˜์กด์„ฑ ์—ญ์ „ ์›๋ฆฌ์— ์˜ํ•ด ๊ตฌํ˜„์ฒด์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์ถ”์ƒ์ ์ธ interface์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. interface์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ง„ํ–‰ ์‹œ mocking์„ ์ ์ ˆํžˆ ์ด์šฉํ•˜๊ธฐ ํŽธํ•ฉ๋‹ˆ๋‹ค.
  • repository (์ผ๋ฐ˜์ ์œผ๋กœ DAO๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ณ„์ธต)
    • ์™ธ๋ถ€ Data source์™€ ์ง์ ‘ ์ž‘์—…์„ ํ•˜๋Š” ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.
    • interface์™€ ๊ทธ์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ์ •์˜ํ•จ์œผ๋กœ์จ ์œ ์—ฐํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. (๋‹คํ˜•์„ฑ๊ณผ ์˜์กด์„ฑ ์—ญ์ „)
      • inferface๋ฅผ ์ •์˜ํ•จ์œผ๋กœ์จ MySQL, SQLite3, Memory์˜ array๋‚˜ map ๊ทธ ์–ด๋–ค ๊ฑธ ์‚ฌ์šฉํ•˜๋“  ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • repository๋ฅผ ์ด์šฉํ•˜๋Š” ๊ณ„์ธต์€ ์ง์ ‘ ๊ตฌํ˜„์ฒด๋ฅผ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  interface๋งŒ์„ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌํ˜„์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
    • ๊ณผ๊ฑฐ gorm์„ ์ด์šฉํ•  ๋• repository ์ž‘์—…์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋‹ค๋ณด๋‹ˆ ์œ„์˜ ์ทจ์†Œ์„ ์œผ๋กœ ํ‘œ์‹œ๋œ ์ž‘์—…๋“ค์ด ํ•„์š”ํ–ˆ์ง€๋งŒ ํ˜„์žฌ๋Š” ent/ent๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๊ฒŒ ๋˜๋ฉด์„œ repository ๊ณ„์ธต์— ๋Œ€ํ•œ ์ถ”์ƒํ™”๊ฐ€ ์ž๋™์œผ๋กœ ์ง„ํ–‰๋˜๊ณ  ํ…Œ์ŠคํŠธ ๋˜ํ•œ ๊ฑฐ์˜ ํ•„์š” ์—†์–ด์กŒ์Œ.
  • http
    • ์ฃผ๋กœ http ํ†ต์‹  ์ž์ฒด์— ๋Œ€ํ•œ ๋กœ์ง์„ ๋‹ด๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. repository์™€ ํ•จ๊ป˜ ๊ฐ€์žฅ ํ•˜์œ„ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.
    • Router, Middleware, Authentication, Authorization ์™€ ๊ฐ™์€ ์ž‘์—…์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
    • struct => json ์œผ๋กœ marshal ํ•œ ๋’ค ๊ทธ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Response๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋กœ์ง์„ ๋‹ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. (e.g. Comment struct๋ฅผ ๋ฐ›์•„์„œ json์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค Response์˜ body๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ๋กœ ์š”์ฒญ์— ๋Œ€ํ•œ ์ž‘์—…์„ usecase๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • container
    • ์ปจํ…Œ์ด๋„ˆ๋Š” ์œ„์˜ ๋ชจ๋“  ๊ณ„์ธต๋“ค๊ณผ ๋‹ฌ๋ฆฌ ์ƒ์œ„ ๊ณ„์ธต, ํ•˜์œ„ ๊ณ„์ธต์˜ ๊ฐœ๋…์„ ๊ฐ–์ง€ ์•Š๊ณ  ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ด€๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.
      • ๊ฐœ๋ฐœ์ž๋Š” ์ˆ˜์ž‘์—…์œผ๋กœ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์ฃผ๊ฑฐ๋‚˜, struct๋ฅผ ์ƒ์„ฑํ•  ํ•„์š” ์—†์ด container๊ฐ€ type์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž์‹ (container)์— ํ•ด๋‹น ํƒ€์ž…์˜ ๋ณ€์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋ฉด ๊ทธ๊ฒƒ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒํ•ด์ฃผ๊ณ , ์—†๋‹ค๋ฉด ์ƒ์„ฑํ•œ ๋’ค ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
    • DI framework์ธ dig๊ฐ€ ์˜์กด์„ฑ ์ฃผ์ž…์„ ์ œ์–ดํ•œ๋‹ค๋Š” ๋ฉด์—์„œ IoC(์ œ์–ด์˜ ์—ญ์ „)์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
      • ํ˜„์žฌ๋Š” uber์˜ dig ํŒจํ‚ค์ง€๋ฅผ ์˜์กด์„ฑ ์ฃผ์ž… ํŒจํ‚ค์ง€๋กœ ์‚ฌ์šฉ ์ค‘์ž…๋‹ˆ๋‹ค. ๊ตฌ๊ธ€์˜ wire ๊ฐ€ ๊ฝค ์œ ๋ช…ํ•œ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๊ฐ€๋…์„ฑ์„ ํ•ด์น  ๊ฒƒ ๊ฐ™๊ณ , ์œ ์—ฐํ•˜์ง€ ์•Š์€ ๋“ฏํ•˜์—ฌ ๋ฐฐ์ œํ–ˆ์Šต๋‹ˆ๋‹ค. uber์˜ fx ๋Š” dig ๋ฅผ ํ•œ ๋‹จ๊ณ„ ๋” ๊ฐ์‹ผ ํŒจํ‚ค์ง€์ธ๋“ฏํ•œ๋ฐ, ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง€๋Š” ๋Š๋‚Œ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.
    • IoC Container์™€ ๊ด€๋ จ๋œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.
  • config : ํ”„๋กœ๊ทธ๋žจ์— ๋Œ€ํ•œ ์„ค์ • ์ •๋ณด๋‚˜ ๊ทธ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ž‘์—…์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

2. TDD(Test Driven Development)๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœํ•˜์ž.

  • ํฐ ์žฅ์ ๋“ค

    • ์ง€์†์ ์ธ ๊ฐœ๋ฐœ์— ๋Œ€ํ•œ ์‹ ๋ขฐ์™€ ์•ˆ์ •์„ฑ์ด ์ƒ์Šนํ•˜๊ณ , ์ด๋Š” ์ƒ์‚ฐ์„ฑ์œผ๋กœ๋„ ์—ฐ๊ฒฐ๋œ๋‹ค.
    • ๋˜ํ•œ ๋‹น์žฅ์˜ ๊ฐœ๋ฐœ์—์„œ๋„ unit test๋ฅผ ํ†ตํ•ด ๊ณ„์ธต์„ ๋‚˜๋ˆ„์–ด ๊ฐœ๋ฐœํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋•Œ๋ฌธ์— ์ƒ์‚ฐ์„ฑ์ด ์ฆ๊ฐ€๋œ๋‹ค.
  • ์›๋ž˜๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ, test code๋ฅผ ์งœ๊ฒŒ ๋˜๋ฉด์„œ ์ˆ˜๋™์œผ๋กœ ์˜์กด์„ฑ์„ ๋„ฃ๋Š” ๊ฒƒ์ด ๋ฒˆ๊ฑฐ๋กญ๊ธฐ๋„ ํ•˜๊ณ  ๊ฐ€๋…์„ฑ๋„ ์•ˆ ์ข‹์€ ๊ฒƒ ๊ฐ™์•„ ์˜์กด์„ฑ ์ฃผ์ž… ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

  • Mocking ํ•˜๋Š” ๊ฒฝ์šฐ

    • struct ํ˜• ์ธ์ž๊ฐ€ ์•„๋‹Œ interface ํ˜• ์ธ์ž๋ฅผ ์ด์šฉํ•˜๋ฉด ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ๋•Œ mock type์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • mock type์„ ์ด์šฉํ•˜๋ฉด ํ•˜์œ„ ๊ณ„์ธต์˜ ๋‚ด์šฉ๊ณผ ๋…๋ฆฝ๋˜๊ฒŒ ํ•ด๋‹น ๊ณ„์ธต๋งŒ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • mock type์„ ์ด์šฉํ•˜๋ฉด ํ•˜์œ„ ๊ณ„์ธต์˜ ํ•˜์œ„ ๊ณ„์ธต์— ๋Œ€ํ•œ ์˜์กด์„ฑ, ๊ทธ ํ•˜์œ„ ๊ณ„์ธต์˜ ๋” ํ•˜์œ„ ๊ณ„์ธต์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ๋ชจ๋‘ ์ฃผ์ž…ํ•ด ์ค„ ํ•„์š” ์—†์ด ๋‚ด๊ฐ€ ์ง์ ‘ ํ•„์š”ํ•œ ๊ณ„์ธต๋งŒ ์ฃผ์ž…ํ•˜๋ฉด ๋œ๋‹ค๋Š” ์ ์ด ํŽธ๋ฆฌํ•˜๋‹ค.
    • ๋‹ค๋งŒ ํ•˜์œ„ ๊ณ„์ธต์„ ํ‰๋‚ด๋ƒˆ๋‹ค๋Š” ์ ์—์„œ ์‹ค์ œ ํ•˜์œ„ ๊ณ„์ธต์˜ ๋™์ž‘๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ฉด์ด ํ•ด๋‹น ๊ณ„์ธต์˜ ํ…Œ์ŠคํŠธ์˜ ์ •ํ™•์„ฑ์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ๋‹ค.
    • ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ด ์˜คํžˆ๋ ค mock methods๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํŽธ๋ฆฌํ•œ ๊ฒฝ์šฐ๋„ ๋งŽ๋‹ค.
  • ๊ฐ Test ๋ณ„ ๋…๋ฆฝ์„ฑ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜์ž.

    • Java Spring์˜ BeforeEach์™€ AfterEach์—์„œ ์•„์ด๋””์–ด๋ฅผ ์–ป์–ด B์™€ A๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ๋“ค์€ ๋ชจ๋‘ ์•„๋ž˜์™€ ๊ฐ™์ด B์™€ A๋ฅผ ์ด์šฉํ•ด Set up๊ณผ clean up์„ ์ง„ํ–‰ํ•œ๋‹ค.

      func TestFoo(t *testing.T) {
          B()
          defer A()
          // some test scenarios.
      }
  • (๊ณ ๋ฏผ ์ค‘) ๋ถˆํ•„์š”ํ•œ ์ฐธ์กฐ ์ œ์•ฝ ์กฐ๊ฑด ์ œ๊ฑฐ

    • ํ˜„์žฌ๋Š” ๋Œ“๊ธ€ ์—”ํ‹ฐํ‹ฐ์— ์ฐธ์กฐ ์ œ์•ฝ ์กฐ๊ฑด์œผ๋กœ ์ž‘์„ฑ์ž๋Š” KhumuUser, ๊ฒŒ์‹œ๊ธ€์€ Article์„ ์ฐธ์กฐํ•˜๊ฒŒ ์„ค์ •๋˜์–ด์žˆ๋‹ค.
      • ๋Œ“๊ธ€์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” KhumuUser์™€ Article ์—”ํ‹ฐํ‹ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•จ.
      • ์ถ”๊ฐ€์ ์œผ๋กœ KhumuUser ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„  Group ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•จ
      • ์ถ”๊ฐ€์ ์œผ๋กœ Article ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„  Board ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•จ.
      • ... ์ด๋Ÿฐ ๊ณผ์ •์ด ๋งŽ๋‹ค.
    • ์œ„์™€ ๊ฐ™์€ ๋ถˆํŽธ์„ ์—†์• ๊ธฐ ์œ„ํ•ด ๋ถˆํ•„์š”ํ•œ ์ฐธ์กฐ ์ œ์•ฝ ์กฐ๊ฑด์€ ์—†์• ๊ณ  DB Indexing๋งŒ ์œ ์ง€ํ•ด ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๋„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉด ์–ด๋–จ๊นŒ ์‹ถ๋‹ค.
    • ์• ์ดˆ์— ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„์—๋Š” ๋ถ„๋ฆฌ๋œ DB๋ฅผ ์ด์šฉํ•˜๊ณ  ํ•„์š”ํ•œ ๊ฒฝ์šฐ message queue ๊ฐ™์€ ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด event๋ฅผ ๋ฐ›์•„ ์ž๊ธฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค DB์— replication์„ ์ง„ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งŽ์ด ์ด์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ ์• ์ดˆ์— ์ด๋Ÿฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด DB์˜ ์ฐธ์กฐ ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ตณ์ด ๊ฑธ ํ•„์š”๊ฐ€ ์—†์ง€ ์•Š์„๊นŒ ์‹ถ๊ธฐ๋„ ํ•˜๋‹ค. ๋Œ“๊ธ€์€ ๊ฒŒ์‹œ๋ฌผ๊ณผ ์œ ์ €๋ฅผ ์ฐธ์กฐํ•˜๋Š”๋ฐ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์ด ์œ„๋ฐฐ๋œ๋‹ค๊ณ ํ•ด์„œ ๊ทธ๋‹ฅ ๋ฌธ์ œ๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ.

๐Ÿ“š Golang ๊ฐœ๋ฐœ ์ด์•ผ๊ธฐ

interface ๋ฅผ ํ†ตํ•ด mock type์„ ์ด์šฉํ•˜์—ฌ ์˜์กด์„ฑ ์ฃผ์ž…์ด ํ•„์š” ์—†๋Š” ํ…Œ์ŠคํŠธํ•˜๊ธฐ

http ๊ณ„์ธต์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ง ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. http๋Š” usecase ๊ณ„์ธต์— ์˜์กด์ ์ด๋‹ค. ๊ทธ๋Ÿผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งค ๋•Œ http ์ƒ์„ฑ ์‹œ usecase ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž…์‹œ์ผœ์ฃผ์–ด์•ผํ•œ๋‹ค. ๊ทผ๋ฐ ๋ฐ˜๋ณต์ ์œผ๋กœ usecase ๋Š” repository ์— ์˜์กด์ ์ด๋ฏ€๋กœ repository ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž…๋ฐ›์•„์•ผํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ์˜์กด ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ์กฑ์‹œ์ผœ์ฃผ๊ธฐ ๋ฒˆ๊ฑฐ๋กญ๊ธฐ๋•Œ๋ฌธ์— mock type์„ ์ด์šฉํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ณ ์‹ถ๋‹ค.

๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ CommentUseCase์— ๊ด€ํ•œ mock type์„ ์ •์˜ํ•œ๋‹ค๋ฉด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ• ๊นŒ?

func NewCommentRouter(root *RootRouter, uc CommentUseCaseStruct) *CommentRouter {
    ... ์ž‘์—… ์ƒ๋žต
    return commentRouter
}

mock type์„ ์ด์šฉํ•˜๊ธฐ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด์œ ๋Š” CommentUseCase ์—ญํ• ์„ ํ•˜๋Š” ์ธ์ž๊ฐ€ struct ํƒ€์ž…์œผ๋กœ ์ •์˜๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. mock type์„ ์ •์˜ํ•œ๋‹ค๊ณ  ํ•ด๋„ ๊ทธ type์€ ์œ„์— ์ •์˜๋œ CommentUseCaseStruct type์ด ๋  ์ˆ˜ ์—†๋‹ค.

๋”ฐ๋ผ์„œ ์ฃผ์ž…๋ฐ›๋Š” ์ธ์ž์˜ type์„ concreteํ•œ struct๊ฐ€ ์•„๋‹Œ abstractํ•œ interface๋กœ ์ •์˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ mock type์ด ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋˜๊ธฐ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ๋“ค๋งŒ ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์›๋ž˜ ํƒ€์ž…์„ ํ‰๋‚ด๋‚ด์–ด ๊ตฌํ˜„ํ•ด์ฃผ๋ฉด๋œ๋‹ค.

type CommentUseCaseInterface interface{
    List() []*model.Comment
}
type CommentUseCaseMock struct{}

// ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์›๋ž˜์˜ ๊ธฐ๋Šฅ์„ ํ‰๋‚ด๋‚ธ๋‹ค.
func (uc *CommentUseCaseMock) List []*model.Comment{
    return []*model.Comment{
        &model.Comment{...์ƒ๋žต}, &model.Comment{...์ƒ๋žต}, &model.Comment{...์ƒ๋žต}
    }
}

// ์ฃผ์ž…๋ฐ›๋Š” ์ธ์ž์˜ ํƒ€์ž…์„ interfaceํ˜•์œผ๋กœ ์ •์˜ํ–ˆ๊ธฐ๋•Œ๋ฌธ์— ์‹ค์ œ์ ์ธ CommentUseCase์ด๋“  ๊ฐ€์งœ์˜ CommentUseCaseMock ํƒ€์ž…์ด๋“ 
// ์ƒ๊ด€ ์—†์ด ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
// ์„œ๋ฒ„๋ฅผ ๋Œ๋ฆด ๋•Œ์—๋Š” CommentUseCase๋ฅผ, ํ…Œ์ŠคํŠธ ํ•  ๋•Œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์ด ํŽธ๋ฆฌํ•œ CommentUseCaseMock์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
func NewCommentRouter(root *RootRouter, uc CommentUseCaseInterface) *CommentRouter {
    ... ์ž‘์—… ์ƒ๋žต
    return commentRouter
}

Gomock์„ ์ด์šฉํ•œ ํŽธ๋ฆฌํ•œ mocking

$ mockgen -package repository -destination usecase/mock.go \
github.com/khu-dev/khumu-comment/usecase \
CommentUseCaseInterface,LikeCommentUseCaseInterface

(์ถ”๊ฐ€ ์˜ˆ์ •) ์šฐ์„ ์€ ์œ„์™€ ๊ฐ™์€ ์ปค๋งจ๋“œ๋ฅผ ์ด์šฉํ•ด ์ž๋™์œผ๋กœ mock type์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ent ์‚ฌ์šฉ๋ฒ•

๊ธฐ์กด์—๋Š” go-gorm์„ ์ด์šฉํ–ˆ์ง€๋งŒ ๋งŽ์€ ๋ถˆํŽธ์„ ๋Š๊ผˆ๊ณ  ๋งˆ์นจ ์—ฌ๋Ÿฌ Golang ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ent/ent๋ฅผ ๊ทน์ฐฌํ–ˆ๊ธฐ์— ent๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ฐœ์„ ํ–ˆ๋‹ค.

# Comment์— ๋Œ€ํ•œ Schema๋ฅผ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•œ ์ดˆ์•ˆ
$ go run entgo.io/ent/cmd/ent init Comment
# ํ˜น์€ ๊ฐ„๋‹จํžˆ
$ ent init Comment

# Schema ์ˆ˜์ • ํ›„์—๋Š” ํ•ญ์ƒ ์žŠ์ง€ ๋ง๊ณ  ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋œ ์ฝ”๋“œ๋ฅผ Generateํ•  ๊ฒƒ.
# ๋งค Compile ๋งˆ๋‹ค ์ˆ˜ํ–‰์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ง€์—ฐ์‹œ๊ฐ„์ด ์ˆ˜ ์ดˆ ๊ฑธ๋ ค์„œ ๋งค๋ฒˆ ํ•˜๋ฉด ๋ฒˆ๊ฑฐ๋กœ์šธ ๋“ฏ.
$ go generate ./ent

ent๋Š” ๊ธฐ๋Šฅ์ด ๋งŽ๊ณ  ํŽธ๋ฆฌํ•œ ๋Œ€์‹  ๋ช‡ ๊ฐ€์ง€ ์ปค๋งจ๋“œ๋ฅผ ์ตํ˜€์•ผํ•œ๋‹ค.

  1. ent ์ปค๋งจ๋“œ๋ฅผ ํ†ตํ•ด ์ •์˜ํ•˜๋ ค๋Š” schema์˜ ์ดˆ์•ˆ์„ ๋งŒ๋“ ๋‹ค.
  2. go generate๋ฅผ ํ†ตํ•ด ํ•ด๋‹น schema๋ฅผ ๋ฐ”ํƒ•์œผ๋กœํ•œ type์ด๋‚˜ ๊ธฐ๋Šฅ ๋“ฑ๋“ฑ์„ ์ž๋™์œผ๋กœ ์ ์šฉํ•œ๋‹ค.

embedding ํ˜น์€ type alias๋ฅผ ์ด์šฉํ•ด ์˜์กด์„ฑ ์ฃผ์ž…ํ•  ํƒ€์ž… ์ •์˜ํ•˜๊ธฐ

type์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์˜์กด์„ฑ ์ฃผ์ž…์„ ์ž๋™ํ™”ํ•˜๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋™์ผ ํƒ€์ž…์ด์ง€๋งŒ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ ๋‚œ๊ฐํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” embedding ์„ ํ†ตํ•ด ์›๋ž˜ ํƒ€์ž…์˜ ๋ฉ”์†Œ๋“œ์™€ ํ•„๋“œ๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฐœ๋ณ„์ ์ธ type์œผ๋กœ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด router์—์„œ๋Š” ์ž์‹ router group์€ parent router group์„ ์ธ์ž๋กœ ๋ฐ›๊ณ ์‹ถ์€๋ฐ, ๊ทธ๋ƒฅ *echo.Group ์„ ์ฃผ์ž…๋ฐ›๊ฒ ๋‹ค๊ณ  ์ •์˜ํ•˜๋ฉด, ์–ด๋–ค *echo.Group ์„ ์ฃผ์ž…๋ฐ›๊ฒŒ ๋  ์ง€ ๋ชจ๋ฅธ๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์ด embedding ์„ ํ†ตํ•ด ์›๋ž˜์˜ ๋ฉ”์†Œ๋“œ์™€ ํ•„๋“œ๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ฃผ์ž…๋ฐ›์„ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

// embedding์„ ํ†ตํ•ด *echo.Group์˜ ๋ฉ”์†Œ๋“œ, ํ•„๋“œ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž… ์ •์˜
// ์ด ํƒ€์ž…์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ๋ฉ”์†Œ๋“œ๋Š” ์ผ๋ฐ˜์ ์ธ *echo.Group ํƒ€์ž…๊ณผ ๊ตฌ๋ณ„๋œ RootRouter Type์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
type RootRouter struct{*echo.Group}
// ํ˜น์€ type RootRout echo.Group ๋„ ๊ฐ€๋Šฅ

func NewRootRouter(echoServer *echo.Echo, ... ์ธ์ž ์ƒ๋žต) *RootRouter{
    g := RootRouter{Group: echoServer.Group("/api")}
    //... ์ž‘์—… ์ƒ๋žต
    return &g
}

func NewCommentRouter(root *RootRouter, ... ์ธ์ž ์ƒ๋žต) *CommentRouter {
    // ํŠน์ดํ•˜๊ฒŒ Type๋ช…๊ณผ ์ด์šฉํ•˜๊ณ ์žํ•˜๋Š” ๋ฉ”์†Œ๋“œ ๋ช…์ด ๊ฐ™์•„์„œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ๋ฟ ์›๋ž˜๋Š” embed ์‹œ root.Group("/comments")๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ 
    group := root.Group.Group("/comments") 
    commentRouter := &CommentRouter{group, ... ์ธ์ž ์ƒ๋žต}
    return commentRouter
}

Redis๋ฅผ ์ด์šฉํ•œ ์บ์‹œ๋กœ ํผํฌ๋จผ์Šค๋ฅผ ๋†’์ด์ž

Redis๋ฅผ ์ด์šฉํ•ด ์บ์‹œ๋กœ ํผํฌ๋จผ์Šค๋ฅผ ๋†’์ด๊ณ ์ž ์ „๋ฐ˜์ ์ธ ์ปค๋ฎค๋‹ˆํ‹ฐ API๋ฅผ ๋‹ด๋‹นํ•˜๋Š” Django ์„œ๋น„์Šค์™€ ๋ณธ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์ธ ๋Œ“๊ธ€ ์„œ๋น„์Šค ๋ชจ๋‘์— ๋ ˆ๋””์Šค ์บ์‹œ๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ Django ์„œ๋น„์Šค์—์„œ๋Š” ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ๋•Œ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•œ ๋Œ“๊ธ€ ๊ฐœ์ˆ˜๊ฐ€ ํ•„์š”ํ–ˆ๊ณ  ์ด๋ฅผ ์ž์ฒด์ ์œผ๋กœ DB์—์„œ ์งˆ์˜ํ•˜๊ฑฐ๋‚˜ ์งˆ์˜ ํ›„ django์— ๋ถ™์ผ ์ˆ˜ ์žˆ๋Š” caching framework์ธ cacheops๋ฅผ ์ด์šฉํ•ด Redis์— ๋Œ“๊ธ€ ๊ด€๋ จ ์บ์‹œ๋ฅผ ์ €์žฅํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋Œ“๊ธ€ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๋Œ“๊ธ€ ์„œ๋น„์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Django ์„œ๋น„์Šค๊ฐ€ ์Šค์Šค๋กœ ์งˆ์˜ํ•˜๋Š” ๊ฒƒ์€ ์ž์‹  ๋„๋ฉ”์ธ์˜ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ช…๋ฐฑํžˆ ๋„˜์–ด์„œ๋Š” ํ–‰์œ„์ด๊ณ  Context๊ฐ€ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ๋˜์ง€ ์•Š์€ ํ˜•ํƒœ์˜€๋‹ค. ๋ณ„๊ฐœ์ธ ๋‹ค์ˆ˜์˜ ์„œ๋น„์Šค๊ฐ€ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šค ํ•  ๊ฒฝ์šฐ์— ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

  1. ๋Œ“๊ธ€ ์ž‘์„ฑ, ์‚ญ์ œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ Django ์„œ๋น„์Šค๋Š” ๊ทธ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์—†์–ด Django ์„œ๋น„์Šค์˜ cacheops๋กœ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์—†์Œ.
  2. ๋Œ“๊ธ€ ์„œ๋น„์Šค๊ฐ€ ์บ์‹œ๋ฅผ invalidateํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์—ฌ์ „ํžˆ Django ์„œ๋น„์Šค์™€ ๋Œ“๊ธ€ ์„œ๋น„์Šค๋ผ๋Š” ๋ณ„๊ฐœ์˜ ๋‘ ์„œ๋น„์Šค๊ฐ€ Redis์—์„œ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ์•ˆํ‹ฐ ํŒจํ„ด์ด ๋ฐœ์ƒํ•จ.
  3. ๋Œ“๊ธ€ ์„œ๋น„์Šค๊ฐ€ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹คํ•ด๋„ cacheops๊ฐ€ ์ง๋ ฌํ™”ํ•˜๋Š” ๋ฐฉ์‹์— ๋งž์ถฐ์ค„ ์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜๋Š” ์—†๊ณ  invalidate ๋ฐ–์— ํ•  ์ˆ˜ ์—†์Œ. ์ด ๊ฒฝ์šฐ ๋‹ค์Œ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์œ ์ €๋Š” ๊ธด latency๋ฅผ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋จ.

๊ทธ๋ž˜์„œ ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐํ•˜๊ณ ์žํ•œ๋‹ค.

  1. Django ์„œ๋น„์Šค๋Š” ๋Œ“๊ธ€ ๊ด€๋ จ ๋‚ด์šฉ์„ ์œ„ํ•ด์„œ๋Š” ๋Œ“๊ธ€ ์„œ๋น„์Šค์—๊ฒŒ๋งŒ ์งˆ์˜ํ•œ๋‹ค. ๋Œ“๊ธ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ์บ์‹œ์—๋Š” ์ ˆ๋Œ€ ์ ‘๊ทผํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • get_comment_count() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒŒ์‹œ๊ธ€์˜ ๋Œ“๊ธ€ ๊ฐœ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•œ ๋’ค cache๋ฅผ disable ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์ถ”์ฒœ.
  2. ๋Œ“๊ธ€ ๊ด€๋ จ ์บ์‹œ๋Š” ๋Œ“๊ธ€ ์„œ๋น„์Šค๊ฐ€ ์ฃผ์ธ์œผ๋กœ์„œ ์•Œ์•„์„œ ๊ด€๋ฆฌํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ ๋ฒค์น˜๋งˆํฌ

without Redis with Redis
๋Œ“๊ธ€ 100๊ฐœ ์กฐํšŒ (๋กœ์ปฌ ํ™˜๊ฒฝ) ์•ฝ 200 ms ์•ฝ 20 ms
๋Œ“๊ธ€ 100๊ฐœ ์กฐํšŒ (๊ฐœ๋ฐœ ํ™˜๊ฒฝ) ์•ฝ 300 ms ์•ฝ 70 ms
Django๊ฐ€ ๊ฒŒ์‹œ๊ธ€ ํ•˜๋‚˜ ์กฐํšŒ ์‹œ ๊ด€๋ จ ๋Œ“๊ธ€ ๊ฐœ์ˆ˜๋ฅผ ์ง์ ‘ ์กฐํšŒ ํ›„ ์‘๋‹ต(๋กœ์ปฌ ํ™˜๊ฒฝ) ์•ฝ 60 ms ์•ฝ 15ms
Django๊ฐ€ ๊ฒŒ์‹œ๊ธ€ ํ•˜๋‚˜ ์กฐํšŒ ์‹œ ๊ด€๋ จ ๋Œ“๊ธ€ ๊ฐœ์ˆ˜๋ฅผ ์ง์ ‘ ์กฐํšŒ ํ›„ ์‘๋‹ต(๊ฐœ๋ฐœ ํ™˜๊ฒฝ) ์•ฝ 150 ms ์•ฝ 40ms

๋ฌผ๋ก  ๋งŽ์€ ๊ฒฝ์šฐ์˜ ์ˆ˜๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ ๋Œ€์ฒด๋กœ Redis๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๊ฑฐ์˜ 5๋ถ„ 1 ์ •๋„๋กœ latency๊ฐ€ ๊ฐ์†Œํ•˜๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ด๋Ÿฌ๋‹ˆ ์บ์‹œ๋ฅผ ์•ˆ ์“ธ ์ˆ˜๊ฐ€ ์—†๋‹ค... Django๊ฐ€ ๊ฒŒ์‹œ๊ธ€ ํ•˜๋‚˜๋ฅผ ์กฐํšŒ ์‹œ ๊ด€๋ จ ๋Œ“๊ธ€์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜๋”๋ผ๋„ Redis๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด latency๊ฐ€ ์ ์€ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ ์ด๋Š” ์„ฑ๋Šฅ์˜ ์ธก๋ฉด์ด ์•„๋‹Œ ๋„๋ฉ”์ธ์„ ๋ถ„๋ฆฌํ•ด์„œ ์„ค๊ณ„ํ•˜๋Š” ์„ค๊ณ„์˜ ์ธก๋ฉด์—์„œ ์ข‹์ง€ ์•Š์€ ํŒจํ„ด์ด๋ฏ€๋กœ ๋Œ“๊ธ€๊ณผ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์€ ๋Œ“๊ธ€ ์„œ๋น„์Šค๊ฐ€ ์ „๋‹ดํ•˜๊ณ ์ž Django๋Š” ๊ฒŒ์‹œ๊ธ€๊ณผ ๊ด€๋ จ๋œ ๋Œ“๊ธ€ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•  ์‹œ ์ง์ ‘ DB๋‚˜ ์บ์‹œ์—์„œ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋Œ“๊ธ€ ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด ์กฐํšŒํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

How to contribute

Maintainer: @umi0410 ([email protected])

  1. ์ด์Šˆ ์ƒ์„ฑ or ๊ฐœ์ธ์ ์ธ ์—ฐ๋ฝ
  2. Fork ํ›„ PR ์ž‘์„ฑ

About

๐ŸŒŸ A comment server of KHU community service

Topics

Resources

License

Stars

Watchers

Forks

Languages