Skip to content

Pure-Basket/Pure-Basket-BE

Folders and files

NameName
Last commit message
Last commit date

Latest commit

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

Repository files navigation

๐Ÿงบ 01. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

๐Ÿ’ก์™œ ์œ ๊ธฐ๋† ์‹ํ’ˆ๋ชฐ์ธ๊ฐ€?

์—”๋ฐ๋ฏน ์‹œ๋Œ€์˜ ๊ฑด๊ฐ• ํŠธ๋ Œ๋“œ์— ๋ฐœ ๋งž์ถฐ ํ—ฌ์‹œ ํ”Œ๋ ˆ์ €(Healthy Pleasure)๋ฌธํ™”๊ฐ€ ๋‹ค๊ฐ€์˜ด์— ๋”ฐ๋ผ ๋‹จ์ˆœํžˆ ์‹ํ’ˆ์„ ํŒ๋งคํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ๊ฑด๊ฐ•ํ•œ ์‚ถ์˜ ๋ฐฉ์‹์„ ์ œ์•ˆํ•˜๊ณ  ๊ณ ๊ฐ์ด ์ž์‹ ์˜ ๊ฑด๊ฐ•์„ ์Šค์Šค๋กœ '๊ด€๋ฆฌ'ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ์กฐ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

image

ํ—ฌ์‹œ ํ”Œ๋ ˆ์ €(Healthy Pleasure)๋Š” Healthy(๊ฑด๊ฐ•ํ•œ)์™€ Pleasure(๊ธฐ์จ)๊ฐ€ ๊ฒฐํ•ฉํ•œ ๋‹จ์–ด๋กœ, ๊ฑด๊ฐ• ๊ด€๋ฆฌ์˜ ์ฆ๊ฑฐ์›€์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“– API & ERD ๋ช…์„ธ์„œ

๐Ÿ“— API

API Image

๐Ÿ“™ ERD

ERD Image


๐ŸŽฏ 02. ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ

๐Ÿ’ก E-commerce ํ”Œ๋žซํผ์˜ ์ฃผ์š” ํŠธ๋ž˜ํ”ฝ ๋ฐœ์ƒ ์ด๋ฒคํŠธ์ธ ํ• ์ธ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ •ํ•˜์—ฌ ์‚ฌ์šฉ์ž๋“ค์˜ ์ฃผ๋ฌธ์ด ๊ธ‰๊ฒฉํžˆ ์ฆ๊ฐ€ํ•˜์˜€์„ ๋•Œ ์ฃผ๋ฌธ ์˜ค๋ฅ˜ ์—†๋Š” ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๊ฐ€ ๊ฐ€๋Šฅํ•œ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ ์กฐํšŒ ์„ฑ๋Šฅ ์ตœ์ ํ™”

  • ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ ์œ ์ €์˜ ์กฐํšŒ ์š”์ฒญ์„ 2์ดˆ ์ด๋‚ด๋กœ ์‘๋‹ต

2๏ธโƒฃ ์ฃผ๋ฌธ ์„ฑ๋Šฅ ์ตœ์ ํ™”

  • ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ ์œ ์ €์˜ ์ฃผ๋ฌธ ์š”์ฒญ์„ 3์ดˆ ์ด๋‚ด๋กœ ์‘๋‹ต

3๏ธโƒฃ ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ ์ƒํ’ˆ ์ฃผ๋ฌธ ๋™์‹œ์„ฑ ์ œ์–ด

  • ์ฃผ๋ฌธ ์‹œ ์‹ค์‹œ๊ฐ„ ์žฌ๊ณ  ์กฐํšŒ๋ฅผ ํ†ตํ•œ ์ฃผ๋ฌธ ์—๋Ÿฌ ๋ฐœ์ƒ์œจ 0%

4๏ธโƒฃ ์‹ค์‹œ๊ฐ„ ์„œ๋ฒ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊ทธ ์ˆ˜์ง‘

  • ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฐœ ๋ป—๊ณ  ์ž˜ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๊ตฌ์ถ•


๐Ÿ—๏ธ 03. ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜

architecture



๐ŸŽข 04. ์ฃผ์š”๊ธฐ๋Šฅ

๐Ÿ” ์ƒํ’ˆ ์กฐํšŒ ๋ฐ ๊ฒ€์ƒ‰ / ๋ ˆ์‹œํ”ผ ์กฐํšŒ
  • ์กฐํšŒ

home

  • ์ƒํ’ˆ ๊ฒ€์ƒ‰

search

  • ๋ ˆ์‹œํ”ผ ์กฐํšŒ

recipe

๐Ÿ›’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€
  • ๊ฐœ๋ณ„ ์ƒํ’ˆ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€

cart

  • ๋ ˆ์‹œํ”ผ ๊ด€๋ จ ์ƒํ’ˆ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€

recipecart

๐Ÿงพ์ƒํ’ˆ ์ฃผ๋ฌธ
  • ๊ฐœ๋ณ„ ์ƒํ’ˆ ์ฃผ๋ฌธ

order

  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ƒํ’ˆ ์ฃผ๋ฌธ

orders

  • ๐Ÿ’ก ์ด๋ฒคํŠธ ์ƒํ’ˆ ์•Œ๋ฆผ

  • โš™ ๊ด€๋ฆฌ์ž ์ƒํ’ˆ/๋ ˆ์‹œํ”ผ CRUD



โœ๏ธ 05. ๊ธฐ์ˆ ์  ์„ ํƒ ๊ณผ์ •

๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์œ„ํ•ด ๋น„๊ด€์  ๋ฝ ์ ์šฉ

๐Ÿ’ก ์—ฌ๋Ÿฌ ์œ ์ €๊ฐ€ ๊ฐ™์€ ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•œ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•ด ์„ฑ๋Šฅ ๋น„๊ต๋ฅผ ํ†ตํ•ด DB ๋น„๊ด€์ ๋ฝ ์ ์šฉ

์˜์‚ฌ ๊ฒฐ์ • ๊ณผ์ •

Lettuce์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ VS Redisson์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ

  • Lettuce๋ฅผ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ
    • spring-data-jpa์˜ ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ํ•˜์ง€ ์•Š์•„๋„ ๋จ
    • spin lock ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด ๋™์‹œ์— ๋งŽ์€ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ํš๋“ํ•˜๋ ค๊ณ  ๋Œ€๊ธฐํ•˜๋Š” ๊ฒฝ์šฐ์—” redis ์„œ๋ฒ„์— ๋ถ€ํ•˜๊ฐ€ ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.
    • retry ๋กœ์ง์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
  • Redisson์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ
    • spring์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ํ•„์š”
    • redis pub-sub ๋ฐฉ์‹์ด๋ผ lettuce์— ๋น„ํ•ด redis์— ๊ฐ€ํ•ด์ง€๋Š” ๋ถ€ํ•˜๊ฐ€ ์ ์Œ

โ‡’ ๋ถ„์‚ฐ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด Redisson์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •

Redis์— ๋ถ€ํ•˜๊ฐ€ ์ ์€ redisson์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ๊ณผ ๋น„๊ด€์ ๋ฝ์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ ๋น„๊ต

  • 10000๊ฐœ์˜ ๋™์‹œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•œ test์ฝ”๋“œ๋ฅผ ์‹œํ–‰ํ•œ ๊ฒฝ์šฐ Redis ๋ถ„์‚ฐ๋ฝ๋ณด๋‹ค ๋น„๊ด€์  ๋ฝ์ด ํ‰๊ท  1๋ถ„ ์ •๋„ ๋” ๋น ๋ฅด๊ฒŒ ๋‚˜์˜ด.
Gradle ํ…Œ์ŠคํŠธ ํ‰๊ท  ์†๋„ redis ๋ถ„์‚ฐ๋ฝ(redisson) ๋น„๊ด€์ ๋ฝ
๋™์‹œ์— 10000๊ฐœ ์š”์ฒญ 5min 9sec 3min 50sec
CI/CD๋ฅผ ์œ„ํ•ด Github Actions ์ ์šฉ

๐Ÿ’ก ๋ ˆํผ๋Ÿฐ์Šค๋Š” ์ ์ง€๋งŒ, ๋น ๋ฅด๊ฒŒ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ณ , ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ํฌ์ง€ ์•Š์•„์„œ Github Actions๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •

Github Actions:

  • ๋ณ„๋„์˜ ์„œ๋ฒ„ ์—†์ด Github์—์„œ ๋ฐ”๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๊ณ  ์ดˆ๊ธฐ ์„ค์ •์ด ์‰ฌ์›€.
  • Jenkins์— ๋น„ํ•ด ํ”Œ๋Ÿฌ๊ทธ์ธ์ด๋‚˜ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ์ ์Œ.
  • ์ž‘์€ ๊ทœ๋ชจ์˜ ํ”„๋กœ์ ํŠธ ๋˜๋Š” ๊ฐ„๋‹จํ•œ ์›Œํฌํ”Œ๋กœ์šฐ์— ์ ํ•ฉ.

Jenkins:

  • ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ง€์›ํ•˜๊ณ  ์ž๋™ํ™” ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•จ.
  • ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ฐ„๋‹จํžˆ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Œ.
  • ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋‹ค์–‘ํ•จ.
  • ์ดˆ๊ธฐ ์„ค์ •์ด ๋ณต์žกํ•˜๊ณ , ๋ณ„๋„์˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ•˜๋ฉฐ ๋Ÿฌ๋‹์ปค๋ธŒ๊ฐ€ ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ€ํŒŒ๋ฆ„.
๋ชจ๋‹ˆํ„ฐ๋ง ํˆด๋กœ Pinpoint์™€ prometheus/grafana ์‚ฌ์šฉ

๐Ÿ’ก ์ดˆ๊ธฐ Prometheus/grafana๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ณ‘๋ชฉ ์ง€์  ํŒŒ์•…์„ ์œ„ํ•ด Pinpoint๋ฅผ ์ถ”๊ฐ€ ๋„์ž…

prometheus/grafana:

  • exporter๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์ƒ ์‹œ์Šคํ…œ์œผ๋กœ๋ถ€ํ„ฐ pull ๋ฐฉ์‹์œผ๋กœ ๋ฉ”ํŠธ๋ฆญ์„ ๋ฐ›์•„์˜ค๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘
  • ํ•˜๋‚˜์˜ ์„œ๋น„์Šค๋งŒ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์—ฐ๊ฒฐ๋œ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋“ค์— ๋Œ€ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง
  • HA๋ฅผ ์œ„ํ•œ ์ด์ค‘ํ™”๋‚˜ ํด๋Ÿฌ์Šคํ„ฐ๋ง์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์„œ, thanos๋ฅผ ์ถ”๊ฐ€ ์„ค์น˜ํ•ด์•ผํ•จ.
  • ์ฝ”๋“œ ๋ ˆ๋ฒจ๋กœ ์„œ๋น„์Šค์˜ ๋ณ‘๋ชฉ ์ง€์ ์„ ์•Œ๋ ค์ฃผ์ง€ ์•Š์Œ.

Pinpoint:

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

๐Ÿ’ก ํ• ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ API๋“ค์˜ ์„œ๋ฒ„ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•˜์—ฌ ์„œ๋ฒ„์˜ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด Kafka ์‚ฌ์šฉ

Kafka RabbitMQ Redis
์ฃผ๋ฌธ ๋‚ด์—ญ ์ด๋ฒคํŠธ ๋ณด์กด ํ•„์š”์„ฑ ์ˆ˜์‹  ํ™•์ธ ๋ฐ ์ด๋ฒคํŠธ ๋ณ„๋„ ์ €์žฅ ์ˆ˜์‹  ํ™•์ธ ์ˆ˜์‹  ํ™•์ธ ์—†์ด ์‚ญ์ œ
๋‹ค์ˆ˜ API์˜ ๋™์‹œ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ์„ฑ Pub/Sub ๋ชจ๋ธ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹จ์ผ ์ˆ˜์‹ ์ž ๋Œ€์ƒ Pub/Sub ๋ชจ๋ธ
Redis๋กœ ์บ์‹œ ์ ์šฉ

๐Ÿ’ก ๋žœ๋”ฉ ํŽ˜์ด์ง€์˜ ์กฐํšŒ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•ด Redis์˜ caching ๊ธฐ๋Šฅ ์‚ฌ์šฉ

Redis Memcached
์‚ฌ์šฉ๋ชฉ์  ๋ถ€ํ•ฉ์„ฑ (Refresh Token, Caching) ์‚ฌ์šฉ ๋ชฉ์ ์— ๋ถ€ํ•ฉํ•˜๋ฉฐ ๋‹ค์ˆ˜์˜ ํ™œ์šฉ ์‚ฌ๋ก€ ์žˆ์Œ ์‚ฌ์šฉ ๋ชฉ์ ์— ๋ถ€ํ•ฉํ•˜๋ฉฐ AWS Elasticache์—์„œ ์ง€์›
๊ณ ๊ฐ€์šฉ์„ฑ Replication ๊ตฌ์ถ• ๊ฐ€๋Šฅ Replication ๋ถˆ๊ฐ€
์‚ฌ์šฉ์„ฑ ์‚ฌ์šฉ ๊ฒฝํ—˜ ์žˆ์Œ ์‚ฌ์šฉ ๊ฒฝํ—˜ ์—†์Œ, ์Šคํ„ฐ๋”” ํ•„์š”
๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ

๐Ÿ’ก ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€๋ฅผ ๋Œ€๋น„ํ•ด ์˜ˆ๋น„ ์„œ๋ฒ„ ์ถ”๊ฐ€๋ฅผ ํ†ตํ•œ Scale out์‹œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋กœ AWS ALB ์‚ฌ์šฉ

ALB Nginx
Scale-out ํŽธ์˜์„ฑ AWS์˜ Auto-scaling ํ™œ์šฉ ๊ฐ€๋Šฅ
Server ์ถ”๊ฐ€ ์‹œ target group์— ์ถ”๊ฐ€๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ scale-out ๊ฐ€๋Šฅ
์ด๋ฒคํŠธ ์‹œ ์ˆ˜๋™ Scale-out ํ•„์š”
server ์ถ”๊ฐ€ ์‹œ์— ์„ค์ • ํŒŒ์ผ ์ง์ ‘ ์ˆ˜์ • ํ•„์š”
์ ์ • ๋ฆฌ์†Œ์Šค ํ• ๋‹น์„ฑ ํŠธ๋ž˜ํ”ฝ์— ๋”ฐ๋ผ ์ž์› ํ• ๋‹น ์˜ˆ์ƒ ํŠธ๋ž˜ํ”ฝ๊ณผ ๋‹ค๋ฅผ ์‹œ ๋ฆฌ์†Œ์Šค ์ดˆ๊ณผ ๋˜๋Š” ๋ถ€์กฑ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
HTTPS ์ ์šฉ Certificate Manger๋กœ HTTPS ์ ์šฉ ๊ฐ€๋Šฅ HTTPS ์ ์šฉ์„ ์œ„ํ•œ ์ธ์ฆ์„œ ๋ฐœ๊ธˆ ๋ฐ ์„ค์ • ํŒŒ์ผ ์ง์ ‘ ์ˆ˜์ • ํ•„์š”


๐Ÿ“ˆ 06. ์„ฑ๋Šฅ๊ฐœ์„ 

Redis ์บ์‹œ๋กœ ์กฐํšŒ ์„ฑ๋Šฅ ๊ฐœ์„ 

๋žœ๋”ฉํŽ˜์ด์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ Redis ์บ์‹œ๋ฅผ ์ ์šฉํ•จ์œผ๋กœ์จ ํ™ˆํŽ˜์ด์ง€ ์กฐํšŒ ์„ฑ๋Šฅ์„ ๊ฐœ์„ .

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

  • BE ์„œ๋ฒ„: t3.2xlarge
  • DB ์„œ๋ฒ„: db.t3.micro
  • Thread: 1000๋ช…
  • Ramp-up time: 1
  • Loop count: 1

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ:

  • Redis Caching ์ ์šฉ ์ „: ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 9445ms, TPS 59.31
  • Redis Caching ์ ์šฉ ํ›„: ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 1241ms, TPS 286.69

1000๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ ์š”์ฒญ์— ๋Œ€ํ•œ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 87% ๊ฐœ์„ ๋จ.

ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ TPS
Redis Cache X 9445ms 59.31
Redis Cache O 1241ms 286.69
Kafka๋ฅผ ํ†ตํ•œ ์ฃผ๋ฌธ ์„ฑ๋Šฅ ๊ฐœ์„ 

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

์นดํ”„์นด ์ ์šฉ ์ „/ํ›„ ์„ฑ๋Šฅ ์ง€ํ‘œ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

  • BE ์„œ๋ฒ„: t3.2xlarge
  • DB ์„œ๋ฒ„: db.t3.micro
  • Thread: 1000๋ช…
  • Ramp-up time: 1
  • Loop count: 1

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ:

  • ์นดํ”„์นด ์ ์šฉ ์ „: ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 5480ms
  • ์นดํ”„์นด ์ ์šฉ ํ›„: ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 2100ms

1000๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ ์š”์ฒญ์— ๋Œ€ํ•œ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 61% ๊ฐœ์„ ๋จ.

ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ TPS
์นดํ”„์นด ์ ์šฉ ์ „ 5480ms 32.54
์นดํ”„์นด ์ ์šฉ ํ›„ 2100ms 68.1
Kafka ์„ฑ๋Šฅ ๊ฐœ์„  ์ด๋ฏธ์ง€
N+1 ๋ฌธ์ œ ํ•ด๊ฒฐ

๐Ÿ’ก Product ํ…Œ์ด๋ธ” ์กฐํšŒ ์‹œ ์—ฐ๊ด€๋œ Image, Purchase, Cart ํ…Œ์ด๋ธ”์—์„œ N+1 ๋ฌธ์ œ ๋ฐœ์ƒ

Product - Image

๐Ÿ’ก @BatchSize๋ฅผ ์ด์šฉํ•˜์—ฌ N+1 ๋ฌธ์ œ ํ•ด๊ฒฐ

  • Product์™€ Image๋Š” 1:N ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„
  • ์ƒํ’ˆ๋“ค์„ ์กฐํšŒํ•˜๋Š” ํŽ˜์ด์ง€์—์„œ ์กฐํšŒ๋œ ์ƒํ’ˆ ์ˆ˜๋งŒํผ ์—ฐ๊ด€๋œ image๋ฅผ ์กฐํšŒํ•˜๋Š” N+1 ๋ฌธ์ œ ๋ฐœ์ƒ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • Fetch Join, @BatchSize, @Fetch(FetchMode.SUBSELECT) ๋น„๊ต
ํ…Œ์ŠคํŠธ ํ‰๊ท  ์‘๋‹ต์†๋„(1๋ฒˆ) ํŠน์ด ์‚ฌํ•ญ
fetch join 617ms ๊ธฐ์กด Pagination ์—์„œ ์‚ฌ์šฉํ•˜๋˜ LIMIT ๊ตฌ๋ฌธ์ด ๋“ฑ์žฅํ•˜์ง€ ์•Š์Œ
firstResult/maxResults specified with collection fetch; applying in memory ๊ฒฝ๊ณ  ๋กœ๊ทธ ๋ฐœ์ƒ
Fetch join ํ•˜๋Š” image๊ฐ€ collection ๋ฐ์ดํ„ฐ๋ผ ์กฐํšŒํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋งค๋ฒˆ ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— limit ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ  ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•œ ํ›„์— memory์—์„œ ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
@BatchSize() 652ms ํ•ญ์ƒ @BatchSize(size = n)์—์„œ ์„ค์ •ํ•œ ์ˆซ์ž๋งŒํผ์˜ product์™€ ๊ด€๋ จ๋œ image๋ฅผ ์กฐํšŒํ•จ.
purebasket ์„œ๋ฒ„์Šค์—์„œ๋Š” ๋ Œ๋”ฉ ํŽ˜์ด์ง€๋‚˜ ๋ ˆ์‹œํ”ผ ํŽ˜์ด์ง€ ๋“ฑ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ์ƒํ’ˆ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์„œ, ํ•ญ์ƒ n๊ฐœ์˜ ์ƒํ’ˆ์— ๋Œ€ํ•œ image๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์€ ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค.
@Fetch(FetchMode.SUBSELECT) 622ms Subquery๋ฅผ ํฌํ•จํ•˜๋ฉด 8๊ฐœ์˜ ์ฟผ๋ฆฌ๋กœ @BatchSize๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋ณด๋‹ค ์ฟผ๋ฆฌ๊ฐ€ ๋งŽ์Œ.
  • FetchJoin์€ collection์„ fetch join ํ•˜๋Š” ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ @BatchSize๋‚˜ @Fetch(FetchMode.SUBSELECT)๋ฅผ ์ด์šฉํ•˜๊ธฐ๋กœ ํ•จ.

  • @BatchSize()์™€ @Fetch(FetchMode.SUBSELECT) ๋น„๊ต

ํ‰๊ท  ์‘๋‹ต ์†๋„(ms) 100 ๋ช… ๋™์‹œ ์š”์ฒญ 500๋ช… ๋™์‹œ ์š”์ฒญ 1000๋ช… ๋™์‹œ ์š”์ฒญ
@BatchSize() 534.2 2544.0 6309.39
@Fetch(FetchMode.SUBSELECT) 651.7 3144.4 6724.22
  • ํŽ˜์ด์ง€๋งˆ๋‹ค ์กฐํšŒํ•ด์•ผ ํ•˜๋Š” ์ƒํ’ˆ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์„œ @BatchSize๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š” ์ด์ƒ์˜ ์ƒํ’ˆ ์กฐํšŒ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ @Fetch(FetchMode.SUBSELECT)๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์•„์„œ @BatchSize๋ฅผ ์ด์šฉํ•˜๊ธฐ๋กœ ํ•จ.
@Entity
@Getter
@Table(name = "product")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Product {
    @BatchSize(size = 21)
    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Image> images = new ArrayList<>();
}

Product - Purchase

- Product์™€ Purchase๋Š” 1:N ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„(Purchase์—์„œ๋งŒ Product ์กฐํšŒ ๊ฐ€๋Šฅ) - ์œ ์ € ๋ณ„ ์ฃผ๋ฌธ ๋‚ด์—ญ์„ ์กฐํšŒํ•˜๋Š” ํŽ˜์ด์ง€์—์„œ ์กฐํšŒ๋œ ์ฃผ๋ฌธ ๋‚ด์—ญ ์ˆ˜๋งŒํผ ์—ฐ๊ด€๋œ product๋ฅผ ์กฐํšŒํ•˜๋Š” N+1 ๋ฌธ์ œ ๋ฐœ์ƒ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • Fetch Join

    purchase ์„œ๋น„์Šค๋Š” pagination์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€๋งŒ, fetch join ๋˜๋Š” ๋Œ€์ƒ์ธ Purchase๊ฐ€ collection์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Fetch Join์„ ์‚ฌ์šฉํ•ด๋„ ๋ฌธ์ œ ์—†์Œ(OutOfMemoryError ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ)

@Repository
public interface PurchaseRepository extends JpaRepository<Purchase, Long> {

    @Query("SELECT p FROM Purchase p JOIN FETCH p.product WHERE p.member = :member")
    Page<Purchase> findAllByMember(Member member, Pageable pageable);

}

Product - Cart

- Product์™€ Cart๋Š” 1:N ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„(Cart์—์„œ๋งŒ Product ์กฐํšŒ ๊ฐ€๋Šฅ) - ์œ ์ € ๋ณ„ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‚ด์—ญ์„ ์กฐํšŒํ•˜๋Š” ํŽ˜์ด์ง€์—์„œ ์กฐํšŒ๋œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‚ด์—ญ ์ˆ˜๋งŒํผ ์—ฐ๊ด€๋œ product๋ฅผ ์กฐํšŒํ•˜๋Š” N+1 ๋ฌธ์ œ ๋ฐœ์ƒ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • Fetch Join

    cart ์„œ๋น„์Šค๋Š” pagination์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€ ์•Š์Œ

    pagination์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด๋„, fetch join ๋˜๋Š” ๋Œ€์ƒ์ธ Product๊ฐ€ collection์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Fetch Join์„ ์‚ฌ์šฉํ•ด๋„ ๋ฌธ์ œ ์—†์Œ(OutOfMemoryError ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ)

@Repository
public interface CartRepository extends JpaRepository<Cart, Long> {

  @Query("SELECT c FROM Cart c JOIN FETCH c.product WHERE c.member = :member")
  List<Cart> findAllByMember(Member member);
  
}



๐Ÿ˜ตโ€๐Ÿ’ซ 07. ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

DB Connection Pool์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ์ „์ฒด ์‘๋‹ต ์‹œ๊ฐ„์˜ 90% ์ด์ƒ์„ ์ ์œ 

๐Ÿ’ก ์ดˆ๋‹น 1000๊ฐœ์˜ ์ฃผ๋ฌธ ์š”์ฒญ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋ณ‘๋ชฉ๊ตฌ๊ฐ„์ด HikariCP์˜ getConnection() ๋ฉ”์„œ๋“œ์ธ ๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜๊ณ  ๋ณ‘๋ชฉ๊ตฌ๊ฐ„ ํ•ด์†Œ ์‹œ๋„

  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

    • BE ์„œ๋ฒ„: 1EA x t3.xlarge
    • DB ์„œ๋ฒ„: 1EA x db.t3.micro (connection pool = 60)
    • Thread: 1000๋ช… / Ramp-up time: 1s / loop count: 1

    image

  • ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ๊ณผ์ •:

    • ์Šคํ”„๋ง์˜ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ max pool size์™€ minimum idle์ด 10์ธ ๊ฒƒ์„ ํ™•์ธ

    • AWS RDS ์„œ๋ฒ„ ํ™•์ธ ๊ฒฐ๊ณผ DB์˜ ์ตœ๋Œ€ connection์€ 60์ธ ๊ฒƒ์„ ํ™•์ธ

      image

    • BE ์„œ๋ฒ„์˜ connection pool ์‚ฌ์ด์ฆˆ ์ฆ๊ฐ€์— ๋Œ€ํ•œ trade-off๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ ์œ ์ธ ๊ฒƒ์„ ํ™•์ธ

    • BE ์„œ๋ฒ„ ์‹œ์Šคํ…œ์˜ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์—ฌ์œ ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  connection pool ์‚ฌ์ด์ฆˆ ํ™•์žฅ

    spring.datasource.hikari.maximum-pool-size=60
    spring.datasource.hikari.minimum-idle=60
  • ๊ฒฐ๋ก :

    • HikariCP์—์„œ ๋ณ‘๋ชฉ๊ตฌ๊ฐ„ ์ง€์†๋จ
    • ํ˜„์žฌ ์„œ๋ฒ„ ๊ตฌ์„ฑ์˜ ํ•œ๊ณ„์ธ 60๊ฐœ์˜ connection์œผ๋กœ๋Š” ์ดˆ๋‹น 1000๊ฐœ์˜ ์ฃผ๋ฌธ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Œ์„ ์ธ์‹ โ†’ BE/DB ์„œ๋ฒ„ Scale up ๊ฒฐ์ •
Scale-up / Scale-out ํ•ด๋„ ์ฃผ๋ฌธ ์„ฑ๋Šฅ ํ–ฅ์ƒ ์•ˆ ๋˜๋Š” ๋ฌธ์ œ ๐Ÿ’ก **DB ์„œ๋ฒ„ scale-up ๋ฐ BE ์„œ๋ฒ„๋ฅผ ๋‘ ๋Œ€๋กœ scale-outํ–ˆ์ง€๋งŒ ์ฃผ๋ฌธ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์ง€ ์•Š์Œ**

scale up ์ „ํ›„ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

  • BE ์„œ๋ฒ„ - 2EA x t3.2xlarge
  • DB ์„œ๋ฒ„ - 1EA x db.t3.xlarge (connection pool = 1600)
  • Thread : 2000๋ช… / Ramp-up time : 1s / loop count : 1

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ TPS
BE ์„œ๋ฒ„ 1๋Œ€ 4410ms 70.62
BE ์„œ๋ฒ„ 2๋Œ€ 3010ms 83.16
  • ํ˜„์žฌ ์žฌ๊ณ  ๊ด€๋ฆฌ๋Š” ์นดํ”„์นด์— ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ์ฒ˜๋ฆฌํ•˜๊ณ  ์žฌ๊ณ  ์ฐจ๊ฐ์„ ์œ„ํ•ด ๋น„๊ด€์  ๋ฝ ์‚ฌ์šฉ์ค‘
  • Pinpoint ํ™•์ธ ๊ฒฐ๊ณผ HikariCP ์˜ getConnection() ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์ด 2~3์ดˆ ์ •๋„ ๊ฑธ๋ฆผ
  • Connection pool ์‚ฌ์ด์ฆˆ ์กฐ์ •ํ•˜๋ฉด์„œ test๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์ง€๋งŒ connection pool ์ปค์ง€๋ฉด getConnection() ์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ ์ค„์ง€๋งŒ, ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐํ•˜๋Š” ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•ด์„œ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ ๋˜์ง€ ์•Š์Œ

HikariCP Connection Pool size ๋ณ„ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

  • ์„œ๋ฒ„ ๊ตฌ์„ฑ - 2EA t3.2xlarge | rds - db.t3.xlarge
  • Thread : 2000๋ช… / Ramp-up time : 1s / loop count : 1
Connection Pool size 10 50 100 150 200
TPS 86.76 75.11 70.89 80.86 69.24
ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 3.00sec 3.38sec 3.41sec 2.81sec 3.18sec

Connection Pool์ด 10์ธ ๊ฒฝ์šฐ

DB Connection Pool์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ 4684ms๋กœ ์ „์ฒด ์‹คํ–‰ ์‹œ๊ฐ„์˜ 97%, ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•œ stock์„ ์กฐํšŒํ•˜๋Š” sql์„ ์‹คํ–‰ํ•  ๋•Œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด 154ms๋กœ ์ „์ฒด์˜ 3%๋ฅผ ์ฐจ์ง€ํ•จ.

Connection Pool 10

Connection Pool์ด 100์ธ ๊ฒฝ์šฐ

DB Connection Pool์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ 2730ms๋กœ ์ „์ฒด์˜ 57%, ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•œ stock์„ ์กฐํšŒํ•˜๋Š” sql์„ ์‹คํ–‰ํ•  ๋•Œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด 2014ms๋กœ 42%๋ฅผ ์ฐจ์ง€ํ•จ.

Connection Pool 100

Connection Pool์ด 200์ธ ๊ฒฝ์šฐ

DB Connection Pool์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ ์—†์ง€๋งŒ, ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•œ stock์„ ์กฐํšŒํ•˜๋Š” sql์„ ์‹คํ–‰ํ•  ๋•Œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด 3933ms๋กœ ์ „์ฒด์˜ 99%๋ฅผ ์ฐจ์ง€ํ•จ.

Connection Pool 200

  • ๊ฒฐ๋ก  Connection Pool์„ ์ฆ๊ฐ€์‹œํ‚ค๋ฉด BE ์„œ๋ฒ„์˜ DB Connection ๋Œ€๊ธฐ์‹œ๊ฐ„์€ 0ms๊ฐ€ ๋˜์—ˆ์œผ๋‚˜, ๋น„๊ด€์ ๋ฝ์ด ์ ์šฉ๋œ stock ํ…Œ์ด๋ธ” ์กฐํšŒ ์ฟผ๋ฆฌ์˜ ์‘๋‹ต ์‹œ๊ฐ„ ์ฆ๊ฐ€ํ•จ. connection pool์ด ์ถฉ๋ถ„ํ•ด๋„ ๋น„๊ด€์  ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐํ•˜๋Š” ์‹œ๊ฐ„์ด ๋ณ‘๋ชฉ์˜ ์›์ธ์ž„. ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„  ์žฌ๊ณ  ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ๋ฝ์„ ์–ป๋Š” ๋กœ์ง์„ consumer์—์„œ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ.
Redis Cache ์ ์šฉ ์‹œ SerializationException ๋žœ๋”ฉ ํŽ˜์ด์ง€ ์ƒํ’ˆ ์กฐํšŒ API์— redis cache๋ฅผ ์ ์šฉํ•˜๊ณ  redis ์— ์ €์žฅ๋œ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ Serialization ๋ฌธ์ œ ๋ฐœ์ƒ
Could not read JSON:Cannot construct instance of `org.springframework.data.domain.PageImpl` (no Creators, like default constructor,      exist): cannot deserialize from Object value

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋Š” Jackson์ด๋‚˜ Gson ๊ฐ™์€ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ๋ณดํ†ต ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋‚˜ getter/settter๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š”๋ฐ, PageImpl์— Creator๊ฐ€ ์—†์–ด์„œ Object๋กœ deserializeํ•  ์ˆ˜ ์—†์–ด์„œ ์ƒ๊น€

@JsonCreator๋กœ PageImpl์— ๋Œ€ํ•œ Constructor๋ฅผ ๋งŒ๋“ค์–ด ์—ญ์ง๋ ฌํ™”์‹œ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์„œ ํ•ด๊ฒฐ

@JsonIgnoreProperties(ignoreUnknown = true, value = {"pageable"})
public class RestPageImpl<T> extends PageImpl<T> {
    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    private RestPageImpl(@JsonProperty("content") List<T> content,
                         @JsonProperty("number") int number,
                         @JsonProperty("size") int size,
                         @JsonProperty("totalElements") Long totalElements) {
        super(content, PageRequest.of(number, size), totalElements);
    }

    private RestPageImpl(Page<T> page) {
        super(page.getContent(), page.getPageable(), page.getTotalElements());
    }

    public static<T> RestPageImpl<T> from(Page<T> page) {
        return new RestPageImpl<T>(page);
    }
}
Kafka ์ ์šฉ ์‹œ ClassNotFoundException ์นดํ”„์นด ์ปจ์Šˆ๋จธ ์„œ๋ฒ„๋ฅผ ๋ถ„๋ฆฌํ•œ ํ›„ ์ปจ์Šˆ๋จธ์—์„œ ClassNotFoundException ๋ฐœ์ƒ
failed to resolve class name. Class not found 
[com.example.purebasketbe.domain.purchase.dto.KafkaPurchaseDto]

์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ๋Š” package ์ด๋ฆ„๊นŒ์ง€ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์—, producer์—์„œ serializeํ•  ๋•Œ class์˜ fullname์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ consumer์—์„œ deserializeํ•  ๋•Œ class is not in the trusted packages ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

JsonDeserializer์— setRemoveTypeHeaders ์ถ”๊ฐ€

์นดํ”„์นด์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•  ๋•Œ headers์— metadata๋ฅผ ๋‹ด์•„์„œ ๋ณด๋‚ด๋Š”๋ฐ, ์ด ๋•Œ ๋‹ด๊ธฐ๋Š” metadata์— target type์„ ํฌํ•จํ•จ.(target type์€ ์ „์†กํ•˜๊ณ ์žํ•˜๋Š” ๊ฐ์ฒด์˜ ํŒจํ‚ค์ง€๋ช…)

consumerFactory์—์„œ useHeadersIfPresent ๊ฐ’์„ false๋กœ ์ง€์ •ํ•˜์—ฌ ์—ญ์ง๋ ฌํ™”์—์„œ header์— ๋‹ด๊ธด ํŒจํ‚ค์ง€๋ช…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•˜์—ฌ ํ•ด๊ฒฐ

@Bean
public ConsumerFactory<String, KafkaPurchaseDto> consumerFactory() {
    Map<String, Object> configs = new HashMap<>();

    configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");

    return new DefaultKafkaConsumerFactory<>(configs, new StringDeserializer(),
            new JsonDeserializer<>(KafkaPurchaseDto.class, false)
    );
}


โšก 08. ๊ธฐ์ˆ ์Šคํƒ

๐Ÿ–ฅ๏ธ Backend

Category Technologies
Tech Stack Spring Boot, Spring JPA, Spring Security, Kafka, Elastic Search
TEST Junit5, Jmeter
CI/CD Github Action
DB AWS RDS (MySQL), Redis
DevOps Docker, AWS EC2, AWS S3
Logging & Monitoring Logstash/Kibana, Prometheus/Grafana, Pinpoint

๐Ÿ“บ Frontend

Category Technologies
Tech Stack React


๐Ÿ‘ช 09. ํŒ€์› ์†Œ๊ฐœ

์—ญํ•  ์ด๋ฆ„ email github ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ
ํŒ€์žฅ ํ™์ค€์˜ [email protected] https://github.com/hjunyoung https://hjunyoung.github.io/
ํŒ€์› ์ด์ƒํœด [email protected] https://github.com/hyouoo https://devlog1921.tistory.com/
ํŒ€์› ๋ฐ•์„œ์œค [email protected] https://github.com/SeoYoonP https://velog.io/@wideskyinme/posts

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •