Skip to content

dozzzang/Team5_BE

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

672 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ‘‘ DailyQ

1. ์„œ๋น„์Šค ๊ฐœ์š”

  • ์„œ๋น„์Šค๋ช…: DailyQ
  • ๋ชฉํ‘œ: ์ทจ์—… ์ค€๋น„์ƒ์ด ํ•˜๋ฃจ 5๋ถ„, ํ•˜๋ฃจ์— ํ•˜๋‚˜์”ฉ ๋ฉด์ ‘ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•˜๋ฉด์„œ
    • ๊พธ์ค€ํžˆ ์—ฐ์Šตํ•˜๋Š” ์Šต๊ด€์„ ๋งŒ๋“ค๊ณ 
    • AI ํ”ผ๋“œ๋ฐฑ์„ ํ†ตํ•ด ๋ถ€์กฑํ•œ ์ ์„ ๋ณด์™„ํ•˜๋ฉฐ
    • ๋ฉด์ ‘ ์‹ค๋ ฅ์„ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ํ•จ.
  • ํ•ต์‹ฌ ๊ฐ€์น˜: ๋ฌด๊ฒ์ง€ ์•Š์€ '1์ผ 1๋ฌธํ•ญ' ๋ฃจํ‹ด โ†’ ๊พธ์ค€ํ•œ ํ•™์Šต โ†’ ์„ฑ์žฅ ์ฒด๊ฐ โ†’ ๋™๊ธฐ ์œ ์ง€

2. ๋ฌธ์ œ ์ •์˜

  • ์ทจ์—… ์ค€๋น„์ƒ์˜ ํ•™์Šต ํŒจํ„ด ๋ฌธ์ œ
    • ๋ฉด์ ‘ ๋Œ€๋น„๋ฅผ ๊พธ์ค€ํžˆ ํ•˜๊ณ  ์‹ถ์ง€๋งŒ, ๋งค์ผ ๋ฌด์—‡์„ ์ค€๋น„ํ•ด์•ผ ํ• ์ง€ ์ฒด๊ณ„๊ฐ€ ์—†์Œ
    • ์ž๊ธฐ ๋‹ต๋ณ€์„ ๊ธฐ๋กํ•˜๊ฑฐ๋‚˜ ํ”ผ๋“œ๋ฐฑ ๋ฐ›์„ ๊ธฐํšŒ๊ฐ€ ๋ถ€์กฑํ•ด, ์ž์‹ ์˜ ์„ฑ์žฅ ์ •๋„๋ฅผ ์ธก์ •ํ•˜๊ธฐ ์–ด๋ ค์›€
    • ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋‹ค ๋ณด๋‹ˆ ์ง€๋ฃจํ•ด์ง€๊ณ , ๋™๊ธฐ๋ถ€์—ฌ๊ฐ€ ๊ธˆ๋ฐฉ ๋–จ์–ด์ ธ ํ•™์Šต์„ ์ค‘๋‹จํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ
  • ๊ธฐ์กด ์„œ๋น„์Šค์˜ ํ•œ๊ณ„
    • ๋ชจ์˜ ๋ฉด์ ‘(์˜ˆ: ์‚ฌ๋žŒ์ธ AI ๋ฉด์ ‘)
      • 1ํšŒ ๋‹จ๊ฐ€๊ฐ€ ๋†’์•„ ์ž์ฃผ ์ด์šฉํ•˜๊ธฐ ์–ด๋ ต๊ณ ,
      • ์‹ค์ œ ํ•™์Šต ๋ฃจํ‹ด์œผ๋กœ ์ด์–ด์ง€๊ธฐ๋ณด๋‹ค๋Š” ์ผํšŒ์„ฑ ์ฒดํ—˜์— ๋จธ๋ฌด๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ
    • ๋ฉ”์ผ ๊ธฐ๋ฐ˜ ํ•™์Šต(์˜ˆ: ๋งค์ผ๋ฉ”์ผ)
      • ์‚ฌ์šฉ์ž๊ฐ€ ๋ฉ”์ผ์„ ์—ด์–ด๋ณด์ง€ ์•Š์œผ๋ฉด ํ•™์Šต์ด ๋Š๊น€ โ†’ ์‚ฌ์šฉ์ž ์ฃผ๋„ํ˜•(passive) ๊ตฌ์กฐ๋ผ ์ง€์†์„ฑ์ด ๋‚ฎ์Œ
      • ์งง์€ ํŒ ์œ„์ฃผ์˜ ์ •๋ณด ์ œ๊ณต์ด๋ผ, ์‹ค์ œ ๋‹ต๋ณ€ ํ›ˆ๋ จ/ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๊ฐ€ ๋ถ€์กฑํ•จ
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ
    • ์ทจ์—… ์ค€๋น„์ƒ์€ ๊พธ์ค€ํžˆ ์—ฐ์Šตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋ฒผ์šด ๋ฃจํ‹ด๊ณผ
    • ์ฆ‰๊ฐ์ ์ธ ํ”ผ๋“œ๋ฐฑ๊ณผ ์„ฑ์ทจ๊ฐ์„ ์ œ๊ณตํ•˜๋Š” ๋„๊ตฌ๋ฅผ ์ฐพ์ง€ ๋ชปํ•ด
    • ํ•™์Šต ์ง€์†๋ฅ ์ด ๋‚ฎ๊ณ , ์‹ค์งˆ์ ์ธ ์—ญ๋Ÿ‰ ํ–ฅ์ƒ์œผ๋กœ ์ด์–ด์ง€์ง€ ๋ชปํ•จ

3. ์†”๋ฃจ์…˜

  • ๋ธŒ๋ผ์šฐ์ €/์•ฑ ์‹คํ–‰ ์‹œ ์ž๋™์œผ๋กœ ํ•˜๋ฃจ 1๋ฌธํ•ญ ๋…ธ์ถœ
  • ๋‹ต๋ณ€์€ ํ…์ŠคํŠธ/์Œ์„ฑ ์ค‘ ์„ ํƒ ๊ฐ€๋Šฅ โ†’ ์Œ์„ฑ์˜ ๊ฒฝ์šฐ ์‹œ๊ฐ„ ์ œํ•œ ์˜ต์…˜ ์ œ๊ณต
  • AI ํ”ผ๋“œ๋ฐฑ์œผ๋กœ ๋‹ต๋ณ€์˜ ๊ตฌ์กฐยท๋ช…ํ™•์„ฑยท๊ทผ๊ฑฐ ๊ฐ•ํ™”๋ฅผ ์ง€์›
  • ์•„์นด์ด๋ธŒ/์ŠคํŠธ๋ฆญ/๋ผ์ด๋ฒŒ ๊ธฐ๋Šฅ์œผ๋กœ ์„ฑ์ทจ๊ฐยท๊ฒฝ์Ÿ์‹ฌ ์ œ๊ณต
  • ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ๊ธฐ๋Šฅ์œผ๋กœ ์‹ค์ œ ๋ฉด์ ‘๊ณผ ์œ ์‚ฌ๋„ ํ™•๋ณด

4. ์ฃผ์š” ๊ธฐ๋Šฅ

  1. ํšŒ์›/์˜จ๋ณด๋”ฉ
    • ์†Œ์…œ ๋กœ๊ทธ์ธ(๊ตฌ๊ธ€/์นด์นด์˜ค)
    • ์›ํ•˜๋Š” ์ง๊ตฐ ์„ ํƒ โ†’ ์ง๊ตฐ ๋‚ด ์„ธ๋ถ€์ง๊ตฐ ๋‹ค์ค‘ ์„ ํƒ
  2. ํ•˜๋ฃจ ์งˆ๋ฌธ ๋ฃจํ‹ด
    • ๋ฉ”์ธ ํŽ˜์ด์ง€ [์˜ค๋Š˜์˜ ์งˆ๋ฌธ] โ†’ ๋‹ต๋ณ€ ์ž…๋ ฅ(ํƒ€์ž/์Œ์„ฑ)
    • ๋‹ต๋ณ€ ์ž…๋ ฅ(ํƒ€์ž/์Œ์„ฑ), ์Œ์„ฑ์˜ ๊ฒฝ์šฐ ์‹œ๊ฐ„ ์ œํ•œ
    • ์Œ์„ฑ ๋‹ต๋ณ€์€ ์ž๋™ STT ์ฒ˜๋ฆฌ
    • AI ํ”ผ๋“œ๋ฐฑ + ์‚ฌ์šฉ์ž๊ฐ€ ๋‚œ์ด๋„ ์ฒดํฌ
    • ์ŠคํŠธ๋ฆญ ์ฆ๊ฐ€
  3. ์•„์นด์ด๋ธŒ
    • ๋‚ด๊ฐ€ ํ‘ผ ๋‹ต๋ณ€/ํ”ผ๋“œ๋ฐฑ ํžˆ์Šคํ† ๋ฆฌ
    • ์ฆ๊ฒจ์ฐพ๊ธฐ(ํ•€)/๋‚œ์ด๋„ ํ•„ํ„ฐ
    • ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ ์ž‘์„ฑ ๊ธฐ๋Šฅ
  4. ๋™๊ธฐ ๋ถ€์—ฌ
    • ์ŠคํŠธ๋ฆญ(์—ฐ์† ๊ธฐ๋ก)
    • ๋ผ์ด๋ฒŒ ์ง€์ •
  5. ์œ ๋ฃŒ ์„œ๋น„์Šค
    • (๊ตฌ๋…) ์ผ์ผ ์งˆ๋ฌธ ํ•œ๋„ ์ถ”๊ฐ€
      • ๋ฉด์ ‘์ด ์ž„๋ฐ•ํ•œ ์‚ฌ์šฉ์ž์˜ ํŽธ์˜์„ฑ ์ฆ๋Œ€

5. ๊ธฐ์ˆ  ํ๋ฆ„

5.0 ํšŒ์›๊ฐ€์ž… ๋ฐ ์˜จ๋ณด๋”ฉ ํ”Œ๋กœ์šฐ

์‹ ๊ทœ ์‚ฌ์šฉ์ž๋Š” ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ํ†ตํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ฐ€์ž…ํ•˜๊ณ , ์˜จ๋ณด๋”ฉ ๊ณผ์ •์„ ํ†ตํ•ด ๋งž์ถคํ˜• ์„œ๋น„์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๋‹จ๊ณ„:

  1. ์†Œ์…œ ๋กœ๊ทธ์ธ ์‹œ์ž‘: ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๊ธ€ ๋˜๋Š” ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  2. OAuth2 ์ธ์ฆ: Spring Security๊ฐ€ OAuth2 ์ธ์ฆ ํ”Œ๋กœ์šฐ๋ฅผ ์‹œ์ž‘ํ•˜๊ณ , ์‚ฌ์šฉ์ž๋ฅผ ๊ตฌ๊ธ€/์นด์นด์˜ค ์ธ์ฆ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ฉ๋‹ˆ๋‹ค.
  3. ์‚ฌ์šฉ์ž ์ธ์ฆ: ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๊ธ€/์นด์นด์˜ค์—์„œ ๋กœ๊ทธ์ธํ•˜๊ณ  ์ •๋ณด ์ œ๊ณต์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ: CustomOAuth2UserService๊ฐ€ Authorization Code๋ฅผ Access Token์œผ๋กœ ๊ตํ™˜ํ•˜๊ณ , ์‚ฌ์šฉ์ž ์ •๋ณด API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  5. ์‚ฌ์šฉ์ž ์ €์žฅ/์—…๋ฐ์ดํŠธ: ์ด๋ฉ”์ผ๋กœ ๊ธฐ์กด ์‚ฌ์šฉ์ž๋ฅผ ์กฐํšŒํ•˜๊ณ , ์—†์œผ๋ฉด ์‹ ๊ทœ ์ƒ์„ฑ, ์žˆ์œผ๋ฉด ์ด๋ฆ„๋งŒ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  6. ์‹ ๊ทœ ์‚ฌ์šฉ์ž ์ดˆ๊ธฐํ™”: ์‹ ๊ทœ ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ UserPreferences์™€ UserFlowProgress๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  7. JWT ํ† ํฐ ๋ฐœ๊ธ‰: OAuth2AuthenticationSuccessHandler์—์„œ Access Token๊ณผ Refresh Token์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  8. ํ† ํฐ ์ €์žฅ: Refresh Token์€ HttpOnly ์ฟ ํ‚ค์— ์ €์žฅ๋˜๊ณ , Access Token์€ URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ”„๋ก ํŠธ์—”๋“œ์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.
  9. ์˜จ๋ณด๋”ฉ ์ง„ํ–‰: ์‹ ๊ทœ ์‚ฌ์šฉ์ž๋Š” ์ง๊ตฐ ์„ ํƒ์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  10. ์„œ๋น„์Šค ์ด์šฉ ์‹œ์ž‘: ์˜จ๋ณด๋”ฉ ์™„๋ฃŒ ํ›„ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์˜ค๋Š˜์˜ ์งˆ๋ฌธ์„ ๋ฐ›์•„ ๋‹ต๋ณ€์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ† ํฐ ๊ฐฑ์‹  ํ”Œ๋กœ์šฐ:

  • Access Token ๋งŒ๋ฃŒ ์‹œ: POST /api/token/refresh๋กœ Refresh Token์„ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด Access Token ๋ฐœ๊ธ‰
  • Refresh Token์€ ์ฟ ํ‚ค์—์„œ ์ž๋™์œผ๋กœ ์ฝ์–ด์˜ด

5.1 ์งˆ๋ฌธ ์กฐํšŒ ํ”Œ๋กœ์šฐ

์‚ฌ์šฉ์ž๊ฐ€ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์˜ค๋Š˜์˜ ์งˆ๋ฌธ์„ ๋ฐ›์•„์˜ค๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์ฃผ์š” ๋‹จ๊ณ„:

  1. ์งˆ๋ฌธ ์š”์ฒญ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ GET /api/questions/random ์—”๋“œํฌ์ธํŠธ๋กœ ๋žœ๋ค ์งˆ๋ฌธ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  2. ์‚ฌ์šฉ์ž ์„ค์ • ์กฐํšŒ: ์„œ๋ฒ„๊ฐ€ UserPreferences๋ฅผ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ ๋ชจ๋“œ(TECH/FLOW), ์ง๊ตฐ ๋“ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  3. ์ผ์ผ ํ•œ๋„ ๊ฒ€์ฆ: ์˜ค๋Š˜ ์ด๋ฏธ ๋‹ต๋ณ€ํ•œ ์งˆ๋ฌธ ์ˆ˜๋ฅผ ํ™•์ธํ•˜์—ฌ ์ผ์ผ ์งˆ๋ฌธ ํ•œ๋„๋ฅผ ์ดˆ๊ณผํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  4. ์งˆ๋ฌธ ๋ชจ๋“œ๋ณ„ ์ฒ˜๋ฆฌ:
    • TECH ๋ชจ๋“œ: ์‚ฌ์šฉ์ž์˜ ์ง๊ตฐ์— ๋งž๋Š” ๊ธฐ์ˆ  ์งˆ๋ฌธ์„ ๋žœ๋ค์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ๋‹ต๋ณ€ํ•œ ์งˆ๋ฌธ์€ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.
    • FLOW ๋ชจ๋“œ: ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ๋ฉด์ ‘ ๋‹จ๊ณ„(INTRO โ†’ MOTIVATION โ†’ TECH โ†’ PERSONALITY)์— ๋งž๋Š” ์งˆ๋ฌธ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. UserFlowProgress๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ๋‹จ๊ณ„๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
  5. ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์šฐ์„  ์ฒ˜๋ฆฌ: ๋ฏธ๋‹ต๋ณ€ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ์ด ์žˆ์œผ๋ฉด ์ผ๋ฐ˜ ์งˆ๋ฌธ๋ณด๋‹ค ์šฐ์„ ์ ์œผ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  6. ์งˆ๋ฌธ ๋ฐ˜ํ™˜: ์„ ํƒ๋œ ์งˆ๋ฌธ๊ณผ ํ•จ๊ป˜ ์งˆ๋ฌธ ๋ชจ๋“œ, ํ˜„์žฌ ๋‹จ๊ณ„, ์‹œ๊ฐ„ ์ œํ•œ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ RandomQuestionResponse๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

5.2 ์Œ์„ฑ ๋‹ต๋ณ€ ํ”Œ๋กœ์šฐ

์Œ์„ฑ ๋‹ต๋ณ€์˜ ๊ฒฝ์šฐ ๋น„๋™๊ธฐ STT ์ฒ˜๋ฆฌ์™€ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๋‹จ๊ณ„:

  1. ์Œ์„ฑ ๋…น์Œ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์Œ์„ฑ์„ ๋…น์Œํ•ฉ๋‹ˆ๋‹ค.
  2. Presigned URL ์š”์ฒญ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ์—…๋กœ๋“œ์šฉ Presigned URL์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  3. ์ง์ ‘ ์—…๋กœ๋“œ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ Presigned URL์„ ์‚ฌ์šฉํ•ด NCP Object Storage์— ์Œ์„ฑ ํŒŒ์ผ์„ ์ง์ ‘ ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
  4. ๋‹ต๋ณ€ ๋“ฑ๋ก: ์Œ์„ฑ ํŒŒ์ผ URL๊ณผ ํ•จ๊ป˜ POST /api/answers ์—”๋“œํฌ์ธํŠธ๋กœ ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  5. SSE ์—ฐ๊ฒฐ: ๋‹ต๋ณ€ ์ œ์ถœ๊ณผ ๋™์‹œ์—, ํด๋ผ์ด์–ธํŠธ๊ฐ€ SSE ์ „์šฉ ์ผํšŒ์šฉ ํ† ํฐ์„ ๋ฐœ๊ธ‰๋ฐ›๊ณ  ์ด๋ฅผ ์‚ฌ์šฉํ•ด GET /api/sse/connect๋กœ SSE ์—ฐ๊ฒฐ์„ ์ˆ˜๋ฆฝํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ค€๋น„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.
  6. Answer ์ƒ์„ฑ: ์„œ๋ฒ„์—์„œ Answer ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜์ง€๋งŒ, ์•„์ง ํ…์ŠคํŠธ๋Š” ์—†๊ณ  ์ƒํƒœ๋Š” PENDING_STT์ž…๋‹ˆ๋‹ค.
  7. STT ์ž‘์—… ์‹œ์ž‘: SttTask๋ฅผ ์ƒ์„ฑํ•˜๊ณ  NCP CLOVA SPEECH API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋น„๋™๊ธฐ ๋ณ€ํ™˜ ์ž‘์—…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  8. STT ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ: NCP CLOVA๊ฐ€ ๋ณ€ํ™˜์„ ์™„๋ฃŒํ•˜๋ฉด POST /api/stt/callback์œผ๋กœ ์ฝœ๋ฐฑ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  9. ํ…์ŠคํŠธ ์—…๋ฐ์ดํŠธ: ์ฝœ๋ฐฑ์—์„œ ๋ฐ›์€ ํ…์ŠคํŠธ๋กœ Answer๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  SSE๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์— STT ์™„๋ฃŒ ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
  10. AI ํ”ผ๋“œ๋ฐฑ ์ƒ์„ฑ: FeedbackService๊ฐ€ OpenAI API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ”ผ๋“œ๋ฐฑ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  11. ๊ฒฐ๊ณผ ์กฐํšŒ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ GET /api/answers/{id}๋กœ ์ตœ์ข… ๋‹ต๋ณ€๊ณผ ํ”ผ๋“œ๋ฐฑ์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์—๋Š” answerText, feedback (AI ํ”ผ๋“œ๋ฐฑ), question ์ •๋ณด๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์—๋Ÿฌ ์ฒ˜๋ฆฌ:

  • STT ์‹คํŒจ ์‹œ: SttFailedEvent ๋ฐœํ–‰ โ†’ SSE๋กœ ์•Œ๋ฆผ โ†’ ํด๋ผ์ด์–ธํŠธ๊ฐ€ /api/answers/{id}/retry-stt๋กœ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ
  • ํ”ผ๋“œ๋ฐฑ ์ƒ์„ฑ ์‹คํŒจ ์‹œ: Feedback ์ƒํƒœ๋ฅผ FAILED๋กœ ์—…๋ฐ์ดํŠธ โ†’ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ

5.3 ํ…์ŠคํŠธ ๋‹ต๋ณ€ ํ”Œ๋กœ์šฐ

ํ…์ŠคํŠธ ๋‹ต๋ณ€์€ ๋” ๋‹จ์ˆœํ•œ ๋™๊ธฐ ํ”Œ๋กœ์šฐ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

์ฃผ์š” ๋‹จ๊ณ„:

  1. ํ…์ŠคํŠธ ์ž…๋ ฅ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ…์ŠคํŠธ๋กœ ๋‹ต๋ณ€์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  2. ๋‹ต๋ณ€ ๋“ฑ๋ก: ํด๋ผ์ด์–ธํŠธ๊ฐ€ POST /api/answers ์—”๋“œํฌ์ธํŠธ๋กœ ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  3. Answer ์ƒ์„ฑ: ์„œ๋ฒ„์—์„œ Answer ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์ ์— ์ด๋ฏธ ํ…์ŠคํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ, feedback ์ƒํƒœ๋Š” PENDING์ž…๋‹ˆ๋‹ค.
  4. AI ํ”ผ๋“œ๋ฐฑ ์ƒ์„ฑ: FeedbackService๊ฐ€ OpenAI GAPI๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ”ผ๋“œ๋ฐฑ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  5. ๊ฒฐ๊ณผ ์กฐํšŒ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ GET /api/answers/{id}๋กœ ์ตœ์ข… ๋‹ต๋ณ€๊ณผ ํ”ผ๋“œ๋ฐฑ์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์—๋Š” answerText, feedback (AI ํ”ผ๋“œ๋ฐฑ), question ์ •๋ณด๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

5.4 ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์ƒ์„ฑ ํ”Œ๋กœ์šฐ

์‚ฌ์šฉ์ž์˜ ๋‹ต๋ณ€์„ ๋ฐ”ํƒ•์œผ๋กœ AI๊ฐ€ ์ถ”๊ฐ€ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•˜์—ฌ ์‹ค์ œ ๋ฉด์ ‘๊ณผ ์œ ์‚ฌํ•œ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๋‹จ๊ณ„:

  1. ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์ƒ์„ฑ ์š”์ฒญ: ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ต๋ณ€์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ํ™•์ธํ•œ ํ›„, POST /api/questions/followUp/{answerId} ์—”๋“œํฌ์ธํŠธ๋กœ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์ƒ์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  2. ๋‹ต๋ณ€ ๋ฐ ์งˆ๋ฌธ ์กฐํšŒ: ์„œ๋ฒ„๊ฐ€ ํ•ด๋‹น Answer์™€ ์›๋ณธ Question ์ •๋ณด๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  3. AI ์งˆ๋ฌธ ์ƒ์„ฑ: FollowUpQuestionService๊ฐ€ OpenAI API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ๋‹ต๋ณ€์„ ๋ถ„์„ํ•˜๊ณ , ๋‹ต๋ณ€์„ ๋” ๊นŠ์ด ์žˆ๊ฒŒ ํƒ๊ตฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ผฌ๋ฆฌ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  4. ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์ €์žฅ: ์ƒ์„ฑ๋œ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ๋“ค์„ FollowUpQuestion ์—”ํ‹ฐํ‹ฐ๋กœ ์ €์žฅํ•˜๊ณ , ์›๋ณธ Answer์™€ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
  5. ์‘๋‹ต ๋ฐ˜ํ™˜: ์ƒ์„ฑ๋œ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ์˜ ๊ฐœ์ˆ˜๋ฅผ ํฌํ•จํ•œ FollowUpGenerationResponse๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  6. ์งˆ๋ฌธ ์กฐํšŒ ์‹œ ์šฐ์„  ์ œ๊ณต: ์ดํ›„ ํด๋ผ์ด์–ธํŠธํŠธ๊ฐ€ GET /api/questions/random์„ ํ˜ธ์ถœํ•˜๋ฉด, ๋ฏธ๋‹ต๋ณ€ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ์ด ์ผ๋ฐ˜ ์งˆ๋ฌธ๋ณด๋‹ค ์šฐ์„ ์ ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

๊ผฌ๋ฆฌ ์งˆ๋ฌธ์˜ ํŠน์ง•:

  • ์‚ฌ์šฉ์ž์˜ ๋‹ต๋ณ€ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋งฅ๋ฝ์— ๋งž๋Š” ์ถ”๊ฐ€ ์งˆ๋ฌธ ์ƒ์„ฑ
  • ์‹ค์ œ ๋ฉด์ ‘์—์„œ ๋ฉด์ ‘๊ด€์ด ํ•  ์ˆ˜ ์žˆ๋Š” ์‹ฌํ™” ์งˆ๋ฌธ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  • ๋‹ต๋ณ€์˜ ๊นŠ์ด์™€ ์™„์„ฑ๋„๋ฅผ ๋†’์ด๋Š” ๋ฐ ๋„์›€

6. ํ•ต์‹ฌ ์ด์Šˆ

SSE + virtual thread

๋„์ž…๊ณ„๊ธฐ

  • CLOVA STT ๋ณ€ํ™˜์€ ๋น„๋™๊ธฐ ์ž‘์—…์œผ๋กœ ์ด ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ '์‹ค์‹œ๊ฐ„'์œผ๋กœ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด SSE๋ฅผ ๋„์ž…
  • ์ด๋•Œ, SSE๋Š” ํŠน์„ฑ์ƒ ๊ฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†ํ•ด ์žˆ๋Š” ๋‚ด๋‚ด ์„œ๋ฒ„ ์Šค๋ ˆ๋“œ 1๊ฐœ๋ฅผ ์ ์œ ํ•จ
  • ํ…Œ์ŠคํŠธ์—์„œ ๋™์‹œ ์ ‘์†์ž๊ฐ€ 200๋ช…๋งŒ ๋„˜์–ด๊ฐ€๋„ ๋ชจ๋“  OS ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ณ ๊ฐˆ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์„œ๋ฒ„ ์ „์ฒด๊ฐ€ ๋‹ค์šด๋˜๋Š” ํ˜„์ƒ ๋ฐœ์ƒ

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

  • Virtual Thread๋ฅผ ๋„์ž…ํ•˜์—ฌ SSE์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜๋”๋ผ๋„ OS๋ฅผ ์ ์œ ํ•˜์ง€ ์•Š๊ณ  10~20๊ฐœ์˜ ์ ์€ OS์“ฐ๋ ˆ๋“œ๋งŒ์œผ๋กœ n์ฒœ๊ฐœ์˜ SSE ๋™์‹œ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•จ

๊ฒ€์ฆ

  • 30์ดˆ๊ฐ„ ์“ฐ๋ ˆ๋“œ๋ฅผ ๊ฐ•์ œ๋กœ ๋Œ€๊ธฐ์‹œํ‚ค๋Š” ์ฆ‰, SSE ์—ฐ๊ฒฐ ํ›„ ๋Œ€๊ธฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ์œ„ํ•œ ํ…Œ์ŠคํŠธ์šฉ API๋ฅผ ๊ตฌํ˜„
  • ๊ธฐ์กด OS ์“ฐ๋ ˆ๋“œ์˜ ํ•œ๊ณ„์น˜๋ฅผ ๋„˜๊ธฐ๊ฒŒ 3000๋ช…์ด ๋™์‹œ์š”์ฒญํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ ํ…Œ์ŠคํŠธ
  • ์ฆ‰ ์„œ๋ฒ„๊ฐ€ 3000๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๋ธ”๋กœํ‚น๋œ ์ƒํƒœ๋ฅผ ๋ฒ„ํ‹ฐ๋„๋ก ๊ตฌ์„ฑ
image
  • ์‚ฌ์ง„(์œ„) virtual thread๋ฅผ ์ผ  ๊ฒฝ์šฐ OS ์“ฐ๋ ˆ๋“œ ๊ณ ๊ฐˆ ์—†์ด ๋ชจ๋‘ ์„ฑ๊ณต
  • ์‚ฌ์ง„ (์•„๋ž˜) ๊ฐ€์ƒ์œ ์ €๊ฐ€ 204๋ช…์— ๋„๋‹ฌํ•˜์ž๋งˆ์ž OS ์“ฐ๋ ˆ๋“œ ๊ณ ๊ฐˆ๋กœ ๋‚˜๋จธ์ง€ ์š”์ฒญ ๋ชจ๋‘ ์‹คํŒจ
nGrinder ๋„์ž…

๋„์ž…๊ณ„๊ธฐ

  • FE๊ฐ€ API๊ฐ€ ๋А๋ฆฌ๋‹ค๊ณ  ๋ฆฌํฌํŠธํ•˜์—ฌ, BEํŒ€์€ ์ •ํ™•ํ•œ ์›์ธ(API ๋ฌธ์ œ/DB ๋ฌธ์ œ/์ธํ”„๋ผ ๋ฌธ์ œ)์„ ์žฌํ˜„ํ•˜๊ธธ ์›ํ•จ

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

  • Groovy ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜ ์‹œ๋‚˜๋ฆฌ์˜ค: VUser(๊ฐ€์ƒ ์œ ์ €)๋ณ„๋กœ ๋ณต์žกํ•œ ๋กœ์ง์„ ์ฝ”๋“œ๋กœ ์ž‘์„ฑ
  • ์ถ”์  ๋ฐ ๊ด€๋ฆฌ : API ์—”๋“œํฌ์ธํŠธ๋ณ„๋กœ ๋ถ„๋ฆฌ
  • ๋ช…ํ™•ํ•œ ๋ณ‘๋ชฉ ์‹œ๊ฐํ™”: VUser ์ฆ๊ฐ€์— ๋”ฐ๋ฅธ Error Rate, TPS, Mean Test Time(MTT)์„ ์‹ค์‹œ๊ฐ„ ๊ทธ๋ž˜ํ”„๋กœ ํ™•์ธ

๊ธฐ๋Œ€ํšจ๊ณผ

  • ๋ช…ํ™•ํ•œ SLA์ œ๊ณต(BE) - ๋ฐ์ดํ„ฐ์— ๊ธฐ๋ฐ˜ํ•œ ๋ช…ํ™•ํ•œ SLA(Service Level Agreement) ์ œ๊ณต
  • ์•ˆํ‹ฐํŒจํ„ด ๋ฐฉ์ง€ ๋ฐ ์‹ ๋ขฐ ๊ตฌ์ถ•(FE) - FEํŒ€์ด "API๊ฐ€ ๋А๋ฆด ๊ฒƒ"์ด๋ผ ์ง€๋ ˆ์ง์ž‘ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์บ์‹ฑ์ด๋‚˜ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์•ˆํ‹ฐํŒจํ„ด์„ ๋ฐฉ์ง€
JWT ๊ธฐ๋ฐ˜ ๋กœ๊ทธ์ธ ์ธ์ฆ/์ธ๊ฐ€

๋„์ž…๊ณ„๊ธฐ

  • JWT(JSON Web Token) ๋„์ž…์˜ ์ฃผ๋œ ์ด์œ ๋Š” ๊ธฐ์กด ์„ธ์…˜(Session) ๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐฉ์‹์˜ ํ•œ๊ณ„์ ์„ ๊ทน๋ณตํ•˜๊ณ , ํ™•์žฅ์„ฑ ๋ฐ ํšจ์œจ์„ฑ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•จ

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

  • ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ์„œ๋ฒ„๊ฐ€ ๋น„๋ฐ€ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด JWT๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ํ•ด๋‹น ํ† ํฐ์„ ์ €์žฅ ํ›„ ์š”์ฒญ ์‹œ ํ—ค๋”(Authorization: Bearer <token>)์— ํฌํ•จ
  • ์„œ๋ฒ„๋Š” ํ† ํฐ ๊ฒ€์ฆ๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋ณ„๋„์˜ ์„ธ์…˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜์ง€ ์•Š์Œ (Stateless ์ธ์ฆ ๊ตฌ์กฐ)
  • ํ† ํฐ์— ์‚ฌ์šฉ์ž ๊ถŒํ•œ(Role) ๋ฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„(Expiration Time)์„ ํฌํ•จํ•˜์—ฌ ์ธ๊ฐ€(Authorization) ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
  • Refresh Token์„ ํ™œ์šฉํ•ด Access Token ์žฌ๋ฐœ๊ธ‰ ํ”„๋กœ์„ธ์Šค ๊ตฌํ˜„์œผ๋กœ ๋ณด์•ˆ์„ฑ๊ณผ ํŽธ์˜์„ฑ ๊ฐ•ํ™”

๊ธฐ๋Œ€ํšจ๊ณผ

  • ์„œ๋ฒ„์— ์„ธ์…˜ ์ €์žฅ์ด ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ ํ™•์žฅ์„ฑ๊ณผ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • REST API, ๋ชจ๋ฐ”์ผ, ํ”„๋ก ํŠธ์—”๋“œ ๋“ฑ ๋‹ค์–‘ํ•œ ํด๋ผ์ด์–ธํŠธ ํ™˜๊ฒฝ์—์„œ ์ผ๊ด€๋œ ์ธ์ฆ ์ฒด๊ณ„ ์œ ์ง€
  • ํ† ํฐ ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ์œผ๋กœ ๋ณด์•ˆ์„ฑ ๊ฐ•ํ™”(๋งŒ๋ฃŒ ์‹œ๊ฐ„, ์„œ๋ช… ๊ฒ€์ฆ, HTTPS ์—ฐ๋™ ๋“ฑ)
๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ๊ตฌํ˜„

๋„์ž…๊ณ„๊ธฐ

  • ์‹œ์Šคํ…œ ์šด์˜ ์ค‘ ์ง์ ‘ DB ์ ‘๊ทผ์ด๋‚˜ ๊ฐœ๋ฐœ์ž ์˜์กด์  ์ ˆ์ฐจ๋กœ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ๋น„ํšจ์œจ ์กด์žฌ
  • ๋ณด์•ˆยท์ •์ฑ…ยท์ฝ˜ํ…์ธ  ๊ด€๋ฆฌ ๋“ฑ ์šด์˜์ž๊ฐ€ ์ง์ ‘ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๋ถ€์žฌ

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

  • JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ/์ธ๊ฐ€ ์—ฐ๋™์œผ๋กœ ์ ‘๊ทผ ์ œ์–ด ๊ฐ•ํ™”
  • ํšŒ์› ์กฐํšŒ ๋ฐ ์ˆ˜์ •/์‚ญ์ œ
    • ์ด๋ฆ„ ๋ฐ ์—ญํ• (Role)๋ณ„ ์ ‘๊ทผ ๊ถŒํ•œ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ์ œ๊ณต (๊ด€๋ฆฌ์ž/๊ตฌ๋…์ž/์ผ๋ฐ˜์‚ฌ์šฉ์ž)
  • ์ง๊ตฐ/์ง์—… ์กฐํšŒ ๋ฐ ์ถ”๊ฐ€/์‚ญ์ œ
  • ์งˆ๋ฌธ ์กฐํšŒ ๋ฐ ์ถ”๊ฐ€/์ˆ˜์ •/์‚ญ์ œ
    • ์งˆ๋ฌธ ๋‚ด์šฉ, ์งˆ๋ฌธ ํƒ€์ž…(TECH/INTRO/MOTIVATION/PERSONALITY), ์—ฐ๊ฒฐ๋œ ์ง์—… ์ข…๋ฅ˜, ํ™œ์„ฑํ™” ์—ฌ๋ถ€(ํ™œ์„ฑ/๋น„ํ™œ์„ฑ) ๊ด€๋ฆฌ ์ œ๊ณต

๊ธฐ๋Œ€ํšจ๊ณผ

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

๋„์ž…๊ณ„๊ธฐ

  • ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ๋ฐœ์ƒ : API ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค try-catch๋ฌธ์ด ๋ฐ˜๋ณต
  • ์ผ๊ด€์„ฑ ์—†๋Š” ์—๋Ÿฌ ์‘๋‹ต
  • ๋””๋ฒ„๊น…์— ๋„์›€์ด ๋˜์ง€ ์•Š๋Š” ๋กœ๊ทธ : log.error("์œ ์ €๋ฅผ ์ฐพ์ง€ ๋ชปํ•จ")์€ ๋„์›€์ด ๋˜์ง€ ์•Š์Œ

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

  • ExceptionHandlerAdvice๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ ํ•ธ๋“ค๋ง
  • BusinissException๊ณผ InfraException ํŒจํ„ด์„ ํ†ตํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๊ธฐ๋Œ€ํšจ๊ณผ

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

๋„์ž…๊ณ„๊ธฐ

AnswerCommandService์˜ submitAnswer ๋ฉ”์„œ๋“œ์—์„œ ์ผ๋ฐ˜ ์งˆ๋ฌธ๊ณผ ๊ผฌ๋ฆฌ ์งˆ๋ฌธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์— ํ˜ผ์žฌ

  • ์ฝ”๋“œ ์ค‘๋ณต: ๊ณตํ†ต ๋กœ์ง(๋‹ต๋ณ€ ๊ฐ์ฒด ์ƒ์„ฑ, ์ €์žฅ, STT ์ฒ˜๋ฆฌ)์ด ๋ฐ˜๋ณต๋จ
  • ๋ณต์žกํ•œ ๋ถ„๊ธฐ: if-else๋กœ ์ผ๋ฐ˜/๊ผฌ๋ฆฌ ์งˆ๋ฌธ์„ ๊ตฌ๋ถ„ํ•˜๋ฉฐ ๊ฐ€๋…์„ฑ ์ €ํ•˜
  • ํ™•์žฅ์„ฑ ๋ถ€์กฑ: ์ƒˆ๋กœ์šด ๋‹ต๋ณ€ ํƒ€์ž… ์ถ”๊ฐ€ ์‹œ ๋ฉ”์„œ๋“œ ์ˆ˜์ • ํ•„์š”
  • ๋‹จ์ผ ์ฑ…์ž„(SRP) ์œ„๋ฐ˜: ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ํƒ€์ž…์˜ ๋‹ต๋ณ€ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ํฌํ•จ

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

  • ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด์„ ์ ์šฉํ•ด ๊ณตํ†ต ํ๋ฆ„์€ ์ถ”์ƒ ํด๋ž˜์Šค์—์„œ ์ •์˜ํ•˜๊ณ , ์ฐจ์ด์ ์€ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•˜๋„๋ก ๋ถ„๋ฆฌ

๊ธฐ๋Œ€ํšจ๊ณผ

  • ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ ํ–ฅ์ƒ: ๊ณตํ†ต ๋กœ์ง์„ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌ
  • ๊ฐ€๋…์„ฑ ๊ฐœ์„ : ๊ฐ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ž์‹ ์˜ ์ฑ…์ž„๋งŒ ๋‹ด๋‹น
  • ํ™•์žฅ์„ฑ: ์ƒˆ๋กœ์šด ๋‹ต๋ณ€ ํƒ€์ž… ์ถ”๊ฐ€ ์‹œ ํ•ธ๋“ค๋Ÿฌ๋งŒ ์ถ”๊ฐ€
  • ์œ ์ง€๋ณด์ˆ˜์„ฑ: ๋ณ€๊ฒฝ ์˜ํ–ฅ ๋ฒ”์œ„๊ฐ€ ๋ช…ํ™•
  • ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ: ๊ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • ํ˜„์žฌ AnswerCommandService๋Š” ํŒฉํ† ๋ฆฌ์—์„œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ›์•„ handle()๋งŒ ํ˜ธ์ถœ
cursor ๊ธฐ๋ฐ˜ ๋žœ๋ค ์งˆ๋ฌธ

๋„์ž…๊ณ„๊ธฐ

  • ๊ธฐ์กด ๋ฐฉ์‹(์ „์ฒด ์งˆ๋ฌธ ์กฐํšŒ ํ›„ ๋žœ๋ค ์„ ํƒ)์€ ์งˆ๋ฌธ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ• ์ˆ˜๋ก O(n) ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๊ณผ ๋А๋ฆฐ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ
  • ํŠนํžˆ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฏธ ๋‹ต๋ณ€ํ•œ ์งˆ๋ฌธ์„ ์ œ์™ธํ•˜๋Š” ์กฐ๊ฑด์„ ํฌํ•จํ•  ๊ฒฝ์šฐ ์ฟผ๋ฆฌ ๋น„์šฉ์ด ๋น„์•ฝ์ ์œผ๋กœ ์ฆ๊ฐ€

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

  • MAX ID๋ฅผ ๋จผ์ € ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์งˆ๋ฌธ์˜ ID ๋ฒ”์œ„๋ฅผ ํŒŒ์•…
  • 1๋ถ€ํ„ฐ MAX ID ์‚ฌ์ด์˜ ๋žœ๋ค ID ์ƒ์„ฑ
  • Cursor ๊ธฐ๋ฐ˜ ์กฐํšŒ(id >= randomId)๋กœ ํ•ด๋‹น ๋ฒ”์œ„์—์„œ ์ฒซ ๋ฒˆ์งธ ์งˆ๋ฌธ ์„ ํƒ
  • ์ธ๋ฑ์Šค๋ฅผ ํ™œ์šฉํ•œ ํšจ์œจ์ ์ธ ์ฟผ๋ฆฌ (ORDER BY id, LIMIT 1)

๊ธฐ๋Œ€ํšจ๊ณผ

  • O(1) ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ์ „์ฒด ์งˆ๋ฌธ์„ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œํ•˜์ง€ ์•Š์Œ
  • ๋น ๋ฅธ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ: ์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜ ๋ฒ”์œ„ ์Šค์บ”์œผ๋กœ O(log n) ์‹œ๊ฐ„ ๋ณต์žก๋„
  • ํ™•์žฅ์„ฑ ํ–ฅ์ƒ: ์งˆ๋ฌธ ์ˆ˜๊ฐ€ ์ˆ˜๋งŒ ๊ฐœ๋กœ ์ฆ๊ฐ€ํ•ด๋„ ์ผ์ •ํ•œ ์„ฑ๋Šฅ ์œ ์ง€
  • ๋žœ๋ค์„ฑ ๋ณด์žฅ: ๊ฐ ์งˆ๋ฌธ์ด ๋™์ผํ•œ ํ™•๋ฅ ๋กœ ์„ ํƒ๋จ
QueryDSL์ด ์•„๋‹Œ JPA Specification ๋„์ž…์œผ๋กœ cursor ๊ธฐ๋ฐ˜ ๋ฌดํ•œ์Šคํฌ๋กค ๊ตฌํ˜„ + Slice

๋„์ž…๊ณ„๊ธฐ

  • ์ „ํ†ต์  Pagination(Offset)์˜ ํ•œ๊ณ„: '์•„์นด์ด๋ธŒ' ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ๋‹ต๋ณ€์„ ์กฐํšŒํ•ด์•ผ ํ•จ
    • ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๊ฐ€ ๋Š˜์–ด๋‚  ์ˆ˜๋ก OFFSET N๊ฐœ ๋งŒํผ์˜ ๋ฐ์ดํ„ฐ ์Šค์บ”์ด ๋ฐœ์ƒํ•˜์—ฌ ์กฐํšŒ ์„ฑ๋Šฅ ์ €ํ•˜
    • UX ๊ด€์ ์—์„œ ์•ˆํ‹ฐํŒจํ„ด์ด ๋ฐœ์ƒ
  • ๋™์  ํ•„ํ„ฐ ์š”๊ตฌ : FE์—์„œ ๋‚ ์งœ, ์ง๋ฌด, ์งˆ๋ฌธ ํƒ€์ž…, ์ฆ๊ฒจ์ฐพ๊ธฐ, ๋‚œ์ด๋„ ๋“ฑ ๋‹ค์–‘ํ•œ ์กฐ๊ฑด์œผ๋กœ ๋™์  ๊ฒ€์ƒ‰ ์š”๊ตฌ

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

  • JPA Specification : JPA ํ‘œ์ค€ ๊ธฐ๋Šฅ์œผ๋กœ ์˜์กด์„ฑ, ๋นŒ๋“œ, ํ•™์Šต ๋น„์šฉ ์ตœ์†Œํ™”
    • DTO๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ Predicate๋ฅผ ์กฐํ•ฉํ•˜๋Š” createSpecification ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์œ ์ง€๋ณด์ˆ˜ ํ™•๋ณด
  • ๋ฌดํ•œ ์Šคํฌ๋กค : Cursor + Slice
    • OFFSET ๋Œ€์‹  lastId์™€ lastCreatedAt์„ ์กฐํ•ฉํ•˜์—ฌ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ณธ ๋ฐ์ดํ„ฐ ๋‹ค์Œ 10๊ฐœ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹ ์ฑ„ํƒ
    • Page๋Š” ๋ถˆํ•„์š”ํ•œ COUNT(*) ์ฟผ๋ฆฌ๋ฅผ ์ถ”๊ฐ€๋กœ ์‹คํ–‰ํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜ -> Slice๋ฅผ ํ†ตํ•ด COUNT์ฟผ๋ฆฌ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  hasNext๋ฅผ FE์—๊ฒŒ ์ „๋‹ฌ

๊ธฐ๋Œ€ํšจ๊ณผ

  • BE ์„ฑ๋Šฅ: ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜๋งŒ ๊ฑด์ด ์กด์žฌํ•˜์—ฌ๋„ OFFSET ์Šค์บ”์ด ์—†์œผ๋ฏ€๋กœ, ํ•ญ์ƒ ์ผ์ •ํ•˜๊ณ  ๋น ๋ฅธ ์กฐํšŒ ์„ฑ๋Šฅ ๋ณด์žฅ
  • BE ์œ ์ง€๋ณด์ˆ˜: QueryDSL ๋„์ž… ๋Œ€๋น„ ํ•™์Šต ๋น„์šฉ ๋‚ฎ์ถ”๊ธฐ
  • FE/UX: ์‚ฌ์šฉ์ž์—๊ฒŒ ๋Š์ž„ ์—†๋Š” ๋ฌดํ•œ ์Šคํฌ๋กค ๊ฒฝํ—˜ ์ œ๊ณต
NCP Object Storage ๋ณด์•ˆ: Pre-Signed URL ๋„์ž…

๋„์ž…๊ณ„๊ธฐ

  • CLOVA SPEECH API๋Š” ํŒŒ์ผ ๊ฒฝ๋กœ(dataKey)๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์—, ์Œ์„ฑ ํŒŒ์ผ์ด Object Storage์— ๋จผ์ € ์—…๋กœ๋“œ๋˜์–ด์•ผ ํ•จ
  • ์„œ๋ฒ„ ๋ถ€ํ•˜: FE โ†’ BE โ†’ Storage๋กœ ํŒŒ์ผ์„ ์ค‘๊ณ„ํ•˜๋ฉด ๋ฐฑ์—”๋“œ ๋ถ€ํ•˜๊ฐ€ ์‹ฌ๊ฐ
  • ๋ณด์•ˆ ์ทจ์•ฝ: FE โ†’ Storage๋กœ ์ง์ ‘ ์—…๋กœ๋“œํ•˜๋ ค๋ฉด, FE์— Secret Key๊ฐ€ ๋…ธ์ถœ๋˜์–ด ์น˜๋ช…์ 

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

  • ๋ฐฑ์—”๋“œ๊ฐ€ Secret Key๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ•˜๋ฉด์„œ, FE์—๊ฒŒ "10๋ถ„ ๋™์•ˆ, ํŠน์ • ๊ฒฝ๋กœ์—, PUT ์š”์ฒญ๋งŒ ํ—ˆ์šฉํ•˜๋Š”" ์ž„์‹œ ์—…๋กœ๋“œ URL์„ ๋ฐœ๊ธ‰.

  • FE โ†’ BE: ์—…๋กœ๋“œ URL ์š”์ฒญ (/api/answers/upload-url)

  • BE โ†’ FE: Pre-signed URL ์ƒ์„ฑ ๋ฐ ๋ฐ˜ํ™˜

  • FE โ†’ NCP Storage: FE๊ฐ€ ์ด ์ž„์‹œ URL์„ ์‚ฌ์šฉํ•ด ์Šคํ† ๋ฆฌ์ง€๋กœ ํŒŒ์ผ์„ ์ง์ ‘ PUT (์—…๋กœ๋“œ)

  • (์ดํ›„) FE โ†’ BE: "์—…๋กœ๋“œ ์™„๋ฃŒ" ์‘๋‹ต ์ดํ›„ STT ์š”์ฒญ (์ด๋•Œ BE๊ฐ€ Clova API ํ˜ธ์ถœ)

  • ์ถ”๊ฐ€ ํ•ต์‹ฌ ๋ณด์•ˆ ๊ฐ•ํ™” ์กฐ์น˜

    • ์‚ฌ์šฉ์ž๋ณ„ ๊ฒฝ๋กœ ๊ฒฉ๋ฆฌ (User Scoping)
      • ๋ฌธ์ œ: ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ™์€ ๊ณณ์— ์—…๋กœ๋“œํ•˜๋ฉด ํŒŒ์ผ์ด ๋ฎ์–ด์จ์ง€๊ฑฐ๋‚˜ ๊ฒฝ๋กœ ์กฐ์ž‘ ๊ณต๊ฒฉ์ด ๊ฐ€๋Šฅ
      • ํ•ด๊ฒฐ: ํŒŒ์ผ ๊ฒฝ๋กœ(objectKey)๋ฅผ **uploads/{userId}/{UUID}.[ํ™•์žฅ์ž]**๋กœ ๊ฐ•์ œํ•˜์—ฌ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ฒฉ๋ฆฌ
    • ํŒŒ์ผ ํ™•์žฅ์ž ๊ฒ€์ฆ (Allow-list)
      • ๋ฌธ์ œ: .html, .exe ๊ฐ™์€ ์•…์„ฑ ํŒŒ์ผ ์—…๋กœ๋“œ ์‹œ๋„ ์œ„ํ—™
      • ํ•ด๊ฒฐ: Clova๊ฐ€ ์ง€์›ํ•˜๋Š” ์˜ค๋””์˜ค ํ˜•์‹(.mp3, .m4a ๋“ฑ)์˜ 'ํ—ˆ์šฉ ๋ชฉ๋ก'๊ณผ ๋น„๊ต ๊ฒ€์ฆ ํ›„ ๋ชฉ๋ก์— ์—†์œผ๋ฉด 400 Bad Request๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์—…๋กœ๋“œ๋ฅผ ์›์ฒœ ์ฐจ๋‹จ
CORS์ •์ฑ… ๋ฐ Preflight ํ•ด๊ฒฐ

๋„์ž…๊ณ„๊ธฐ

  • CORS ์ •์ฑ… ์œ„๋ฐ˜: ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„์˜ Origin์ด ๋‹ฌ๋ผ, ๋ธŒ๋ผ์šฐ์ €์˜ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…(SOP)์— ์˜ํ•ด API ์š”์ฒญ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฐจ๋‹จ๋จ
  • Preflight (OPTIONS) ์š”์ฒญ ๋ฐœ์ƒ: ๋ณธ ์š”์ฒญ(POST, PUT ๋“ฑ)์— Authorization ํ—ค๋”(JWT ํ† ํฐ)๋ฅผ ํฌํ•จ์‹œํ‚ด
  • Authorization ํ—ค๋”๋Š” Simple Request ์กฐ๊ฑด์— ํ•ด๋‹นํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ณธ ์š”์ฒญ ์ „ ์„œ๋ฒ„์˜ ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ๋ฌป๋Š” OPTIONS ๋ฉ”์„œ๋“œ(Preflight) ์š”์ฒญ์„ ๋จผ์ € ์ „์†กํ•จ
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ณด๋‚ธ OPTIONS ์š”์ฒญ์—๋Š” ์ธ์ฆ ํ† ํฐ(Authorization ํ—ค๋”)์ด ์—†์Œ
    • ๋”ฐ๋ผ์„œ JwtAuthenticationFilter ์ด์ „์— Spring Security์˜ ์ธ์ฆ ์ฒด์ธ์ด ๋จผ์ € ๋™์ž‘ํ•˜์—ฌ, ์ด OPTIONS ์š”์ฒญ์„ 401 Unauthorized ๋˜๋Š” 403 Forbidden์œผ๋กœ ์ฐจ๋‹จํ•จ
  • ๋ธŒ๋ผ์šฐ์ €๋Š” Preflight ์š”์ฒญ์ด ์‹คํŒจ(200 OK๊ฐ€ ์•„๋‹˜)ํ–ˆ์œผ๋ฏ€๋กœ, ๋ณธ ์š”์ฒญ(POST ๋“ฑ)์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  CORS ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด

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

  • OPTIONS ์š”์ฒญ์— ๋Œ€ํ•œ Spring Security ์ธ์ฆ ํ•ด์ œ
    • filterChain ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ authorizeHttpRequests ์„ค์ •์„ ํ†ตํ•ด ๋ชจ๋“  OPTIONS ๋ฉ”์„œ๋“œ ์š”์ฒญ์€ ์ธ์ฆ ์ ˆ์ฐจ ์—†์ด ํ†ต๊ณผ(permit)์‹œํ‚ด
  • ๊ตฌ์ฒด์ ์ธ CORS ์ •์ฑ… ์ •์˜ ๋ฐ ์ ์šฉ
    • ํ—ˆ์šฉ ์ถœ์ฒ˜ (Origins): application.yml์— ์ •์˜๋œ ํ”„๋ก ํŠธ์—”๋“œ ๋„๋ฉ”์ธ ๋ชฉ๋ก์„ ํ—ˆ์šฉ
    • ํ—ˆ์šฉ ๋ฉ”์„œ๋“œ (Methods): OPTIONS๋ฅผ ํฌํ•จํ•œ GET, POST, PUT, DELETE ๋“ฑ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ํ—ˆ์šฉ
    • ํ—ˆ์šฉ ํ—ค๋” (Headers): Authorization ํ—ค๋”๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ํ—ค๋” ํ—ˆ์šฉ (setAllowedHeaders(List.of("*")))
    • ์ž๊ฒฉ ์ฆ๋ช… (Credentials): ์ฟ ํ‚ค(์˜ˆ: OAuth refresh_token)๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉ (setAllowCredentials(true))
    • filterChain ๋‚ด์—์„œ .cors(cors -> cors.configurationSource(corsConfigurationSource))๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ, Spring Security๊ฐ€ ์ด CORS ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •

๊ธฐ๋Œ€ํšจ๊ณผ

  • Preflight ์š”์ฒญ ์ฒ˜๋ฆฌ: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ OPTIONS ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, Spring Security์˜ permitAll ๊ทœ์น™ ๋•๋ถ„์— ์ธ์ฆ ์—†์ด ํ†ต๊ณผ
  • ๋ณธ ์š”์ฒญ ์ฒ˜๋ฆฌ: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ Preflight ์„ฑ๊ณต์„ ํ™•์ธํ•˜๊ณ  Authorization ํ—ค๋”๊ฐ€ ํฌํ•จ๋œ ์‹ค์ œ POST ์š”์ฒญ์„ ์ „์†ก
  • ์ด ์š”์ฒญ์€ OPTIONS๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ permitAll ๊ทœ์น™์— ๊ฑธ๋ฆฌ์ง€ ์•Š๊ณ , anyRequest().authenticated() ๊ทœ์น™์— ๋”ฐ๋ผ ์ธ์ฆ์ด ํ•„์š”
  • JwtAuthenticationFilter๊ฐ€ ํ† ํฐ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฒ€์ฆํ•˜์—ฌ ์ธ์ฆ์„ ์™„๋ฃŒ์‹œํ‚ค๊ณ , ์ปจํŠธ๋กค๋Ÿฌ๊นŒ์ง€ ์š”์ฒญ์ด ์ •์ƒ์ ์œผ๋กœ ๋„๋‹ฌ

7. ๊ธฐ์ˆ  ์Šคํƒ

7.0 ์ „์ฒด ์•„ํ‚คํ…์ฒ˜

แ„‡แ…ขแ„€แ…ญแ†ผ

์ฃผ์š” ์ปดํฌ๋„ŒํŠธ:

  • ํด๋ผ์ด์–ธํŠธ: Vite React ๊ธฐ๋ฐ˜ ์›น์•ฑ ๋˜๋Š” Chrome Extension
  • Spring Boot ์„œ๋ฒ„: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ๋ฐ API ์ œ๊ณต
  • NCP Object Storage: ์Œ์„ฑ ํŒŒ์ผ ์ €์žฅ์†Œ
  • NCP CLOVA STT: ์Œ์„ฑ์„ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
  • OpenAI GPT: ๋‹ต๋ณ€์— ๋Œ€ํ•œ AI ํ”ผ๋“œ๋ฐฑ ์ƒ์„ฑ
  • MySQL: Answer, Feedback, Question ๋“ฑ ๋ฐ์ดํ„ฐ ์˜๊ตฌ ์ €์žฅ

7.1 ๋ฐฑ์—”๋“œ

Java v21 MySQL v8.0 Spring v3.5.5 Docker v27.3.1 H2 v2.2.224 nGrinder v3.5.9
KakaoTalk_Photo_2025-11-06-19-13-00
  • ํ”„๋ ˆ์ž„์›Œํฌ: Spring Boot 3.5.5
  • ์–ธ์–ด: Java 21
  • ๋นŒ๋“œ ๋„๊ตฌ: Gradle 8.11
  • ์ธ์ฆ: Spring Security OAuth2 Client, JWT (jjwt 0.11.5)
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: MySQL 8.0 (InnoDB)
  • ORM: Spring Data JPA
  • AI ์—ฐ๋™:
    • OpenAI GPT (Spring AI 1.0.0)
    • NCP CLOVA STT (์Œ์„ฑโ†’ํ…์ŠคํŠธ ๋ณ€ํ™˜)
  • ์Šคํ† ๋ฆฌ์ง€: NCP Object Storage (AWS S3 ํ˜ธํ™˜)
  • ๋ชจ๋‹ˆํ„ฐ๋ง: Spring Actuator, nGrinder 3.5.9
  • ๋ฌธ์„œํ™”: SpringDoc OpenAPI 2.8.1
  • ๋น„๋™๊ธฐ ํ†ต์‹ : Server-Sent Events (SSE)

7.2 FrontEnd

  • ํ”„๋ ˆ์ž„์›Œํฌ: React (TypeScript)
  • ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ: Chrome Extension

7.3 ์ธํ”„๋ผ

  • ์ปจํ…Œ์ด๋„ˆํ™”: Docker, Docker Compose
  • ์ด๋ฏธ์ง€ ๋นŒ๋“œ: Jib
  • CI/CD: GitHub Actions
  • ๋ฐฐํฌ: SSH ๊ธฐ๋ฐ˜ ์ž๋™ ๋ฐฐํฌ

8. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

dailyq/
โ”œโ”€โ”€ src/main/java/com/knuissant/dailyq/
โ”‚   โ”œโ”€โ”€ config/              # ์„ค์ • ํด๋ž˜์Šค (Security, OAuth2, JWT ๋“ฑ)
โ”‚   โ”œโ”€โ”€ controller/          # REST API ์ปจํŠธ๋กค๋Ÿฌ
โ”‚   โ”œโ”€โ”€ domain/              # ๋„๋ฉ”์ธ ์—”ํ‹ฐํ‹ฐ (Answer, Question, User ๋“ฑ)
โ”‚   โ”œโ”€โ”€ dto/                 # ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ์ฒด
โ”‚   โ”œโ”€โ”€ event/               # ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
โ”‚   โ”œโ”€โ”€ exception/           # ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐ ์—๋Ÿฌ ์ฝ”๋“œ
โ”‚   โ”œโ”€โ”€ external/            # ์™ธ๋ถ€ API ์—ฐ๋™ (GPT, NCP)
โ”‚   โ”œโ”€โ”€ jwt/                 # JWT ํ† ํฐ ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ
โ”‚   โ”œโ”€โ”€ repository/          # ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต (JPA Repository)
โ”‚   โ””โ”€โ”€ service/             # ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ณ„์ธต
โ”œโ”€โ”€ src/main/resources/
โ”‚   โ”œโ”€โ”€ application.yml      # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •
โ”‚   โ”œโ”€โ”€ prompts/             # AI ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
โ”‚   โ””โ”€โ”€ static/              # SQL ์Šคํฌ๋ฆฝํŠธ (์Šคํ‚ค๋งˆ, ๋ชฉ ๋ฐ์ดํ„ฐ)
โ””โ”€โ”€ build.gradle             # ์˜์กด์„ฑ ๊ด€๋ฆฌ

E-R ๋‹ค์ด์–ด๊ทธ๋žจ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-11-07 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 1 37 05

9. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •

ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ

  • Java 21 ์ด์ƒ
  • Gradle 8.11 ์ด์ƒ
  • Docker ๋ฐ Docker Compose
  • MySQL 8.0

10. ๊ธฐ๋Œ€ ํšจ๊ณผ

  • ์‚ฌ์šฉ์ž: ๋งค์ผ ๊พธ์ค€ํ•œ ์—ฐ์Šต โ†’ ์„ฑ์žฅ ์ฒด๊ฐ โ†’ ๋™๊ธฐ ์œ ์ง€ โ†’ ์ทจ์—… ์„ฑ๊ณต ํ™•๋ฅ โ†‘
  • ์šด์˜์ž: ๋ฐ์ดํ„ฐ ์ถ•์ (๋‹ต๋ณ€/๋‚œ์ด๋„/์ž์†Œ์„œ) โ†’ AI ํ•™์Šต ์ž์› ํ™•๋ณด โ†’ ์„œ๋น„์Šค ๊ณ ๋„ํ™”
  • ์‹œ์žฅ ๊ฒฝ์Ÿ๋ ฅ: ๊ธฐ์กด ๋ชจ์˜๋ฉด์ ‘ ๋Œ€๋น„ ๊ฐ€๋ณ๊ณ , ๋ฉ”์ผ ๊ธฐ๋ฐ˜ ๋Œ€๋น„ ๊ฐ•์ œ์„ฑ์ด ์žˆ๋Š” '๋ฐ์ผ๋ฆฌ ๋ฃจํ‹ด' ์ฐจ๋ณ„ํ™”

11. ์ถ”ํ›„ ๋กœ๋“œ๋งต

  • ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ํ”ผ๋“œ๋ฐฑ
    • ํ˜„์žฌ๋Š” ํ…์ŠคํŠธ ์œ„์ฃผ์˜ ํ”ผ๋“œ๋ฐฑ โ†’ ํ†ค ๋ถ„์„๊นŒ์ง€ ํ™•์žฅ
    • ์Œ์„ฑ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํƒœ๋„ยท๋ชฉ์†Œ๋ฆฌ ์•ˆ์ •์„ฑ๊นŒ์ง€ ๋ถ„์„
  • ์ž์†Œ์„œ ๊ธฐ๋ฐ˜ ๋งž์ถค ์งˆ๋ฌธ ์ƒ์„ฑ
    • ์‚ฌ์šฉ์ž ์ž์†Œ์„œ ์—…๋กœ๋“œ โ†’ N๊ฐœ ํ•ต์‹ฌ ๋ฌธ์žฅ ์ถ”์ถœ โ†’ ์ง๊ตฐ/๊ธฐ์—… ์ปจํ…์ŠคํŠธ๋กœ ์งˆ๋ฌธ ์ƒ์„ฑ

12. ํŒ€ ์ปฌ์ณ

ํŒ€์› ์†Œ๊ฐœ

BE

๊น€๋„ํ˜„ ๊น€์ง„ํ˜„ ๋ฐ•์ค€ํฌ ๋ฐ•์†Œํ˜„
๋ฉ”์ด์ปค ํ…Œํฌ๋ฆฌ๋” ํŒ€ ๋ฆฌ๋” ํ”Œ๋ž˜๋„ˆ

FE

์ด์ฐฝ๋ชฉ ์œค์ž๋นˆ ์ง„๋™ํ˜„
ํ”Œ๋ž˜๋„ˆ ๋ฉ”์ด์ปค ํ…Œํฌ๋ฆฌ๋”

๋Œ€๋ฉด๋ฏธํŒ…

image

About

kakao-tech-campus TeamProject Backend

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 87.8%
  • HTML 10.1%
  • Shell 1.5%
  • Dockerfile 0.6%