Skip to content

ttasjwi/board-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

97 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ‘‹ Board-System (๊ฒŒ์‹œํŒ ํ”„๋กœ์ ํŠธ)

๐Ÿฅ… ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

  • ์„œ๋น„์Šค ์„ค๋ช… : ์‚ฌ์šฉ์ž๋“ค์ด ๋ˆ„๊ตฌ๋‚˜ ๊ฒŒ์‹œํŒ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , ๊ฒŒ์‹œํŒ์„ ํ†ตํ•ด ์ •๋ณด, ์ผ์ƒ ๊ณต์œ ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•œ Restful API ํ˜•ํƒœ์˜ ์ปค๋ฎค๋‹ˆํ‹ฐ ์„œ๋น„์Šค
    • ๋ฐฑ์—”๋“œ ๊ธฐ๋Šฅ์— ์ง‘์ค‘ํ•˜๊ณ ์ž, ํ”„๋ก ํŠธ์—”๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์€ ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • API ๋ฃจํŠธ ๊ฒฝ๋กœ : https://api.board-system.site
  • API ๋ฌธ์„œ : https://api.board-system.site/docs
  • Github ๋งํฌ : https://github.com/ttasjwi/board-system
  • Wiki: https://github.com/ttasjwi/board-system/wiki
  • ์ฃผ์š”๊ธฐ๋Šฅ : ๋กœ๊ทธ์ธ/์†Œ์…œ๋กœ๊ทธ์ธ, ๊ฒŒ์‹œ๊ธ€ ๊ธฐ๋Šฅ, ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š”/์‹ซ์–ด์š” ๊ธฐ๋Šฅ, ๊ฒŒ์‹œ๊ธ€ ๋Œ“๊ธ€ ๊ธฐ๋Šฅ, ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜ ๊ธฐ๋Šฅ, โ€ฆ
  • Kotlin, Spring Boot, MySQL, Redis, JPA, AWS(EC2, RDS, ElastiCache), Docker, Github Actions, K6, ...

๐Ÿ› ๏ธ ๊ธฐ์ˆ /๋ฌธ์ œํ•ด๊ฒฐ ์ƒ์„ธ

  • ๋„์ž…๋ฐฐ๊ฒฝ

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

    • ํšŒ์› 1,200๋งŒ ๊ฑด, ๊ฒŒ์‹œ๊ธ€ 2,400๋งŒ ๊ฑด, ๋Œ“๊ธ€ 2,400๋งŒ ๊ฑด ๋“ฑ ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ์šด์˜ ํ™˜๊ฒฝ์— ๊ฐ€๊นŒ์šด ๋ฐ์ดํ„ฐ ์Šค์ผ€์ผ์„ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋‹จ์ˆœํžˆ ๋กœ์ปฌ ์ปดํ“จํ„ฐ ํ…Œ์ŠคํŠธ์—์„œ ๊ทธ์น˜์ง€ ์•Š๊ณ , AWS EC2 ์ธ์Šคํ„ด์Šค์— ์‹ค์ œ๋กœ ์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ•จ์œผ๋กœ์„œ ์‹ค๋ฌด์™€ ์œ ์‚ฌํ•œ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋กœ๋Š” ์˜คํ”ˆ์†Œ์Šค์ธ k6๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. k6๋Š” ๊ฐ€๋ณ๊ณ , ์„ค์น˜ ๋ฐ ์„ค์ •์ด ๊ฐ„ํŽธํ•˜๋ฉฐ, ๋ถ€ํ•˜ ์ƒ์„ฑ ๋Šฅ๋ ฅ๋„ ์ถฉ๋ถ„ํ•ด ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๋™์‹œ ์š”์ฒญ ์ˆ˜๋ฅผ 1๋ช…๋ถ€ํ„ฐ 50๋ช…(๋งŽ๊ฒŒ๋Š” 1,000๋ช…)๊นŒ์ง€ ์ ์ง„์ ์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ, ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ ์ˆ˜์™€ ์‘๋‹ต ์‹œ๊ฐ„์˜ ๋ณ€ํ™” ๋“ฑ์„ ์ธก์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    performance-test-1

    performance-test-2

    • ๋กœ์ปฌ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ์›ํ™œํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋˜ ๊ธฐ๋Šฅ์ด์—ˆ์ง€๋งŒ, ์‹ค์ œ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์‘๋‹ต ์†๋„๊ฐ€ ์ตœ๋Œ€ 30์ดˆ๊นŒ์ง€ ์น˜์†Ÿ๊ณ , TPS๊ฐ€ ๋ถˆ์•ˆ์ •ํ•ด์ง€๋Š” ํ˜„์ƒ์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • CloudWatch๋ฅผ ํ†ตํ•ด RDS์˜ CPU ์‚ฌ์šฉ๋ฅ ์€ ๊ธ‰์ฆํ•œ ๋ฐ˜๋ฉด EC2๋Š” ๋‚ฎ์€ ์ˆ˜์ค€์„ ์œ ์ง€ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ ์—์„œ DB ์„ฑ๋Šฅ ๋ณ‘๋ชฉ์ด ์›์ธ์ž„์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ๊ณ„๊ธฐ๋กœ DB ์ธ๋ฑ์‹ฑ ์ตœ์ ํ™”, ์กฐํšŒ ์ฟผ๋ฆฌ ๊ฐœ์„ , ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค ํ™œ์šฉ ๋“ฑ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์ง„ํ–‰ํ–ˆ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜ ํ•ญ๋ชฉ์—์„œ ์ƒ์„ธํžˆ ์„œ์ˆ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋„์ž…๋ฐฐ๊ฒฝ

    • ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ, ๊ฒŒ์‹œ๊ธ€ ์ˆ˜๊ฐ€ ์•ฝ 2,400๋งŒ ๊ฑด์— ์ด๋ฅด๋Š” ์ƒํ™ฉ์—์„œ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์†๋„๊ฐ€ ๊ธ‰๊ฒฉํžˆ ๋А๋ ค์ง€๋Š” ํ˜„์ƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ ์›์ธ ๋ถ„์„
    index-1

    • MySQL์˜ ์‹คํ–‰ ๊ณ„ํš ๋„๊ตฌ(EXPLAIN, EXPLAIN ANALYZE)๋ฅผ ํ™œ์šฉํ•ด ์ฟผ๋ฆฌ์˜ ๋น„ํšจ์œจ์ ์ธ ์‹คํ–‰ ๋ฐฉ์‹์„ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ถ„์„ ๊ฒฐ๊ณผ, ๊ธฐ์กด ์ฟผ๋ฆฌ๋Š” ๋ชจ๋“  ํ…Œ์ด๋ธ”์„ ํ’€์Šค์บ”ํ•œ ๋’ค ์กฐ๊ฑด์— ๋งž๋Š” ๊ฒŒ์‹œ๊ธ€์„ ํ•„ํ„ฐ๋งํ•˜๊ณ , ์ •๋ ฌ ๋ฐ Offset์„ ์ ์šฉํ•ด LIMIT ์ˆ˜๋งŒํผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ด๋Š” ์ •๋ ฌ ๊ธฐ์ค€์ด ์ธ๋ฑ์Šค์— ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์•„, ๋งค๋ฒˆ ์ •๋ ฌ ๋ฐ ํ•„ํ„ฐ๋ง ์—ฐ์‚ฐ์ด ์ถ”๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์—ˆ๊ณ , ์ด๋กœ ์ธํ•ด ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ์ €ํ•˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์„ ๋ฐฉํ–ฅ
    index-2.webp

    index-3.webp

    • MySQL์ด ํ•„ํ„ฐ๋ง๊ณผ ์ •๋ ฌ ์—†์ด๋„ ํšจ์œจ์ ์œผ๋กœ ๊ฒŒ์‹œ๊ธ€์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ€ํ‹ฐ ์ปฌ๋Ÿผ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ์ธ๋ฑ์Šค ๊ตฌ์„ฑ: board_id ASC, article_id DESC
    • ์ด ์ธ๋ฑ์Šค๋Š” โ€œํŠน์ • ๊ฒŒ์‹œํŒ ๋‚ด์—์„œ ์ตœ์‹  ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ์กฐํšŒโ€ ํ•˜๋Š” ํŒจํ„ด๊ณผ ์™„์ „ํžˆ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.
    • ์ธ๋ฑ์Šค๊ฐ€ ์ ์šฉ๋œ ์ดํ›„, MySQL์€ ๋” ์ด์ƒ ํ…Œ์ด๋ธ” ํ’€์Šค์บ” ์—†์ด ์ธ๋ฑ์Šค ๋ ˆ์ธ์ง€ ์Šค์บ”๋งŒ์œผ๋กœ ํ•„์š”ํ•œ ๊ฒŒ์‹œ๊ธ€์„ ๋น ๋ฅด๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    index-4.webp

    index-5.webp

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

    index-6.webp

    • ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์„ฑ๊ณผ
      • ๊ฐœ์„  ์ „: ๋‚ฎ์€ ๋™์‹œ ์‚ฌ์šฉ์ž ์ˆ˜(VUs)์—์„œ๋„ TPS๊ฐ€ ๋ถˆ์•ˆ์ •
      • ๊ฐœ์„  ํ›„: TPS๊ฐ€ VUs 23 ๋ถ€๊ทผ์—์„œ ์ตœ๋Œ€ 20.8๊นŒ์ง€ ์ƒ์Šน, ๋ถ€ํ•˜๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌ
    • API ์‘๋‹ต ์†๋„ ๋น„๊ต
      • ๊ฐœ์„  ์ „: 500๋ฒˆ ๊ฒŒ์‹œํŒ์˜ 20ํŽ˜์ด์ง€ ์กฐํšŒ ์‹œ ์•ฝ 1,550ms
      • ๊ฐœ์„  ํ›„: ๋™์ผ ์กฐ๊ฑด์—์„œ ์•ฝ 43ms
        โ†’ ์•ฝ 36๋ฐฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ
    • ์ด์™€ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ๋Œ“๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์„ฑ๋Šฅ ์—ญ์‹œ ๋Œ€ํญ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
      • ๊ฐœ์„  ์ „: 1์‹œ๊ฐ„์ด ๋„˜๋„๋ก ๋Œ“๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต์ด ์˜ค์ง€ ์•Š์Œ. (์„ฑ๋Šฅ ํ™•์ธ ๋ถˆ๊ฐ€๋Šฅ ์ˆ˜์ค€)
      • ๊ฐœ์„  ํ›„: 104ms ๋งŒ์— ๋Œ“๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต์ด ์˜ค๋„๋ก API ์„ฑ๋Šฅ ๊ฐœ์„ 
  • ๋„์ž…๋ฐฐ๊ฒฝ

    • ์ธ๋ฑ์Šค๋ฅผ ์ ์šฉํ•ด 36๋ฐฐ์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์ด๋ฃจ์—ˆ์ง€๋งŒ, ์ธ๊ธฐ ๊ฒŒ์‹œํŒ์—์„œ ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๊ฐ€ 1,000 ์ด์ƒ์œผ๋กœ ์ปค์งˆ ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ ์›์ธ ๋ถ„์„
    upgrade-query-1

    • ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก OFFSET์ด ์ปค์ง€๊ณ , ๊ทธ๋กœ ์ธํ•ด ์กฐํšŒ ์‹œ๊ฐ„์ด ๊ธ‰๊ฒฉํžˆ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค.
    • ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ์กฐํšŒํ•  ๋•Œ ์ „์ฒด ๊ฒŒ์‹œ๊ธ€ ์ˆ˜๋ฅผ ๊ตฌํ•˜๊ธฐ ์œ„ํ•œ COUNT(*) ์ฟผ๋ฆฌ๋„ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์›์ธ์ด ๋ฉ๋‹ˆ๋‹ค.
    • ๋˜ํ•œ, ์กฐํšŒ ๋Œ€์ƒ ๊ฒŒ์‹œ๊ธ€์— ํฌํ•จ๋œ ์ปฌ๋Ÿผ๋“ค์ด ์ธ๋ฑ์Šค์— ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ํ…Œ์ด๋ธ” ์ ‘๊ทผ์ด ๋ฐœ์ƒํ•˜๊ณ , ์ด๋Š” Disk I/O ์ฆ๊ฐ€๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.
    • ๋”ฐ๋ผ์„œ ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ ์ •๋ ฌยทํ•„ํ„ฐ๋งํ•˜๊ณ , ์‹ค์ œ ํ…Œ์ด๋ธ” ์ ‘๊ทผ์€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉํ–ฅ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์„  ๋ฐฉํ–ฅ

    • ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ ์ œํ•œ: ๊ณผ๋„ํ•œ OFFSET์œผ๋กœ ์ธํ•œ ๋น„ํšจ์œจ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ฅผ 1~100 ์‚ฌ์ด๋กœ ์ œํ•œํ–ˆ์Šต๋‹ˆ๋‹ค.

    • ๊ฒŒ์‹œ๊ธ€ ์ˆ˜ ๋น„์ •๊ทœํ™”: ๊ฒŒ์‹œํŒ ๋ณ„ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜๋ฅผ ๋ณ„๋„ ํ…Œ์ด๋ธ”์—์„œ ๊ด€๋ฆฌํ•จ์œผ๋กœ์จ COUNT(*) ์—†์ด๋„ ์ด ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

    • ์ฟผ๋ฆฌ ํŠœ๋‹ (CTE + ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค):
      upgrade-query-2

      -OFFSET ์ ‘๊ทผ ์‹œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค๊ฐ€ ์ ์šฉ๋˜๋Š” ํ˜•ํƒœ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

      • ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ ์•ˆ์—์„œ CTE(Common Table Expression) ๋ฅผ ํ™œ์šฉํ•˜์—ฌ, ๋จผ์ € ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ ์›ํ•˜๋Š” ๊ฒŒ์‹œ๊ธ€ ID ๋ชฉ๋ก์„ ์ถ”์ถœํ•œ ๋’ค, ํ•ด๋‹น ID๋“ค์„ ๊ธฐ์ค€์œผ๋กœ ์‹ค์ œ ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
      • ์˜ˆ์‹œ ์ฟผ๋ฆฌ:
      WITH cte(article_id) AS (
          SELECT article_id
          FROM articles
          WHERE board_id = 1
          ORDER BY article_id DESC
          LIMIT 50 OFFSET 199950
      )
      SELECT ...
      FROM articles a
      JOIN cte ON a.article_id = cte.article_id
      ...
      • CTE ๋‚ด๋ถ€์˜ SELECT๋Š” ์ธ๋ฑ์Šค ์ปฌ๋Ÿผ(board_id, article_id)๋งŒ์„ ์กฐํšŒํ•˜์—ฌ ๋””์Šคํฌ ์ ‘๊ทผ ์—†์ด ์ธ๋ฑ์Šค ๋ ˆ๋ฒจ์—์„œ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
      • ์ดํ›„ ๋ณธ๋ฌธ ์ฟผ๋ฆฌ์—์„œ๋Š” JOIN์„ ํ†ตํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ํ…Œ์ด๋ธ”์—์„œ ์กฐํšŒํ•จ์œผ๋กœ์จ Disk I/O๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ , ์ •๋ ฌ/ํ•„ํ„ฐ๋ง ๋น„์šฉ๋„ ์ค„์ด๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ

    • ๊ฐœ์„  ์ „:

      • ์ตœ๋Œ€ TPS: 20.8 (VUs 23 ๊ธฐ์ค€)
      • RDS๊ฐ€ ๋ณ‘๋ชฉ ์ง€์ 
    • ๊ฐœ์„  ํ›„:
      upgrade-query-3

      upgrade-query-4

      • ์ตœ๋Œ€ TPS: 207.5 (VUs 224 ๊ธฐ์ค€, 10.4๋ฐฐ ํ–ฅ์ƒ)
      • RDS CPU ์‚ฌ์šฉ๋ฅ : 21.4%๋กœ ๊ฐ์†Œ
      • EC2 CPU ์‚ฌ์šฉ๋ฅ : 99%์— ๋„๋‹ฌ โ†’ ๋ณ‘๋ชฉ์ด EC2๋กœ ์ด๋™
    • ์ด๋ฅผ ํ†ตํ•ด ๋ณ‘๋ชฉ ์›์ธ์„ RDS โ†’ EC2 ๋กœ ์ด๋™์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ดํ›„ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ๋” ๋†’์ด๊ธฐ ์œ„ํ•ด์„œ๋Š” EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์ˆ˜ํ‰ ํ™•์žฅํ•˜๋Š” ๋ฐฉ์•ˆ์ด ํ•„์š”ํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋น„์šฉ ๋“ฑ์˜ ์ด์œ ๋กœ ์‹ค์ œ ํ™•์žฅ์€ ์ง„ํ–‰ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

  • ๋„์ž…๋ฐฐ๊ฒฝ
    like-count-1

    • ๊ฒŒ์‹œ๊ธ€์— "์ข‹์•„์š”"๋ฅผ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ, ์ข‹์•„์š” ์ˆ˜๋Š” ์กฐํšŒ ์„ฑ๋Šฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋น„์ •๊ทœํ™”๋œ ํ˜•ํƒœ๋กœ ๋ณ„๋„๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ์ˆ˜์ฒœ ๋ช…์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๊ฒŒ์‹œ๊ธ€์— ์ข‹์•„์š” ์š”์ฒญ์„ ๋ณด๋‚ผ ๊ฒฝ์šฐ, ๊ธฐ๋Œ€๊ฐ’๋ณด๋‹ค ์ ์€ ์ˆ˜์˜ ์ข‹์•„์š”๊ฐ€ ๋ฐ˜์˜๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, 3000๋ช…์ด ๋™์‹œ์— ์š”์ฒญํ–ˆ์Œ์—๋„ ์ข‹์•„์š” ์ˆ˜๋Š” 3000์ด ์•„๋‹Œ 302 ์ •๋„๋งŒ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ ์›์ธ ๋ถ„์„
    like-count-2

    • ํ•ด๋‹น ํ˜„์ƒ์€ ์ข‹์•„์š” ์ˆ˜๋ฅผ ์กฐํšŒ โ†’ ์ฆ๊ฐ€ โ†’ ์ €์žฅํ•˜๋Š” ๊ธฐ์กด์˜ ๊ตฌํ˜„ ๋ฐฉ์‹์ด ํŠธ๋žœ์žญ์…˜ ๊ฐ„ ์ถฉ๋Œ์— ์ทจ์•ฝํ•œ ๊ตฌ์กฐ์˜€๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋‹ค์ˆ˜์˜ ํŠธ๋žœ์žญ์…˜์ด ๋™์ผํ•œ ์ข‹์•„์š” ์ˆ˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ฑฐ์˜ ๋™์‹œ์— ์กฐํšŒํ•˜๊ณ , ๊ฐ์ž์˜ ์ƒํƒœ์—์„œ 1์„ ์ฆ๊ฐ€์‹œํ‚จ ํ›„ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ณ , ์ด๋กœ ์ธํ•ด ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ€ ๋ฎ์–ด์จ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ํŠนํžˆ ์ข‹์•„์š” ์ˆ˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์ดˆ๊ธฐํ™” ๊ณผ์ •์—์„œ๋„ ์ค‘๋ณต INSERT ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด, ๋‹จ์ˆœํžˆ ๋ฝ์„ ๊ฑธ์–ด๋„ ์™„์ „ํ•œ ํ•ด๊ฒฐ์ด ์–ด๋ ค์šด ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œํ•ด๊ฒฐ ๋ฐฉ์•ˆ ๊ณ ๋ฏผ

    • ๋‚™๊ด€์  ๋ฝ (Optimistic Lock)
      • version ํ•„๋“œ๋ฅผ ํ†ตํ•ด ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•˜๊ณ  ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
      • ๊ตฌํ˜„์€ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ๋™์‹œ ์š”์ฒญ์ด ๋งŽ์„ ๊ฒฝ์šฐ ์ถฉ๋Œ๋ฅ ์ด ๋†’์•„ ์„ฑ๋Šฅ ์ €ํ•˜ ์šฐ๋ ค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ตœ์ดˆ ์ข‹์•„์š”์ˆ˜ ์ดˆ๊ธฐํ™” ์‹œ ํ–‰ ์ž์ฒด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ค‘๋ณต insert ์ถฉ๋Œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    • ๋น„๊ด€์  ๋ฝ (Pessimistic Lock)
      • SELECT ... FOR UPDATE ๋ฅผ ์‚ฌ์šฉํ•ด ํ–‰์— ๋ฐฐํƒ€์  ๋ฝ์„ ๊ฑธ๊ณ  ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค
      • ์ •ํ™•๋„๋Š” ๋ณด์žฅ๋˜์ง€๋งŒ, ๋ณ‘๋ชฉ ๊ฐ€๋Šฅ์„ฑ๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ถ€ํ•˜ ์ฆ๊ฐ€ ์šฐ๋ ค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ตœ์ดˆ ์ข‹์•„์š”์ˆ˜ ์ดˆ๊ธฐํ™” ์‹œ ํ–‰ ์ž์ฒด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ค‘๋ณต insert ์ถฉ๋Œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    • Redis ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ ๋ฝ
      • Redis ์˜ SetIfAbsent ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•œ ๋ถ„์‚ฐ๋ฝ์„ ํ†ตํ•ด ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š” ๊ธฐ๋Šฅ์— ๋™์‹œ ์ ‘๊ทผ ์ œํ•œ์„ ๋‘ก๋‹ˆ๋‹ค.
      • ์ตœ์ดˆ ์ข‹์•„์š”์ˆ˜ ์ดˆ๊ธฐํ™” ๋ฅผ ํ•˜๋Š” ์ƒํ™ฉ์—๋„ Redis ๋ฅผ ํ†ตํ•ด ๋ฝ์„ ํš๋“ํ•˜๋ฏ€๋กœ, ์ค‘๋ณต insert ์ถฉ๋Œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ธด ํ•ฉ๋‹ˆ๋‹ค.
      • ์‹ ๋ขฐ์„ฑ์ด ๋†’์ง€๋งŒ ์ธํ”„๋ผ ๋ณต์žก๋„ ๋ฐ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    • ์ดˆ๊ธฐํ™” ์‹œ์  ๋ณ€๊ฒฝ + ๋‚™๊ด€์ ๋ฝ / ๋น„๊ด€์ ๋ฝ ์‚ฌ์šฉ
      • ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ฑ ์‹œ ์ข‹์•„์š” ์ˆ˜๋ฅผ ํ•จ๊ป˜ ์ดˆ๊ธฐํ™” ์ƒ์„ฑํ•˜์—ฌ ์ตœ์ดˆ ์ข‹์•„์š” ์‹œ์ ์—๋„ โ€˜์ข‹์•„์š” ์ˆ˜โ€™๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
      • ์œ„์˜ ๋‚™๊ด€์  ๋ฝ / ๋น„๊ด€์ ๋ฝ ๋ฐฉ์‹์„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
      • ํ•˜์ง€๋งŒ ๊ฒŒ์‹œ๊ธ€ ๋งฅ๋ฝ์—์„œ โ€˜๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š”โ€™ ๋งฅ๋ฝ์„ ์˜์กดํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ, ๋งฅ๋ฝ๊ฐ„ ๊ฐœ๋… ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ์ฑ…
    like-count-3

    like-count-4

    • ๊ธฐ์กด ๊ตฌ์กฐ์˜ ๋ณต์žก๋„์™€ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด์„œ๋„ ๋ฝ ์—†์ด ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, MySQL์˜ INSERT ... ON DUPLICATE KEY UPDATE ์ฟผ๋ฆฌ ์‚ฌ์šฉ์œผ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ์ข‹์•„์š” ์ˆ˜๋ฅผ ์กฐํšŒํ•˜๊ณ  ๊ฐ์ฒด๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋Œ€์‹ , ****๋งค ์š”์ฒญ ์‹œ์ ๋งˆ๋‹ค DB์— ์ง์ ‘ INSERT ์‹œ๋„
      • ์ด๋ฏธ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•œ ์ข‹์•„์š” ์ˆ˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ, ์ค‘๋ณตํ‚ค ์ถฉ๋Œ์„ ์œ ๋„ํ•ด UPDATE ๋กœ ์ „ํ™˜
      • ๊ฒฐ๊ณผ์ ์œผ๋กœ DB ์ƒํƒœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฆ‰์‹œ 1 ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์›์ž ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋จ
    • ์ด ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
      • ๋ฝ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ข‹์•„์š” ์ˆ˜ ๊ฐ์ฒด์˜ ์กด์žฌ ์œ ๋ฌด์™€ ๊ด€๊ณ„์—†์ด ๋™์ผํ•œ ์ฟผ๋ฆฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ถ”๊ฐ€์ ์ธ ์ธํ”„๋ผ ๋น„์šฉ ํˆฌ์ž ์—†์ด๋„ ์ •ํ™•์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ํ™•๋ณด ๊ฐ€๋Šฅ
  • ์„ฑ๊ณผ
    like-count-5

    like-count-6

    • ๊ธฐ์กด์—๋Š” ์ˆ˜์ฒœ ๋ช…์ด ๋™์‹œ์— ์š”์ฒญํ•ด๋„ ์ˆ˜์‹ญ ๊ฑด๋งŒ ๋ฐ˜์˜๋˜๋˜ ๋ฌธ์ œ๊ฐ€, 3000๋ช…์ด ๋™์‹œ์— ์ข‹์•„์š” ์š”์ฒญ ์‹œ ์ •ํ™•ํžˆ 3000๊ฑด์ด ๋ฐ˜์˜๋˜๋„๋ก ๊ฐœ์„ ๋์Šต๋‹ˆ๋‹ค.
    • Redis ๋“ฑ ์™ธ๋ถ€ ์บ์‹œ๋‚˜ ๋ฝ ์ €์žฅ์†Œ๋ฅผ ๋„์ž…ํ•˜์ง€ ์•Š๊ณ ๋„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(MySQL)์˜ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ์ •ํ•ฉ์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ๋กœ์ง์ด ๋„๋ฉ”์ธ ๊ฐ์ฒด ์ค‘์‹ฌ์ด ์•„๋‹Œ DB ์—ฐ์‚ฐ ์ค‘์‹ฌ์ด ๋˜๋Š” ์ ์—์„œ ๋‹ค์†Œ ์•„์‰ฌ์›€์ด ์žˆ์ง€๋งŒ, ์ •ํ•ฉ์„ฑ, ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ๋ชจ๋‘ ๋งŒ์กฑ์‹œํ‚ค๋Š” ์‹ค์šฉ์ ์ธ ์„ ํƒ์ด์˜€๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๋„์ž…๋ฐฐ๊ฒฝ
    view-count-1

    • ์šฐ๋ฆฌ ์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌ์šฉ์ž ์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€ API ๋ฅผ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญ์„ ํ•˜๋ฉด ๊ฒŒ์‹œ๊ธ€์˜ ์กฐํšŒ์ˆ˜๊ฐ€ 1 ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ๋™์‹œ์— ํŠน์ • ์‚ฌ์šฉ์ž๊ฐ€ ์•…์šฉ ๋ชฉ์ ์œผ๋กœ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€๋ฅผ ๋™์‹œ์— ๋งค์šฐ ๋งŽ์ด ์š”์ฒญํ•  ๊ฒฝ์šฐ, ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜๊ฐ€ ์ˆœ์‹๊ฐ„์— ํญ์ฆํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. (์•…์šฉ, ์–ด๋ทฐ์ง•)
  • ๊ฐœ์„ 
    view-count-2

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

    view-count-3

    • Redis ์—์„œ๋Š” setIfAbsent ๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
      • key ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด value ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €์žฅ์— ์„ฑ๊ณตํ•˜๋ฉด true ๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.(ttl ์ง€์ • ๊ฐ€๋Šฅ)
      • key ๊ฐ€ ์กด์žฌํ•˜๋ฉด value๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ false๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
    • ์ตœ์ดˆ ๋ฝ ํš๋“ ์‹œ๋„์‹œ, 10๋ถ„๊ฐ„ ์œ ํšจํ•˜๋„๋ก setIfAbsent ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ, 10๋ถ„๊ฐ„ value ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์—†๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
      • ์ตœ์ดˆ ์š”์ฒญ ์‹œ set์— ์„ฑ๊ณตํ•˜๋ฉด true ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋Š”๋ฐ(10๋ถ„๊ฐ„ ์ €์žฅ๋จ) ์ด๋ฅผ ๋ฝ ํš๋“ ์„ฑ๊ณต์œผ๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค.
      • ์ดํ›„ 10๋ถ„๊ฐ„ ์š”์ฒญ ์‹œ false ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๋Š”๋ฐ, ์ด๋ฅผ ๋ฝ ํšŒ๋“ ์‹คํŒจ๋กœ ๊ฐ„์ฃผํ•˜๊ฒŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
    • ์ด ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ ๋ถ„์‚ฐ๋ฝ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    view-count-4

    • ๋™์‹œ์— ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ 1๋งŒ๋ฒˆ ์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€ ์š”์ฒญ์„ ํ•˜๊ฒŒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ•œ ๊ฒฐ๊ณผ(100๊ฐœ ์Šค๋ ˆ๋“œ), ์กฐํšŒ์ˆ˜๋Š” 1 ์ •๋„๋งŒ ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    • 10๋ถ„ ์ด๋‚ด๋กœ ๊ฐ™์€ ์‚ฌ์šฉ์ž๊ฐ€ ์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€ ์–ด๋ทฐ์ง•์„ ํ•˜๋”๋ผ๋„ ์กฐํšŒ์ˆ˜๊ฐ€ ํญ์ฆํ•˜๋Š” ์–ด๋ทฐ์ง• ํ˜„์ƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

[GitHub Actions ๋ฅผ ํ™œ์šฉํ•œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•(with. Docker)]

  • ๋„์ž… ๋ฐฐ๊ฒฝ

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

    GitHub Actions์™€ Docker๋ฅผ ํ™œ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์ž๋™ํ™”ํ•˜๊ณ , EC2 + Nginx ๊ธฐ๋ฐ˜์˜ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.

    • CI ๋‹จ๊ณ„:

      • PR ์ƒ์„ฑ ์‹œ ์ž๋™์œผ๋กœ ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ ์‹คํ–‰
      • ํ…Œ์ŠคํŠธ ์‹คํŒจ ์‹œ ๋ณ‘ํ•ฉ ์ฐจ๋‹จ โ†’ ์ฝ”๋“œ ํ’ˆ์งˆ ๋ณด์žฅ
    • CD๋‹จ๊ณ„: master ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉ๋˜๋ฉด ์ž๋™์œผ๋กœ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰

      1. JAR โ†’ Docker ์ด๋ฏธ์ง€ ์ƒ์„ฑ
      2. DockerHub๋กœ Push
      3. EC2 ์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€ Pull ๋ฐ ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰
      4. ํ—ฌ์Šค์ฒดํฌ ์™„๋ฃŒ ์‹œ, Nginx์˜ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ๋ผ์šฐํŒ… ๋ณ€๊ฒฝ
      5. ์ด์ „ ๋ฒ„์ „ ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ ๋ฐ ๋ถˆํ•„์š”ํ•œ ์ด๋ฏธ์ง€ ์ •๋ฆฌ
    • ํ™˜๊ฒฝ ๊ฐ„ ์ผ๊ด€์„ฑ ํ™•๋ณด:

      • Docker๋ฅผ ํ†ตํ•ด ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ์ปจํ…Œ์ด๋„ˆํ™”ํ•จ์œผ๋กœ์จ, ํ™˜๊ฒฝ ์ฐจ์ด์— ์˜ํ•œ ๋ฐฐํฌ ์˜ค๋ฅ˜ ๋ฐฉ์ง€
      • EC2 ์ธ์Šคํ„ด์Šค ์–ด๋””์„œ๋“  ๋™์ผํ•œ ํ™˜๊ฒฝ ์กฐ๊ฑด์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ๊ฐ€๋Šฅ
  • ์„ฑ๊ณผ

    • ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ ๋ฐ ๋ฐฐํฌ๋กœ ์‹ค์ˆ˜ ๊ฐ€๋Šฅ์„ฑ ๊ฐ์†Œ ๋ฐ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ
    • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌํ˜„: ๊ธฐ์กด ํŠธ๋ž˜ํ”ฝ ์ฒ˜๋ฆฌ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ  ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „ํ™˜
    • Docker ๊ธฐ๋ฐ˜ ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ๋ฅผ ํ†ตํ•ด, ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ์„œ๋น„์Šค ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ํ˜ธ์ŠคํŠธ ํ™˜๊ฒฝ์—๋Š” ์˜ํ–ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ๋„์ž…๋ฐฐ๊ฒฝ
    architecture-1.webp

    • ๊ธฐ์กด์—๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์ด ํ•˜๋‚˜์˜ ๋ชจ๋“ˆ์— ๊ฒฐํ•ฉ๋˜๊ณ  ํŒจํ‚ค์ง€ ์˜์กด ๋ฐฉํ–ฅ์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์€ ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค.
    • ๊ธฐ๋Šฅ์ด ๋Š˜์–ด๋‚˜๋ฉด์„œ ์˜์กด์„ฑ ๋ฐฉํ–ฅ์ด ๋’ค์„ž์ด๊ณ  ํ†ต์ œ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹จ์ˆœํžˆ Redis ๊ธฐ๋Šฅ๋งŒ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์–ด๋„, ์ „ํ˜€ ์ƒ๊ด€์—†๋Š” RDBMS ์„ค์ • ์˜ค๋ฅ˜๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋Š” ์ผ์ด ์ž์ฃผ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์„ ๋ฐฉํ–ฅ

    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ์ ์šฉ + ์˜์กด์„ฑ ์—ญ์ „ ์›์น™(DIP) ์ ๊ทน ๋„์ž…
      • ๊ณ„์ธต ๊ฐ„ ์˜์กด์€ ๋‚ด๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐ
      • ์‹ค์ œ ๊ตฌํ˜„์ฒด ๋Œ€์‹  mock, fixture ๋“ฑ์„ ํ™œ์šฉํ•œ ๋…๋ฆฝ์  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
      • ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์ถ”์ƒํ™” ์ˆ˜์ค€์—์„œ์˜ ์ˆ˜์ •์œผ๋กœ ๋ณ€๊ฒฝ ์˜ํ–ฅ ์ตœ์†Œํ™”
    • ๊ธฐ๋Šฅ ๋‹จ์œ„ ๋ชจ๋“ˆํ™” ๋ฐ ์˜์กด ๋ฐฉํ–ฅ ๋ช…์‹œ์  ํ†ต์ œ
      • ํ‘œํ˜„ ๊ณ„์ธต์€ ๋„๋ฉ”์ธ ๊ณ„์ธต์„ ๋ชจ๋ฅด๋„๋ก
      • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ณ„์ธต์€ ์˜์†์„ฑ ๊ณ„์ธต์„ ๋ชจ๋ฅด๋„๋ก
      • ๊ฒŒ์‹œ๊ธ€์€ ์ข‹์•„์š” ๊ธฐ๋Šฅ์„ ๋ชจ๋ฅด๋„๋ก
      • Redis ๋ชจ๋“ˆ์€ RDBMS ๋ชจ๋“ˆ๊ณผ ์™„์ „ํžˆ ๋ถ„๋ฆฌ
  • ์„ฑ๊ณผ
    architecture-2.webp

    • ๊ณ„์ธต๋ณ„ ์—ญํ•  ๋ถ„๋ฆฌ์™€ ๋ชจ๋“ˆํ™”๋กœ ์ธํ•ด, ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋ณ๊ณ  ๋ช…ํ™•ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
      • ์˜ˆ: Redis ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ ์‹œ RDBMS ์„ค์ • ๋ถˆํ•„์š”
    • ๋ชจ๋“  ๊ณ„์ธต์€ ์ž์‹ ์—๊ฒŒ ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์˜์กดํ•˜๊ฒŒ ๋˜์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค.
    • ๋„๋ฉ”์ธ ๊ฐœ๋…์„ โ€˜๋งฅ๋ฝโ€™ ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ์˜์กด์„ฑ ๋ฐฉํ–ฅ์„ ๋ช…ํ™•ํžˆ ํ†ต์ œํ•ด ํ–ฅํ›„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ตฌ์กฐ๋กœ์˜ ์ „ํ™˜๋„ ์ˆ˜์›”ํ•œ ๊ธฐ๋ฐ˜์ด ๋งˆ๋ จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋˜๋Š” ๊ธฐ์ˆ  ๊ต์ฒด ์‹œ, ์ „์ฒด ๊ตฌ์กฐ๋ฅผ ๊ฑด๋“œ๋ฆด ํ•„์š” ์—†์ด ๋ถ€๋ถ„ ์ˆ˜์ • ๊ฐ€๋Šฅํ•œ ์œ ์—ฐํ•œ ๊ตฌ์กฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋„์ž…๋ฐฐ๊ฒฝ
    test-fixtures-1.webp

    • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ, ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ๋„๋ฉ”์ธ ๋ชจ๋ธ ๊ฐ์ฒด ์ƒ์„ฑ์—๋Š” ๋งŽ์€ ์–‘์˜ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ฝ”๋“œ๋“ค์€ ๋ณดํ†ต fixture ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์—์„œ ๋™์ผํ•œ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ณ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•  ๋•Œ๊ฐ€ ๋งŽ์€๋ฐ ์ด๋Ÿด ๊ฒฝ์šฐ test ์†Œ์Šค์…‹ ๋‚ด๋ถ€์—์„œ ๋™์ผํ•œ fixture ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

    test-fixtures-2.webp

    • ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์—์„œ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ„๋„์˜ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ณ ๋ชจ๋“ˆ์—์„œ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ, ์›๋ณธ ๋ชจ๋“ˆ ๋‚ด๋ถ€์—์„œ๋Š” ์บก์Аํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋„๋ฉ”์ธ ์ง€์‹์ด ์™ธ๋ถ€๋กœ ์œ ์ถœ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ , ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์„  ๋ฐฉํ–ฅ

    • gradle์˜ java-fixtures ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ™œ์šฉํ•˜์—ฌ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ณ๋ฅผ textFixtures ์†Œ์Šค์…‹์œผ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • ์›๋ณธ ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ ๊ฐ™์€ ๋ชจ๋“ˆ ๋‚ด์—์„œ, testFixtures ์†Œ์Šค์…‹์— ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ณ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›๋ณธ ๋ชจ๋“ˆ ๋‚ด๋ถ€์—์„œ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ณ ์ƒ์„ฑ์— ๊ด€ํ•œ ํ•ต์‹ฌ ๋„๋ฉ”์ธ ์ง€์‹์„ ์บก์Аํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    test-fixtures-3.webp

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

[JWT, Spring Security, AOP๋ฅผ ํ™œ์šฉํ•œ ์ธ์ฆ/์ธ๊ฐ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ•]

  • ๋„์ž…๋ฐฐ๊ฒฝ
    • REST API ๊ธฐ๋ฐ˜์˜ ์„œ๋น„์Šค์—์„œ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ๋งˆ๋‹ค ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•  ์ˆ˜๋‹จ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ๋˜ํ•œ API๋งˆ๋‹ค ์ ‘๊ทผ ๊ถŒํ•œ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—, ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ฑฐ๋‚˜, ๊ด€๋ฆฌ์ž๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ์„ธ๋ถ„ํ™”๋œ ์ธ๊ฐ€ ๋กœ์ง์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐฉ์‹์„ ์“ฐ๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ดค์œผ๋‚˜, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ํ™•์žฅ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์— ์ œ์•ฝ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ฐ API ๋ณ„๋กœ ์ธ์ฆ/์ธ๊ฐ€ ๊ธฐ๋Šฅ์„ ์ˆ˜๋™์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ์—” ๊ฐ API๋งˆ๋‹ค ๋ฐ˜๋ณต์ ์ธ ๋ณด์•ˆ ๋กœ์ง์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ์ˆ  ์„ ํƒ ๋ฐ ๊ตฌํ˜„
    • JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ (Access Token + Refresh Token)

      auth-1.webp

      • Statelessํ•œ ๊ตฌ์กฐ๋ฅผ ์œ„ํ•ด ์„ธ์…˜์ด ์•„๋‹Œ JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.
      • Access Token์—๋Š” ์‚ฌ์šฉ์ž ์‹๋ณ„์„ ์œ„ํ•œ ์ตœ์†Œ ์ •๋ณด๋งŒ ๋‹ด๊ณ , ์„œ๋ช… ํ‚ค๋ฅผ ์ด์šฉํ•ด ์œ„๋ณ€์กฐ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
      • ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ์œ ํšจ๊ธฐ๊ฐ„์ด ์งง์€ Access Token๊ณผ, ๊ฐฑ์‹  ์šฉ๋„์˜ Refresh Token์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
      • Refresh Token์€ Redis์— ์ €์žฅํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž๋‹น ์ตœ๋Œ€ 5๊ฐœ๊นŒ์ง€๋งŒ ์œ ์ง€ํ•˜์—ฌ ๋™์‹œ ๋กœ๊ทธ์ธ ์ˆ˜ ์ œํ•œ ๋ฐ ํ† ํฐ ํƒˆ์ทจ ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Spring Security๋ฅผ ํ™œ์šฉํ•œ ์ธ์ฆ ํ๋ฆ„ ๊ตฌ์„ฑ

      auth-2.webp

      • Spring Security์˜ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ ์ฒด์ธ ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ, ์ธ์ฆ ๋กœ์ง์„ MVC ์•ž๋‹จ์—์„œ ๊ณตํ†ต ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ์‚ฌ์šฉ์ž๋Š” ๋งค ์š”์ฒญ ์‹œ ํ† ํฐ์„ ํ—ค๋”์— ๋‹ด์•„ ์ „์†กํ•˜๋ฉฐ, ํ•„ํ„ฐ์—์„œ๋Š” ํ† ํฐ ํŒŒ์‹ฑ ๋ฐ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ฐ์ฒด(SecurityContext) ์ฃผ์ž…์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
      • ์ด ๊ตฌ์กฐ๋ฅผ ํ†ตํ•ด API ๋ฉ”์„œ๋“œ๋งˆ๋‹ค ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ณ  ์ „์—ญ ์ธ์ฆ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
    • Spring AOP ๊ธฐ๋ฐ˜ ์ธ๊ฐ€(Authorization) ์ฒ˜๋ฆฌ

      auth-3.webp

      • API๋ณ„ ์ ‘๊ทผ ์ œ์–ด๋Š” ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜(@RequireAuthnticated, @RequireAdminRole ๋“ฑ) ์„ ์ปจํŠธ๋กค๋Ÿฌ์— ์„ ์–ธํ•˜์—ฌ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
      • ๊ณตํ†ต ๊ถŒํ•œ ๊ฒ€์ฆ ๋กœ์ง์€ Aspect๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ, ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž๋™ ์ ์šฉ๋˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
      • AOP ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์‹ค์ œ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ์ธ๊ฐ€ ์‹คํŒจ ์‹œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ****์ปจํŠธ๋กค๋Ÿฌ ์ง„์ž…์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    • ์ธ์ฆยท์ธ๊ฐ€ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ, API๋งˆ๋‹ค ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.
    • ํ–ฅํ›„ ์„œ๋น„์Šค๊ฐ€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ํ™•์žฅ๋˜๋”๋ผ๋„, JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐฉ์‹์„ ํ†ตํ•ด ๊ฐ ์„œ๋น„์Šค ๊ฐ„ ์„ธ์…˜ ๊ณต์œ  ์—†์ด ์ธ์ฆ ์—ฐ๋™์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • Refresh Token์„ ๋ณ„๋„๋กœ ์ €์žฅํ•ด ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋‹ด์€ ์žˆ์ง€๋งŒ, ํšŒ์›/์ธ์ฆ ์„œ๋ฒ„ ๋‹จ์ผ ์ง€์ (์„œ๋น„์Šค ๋ถ„๋ฆฌ ์‹œ)์—์„œ๋งŒ ๊ด€๋ฆฌ๋˜๋ฏ€๋กœ ๊ตฌ์กฐ์ ์œผ๋กœ ๋‹จ์ˆœํ•˜๋ฉฐ, ํ† ํฐ ํƒˆ์ทจ์™€ ๊ฐ™์€ ๋ณด์•ˆ ์ด์Šˆ์—๋„ ๋” ๊ฒฌ๊ณ ํ•œ ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
    • ์‹ ๊ทœ API ์ถ”๊ฐ€ ์‹œ ์–ด๋…ธํ…Œ์ด์…˜ ํ•œ ์ค„๋งŒ์œผ๋กœ ๊ถŒํ•œ ์ œ์–ด๊ฐ€ ์ ์šฉ๋˜์–ด, ๋ณด์•ˆ ์ •์ฑ… ์ ์šฉ์ด ์ผ๊ด€๋˜๊ณ  ์œ ์ง€๋ณด์ˆ˜๋„ ๊ฐ„ํŽธํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

[์†Œ์…œ ๋กœ๊ทธ์ธ๊ธฐ๋Šฅ ๊ตฌํ˜„]

  • ๋„์ž…๋ฐฐ๊ฒฝ

    oauth2-1.webp

    oauth2-2.webp

    • ์ผ๋ฐ˜์ ์ธ ์ด๋ฉ”์ผ/ํŒจ์Šค์›Œ๋“œ ๋ฐฉ์‹ ์™ธ์—๋„, ์‚ฌ์šฉ์ž ์ ‘๊ทผ์„ฑ์ด ๋†’์€ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํŠนํžˆ Google, Kakao, Naver ๋“ฑ ๋Œ€์ค‘์ ์ธ ์„œ๋น„์Šค๋Š” OAuth2 ๊ธฐ๋ฐ˜ ์ธ์ฆ/์ธ๊ฐ€ ํ๋ฆ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๋Š” ๊ฐ ์„œ๋น„์Šค์— ๋ณ„๋„์˜ ๊ณ„์ •์„ ์ƒ์„ฑํ•  ํ•„์š” ์—†์ด, ๊ธฐ์กด ์†Œ์…œ ๊ณ„์ •์„ ํ™œ์šฉํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ์ƒํ™ฉ

    • ํ™•์žฅ์„ฑ ๋ฌธ์ œ

      oauth2-3.webp

      • OAuth2 ๊ธฐ๋ฐ˜์ด๋ผ๋„ ๊ฐ ์†Œ์…œ ์„œ๋น„์Šค๋งˆ๋‹ค API ๋ช…์„ธ๋‚˜ ์‘๋‹ต ํ˜•์‹์ด ์ƒ์ดํ•ฉ๋‹ˆ๋‹ค.
      • ์ดˆ๊ธฐ์—๋Š” ํ•˜๋‚˜์˜ ์†Œ์…œ ์„œ๋น„์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌํ˜„ํ•˜๋”๋ผ๋„, ๋‹ค๋ฅธ ์†Œ์…œ ์„œ๋น„์Šค๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋  ์šฐ๋ ค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ๊ฐ ์†Œ์…œ API๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ ์œ ์ง€๋ณด์ˆ˜ ๋ถ€๋‹ด์ด ์ปค์ง€๊ณ , ๊ณตํ†ต ํ๋ฆ„์„ ์žฌ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณด์•ˆ ๋ฌธ์ œ

      oauth2-4.webp

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

    • ์ดˆ๊ธฐ์—๋Š” Spring Security OAuth2 Client๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ๋งŽ์€ ์„ค์ •๊ณผ ๋กœ์ง์ด ์ž๋™ํ™”๋˜์–ด ๋น ๋ฅด๊ฒŒ ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ์ ์šฉํ•ด๋ณธ ๊ฒฐ๊ณผ, ์ „๋ฐ˜์ ์ธ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ๊ด€์ ์—์„œ ํ•œ๊ณ„๋ฅผ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค.
      • ์ฃผ์š” OAuth2 ์ธ์ฆ/์ธ๊ฐ€ ํ๋ฆ„์ด Spring Security ํ•„ํ„ฐ ์ฒด์ธ ๋ฐ ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ์— ์ˆจ๊ฒจ์ ธ ์žˆ์–ด, ๋กœ์ง์„ ๋ช…ํ™•ํžˆ ์ถ”์ ํ•˜๊ณ  ์ œ์–ดํ•˜๊ธฐ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค.
      • ์ผ๋ฐ˜์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ์ปจํŠธ๋กค๋Ÿฌ โ†’ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค์˜ ๋ช…ํ™•ํ•œ ํ๋ฆ„์„ ๋”ฐ๋ฅด์ง€๋งŒ, Spring Security OAuth2 Client ๋ฐฉ์‹์€ ๋กœ์ง์ด ํ•„ํ„ฐ ์ฒด์ธ ๊นŠ์ˆ™์ด ์œ„์น˜ํ•˜๊ณ  ์ถ”์ƒํ™”์˜ ๊นŠ์ด๊ฐ€ ๊นŠ์–ด์ ธ ๋””๋ฒ„๊น…๊ณผ ํ…Œ์ŠคํŠธ๊ฐ€ ๋ณต์žกํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
    • ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ, Spring Security์˜ OAuth2 ํ๋ฆ„์„ ๊ทธ๋Œ€๋กœ ๋ชจ๋ฐฉํ•˜๋˜, ์ปค์Šคํ…€ ๋ฐฉ์‹์œผ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ๋‚ด๋ถ€ ๋กœ์ง์„ ๊ธฐ์กด์˜ ์ด๋ฉ”์ผ ๋กœ๊ทธ์ธ ๋“ฑ ๋‹ค๋ฅธ ์ธ์ฆ API๋“ค๊ณผ ๋™์ผํ•˜๊ฒŒ, ์ปจํŠธ๋กค๋Ÿฌ โ†’ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค ์˜ ํ๋ฆ„ ์•ˆ์— ์†Œ์…œ ๋กœ๊ทธ์ธ ๋กœ์ง์„ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
      • ์ด๋ฅผ ํ†ตํ•ด ๋„๋ฉ”์ธ ๋กœ์ง์˜ ์ฑ…์ž„ ๋ถ„๋ฆฌ ์›์น™์„ ์œ ์ง€ํ•˜๋ฉด์„œ๋„, ๊ธฐ๋Šฅ ํ๋ฆ„์„ ์ผ๊ด€๋˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •

    • ์ธ๊ฐ€ ์š”์ฒญ ๋ถ„๋ฆฌ ๋ฐ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ๋ฆ„ ๊ตฌ์„ฑ

      • ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ์šฐ์„  ์šฐ๋ฆฌ API ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.(๋‹ค๋งŒ ์›น์‚ฌ์ดํŠธ๋Š” ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.)
      • ์„œ๋ฒ„๋Š” ์ธ๊ฐ€ ์š”์ฒญ์— ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ ์ƒ์„ฑ ๋ฐ ์ €์žฅํ•œ ๋’ค, ์‚ฌ์šฉ์ž๋ฅผ ์†Œ์…œ ์„œ๋น„์Šค๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ฉ๋‹ˆ๋‹ค.
      • ์ด ๋ฐฉ์‹์€ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ „์ ์œผ๋กœ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉฐ, ๋ณด์•ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณด์•ˆ ํŒŒ๋ผ๋ฏธํ„ฐ ์ ์šฉ

      • state: ์‚ฌ์šฉ์ž์˜ ์ธ๊ฐ€ ์š”์ฒญ ์ƒํƒœ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๊ฐ’

      • nonce: OpenID Connect ๋ฐฉ์‹์—์„œ ID ํ† ํฐ ์œ„๋ณ€์กฐ ๋ฐฉ์ง€์šฉ์œผ๋กœ ์‚ฌ์šฉ.

      • PKCE (Proof Key for Code Exchange) ์ ์šฉ

        oauth2-5.webp

        • code_verifier๋ฅผ ๋žœ๋ค์œผ๋กœ ์ƒ์„ฑ
        • code_challenge = hash(code_verifier)
        • ์ตœ์ดˆ ์ธ๊ฐ€ ์š”์ฒญ ์‹œ code_challenge ์ „์†ก
        • ํ† ํฐ ์š”์ฒญ ์‹œ code_verifier๋ฅผ ํ•จ๊ป˜ ๋ณด๋‚ด ๊ฒ€์ฆ
        • โ†’ ์ด๋ฅผ ํ†ตํ•ด ํ† ํฐ ์š”์ฒญ์˜ ์œ„์กฐ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ตฌ์กฐ ์„ค๊ณ„ ๋ฐ ์ถ”์ƒํ™” : Spring Security OAuth2 Client ํ๋ฆ„์„ ์ฐธ์กฐํ•˜์—ฌ ๊ณตํ†ต ํ๋ฆ„์„ ์ถ”์ƒํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

      oauth2-6.webp

      oauth2-7.webp

      • ๋‹ค์Œ์˜ ํ๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ์„œ๋น„์Šค๋ณ„ ์ฐจ์ด๋ฅผ ์„ค์ • ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณตํ†ต๋กœ์ง๊ณผ, ๊ฐœ๋ณ„ ๊ตฌํ˜„์‚ฌํ•ญ์„ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
        • ์†Œ์…œ์„œ๋น„์Šค๋ณ„ ์„ค์ •: ์„ค์ • ํŒŒ์ผ ๊ธฐ๋ฐ˜์œผ๋กœ ClientRegistration ์ž๋™ ์ƒ์„ฑ
        • ์‚ฌ์ „์š”์ฒญ ์ƒ์„ฑ: OAuth2AuthorizationRequest ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ €์žฅ
        • ์‚ฌ์šฉ์ž ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ: OAuth2AuthorizationRequest ๋ฅผ ํ†ตํ•ด ์ธ๊ฐ€ URL ์ƒ์„ฑ ํ›„ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์†Œ์…œ ์„œ๋น„์Šค๋กœ ์ด๋™
        • ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ: state ๊ฐ’์œผ๋กœ ์š”์ฒญ ์ •๋ณด ์กฐํšŒ ํ›„ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ
      • ์ „์ฒด ํ๋ฆ„์€ ๋‹จ์ผํ•œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค ๊ณ„์ธต์œผ๋กœ ์ถ”์ƒํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ดํ›„ ์†Œ์…œ ์„œ๋น„์Šค๊ฐ€ ์ถ”๊ฐ€๋˜๋”๋ผ๋„ ์„ค์ • ๋ฐ ์‘๋‹ต ๋งคํ•‘ ์ •๋„๋งŒ ํ™•์žฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ

    • ์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ ์™ธ์—๋„ Google, Naver, Kakao ๋กœ๊ทธ์ธ์ด ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
    • ์ธ๊ฐ€ ์š”์ฒญ์˜ ํ๋ฆ„์„ ์ „๋ฉด ํ†ต์ œํ•จ์œผ๋กœ์จ ๋ณด์•ˆ ์š”์†Œ(nonce, PKCE, state ๋“ฑ)๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ํ•ต์‹ฌ ํ๋ฆ„์€ ๊ณตํ†ตํ™” ๋ฐ ์ถ”์ƒํ™”๋˜์–ด ์žˆ์–ด, ํ–ฅํ›„ Facebook, GitHub ๋“ฑ์˜ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ถ”๊ฐ€๋„ ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ๋งŒ์œผ๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋„์ž…๋ฐฐ๊ฒฝ

    • API ๋ช…์„ธ๋Š” ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ๊ฐ„ ํ˜‘์—…์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ์ดˆ๊ธฐ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋Š” API ์‚ฌ์–‘์ด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๊ณ , ๊ทธ์— ๋”ฐ๋ฅธ ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ง€์—ฐ๋˜๋ฉฐ API ๋ฌธ์„œ์™€ ์‹ค์ œ ๋™์ž‘ ๊ฐ„ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ์ƒํ™ฉ
    rest-docs-1.webp

    • ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด๋ฉด, ๊ธฐ์กด ํšŒ์›๊ฐ€์ž… API ์—”๋“œํฌ์ธํŠธ๋Š” /api/v1/members ์ธ๋ฐ ์ดํ›„ /api/v1/users ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๊ณ  ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ GitHub Wiki์— ๋‚จ๊ฒจ๋‘” ๋ช…์„ธ๋Š” ์ „ํžˆ /api/v1/members๋กœ ๋˜์–ด ์žˆ์–ด ํ˜ผ๋ž€์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • API ๋ณ€๊ฒฝ ์‹œ ์ˆ˜๋™์œผ๋กœ ๋ช…์„ธ๋ฅผ ๊ฐฑ์‹ ํ•ด์•ผ ํ•˜๋Š” ์ ์ด ๊ฐœ๋ฐœ์ž-ํด๋ผ์ด์–ธํŠธ ๊ฐ„ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๋น„์šฉ์„ ์ฆ๊ฐ€์‹œํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋Œ€์•ˆ ๊ฒ€ํ† 

    • GitHub Wiki, Notion ๋“ฑ ์ˆ˜๊ธฐ ์ž‘์„ฑ (๊ธฐ์กด ๋ฐฉ์‹)
      • ์ž์œ ๋กœ์šด ์„œ์ˆ ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ๋ฐ˜์˜์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • Swagger
      • ์ž๋™ํ™”๋Š” ๋˜์ง€๋งŒ, ์‹ค์ œ ๋™์ž‘๊ณผ ์–ด๊ธ‹๋‚  ์ˆ˜ ์žˆ๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์— ๋ฌธ์„œํ™”๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ๊ฐ€ ์นจํˆฌํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
    • Spring Rest Docs (์ตœ์ข… ์„ ํƒ)
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ช…์„ธ๋ฅผ ์ƒ์„ฑํ•ด, API ๋ฌธ์„œ์™€ ์‹ค์ œ ๋™์ž‘์˜ ์™„์ „ํ•œ ์ผ์น˜๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
      • ๋ฌธ์„œํ™”๋ฅผ ์œ„ํ•œ ๋ณ„๋„ ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
      • Swagger ๋Œ€๋น„ UI๋Š” ๋‹ค์†Œ ๋‹จ์ˆœํ•˜์ง€๋งŒ, ์ •ํ™•์„ฑ ๋ฉด์—์„œ ํƒ์›”ํ•ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    rest-docs-2.webp

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

[๋‹ค๊ตญ์–ด ์ง€์› ๋ฐ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ๊ณตํ†ตํ™”]

  • ๋„์ž…๋ฐฐ๊ฒฝ

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

    • ์˜ˆ์™ธ ํด๋ž˜์Šค ์ถ”์ƒํ™”: ๋ชจ๋“  ๋„๋ฉ”์ธ ์˜ˆ์™ธ๋Š” CustomException์„ ์ƒ์†๋ฐ›์•„ ๊ตฌํ˜„๋˜๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

      abstract class CustomException(
          val status: ErrorStatus, // ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ด์œ ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ์ƒํƒœ ์ •๋ณด.
          val code: String, // ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ ๊ตฌ์„ฑ์„ ์œ„ํ•œ ์ฝ”๋“œ ("Error.xxx" ํ˜•์‹).
          val args: List<Any?>, // ๋ฉ”์‹œ์ง€ ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉํ•  ์ธ์ž๋“ค (๋นˆ ๋ฆฌ์ŠคํŠธ์ผ ๊ฒฝ์šฐ ์ธ์ž ์—†๊ณ , ์ˆœ์„œ๋Œ€๋กœ ์‚ฌ์šฉ๋จ).
          val source: String, // ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ํ•„๋“œ ๋˜๋Š” ๋งฅ๋ฝ์„ ์„ค๋ช…ํ•˜๋Š” ๊ฐ’ (์˜ˆ: "nickname")
          val debugMessage: String, // ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ (์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณต๋˜์ง€ ์•Š์Œ).
          cause: Throwable? = null // ๊ทผ๋ณธ ์›์ธ์ด ๋˜๋Š” ์˜ˆ์™ธ (์„ ํƒ์ ).
      ) : RuntimeException(debugMessage, cause)
      • ๊ฐ ์˜ˆ์™ธ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ง์ ‘ ๋“ค๊ณ  ์žˆ์ง€ ์•Š๊ณ , ์˜ˆ์™ธ ์ฝ”๋“œ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ณด์œ ํ•ฉ๋‹ˆ๋‹ค.
      • ์ด๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ์€ ๋กœ์ผ€์ผ ๋ฐ ๋ฉ”์‹œ์ง€ ํŒŒ์ผ์„ ํ†ตํ•ด ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
    • ๋‹ค๊ตญ์–ด ๋ฉ”์‹œ์ง€ ๊ตฌ์„ฑ (YAML)

      NullArgument:
        message: "ํ•„์ˆ˜๊ฐ’ ๋ˆ„๋ฝ"
        description: "''{0}''์€(๋Š”) ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."
      
      InvalidEmailFormat:
        message: "์œ ํšจํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ ํฌ๋งท"
        description: "์ด๋ฉ”์ผ ํฌ๋งท์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (email = {0})"
        
      # ์ƒ๋žต
      NullArgument:
        message: "Missing required value"
        description: "The field ''{0}'' is required."
      
      InvalidEmailFormat:
        message: "Invalid email format"
        description: "The email format is not valid. (email = {0})"
      # ์ƒ๋žต
      • ๊ฐ ๋ฉ”์‹œ์ง€๋Š” ์˜ˆ์™ธ ์ฝ”๋“œ (code)๋ฅผ ํ‚ค๋กœ ํ•˜์—ฌ ๊ตญ๊ฐ€๋ณ„๋กœ ๋‹ค๋ฅด๊ฒŒ ๋ฒˆ์—ญ๋ฉ๋‹ˆ๋‹ค.
      • {0}, {1} ๋“ฑ์˜ ์œ„์น˜ ์ธ์ž๋ฅผ ํ†ตํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ์œ ์—ฐํ•˜๊ฒŒ ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ณตํ†ตํ™”
      exception-1.webp

      • ์˜ˆ์™ธ๋Š” @RestControllerAdvice๋ฅผ ํ†ตํ•ด ์ „์—ญ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
        • ์ด๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Spring MVC๊ฐ€ ๋“ฑ๋กํ•œ HandlerExceptionResolver ๊ตฌํ˜„์ฒด๊ฐ€ @RestControllerAdvice ํด๋ž˜์Šค๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋กœ ์œ„์ž„ํ•˜์—ฌ ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
        • Accept-Language ํ—ค๋”๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ๋กœ์ผ€์ผ์„ ๊ฐ์ง€ํ•˜๊ณ ,
        • ์˜ˆ์™ธ ์ธ์Šคํ„ด์Šค์˜ code, args๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์ ˆํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
      • ์Šคํ”„๋ง MVC ์•ž๋‹จ(์˜ˆ: ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ)์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋„๋ก,
        • ์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์šฉ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
        • ์ด ํ•„ํ„ฐ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ HandlerExceptionResolver๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ๊ณตํ†ต ์ฒ˜๋ฆฌ ํ๋ฆ„์— ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณผ
    exception-2.webp

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

๐Ÿ›๏ธ ํ”„๋กœ์ ํŠธ ์™ธ๋ถ€ ์•„ํ‚คํ…์ฒ˜

infra.png

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ Amazon AWS ์ธํ”„๋ผ ์œ„์— ๋ฐฐํฌ ๋ฐ ์šด์˜๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค ์ œ๊ณต๊ณผ ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ๋‹ค์–‘ํ•œ AWS ์„œ๋น„์Šค๋ฅผ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ฃผ์š” ์ธํ”„๋ผ ๊ตฌ์„ฑ
    • VPC (Virtual Private Cloud)
      • AWS์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ€์ƒ ์‚ฌ์„ค๋ง(Virtual Network).
      • ์„œ๋น„์Šค ์ „๋ฐ˜์„ ํ•˜๋‚˜์˜ VPC ๋‹จ์œ„๋กœ ๊ฒฉ๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • ์„œ๋ธŒ๋„ท ๋ถ„๋ฆฌ (Subnet)
      • ๋ณด์•ˆ ์ˆ˜์ค€์— ๋”ฐ๋ผ ํผ๋ธ”๋ฆญ/ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท์œผ๋กœ ๋ถ„๋ฆฌํ•ด ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ํผ๋ธ”๋ฆญ ์„œ๋ธŒ๋„ท: ์™ธ๋ถ€ ์‚ฌ์šฉ์ž ์ ‘๊ทผ์ด ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค (์˜ˆ: EC2).
      • ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท: ์™ธ๋ถ€ ์ ‘๊ทผ์ด ์ฐจ๋‹จ๋œ ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค (์˜ˆ: RDS, ElastiCache).
    • ๋ณด์•ˆ ๊ทธ๋ฃน (Security Group)
      • ELB, EC2, RDS, ElastiCache ๋“ฑ ๊ฐ ๋ฆฌ์†Œ์Šค๋งˆ๋‹ค ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ IP, ํฌํŠธ ๋ฒ”์œ„๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.
      • ์ตœ์†Œํ•œ์˜ ์ ‘๊ทผ๋งŒ ํ—ˆ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ELB (Elastic Load Balancer)
      • HTTPS ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
      • SLA(์„œ๋น„์Šค ์ˆ˜์ค€ ๊ณ„์•ฝ) ๊ธฐ์ค€ ๊ฐ€์šฉ์„ฑ:
        • ๋‹ค์ค‘ AZ: 99.99%
        • ๋‹จ์ผ AZ: 99.9%
      • ์š”์ฒญ์€ ELB๋ฅผ ํ†ตํ•ด EC2 ์ธ์Šคํ„ด์Šค๋กœ ๋ถ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.
      • ์ปจํ…Œ์ด๋„ˆ ์ˆ˜ํ‰ ํ™•์žฅ ์‹œ์—๋„ ์œ ์—ฐํ•œ ๋ถ€ํ•˜ ๋ถ„์‚ฐ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • Route 53 (DNS ์„œ๋น„์Šค)
      • AWS์˜ ๊ณ ๊ฐ€์šฉ์„ฑ ๋„๋ฉ”์ธ ๋„ค์ž„ ์‹œ์Šคํ…œ(DNS) ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
      • "api.board-system.site"์™€ ๊ฐ™์€ ๋„๋ฉ”์ธ ์ด๋ฆ„์„ ELB์— ๋งคํ•‘ํ•˜์—ฌ, ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์ด ์˜ฌ๋ฐ”๋ฅธ ์—”๋“œํฌ์ธํŠธ๋กœ ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • EC2
      • ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐฐํฌ๋˜๋Š” ๊ฐ€์ƒ ๋จธ์‹ .
      • ํ˜„์žฌ๋Š” ๋‹จ์ผ EC2 ์ธ์Šคํ„ด์Šค์—์„œ ์šด์˜๋˜๊ณ  ์žˆ์œผ๋ฉฐ, Nginx ๋ฐ Spring Boot ์ปจํ…Œ์ด๋„ˆ๊ฐ€ Docker ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.
    • RDS(Relational Database Service)
      • MySQL ๊ธฐ๋ฐ˜์˜ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค.
      • ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท์— ์œ„์น˜์‹œ์ผœ ์™ธ๋ถ€ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ•˜๊ณ , EC2 ์ปจํ…Œ์ด๋„ˆ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ElastiCache
      • ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ์„œ๋ฒ„๋กœ Redis๋ฅผ ์‚ฌ์šฉ.
      • ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท์— ์œ„์น˜ํ•˜๋ฉฐ, ์™ธ๋ถ€ ๋…ธ์ถœ ์—†์ด EC2 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ ํ๋ฆ„
    1. ์‚ฌ์šฉ์ž๊ฐ€ https://api.board-system.site ๋กœ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์ด ๋„๋ฉ”์ธ์€ Amazon Route 53์— ๋“ฑ๋ก๋˜์–ด ์žˆ์œผ๋ฉฐ, DNS ์งˆ์˜ ์‹œ AWS ELB(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ) ์˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.
    2. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์€ HTTPS ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ELB์˜ 443 ํฌํŠธ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ELB๋Š” ๋“ฑ๋ก๋œ EC2 ์ธ์Šคํ„ด์Šค๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ€ํ•˜ ๋ถ„์‚ฐํ•˜๋ฉฐ, ํ˜„์žฌ๋Š” ๋‹จ์ผ EC2 ์ธ์Šคํ„ด์Šค๊ฐ€ ๋™์ž‘ ์ค‘์ž…๋‹ˆ๋‹ค.
    3. ELB๋Š” EC2 ์ธ์Šคํ„ด์Šค์˜ 80๋ฒˆ ํฌํŠธ๋กœ ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๊ณ , ์ด ํฌํŠธ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Docker ์ปจํ…Œ์ด๋„ˆ์˜ 80๋ฒˆ ํฌํŠธ์™€ ํฌ์›Œ๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ปจํ…Œ์ด๋„ˆ์—๋Š” Nginx ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    4. Nginx๋Š” ํ˜„์žฌ ์„ค์ •์— ๋”ฐ๋ผ Blue ๋˜๋Š” Green ์ปจํ…Œ์ด๋„ˆ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋‘ ์ปจํ…Œ์ด๋„ˆ ๋ชจ๋‘ Spring Boot ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰ ์ค‘์ด๋ฉฐ, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    5. Blue/Green ๋ฐฐํฌ ์ „๋žต์ด ์ ์šฉ๋˜์–ด ์žˆ์–ด, ์ƒˆ๋กœ์šด ๋ฒ„์ „์ด ๋ฐฐํฌ๋  ๋•Œ๋Š” ํ˜„์žฌ ๋™์ž‘ ์ค‘์ด์ง€ ์•Š์€ ์ปจํ…Œ์ด๋„ˆ(Green ๋˜๋Š” Blue)์— ์ƒˆ ๋ฒ„์ „์„ ๋ฐฐํฌํ•˜๊ณ , ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•จ์„ ํ™•์ธํ•œ ํ›„ Nginx๊ฐ€ ์ƒˆ ์ปจํ…Œ์ด๋„ˆ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ(Zero Downtime Deployment) ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    6. ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋‚ด๋ถ€์ ์œผ๋กœ AWS์˜ RDS(MySQL) ์™€ ElastiCache(Redis) ๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ๋‘ ์„œ๋น„์Šค ๋ชจ๋‘ ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท ๋‚ด์— ์œ„์น˜ํ•˜์—ฌ ์™ธ๋ถ€ ์ ‘๊ทผ์ด ์ฐจ๋‹จ๋œ ์•ˆ์ „ํ•œ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

๐Ÿ›ข๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„

erd

  • ์šฐ๋ฆฌ ์‹œ์Šคํ…œ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ERD ์ž…๋‹ˆ๋‹ค.
  • ๋Œ€๋ถ€๋ถ„์€ ์ •๊ทœํ™” ๋œ ํ˜•ํƒœ๋กœ ์„ค๊ณ„ํ–ˆ์œผ๋‚˜, ์ผ๋ถ€๋ถ„ ํ…Œ์ด๋ธ”์€ ์„ฑ๋Šฅํ…Œ์ŠคํŠธ ์‹œ ๋ฐœ๊ฒฌํ•œ ์กฐํšŒ ์„ฑ๋Šฅ ๋ฌธ์ œ๋กœ ์ธํ•ด ๋น„์ •๊ทœํ™”๋ฅผ ํ–ˆ๊ณ , ์ •ํ•ฉ์„ฑ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ˆ˜์ค€์—์„œ ๋งž์ถ”๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตฌ์„ฑ ํ…Œ์ด๋ธ”
    • users: ์‚ฌ์šฉ์ž
    • social_connections: ์†Œ์…œ์—ฐ๋™
    • boards: ๊ฒŒ์‹œํŒ
    • article_categories: ๊ฒŒ์‹œ๊ธ€ ์นดํ…Œ๊ณ ๋ฆฌ
    • articles: ๊ฒŒ์‹œ๊ธ€
    • board_article_counts: ๊ฒŒ์‹œํŒ๋ณ„ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜ (๋น„์ •๊ทœํ™”)
    • article_likes: ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š”
    • article_like_counts: ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š” ์ˆ˜ (๋น„์ •๊ทœํ™”)
    • article_dislikes: ๊ฒŒ์‹œ๊ธ€ ์‹ซ์–ด์š”
    • article_dislike_counts: ๊ฒŒ์‹œ๊ธ€ ์‹ซ์–ด์š” ์ˆ˜ (๋น„์ •๊ทœํ™”)
    • article_comments: ๊ฒŒ์‹œ๊ธ€ ๋Œ“๊ธ€
    • article_comment_counts: ๊ฒŒ์‹œ๊ธ€ ๋Œ“๊ธ€ ์ˆ˜ (๋น„์ •๊ทœํ™”)

โœ… ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

test-code.png

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์•ˆ์ •์„ฑ, ์‹ ๋ขฐ์„ฑ ๋ณด์žฅ์„ ์œ„ํ•ด ๊ฐ ๋ชจ๋“ˆ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ๋ณธ๊ณ„์ธต์ธ api ๊ณ„์ธต, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ณ„์ธต, ๋„๋ฉ”์ธ ๊ณ„์ธต์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์ตœ๋Œ€ํ•œ ์œ ์ง€ํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. (์ธํ…”๋ฆฌ์ œ์ด ๊ธฐ์ค€ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ 100% ์ง€ํ–ฅ)
  • ์™ธ๋ถ€ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ถ„์€ ์ฃผ์š” ์ง€์ ์—์„œ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ถ”๊ฐ€์ ์ธ ๋Œ€์‘ ๋ฐ ํ…Œ์ŠคํŠธ ๋ณด๊ฐ•ํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ™œ์šฉ
    • CI/CD ์Šคํฌ๋ฆฝํŠธ์˜ ๋นŒ๋“œํ…Œ์ŠคํŠธ ๊ณผ์ •์—์„œ ํ•จ๊ป˜ ๊ณ„์† ์‹คํ–‰๋˜์–ด, ํ…Œ์ŠคํŠธ๊ฐ€ ๊นจ์ง€๋Š” ์ฝ”๋“œ๊ฐ€ ๋ฐฐํฌ๋˜์ง€ ์•Š๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    • Spring Rest Docs ๋ฌธ์„œํ™” ๊ณผ์ •์—์„œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ ํ†ต๊ณผ๋ฅผ ์„ ํ–‰ ์กฐ๊ฑด์œผ๋กœ ๋‘ ์œผ๋กœ์„œ, API ๋ฌธ์„œ ์ตœ์‹ ํ™”์—๋„ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • 2025.06.25 ๊ธฐ์ค€ 1102๊ฐœ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ณตํ†ต ๋ชจ๋“ˆ: 113๊ฐœ
      • core: 90๊ฐœ
      • data-serializer: 1๊ฐœ
      • id-generator: 2๊ฐœ
      • token: 20๊ฐœ
    • ์‚ฌ์šฉ์ž ๋ชจ๋“ˆ: 261๊ฐœ
      • user-application-output-port: 38๊ฐœ
      • user-application-service: 86๊ฐœ
      • user-domain: 116๊ฐœ
      • user-email-format-validate-adapter: 5๊ฐœ
      • user-email-oauth2-client-adapter: 3๊ฐœ
      • user-password-adapter: 3๊ฐœ
      • user-web-adapter: 10๊ฐœ
    • ๊ฒŒ์‹œํŒ ๋ชจ๋“ˆ: 128๊ฐœ
      • board-application-output-port: 16๊ฐœ
      • board-application-service: 27๊ฐœ
      • board-domain: 81๊ฐœ
      • board-web-adapter: 4๊ฐœ
    • ๊ฒŒ์‹œ๊ธ€ ๋ชจ๋“ˆ: 105๊ฐœ
      • article-application-output-port: 14๊ฐœ
      • article-application-service: 44๊ฐœ
      • article-domain: 40๊ฐœ
      • article-web-adapter: 7๊ฐœ
    • ๊ฒŒ์‹œ๊ธ€ ๋Œ“๊ธ€ ๋ชจ๋“ˆ: 101๊ฐœ
      • article-comment-application-output-port: 17๊ฐœ
      • article-comment-application-service: 41๊ฐœ
      • article-comment-domain: 37๊ฐœ
      • article-comment-web-adapter: 6๊ฐœ
    • ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š”/์‹ซ์–ด์š” ๋ชจ๋“ˆ: 103๊ฐœ
      • article-like-application-output-port: 28๊ฐœ
      • article-like-application-service: 32๊ฐœ
      • article-like-domain: 33๊ฐœ
      • article-like-web-adapter: 10๊ฐœ
    • ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ๋ชจ๋“ˆ: 47๊ฐœ
      • article-read-application-output-port: 15๊ฐœ
      • article-read-application-service: 22๊ฐœ
      • article-read-domain: 7๊ฐœ
      • article-read-web-adapter: 3๊ฐœ
    • ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ์ˆ˜ ๋ชจ๋“ˆ: 23๊ฐœ
      • article-view-application-output-port: 7๊ฐœ
      • article-view-application-service: 8๊ฐœ
      • article-view-domain: 5๊ฐœ
      • article-view-web-adapter: 3๊ฐœ
    • ์ด๋ฉ”์ผ ๋ฐœ์†ก ๋ชจ๋“ˆ: 2๊ฐœ
      • email-sender: 2๊ฐœ
    • ์™ธ๋ถ€ ๊ธฐ์ˆ  ๋ชจ๋“ˆ: 219๊ฐœ
      • database-adapter: 109๊ฐœ
      • event-publisher: 1๊ฐœ
      • jwt: 18๊ฐœ
      • redis-adapter: 24๊ฐœ
      • web-support: 67๊ฐœ

About

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages