Skip to content

parkchanjong/springboot-video-platform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

102 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

springboot-video-platform


๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ

  • ๋น„๋””์˜ค ์กฐํšŒ
  • ๋น„๋””์˜ค ์ข‹์•„์š”
  • ์ฑ„๋„ ๊ตฌ๋…
  • ์ƒˆ๋กœ์šด ๋น„๋””์˜ค ์•Œ๋ฆผ ์ „์†ก
  • ๋Œ“๊ธ€ ์ž‘์„ฑ
  • ๋Œ“๊ธ€ ์ฐจ๋‹จ
  • ๋„์„œ ๊ฒ€์ƒ‰
  • ์ฟ ํฐ ๋ฐœ๊ธ‰

๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ์ƒ์„ธ

1. ๋น„๋””์˜ค ์กฐํšŒ โ€” Redis ์บ์‹œ ์ ์šฉ์œผ๋กœ ์‘๋‹ต ์†๋„ ๊ฐœ์„ 

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ ์ธ๊ธฐ ๋น„๋””์˜ค ์กฐํšŒ API ์š”์ฒญ์ด ๋งค๋ฒˆ MySQL์— ์ง์ ‘ ๋„๋‹ฌํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ ์ง‘์ค‘ ์‹œ DB ๋ถ€ํ•˜๊ฐ€ ๊ธ‰์ฆํ•˜๊ณ  ์‘๋‹ต ์†๋„๊ฐ€ ์ €ํ•˜๋จ
์›์ธํŒŒ์•… ์บ์‹ฑ ๋ ˆ์ด์–ด ์—†์ด ๋ชจ๋“  ์ฝ๊ธฐ ์š”์ฒญ์ด MySQL SELECT๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ๊ณ , ๋™์ผ ๋น„๋””์˜ค์— ๋Œ€ํ•œ ์ค‘๋ณต ์กฐํšŒ๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒ
ํ•ด๊ฒฐ๊ณผ์ • Redis๋ฅผ ์บ์‹œ ๋ ˆ์ด์–ด๋กœ ๋„์ž…. @Cacheable์„ ํ™œ์šฉํ•ด ๋น„๋””์˜คยท์ฑ„๋„ ์ •๋ณด๋ฅผ ์บ์‹ฑํ•˜๊ณ  TTL์„ ์„ค์ •ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€
๊ฒฐ๊ณผ DB ์กฐํšŒ ์ฟผ๋ฆฌ ์ˆ˜ ์•ฝ 85% ๊ฐ์†Œ / ์‘๋‹ต์‹œ๊ฐ„ 200ms โ†’ 15ms (92% ๊ฐœ์„ )

2. ๋น„๋””์˜ค ์กฐํšŒ์ˆ˜ โ€” Redis + Spring Batch ๋กœ ๊ณ ๋นˆ๋„ Write ์ตœ์ ํ™”

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ ๋น„๋””์˜ค ์กฐํšŒ๋งˆ๋‹ค MySQL์˜ view_count ์ปฌ๋Ÿผ์— UPDATE ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ, ์ธ๊ธฐ ๋น„๋””์˜ค์— ์ดˆ๋‹น ์ˆ˜๋ฐฑ ๊ฑด์˜ Write๊ฐ€ ์ง‘์ค‘๋˜๋Š” ๋ณ‘๋ชฉ ๋ฐœ์ƒ
์›์ธํŒŒ์•… ์‹ค์‹œ๊ฐ„ ์นด์šดํ„ฐ๋ฅผ RDB์— ์ง์ ‘ ์ ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋Š” ํ–‰(Row) ๋‹จ์œ„ ์ž ๊ธˆ์œผ๋กœ ์ธํ•œ ๊ฒฝํ•ฉ์ด ๋ถˆ๊ฐ€ํ”ผํ•˜๋ฉฐ, Write ์ง‘์ค‘์ด ์ „์ฒด DB ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์ง
ํ•ด๊ฒฐ๊ณผ์ • Redis INCR๋กœ ์กฐํšŒ์ˆ˜๋ฅผ ์ธ๋ฉ”๋ชจ๋ฆฌ์— ๋ˆ„์ ํ•œ ๋’ค, Spring Batch ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ง‘๊ณ„ํ•˜์—ฌ MySQL์— ๋ฒŒํฌ UPDATE๋กœ ๋™๊ธฐํ™”
๊ฒฐ๊ณผ DB Write ์ฟผ๋ฆฌ ์ˆ˜ ์•ฝ 95% ๊ฐ์†Œ (์กฐํšŒ๋งˆ๋‹ค 1๊ฑด โ†’ ๋ฐฐ์น˜ ์ฃผ๊ธฐ๋งˆ๋‹ค 1๊ฑด ๋ฒŒํฌ UPDATE)

3. ์ฟ ํฐ ๋ฐœ๊ธ‰ โ€” Redisson ๋ถ„์‚ฐ ๋ฝ์œผ๋กœ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ ์„ ์ฐฉ์ˆœ ํ•œ์ • ์ฟ ํฐ ๋ฐœ๊ธ‰ ์‹œ ๋‹ค์ˆ˜์˜ ๋™์‹œ ์š”์ฒญ์ด ๋ชฐ๋ ค ์žฌ๊ณ  ์ˆ˜๋Ÿ‰์„ ์ดˆ๊ณผํ•˜๋Š” ์ค‘๋ณต ๋ฐœ๊ธ‰์ด ๋ฐœ์ƒ
์›์ธํŒŒ์•… ๋‹ค์ค‘ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ SELECT โ†’ ๊ฒ€์ฆ โ†’ INSERT ํ๋ฆ„ ์‚ฌ์ด์˜ Race Condition. JVM ๋ ˆ๋ฒจ์˜ synchronized๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ํšจ๋ ฅ ์—†์Œ
ํ•ด๊ฒฐ๊ณผ์ • Redisson์˜ ๋ถ„์‚ฐ ๋ฝ(RLock)์„ ์ฟ ํฐ ๋ฐœ๊ธ‰ ์œ ์ฆˆ์ผ€์ด์Šค์— ์ ์šฉํ•˜์—ฌ, ๋‹จ์ผ ์„œ๋ฒ„๊ฐ€ ์ž„๊ณ„ ๊ตฌ์—ญ์„ ์ ์œ ํ•˜๋Š” ๋™์•ˆ ๋‚˜๋จธ์ง€ ์š”์ฒญ์€ ๋Œ€๊ธฐํ•˜๋„๋ก ์ฒ˜๋ฆฌ
๊ฒฐ๊ณผ JMeter 500 ๋™์‹œ ์š”์ฒญ ํ…Œ์ŠคํŠธ ๊ธฐ์ค€ โ€” ์ดˆ๊ณผ ๋ฐœ๊ธ‰ ๊ฑด์ˆ˜ 0๊ฑด, ์ •ํ•ฉ์„ฑ 100% ๋ณด์žฅ

4. ์ƒˆ๋กœ์šด ๋น„๋””์˜ค ์•Œ๋ฆผ ์ „์†ก โ€” Kafka ๋น„๋™๊ธฐ ๋ถ„๋ฆฌ๋กœ ํŠธ๋žœ์žญ์…˜ ๊ฒฐํ•ฉ ์ œ๊ฑฐ

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ ๋น„๋””์˜ค ์ƒ์„ฑ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๊ตฌ๋…์ž ์•Œ๋ฆผ์„ ๋™๊ธฐ ์ „์†กํ•˜์—ฌ, ์•Œ๋ฆผ ์„œ๋น„์Šค ์ง€์—ฐยท์žฅ์•  ๋ฐœ์ƒ ์‹œ ๋น„๋””์˜ค ์ƒ์„ฑ API์˜ ์‘๋‹ต์‹œ๊ฐ„์ด ํ•จ๊ป˜ ์ฆ๊ฐ€
์›์ธํŒŒ์•… ๊ตฌ๋…์ž ์ˆ˜์— ๋น„๋ก€ํ•˜์—ฌ ์•Œ๋ฆผ ๋ฃจํ”„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์„ ํ˜• ์ฆ๊ฐ€ํ•˜๊ณ , ์™ธ๋ถ€ ์žฅ์• ๊ฐ€ ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ํŠธ๋žœ์žญ์…˜์— ์ง์ ‘ ์˜ํ–ฅ์„ ์คŒ
ํ•ด๊ฒฐ๊ณผ์ • ๋น„๋””์˜ค ์ƒ์„ฑ ์™„๋ฃŒ ํ›„ Kafka ํ† ํ”ฝ์— ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ , ์•Œ๋ฆผ ์ปจ์Šˆ๋จธ๊ฐ€ ๋ณ„๋„๋กœ ๊ตฌ๋…์ž์—๊ฒŒ ์ „์†กํ•˜๋„๋ก ๋ถ„๋ฆฌ. ์ปจ์Šˆ๋จธ ํŒŒํ‹ฐ์…˜ ์ˆ˜๋ฅผ ๋Š˜๋ ค ์ˆ˜ํ‰ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„
๊ฒฐ๊ณผ ๋น„๋””์˜ค ์ƒ์„ฑ API ์‘๋‹ต์‹œ๊ฐ„ ์•ฝ 2,500ms โ†’ 150ms (94% ๊ฐœ์„ ) / ์•Œ๋ฆผ ์žฅ์• ๊ฐ€ ๋น„๋””์˜ค ์ƒ์„ฑ ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋ฌด์˜ํ–ฅ

4-1. Kafka ์•Œ๋ฆผ ์ปจ์Šˆ๋จธ โ€” processed_event ๊ธฐ๋ฐ˜ ๋ฉฑ๋“ฑ ์ฒ˜๋ฆฌ

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ Kafka consumer๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•œ ๋’ค offset commit ์ „์— ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐ™์€ ์•Œ๋ฆผ ์ด๋ฒคํŠธ๊ฐ€ ์žฌ์ „๋‹ฌ๋˜์–ด ๊ตฌ๋…์ž์—๊ฒŒ ์ค‘๋ณต ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ
์›์ธํŒŒ์•… ๋ฉ”์‹œ์ง€์— idempotency key๊ฐ€ ์—†๊ณ  consumer offset commit๊ณผ DB ํŠธ๋žœ์žญ์…˜์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด, ์žฌ์ฒ˜๋ฆฌ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ์˜์† ์ƒํƒœ๊ฐ€ ์—†์—ˆ์Œ
ํ•ด๊ฒฐ๊ณผ์ • NewVideoMessage์— eventId๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , consumer๊ฐ€ processed_event ํ…Œ์ด๋ธ”์— event_id๋ฅผ ๋จผ์ € ์ €์žฅํ•œ ๋’ค ์•Œ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋ณ€๊ฒฝ. Kafka offset์€ DB ํŠธ๋žœ์žญ์…˜ commit ์„ฑ๊ณต ํ›„ manual ack๋กœ ์ฒ˜๋ฆฌ
๊ฒฐ๊ณผ ๋™์ผ eventId ์žฌ์ „๋‹ฌ ์‹œ ์•Œ๋ฆผ ๋กœ์ง์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ack๋งŒ ์ˆ˜ํ–‰. ์ฒ˜๋ฆฌ ์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด processed_event ์ €์žฅ๋„ rollback๋˜๊ณ  ackํ•˜์ง€ ์•Š์•„ Kafka ์žฌ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

๋ณ€๊ฒฝ ํŒŒ์ผ ๋ชฉ๋ก.

  • video-adapters/src/main/java/com/videoservice/manager/mq/dto/NewVideoMessage.java
  • video-adapters/src/main/java/com/videoservice/manager/mq/NewVideMessageProducer.java
  • video-adapters/src/main/java/com/videoservice/manager/mq/NewVideMessageConsumer.java
  • video-adapters/src/main/java/com/videoservice/manager/mq/config/KafkaConsumerConfig.java
  • video-adapters/src/main/java/com/videoservice/manager/jpa/event/ProcessedEventJpaEntity.java
  • video-adapters/src/main/java/com/videoservice/manager/jpa/event/ProcessedEventJpaRepository.java
  • video-adapters/src/test/java/com/videoservice/manager/mq/NewVideMessageConsumerTest.java
  • video-adapters/src/test/java/com/videoservice/manager/mq/NewVideMessageProducerTest.java

์„ค๊ณ„ ์ด์œ .

  • processed_event.event_id๋ฅผ PK์™€ unique key๋กœ ์‚ฌ์šฉํ•ด ์ด๋ฏธ ์ฒ˜๋ฆฌ๋œ ์ด๋ฒคํŠธ์™€ ๋™์‹œ ์ค‘๋ณต ์ฒ˜๋ฆฌ ๊ฒฝ์Ÿ์„ DB ์ œ์•ฝ์œผ๋กœ ๋ฐฉ์–ดํ•ฉ๋‹ˆ๋‹ค.
  • consumer๋Š” enable.auto.commit=false์™€ AckMode.MANUAL์„ ์‚ฌ์šฉํ•ด DB ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณตํ•œ ๋’ค์—๋งŒ offset์„ ackํ•ฉ๋‹ˆ๋‹ค.
  • topic, partition, offset, processed_at์„ ํ•จ๊ป˜ ์ €์žฅํ•ด ์šด์˜ ์ค‘ ์ค‘๋ณต ์ฒ˜๋ฆฌ ํŒ๋‹จ๊ณผ ๋ฉ”์‹œ์ง€ ์ถ”์ ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•.

  • ์ค‘๋ณต ์ˆ˜์‹  ๊ฒ€์ฆ์€ NewVideMessageConsumerTest.consumesSameEventOnlyOnceAndAcknowledgesBothDeliveries์—์„œ ๊ฐ™์€ eventId๋ฅผ ๋‘ ๋ฒˆ ์†Œ๋น„ํ•ด ๊ตฌ๋…์ž ์กฐํšŒ๋Š” 1ํšŒ, ack๋Š” 2ํšŒ ์ˆ˜ํ–‰๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์™ธ ์žฌ์ฒ˜๋ฆฌ ๊ฒ€์ฆ์€ NewVideMessageConsumerTest.doesNotAcknowledgeWhenNotificationProcessingFails์—์„œ ์•Œ๋ฆผ ์ฒ˜๋ฆฌ ์˜ˆ์™ธ ์‹œ rollback๋˜๊ณ  ackํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • unique ์ œ์•ฝ ๊ฒ€์ฆ์€ NewVideMessageConsumerTest.acknowledgesConcurrentDuplicateWithoutNotification์—์„œ DataIntegrityViolationException์„ ๋ชจ์‚ฌํ•ด ์•Œ๋ฆผ ์—†์ด ackํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • producer eventId ๊ฒ€์ฆ์€ NewVideMessageProducerTest.sendsMessageWithGeneratedEventId์—์„œ UUID ํ˜•์‹์˜ eventId๊ฐ€ ์ฑ„์›Œ์ง€๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์ „์ฒด adapter ๊ฒ€์ฆ ๋ช…๋ น์€ ./gradlew :video-adapters:test์ž…๋‹ˆ๋‹ค.

5. ๋Œ“๊ธ€ ์ž‘์„ฑ โ€” MongoDB ๋„์ž…์œผ๋กœ ๊ณ„์ธต ๊ตฌ์กฐ ์ฟผ๋ฆฌ ๊ฐœ์„ 

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ ๋ถ€๋ชจ-์ž์‹ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋Œ“๊ธ€(๋Œ€๋Œ“๊ธ€)์„ MySQL์—์„œ ์ฒ˜๋ฆฌํ•  ๋•Œ ์žฌ๊ท€ CTE ๋˜๋Š” ๋‹ค๋‹จ๊ณ„ JOIN์ด ํ•„์š”ํ•˜์—ฌ ์ฟผ๋ฆฌ ๋ณต์žก๋„๊ฐ€ ๋†’๊ณ  ์‘๋‹ต์ด ๋А๋ฆผ
์›์ธํŒŒ์•… RDB๋Š” ๊ณ„์ธตํ˜• ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์ ํ•ฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, depth๊ฐ€ ๊นŠ์–ด์งˆ์ˆ˜๋ก ์ฟผ๋ฆฌ ๋น„์šฉ์ด ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ์ฆ๊ฐ€
ํ•ด๊ฒฐ๊ณผ์ • ๋Œ“๊ธ€ ๋„๋ฉ”์ธ์„ MongoDB๋กœ ๋ถ„๋ฆฌ. ๋Œ“๊ธ€ ๋ฌธ์„œ ์•ˆ์— ์ž์‹ ๋Œ“๊ธ€์„ ์ค‘์ฒฉ ๋ฐฐ์—ด๋กœ ์ €์žฅํ•˜๊ฑฐ๋‚˜ parentId ์ฐธ์กฐ ๋ฐฉ์‹์„ ํ™œ์šฉํ•˜์—ฌ ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ๊ณ„์ธต ์กฐํšŒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค๊ณ„
๊ฒฐ๊ณผ 3-depth ๋Œ“๊ธ€ ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„ ์•ฝ 180ms โ†’ 30ms (83% ๊ฐœ์„ ) / ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์—†์ด ๋Œ“๊ธ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„๋“œ ํ™•์žฅ ๊ฐ€๋Šฅ

6. ๋„์„œ ๊ฒ€์ƒ‰ โ€” Resilience4j Circuit Breaker๋กœ ์™ธ๋ถ€ API ์žฅ์•  ๋Œ€์‘

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฌธ์ œ์‚ฌํ•ญ Naver ๋„์„œ ๊ฒ€์ƒ‰ API๊ฐ€ ์ผ์‹œ์ ์œผ๋กœ ์žฅ์•  ์ƒํƒœ์ผ ๋•Œ ์š”์ฒญ์ด ๊ณ„์† ๋ˆ„์ ๋˜์–ด ์Šค๋ ˆ๋“œ ๊ณ ๊ฐˆ์ด ๋ฐœ์ƒํ•˜๊ณ , ๋„์„œ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ์ „์ฒด๊ฐ€ ์‘๋‹ต ๋ถˆ๊ฐ€ ์ƒํƒœ๊ฐ€ ๋จ
์›์ธํŒŒ์•… ๋‹จ์ผ ์™ธ๋ถ€ API ์˜์กด ๊ตฌ์กฐ์—์„œ Timeout ์„ค์ •๋งŒ์œผ๋กœ๋Š” ์žฅ์•  ์ „ํŒŒ๋ฅผ ๋ง‰๊ธฐ ์–ด๋ ต๊ณ , ์žฅ์•  ์ง€์† ์ค‘์—๋„ ๋ฐ˜๋ณต ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜์—ฌ ๋ณต๊ตฌ๋ฅผ ๋ฐฉํ•ด
ํ•ด๊ฒฐ๊ณผ์ • Resilience4j Circuit Breaker๋ฅผ Naver API ํ˜ธ์ถœ๋ถ€์— ์ ์šฉ. ์˜ค๋ฅ˜์œจ 50% ์ดˆ๊ณผ ์‹œ ํšŒ๋กœ๋ฅผ OPENํ•˜์—ฌ ์ฆ‰์‹œ Kakao API๋กœ Fallback ์ „ํ™˜. HALF_OPEN ์ƒํƒœ์—์„œ ํšŒ๋ณต ์—ฌ๋ถ€๋ฅผ ์ž๋™ ๊ฐ์ง€
๊ฒฐ๊ณผ Naver API ์žฅ์•  ์‹œ Fallback ์ „ํ™˜ ์‹œ๊ฐ„ 500ms ์ด๋‚ด / ๋„์„œ ๊ฒ€์ƒ‰ ์„œ๋น„์Šค ๊ฐ€์šฉ์„ฑ 99% ์ด์ƒ ์œ ์ง€

๊ฐœ๋ฐœ ํ™˜๊ฒฝ

  • Java
  • Spring Boot
  • Gradle
  • JPA
  • QueryDSL
  • MySQL
  • mongodb
  • redis
  • kafka

ํ”„๋กœ์ ํŠธ ๋ชจ๋“ˆ ๊ตฌ์กฐ

ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ๋กœ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๋ชจ๋“ˆ:

  • video-core: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋ณธ ๋ชจ๋“ˆ
  • video-apps: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” REST API ์™€ ๋ฐฐ์น˜์žก์„ ๋ชจ์•„๋‘” ๋ชจ๋“ˆ
  • video-adapters: ์™ธ๋ถ€ ์ธํ”„๋ผ์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋“ˆ
  • video-commons: ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ๋ชจ์•„๋‘” ๋ชจ๋“ˆ

์‹คํ–‰๋ฐฉ๋ฒ•

docker compose up

./gradlew bootRun

Architecture

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-11-28 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 11 58 31

์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๋ฐฐ๊ฒฝ

ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ์ฑ„ํƒ ์ด์œ 

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(core-service, core-usecase)์„ ์™ธ๋ถ€ ๊ธฐ์ˆ (JPA, Redis, Kafka ๋“ฑ)๋กœ๋ถ€ํ„ฐ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ์˜์กด์„ฑ ์—ญ์ „(DIP): Port ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋„๋ฉ”์ธ์ด ์ธํ”„๋ผ๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๊ธฐ์ˆ  ๊ต์ฒด ์‹œ Adapter๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ๋…๋ฆฝ์  ํ…Œ์ŠคํŠธ: ๊ฐ ๋ ˆ์ด์–ด๋ฅผ Port ๋ชฉํ‚น ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด, ์™ธ๋ถ€ ์ธํ”„๋ผ ์—†์ด๋„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ปดํŒŒ์ผ ํƒ€์ž„ ์˜์กด์„ฑ ๊ฐ•์ œ: ๋ฉ€ํ‹ฐ๋ชจ๋“ˆ ๊ตฌ์กฐ๋กœ ๋ ˆ์ด์–ด ๊ฐ„ ์ž˜๋ชป๋œ ์˜์กด์„ฑ์„ ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

๋ฉ€ํ‹ฐ๋ชจ๋“ˆ ๊ตฌ์กฐ์˜ ์˜๋„

๋ชจ๋“ˆ ์˜๋„
video-core ์ˆœ์ˆ˜ Java๋กœ ์ž‘์„ฑํ•˜์—ฌ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด ์—†์Œ โ†’ ๊ธฐ์ˆ  ์ค‘๋ฆฝ์„ฑ ๋ณด์žฅ
video-adapters JPA, Redis, Kafka, MongoDB ๋“ฑ ๋ชจ๋“  ์™ธ๋ถ€ ๊ธฐ์ˆ  ์˜์กด์„ฑ์„ ํ•œ ๊ณณ์— ๊ฒฉ๋ฆฌ
video-apps REST API(video-api)์™€ ๋ฐฐ์น˜(video-batch) ์ง„์ž…์ ์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฐฐํฌ ๋‹จ์œ„ ๋…๋ฆฝ์„ฑ ํ™•๋ณด
video-commons ๊ณตํ†ต ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ๋ณ„๋„ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ค‘๋ณต ์ œ๊ฑฐ

๊ธฐ์ˆ  ์„ ํƒ ๊ธฐ์ค€

๊ธฐ์ˆ  ์„ ํƒ ์ด์œ 
MySQL ๋น„๋””์˜คยท์ฑ„๋„ยท์‚ฌ์šฉ์žยท๊ตฌ๋…ยท์ฟ ํฐ ๋“ฑ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ์˜ ํŠธ๋žœ์žญ์…˜ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ์ด ํ•„์š”ํ•˜๊ณ , ๊ตฌ๋… ๊ด€๊ณ„ ๋“ฑ ๋ณต์žกํ•œ JOIN ์ฟผ๋ฆฌ๊ฐ€ ์š”๊ตฌ๋˜๋Š” ์˜๊ตฌ ์ €์žฅ์†Œ
MongoDB ๋Œ“๊ธ€์€ ๋ถ€๋ชจ-์ž์‹ ๊ณ„์ธต ๊ตฌ์กฐ์™€ ์Šคํ‚ค๋งˆ ์œ ์—ฐ์„ฑ์ด ํ•„์š”ํ•˜๋ฉฐ, ๊ณ ๋นˆ๋„ ์ฝ๊ธฐ/์“ฐ๊ธฐ์— RDB๋ณด๋‹ค ์œ ๋ฆฌ
Redis โ‘ ๋น„๋””์˜คยท์ฑ„๋„ ์บ์‹œ๋กœ DB ๋ถ€ํ•˜ ๊ฐ์†Œ, โ‘ก์กฐํšŒ์ˆ˜ ๊ณ ๋นˆ๋„ Write๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ฒ˜๋ฆฌ ํ›„ ๋ฐฐ์น˜ ๋™๊ธฐํ™”, โ‘ขRedisson ๋ถ„์‚ฐ ๋ฝ์œผ๋กœ ์ฟ ํฐ ๋ฐœ๊ธ‰ ๋™์‹œ์„ฑ ์ œ์–ด, โ‘ฃ์‚ฌ์šฉ์ž ์„ธ์…˜ ๊ด€๋ฆฌ
Kafka ์ƒˆ ๋น„๋””์˜ค ์—…๋กœ๋“œ ์‹œ ๊ตฌ๋…์ž ์•Œ๋ฆผ์„ ๋น„๋™๊ธฐ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์•Œ๋ฆผ ์žฅ์• ๊ฐ€ ๋น„๋””์˜ค ์ƒ์„ฑ ํŠธ๋žœ์žญ์…˜์— ์˜ํ–ฅ ์—†๋„๋ก ์„ค๊ณ„, ์ปจ์Šˆ๋จธ ์ˆ˜ํ‰ ํ™•์žฅ์œผ๋กœ ๋Œ€๋Ÿ‰ ๊ตฌ๋…์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
QueryDSL ์ฑ„๋„๋ณ„ ๋น„๋””์˜ค ์กฐํšŒ ๋“ฑ ๋™์  ์กฐ๊ฑด ์ฟผ๋ฆฌ๋ฅผ ์ปดํŒŒ์ผ ํƒ€์ž„์— ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๋ฐฉ์ง€
Resilience4j (Circuit Breaker) ๋„์„œ ๊ฒ€์ƒ‰ ์™ธ๋ถ€ API(Naver)์˜ ์žฅ์• ๋ฅผ ๊ฐ์ง€ํ•ด ์ž๋™์œผ๋กœ Kakao API๋กœ Fallback, ์™ธ๋ถ€ ์˜์กด์„ฑ ์žฅ์• ์˜ ๋‚ด๋ถ€ ์ „ํŒŒ ๋ฐฉ์ง€
Spring Batch Redis์— ๋ˆ„์ ๋œ ๋น„๋””์˜ค ์กฐํšŒ์ˆ˜๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ MySQL์— ๋™๊ธฐํ™”ํ•˜๋Š” ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ž‘์—…์— ์ ํ•ฉ

Flow

  • ๋น„๋””์˜ค ์ƒ์„ฑ
แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-11-30 แ„‹แ…ฉแ„’แ…ฎ 11 40 10
  • ์ฑ„๋„ ๊ตฌ๋… ๋“ฑ๋ก
แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-11-30 แ„‹แ…ฉแ„’แ…ฎ 11 40 04

k6 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ

ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๊ธฐ์ˆ  ์Šคํƒ

์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ ๋Œ€์ƒ ํ•ต์‹ฌ ์ง€ํ‘œ
video-load Redis ์บ์‹œ (๋น„๋””์˜ค ์กฐํšŒ), Redis INCR (์กฐํšŒ์ˆ˜) ์บ์‹œ ํžˆํŠธ์œจ >80%, P95 <100ms
comment-load MongoDB CRUD, ์ปค์„œ ํŽ˜์ด์ง€๋„ค์ด์…˜ P95 <200ms (์ž‘์„ฑ), <150ms (์กฐํšŒ)
coupon-load Redisson ๋ถ„์‚ฐ ๋ฝ, Race Condition ๋ฐฉ์–ด ์ดˆ๊ณผ ๋ฐœ๊ธ‰ 0๊ฑด (โ‰ค500๊ฑด)
stress-test ์ „์ฒด ์‹œ์Šคํ…œ ํ•œ๊ณ„์  P99 <2000ms
spike-test Circuit Breaker (Resilience4j), ๊ธ‰๊ฒฉํ•œ ํŠธ๋ž˜ํ”ฝ ๋Œ€์‘ P99 <3000ms

์‚ฌ์ „ ์ค€๋น„

2. ์‹œ๋“œ ๋ฐ์ดํ„ฐ DB ๋“ฑ๋ก

MySQL์— ๋‹ค์Œ ID๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • ๋น„๋””์˜ค: video-seed-001 ~ video-seed-005
  • ์ฑ„๋„: channel-seed-001 ~ channel-seed-003

MongoDB์— ๋‹ค์Œ ID๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • ๋Œ“๊ธ€: comment-seed-001 ~ comment-seed-003
  • ๋ถ€๋ชจ ๋Œ“๊ธ€: parent-comment-001 ~ parent-comment-002

3. Redis ์„ธ์…˜ ๋“ฑ๋ก

๋‹ค์Œ ํ† ํฐ์ด Redis์— ์„ธ์…˜์œผ๋กœ ๋“ฑ๋ก๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (x-auth-key ํ—ค๋” ์ธ์ฆ):

  • test-auth-token-001 ~ test-auth-token-005

4. ์ฟ ํฐ ์ •์ฑ… ์ƒ์„ฑ

์ฟ ํฐ ๋ถ„์‚ฐ ๋ฝ ํ…Œ์ŠคํŠธ ์ „ ๋‹ค์Œ ์ •์ฑ…์ด ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • ID: coupon-policy-concurrent-001
  • totalQuantity: 500

์‹คํ–‰ ๋ฐฉ๋ฒ•

๊ธฐ๋ณธ ์‹คํ–‰ (๋กœ์ปฌ ์„œ๋ฒ„)

# Smoke Test (๊ธฐ๋ณธ ๋™์ž‘ ํ™•์ธ โ€” ๋ฐฐํฌ ์งํ›„ ํ•„์ˆ˜)
k6 run k6-scripts/scenarios/smoke/smoke-test.js

# Video Load Test (Redis ์บ์‹œ ์„ฑ๋Šฅ)
k6 run k6-scripts/scenarios/load/video-load.js

# Video Cache Stampede Test (ํ•ซํ‚ค ์บ์‹œ ๋งŒ๋ฃŒ ๋™์‹œ ์š”์ฒญ)
k6 run k6-scripts/scenarios/load/video-cache-stampede.js

# Comment Load Test (MongoDB CRUD)
k6 run k6-scripts/scenarios/load/comment-load.js

# Coupon Load Test (Redisson ๋ถ„์‚ฐ ๋ฝ) โ€” ํ•ต์‹ฌ
k6 run k6-scripts/scenarios/load/coupon-load.js

# Stress Test (ํ•œ๊ณ„์  ํƒ์ƒ‰)
k6 run k6-scripts/scenarios/stress/stress-test.js

# Spike Test (ํŠธ๋ž˜ํ”ฝ ํญ์ฆ)
k6 run k6-scripts/scenarios/spike/spike-test.js

ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์„œ๋ฒ„ ์ง€์ •

BASE_URL=http://your-server:8080 k6 run k6-scripts/scenarios/smoke/smoke-test.js

Redis hot key / cache stampede ์‹คํ—˜

๋Œ€์ƒ API๋Š” ๋น„๋””์˜ค ์ƒ์„ธ ์กฐํšŒ GET /api/v1/videos/{videoId}์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ํ•ซ ๋น„๋””์˜ค๋ฅผ ํ•œ ๋ฒˆ warm-upํ•ด์„œ Redis์— ์ €์žฅํ•œ ๋’ค, ์งง์€ TTL ๋งŒ๋ฃŒ ์งํ›„ 100~300๊ฐœ VU๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ํ‚ค๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ ์žฌํ˜„ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# ๊ฐœ์„  ์ „ ๋ชจ๋“œ๋กœ ์•ฑ ์‹คํ–‰
VIDEO_CACHE_STAMPEDE_PROTECTION_ENABLED=false \
VIDEO_CACHE_DETAIL_TTL=3s \
./gradlew :video-apps:app-api:bootRun

# TTL ๋งŒ๋ฃŒ ์งํ›„ 200 ๋™์‹œ ์š”์ฒญ
STAMPEDE_CONCURRENCY=200 \
CACHE_TTL_SECONDS=3 \
HOT_VIDEO_ID=video-seed-001 \
k6 run k6-scripts/scenarios/load/video-cache-stampede.js

๊ฐœ์„  ํ›„ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# ๊ฐœ์„  ํ›„ ๋ชจ๋“œ๋กœ ์•ฑ ์‹คํ–‰
VIDEO_CACHE_STAMPEDE_PROTECTION_ENABLED=true \
VIDEO_CACHE_DETAIL_TTL=3s \
./gradlew :video-apps:app-api:bootRun

# ๊ฐ™์€ ์กฐ๊ฑด์œผ๋กœ ์žฌ์‹คํ–‰
STAMPEDE_CONCURRENCY=200 \
CACHE_TTL_SECONDS=3 \
HOT_VIDEO_ID=video-seed-001 \
k6 run k6-scripts/scenarios/load/video-cache-stampede.js

์›์ธ์€ Cache-Aside ๊ตฌ์กฐ์—์„œ ํ•ซํ‚ค TTL์ด ๋งŒ๋ฃŒ๋˜๋Š” ์ˆœ๊ฐ„ ๋ชจ๋“  ์š”์ฒญ์ด ๋™์‹œ์— cache miss๋ฅผ ๋งž๊ณ  MySQL ์กฐํšŒ๋กœ ์ง„์ž…ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐœ์„  ํ›„ ๋ชจ๋“œ๋Š” Redisson RLock ๊ธฐ๋ฐ˜ per-key lock์„ ์‚ฌ์šฉํ•˜๊ณ , ๋ฝ ํš๋“ ํ›„ DB ์กฐํšŒ ์ „ ์บ์‹œ๋ฅผ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋ฝ์„ ์–ป์ง€ ๋ชปํ•œ ์š”์ฒญ์€ ์งง๊ฒŒ ๋Œ€๊ธฐํ•˜๋ฉฐ ์บ์‹œ๋ฅผ ์žฌ์กฐํšŒํ•˜๊ณ , ์ œํ•œ ์‹œ๊ฐ„ ๋‚ด ์บ์‹œ๊ฐ€ ์ฑ„์›Œ์ง€๋ฉด DB๋ฅผ ์กฐํšŒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋๊นŒ์ง€ ์บ์‹œ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ๋Œ€๊ธฐ ์ œํ•œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๊ธฐ๋กํ•œ ๋’ค DB fallback์œผ๋กœ ์‘๋‹ต์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Actuator/Prometheus์—์„œ ํ™•์ธํ•  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”ํŠธ๋ฆญ์ž…๋‹ˆ๋‹ค.

๋ฉ”ํŠธ๋ฆญ ์˜๋ฏธ
video.cache.hit ๋น„๋””์˜ค ์ƒ์„ธ ์บ์‹œ hit
video.cache.miss ๋น„๋””์˜ค ์ƒ์„ธ ์ดˆ๊ธฐ cache miss
video.cache.db.load ์‹ค์ œ MySQL ๋น„๋””์˜ค ์ƒ์„ธ ์กฐํšŒ
video.cache.lock.acquired stampede lock ํš๋“
video.cache.lock.wait lock ํš๋“ ์‹คํŒจ ํ›„ ์บ์‹œ ๋Œ€๊ธฐ
video.cache.lock.timeout ๋Œ€๊ธฐ ์ œํ•œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ํ›„ DB fallback

๊ฒฐ๊ณผ ๊ธฐ๋ก ํ‘œ์ž…๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ธํ”„๋ผ์™€ ์‹œ๋“œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋œ ํ™˜๊ฒฝ์—์„œ ์œ„ ๋ช…๋ น์œผ๋กœ ์ธก์ •ํ•œ ๊ฐ’์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“œ ๋™์‹œ ์š”์ฒญ TTL video.cache.db.load ์ฆ๊ฐ€๋Ÿ‰ video.cache.lock.acquired ์ฆ๊ฐ€๋Ÿ‰ video.cache.lock.timeout ์ฆ๊ฐ€๋Ÿ‰ k6 P95 ๋น„๊ณ 
๊ฐœ์„  ์ „ 200 3s ์•ฝ 200๊ฑด 0๊ฑด 0๊ฑด ์•ฝ 300ms stampede protection off, ์ž„์‹œ๊ฐ’
๊ฐœ์„  ํ›„ 200 3s ์•ฝ 1๊ฑด 1๊ฑด 0๊ฑด ์•ฝ 80ms stampede protection on, ์ž„์‹œ๊ฐ’

๊ฒฐ๊ณผ๋ฅผ JSON์œผ๋กœ ์ €์žฅ

k6 run --out json=result.json k6-scripts/scenarios/load/coupon-load.js

Grafana + InfluxDB ์—ฐ๋™ (์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง)

k6 run --out influxdb=http://localhost:8086/k6 k6-scripts/scenarios/stress/stress-test.js

์„ฑ๊ณต ๊ธฐ์ค€

์‹œ๋‚˜๋ฆฌ์˜ค ๋ชฉํ‘œ ์ง€ํ‘œ ์ž„๊ณ„๊ฐ’
Smoke P95 ์‘๋‹ต์‹œ๊ฐ„ < 500ms
Smoke ์‹คํŒจ์œจ < 1%
Video Load P95 ์‘๋‹ต์‹œ๊ฐ„ (์กฐํšŒ) < 100ms
Video Load Redis ์บ์‹œ ํžˆํŠธ์œจ > 80%
Video Load ์‹คํŒจ์œจ < 1%
Comment Load P95 ์‘๋‹ต์‹œ๊ฐ„ (์ž‘์„ฑ) < 200ms
Comment Load P95 ์‘๋‹ต์‹œ๊ฐ„ (๋ชฉ๋ก) < 150ms
Comment Load ์‹คํŒจ์œจ < 2%
Coupon Load ๋ฐœ๊ธ‰ ์„ฑ๊ณต ๊ฑด์ˆ˜ โ‰ค 500๊ฑด (์ ˆ๋Œ€)
Coupon Load ์„œ๋ฒ„ ์—๋Ÿฌ์œจ < 5%
Stress P99 ์‘๋‹ต์‹œ๊ฐ„ < 2000ms
Stress ์‹คํŒจ์œจ < 10%
Spike P99 ์‘๋‹ต์‹œ๊ฐ„ < 3000ms
Spike ์‹คํŒจ์œจ < 15%

API ์—”๋“œํฌ์ธํŠธ ์ฐธ์กฐ

๊ธฐ๋Šฅ ๋ฉ”์„œ๋“œ URL ์ธ์ฆ ํ•„์š”
๋น„๋””์˜ค ์กฐํšŒ GET /api/v1/videos/{videoId} ๋ถˆํ•„์š”
์ฑ„๋„๋ณ„ ๋น„๋””์˜ค ๋ชฉ๋ก GET /api/v1/videos?channelId={id} ๋ถˆํ•„์š”
๋น„๋””์˜ค ์ƒ์„ฑ POST /api/v1/videos ๋ถˆํ•„์š”
์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€ POST /api/v1/videos/{videoId}/view ๋ถˆํ•„์š”
์ฑ„๋„ ์ƒ์„ฑ POST /api/v1/channels ๋ถˆํ•„์š”
์ฑ„๋„ ์ˆ˜์ • PUT /api/v1/channels/{channelId} ๋ถˆํ•„์š”
์ฑ„๋„ ์กฐํšŒ GET /api/v1/channels/{channelId} ๋ถˆํ•„์š”
๋Œ“๊ธ€ ์ž‘์„ฑ POST /api/v1/comments ํ•„์š”
๋Œ“๊ธ€ ์ˆ˜์ • PUT /api/v1/comments/{commentId} ํ•„์š”
๋Œ“๊ธ€ ์‚ญ์ œ DELETE /api/v1/comments/{commentId} ํ•„์š”
๋Œ“๊ธ€ ๋ชฉ๋ก GET /api/v1/comments/list?videoId=&order=&offset=&maxSize= ํ•„์š”
๋Œ€๋Œ“๊ธ€ ๋ชฉ๋ก GET /api/v1/comments/reply?parentId=&offset=&maxSize= ๋ถˆํ•„์š”
๊ตฌ๋… POST /api/v1/subscribe?channelId={id} ํ•„์š”
๊ตฌ๋… ์ทจ์†Œ DELETE /api/v1/subscribe?subscribeId={id} ํ•„์š”
๋‚ด ๊ตฌ๋… ๋ชฉ๋ก GET /api/v1/subscribe/mine ํ•„์š”
์ฟ ํฐ ๋ฐœ๊ธ‰ POST /api/v1/coupons?couponPolicyId={id} ํ•„์š”
๋น„๋””์˜ค ์ข‹์•„์š” POST /api/v1/videos/rate?videoId=&rating=like ํ•„์š”
๋„์„œ ๊ฒ€์ƒ‰ GET /api/v1/book/list?query=&page=&size= ๋ถˆํ•„์š”

์ธ์ฆ: x-auth-key ํ—ค๋”์— ํ† ํฐ ๊ฐ’ ์ „๋‹ฌ (HeaderAttribute.java ์ฐธ์กฐ)

์ปค์Šคํ…€ ๋ฉ”ํŠธ๋ฆญ

๋ฉ”ํŠธ๋ฆญ ์œ ํ˜• ์„ค๋ช…
video_cache_hit_rate Rate 50ms ๋ฏธ๋งŒ ์‘๋‹ต = Redis ์บ์‹œ ํžˆํŠธ ํŒ์ •
video_get_duration Trend ๋น„๋””์˜ค ๋‹จ๊ฑด ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„
video_view_duration Trend ์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€ ์‘๋‹ต์‹œ๊ฐ„
stampede_video_get_duration Trend cache stampede ์‹คํ—˜์˜ ๋น„๋””์˜ค ๋‹จ๊ฑด ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„
stampede_success_total Counter cache stampede ์‹คํ—˜์˜ ์„ฑ๊ณต ์‘๋‹ต ์ˆ˜
stampede_failed_total Counter cache stampede ์‹คํ—˜์˜ ์‹คํŒจ ์‘๋‹ต ์ˆ˜
comment_create_duration Trend ๋Œ“๊ธ€ ์ž‘์„ฑ ์‘๋‹ต์‹œ๊ฐ„
comment_list_duration Trend ๋Œ“๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„
comment_reply_duration Trend ๋Œ€๋Œ“๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„
coupon_issue_success_total Counter ์ฟ ํฐ ๋ฐœ๊ธ‰ ์„ฑ๊ณต ๊ฑด์ˆ˜
coupon_issue_conflict_total Counter ์žฌ๊ณ  ์†Œ์ง„/์ค‘๋ณต (์ •์ƒ ์‹คํŒจ)
coupon_issue_failed_total Counter ์„œ๋ฒ„ ์—๋Ÿฌ (๋น„์ •์ƒ ์‹คํŒจ)
coupon_success_rate Rate ์ฟ ํฐ ๋ฐœ๊ธ‰ ์„ฑ๊ณต๋ฅ 
circuit_breaker_open_total Counter Circuit Breaker ์˜คํ”ˆ ํšŸ์ˆ˜
spike_video_get_duration Trend ์ŠคํŒŒ์ดํฌ ๊ตฌ๊ฐ„ ๋น„๋””์˜ค ์กฐํšŒ ์‘๋‹ต์‹œ๊ฐ„
spike_error_rate Rate ์ŠคํŒŒ์ดํฌ ๊ตฌ๊ฐ„ ์—๋Ÿฌ์œจ

Rules

๋‹ค์Œ์˜ ๋ฃฐ์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

๐Ÿ’ฌ Commit Convention

์ž‘์—… ํƒœ๊ทธ ์„ค๋ช…
feat ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ / ์ผ๋ถ€ ์ฝ”๋“œ ์ถ”๊ฐ€ / ์ผ๋ถ€ ์ฝ”๋“œ ์ˆ˜์ • (๋ฆฌํŒฉํ† ๋ง๊ณผ ๊ตฌ๋ถ„) / ๋””์ž์ธ ์š”์†Œ ์ˆ˜์ •
fix ๋ฒ„๊ทธ ์ˆ˜์ •
refactor ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง
style ์ฝ”๋“œ ์˜๋ฏธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๋ณ€๊ฒฝ์‚ฌํ•ญ (์ฝ”๋“œ ํฌ๋งทํŒ…, ์˜คํƒ€ ์ˆ˜์ •, ๋ณ€์ˆ˜๋ช… ๋ณ€๊ฒฝ, ์—์…‹ ์ถ”๊ฐ€)
chore ๋นŒ๋“œ ๋ถ€๋ถ„ ํ˜น์€ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ˆ˜์ • ์‚ฌํ•ญ / ํŒŒ์ผ ์ด๋ฆ„ ๋ณ€๊ฒฝ ๋ฐ ์œ„์น˜ ๋ณ€๊ฒฝ / ํŒŒ์ผ ์‚ญ์ œ
docs ๋ฌธ์„œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •
rename ํŒจํ‚ค์ง€ ํ˜น์€ ํด๋”๋ช…, ํด๋ž˜์Šค๋ช… ์ˆ˜์ • (๋‹จ๋…์œผ๋กœ ์‹œํ–‰ํ•˜์˜€์„ ์‹œ)
remove ํŒจํ‚ค์ง€ ํ˜น์€ ํด๋”, ํด๋ž˜์Šค๋ฅผ ์‚ญ์ œํ•˜์˜€์„ ๋•Œ (๋‹จ๋…์œผ๋กœ ์‹œํ–‰ํ•˜์˜€์„ ์‹œ)

About

springboot-video-platform

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors