diff --git a/README.md b/README.md index 23522f5e16..b74beed1a7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,67 @@ -# ๐Ÿ‘‹ ์™ธ๊ตญ์ธ ์œ ํ•™์ƒ๋“ค์„ ์œ„ํ•œ ์•ฑ, ์™ธ๊ตญ๋ฏผ -![แ„Žแ…ฌแ„Œแ…ฉแ†ผ แ„‡แ…กแ†ฏแ„‘แ…ญแ„Œแ…กแ„…แ…ญ](https://github.com/kookmin-sw/capstone-2024-30/assets/52407470/20fc41c1-8a22-4c90-a1f1-8539dea92ed1) +# ๐Ÿ’ป ์บก์Šคํ†ค 30์กฐ ๊ฒฐ๊ณผ๋ฌผ ์†Œ๊ฐœ + +
+ +[![Typing SVG](https://readme-typing-svg.demolab.com?font=Jua&size=25&pause=1000&color=6D2FF4&random=false&width=435&lines=%F0%9F%91%8B+%EC%99%B8%EA%B5%AD%EC%9D%B8+%EC%9C%A0%ED%95%99%EC%83%9D%EB%93%A4%EC%9D%84+%EC%9C%84%ED%95%9C+%EC%95%B1%2C+%EC%99%B8%EA%B5%AD%EB%AF%BC)](https://git.io/typing-svg) + -[์ค‘๊ฐ„๋ฐœํ‘œ ์ž๋ฃŒ ๋ฐ ๋ณด๊ณ ์„œ](https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link) +![แ„Žแ…ฌแ„Œแ…ฉแ†ผ แ„‡แ…กแ†ฏแ„‘แ…ญแ„Œแ…กแ„…แ…ญ](https://github.com/kookmin-sw/capstone-2024-30/assets/52407470/20fc41c1-8a22-4c90-a1f1-8539dea92ed1) -[์ตœ์ข…๋ฐœํ‘œ ์ž๋ฃŒ ๋ฐ ๋ณด๊ณ ์„œ](https://drive.google.com/drive/folders/1gVtsjX9nk8KhyeNu-hTzpeZjZnjj-pR8?usp=sharing) +
+ + [์ค‘๊ฐ„๋ฐœํ‘œ ์ž๋ฃŒ ๋ฐ ๋ณด๊ณ ์„œ] + +
+
+ + [์ตœ์ข…๋ฐœํ‘œ ์ž๋ฃŒ&ํฌ์Šคํ„ฐ ๋ฐ ๋ณด๊ณ ์„œ] + +
+
+ + [Github ํ™ˆํŽ˜์ด์ง€] + +
+
+ ํŒŒํŠธ๋ณ„ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ฐ ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ ๋งˆํฌ๋‹ค์šด ๋ฌธ์„œ์— ์žˆ์Šต๋‹ˆ๋‹ค. +
+
+ + + + + + + + + + + +
+ ํ”„๋ก ํŠธ + + ๋ฐฑ + + AI +
+ FRONT.md + + BACK.md + + AI.md +
+

+
+

+ +

+
+ Play ์Šคํ† ์–ด ๋‹ค์šด๋กœ๋“œ +
+ ## 1. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ ์ด ํ”„๋กœ์ ํŠธ๋Š” ๊ตญ๋ฏผ๋Œ€ํ•™๊ต ์œ ํ•™์ƒ๋“ค์ด ๊ฒช๋Š” ์–ธ์–ด์ , ๋ฌธํ™”์  ๋ถˆํŽธํ•จ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” ์•ฑ์—์„œ ์œ ํ•™์ƒ๋“ค์ด ์บ ํผ์Šค ์ƒํ™œ์— ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ์ •๋ณด์™€ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. @@ -19,7 +74,13 @@ This project aims to develop a comprehensive app service for international stude
-## 3. ํ”„๋กœ์ ํŠธ ๊ธฐ๋Šฅ +## 3. ์†Œ๊ฐœ ์˜์ƒ + +https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/69c09b39-f44e-4edf-abab-6b16485a2f57 + +
+ +## 4. ํ”„๋กœ์ ํŠธ ๊ธฐ๋Šฅ #### 1๏ธโƒฃ ๋ฒˆ์—ญ๋œ ๊ณต์ง€์‚ฌํ•ญ / ํ•™์‹ / ํ•™๊ต์ •๋ณด ์ œ๊ณต @@ -27,35 +88,54 @@ This project aims to develop a comprehensive app service for international stude ๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ ์„œ๋น„์Šค๋Š” ์„ค์ •ํ•œ ์–ธ์–ด์— ๋งž์ถฐ์„œ ๊ณต์ง€์‚ฌํ•ญ/ํ•™์‹/ํ•™๊ต์ •๋ณด ๋ฒˆ์—ญ๋ณธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +|๋ฒˆ์—ญ๋œ ๊ณต์ง€์‚ฌํ•ญ|๊ณต์ง€์‚ฌํ•ญ ๋””ํ…Œ์ผ|๋ฒˆ์—ญ๋œ ํ•™์‹์ •๋ณด| +|------|---|---| +|||| +
+ #### 2๏ธโƒฃ ์ฑ—๋ด‡ ๊ธฐ๋Šฅ ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์—์„œ๋Š” ON๊ตญ๋ฏผ ์ฑ—๋ด‡ "์ฟ ๋ฏผ์ด"๋ฅผ ์„œ๋น„์Šคํ•˜๊ณ ์žˆ์œผ๋‚˜, ์„ฑ๋Šฅ์ด ๋งค์šฐ ํ˜•ํŽธ์—†์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์งˆ๋ฌธ์—๋„ ๋™๋ฌธ์„œ๋‹ต์„ ํ•˜๊ฑฐ๋‚˜, ์˜์–ด๋กœ ์งˆ๋ฌธํ–ˆ๋Š”๋ฐ ํ•œ๊ธ€๋กœ ๋‹ต๋ณ€ํ•˜๋Š” ๋“ฑ ์ „ํ˜€ ์ฑ—๋ด‡์œผ๋กœ์„œ์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ RAG์™€ LLM์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์— ํŠนํ™”๋œ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•˜๊ณ  ๋‹ค๊ตญ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” "KuKu" ์ฑ—๋ด‡์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +|๊ตญ๋ฏผ๋Œ€ ๊ด€๋ จ ์งˆ๋ฌธ|๋‹ค๊ตญ์–ด ์ง€์›|์ผ์ƒ ๋Œ€ํ™”| +|------|---|---| +|||| +
+ #### 3๏ธโƒฃ ๋ฐœ์Œ ๊ต์ • ๊ธฐ๋Šฅ ๋งŽ์€ ์™ธ๊ตญ์ธ๋“ค์€ ํ•œ๊ตญ์— ์™€์„œ ์–ธ์–ด ๋ฌธ์ œ๋กœ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ํ•™๊ต ์ƒํ™œ์„ ํ•˜๋‹ค๋ณด๋ฉด ๋ฐœํ‘œ๋ฅผ ํ•˜๊ฑฐ๋‚˜ ์ผ์ƒ์ƒํ™œ์—์„œ ์˜์‚ฌ์†Œํ†ต์„ ํ•ด์•ผํ•  ๋•Œ, ๋ณธ์ธ์˜ ๋ฐœ์Œ์ด ์ •ํ™•ํ•œ์ง€ ํ™•์ธํ•  ๋ฐฉ๋ฒ•์ด ์—†์–ด์„œ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ ์ž์‹ ์˜ ๋ฐœํ‘œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋„ฃ์–ด์„œ ๋ฐœ์Œ ํ‰๊ฐ€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ํ•œ๊ตญ์˜ ์ผ์ƒ์ƒํ™œ์—์„œ ๋งŽ์ด ์“ฐ์ด๋Š” ์—ฌ๋Ÿฌ ํ‘œํ˜„๋“ค์„ ์—ฐ์Šตํ•  ์ˆ˜ ์žˆ๋„๋กํ•˜์—ฌ ํ•œ๊ตญ ์œ ํ•™์ƒํ™œ์„ ๋•๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. +|์ผ์ƒ์ƒํ™œ์— ์ž์ฃผ์“ฐ๋Š” ์˜ˆ๋ฌธ ์ง€์›|๋ฌธ์žฅ ์ปค์Šคํ…€ ์ง€์›|๋ฐœ์Œ ํ‰๊ฐ€ ์ œ๊ณต| +|------|---|---| +|||| +
+ #### 4๏ธโƒฃ ํ—ฌํผ ๋งค์นญ ๊ธฐ๋Šฅ ๋งŽ์€ ์™ธ๊ตญ์ธ๋“ค์ด ๋‚ฏ์„  ๋•…์— ์™”์„ ๋•Œ ๋„์›€์„ ๋ฐ›์„ ์‚ฌ๋žŒ์ด ์—†์–ด์„œ ๋งค์šฐ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ ์™ธ๊ตญ์ธ๋“ค์„ ๋„์šธ ์ˆ˜ ์žˆ๋„๋ก ํ—ฌํผ ๋งค์นญ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•œ๊ตญ์ธ or ์˜ค๋žœ ์œ ํ•™์ƒํ™œ์„ ํ•˜์—ฌ ํ•œ๊ตญ ์ƒํ™œ์— ์ต์ˆ™ํ•ด์ง„ ์™ธ๊ตญ์ธ ํ—ฌํผ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +|ํ—ฌํผ ๋ฐ ํ—ฌํ”ผ ๊ฒŒ์‹œํŒ|๋””ํ…Œ์ผ|์ฑ„ํŒ…| +|------|---|---| +|||| +
+ #### 5๏ธโƒฃ Q&A์™€ FAQ ๊ธฐ๋Šฅ ์œ ํ•™์ƒ๋“ค์ด ํ•œ๊ตญ์ƒํ™œ์—์„œ ๊ถ๊ธˆํ•œ ๊ฒƒ์„ ๋ฌผ์–ด๋ณผ๋งŒํ•œ ๊ณณ์ด ๋งˆ๋•…์น˜ ์•Š๊ณ , ON๊ตญ๋ฏผ์— ์žˆ๋Š” FAQ์˜ ์กด์žฌ๋ฅผ ์•Œ๊ธฐ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด FAQ ๋˜ํ•œ ๋ฒˆ์—ญ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ Q&A ๊ฒŒ์‹œํŒ๊ณผ ๋‹ค๊ตญ์–ด๋กœ ๋ฒˆ์—ญ๋œ FAQ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. -
- -## 4. ์†Œ๊ฐœ ์˜์ƒ +|Q&A ๊ฒŒ์‹œํŒ|๋””ํ…Œ์ผ|FAQ ์กฐํšŒ| +|------|---|---| +|||| -ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœํ•˜๋Š” ์˜์ƒ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”
@@ -68,9 +148,9 @@ This project aims to develop a comprehensive app service for international stude - ์ตœ์ง€ํ›ˆ - ๊น€๋ฏผ์ œ - ์กฐํ˜„์ง„ + ์ตœ์ง€ํ›ˆ + ๊น€๋ฏผ์ œ + ์กฐํ˜„์ง„ ****1683 @@ -91,9 +171,9 @@ This project aims to develop a comprehensive app service for international stude - ์ฑ„์›์ฐฌ - ๊น€ํ˜œ์„ฑ - ์ตœ์˜๋ฝ + ์ฑ„์›์ฐฌ + ๊น€ํ˜œ์„ฑ + ์ตœ์˜๋ฝ ****1676 @@ -113,46 +193,49 @@ This project aims to develop a comprehensive app service for international stude ### ๐Ÿ›  Frontend -|์—ญํ• |์ข…๋ฅ˜| -|-|-| -|Framework|| -|Database|RED RED| -|Programming Language|| -|Device| | +| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Framework | | +| Database | RED RED | +| Programming Language | | +| Device | | +
### ๐Ÿ’พ Backend -|์—ญํ• |์ข…๋ฅ˜| -|-|-| -|Framework|RED RED RED RED| -|Database|RED RED| -|Programming Language|RED RED| -|Test|RED RED | -|Deploy|RED RED RED RED RED RED RED| -|CI/CD|RED| -|ETC|RED RED RED| -
+| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Framework | RED RED RED RED | +| Database | RED RED | +| Programming Language | RED RED | +| Test | RED RED | +| Deploy | RED RED RED RED RED RED RED | +| CI/CD | RED | +| ETC | RED RED RED | +
### ๐Ÿ“ป AI -|์—ญํ• |์ข…๋ฅ˜| -|-|-| -|Programming Language|RED| -|Development|RED RED | -|Technology|RED RED | -|Test|| -|Server|| +| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Programming Language | RED | +| Development | RED RED | +| Technology | RED RED | +| Test | | +| Server | | +
### ๐Ÿ”จ Tools -|์—ญํ• |์ข…๋ฅ˜| -|-|-| -|Version Control| | -|Cooperation|RED RED RED| -|Test| | +| ์—ญํ•  | ์ข…๋ฅ˜ | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Version Control | | +| Cooperation | RED RED RED | +| Test | | +

@@ -162,7 +245,7 @@ This project aims to develop a comprehensive app service for international stude ### ๐Ÿ’ป ์„œ๋น„์Šค ์•„ํ‚คํƒ์ฒ˜
- + ### ๐Ÿค– ์ฑ—๋ด‡ ์•„ํ‚คํ…์ฒ˜ @@ -219,6 +302,9 @@ flutter run ### Backend +> [!NOTE] +> ๋„์ปค๊ฐ€ ์„ค์น˜๋œ ํ™˜๊ฒฝ์—์„œ๋งŒ ์ง„ํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + `.env.example`์„ ๋ฐ”ํƒ•์œผ๋กœ `.env`๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ ``` @@ -231,33 +317,28 @@ docker-compose up -d ### AI -### Chat bot `KUKU` ์†Œ๊ฐœ - -๊ตญ๋ฏผ๋Œ€์— ๊ด€ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๋ฌผ์–ด๋ณด์„ธ์š” ! -๊ตญ๋ฏผ๋Œ€ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์ฑ—๋ด‡ <์ฟ ์ฟ >์ž…๋‹ˆ๋‹ค. - -### ํ•™์Šตํ•œ ๋ฐ์ดํ„ฐ +1. `git clone https://github.com/kookmin-sw/capstone-2024-30.git` +2. `cd YOUR PATH/ai/` +3. `pip install -r requirements.txt` +4. ๋ฒกํ„ฐ ์ €์žฅ์†Œ FAISS ํด๋”๋ฅผ `/ai` ์— ์œ„์น˜ [๋‹ค์šด๋กœ๋“œ ๋งํฌ](https://drive.google.com/file/d/1-U5X_xRg0PLITrDWDNMeZK_NAP5IIwsL/view?usp=sharing) +5. `python run_chatbot.py` -- [๊ตญ๋ฏผ๋Œ€ 2023 ์š”๋žŒ PDF](https://www.kookmin.ac.kr/comm/cmfile/thumbnail2.do?encSvrFileNm=223d2bdfdbb4df30ad85271267bd6e6a0a913159736ab9843bb76fc00eeeb5ddb1e88f35002b9cf1b749bfe96b751f16b8be21ad5273d348a74b10a57513dd4540bbcb178d3151db4d507c693a1f7ef9&encFileGrpSeq=8e8e9041def64eb5f7f8c21154bcff06&encFileSeq=cf9f1626435aafc6e0e182b36c8e23d9) -- [2023~2024.03.28 ๊ตญ๋ฏผ๋Œ€ ์ „์ฒด ๊ณต์ง€์‚ฌํ•ญ](https://www.kookmin.ac.kr/user/kmuNews/notice/index.do) - -### ์‹คํ–‰ ๋ฐฉ๋ฒ• - -2024_03_30 ์•„๋‚˜์ฝ˜๋‹ค ํ™˜๊ฒฝ python 3.8 ์—์„œ ์‹คํ–‰ ํ™•์ธ - -1. /ai ๊ฒฝ๋กœ๋กœ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์ด๋™ -2. ํŒจํ‚ค์ง€ ์„ค์น˜ `pip install -r requirements.txt` -3. /ai ๊ฒฝ๋กœ์— .env ํŒŒ์ผ ์ƒ์„ฑ (OPENAI_API_KEY = 'your_api') -4. `python run_chatbot.py` +
-### TEST +## 9. ๊ธฐํƒ€ -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/6b7c8514-35dd-41bb-9815-56a182a7ccfb) +### ์ฑ—๋ด‡ ์ฃผ์š” ์ˆ˜์ง‘ ๋ฐ์ดํ„ฐ ์ถœ์ฒ˜ -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/e37af8f8-a35b-4808-a357-aff5aebb8d88) +- [๊ตญ๋ฏผ๋Œ€ 2023 ์š”๋žŒ PDF](https://www.kookmin.ac.kr/comm/cmfile/thumbnail2.do?encSvrFileNm=223d2bdfdbb4df30ad85271267bd6e6a0a913159736ab9843bb76fc00eeeb5ddb1e88f35002b9cf1b749bfe96b751f16b8be21ad5273d348a74b10a57513dd4540bbcb178d3151db4d507c693a1f7ef9&encFileGrpSeq=8e8e9041def64eb5f7f8c21154bcff06&encFileSeq=cf9f1626435aafc6e0e182b36c8e23d9) +- [๊ตญ๋ฏผ๋Œ€ ์ „์ฒด ๊ณต์ง€์‚ฌํ•ญ](https://www.kookmin.ac.kr/user/kmuNews/notice/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ๋Œ€ํ•™์†Œ๊ฐœ](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ๋Œ€ํ•™์ƒํ™œ](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ํ•™์‚ฌ์•ˆ๋‚ด](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ์†Œํ”„ํŠธ์›จ์–ด์œตํ•ฉ๋Œ€ํ•™](https://cs.kookmin.ac.kr/intro/intro) +- [๊ตญ๋ฏผ๋Œ€ ์†Œํ”„ํŠธ์›จ์–ด์œตํ•ฉ๋Œ€ํ•™ ๊ณต์ง€์‚ฌํ•ญ](https://cs.kookmin.ac.kr/news/notice/) +- ๊ทธ ์™ธ ๋„ค์ด๋ฒ„ ๋ฐ ๊ตฌ๊ธ€ ๊ธฐํƒ€ ์ˆ˜์ง‘ ๋ฐ์ดํ„ฐ -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/d7b5cd40-43b7-4cca-8be8-6fff257ed303) +### Metrics -## 9. ๊ธฐํƒ€ +[RAGAS](https://docs.ragas.io/en/stable/concepts/metrics/index.html) -
diff --git a/ai/.env.example b/ai/.env.example new file mode 100644 index 0000000000..a436a61050 --- /dev/null +++ b/ai/.env.example @@ -0,0 +1,16 @@ +### API KEY + +OPENAI_API_KEY = +CLOVA_KEY = +CLOVA_URL = +LANGCHAIN_API_KEY = +TAVILY_API_KEY = +DEEPL_API_KEY = + +### Database + +DB_ENDPOINT = +DB_PORT = +DB_NAME = +MYSQL_USERNAME = +MYSQL_PASSWORD = \ No newline at end of file diff --git a/ai/.gitignore b/ai/.gitignore index 9cee807084..e61c908eff 100644 --- a/ai/.gitignore +++ b/ai/.gitignore @@ -3,6 +3,11 @@ .env *.zip *.pyc -__pycache__ +ai/llm/__pycache__/* +ai/vectordb/__pycache__/* data/ -cap30.pem \ No newline at end of file +cap30.pem +chatbot.pem +FAISS/ +*.pkl +*.faiss diff --git a/ai/AI.md b/ai/AI.md new file mode 100644 index 0000000000..4897526933 --- /dev/null +++ b/ai/AI.md @@ -0,0 +1,156 @@ +# ๐Ÿค– KUKU ์ฑ—๋ด‡ Overview + +## **์™ธ๊ตญ๋ฏผ KUKU (OURS)** + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/dcec3a29-6d66-45f7-a5af-8f7962fd81e7) + + +- **์‹œ๋‚˜๋ฆฌ์˜ค 1 : ์œ ์ € ์ฟผ๋ฆฌ๊ฐ€ ์ผ์ƒ๋Œ€ํ™”์ผ ๋•Œ** + +์œ ์ € ์ฟผ๋ฆฌ๊ฐ€ ์งˆ๋ฌธ์ด ์•„๋‹Œ, โ€˜์•ˆ๋…•, โ€˜๋ฐฐ๊ณ ํŒŒโ€™, โ€˜์˜ค๋Š˜ ๊ธฐ๋ถ„์ด ์ข‹์•„โ€™ ๊ฐ™์€ casual ๋Œ€ํ™”์ผ ๋•Œ ํŒŒ์ธํŠœ๋‹๋œ LLM์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๋ผ์šฐํ„ฐ๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ๊ฐ€ โ€˜์งˆ๋ฌธ์ธ์ง€ ์•„๋‹Œ์ง€โ€™๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค. causal ๋Œ€ํ™”์ผ ๋•Œ๋Š” โ€˜๊ตญ๋ฏผ๋Œ€ํ•™๊ต ํ•™์ƒ๋“ค๊ณผ ๋Œ€ํ™”๋ฅผ ํ•˜๋Š” ์นœ์ ˆํ•œ ์–ด์‹œ์Šคํ„ดํŠธโ€™ ๋ผ๋Š” ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” LLM ๋ชจ๋ธ๋กœ ์ „๋‹ฌ๋˜์–ด ์ผ์ƒ์ ์ธ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๊ณ , ์œ ์ €ํ•œํ…Œ ์ „๋‹ฌํ•œ๋‹ค. + +- **์‹œ๋‚˜๋ฆฌ์˜ค 2 : ์œ ์ € ์ฟผ๋ฆฌ๊ฐ€ ์งˆ๋ฌธ์ผ ๋•Œ** + +์งˆ๋ฌธ ์ฟผ๋ฆฌ๋Š” ์šฐ์„  ์–ธ์–ด์™€ ์ƒ๊ด€ ์—†์ด ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญ๋œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์ˆ˜์ง‘ํ•œ ๊ฑฐ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ๊ตญ์–ด๋กœ ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ฟผ๋ฆฌ์™€ ๋ฌธ์„œ์˜ ์œ ์‚ฌ๋„ ๋น„๊ต๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๋ชจ๋‘ ๋ฒกํ„ฐํ™” ๋˜์–ด ์žˆ๊ณ  '๊ณต์ง€์‚ฌํ•ญ ๊ด€๋ จ', 'ํ•™๊ต ์ƒํ™œ ๊ด€๋ จ', '๊ทธ ์™ธ ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ' ์„ธ ๊ฐ€์ง€ ๋ถ„๋ฅ˜๋กœ ๋‚˜๋ˆ ์ ธ ์žˆ๋‹ค. ๊ฐ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์— ์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•œ ๊ฐ€์ค‘์น˜์— ๋”ฐ๋ผ langchain์˜ ์•™์ƒ๋ธ” ๊ฒ€์ƒ‰๊ธฐ๋ฅผ ์ˆ˜ํ–‰๋œ๋‹ค. ์ƒ์œ„ K๊ฐœ(k=10)๊ฐœ์˜ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ LLM์€ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•œ๋‹ค. +์ดํ›„, ์ด ๋‹ต๋ณ€์€ โ€˜๋‹ต๋ณ€์ด ์ ์ ˆํ•œ์ง€ ์•„๋‹Œ์ง€โ€™ ๋ถ„๋ฅ˜๋ฅผ ์œ„ํ•ด Fine tuning๋œ LLM์— ์ „๋‹ฌ๋œ๋‹ค. + + - **์‹œ๋‚˜๋ฆฌ์˜ค 2-1 : ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ด ์ ์ ˆํ•  ๋•Œ** + +์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ด ์ ์ ˆํ•  ๋•Œ, ํ•œ๊ตญ์–ด๋กœ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ์–ธ์–ด๋กœ ๋ฒˆ์—ญ ๋˜์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌ ๋œ๋‹ค. + + - **์‹œ๋‚˜๋ฆฌ์˜ค 2-2 : ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ด ์ ์ ˆํ•˜์ง€ ์•Š์„ ๋•Œ** + +์ด์ „์— ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ๋ฒ„๋ฆฌ๊ณ  ๊ตฌ๊ธ€ ๊ฒ€์ƒ‰ ๊ธฐ๋ฐ˜ RAG ์‹œ์Šคํ…œ โ€˜Tavily Search APIโ€™๋ฅผ ํ†ตํ•ด ์ƒˆ๋กญ๊ฒŒ ๋‹ต๋ณ€์„ ๊ตฌ์„ฑํ•œ๋‹ค. ์ดํ›„ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ์–ธ์–ด๋กœ ๋ฒˆ์—ญ ํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค. + +## **Simple RAG** + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/02bc878b-a121-4ffc-9f09-6be149cf004d) + +# Usage +๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ์„ ํƒ + +### 1. ์™ธ๊ตญ๋ฏผ App (์ถ”์ฒœ) `gpt-4o ๋ชจ๋ธ` +ํ”Œ๋ ˆ์ด์Šคํ† ์–ด์˜ ์™ธ๊ตญ๋ฏผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์šด๋ฐ›์•„ ์ฑ—๋ด‡์„ ์‚ฌ์šฉ + +### 2. Python + +์ด ๋ฐฉ๋ฒ•์€ API KEY๊ฐ€ ํ•„์š” ํ•ฉ๋‹ˆ๋‹ค! + +1. `git clone https://github.com/kookmin-sw/capstone-2024-30.git` +2. `cd YOUR PATH/ai/` +3. `pip install -r requirements.txt` +4. ๋ฒกํ„ฐ ์ €์žฅ์†Œ FAISS ํด๋”๋ฅผ `/ai` ์— ์œ„์น˜ [๋‹ค์šด๋กœ๋“œ ๋งํฌ](https://drive.google.com/file/d/1-U5X_xRg0PLITrDWDNMeZK_NAP5IIwsL/view?usp=sharing) +5. /ai์— `.env` ํŒŒ์ผ ์ƒ์„ฑ +``` +OPENAI_API_KEY = +LANGCHAIN_API_KEY = +TAVILY_API_KEY = +CHANNEL_ID = +DEEPL_API_KEY = +PAPAGO_ID = +PAPAGO_API_KEY = +``` +7. `python run_chatbot.py` + +### 3. DiscordBot `gpt-3.5-turbo` + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/ea2fd088-3bfa-4fb1-940f-ab34dc6b74d0) + +- ๋””์Šค์ฝ”๋“œ ์ฑ„๋„์˜ KUKU ๋ด‡ ์ดˆ๋Œ€ ํ›„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ฑ„ํŒ… +- ๋ด‡ ์ดˆ๋Œ€ ๊ถŒํ•œ ํ•„์š” (์„œ๋ฒ„ ๊ด€๋ฆฌ์ž) +- ๋ด‡ ์ดˆ๋Œ€ ๋งํฌ : https://discord.com/oauth2/authorize?client_id=1229021729192677488 +- `!p ์งˆ๋ฌธ ๋‚ด์šฉ` ๋ช…๋ น์–ด๋กœ ์ฑ„ํŒ… ๊ฐ€๋Šฅ +- (๋น„์šฉ ๋ฌธ์ œ๋กœ gpt-3.5-turbo๋ฅผ ์‚ฌ์šฉ์ค‘. ์™ธ๊ตญ๋ฏผ ์•ฑ๊ณผ ์„ฑ๋Šฅ์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ) + +# Metrics +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/2d4ed4fc-1f19-44a4-aa30-915b39c84a1c) +[RAGAS](https://docs.ragas.io/en/stable/concepts/metrics/index.html) + +``` +Ragas is a framework that helps you evaluate your Retrieval Augmented Generation (RAG) pipelines. RAG denotes a class of LLM applications that use external data to augment the LLMโ€™s context. There are existing tools and frameworks that help you build these pipelines but evaluating it and quantifying your pipeline performance can be hard. This is where Ragas (RAG Assessment) comes in. +``` + + + > __๋น„๊ต ๋ชจ๋ธ__ (Comparison model) +- ์™ธ๊ตญ๋ฏผ KUKU (OURS) +- SimpleRAG +- ChatGPT(gpt-3.5-turbo) +- ON๊ตญ๋ฏผ ์ฟ ๋ฏผ์ด + +ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉ๋œ ์•ฝ 200๊ฐœ์˜ ์งˆ๋ฌธ์€ [question_list.md](./question_list.md) ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ + +## Faithfulness + +``` +This measures the factual consistency of the generated answer against the given context. It is calculated from answer and retrieved context. The answer is scaled to (0,1) range. Higher the better. +The generated answer is regarded as faithful if all the claims that are made in the answer can be inferred from the given context. To calculate this a set of claims from the generated answer is first identified. Then each one of these claims are cross checked with given context to determine if it can be inferred from given context or not. The faithfulness score is given by divided by +``` + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/612cce91-8e97-4613-8463-926a731d1d15) +- Faithfulness๋Š” ์ฑ—๋ด‡์˜ ๋‹ต๋ณ€ ๋น„์œจ์ด ์–ผ๋งˆ๋‚˜ ์‚ฌ์‹ค์— ๊ธฐ๋ฐ˜ํ–ˆ๋Š”์ง€ ํ‰๊ฐ€ํ•˜๋Š” ์ง€ํ‘œ์ด๋‹ค. +- 0~1 ๋ฒ”์œ„์˜ ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ ๋†’์„ ์ˆ˜๋ก ์ข‹๋‹ค + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/61d253af-08b1-4fc6-b9d0-85b3f924df16) +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/3e444258-7d20-4fa5-b041-a69ff6764969) + +- ๋””์Šค์ฝ”๋“œ ๋ด‡์„ ํ†ตํ•ด ๊ตญ๋ฏผ๋Œ€ 19ํ•™๋ฒˆ์œผ๋กœ ๊ตฌ์„ฑ๋œ โ€˜์™ธ๊ตญ๋ฏผโ€™ ํŒ€์›์ด ์ง์ ‘ ์‚ฌ์‹ค๊ด€๊ณ„๋ฅผ ํ‰๊ฐ€ํ•˜์—ฌ ์ธก์ • +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/c49c1e68-b4d9-4bbf-8d32-7cb2036f15cd) + + +## Answer Relevancy + +``` +The evaluation metric, Answer Relevancy, focuses on assessing how pertinent the generated answer is to the given prompt. A lower score is assigned to answers that are incomplete or contain redundant information and higher scores indicate better relevancy. This metric is computed using the question, the context and the answer. +``` + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/acd26582-174f-4b4a-9c09-6f355db0190a) +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/941267d5-135c-4872-a0ed-97e64253ddb5) + +``` +The Answer Relevancy is defined as the mean cosine similartiy of the original question to a number of artifical questions, which where generated (reverse engineered) based on the answer +``` + +- Answer Relevance์€ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์ด ์ฃผ์–ด์ง„ ํ”„๋กฌํ”„ํŠธ์™€ ์–ผ๋งˆ๋‚˜ ์ ์ ˆํ•œ์ง€๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋ฐ ์ดˆ์ ์„ ๋งž์ถ˜๋‹ค. +- ๋ถˆ์™„์ „ํ•˜๊ฑฐ๋‚˜ ์ค‘๋ณต ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๋‹ต๋ณ€์—๋Š” ๋‚ฎ์€ ์ ์ˆ˜๊ฐ€ ํ• ๋‹น๋˜๊ณ , ๋” ๋†’์€ ์ ์ˆ˜๋Š” ๋” ์ข‹์€ ์ ํ•ฉ์„ฑ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. +- 0~1 ๋ฒ”์œ„์˜ ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ ๋†’์„ ์ˆ˜๋ก ์ข‹๋‹ค + + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/b1b1e217-5ad8-4238-8092-605e9ce764da) + +Answer Relevance๋Š” Reverse Engineering์„ ํ†ตํ•ด ๊ณ„์‚ฐ๋œ๋‹ค. ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. +1. ์ฑ—๋ด‡์—๊ฒŒ ์งˆ๋ฌธ ํ›„ ๋‹ต๋ณ€์„ ๋ฐ›๋Š”๋‹ค. +2. ๋‹ต๋ณ€์„ llm์˜ ์ž…๋ ฅ์œผ๋กœ ๋„ฃ์–ด ์งˆ๋ฌธ์„ ์˜ˆ์ธกํ•˜๊ฒŒ ํ•œ๋‹ค. +3. ์˜ˆ์ธก๋œ ์งˆ๋ฌธ๊ณผ ์›๋ž˜์˜ ์งˆ๋ฌธ์˜ ์œ ์‚ฌ๋„(์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„)๋ฅผ ๋น„๊ตํ•œ๋‹ค. + +## Latency +- ์ฑ—๋ด‡์—๊ฒŒ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ธ ํ›„ ๋‹ต๋ณ€ ์‘๋‹ต๊นŒ์ง€ ๊ฑธ๋ฆฐ ํ‰๊ท  ์†Œ์š” ์‹œ๊ฐ„(sec) +- ๋‚ฎ์„ ์ˆ˜๋ก ์ข‹๋‹ค. + + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/30caad13-3e69-4345-b1aa-727aaf97e93c) + + +# Test Sample + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/5a5bc026-8884-4e44-8b1a-cec26b3216f3) +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/80c9b745-4c78-430e-9f39-bb0945a27000) + +# Test Log + +`chatbot_result.xlsx` + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/d6442b14-606f-4182-976c-a1dfa241b5ee) + +# LangSmith Tracing + +LangSmith๋ฅผ ํ†ตํ•ด ๋””๋ฒ„๊น… ๋ฐ ์ถ”์  + +- ๋žญ์Šค๋ฏธ์Šค๋ฅผ ํ†ตํ•ด Langchain์˜ ์ฒด์ธ๊ฐ„์˜ ์ž…์ถœ๋ ฅ ํ™•์ธ ๊ฐ€๋Šฅ +- Retriever์˜ ๊ฒฐ๊ณผ๋กœ ์–ด๋–ค ๋ฌธ์„œ๊ฐ€ ๊ฒ€์ƒ‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธ ๊ฐ€๋Šฅ + + +์˜ˆ์‹œ) +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/9602ab01-9ce5-4847-a681-6a022045908b) + + + diff --git a/ai/FAISS/NAVER/index.faiss b/ai/FAISS/NAVER/index.faiss new file mode 100644 index 0000000000..e32bf38e56 Binary files /dev/null and b/ai/FAISS/NAVER/index.faiss differ diff --git a/ai/FAISS/NAVER/index.pkl b/ai/FAISS/NAVER/index.pkl new file mode 100644 index 0000000000..d7901efac4 Binary files /dev/null and b/ai/FAISS/NAVER/index.pkl differ diff --git a/ai/__pycache__/llm_rag.cpython-312.pyc b/ai/__pycache__/llm_rag.cpython-312.pyc deleted file mode 100644 index 593eeede8b..0000000000 Binary files a/ai/__pycache__/llm_rag.cpython-312.pyc and /dev/null differ diff --git a/ai/__pycache__/llm_rag.cpython-39.pyc b/ai/__pycache__/llm_rag.cpython-39.pyc deleted file mode 100644 index a20fbb255e..0000000000 Binary files a/ai/__pycache__/llm_rag.cpython-39.pyc and /dev/null differ diff --git a/ai/__pycache__/vector_db.cpython-39.pyc b/ai/__pycache__/vector_db.cpython-39.pyc deleted file mode 100644 index a2bc278e5c..0000000000 Binary files a/ai/__pycache__/vector_db.cpython-39.pyc and /dev/null differ diff --git a/ai/chatbot_result.xlsx b/ai/chatbot_result.xlsx new file mode 100644 index 0000000000..1f77c7df5b Binary files /dev/null and b/ai/chatbot_result.xlsx differ diff --git a/ai/crawler/pdf_reader.py b/ai/crawler/pdf_reader.py index afbe18cb1c..9c5d3a7f7b 100644 --- a/ai/crawler/pdf_reader.py +++ b/ai/crawler/pdf_reader.py @@ -19,7 +19,8 @@ def read_pdf(self, filepath, path='./data/', name='default'): pages = loader.load() print('-- start --') total_pdf = [] - for page_no in tqdm(range(10)): + print(type(pages[0])) + for page_no in tqdm(range(len(pages))): doc = pages[page_no] doc.page_content = doc.page_content.replace(u"\xa0", u" ") doc.page_content = doc.page_content.replace("ยท", "") @@ -27,4 +28,6 @@ def read_pdf(self, filepath, path='./data/', name='default'): total_pdf.append(doc) with open(path+name+'.pkl', 'wb') as f: pickle.dump(total_pdf, f) - print(total_pdf) \ No newline at end of file + +tmp = PdfReader() +tmp.read_pdf('./2023+๊ตญ๋ฏผ๋Œ€ํ•™๊ต+์š”๋žŒ.pdf') \ No newline at end of file diff --git a/ai/crawler/test_by_sampling.py b/ai/crawler/test_by_sampling.py index 2bef6138bf..feccb148cd 100644 --- a/ai/crawler/test_by_sampling.py +++ b/ai/crawler/test_by_sampling.py @@ -10,7 +10,7 @@ os.chdir(parent_dir) # ํŒŒ์ผ์—์„œ ๊ฐ์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค. -with open('./data/SW/sw_notice.pkl', 'rb') as f: +with open('./data/NAVER/kookhee_kmu_0_3.pkl', 'rb') as f: loaded_data1 = pickle.load(f, encoding='utf-8') # ๋ถˆ๋Ÿฌ์˜จ ๊ฐ์ฒด๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. @@ -18,9 +18,9 @@ # print(type(loaded_data2[0])) #print(loaded_data1) -text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50) +text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) -splits1 = text_splitter.split_documents(loaded_data1)[5] +splits1 = text_splitter.split_documents(loaded_data1) print(splits1) print('++++++++++++++++++++++++++++') #print(splits2) diff --git a/ai/database.py b/ai/database.py new file mode 100644 index 0000000000..79a084e4a9 --- /dev/null +++ b/ai/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import * +from sqlalchemy.orm import sessionmaker +import os +from dotenv import load_dotenv + +load_dotenv() + +DB_URL = f'mysql+pymysql://{os.getenv("MYSQL_USERNAME")}:{os.getenv("MYSQL_PASSWORD")}@{os.getenv("DB_ENDPOINT")}:{os.getenv("DB_PORT")}/{os.getenv("DB_NAME")}' + +class Engineconn: + + def __init__(self): + self.engine = create_engine(DB_URL, pool_recycle = 500) + + def sessionmaker(self): + Session = sessionmaker(bind=self.engine) + session = Session() + return session + + def connection(self): + conn = self.engine.connect() + return conn + diff --git a/ai/infinite_try_crawling.py b/ai/infinite_try_crawling.py deleted file mode 100644 index 9b657a8119..0000000000 --- a/ai/infinite_try_crawling.py +++ /dev/null @@ -1,22 +0,0 @@ -import subprocess -import time - -def run_infinite(file_path): - while True: - print(f"Executing {file_path}...") - try: - subprocess.run(["python", file_path]) - print(f"{file_path} execution completed. Restarting...") - time.sleep(1) # ์‹คํ–‰ ํ›„ 1์ดˆ ๋Œ€๊ธฐ - except: - print('error') - - with open('./crawler/data/Notice/notice_count.txt') as r: - data = int(r.readline()) - if data == 195: - break - - -if __name__ == "__main__": - python_file_path = "run_crawler.py" # ์‹คํ–‰ํ•  ํŒŒ์ด์ฌ ํŒŒ์ผ ๊ฒฝ๋กœ - run_infinite(python_file_path) diff --git a/ai/llm/__pycache__/llm_rag.cpython-312.pyc b/ai/llm/__pycache__/llm_rag.cpython-312.pyc deleted file mode 100644 index ae6e9159b6..0000000000 Binary files a/ai/llm/__pycache__/llm_rag.cpython-312.pyc and /dev/null differ diff --git a/ai/llm/__pycache__/llm_rag.cpython-39.pyc b/ai/llm/__pycache__/llm_rag.cpython-39.pyc deleted file mode 100644 index 9eda71304d..0000000000 Binary files a/ai/llm/__pycache__/llm_rag.cpython-39.pyc and /dev/null differ diff --git a/ai/llm/__pycache__/prompt_template.cpython-39.pyc b/ai/llm/__pycache__/prompt_template.cpython-39.pyc deleted file mode 100644 index 6b5c183cc8..0000000000 Binary files a/ai/llm/__pycache__/prompt_template.cpython-39.pyc and /dev/null differ diff --git a/ai/llm/llm_rag.py b/ai/llm/llm_rag.py index 864e537f7a..87f2b020a0 100644 --- a/ai/llm/llm_rag.py +++ b/ai/llm/llm_rag.py @@ -3,8 +3,13 @@ from langchain_core.runnables import RunnablePassthrough, RunnableLambda from langchain_openai import ChatOpenAI from tavily import TavilyClient -from llm.prompt import casual_prompt, is_qna_prompt, combine_result_prompt, score_prompt, translate_prompt +from llm.prompt import casual_prompt, is_qna_prompt, score_prompt, rag_prompt +from llm.papago import Papago from langchain.retrievers.multi_query import MultiQueryRetriever +from langchain.retrievers import EnsembleRetriever +from langchain.memory import ChatMessageHistory +from langchain_core.runnables.history import RunnableWithMessageHistory +from langchain_core.chat_history import BaseChatMessageHistory import os class LLM_RAG: @@ -13,17 +18,17 @@ def __init__(self, trace = False): self.question = '' self.llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) self.tavily = TavilyClient(os.getenv('TAVILY_API_KEY')) - self.rag_prompt = hub.pull("rlm/rag-prompt") + self.rag_prompt = rag_prompt() self.casual_prompt = casual_prompt() self.is_qna_prompt = is_qna_prompt() - self.combine_result_prompt = combine_result_prompt() self.score_prompt = score_prompt() - self.translate_prompt = translate_prompt() + self.papago = Papago() + self.ko_query = None + self.result_lang = None self.notice_retriever = None self.school_retriever = None - self.notice_multiquery_retriever = None - self.school_multiquery_retriever = None - + self.naver_retriever = None + self.ensemble_retriever = None if trace: self.langsmith_trace() @@ -37,15 +42,22 @@ def langsmith_trace(self): def set_retriver(self, data_type, retriever): if data_type == 'notice': self.notice_retriever = retriever - self.notice_multiquery_retriever = MultiQueryRetriever.from_llm(retriever=retriever, llm=self.llm) elif data_type == 'school_info': self.school_retriever = retriever - self.school_multiquery_retriever = MultiQueryRetriever.from_llm(retriever=retriever, llm=self.llm) + + elif data_type == 'naver': + self.naver_retriever = retriever + else: print('Choose valid type!') def set_chain(self): + + self.ensemble_retriever = EnsembleRetriever( + retrievers= [self.notice_retriever, self.school_retriever, self.naver_retriever], + weights=[0.2, 0.3, 0.5]) + self.qna_router = ( self.is_qna_prompt | self.llm @@ -63,26 +75,12 @@ def set_chain(self): | StrOutputParser() ) - self.notice_rag_chain = ( - {"context": self.notice_multiquery_retriever | self.format_docs, "question": RunnablePassthrough()} + self.rag_chain = ( + {"context": self.ensemble_retriever | self.format_docs, "question": RunnablePassthrough()} | self.rag_prompt | self.llm | StrOutputParser() ) - - self.schl_info_rag_chain = ( - {"context": self.school_multiquery_retriever | self.format_docs, "question": RunnablePassthrough()} - | self.rag_prompt - | self.llm - | StrOutputParser() - ) - - self.rag_combine_chain = ( - {"notice_info": self.notice_rag_chain, "school_info": self.schl_info_rag_chain, "question": RunnablePassthrough()} - | self.combine_result_prompt - | self.llm - | StrOutputParser() - ) self.score_chain = ( self.score_prompt @@ -94,41 +92,39 @@ def set_chain(self): self.score_route ) - self.translate_chain = ( - self.translate_prompt - | self.llm - | StrOutputParser() - ) + def qna_route(self, info): + if "casual" in info["topic"].lower(): + self.result = self.casual_answer_chain.invoke(self.ko_query) + self.result = self.papago.translate_text(self.result, target_lang=self.result_lang) - def qna_route(self, info): - if "question" in info["topic"].lower(): - - self.result = self.rag_combine_chain.invoke(info["question"]) - score = self.score_chain.invoke({"question" : self.question, "answer": self.result}) - self.score_invoke_chain.invoke({"score" : score, "question": self.question}) - - elif "casual" in info["topic"].lower(): - self.result = self.casual_answer_chain.invoke(info['question']) - - else: - self.result = self.rag_combine_chain.invoke(info["question"]) + else: #if "question" in info["topic"].lower(): + self.result = self.rag_chain.invoke(self.ko_query) + score = self.score_chain.invoke({"question" : self.ko_query, "answer": self.result}) + self.score_invoke_chain.invoke({"score" : score, "question": self.ko_query}) def score_route(self, info): if "good" in info["score"].lower(): - return self.result + self.result = self.papago.translate_text(self.result, target_lang=self.result_lang) else: - print('-- google search --') - content = self.tavily.qna_search(query='๊ตญ๋ฏผ๋Œ€ํ•™๊ต ' + self.question) - self.result = "๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์—†์–ด์„œ ๊ตฌ๊ธ€์— ๊ฒ€์ƒ‰ํ–ˆ์Šต๋‹ˆ๋‹ค.\n\n" - self.result += self.translate_chain.invoke({'content' : content, 'question':self.question}) - return self.result + #print('-- google search --') + content = self.tavily.qna_search(query='๊ตญ๋ฏผ๋Œ€ํ•™๊ต ' + self.ko_query) + content = self.papago.translate_text(content, target_lang=self.result_lang) + base = "I couldn't find the answer, so I searched on Google.\n\n" + base = self.papago.translate_text(base, target_lang=self.result_lang) + self.result = base + content + + # def format_docs(self, docs): + # # ๊ฒ€์ƒ‰ํ•œ ๋ฌธ์„œ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ๋‹จ์œผ๋กœ ํ•ฉ์ณ์ค๋‹ˆ๋‹ค. + # return "\n\n".join(doc.page_content + '\nmetadata=' + str(doc.metadata) for doc in docs) def format_docs(self, docs): # ๊ฒ€์ƒ‰ํ•œ ๋ฌธ์„œ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ๋‹จ์œผ๋กœ ํ•ฉ์ณ์ค๋‹ˆ๋‹ค. - return "\n\n".join(doc.page_content + '\nmetadata=' + str(doc.metadata) for doc in docs) + return "\n\n".join(doc.page_content for doc in docs) - def query(self, question): + def query(self, question, result_lang): self.question = question + self.ko_query = self.papago.translate_text(self.question, target_lang='ko') + self.result_lang = result_lang self.qna_route_chain.invoke(question) - return self.result + return self.result \ No newline at end of file diff --git a/ai/llm/papago.py b/ai/llm/papago.py new file mode 100644 index 0000000000..61a2b39a45 --- /dev/null +++ b/ai/llm/papago.py @@ -0,0 +1,55 @@ +import urllib.request +from dotenv import load_dotenv +import json +import os + +load_dotenv() + +class Papago: + def __init__(self) -> None: + self.client_id = os.getenv("PAPAGO_ID") + self.client_secret = os.getenv("PAPAGO_API_KEY") + + def translate_text(self, text, target_lang='ko'): + encText = urllib.parse.quote(text) + source = self.detection_lang(text) + if source == target_lang: + return text + target = target_lang + data = f"source={source}&target={target}&text=" + encText + url = "https://naveropenapi.apigw.ntruss.com/nmt/v1/translation" + request = urllib.request.Request(url) + request.add_header("X-NCP-APIGW-API-KEY-ID",self.client_id) + request.add_header("X-NCP-APIGW-API-KEY",self.client_secret) + response = urllib.request.urlopen(request, data=data.encode("utf-8")) + rescode = response.getcode() + if(rescode==200): + response_body = response.read() + json_str = response_body.decode('utf-8') + dict_obj = json.loads(json_str) + return dict_obj['message']['result']['translatedText'] + else: + print("Error Code:" + rescode) + return + + def detection_lang(self, text): + encQuery = urllib.parse.quote(text) + data = "query=" + encQuery + url = "https://naveropenapi.apigw.ntruss.com/langs/v1/dect" + request = urllib.request.Request(url) + request.add_header("X-NCP-APIGW-API-KEY-ID",self.client_id) + request.add_header("X-NCP-APIGW-API-KEY",self.client_secret) + response = urllib.request.urlopen(request, data=data.encode("utf-8")) + rescode = response.getcode() + if(rescode==200): + response_body = response.read() + json_str = response_body.decode('utf-8') + dict_obj = json.loads(json_str) + return dict_obj['langCode'] + else: + print("Error Code:" + rescode) + +if __name__ == '__main__': + pg = Papago() + txt = pg.translate_text('์•ˆ๋…•') + print(txt) \ No newline at end of file diff --git a/ai/llm/prompt.py b/ai/llm/prompt.py index 725da7cb6a..6b323ddfa5 100644 --- a/ai/llm/prompt.py +++ b/ai/llm/prompt.py @@ -1,17 +1,20 @@ from langchain import PromptTemplate +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder def casual_prompt(): - prompt = PromptTemplate.from_template( - """ - question: {question} - answer:""" + system_prompt = 'You are โ€˜KUKUโ€™, a friendly chatbot in charge of counseling for Kookmin University students.' + prompt = ChatPromptTemplate.from_messages( + [ + ("system", system_prompt), + ("human", "{question}"), + ] ) return prompt def is_qna_prompt(): prompt = PromptTemplate.from_template( - """Given the user input below, classify it as either being about `question`, `casual` or 'other'. - Do not respond with more than one word. + """Given the user input below, classify it as either being about `question`, `casual`. + Input that asks for information is always classified as a 'question'. On the other hand, inputs that have casual conversations or ask about you should be classified as casual. {input} @@ -20,32 +23,32 @@ def is_qna_prompt(): ) return prompt -def combine_result_prompt(): - prompt = PromptTemplate.from_template(""" - You are an assistant for question-answering tasks. Use the following informations of 'notice_info' and 'school_info' to answer the question. If you don't know the answer, just say that you don't know. Include as much of the provided information as possible. - Question: {question} - Notice_info: {notice_info} - School_info: {school_info} - Answer:""" - ) - return prompt - def score_prompt(): prompt = PromptTemplate.from_template(""" - I am a student at Kookmin University. Given a question-answer pair, you need to respond whether the answer to the question is "good" or "bad". Also, even if you cannot answer, you must respond as 'bad'. Do not respond with more than one word. +You are a student at Kookmin University. Given a question-answer pair, you need to respond whether the answer to the question is "good" or "bad". +If the answer matches the question, answer 'good' if possible. If the answer to the question is not appropriate at all, respond 'bad'. Otherwise respond 'good'. +Also, even if there is a context in the answer that says 'I don't know', respond 'bad'. Do not respond with more than one word. - question: {question} - answer: {answer} +question: {question} +answer: {answer} - Classification:""" +Classification:""" ) return prompt -def translate_prompt(): - prompt = PromptTemplate.from_template(""" - You are a translator with vast knowledge of human languages. Translate the content into the language corresponding to the question. You should only translate and never answer questions. - - question : {question} - content : {content} - result :""") - return prompt \ No newline at end of file + +def rag_prompt(): + prompt = PromptTemplate.from_template( +''' +You are an assistant 'KUKU' for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. + +REMEMBER : your name is 'KUKU'. + +Question: {question} + +Context: {context} + +Answer: +''') + + return prompt diff --git a/ai/models.py b/ai/models.py new file mode 100644 index 0000000000..828f8d193e --- /dev/null +++ b/ai/models.py @@ -0,0 +1,33 @@ +from sqlalchemy import Column, Integer, String, ForeignKey, DateTime +from sqlalchemy.orm import relationship +from sqlalchemy.ext.declarative import declarative_base +import pytz +from datetime import datetime +from database import Engineconn + +Base = declarative_base() +engine = Engineconn() + +class Announcement(Base): + __tablename__ = 'announcements' + + id = Column(Integer, name="announcement_id", primary_key=True) + type = Column(String, nullable=False) + title = Column(String, nullable=False) + writtenDate = Column(DateTime, name="written_date", default=datetime.now(pytz.timezone('Asia/Seoul'))) # ์„œ์šธ ์‹œ๊ฐ„๋Œ€๋กœ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • + department = Column(String) + author = Column(String) + authorPhone = Column(String, name="author_phone") + document = Column(String) + language = Column(String) + url = Column(String) + files = relationship('AnnouncementFile', back_populates='announcement') + +class AnnouncementFile(Base): + __tablename__ = 'announcement_files' + + id = Column(Integer, primary_key=True) + link = Column(String) + title = Column(String) + announcement_id = Column(Integer, ForeignKey('announcements.announcement_id')) + announcement = relationship('Announcement', back_populates='files') \ No newline at end of file diff --git a/ai/question_list.md b/ai/question_list.md new file mode 100644 index 0000000000..abc14ce96b --- /dev/null +++ b/ai/question_list.md @@ -0,0 +1,115 @@ +- ๋ถ์•…๊ด€ ์œ„์น˜ ์•Œ๋ ค์ค˜ +- ์†Œ์œต๋Œ€ ๋ฐ•ํ•˜๋ช… ๊ต์ˆ˜๋‹˜ ์—ฐ๋ฝ์ฒ˜ ์•Œ๋ ค์ค˜ +- ๋ถ์•…๊ด€ ์„œ๋ธŒ์›จ์ด ๋ฉ”๋‰ด์—” ๋ญ๊ฐ€ ์žˆ์–ด? +- ์—ฐ์œ ๋ฐ”๊ฒŒํŠธ๋Š” ์–ด๋””์„œ ์‚ฌ? +- ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์—์„œ ํ˜„์žฅ์‹ค์Šต์ด๋‚˜ ์ธํ„ด์„ ์ง€์›ํ•ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์žˆ์„๊นŒ? ๊ฐœ๋ฐœ์ž ๊ด€๋ จ์œผ๋กœ ์ง‘์ค‘์ ์œผ๋กœ ์•Œ๋ ค์ค˜ +- ์•ŒํŒŒํ”„๋กœ์ ํŠธ๋กœ ํƒ€์ง€์—ญ ํ•™ํšŒ๋ฅผ ๊ฐ€๊ณ ์‹ถ์€๋ฐ ์ง€์›์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์„๊นŒ? +- ๊ต๋‚ด ์‹๋‹น์— ๋Œ€ํ•ด ์•Œ๋ ค์ค˜ +- ON๊ตญ๋ฏผ์‹œ์Šคํ…œ์€ ์–ด๋–ค ์‹œ์Šคํ…œ์ธ๊ฐ€์š”? +- ํ†ตํ•ฉ์•„์ด๋””๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”? +- ํ†ตํ•ฉ์•„์ด๋””๋ฅผ ์–ด๋–ป๊ฒŒ ๋งŒ๋“œ๋‚˜์š”? +- ํ†ตํ•ฉ์•„์ด๋””๋ฅผ ํ•™๋ฒˆ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‚˜์š”? +- ํ†ตํ•ฉ์•„์ด๋””๋Š” ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•œ๊ฐ€์š”? +- ON๊ตญ๋ฏผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ์ค‘ ๋ฌธ์˜๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”? +- ON๊ตญ๋ฏผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ์ค‘ ์˜ค๋ฅ˜์‚ฌํ•ญ์€ ์–ด๋””์— ์‹ ๊ณ ํ•˜๋‚˜์š”? +- 1์ „๊ณต ์กธ์—…์š”๊ฑด(์กธ์—…ํ•„์ˆ˜๊ณผ๋ชฉ)์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. +- ์กธ์—…ํ•˜๋ ค๋ฉด ๋ช‡ ํ•™์ ์„ ๋“ค์–ด์•ผ ํ•˜๋‚˜์š”? +- ๊ณ„์ ˆํ•™๊ธฐ ์ˆ˜๊ฐ•์‹ ์ฒญ์€ ์–ธ์ œ ํ•˜๋‚˜์š”? +- ์‹ ์ž…์ƒ์€ ์–ธ์ œ ํœดํ•™์‹ ์ฒญ์ด ๊ฐ€๋Šฅํ•œ๊ฐ€์š”? +- ํœดํ•™ํ•˜๊ณ  ์‹ถ์€๋ฐ ๋“ฑ๋ก๊ธˆ ํ™˜๋ถˆ์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ๋Œ€ํ•™ ์ง์žฅ ์˜ˆ๋น„๊ตฐ ํŽธ์„ฑ ๋Œ€์ƒ์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. +- ์žฅํ•™๊ธˆ ์ข…๋ฅ˜์™€ ๊ทธ ๊ธˆ์•ก์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ตํ™˜ํ•™์ƒ ํ”„๋กœ๊ทธ๋žจ์€ ์–ด๋–ค๊ฑด๊ฐ€์š”? +- ์—ฐ์œ ๋ฐ”๊ฒŒํŠธ๋Š” ์–ด๋””์„œ ์‚ด ์ˆ˜ ์žˆ์–ด? +- ๋ฏธ๋ž˜๊ด€ ์œ„์น˜ ์•Œ๋ ค์ค˜ +- ๊ต์–‘๊ณผ๋ชฉ ์ถ”์ฒœํ•ด์ค„ ์ˆ˜ ์žˆ์–ด? +- ๊ต๋‚ด ์นดํŽ˜์™€ ํŽธ์˜์  ์œ„์น˜๋Š” ์–ด๋””์ธ๊ฐ€์š”? +- ๋„์„œ๊ด€ ์šด์˜ ์‹œ๊ฐ„์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ์ฃผ์ฐจ๊ถŒ ์‹ ์ฒญ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ๋ ค์ค˜ +- ํ•™๊ต ์ฃผ๋ณ€ ๋ง›์ง‘์„ ์ถ”์ฒœํ•ด ์ฃผ์„ธ์š”. +- ํ•™๊ต ๋‚ด ์‹œ์„ค ์˜ˆ์•ฝ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ๊ธฐ์ˆ™์‚ฌ ์‹ ์ฒญ ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ธ๊ฐ€์š”? +- ๋™์•„๋ฆฌ ๊ฐ€์ž… ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ํœดํ•™ ์‹ ์ฒญ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ์„ฑ์  ์กฐํšŒ๋Š” ์–ด๋””์„œ ํ•˜๋‚˜์š”? +- ์ƒ๋‹ด ์„ผํ„ฐ ์ด์šฉ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ํ•™๋‚ด ๋™์•„๋ฆฌ ๋ชฉ๋ก์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ณณ์€ ์–ด๋””์ธ๊ฐ€์š”? +- ๋ณตํ•™ ์ ˆ์ฐจ๋Š” ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ์ „๊ณต ๋ณ€๊ฒฝ ์ ˆ์ฐจ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”? +- ๊ต๋‚ด ์™€์ดํŒŒ์ด ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๋ถ„์‹ค๋ฌผ ๋ณด๊ด€์†Œ ์œ„์น˜์™€ ์šด์˜ ์‹œ๊ฐ„์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ํ•ด์™ธ ๋Œ€ํ•™๊ณผ์˜ ํ˜‘์ • ํ˜„ํ™ฉ์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ๊ต๋‚ด ์€ํ–‰๊ณผ ATM ์œ„์น˜๋ฅผ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ์บ ํผ์Šค ๋‚ด ํ—ฌ์Šค์žฅ ์ด์šฉ ์š”๊ธˆ์€ ์–ผ๋งˆ์ธ๊ฐ€์š”? +- ๋ณต์‚ฌ๊ธฐ์™€ ํ”„๋ฆฐํ„ฐ๋Š” ์–ด๋””์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”? +- ๊ต๋‚ด ์นดํŽ˜์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”๋‰ด๋ฅผ ์•Œ๊ณ  ์‹ถ์–ด์š”. +- ์ฃผ๋ง์—๋„ ์šด์˜ํ•˜๋Š” ์‹๋‹น์ด ์žˆ๋‚˜์š”? +- ๊ธฐ์ˆ™์‚ฌ ๋‚ด ๊ทœ์ •๊ณผ ๊ทœ์น™์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ํ•™๋น„ ๋‚ฉ๋ถ€ ๊ธฐํ•œ๊ณผ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์„ธ์š”. +- ๊ต๋‚ด ๊ทผ๋กœ ์žฅํ•™์ƒ์œผ๋กœ ์ผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์‹ถ์–ด์š”. +- ํ•™์ž๊ธˆ ๋Œ€์ถœ ์‹ ์ฒญ ์ ˆ์ฐจ๋Š” ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ์ปค๋ฆฌ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ๊ทธ๋žจ์€ ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๋‚˜์š”? +- ๊ทธ๋ฃน ์Šคํ„ฐ๋””๋ฃธ ์˜ˆ์•ฝ ๋ฐฉ๋ฒ•์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ์กฐ๊ธฐ ์กธ์—… ์‹ ์ฒญ ์กฐ๊ฑด๊ณผ ์ ˆ์ฐจ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”? +- ๊ธฐ์ดˆ ๊ต์–‘ ํ•„์ˆ˜ ๊ณผ๋ชฉ ๋ชฉ๋ก์€ ์–ด๋””์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‚˜์š”? +- ์•ŒํŒŒ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”? +- ์•ŒํŒŒ ํ”„๋กœ์ ํŠธ๋Š” ์–ด๋–ป๊ฒŒ ์‹ ์ฒญํ•  ์ˆ˜ ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ๋ฐ›์€ ๋ ˆ๋…ธ๋ฒ„ ๋…ธํŠธ๋ถ์ด ๊ณ ์žฅ๋‚ฌ๋Š”๋ฐ ์–ด๋–กํ•˜๋‚˜์š”? +- ์†Œ์œต๋Œ€ 3ํ•™๋…„ ๊ณผ๋ชฉ์—๋Š” ์–ด๋–ค๊ฒŒ ์žˆ๋‚˜์š”? +- ์ž์œจ์ฃผํ–‰์ŠคํŠœ๋””์˜ค ์šด์˜์‹œ๊ฐ„์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? +- ์ด์žฌ๊ตฌ ๊ต์ˆ˜๋‹˜ ์—ฐ๋ฝ์ฒ˜๋ฅผ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊น€์žฅํ˜ธ ๊ต์ˆ˜๋‹˜์— ๋Œ€ํ•ด ์•Œ๊ณ  ์žˆ๋‚˜์š”? +- ํ•™๊ต ๋นต์ง‘์—๋Š” ์–ด๋–ค ๋ฉ”๋‰ด๊ฐ€ ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ๊ฝƒ์„ ์‚ด ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์žˆ๋‚˜์š”? +- ์™ธ๊ตญ์ธ ํ•™์ƒ๊ณผ ๊ต๋ฅ˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ๊ฐ€ ์žˆ๋‚˜์š”? +- ์ „ ์™ธ๊ตญ์ธ ํ•™์ƒ์ธ๋ฐ, ์ทจ์—… ์ƒ๋‹ด์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์žˆ์„๊นŒ์š”? +- K-bop์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ํ•™๊ต ์„œ์ ์€ ์–ด๋””์— ์žˆ๋‚˜์š”? +- ๊ณตํ•™๊ด€์€ ์–ด๋””์— ์žˆ๋‚˜์š”? +- ๊ณผํ•™๊ด€์€ ์–ด๋””์— ์žˆ๋‚˜์š”? +- ๊ณตํ•™๊ด€ 1์ธต์—๋Š” ๋ญ๊ฐ€ ์žˆ๋‚˜์š”? +- ๊ตญ๋ฏผ๋Œ€ํ•™๊ต ๋นต์ง‘์˜ ์ด๋ฆ„์€ ๋ฌด์—‡์ธ๊ฐ€์š”? +- ์ฒญํ–ฅ์€ ์™œ ๋‹ค๋ฅธ ์‹๋‹น๋ณด๋‹ค ๋ฉ”๋‰ด ๊ฐ€๊ฒฉ์ด ๋น„์‹ผ๊ฐ€์š”? +- ๊ณ„์•ฝ์ง์›์ด ๋ฌด์—‡์ธ๊ฐ€์š”? +- ์™ธ๊ตญ์ธ ํ•™์ƒ๊ณผ ๊ต๋ฅ˜ํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ์™ธ๊ตญ์ธ์„ ์œ„ํ•œ ํ—ฌํผ ์‹œ์Šคํ…œ์ด ์žˆ๋‚˜์š”? +- ํ•œํ•™๊ธฐ๋‹น ์ตœ๋Œ€ ๋ช‡ ํ•™์ ๊นŒ์ง€ ์ด์ˆ˜ํ•  ์ˆ˜ ์žˆ๋‚˜์š”? +- ๋ฐฉํ•™์—๋„ ํ•™์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‚˜์š”? +- ๊ตญ๋ฏผ๋Œ€ ๊ณ ์–‘์ด๋ฅผ ์–ด๋””์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋‚˜์š”? +- ๊ตญ๋ฏผ๋Œ€ ๊ณ ์–‘์ด๋Š” ๋ช‡ ๋งˆ๋ฆฌ๊ฐ€ ์žˆ๋‚˜์š”? +- ์™ธ๊ตญ์ธ์„ ์œ„ํ•œ ๋™์•„๋ฆฌ๊ฐ€ ์žˆ๋‚˜์š”? +- ํŠน์ • ๊ณผ๋ชฉ์˜ ๊ต์ˆ˜๋‹˜ ์—ฐ๋ฝ์ฒ˜๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‚˜์š”? +- ๊ต์ˆ˜๋‹˜๊ป˜ ์„ฑ์  ์ด์˜ ์‹ ์ฒญ์„ ํ•˜๊ณ  ์‹ถ์–ด์š” +- ๋†๊ตฌ๊ณต์€ ์–ด๋””์„œ ๋นŒ๋ฆด ์ˆ˜ ์žˆ๋‚˜์š”? +- ์ผ๋ฐ˜ ํ•™์ƒ๋„ ํƒ๊ตฌ์žฅ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”? +- ์ฃผ์š”ํ•œ ๊ต๋‚ด ๊ทœ์น™์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ์˜๋ฌด์‹ค์€ ์–ด๋””์— ์žˆ๋‚˜์š”? +- ๋ถ์•…๊ด€๊นŒ์ง€ ๊ฐ€๋Š” ์ง€๋ฆ„๊ธธ์ด ์žˆ์„๊นŒ์š”? +- ์‚ฌ๊ณ ๊ฐ€ ๋‚˜์„œ ๊ฒฐ์„์„ ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ๊ต์ˆ˜๋‹˜๊ป˜ ์ถœ์„ ์ธ์ •์„ ๋ฐ›๊ณ  ์‹ถ์–ด์š”. ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋ ๊นŒ์š”? +- ์šฉ๋‘๋ฆฌ์—์„œ ์ˆ˜์˜ํ•ด๋„ ๋ ๊นŒ์š”? ์ง„์ง€ํ•ฉ๋‹ˆ๋‹ค +- ์šฉ๋‘๋ฆฌ์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ตญ๋ฏผ๋Œ€์—์„œ ์‚ฌ์ง„ ์ฐ๊ธฐ ์ข‹์€ ๊ณณ์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ตญ๋ฏผ๋Œ€ ์‚ฐ์ฑ… ์ฝ”์Šค๋ฅผ ์ถ”์ฒœํ•ด์ฃผ์„ธ์š” +- ๊ตญ๋ฏผ๋Œ€ ์ถ•์ œ์— ์ดˆ์ฒญ๋œ ์—ฐ์˜ˆ์ธ์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ตญ๋ฏผ๋Œ€์— ์ธ์ƒ๋„ค์ปท์ด ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ์‰ด ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ์ž˜ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ๋– ๋“ค๋ฉด์„œ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์„ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๋„์„œ๊ด€ ์ง€ํ•˜ 1์ธต์€ ๋ญํ•˜๋Š” ๊ณณ์ธ๊ฐ€์š”? +- ์บ๋…ผ๋ณผ ๋™์•„๋ฆฌ์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ต๋‚ด์— ์žˆ๋Š” ์žํŒ๊ธฐ ์ข…๋ฅ˜์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ต๋‚ด์—์„œ ํฐ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์žˆ๋‚˜์š”? +- ๊ต๋‚ด์— ์ „์ž๋ ˆ์ธ์ง€๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์žˆ๋‚˜์š”? +- ๊ต๋‚ด์—์„œ ์ปดํ“จํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์žˆ๋‚˜์š”? +- ๊ตญ๋ฏผ๋Œ€์—์„œ ๊ฐ€์žฅ ๋œจ๊ฑฐ์› ๋˜ ๋…ผ๋ž€๊ฑฐ๋ฆฌ์— ๋Œ€ํ•ด ์•Œ๋ ค์ค˜์š” +- ๊น€๊ฑดํฌ์™€ ๊ตญ๋ฏผ๋Œ€๋Š” ๋ฌด์Šจ ๊ด€๊ณ„์ธ๊ฐ€์š”? +- ์ค‘์•™๋™์•„๋ฆฌ์— ๋Œ€ํ•ด์„œ ์•Œ๋ ค์ฃผ์„ธ์š” +- ๊ตญ๋ฏผ๋Œ€ํ•™๊ต ๊ฐœ๊ต๊ธฐ๋…์ผ์€ ์–ธ์ œ์ธ๊ฐ€์š”? +- ์ถ”์–ด์˜ค๋ผ๋Š” ๋™์•„๋ฆฌ์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ์„ธ์š” +- ํ•™๊ต์—์„œ ๋ถˆ์ด ๋‚˜๋ฉด ์–ด๋–ป๊ฒŒ ๋Œ€ํ”ผํ•˜๋‚˜์š”? +- ํ•™๊ต ๊ทผ์ฒ˜์— ํ”ผ์‹œ๋ฐฉ์ด ์žˆ๋‚˜์š”? +- ํ•™๊ต์—์„œ ํ†ต์žฅ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‚˜์š”? +- ๊ต๋‚ด ์ฃผ์ฐจ์žฅ ์ด์šฉ์š”๊ธˆ์— ๋Œ€ํ•ด ์•Œ๋ ค์ค˜ +- ํ† ์ต ์žฅํ•™๊ธˆ์— ๋Œ€ํ•ด ์•Œ๋ ค์ค˜ +- ํ•™๋ถ€์—ฐ๊ตฌ์ƒ์ด ๋˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ค˜ +- ๋Œ€ํ•™์›์— ์ง„ํ•™ํ•˜๊ณ  ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ค˜ diff --git a/ai/readme.md b/ai/readme.md deleted file mode 100644 index 56b10cfa93..0000000000 --- a/ai/readme.md +++ /dev/null @@ -1,27 +0,0 @@ -# Chat bot `KUKU` ์†Œ๊ฐœ - -๊ตญ๋ฏผ๋Œ€์— ๊ด€ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๋ฌผ์–ด๋ณด์„ธ์š” ! -๊ตญ๋ฏผ๋Œ€ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์ฑ—๋ด‡ <์ฟ ์ฟ >์ž…๋‹ˆ๋‹ค. - -# ํ•™์Šตํ•œ ๋ฐ์ดํ„ฐ - -- [๊ตญ๋ฏผ๋Œ€ 2023 ์š”๋žŒ PDF](https://www.kookmin.ac.kr/comm/cmfile/thumbnail2.do?encSvrFileNm=223d2bdfdbb4df30ad85271267bd6e6a0a913159736ab9843bb76fc00eeeb5ddb1e88f35002b9cf1b749bfe96b751f16b8be21ad5273d348a74b10a57513dd4540bbcb178d3151db4d507c693a1f7ef9&encFileGrpSeq=8e8e9041def64eb5f7f8c21154bcff06&encFileSeq=cf9f1626435aafc6e0e182b36c8e23d9) -- [2023~2024.03.28 ๊ตญ๋ฏผ๋Œ€ ์ „์ฒด ๊ณต์ง€์‚ฌํ•ญ](https://www.kookmin.ac.kr/user/kmuNews/notice/index.do) - -# ์‹คํ–‰ ๋ฐฉ๋ฒ• - - -2024_03_30 ์•„๋‚˜์ฝ˜๋‹ค ํ™˜๊ฒฝ python 3.8 ์—์„œ ์‹คํ–‰ ํ™•์ธ - -1. /ai ๊ฒฝ๋กœ๋กœ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์ด๋™ -2. ํŒจํ‚ค์ง€ ์„ค์น˜ `pip install -r requirements.txt` -3. /ai ๊ฒฝ๋กœ์— .env ํŒŒ์ผ ์ƒ์„ฑ (OPENAI_API_KEY = 'your_api') -4. `python run_chatbot.py` - -# TEST - -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/6b7c8514-35dd-41bb-9815-56a182a7ccfb) - -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/e37af8f8-a35b-4808-a357-aff5aebb8d88) - -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/d7b5cd40-43b7-4cca-8be8-6fff257ed303) diff --git a/ai/requirements.txt b/ai/requirements.txt index ca2b5f9a34..55f47821c1 100644 --- a/ai/requirements.txt +++ b/ai/requirements.txt @@ -6,4 +6,5 @@ langchainhub==0.1.15 beautifulsoup4==4.12.3 pypdf==4.1.0 python-dotenv==1.0.1 -langchain-text-splitters==0.0.1 \ No newline at end of file +langchain-text-splitters==0.0.1 +tavily-python \ No newline at end of file diff --git a/ai/run_chatbot.py b/ai/run_chatbot.py index d097a7d41e..68ac01588e 100644 --- a/ai/run_chatbot.py +++ b/ai/run_chatbot.py @@ -25,10 +25,13 @@ notice_vdb.load_local(vector_db_path + '/NOTICE') school_vdb = VectorDB() school_vdb.load_local(vector_db_path + '/SCHOOL_INFO') +naver_vdb = VectorDB() +naver_vdb.load_local(vector_db_path + '/NAVER') llm = LLM_RAG(trace=True) -llm.set_retriver(data_type='notice', retriever=notice_vdb.get_retriever()) -llm.set_retriver(data_type='school_info', retriever=school_vdb.get_retriever()) +llm.set_retriver(data_type='notice', retriever=notice_vdb.get_retriever(k=2)) +llm.set_retriver(data_type='school_info', retriever=school_vdb.get_retriever(k=3)) +llm.set_retriver(data_type='naver', retriever= naver_vdb.get_retriever(k=5)) llm.set_chain() print("AI: hello! if you want to stop, please enter '0'") @@ -40,5 +43,5 @@ if q == str(0): break print('AI : ', end='') - print(llm.query(q)) + print(llm.query(q, 'ko')) print() \ No newline at end of file diff --git a/ai/server.py b/ai/server.py index 4a8ea05504..e1ad0baf98 100644 --- a/ai/server.py +++ b/ai/server.py @@ -1,5 +1,6 @@ -from typing import Union -from fastapi import FastAPI, UploadFile +from typing import Union, Optional +from fastapi import FastAPI, UploadFile, BackgroundTasks, Request +from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager from pydantic import BaseModel @@ -8,15 +9,29 @@ from dotenv import load_dotenv import os import uvicorn +import datetime +from database import Engineconn +from models import Announcement +import sched +from apscheduler.schedulers.background import BackgroundScheduler +import logging +from langchain_core.documents.base import Document +import pickle + +sched = BackgroundScheduler(timezone='Asia/Seoul') class Query(BaseModel): query: str + target_lang: str + +class CustomException(Exception): + def __init__(self, name: str): + self.name = name @asynccontextmanager async def lifespan(app:FastAPI): global llm global vdb - print("a") current_directory = os.path.dirname(os.path.realpath(__file__)) os.chdir(current_directory) @@ -29,15 +44,23 @@ async def lifespan(app:FastAPI): notice_vdb.load_local(vector_db_path + '/NOTICE') school_vdb = VectorDB() school_vdb.load_local(vector_db_path + '/SCHOOL_INFO') + naver_vdb = VectorDB() + naver_vdb.load_local(vector_db_path + '/NAVER') llm = LLM_RAG(trace=True) llm.set_retriver(data_type='notice', retriever=notice_vdb.get_retriever()) llm.set_retriver(data_type='school_info', retriever=school_vdb.get_retriever()) + llm.set_retriver(data_type='naver', retriever=school_vdb.get_retriever()) llm.set_chain() + vdb = VectorDB() yield app = FastAPI(lifespan=lifespan) +engineconn = Engineconn() +engine = engineconn.engine +session = engineconn.sessionmaker() + origins = [ "http://localhost.tiangolo.com", "https://localhost.tiangolo.com", @@ -54,22 +77,46 @@ async def lifespan(app:FastAPI): allow_headers=["*"], ) +@sched.scheduled_job('cron', hour='0', minute='30', id='load announcement') +def log_new_announcements(): + # ํ•˜๋ฃจ ์ „์˜ ์‹œ๊ฐ„ ๊ณ„์‚ฐ + one_day_ago = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1) + # ํ•˜๋ฃจ ๋™์•ˆ ์ถ”๊ฐ€๋œ announcement ์กฐํšŒ + new_announcements = session.query(Announcement).filter(Announcement.writtenDate >= one_day_ago).all() + # ์กฐํšŒ๋œ announcement ๋กœ๊ทธ์— ๊ธฐ๋ก + docs = [] + for announcement in new_announcements: + doc = Document(announcement.document) + doc.metadata['title'] = announcement.title + doc.metadata['datetime'] = announcement.writtenDate + docs.append(doc) + with open(f'./data/{datetime.datetime.now(datetime.UTC).date()}.pkl', 'wb') as f: + pickle.dump(docs, f) + vdb.add_content(f'./data/{datetime.datetime.now(datetime.UTC).date()}.pkl', './FAISS/NOTICE') + @app.get("/") async def initiate(): + sched.start() return "์•ˆ๋…•ํ•˜์„ธ์š”! ๊ตญ๋ฏผ๋Œ€ํ•™๊ต ์ „์šฉ ์ฑ—๋ด‡ KUKU์ž…๋‹ˆ๋‹ค. ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์— ๋Œ€ํ•œ ๊ฑด ๋ชจ๋“  ์งˆ๋ฌธํ•ด์ฃผ์„ธ์š”!" -@app.post("/query") +@app.post("/api/chatbot") async def query(query: Query): - return {'code': '200', - 'message': 'success', - 'response': { - 'answer': llm.query(query.query) - }} - -@app.post("/input") -async def input(data: UploadFile): - vdb.add_content(data.file) - return + try: + ans = llm.query(query.query, query.target_lang) + except: + raise CustomException(name="chatbot error") + return JSONResponse(status_code=200, content={'success': True, + 'message': 'success', + 'response': { + 'answer': ans + }}) + + +@app.exception_handler(CustomException) +async def MyCustomExceptionHandler(request: Request, exception: CustomException): + return JSONResponse(status_code = 400, + content = {'success': False, + 'message': 'failed'}) if __name__ == "__main__": diff --git a/ai/vectordb/__pycache__/vector_db.cpython-39.pyc b/ai/vectordb/__pycache__/vector_db.cpython-39.pyc deleted file mode 100644 index 22bfb092bf..0000000000 Binary files a/ai/vectordb/__pycache__/vector_db.cpython-39.pyc and /dev/null differ diff --git a/ai/vectordb/vector_db.py b/ai/vectordb/vector_db.py index 8b004ff489..6d438fd142 100644 --- a/ai/vectordb/vector_db.py +++ b/ai/vectordb/vector_db.py @@ -16,7 +16,7 @@ def add_content(self, content, vector_db_path='./'): # url, txt, pdf, csv ๋˜๋Š” self.load_local(vector_db_path) - text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50) + text_splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=150) if content.startswith('https:'): loader = WebBaseLoader(content) elif content.endswith('.txt'): @@ -43,7 +43,7 @@ def add_content(self, content, vector_db_path='./'): # url, txt, pdf, csv ๋˜๋Š” return docs = loader.load() - text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50) + text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100) splits = text_splitter.split_documents(docs) FAISS.add_documents(self.vectorstore, splits) @@ -62,5 +62,5 @@ def load_local(self, path=''): def similarity_search(self, query, k=1): return self.vectorstore.similarity_search(query, k)[0].page_content - def get_retriever(self): - return self.vectorstore.as_retriever() \ No newline at end of file + def get_retriever(self, k=2): + return self.vectorstore.as_retriever(search_kwargs={"k": k}) diff --git a/back-chat/README.md b/back-chat/README.md deleted file mode 100644 index a4acb87819..0000000000 --- a/back-chat/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# README - -This README would normally document whatever steps are necessary to get the -application up and running. - -Things you may want to cover: - -* Ruby version - -* System dependencies - -* Configuration - -* Database creation - -* Database initialization - -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... diff --git a/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java b/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java index edf3001d4d..206f34a1dd 100644 --- a/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java +++ b/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java @@ -7,21 +7,13 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.nio.charset.StandardCharsets; - @Slf4j @Configuration public class GlobalLoggingFilter { - private final ObjectMapper objectMapper = new ObjectMapper(); - @Bean @Order(-1) public GlobalFilter preLoggingFilter() { @@ -35,37 +27,7 @@ public GlobalFilter preLoggingFilter() { log.info("Authorization: {}", request.getHeaders().get("Authorization")); } - if (request.getMethod().toString().equals("GET")) { - return chain.filter(exchange); - } - - return DataBufferUtils.join(request.getBody()) - .flatMap(dataBuffer -> { - byte[] bodyBytes = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(bodyBytes); - DataBufferUtils.release(dataBuffer); - String bodyString = new String(bodyBytes, StandardCharsets.UTF_8); - - String jsonBody; - try { - Object json = objectMapper.readValue(bodyString, Object.class); - jsonBody = objectMapper.writeValueAsString(json); - } catch (Exception e) { - jsonBody = bodyString; - } - - log.info("Request Body: {}", jsonBody); - - ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) { - @Override - public Flux getBody() { - DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bodyBytes); - return Flux.just(buffer); - } - }; - - return chain.filter(exchange.mutate().request(mutatedRequest).build()); - }); + return chain.filter(exchange); }; } diff --git a/back/BACK.md b/back/BACK.md new file mode 100644 index 0000000000..b2d522da3f --- /dev/null +++ b/back/BACK.md @@ -0,0 +1,379 @@ +# **๐Ÿ’พ Backend ๊ธฐ์ˆ  ๋ฌธ์„œ** + +## **๊ธฐ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ** + +
+ +### **์ฑ„ํŒ… ๊ตฌํ˜„** + +์ฑ„ํŒ… ์„œ๋ฒ„๋Š” ํฌ๊ฒŒ Polling, Long Polling, Steaming, Websocket ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ž์„  3๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ์ผ๋ฐ˜์ ์ธ RESTFUL API๋ฅผ ์ด์šฉํ•œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋น„๊ต์  ๊ตฌํ˜„์ด ์‰ฝ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋“ค์˜ ํŠน์„ฑ์ƒ ํด๋ผ์ด์–ธํŠธ โ†’ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์„œ๋ฒ„ โ†’ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋˜ํ•œ, 100% ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋‚˜์˜จ ๋ฐฉ๋ฒ•์ด ๋ฐ”๋กœ 4๋ฒˆ WebSocket ๋ฐฉ์‹์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ฑ„ํŒ…์„ websocket์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด, ๊ต‰์žฅํžˆ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์žก์•„๋จน๊ณ  ๊ตฌํ˜„์ด ๊นŒ๋‹ค๋กญ๋‹ค. + + + +๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Long Polling์„ ์ด์šฉํ•˜์—ฌ ์ฑ„ํŒ…์„ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ฑ„ํŒ…์€ 100% ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•  ํ•„์š” ์—†์„ ๋ฟ๋”๋Ÿฌ, ์ฑ„ํŒ…์ด ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๊ณ , ํ”„๋กœ์ ํŠธ ๋งˆ๊ฐ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•ด์•ผ๋˜๊ธฐ ๋•Œ๋ฌธ์— Long Polling์„ ์ด์šฉํ•˜์—ฌ ์ฑ„ํŒ…์„ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. + +์ฑ„ํŒ… ์‹œ์Šคํ…œ ์„ค๊ณ„ ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๊ด€ํ•œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ [์ฑ„ํŒ… ๊ตฌํ˜„์— ๋Œ€ํ•œ ๊ณ ์ฐฐ](https://mclub4.tistory.com/31) ํ•„์ž์˜ ๋ธ”๋กœ๊ทธ์— ์„œ์ˆ ํ•ด๋†“์•˜๋‹ค. + +
+ +### **ํฌ๋กค๋ง** + +ํ˜„์žฌ ํฌ๋กค๋ง์€ ํ•™์‹๊ณผ ๊ณต์ง€์‚ฌํ•ญ ๋ถ„์•ผ์—์„œ ์‹ค์‹œ๋˜๊ณ  ์žˆ๋‹ค. ํฌ๋กค๋ง ํ•  ๋•Œ๋„ ๋ช‡๊ฐ€์ง€ ๊ณ ๋ คํ•  ์‚ฌํ•ญ์ด ์กด์žฌํ•œ๋‹ค. + +์ฒซ๋ฒˆ์งธ, ์ •์  ํฌ๋กค๋ง๊ณผ ๋™์  ํฌ๋กค๋ง ์–ด๋–ค ๊ฒƒ์ด ํ•„์š”ํ•œ๊ฐ€ ๊ณ ๋ คํ•ด๋ด์•ผ ํ•œ๋‹ค. ๊ณต์ง€์‚ฌํ•ญ๊ณผ ํ•™์‹์€ ๊ตณ์ด ๋กœ๊ทธ์ธ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตณ์ด ๋™์  ํฌ๋กค๋ง์ด ํ•„์š”ํ•˜์ง€ ์•Š์•˜๋‹ค. ๋˜ํ•œ, ๋™์  ํฌ๋กค๋ง์€ Selenium ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๊ณ , Chrome Driver๋ฅผ ๊นŒ๋Š” ๋“ฑ ์ถ”๊ฐ€์ ์ธ ์„ค์ •์ด ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค. ๋”ฐ๋ผ์„œ, ์‚ฌ์šฉ์ด ๊ฐ„๋‹จํ•œ Jsoup์„ ์ด์šฉํ•ด์„œ ์ •์  ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. + +๋‘๋ฒˆ์งธ, ํฌ๋กค๋ง์€ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ์™€ ํ†ต์‹ ํ•˜์—ฌ ์ด๋ฃจ์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ ๋„คํŠธ์›Œํฌ I/O ๋ณ‘๋ชฉ์ง€์ ์„ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊ฒƒ์ด๊ณ  ์ด ๋•Œ ์ž์›์„ ์ตœ๋Œ€ํ•œ ์•„๋‚„ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ณ ๋ คํ•ด๋ด์•ผ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, ํฌ๋กค๋ง์€ Async๋ฅผ ์ด์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋„๋ก ํ•˜์—ฌ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ค„์ด๋„๋ก ํ•˜์˜€๋‹ค. + +์„ธ๋ฒˆ์งธ, ๊ณต๊ฒฉ์ ์ธ ํฌ๋กค๋Ÿฌ๋Š” ์ƒ๋Œ€ ์„œ๋ฒ„์—๊ฒŒ ๋ฌด๋ฆฌ๋ฅผ ์ค„ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•  ๋•Œ ์ƒ๋Œ€ ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค. ๊ทธ๋ž˜์„œ, ํ•™์‹๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๊ตญ๋ฏผ๋Œ€ ํ•™์‹ API๊ฐ€ ์กด์žฌํ•˜์—ฌ ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™”๋‹ค. ํ•˜์ง€๋งŒ, ๊ณต์ง€์‚ฌํ•ญ์€ ์ด๋Ÿฐ API๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ๊ณ ๋ คํ•ด์•ผ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํฌ๋กค๋ง ํšŸ์ˆ˜๋ฅผ ํ•˜๋ฃจ์— 1~2๋ฒˆ์œผ๋กœ ์ค„์˜€๊ณ , ๋˜๋„๋ก ์‚ฌ๋žŒ์ด ์ ๊ฒŒ ๋ชฐ๋ฆฌ๋Š” ์ƒˆ๋ฒฝ ๋˜๋Š” ์‹์‚ฌ ์‹œ๊ฐ„๋Œ€์— ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋˜ํ•œ, ์›น์‚ฌ์ดํŠธ์˜ robots.txt์„ ์ค€์ˆ˜ํ•˜์—ฌ ํฌ๋กค๋ง์ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์˜์—ญ์€ ์ง„ํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค. + +๋„ค๋ฒˆ์งธ, Jsoup๋ฅผ ์ด์šฉํ•˜์—ฌ ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•˜์˜€๋Š”๋ฐ, Jsoup์€ ์ •์  ๋ฉ”์†Œ๋“œ๋งŒ ์ œ๊ณตํ•ด์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ต๋‹ค๋Š” ์ ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ. ๊ทธ๋ž˜์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ์‹œ Mockito๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ํ•˜์ง€๋งŒ, ์ด ๋ถ€๋ถ„์€ ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•˜์—ฌ ๊ณ ๋ ค๋งŒํ•˜๊ณ  ์ง„ํ–‰ํ•˜์ง€ ๋ชปํ•˜์˜€๋‹ค. ์ถ”ํ›„, ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ๋Š” ์ด ์ ์„ ๊ณ ๋ คํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ด๋‹ค. + +
+ +### **๋ฐœ์Œ ์—ฐ์Šต** + +๋ฐœ์Œ ์—ฐ์Šต ๊ตฌํ˜„์„ ์œ„ํ•ด์„œ [Azure์—์„œ ์ œ๊ณตํ•˜๋Š” Speech Service](https://learn.microsoft.com/ko-kr/azure/ai-services/speech-service/)๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. + +๊ทธ๋Ÿฐ๋ฐ, ๋ฐœ์Œ ์—ฐ์Šต์ด ๋ถ„๋ช…ํžˆ Local์—์„œ๋Š” ์ž˜๋์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , Docker๋กœ Containerํ•œ ํ›„, ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„์— ์˜ฌ๋ฆฌ๋‹ˆ +``` +Could not initialize class com.microsoft.cognitiveservices.speech.SpeechConfig +``` +์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. + +์ด์œ ๋Š” Spring Docker๋ฅผ ์œ„ํ•ด, JDK ๊ฒฝ๋Ÿ‰ํ™” ์ด๋ฏธ์ง€์ธ alpine์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, [SDK ์„ค์น˜ ์„ค๋ช…์„œ](https://learn.microsoft.com/ko-kr/azure/ai-services/speech-service/quickstarts/setup-platform?tabs=linux%2Cubuntu%2Cdotnetcli%2Cdotnet%2Cjre%2Cmaven%2Cnodejs%2Cmac%2Cpypi&pivots=programming-language-java)๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด Debian ๋˜๋Š” Ubuntu ํ™˜๊ฒฝ์—์„œ๋งŒ ์ž‘๋™ํ•œ๋‹ค๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ alpine์ด ์•„๋‹Œ ubuntu ๊ธฐ๋ฐ˜์˜ JDK Image๋ฅผ ์‚ฌ์šฉํ•ด์„œ Build๋ฅผ ํ•˜์˜€๋‹ค. + +ํ•˜์ง€๋งŒ, ๋˜๋‹ค๋ฅธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ๋ชจ๋“  ์ ์ˆ˜๊ฐ€ 0์ ์ด ๋‚˜์˜ค๋Š” ์˜ค๋ฅ˜์˜€๋‹ค. ์ด์œ ๋Š” ์—ญ์‹œ ์„ค๋ช…์„œ์— ๋ช…์‹œ๋˜์–ด ์žˆ์—ˆ๋‹ค. + + + +์„ค๋ช…์„œ๋ฅผ ์ž˜ ๋ณด๋ฉด, Ubuntu 22.04๋Š” ๊ธฐ๋ณธ๊ฐ’์ด OpenSSL 3.0์ธ๋ฐ, ์ด๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , Docker Build๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•œ eclipse-temurin:17-jdk๋Š” Ubuntu 22.04์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ธฐ์กด 3.0๋ฒ„์ „์˜ Open SSL์„ ์ง€์šฐ๊ณ , 1.1 ๋ฒ„์ „์„ ์„ค์น˜ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ [Speech SDK Github Issue](https://github.com/Azure-Samples/cognitive-services-speech-sdk/issues/2276)์— ์˜ฌ๋ผ์™€์žˆ๋Š” OpenSSL ๋‹ค์šด๊ทธ๋ ˆ์ด๋“œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ƒˆ๋กœ Dockerfile์„ ์ž‘์„ฑํ•˜์—ฌ ๊ณ ์ณค๋‹ค. + +์ž์„ธํ•œ Dockerfile์€ ํ•„์ž ๋ธ”๋กœ๊ทธ์˜ [Azure ๋ฐœ์Œํ‰๊ฐ€ ํ•ด๊ฒฐ๊ณผ์ • ํฌ์ŠคํŒ…](https://mclub4.tistory.com/33)์„ ์ฐธ๊ณ ํ•ด์ฃผ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ๋‹ค. + +
+ +### **๋กœ๊น…** + +๋กœ๊น…์€ ์šฐ์„  ํ•  ์ˆ˜ ์žˆ๋Š” ๋งŒํผ ์ตœ๋Œ€ํ•œ ๋งŽ์ด, ๊ทธ ๋‹ค์Œ์— ํ•„์š” ์—†๋Š” ๋กœ๊ทธ๋Š” ์ง€์šฐ๊ฑฐ๋‚˜ ๋ ˆ๋ฒจ์„ ๋‚ฎ์ถ”๋Š” ์‹์œผ๋กœ ์šด์˜ํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค. ๊ทธ๋ž˜์„œ, ๋กœ๊น…์€ ๋‘๊ฐ€์ง€ ๋ถ€๋ถ„์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์˜€๋‹ค. + +์ฒซ๋ฒˆ์งธ, API Gateway๋‹จ์—์„œ ๋ชจ๋“  Request๋ฅผ Loggingํ•œ๋‹ค. ์ด๊ณณ์—์„œ ์š”์ฒญํ•œ url, method, request body, Authorization Header๋ฅผ Logging ํ•˜์˜€๋‹ค. ์ด๋กœ์จ, ํ•œ ๊ณณ์—์„œ ๊ณตํ†ต์ ์œผ๋กœ Logging์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. + +๋‘๋ฒˆ์งธ, Spring์—์„œ AOP๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ณ„ ์‹คํ–‰ ์‹œ๊ฐ„ ๊ธฐ๋ก์„ ์ง„ํ–‰ํ•˜์˜€๋‹ค. + + + +์œ„์™€ ๊ฐ™์ด Spring ์„œ๋ฒ„์—์„œ ๊ตฌ์„ฑ์„ ํ•˜์—ฌ, ๋ฉ”์„œ๋“œ๋ณ„ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์‰ฝ๊ฒŒ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. + +
+ +### **API ์‘๋‹ต ๋ฐ ๋ฌธ์„œ** + +Response๋กœ ๋ฐ›๋Š” Json ํ˜•์‹์ด ์ •ํ•ด์ ธ์žˆ์ง€ ์•Š๋‹ค๋ฉด, ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋งค๋ฒˆ API ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋งค๋ฒˆ ๊ทธ์— ๋งž์ถฐ ์ˆ˜์ •ํ•ด์•ผ๋  ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ์œ ์—ฐํ•˜๊ฒŒ Response๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด API ๊ณตํ†ต ์‘๋‹ต์„ ์„ค๊ณ„ํ–ˆ๋‹ค. ๊ณตํ†ต API ์‘๋‹ต ์„ค๊ณ„๋Š” [์นด์นด์˜ค์˜ API ๊ณตํ†ต์‘๋‹ต ๊ธฐ์ˆ ๋ฌธ์„œ](https://docs.kakaoi.ai/kakao_work/webapireference/commonguide/)๋ฅผ ์ฐธ๊ณ ํ•˜์˜€๋‹ค. + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/5dd65c4f-dd1e-4e6c-848f-3ede0d82194a) + +์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋Š” ์œ„์™€ ๊ฐ™์ด ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” success, ๊ทธ์— ๋Œ€ํ•œ message, ๊ทธ๋ฆฌ๊ณ  ์‘๋‹ต์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ’์ธ response๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. response์•ˆ์— ์ด์ œ ํ”„๋ก ํŠธ ์ธก์—์„œ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‹ด๊ฒจ์žˆ๋‹ค. + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/9f1a40dc-03e6-4a19-adb6-2339d7c8ecfd) + +์‹คํŒจํ–ˆ์„ ๋•Œ๋Š” success๊ฐ€ false, ์‹คํŒจ์— ๋Œ€ํ•œ message, ๊ทธ๋ฆฌ๊ณ  ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ์•Œ๋ ค์ฃผ๋Š” code๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. ํ•ด๋‹น ์˜ค๋ฅ˜ ์ฝ”๋“œ๋Š” ์„œ๋ฒ„ ์ธก์—์„œ Enum์œผ๋กœ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ…Œ์ŠคํŠธ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ์œ„ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋‹ค. + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/609b8800-59ba-445c-8a9c-8a075cc69a73) + +API ๋ฌธ์„œ๋Š” Swagger๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์†Œํ”„ํŠธ์›จ์–ด๊ณตํ•™์—์„œ Agile Process์— ๋Œ€ํ•ด์„œ ๊ณผ๋„ํ•œ ๋ฌธ์„œํ™”๋ฅผ ํ•˜์ง€ ๋ง๋ผ๊ณ  ์–ธ๊ธ‰ํ•˜์˜€๋‹ค. ํŠนํžˆ, API ๋ฌธ์„œํ™” ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๊ณ„์†ํ•ด์„œ ์‘๋‹ต ํ˜•์‹์ด๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ์ด ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋งค๋ฒˆ API ๋ฌธ์„œ๋ฅผ ์ตœ์‹ ํ™” ํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋ ต๊ณ , ์„œ๋ฒ„์™€ ๋ฌธ์„œ๊ฐ€ ์„œ๋กœ ์•ˆ๋งž๋Š” ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์˜คํžˆ๋ ค ๋ฌธ์„œ๋ฅผ ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ Cost์™€ Time์„ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค. + +๋”ฐ๋ผ์„œ, Swagger๋ฅผ ์ด์šฉํ•˜์—ฌ API๊ฐ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ์ž๋™์œผ๋กœ API ๋ฌธ์„œ๋ฅผ ์ตœ์‹ ํ™” ํ•ด๋„๋ก ๊ตฌ์„ฑํ•˜์˜€๋‹ค. ์ด๋กœ์จ, API ๋ฌธ์„œ๋ฅผ ์œ ์ง€ ๋ณด์ˆ˜ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋˜ํ•œ, Swagger๋ฅผ ํ†ตํ•ด API ๋ฌธ์„œ์—์„œ ๋ฐ”๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์–ด์„œ ํ˜‘์—…์˜ ๋Šฅ๋ฅ ์„ ํ–ฅ์ƒ์‹œ์ผฐ๋‹ค. + +
+ +### **๋ฐฐํฌ** + +AWS EC2 ์ธ์Šคํ„ด์Šค์— ๋ฐฐํฌํ•œ๋‹ค๋˜์ง€, ์ปดํ“จํ„ฐ์™€ ๋…ธํŠธ๋ถ์„ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ๊ฐœ๋ฐœํ•˜๋‹ค๋˜์ง€, ์ด๋Ÿฌ๋ฉด ๊ฐ๊ฐ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ด ํ‹€๋ ค ๋งค๋ฒˆ ์„ค์ •์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ Docker๊ฐ€ ์žˆ๋‹ค๋ฉด? ๊ท€์ฐฎ๊ฒŒ ๊ทธ๋Ÿด ํ•„์š” ์—†์ด ์‰ฝ๊ฒŒ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์‹คํ–‰ํ•˜๋Š” OS ํ™˜๊ฒฝ์— ์ƒ๊ด€์—†์ด, ์–ธ์ œ๋‚˜ ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋„๋ก Docker Containerํ™”๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค. Ruby On Rails๋‚˜ AI์„œ๋ฒ„์˜ Docker Containerํ™”๋Š” ๋‹ค๋ฅธ ๊ฒƒ๊ณผ ๋ณ„๋ฐ˜ ๋‹ค๋ฅผ๊ฒŒ ์—†๋Š”๋ฐ, Spring Containerํ™”๋ฅผ ์ข€ ํŠน์ดํ•˜๊ฒŒ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์ผ๋ฐ˜์ ์ธ Docker ๋ฐฐํฌ ๋ฐฉ์‹์œผ๋กœ Spring ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•œ๋‹ค๋ฉด, jar ํŒŒ์ผ์ด ๋ฌด๊ฑฐ์›Œ์งˆ์ˆ˜๋ก docker image๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์ด ๋น„ํšจ์œจ์ ์œผ๋กœ ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, Docker์˜ ์žฅ์ ์ด ๋ ˆ์ด์–ด๋งˆ๋‹ค ์บ์‰ฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๋น ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ํ•ด๋‹น ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์†Œ์Šค ์ฝ”๋“œ๊ฐ€ ํ•œ์ค„๋งŒ ๋ฐ”๊ฟ”๋„ ์บ์‰ฌ๊ฐ€ ๊นจ์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ ์—ฐ์‚ฐ์„ ํ•ด์•ผ๋˜๊ฐ€์ง€๊ณ , Docker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์ ์ด ์—†์–ด์ง„๋‹ค. ์™œ๋ƒํ•˜๋ฉด, Spring์˜ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Single layer์— ๋ฐฐ์น˜๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ฒฐ๊ตญ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ์˜ ์‹œ์ž‘ ์‹œ๊ฐ„์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค. + +๊ทธ๋ž˜์„œ Multistage Build์™€ Jar ํŒŒ์ผ์˜ ํŠน์„ฑ ๋‘๊ฐ€์ง€๋ฅผ ๊ณ ๋ คํ•ด์„œ Dockerfile์„ ์ž‘์„ฑํ–ˆ๋‹ค. ์šฐ์„ , ์ผ๋ฐ˜์ ์ธ ๋ฐฉ์‹์œผ๋กœ Docker Image๋ฅผ ์ƒ์„ฑ์‹œ, ์šฐ๋ฆฌ๋Š” jar ํŒŒ์ผ๋งŒ ํ•„์š”ํ•œ๋ฐ ์“ธ๋ฐ์—†๋Š” ์†Œ์Šค ์ฝ”๋“œ๊นŒ์ง€ ๋‹ค ํฌํ•จํ•˜๊ฒŒ ๋˜์–ด์„œ ์šฉ๋Ÿ‰์ด ์ง€๋‚˜์น˜๊ฒŒ ์ปค์ง€๊ณ , ์—…๋กœ๋“œํ•˜๋Š”๋ฐ๋„ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, MultiStage Build๋ฅผ ์ด์šฉํ•˜์—ฌ Buildํ•  ๋•Œ๋งŒ ํ•„์ˆ˜์ ์ธ ์ฝ”๋“œ๋กœ jar ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ์ตœ์ข… Docker image์—๋Š” ์ด๊ฒƒ๋“ค์ด ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. + + + +๋˜ํ•œ, Spring ๊ณต์‹ Docs๋ฅผ ๋“ค์–ด๊ฐ€๋ณด๋ฉด Spring์˜ Jar ํŒŒ์ผ์€ ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด 4๊ฐ€์ง€ Layer๋กœ ๋‚˜๋‰œ๋‹ค๊ณ  ํ•œ๋‹ค. ํ•ด๋‹น ๋ฌธ์„œ์—์„œ, application layer๊ฐ€ ๊ฐ€์žฅ ์ž์ฃผ ๋ฐ”๋€Œ๊ณ , ๊ทธ๋‹ค์Œ์€ snatphot, ๊ทธ๋‹ค์Œ์€ spring-boot-loader, dependencies ์ˆœ์œผ๋กœ ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. ์ฆ‰, Dependencies๊ฐ€ ๊ฐ€์žฅ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด Dockerfile์„ ์ž‘์„ฑํ•  ๋•Œ, Docker File์€ ์ œ์ผ ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์ฐจ๋ก€๋กœ ์ž‘์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์˜ ์—ญ์ˆœ์œผ๋กœ Docker File์„ ์ž‘์„ฑํ•˜์˜€๋‹ค. + +CI/CD๋กœ ๋Œ€ํ‘œ์ ์ธ ๊ฒƒ์€ Jenkins์™€ Git Actions๊ฐ€ ์žˆ๋Š”๋ฐ, ์šฐ๋ฆฌ๋Š” Git Actions์„ ํƒํ–ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” Github์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค ๋ณด๋‹ˆ๊น, ์ฝ”๋“œ ์ €์žฅ์†Œ๋ž‘ ๋ฐ”๋กœ๋ฐ”๋กœ ์—ฐ๊ฒฐ์ด ๋˜๊ณ , PR์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด GitHub Actions๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์ฝ”๋“œ ๋ณ€๊ฒฝ ๋ถ€๋ถ„์— ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€ ๊ฐ์ข… ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. + + + +์œ„์™€ ๊ฐ™์€ ํ”Œ๋กœ์šฐ๋กœ Github์—์„œ ์ž๋™์œผ๋กœ ์„œ๋ฒ„๋“ค์ด ๋ฐฐํฌ๋  ์ˆ˜ ์žˆ๋„๋ก Git Actions ์„ค์ •์„ ํ•ด๋†จ๋‹ค. + +setup-env์—์„œ git actions secret์œผ๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ .env๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฑธ ์šฐ๋ฆฌ ec2์— scp ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•ด์„œ ์ „์†กํ•œ๋‹ค. ๋˜ํ•œ, ์‹ค์งˆ์ ์ธ ์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ๋‹ด๋‹นํ•œ docker-compose.yml๋„ scp ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด์„œ ์ „์†กํ•œ๋‹ค. + +๊ทธ ๋‹ค์Œ, ์šฐ๋ฆฌ์˜ spring, ruby, spring gateway, nginx ๋“ฑ์„ docker image๋กœ buildํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. Git Actions๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ์šด RunTime ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์—, docker์˜ ์ด์ ์ธ docker cache๋ฅผ ์ ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๊ทธ๋ž˜์„œ Build ํ•  ๋•Œ ๊ต‰์žฅํžˆ ์‹œ๊ฐ„์ด ์˜ค๋ž˜๊ฑธ๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ, cache๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ docker์—์„œ ๊ณต์‹์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” docker buildX๋ฅผ ์ด์šฉํ–ˆ๋‹ค. BuildX๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Git Actions ๋‚ด๋ถ€์ ์ธ Cache ์ €์žฅ์†Œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ Docker Cache๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. **์‹ค์ œ๋กœ, BuildX๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ „์—๋Š” Spring Container ๋นŒ๋“œ ์‹œ๊ฐ„์ด 13 ~ 15๋ถ„ ์ •๋„ ๊ฑธ๋ ธ๋‹ค. ํ•˜์ง€๋งŒ, ์บ์‹œ๋ฅผ ์ ์šฉํ•œ ์ดํ›„๋กœ๋Š” ์œ„ ์‚ฌ์ง„์—์„œ ๋ณด์ด๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ 5๋ถ„์œผ๋กœ Build ์‹œ๊ฐ„์ด 10๋ถ„ ๊ฐ€๊นŒ์ด ์ค„์–ด๋“ค์—ˆ๋‹ค.** + +๋งˆ์ง€๋ง‰์œผ๋กœ, ์šฐ๋ฆฌ ec2์— ssh๋กœ ์ ‘์†ํ•ด์„œ ์ด๋ฏธ์ง€๋ฅผ ๋‚ด๋ ค๋ฐ›๊ณ  docker-compose๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋ฐฐํฌ๋ฅผ ์™„๋ฃŒํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. + +
+ +## **์„ฑ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ** + +
+ +### **์บ์‹ฑ** + +์œ ์ €๋“ค์ด ์ž์ฃผ ์ ‘๊ทผํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์บ์‹ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. +์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ๊ณต์ง€์‚ฌํ•ญ ๋ชฉ๋ก์ด๋‚˜ Q&A ๋ชฉ๋ก์ธ ๊ฒฝ์šฐ, ์ž์ฃผ ๋ฐ”๋€Œ์ง€๋„ ์•Š์„ํ…๋ฐ ์œ ์ €๋“ค์ด ํ•ด๋‹น ๊ฒŒ์‹œํŒ์— ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ RDB์—์„œ ๋ชฉ๋ก์„ ์ฝ์–ด์˜ค๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด๋‹ค. +๋”ฐ๋ผ์„œ, Redis๊ฐ™์€ ๊ณณ์— ์บ์‹ฑํ•ด๋‘๊ณ , ๋˜‘๊ฐ™์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋น ๋ฅด๊ฒŒ Redis์—์„œ ๋ฝ‘์•„๊ฐ€๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. +์บ์‹ฑ ์ „๋žต์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. + +์บ์‹ฑ ์ „๋žต์€ Look Aside์™€ Write Around ์ „๋žต์˜ ์กฐํ•ฉ์„ ํƒํ•˜์˜€๋‹ค. +Read ์ „๋žต์€ Look Aside ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ์šฐ์„ ์ ์œผ๋กœ Redis์—์„œ ์ฝ์–ด์˜ค๊ณ , cache miss๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ, RDS์—์„œ ์ฝ์–ด์˜ค๊ณ  ๋‹ค์‹œ redis์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. +Write ์ „๋žต์€ Write Around ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ๋•Œ, Redis์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๋ฐ˜๋“œ์‹œ RDB์— ์ €์žฅํ•˜๊ณ  ๊ธฐ์กด ์บ์‹œ๋Š” ๋ฌดํšจํ™”ํ•˜๋Š” ์ „๋žต์ด๋‹ค. + +์‹ค์ œ ์บ์‹ฑ์„ ์ ์šฉํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด๋ณด์•˜๋‹ค. + +์บ์‹ฑ ์ „์—๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ 414ms๊ฐ€ ๊ฑธ๋ ธ๋‹ค. + + + +ํ•˜์ง€๋งŒ **์บ์‹ฑ ํ›„์—๋Š” 12ms๋กœ ๋‹จ์ถ•๋˜์—ˆ๋‹ค. ์‹คํ–‰์‹œ๊ฐ„์ด 1/40 ๋‹จ์ถ•๋œ ๊ฒƒ์ด๋‹ค.** + + + +๊ทธ๋Ÿฐ๋ฐ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉด, class ์ •๋ณด์— ๋Œ€ํ•œ field๊ฐ€ json์— ์ถ”๊ฐ€๋˜๋Š” ํ˜„์ƒ์ด ์žˆ์–ด์„œ ์ถ”๊ฐ€ ๋ณด๋ฅ˜๋ฅผ ํ•˜์˜€๋‹ค. + +๋˜ํ•œ, ์ด๋ฏธ์ง€๋ฅผ ๋ฐ›์•„์˜ค๋Š” S3๋„ ์บ์‹ฑ์„ ๋ฌด์กฐ๊ฑด ํ•ด์•ผํ•œ๋‹ค. ์•„๋‹ˆ๋ฉด S3 ๋น„์šฉ์ด ๊ต‰์žฅํžˆ ๋งŽ์•„์งˆ ๊ฒƒ์ด๊ณ  ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ AWS CloudFront๋ฅผ S3 ์•ž๋‹จ์— ๋‘์–ด ์บ์‹ฑ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. + +
+ +### **๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ** + +๋ชจ๋“  ์š”์ฒญ์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๋‹ค. ํŠนํžˆ, ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜, ๋ฒˆ์—ญ API ๊ฐ™์€ ๋‹ค๋ฅธ API์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ฑฐ๋‚˜, ํฌ๋กค๋ง ๋“ฑ์€ ๋งค์šฐ ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์ด๋‹ค. ์ด ๋ชจ๋“  ์ž‘์—…์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด, ์„ฑ๋Šฅ์— ๋งค์šฐ ์น˜๋ช…์ ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Async๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์‹ค์‹œํ•˜์˜€๋‹ค. + +์ด ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ Spring์—์„œ ThreadExecutor๋ฅผ ์ด์šฉํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ๊ธฐ๋ณธ Async Executor SimpleAsyncTaskExecutor์ธ๋ฐ, ์ด๋Š” ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„, ์„ฑ๋Šฅ ์ €ํ•˜, ์Šค์ผ€์ผ๋ง ๋ฌธ์ œ ๋“ฑ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด Thread Pool ๋ฐฉ์‹ Executor๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ์Šค๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ์„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํ–‰์‹œ๊ฐ„์ด ์งง์€ ๋งŽ์€ ๋Ÿ‰์˜ Task๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ถˆ๋ฆฌํ•˜๋‹ค. ๋ฆฌ์†Œ์Šค ์ธก๋ฉด์—์„œ๋Š” ๊ฐ ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ๋™์‹œ์— ๋งŽ์€ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์š”์ฒญ๋˜๋ฉด ๋งค๋ฒˆ ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ๋”ฐ๋ผ์„œ CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ฆฌ์†Œ์Šค์˜ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ณผ๋„ํ•˜๊ฒŒ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฉด์—์„œ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์†Œ๋ฉธ์‹œํ‚ค๋Š” ๋ฐ๋Š” ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋ฆฌ์†Œ์Šค๊ฐ€ ์†Œ์š”๋œ๋‹ค. ๊ฐ ์ž‘์—…๋งˆ๋‹ค ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ด๋Ÿฐ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๊ณ , ์ „์ฒด์ ์ธ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ผ์น  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, SimpleAsyncTaskExecutor๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜์— ๋Œ€ํ•œ ์ œํ•œ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋™์‹œ์— ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ •์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์—†๋Š” ์ˆ˜์ค€์œผ๋กœ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๊ณง OutOfMemoryError ๋“ฑ์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. + + + +๊ทธ๋ž˜์„œ, ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด Async์˜ Executor๋ฅผ ThreadPool๋ฐฉ์‹์˜ Executor๋กœ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋Š”, ์‚ฌ์šฉํ•  ์Šค๋ ˆ๋“œ ํ’€์— ์†ํ•œ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ˆ˜์ธ corepoolsize, corepoolsize๊ฐ€ ๊ฐ€๋“ ์ฐฌ ์ƒํƒœ์—์„œ ๋”์ด์ƒ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ• ๋•Œ, ๋Œ€๊ธฐํ•˜๋Š” ์žฅ์†Œ ํฌ๊ธฐ์ธ queuecapacity, ์Šค๋ ˆ๋“œ ํ’€์ด ํ™•์žฅ๋  ์ˆ˜ ์žˆ๋Š” ์Šค๋ ˆ๋“œ์˜ ์ƒํ•œ์„ , ์ฆ‰ ์Šค๋ ˆ๋“œ ์ˆ˜์˜ ์ƒํ•œ์„ ์ธ maxpoolsize๋ฅผ ์กฐ์ •ํ•˜์—ฌ ThreadPool๋ฐฉ์‹์˜ Async Executor๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค. + +
+ +### **Race Condition ํ•ด๊ฒฐ** + +ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ถ”์ฒœ / ์ถ”์ฒœ ํ•ด์ œ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ถ”์ฒœ์ˆ˜, ์กฐํšŒ์ˆ˜์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ํ•˜๋‚˜์˜ ํ•„๋“œ์— ๋Œ€ํ•ด ์ˆ˜๋งŽ์€ ํŠธ๋žœ์ ์…˜ ์š”์ฒญ์ด ์˜ค๊ฐ€๋Š” ๊ธฐ๋Šฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒฝ์šฐ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ Race Condition, ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ Redis๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. Redis๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ์ž์›์— ๋Œ€ํ•œ Race Condition ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Cache Write ์ „๋žต์ค‘์—์„œ Write Back ์ „๋žต์„ ์‘์šฉํ•˜์˜€๋‹ค. Redis์— ์š”์ฒญ์„ ๋ณด๋‚ผ countTemplate๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ด์„œ Redis์— ๊ฐ์ฒด์™€ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ณ  ์ผ์ •์‹œ๊ฐ„๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ Cache์— ์ €์žฅ๋œ ๋‚ด์šฉ์„ DB์— ๋ฐ˜์˜์‹œํ‚ค๋„๋ก ํ•˜์˜€๋‹ค. ์ด๋ ‡๊ฒŒ Redis์— ์ €์žฅ๋œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ 30์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ DB์— ๋ฐ˜์˜๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. + +ํ•˜์ง€๋งŒ, Redis๋ฅผ ์ด์šฉํ•˜๋”๋ผ๋„ ๊ฒฐ๊ตญ์—๋Š” Redis์—์„œ ๊ฐ’์„ ์ฝ๊ณ  ๋น„๊ตํ•˜๊ณ  ์“ฐ๋Š” ์ด ์ผ๋ จ์˜ ํ–‰์œ„๋Š” Spring ์ฝ”๋“œ์—์„œ Lock์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๊ณ  ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— Atomicํ•˜๊ฒŒ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Redis Lock์„ ๊ฑธ์–ด์ฃผ์–ด Atomicํ•˜๊ฒŒ ์‹คํ–‰๋˜๋„๋ก ๋ณด์žฅํ•˜์˜€๋‹ค. ๋ฌผ๋ก , ์šฐ๋ฆฌ๋Š” Redis Lock์„ ์ด์šฉํ•˜์˜€์ง€๋งŒ, ์ด์™ธ์—๋„ Redis Transaction ์ด์šฉ, Redis ๋‚ด๋ถ€์—์„œ Lua Script๋ฅผ ์ด์šฉํ•˜์—ฌ Atomicํ•˜๊ฒŒ ์‹คํ–‰, DB Lock, DB์˜ Isolation Level ์กฐ์ • ๋“ฑ์˜ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค. + +ํ˜„์žฌ ๋ฐฉ๋ฒ•๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ๋‚ด๊ฐ€ ์ถ”์ฒœํ•œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๊ฐ€ ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์–ด ๋ณด์—ฌ์ง€์ง€๋Š” ์•Š๋Š”๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ฌ๋•Œ๋Š” ์บ์‹œ์—์„œ ์ฐพ๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ถ”ํ›„์—๋Š” ์บ์‹œ์—์„œ ๊ฐ’์„ ์ฐพ๋„๋ก ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. + +
+ +### **N+1 ๋ฌธ์ œ** + +์—ฌ๋Ÿฌ ์ฐธ์กฐ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ด๋“ค์„ Joinํ•˜์—ฌ ํ˜ธ์ถœํ•˜๋Š” ์ƒํ™ฉ์ด ๋งŽ์ด ์žˆ์—ˆ์œผ๋ฉฐ ์ด๋•Œ N+1๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Fetch Join์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Projection ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์˜ Inner Join๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ฒฝ์šฐ์˜ N+1์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋ชจ์ƒ‰ํ•˜์—ฌ ์ ์šฉํ•˜์˜€๋‹ค. ์ด๋กœ ์ธํ•ด ์„œ๋ฒ„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ์„ฑ๋Šฅํ–ฅ์ƒ์ด ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ ์‹œํ–‰ ์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ฐฉ์‹์€ Projection์„ ์‚ฌ์šฉํ•˜์—ฌ DTO๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ JPA ๊ฒ€์ƒ‰์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋Š”๋ฐ ์ด ๊ฒฝ์šฐ Fetch Join์ด๋‚˜ EntityGraph๋กœ๋Š” ํ•ด๊ฒฐ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. @Projection์„ ์‚ฌ์šฉํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ๋„ ์žˆ์œผ๋‚˜ ์ข€ ๋” ๊ณ ๋ฏผํ•ด๋ณธ ๊ฒฐ๊ณผ Join์˜ ๋ชฉ์ ์—์„œ N+1๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•  ํ•„๋“œ๊ฐ’์ด ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด InnerJoin์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ํ•ด๊ฒฐ๋˜๋Š” ๋ฒ”์ฃผ์˜€๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜๋‹ค. + +๋˜ํ•œ, batch size๋ฅผ ์กฐ์ ˆํ•ด์„œ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ์„œ๋น„์Šค ์ดˆ๊ธฐ๋ผ์„œ batch size๋กœ ์˜ํ–ฅ์ด ๊ฐˆ ๋งŒํผ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ ์€ ๋ณด๋ฅ˜ํ•˜์˜€๋‹ค. + +
+ +## **์•ˆ์ •์„ฑ ๊ณ ๋ ค ์‚ฌํ•ญ** + +
+ +### **์‚ฌ์šฉ์ž ํŠธ๋ž˜ํ”ฝ ์ œํ•œ** + +A๋ผ๋Š” ์‚ฌ๋žŒ์ด ์•…์˜์ ์œผ๋กœ ์šฐ๋ฆฌ AI ๋ชจ๋ธ์—๊ฒŒ 1๋ถ„์— 100๋งŒ๋ฒˆ์˜ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์šฐ๋ฆฌ AI Token์€ ๋ชจ๋‘ ์‚ฌ๋ผ์ง„๋‹ค. B๋ผ๋Š” ์‚ฌ๋žŒ์€ Spring ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์— 1์ดˆ์— 1000๋งŒ๋ฒˆ ์š”์ฒญ์„ ๋‚ ๋ฆฐ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์—ญ์‹œ ์„œ๋ฒ„๊ฐ€ ํ„ฐ์งˆ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด์—์„œ ๋ฏธ๋“ค์›จ์–ด ์—ญํ• ์„ ํ•˜์—ฌ ๋ถ„๋‹น API ์‚ฌ์šฉ๋Ÿ‰์„ ์ œํ•œํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ API Rate Limiter๋ฅผ ์ ์šฉํ–ˆ๋‹ค. + + + +์‚ฌ์šฉ์ž๊ฐ€ ์ตœ๋Œ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” Token Bucket Capacity๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ณ„์†, ์ง€์†์ ์œผ๋กœ ์ผ์ •์‹œ๊ฐ„๋งˆ๋‹ค Token์ด ๋ฆฌํ•„์ด ๋œ๋‹ค. ์ด Token ๋ฆฌํ•„์€ Bucket Size๊ฐ€ ๊ฝ‰์ฐผ์„๋•Œ๋Š” ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ API ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ๋งˆ๋‹ค Bucket์—์„œ Token์„ ๊บผ๋‚ด๊ฐ„๋‹ค. ๋งŒ์•ฝ, ๊บผ๋‚ผ Token์ด ์—†๋‹ค๋ฉด, Status 429๋ฅผ ๋ณด๋‚ด์ค€๋‹ค. (Too Many Requests) ์ด์™ธ์—๋„ ๋ˆ„์ถœ ๋ฒ„ํ‚ท ์•Œ๊ณ ๋ฆฌ์ฆ˜, ๊ณ ์ • ์œˆ๋„ ์นด์šดํ„ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ด๋™ ์œˆ๋„ ๋กœ๊น… ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ด๋™ ์œˆ๋„ ์นด์šดํ„ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋“ฑ์ด ์กด์žฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ Token Bucket Algorithm์„ ๋งŽ์ด ์“ฐ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. + + + +Redis๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ DB์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ๋นจ๋ผ์„œ ์ ํ•ฉํ•˜๋‹ค. ๋˜ํ•œ, Redis์˜ ๋˜ ๋‹ค๋ฅธ ํŠน์ง•์€ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ๋‹ค์ค‘ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์—๋„ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ดˆ๋‹น ์‚ฌ์šฉ์ž๊ฐ€ 6~7๋ฒˆ์˜ ์š”์ฒญ๊นŒ์ง€๋งŒ ๋ณด๋‚ด๊ณ , ์•„๋‹๊ฒฝ์šฐ 429 Error๋ฅผ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•ด์„œ ํŠธ๋ž˜ํ”ฝ ์ œํ•œ์„ ๊ฑธ์—ˆ๋‹ค. + + + +์‹ค์ œ๋กœ ์œ„์— ์‚ฌ์ง„์ฒ˜๋Ÿผ Apache Jmeter๋กœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ๋Œ๋ ค๋ณด๋ฉด, 429 Error๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. + +
+ +### **์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ํŒŒ์ผ ์šฉ๋Ÿ‰ ์ œํ•œ** + +์™ธ๊ตญ๋ฏผ ์„œ๋น„์Šค์—๋Š” ๋ฐœ์Œํ‰๊ฐ€์™€ ๊ฒŒ์‹œํŒ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์กด์žฌํ•œ๋‹ค. ์ด ์ƒํ™ฉ์—์„œ, Client๋Š” Server์— ํŒŒ์ผ์„ ์—…๋กœ๋“œ ํ• ํ…๋ฐ, ๋งŒ์•ฝ์— ๋„ˆ๋ฌด ํฌ๊ธฐ๊ฐ€ ํฐ ํŒŒ์ผ์„ ๋ณด๋‚ธ๋‹ค๋ฉด ์„œ๋ฒ„์— ํฐ ๋ถ€๋‹ด์„ ์ค„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, Nginx๋ฅผ ์ด์šฉํ•˜์—ฌ ์ œ์ผ ์ตœ์ „๋ฐฉ์—์„œ ๋น ๋ฅด๊ฒŒ ์šฉ๋Ÿ‰์ด ๋„ˆ๋ฌด ํฐ ํŒŒ์ผ์€ ์˜ฌ๋ฆฌ์ง€ ๋ชปํ•˜๋„๋ก ์ฐจ๋‹จํ•˜์˜€๋‹ค. + +
+ +## **๋ณด์•ˆ์  ๊ณ ๋ ค ์‚ฌํ•ญ** + +
+ +### **์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”** + +์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ทธ๋Œ€๋กœ DB์— ๋ณด๊ด€ํ•˜๋Š” ๊ฒƒ์€ ์ž˜๋ชป๋œ ์ผ์ด๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์ง€ ์•Š๊ณ  ์ €์žฅํ•˜๋ฉด, DB๊ฐ€ ํ•ดํ‚น๋‹นํ–ˆ์„ ๊ฒฝ์šฐ ํฐ ํ”ผํ•ด๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์‹ค์ œ๋กœ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”๋Š” ์ ค ๊ธฐ์ดˆ์ ์ธ ์ž‘์—…์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ํ•˜์ง€์•Š์•„ ๊ฐœ์ธ์ •๋ณด์œ ์ถœ์—์„œ ํฐ ํ”ผํ•ด๋ฅผ ๋ณธ ๊ธฐ์—…๋“ค์ด ๋งŽ์ด ์žˆ๋‹ค. + +๊ทธ๋ž˜์„œ, โ€œ์™ธ๊ตญ๋ฏผโ€ ์„œ๋น„์Šค๋Š” Firebase๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ID์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. Firebase๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ž˜ ๋ณด๊ด€ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. + +
+ +### **JWT๋ฅผ ํ†ตํ•œ Authentication** + +Token์€ ์„œ๋ฒ„๊ฐ€ ๊ฐ๊ฐ์˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์‚ฌ์šฉ์ž์˜ ์œ ๋‹ˆํฌํ•œ ์ •๋ณด๋ฅผ ๋‹ด์€ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ์ด๋‹ค. ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ •์ƒ์ผ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋กœ๊ทธ์ธ์‹œ Main Business Server์—์„œ Access ๋ฐ Refresh Token์„ ๋ฐœ๊ธ‰ํ•ด์ฃผ๊ณ , ๋งค ์š”์ฒญ๋งˆ๋‹ค Header๋กœ AccessToken์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด API Gateway์„œ๋ฒ„์—์„œ ํ•ด๋‹น ํ† ํฐ์„ ํ™•์ธํ•ด๋ณด๊ณ , Invalidํ•œ JWT Token์ผ ๊ฒฝ์šฐ 403 ์—๋Ÿฌ๋ฅผ ๋ณด๋‚ด์ค€๋‹ค. ์ •์ƒ์ ์ธ Token์ผ ๊ฒฝ์šฐ, ํ•ด๋‹น ์„œ๋ฒ„๋กœ ๋ผ์šฐํŒ…์„ ํ•ด์ฃผ์–ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. + +
+ +### **Token ํƒˆ์ทจ ์ƒํ™ฉ ์˜ˆ๋ฐฉ** + +JWTํ† ํฐ์€ ํƒˆ์ทจ์˜ ์œ„ํ—˜์„ฑ์ด ์žˆ๋‹ค. JWT๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐฉ์‹์—์„œ๋Š” ์„ธ์…˜๊ณผ ๋‹ค๋ฅด๊ฒŒ Statelessํ•˜๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Token์ด ํƒˆ์ทจ๋‹นํ•ด๋„ ์šฐ๋ฆฌ ์„œ๋ฒ„๊ฐ€ ๊ทธ๊ฑธ ์•Œ์•„์ฐจ๋ฆฌ๊ณ  ํ•ด๋‹น Token์„ ์ค‘์ง€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ, accessToken์€ ์œ ํšจ๊ธฐ๊ฐ„์ด ์งง์€ ๋‹จ์œ„์˜ ํ† ํฐ์œผ๋กœ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์ฃผ๋กœ 15๋ถ„ ์ •๋„๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ ‡๊ฒŒ ์งง์€ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž๋Š” ๊ณ„์†ํ•ด์„œ ์žฌ๋กœ๊ทธ์ธ์„ ํ•ด์•ผ๋œ๋‹ค๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด Refresh Token์ด๋‹ค. + + + +Refresh Token์€ AccessToken๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋งค์šฐ ๊ธด ์œ ํšจ๊ธฐ๊ฐ„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. Access Token๊ณผ Refresh Token์„ ์‚ฌ์šฉํ•  ๋•Œ, ํ”Œ๋กœ์šฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ˜๋Ÿฌ๊ฐ„๋‹ค. + +1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํšŒ์›๊ฐ€์ž… ํ˜น์€ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด ์„œ๋ฒ„๋Š” ์‘๋‹ต๊ฐ’์œผ๋กœ Access Token๊ณผ Refresh Token์„ ํ•จ๊ป˜ ์ œ๊ณตํ•œ๋‹ค. +2. ํด๋ผ์ด์–ธํŠธ๋Š” API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ Access Token ๋‹ด์•„ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„์—์„œ Access Token์„ ํ†ตํ•ด ์œ ์ €๋ฅผ ์ธ์ฆํ•œ๋‹ค. +3. ๋งŒ์•ฝ Access Token์˜ ์œ ํšจ ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด, ํด๋ผ์ด์–ธํŠธ๋Š” Refresh Token์„ ์„œ๋ฒ„์— ์ „๋‹ฌํ•˜์—ฌ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค. + +์ฆ‰, Access Token๊ณผ Refresh Token์œผ๋กœ ๋‚˜๋ˆ„๊ณ , Access Token์„ ํ†ตํ•ด ํ†ต์‹ ํ•จ์œผ๋กœ์จ, ์•…์˜์  ์ด์šฉ์ž์— ์˜ํ•ด Access Token์ด ํƒˆ์ทจ๋‹นํ•˜๋”๋ผ๋„ ์œ ํšจ๊ธฐ๊ฐ„์ด ์งง๊ธฐ์— ํฌ๊ฒŒ ๋ถ€๋‹ด์ด ๋˜์ง€ ์•Š๊ณ , ์ฃผ ํ†ต์‹ ์€ Access Token์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— Refresh Token์ด ํƒˆ์ทจ๋‹นํ•  ๊ฐ€๋Šฅ์„ฑ์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. + +๊ทธ๋Ÿฐ๋ฐ RefreshToken๋„ ํƒˆ์ทจ๋‹นํ•˜๋ฉด ์–ด๋–ป๊ฒŒํ•˜์ง€?๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งŽ์ด ์“ฐ๋Š” ๋ฐฉ๋ฒ•์ด Refresh Token Rotation(RTR) ๋ฐฉ๋ฒ•์ด๋‹ค. + + + +Refresh Token Ratation(RTR)์ด๋ž€ Access Token์ด ๋งŒ๋ฃŒ๋˜๊ณ  Refresh Token์œผ๋กœ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐ›์•„์˜ฌ ๋•Œ, ์ƒˆ๋กœ์šด Refresh Token๋„ ๋ฐ›์•„์˜ค๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์ฆ‰. Refresh Token์ด ์‚ฌ์šฉ๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด Access Token๊ณผ Refresh Token์„ ๋ฐœ๊ธ‰ํ•˜์—ฌ ์ด์ „์— ๋ฐœ๊ธ‰๋œ Token๋“ค์€ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค. + +
+ +### **AccessToken Ban** + +๋งŒ์•ฝ์— ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒ์„ ํ–ˆ๋‹ค๊ณ  ํ•ด๋ณด์ž. AccessToken์˜ ๊ธฐ๊ฐ„์ด ๋งŒ์•ฝ์— 30๋ถ„์ธ๋ฐ, 5๋ถ„๋งŒ์— ๋กœ๊ทธ์•„์›ƒ์„ ํ–ˆ๋Š”๋ฐ๋„, ๊ทธ Token๊ฐ€์ง€๊ณ  ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋กœ๊ทธ์•„์›ƒ์„ ํ•  ๊ฒฝ์šฐ, Redis์— ํ•ด๋‹น AccessToken์„ Ban์„ ํ•ด๋‘์–ด์„œ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ๋ชปํ•˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. + +
+ +### **HMAC** + +์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ๋Œ€๋ถ€๋ถ„์— AccessToken์„ ์š”๊ตฌํ•˜๋”๋ผ๋„, ๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž… ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” Token์ด ์—†๋Š” ์ƒํ™ฉ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์š”์ฒญ์ด๋‹ค. ํ•˜์ง€๋งŒ, ๋ˆ„๊ตฐ๊ฐ€ ์šฐ๋ฆฌ Endpoint๋ฅผ ์•Œ์•„๋‚ด๊ณ , Flutter ์•ฑ์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๋ฉด ์ด๋Š” ๋น„์ •์ƒ์ ์ธ ์š”์ฒญ์ผ ๊ฒƒ์ด๋‹ค. + + + +๋”ฐ๋ผ์„œ, ํšŒ์›๊ฐ€์ž… ๋ฐ ๋กœ๊ทธ์ธ ์‹œ์—๋Š” HMAC์„ ํฌํ•จํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ์šฐ๋ฆฌ Flutter ์•ฑ๊ณผ ์„œ๋ฒ„ ๋ชจ๋‘ ๊ณตํ†ต๋œ Secret๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ์ด Secret์„ ๊ฐ€์ง€๊ณ  ์šฐ๋ฆฌ Request์˜ HMAC์„ ๊ฐ™์ด ๋ณด๋‚ด๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์šฐ๋ฆฌ ์•ฑ์—์„œ ๋ณด๋‚ด์ง€ ์•Š์€ ์š”์ฒญ๋“ค์€ ๋ชจ๋‘ ๋ง‰์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. + +
+ +### **ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ ์šฉ** + +JWT Secret, HMAC Secret, API Key ๋“ฑ๋“ฑ์„ ์šฐ๋ฆฌ Code์— Hard Codingํ•ด์„œ๋Š” ์ ˆ๋Œ€ ์•ˆ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, env ํŒŒ์ผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. + +
+ +## **ํ…Œ์ŠคํŠธ ๊ณ ๋ ค ์‚ฌํ•ญ** + +
+ +### **Test Container๋ฅผ ์ด์šฉํ•œ ๊ฐ€์ƒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•** + +๋˜ํ•œ, ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ ์„œ๋น„์Šคํ•˜๋Š” RDS๋ฅผ ๊ฐ€์ง€๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค๋ฉด, ๋งค์šฐ ์œ„ํ—˜ํ•  ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ง„ํ–‰์„ ์œ„ํ•ด์„œ ๋ถ„๋ช…ํžˆ DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ•ด๋ด์•ผ๋˜๋Š” ์ˆœ๊ฐ„์ด ์กด์žฌํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Test Container๋ฅผ ์ด์šฉํ•˜์—ฌ MySQL, Redis ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“  ๋’ค, ๋…๋ฆฝ๋œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค. + +
+ +### **Apache Jmeter๋ฅผ ์ด์šฉํ•œ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ** + + + +ํ”„๋กœ์ ํŠธ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ…Œ์ŠคํŠธ์™€ ๊ฐ™์ด ๋Œ€๊ทœ๋ชจ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ, ์‰ฝ๊ฒŒ ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ์„ ์ƒ์„ฑํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์™ธ๋ถ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ด์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ TPS๋ฅผ ์ธก์ •ํ•  ์ˆ˜๋„ ์žˆ์—ˆ๋‹ค. + +์‹ค์ œ๋กœ ๊ณต์ง€์‚ฌํ•ญ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ›์•„์˜ค๋Š” API์— ๋Œ€ํ•˜์—ฌ RPS๋ฅผ ์ธก์ •ํ•ด๋ณด๋‹ˆ 2000 RPS ์ •๋„๊นŒ์ง€๋Š” ๋ฌด๋ฆฌ ์—†์ด ์ž˜ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ด ์ด์ƒ์œผ๋กœ๋Š” ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ง€ ์•Š์•˜์ง€๋งŒ, ๋” ๊ฐ€๋Šฅํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. + +
+ +## **์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„** + + + +๊ธฐ์กด ์•„ํ‚คํ…์ฒ˜๋Š” ์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์œ„์˜ ์•„ํ‚คํ…์ฒ˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ์žˆ์—ˆ๋‹ค. ์šฐ์„ , ์šฐ๋ฆฌ ๋ฉ”์ธ Spring ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„๋Š” ๋‹ค๋ฅธ๊ฑฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋„ ๋ฐ”์˜๋‹ค. ๊ฒŒ๋‹ค๊ฐ€, ์ €ํฌ ์„œ๋น„์Šค๋Š” ์ฑ„ํŒ…์„ Long Polling์œผ๋กœ ๊ฐœ๋ฐœํ•˜์˜€๋Š”๋ฐ, ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ ๋‹ค๋ฐœ์ ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค? ๊ทธ๋Ÿผ ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ์„ ๋งค์šฐ ๋–จ์–ด์งˆ ๊ฒƒ์ด๋ฉฐ, ์‘๋‹ต์‹œ๊ฐ„์€ ๊ธธ์–ด์งˆ ๊ฒƒ์ด๋‹ค. ๊ฒฐ๊ตญ Software Quality๊ฐ€ ๋–จ์–ด์งˆ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ฑ„ํŒ… ์„œ๋ฒ„๋ฅผ ๋ถ„๋ฆฌํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. + +๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, api ์š”์ฒญ์„ ๋ณด๋‚ผ๋•Œ https๊ฐ€ ์•„๋‹Œ http๋กœ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ์˜ Integrity๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์š”์ฒญ์„ https๋กœ ์•”ํ˜ธํ™”ํ•ด์„œ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. + +๋งˆ์ง€๋ง‰์œผ๋กœ, ์ฑ—๋ด‡, ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, ์ฑ„ํŒ… ์„œ๋ฒ„ ๋ชจ๋‘ JWT Token์„ ํ™•์ธํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค. ํ•˜์ง€๋งŒ, ์œ„์™€ ๊ฐ™์ด ๋””์ž์ธํ•˜๋ฉด, ๊ฐ๊ฐ ์„œ๋ฒ„์—์„œ JWT Token์„ ํ™•์ธํ•˜๋Š” ๋กœ์ง์„ ์งœ์•ผ๋˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ ์ด๋‹ค. ๋˜ํ•œ, API ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ์„ ๊ฑธ๊ธฐ ์œ„ํ•ด์„œ ๊ฐ๊ฐ API Rate Limiter๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, API Gateway๋ฅผ ๊ตฌ์ถ•ํ•ด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. + + + + +๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์€ ์•„ํ‚คํ…์ฒ˜๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. + +์šฐ์„ , ์ฑ„ํŒ… ์„œ๋ฒ„ ๋ถ„๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์ฑ„ํŒ… ์„œ๋ฒ„๋Š” Ruby On Rails๋กœ ๊ฐœ๋ฐœํ•˜์˜€๋‹ค. ๊ทธ ์ด์œ ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€์ด๋‹ค. ์ฒซ๋ฒˆ์งธ, Ruby๋ผ๋Š” ์–ธ์–ด๋Š” ๋งค์šฐ ์‰ฝ๋‹ค. ํŒŒ์ด์ฌ์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•ด๋ณด์ง€ ์•Š์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ž…๋ฌธ์šฉ์œผ๋กœ ์ถ”์ฒœ์ด ๊ฐ€๋Šฅํ•  ์ •๋„๋กœ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ํ•„์ž๋Š” ์–ด๋ฆด ๋•Œ RPG VX ACE๋ผ๋Š” ๊ฒŒ์ž„ ๋งŒ๋“ค๊ธฐ ํˆด์—์„œ, ์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋กœ Ruby๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์–ด๋Š์ •๋„ ์กฐ๊ธˆ์€ ์•„๋Š” ์ƒํƒœ์˜€๋‹ค. ๋‘๋ฒˆ์งธ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ๋งค์šฐ ์‰ฝ๋‹ค. Spring์€ Test Code๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๊ต‰์žฅํžˆ ์—ฌ๋Ÿฌ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผํ•˜๋ฉฐ ๋ณต์žกํ•˜๋‹ค. ๋ฐ˜๋ฉด์— Ruby On Rails์€ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์„ฑ๋˜์žˆ๋‹ค. ์„ธ๋ฒˆ์งธ, Ruby On Rails๋Š” ๋น ๋ฅธ ๊ฐœ๋ฐœ์„ ์ง€ํ–ฅํ•œ๋‹ค. COC์›์น™์„ ์ค‘์š”์‹œํ•ด์„œ, ์„ค์ •์ด ๊ฑฐ์˜ ์—†๋‹ค. Spring์œผ๋กœ ์„œ๋ฒ„ ํ•˜๋‚˜ ๋งŒ๋“ค๋ ค๋ฉด ํ•˜๋‚˜๋ถ€ํ„ฐ ์—ด๊นŒ์ง€ ์„ค์ •ํ• ๊ฒŒ ๋งŽ์•„์„œ ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค. ํ”„๋กœ์ ํŠธ ๋งˆ๊ฐ ๊ธฐํ•œ์ด ์–ผ๋งˆ ์•ˆ๋‚จ์€ ์ง€๊ธˆ, Ruby On Rails ๋˜๋Š” Express๋งŒํผ ์ข‹์„๊ฒŒ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ํŠนํžˆ, ์–ธ์–ด์˜ ํŠน์„ฑ์ด ์• ์ž์ผ์— ์ดˆ์ ์ด ๋งž์ถฐ์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์‚ฐ์„ฑ์ด ๋†’๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์ฑ„ํŒ… ์„œ๋ฒ„ ๊ฐœ๋ฐœ์— Ruby On Rails๋ฅผ ์ฑ„ํƒํ–ˆ๋‹ค. + +๋‘๋ฒˆ์งธ, ์ž๋™์œผ๋กœ CI/CD๊ฐ€ ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, ์ฑ„ํŒ… ์„œ๋ฒ„, AI ์„œ๋ฒ„์— Git Actions๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋กœ์จ, ๊ตณ์ด ์šฐ๋ฆฌ EC2์— ์ ‘์†ํ•  ํ•„์š” ์—†์ด ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ ๋ฐฐํฌ๊ฐ€ ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. + +์„ธ๋ฒˆ์งธ, Route53์„ ์ด์šฉํ•ด ๋„๋ฉ”์ธ์„ ์ ์šฉํ–ˆ๋‹ค. ๊ฐ€๋น„์•„ ๋„๋ฉ”์ธ์—์„œ capstone30.shop์ด๋ผ๋Š” ๋„๋ฉ”์ธ์„ ๊ตฌ๋งคํ–ˆ๋‹ค. ์šฐ๋ฆฌ ํŒ€์€ API ๋ฌธ์„œ๋ฅผ Swagger๋ฅผ ์ด์šฉํ•ด์„œ ๊ณต์œ ํ•˜๋Š”๋ฐ, ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ ์„œ๋ฒ„ ๋„๋ฉ”์ธ์„ ์ณ์•ผํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๊ธฐ์กด AWS EC2 ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์€ ๊ธฐ์–ตํ•˜๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์„œ, ์™ธ์šฐ๊ธฐ ์‰ฌ์šด ๋„๋ฉ”์ธ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค. ๋˜ํ•œ, Https๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด DV SSL ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์•˜์–ด์•ผ ํ–ˆ๋Š”๋ฐ, AWS EC2 ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์œผ๋กœ๋Š” ์ธ์ฆ์„œ ๋ฐœ๊ธ‰์ด ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋”ฐ๋กœ ๋„๋ฉ”์ธ์„ ๊ตฌ๋งคํ•ด์„œ ์ ์šฉํ–ˆ๋‹ค. + +๋„ค๋ฒˆ์งธ, Nginx์™€ Certbot์„ ์ด์šฉํ•˜์—ฌ Https๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ Letโ€™s Encrypt๋ผ๋Š” ๋ฌด๋ฃŒ๋กœ DV ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” CA๋กœ๋ถ€ํ„ฐ SSL์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ Https๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋กœ์จ, ๋ฐ์ดํ„ฐ์˜ Integrity๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ, Letโ€™s Encrypt ์ธ์ฆ์„œ๋Š” 90์ผ๋งˆ๋‹ค ์ƒˆ๋กœ ๋ฐœ๊ธ‰ ๋ฐ›์•„์•ผํ•˜๋Š”๋ฐ, Docker Compose์— ์ฃผ๊ธฐ์ ์œผ๋กœ ์ธ์ฆ์„œ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š” command๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ์ด ๊ณผ์ •์ด ์ž๋™์œผ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋˜ํ•œ, Spring Cloud Gateway ์•ž๋‹จ์—, Nginx๊ฐ€ ๋จผ์ € ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„ https์„ ๋‚ด๋ถ€์ ์œผ๋กœ http๋กœ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, ๋กœ์ปฌ ์„œ๋ฒ„ ๋‚ด์—์„œ๋Š” ์•”ํ˜ธํ™”ํ•˜๋Š”๋ฐ ๋ฆฌ์†Œ์Šค๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. + +๋‹ค์„ฏ๋ฒˆ์งธ, Spring ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, Ruby ์ฑ„ํŒ… ์„œ๋ฒ„, AI ์„œ๋ฒ„ ์•ž๋‹จ์—์„œ JWT ํ† ํฐ Authentication์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๋Š” Gateway๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋กœ์จ, ๊ฐ๊ฐ ์„œ๋ฒ„์—์„œ ํ† ํฐ ํ™•์ธ ๋กœ์ง์„ ๋งŒ๋“ค ํ•„์š” ์—†์ด ๊ฒŒ์ดํŠธ์›จ์ด์—์„œ ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ, Gateway์—์„œ Redis๋ฅผ ์ด์šฉํ•˜์—ฌ Token Bucket Algorithm์„ ๊ตฌํ˜„ํ•˜์—ฌ API Rate Limiter๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋กœ์จ, ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ํ•œ๋ฒˆ์— ๋งŽ์€ ์š”์ฒญ์„ ์šฐ๋ฆฌ ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. + +๋งˆ์ง€๋ง‰์œผ๋กœ, Spring ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์— Redis๋ฅผ ์ ์šฉํ•˜์˜€๋‹ค. ์ด๋Š” Redis๋ฅผ ์ด์šฉํ•˜์—ฌ Refresh Token์„ ์ €์žฅํ•˜๊ณ , ๋กœ๊ทธ์•„์›ƒ์‹œ Access Token์„ Banํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ, ๊ธ€์˜ ์ถ”์ฒœ์ˆ˜๋‚˜ ์กฐํšŒ์ˆ˜ ๋“ฑ์—์„œ Race Condition์„ ์˜ˆ๋ฐฉํ•˜๋Š” ์šฉ๋„๋กœ๋„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์›๋ž˜ ๊ณง๋ฐ”๋กœ ์šฐ๋ฆฌ RDB์— ์ €์žฅํ•˜๋ฉด ๋™์‹œ์— ๋งŽ์€ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ Race Condition์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋งค์šฐ ๋†’์•˜๋‹ค. ํ•˜์ง€๋งŒ, Redis๋Š” ์‹ฑ๊ธ€์Šค๋ ˆ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์—, Redis์— ์ž„์‹œ๋กœ ์กฐํšŒ์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ณ , ์ฃผ๊ธฐ์ ์œผ๋กœ Scheduler๋ฅผ ์ด์šฉํ•ด ์ง„์งœ ์šฐ๋ฆฌ ์„œ๋น„์Šค RDB์— ๋ฐ˜์˜ํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, Redis๋ฅผ ์ด์šฉํ•ด Cache๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋งค๋ฒˆ ๋˜‘๊ฐ™์€ ์ •๋ณด๋ฅผ RDB์— ์ ‘๊ทผํ•˜์—ฌ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ์€ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ํฌ๋ฏ€๋กœ Redis์— Caching์„ ํ•จ์œผ๋กœ์จ Response Time์„ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค. + +
+ +## **ํ˜„์‹ค์  ์ œํ•œ ๋ฐ ๊ฐœ์„  ํ•„์š” ์‚ฌํ•ญ** + +
+ +### **๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ฐœ์„ ** + +ํ˜„์žฌ ๊ฒŒ์‹œ๋ฌผ ๋ฐ ๊ณต์ง€์‚ฌํ•ญ ๊ฒ€์ƒ‰์€ ์ œ๋ชฉ์œผ๋กœ๋งŒ ๊ฒ€์ƒ‰๋œ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ณด๋‹ค ์ œ๋ชฉ + ๋‚ด์šฉ์œผ๋กœ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ SQL Like ์—ฐ์‚ฐ์ž๋กœ ๋‚ด์šฉ๊นŒ์ง€ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜๊ธด ํ•˜๋‚˜, Like ์—ฐ์‚ฐ์ž๋Š” ์„ ํ˜• ์—ฐ์‚ฐ์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ElasticSearch๋ฅผ ์ด์šฉํ•ด์„œ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ElasticSearch๋Š” ๋ถ„์‚ฐ ๊ฒ€์ƒ‰ ๋ฐ ๋ถ„์„ ์—”์ง„์œผ๋กœ, ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์—์„œ ๋น ๋ฅธ ์ „์ฒด ํ…์ŠคํŠธ ๊ฒ€์ƒ‰์„ ์ง€์›ํ•œ๋‹ค. ์ถ”ํ›„, ElasticSearch๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ RDB์— ์ €์žฅ๋œ ๊ธ€๋“ค์„ ์—ญ์ƒ‰์ธํ•˜์—ฌ ๊ฑฐ์˜ ์‹ค์‹œ๊ฐ„์— ๊ฐ€๊น๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋„๋ก ๊ฐœ์„ ํ•  ๊ฒƒ์ด๋‹ค. + +
+ +### **ํ…Œ์ŠคํŠธ ์ง„ํ–‰ ๊ฐ•ํ™”** + +ํ˜„์žฌ ์‹œ๊ฐ„ ๋ถ€์กฑ์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์ผ๋ถ€ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•ด์„œ๋งŒ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๊ฐ€ ์ง„ํ–‰๋˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ๊ฐ Domain์— ๋Œ€ํ•˜์—ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์ง„ํ–‰๋  ํ•„์š”์„ฑ์ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋”์šฑ ์ด์ƒ์ ์ธ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์ด ์ง„ํ–‰๋˜๊ธฐ ์œ„ํ•ด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๊ฐ•ํ™”ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. + +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/3a40cdfd-ce4f-4716-a9c0-06d96cafbfc5) + +๋˜ํ•œ, ํ˜„์žฌ๋Š” ์šฐ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ Code Coverage๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ๊ทธ๋ž˜์„œ, ์šฐ๋ฆฌ๊ฐ€ ๋ฏธ์ณ ์ƒ๊ฐํ•˜์ง€ ๋ชปํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋‚˜ Domain์— ๋Œ€ํ•˜์—ฌ ๋ˆ„๋ฝ๋  ๊ฐ€๋Šฅ์„ฑ๋„ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ถ”ํ›„ ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด Jacoco๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์šฐ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ Code Coverage๋ฅผ ์ธก์ •ํ•˜๊ณ , html๋กœ ๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํŒ€์›๋“ค์ด ์‰ฝ๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ถ”์ถœํ•  ๊ฒƒ์ด๋‹ค. + +
+ +### **์ธ์Šคํ„ด์Šค ์„ฑ๋Šฅ์˜ ํ•œ๊ณ„** + + + +ํ˜„์žฌ ์šฐ๋ฆฌ์„œ๋ฒ„๋Š” Free Tier๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๋Š” ํŠธ๋ž˜ํ”ฝ์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ˆ˜์ง์  ํ™•์žฅ ๋˜๋Š” ์ˆ˜ํ‰์  ํ™•์žฅ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ Free Tier ์„ฑ๋Šฅ์ด ์•„๋‹Œ ๋” ์ข‹์€ EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๊ฐœ์˜ EC2๋ฅผ ๋งŒ๋“ค์–ด Load Balancing์„ ์ ์šฉํ•˜์—ฌ Scale Out์ ์ธ ํ™•์žฅ์„ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ๋Š” ๋น„์šฉ์ ์ธ ๋ฌธ์ œ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด๋‹ค. + +
+ +### **๋ฌด์ค‘๋‹จ ๋ฐฐํฌ** + +ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Git Actions๋ฅผ ์ด์šฉํ•œ CI/CD๋ฅผ ์ ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ ๋ฐฉ์‹์€ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ๋ฐฐํฌ๋  ๋•Œ ๋ฐ˜๋“œ์‹œ 1~2๋ถ„์ •๋„ ์„œ๋ฒ„๊ฐ€ ์ค‘๋‹จ๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ์ด๋Š”, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค. + +
+ + + +
+ +๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹์œผ๋กœ๋Š” Rolling, Canary, Blue-Green ๋ฐฐํฌ ๋ฐฉ์‹ ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ, ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•œ๋‹ค๋ฉด Blue-Green ๋ฐฐํฌ ๋ฐฉ์‹์„ ์ ์šฉํ•  ๊ฒƒ์ด๋‹ค. Blue-Green์ด๋ž€ ๋‘ ๊ฐœ์˜ ๋™์ผํ•œ ํ™˜๊ฒฝ์ธ "๋ธ”๋ฃจ"์™€ "๊ทธ๋ฆฐ"์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ๋กค๋ฐฑํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๋ธ”๋ฃจ ๊ทธ๋ฆฐ ๋ฐฐํฌ ๋ฐฉ์‹์€ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹์ด๋ฉฐ, ์นด๋‚˜๋ฆฌ์— ๋น„ํ•ด์„œ ๊ตฌํ˜„๋„ ๊ฐ„๋‹จํ•˜๊ณ , Rolling ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ •์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์ถ”ํ›„ Blue-Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•  ๊ณ„ํš์ด๋‹ค. + +
+ +### **๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ** + +์„œ๋ฒ„๋ฅผ ๋‹จ์ผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด, ๋งŽ์€ ํŠธ๋ž˜ํ”ฝ์ด ๋ชฐ๋ ธ์„ ๋•Œ ๊ฐ๋‹นํ•˜๊ธฐ ํž˜๋“ค ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์„œ๋ฒ„๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋งŒ๋“ค๊ณ , Load Balacningํ•˜๋Š”๊ฒŒ ์ข‹์€ ์„ ํƒ์ง€์ผ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ํ•˜๊ธฐ์—” ๋น„์šฉ์ ์ธ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด ์–ด๋ ต๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์—ฌ๋Ÿฌ๊ฐœ์˜ EC2๋ฅผ ๋„์›Œ์•ผ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ์–ด์ฉ” ์ˆ˜ ์—†์ด ๋‹จ์ผ ์„œ๋ฒ„๋กœ ์šด์˜ํ•˜๊ฒŒ ๋˜์—ˆ์ง€๋งŒ, ์ถ”ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ์ ์šฉํ•  ๊ณ„ํš์ด๋‹ค. + +
+ +### **๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น… ๊ฐ•ํ™”** + + + +์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ „๋ฐ˜์ ์ธ ๋กœ๊น…์„ ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค. ํ˜„์žฌ ๋กœ๊น…์€ Nginx ๋‹จ์—์„œ ํŒŒ์ผ ์ €์žฅ์œผ๋กœ ํ•œ๋ฒˆ, API Gateway๋‹จ์—์„œ ์š”์ฒญ url, body, Authorization์œผ๋กœ ํ•œ๋ฒˆ, ๊ฐ MSA ์„œ๋ฒ„ ๋‹จ์—์„œ ํ•œ๋ฒˆ ์ด๋ฃจ์–ด์ง„๋‹ค. ํ•˜์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์—์„œ์˜ ๋กœ๊ทธ๋Š” ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ปจํ…Œ์ด๋„ˆ์— ์ ‘๊ทผํ•ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ์ฐพ์•„๋ด์•ผํ•œ๋‹ค. ๋˜ํ•œ, MSA ๊ตฌ์กฐ๋‹ค ๋ณด๋‹ˆ๊น, ๊ฐ๊ฐ ์ปจํ…Œ์ด๋„ˆ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ ๋ด์•ผํ•œ๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฐ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํŒŒ์•…ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์„ ๋” ์จ์„œ ์„œ๋น„์Šค์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Kafka + ELK Stack์„ ํ†ตํ•˜์—ฌ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ELK Stack์œผ๋กœ ์ „์†กํ•˜๊ณ , ์ด๋ฅผ ๋ถ„์„ํ•œ ํ›„, ๋Œ€์‹œ๋ณด๋“œ ํ˜•ํƒœ์˜ ์‹œ๊ฐ์ ์ธ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์‹œ์Šคํ…œ์ด ๊ตฌ์ถ•๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. + + + +๋˜ํ•œ, ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•  ๋•Œ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์„ฑ๋Šฅ ๊ด€๋ฆฌ๋Š” ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค. ์šฐ๋ฆฌ๊ฐ€ 24์‹œ๊ฐ„ 365์ผ ์ปดํ“จํ„ฐ ์•ž์— ์ƒ์ฃผํ•˜์—ฌ ์„œ๋ฒ„ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Prometheus + Grafana๋‚˜ Datadog ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•˜๋Š” ํ™˜๊ฒฝ์€ ํ•„์ˆ˜์ ์ด๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด๋‘๋ฉด ์„œ๋ฒ„์˜ CPU๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ธ‰์ฆ ํ–ˆ์„ ๋•Œ, ์šฐ๋ฆฌ ํŒ€ Slack์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ „์†กํ•˜๊ณ  ๋Œ€์‹œ๋ณด๋“œ๋กœ ํ˜„ํ™ฉ์„ ์‰ฝ๊ฒŒ ํ˜„ํ™ฉ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. + +
+ +### **์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜** + + + +ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” MSA ๊ตฌ์กฐ์— ๊ฐ€๊นŒ์šด ํ˜•ํƒœ์—ฌ์„œ ์ˆ˜๋งŽ์€ Container๋“ค์ด ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค. ๋Œ์•„๊ฐ€๋Š” Container๋งŒ ์„ธ๋ณด๋”๋ผ๋„, Redis, Spring, Spring Cloud Gateway, Ruby On Rails, Nginx, Certbot, FastAPI ๋ฒŒ์จ 7๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์— ์•ž์„œ ์–ธ๊ธ‰ํ•œ Kafka, Grafana, Prometheus, DB Replication, ELK Stack ๋“ฑ ๊นŒ์ง€ ์ ์šฉํ•˜๋ฉด ์ˆ˜๋งŽ์€ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋Œ์•„๊ฐˆ ๊ฒƒ์ด๋‹ค. ๊ฑฐ๊ธฐ์— ๋กœ๋“œ ๋ฐธ๋ฆฐ์„ฑ๊นŒ์ง€ ์ ์šฉํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ด ์ปจํ…Œ์ด๋„ˆ๋“ค์„ ํ•œ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋ฌด๋ฆฌ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, EKS, Docker Swarm, Kubernetes๊ฐ™์€ ์ž๋™์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์žฅ์•  ๋ณต๊ตฌ๋ฅผ ๋„์™€์ฃผ๋Š” ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค. + + diff --git a/back/README.md b/back/README.md deleted file mode 100644 index 91925ccffe..0000000000 --- a/back/README.md +++ /dev/null @@ -1,173 +0,0 @@ -### ๊ธฐ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ - -### ์„ฑ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ - -#### ์บ์‹ฑ - -์œ ์ €๋“ค์ด ์ž์ฃผ ์ ‘๊ทผํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์บ์‹ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. -์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ๊ณต์ง€์‚ฌํ•ญ ๋ชฉ๋ก์ด๋‚˜ Q&A ๋ชฉ๋ก์ธ ๊ฒฝ์šฐ, ์ž์ฃผ ๋ฐ”๋€Œ์ง€๋„ ์•Š์„ํ…๋ฐ ์œ ์ €๋“ค์ด ํ•ด๋‹น ๊ฒŒ์‹œํŒ์— ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ RDB์—์„œ ๋ชฉ๋ก์„ ์ฝ์–ด์˜ค๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด๋‹ค. -๋”ฐ๋ผ์„œ, Redis๊ฐ™์€ ๊ณณ์— ์บ์‹ฑํ•ด๋‘๊ณ , ๋˜‘๊ฐ™์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋น ๋ฅด๊ฒŒ Redis์—์„œ ๋ฝ‘์•„๊ฐ€๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. -์บ์‹ฑ ์ „๋žต์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. - -์บ์‹ฑ ์ „๋žต์€ Look Aside์™€ Write Around ์ „๋žต์˜ ์กฐํ•ฉ์„ ํƒํ•˜์˜€๋‹ค. -Read ์ „๋žต์€ Look Aside ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ์šฐ์„ ์ ์œผ๋กœ Redis์—์„œ ์ฝ์–ด์˜ค๊ณ , cache miss๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ, RDS์—์„œ ์ฝ์–ด์˜ค๊ณ  ๋‹ค์‹œ redis์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. -Write ์ „๋žต์€ Write Around ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ๋•Œ, Redis์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๋ฐ˜๋“œ์‹œ RDB์— ์ €์žฅํ•˜๊ณ  ๊ธฐ์กด ์บ์‹œ๋Š” ๋ฌดํšจํ™”ํ•˜๋Š” ์ „๋žต์ด๋‹ค. - -์‹ค์ œ ์บ์‹ฑ์„ ์ ์šฉํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด๋ณด์•˜๋‹ค. - -์บ์‹ฑ ์ „์—๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ 414ms๊ฐ€ ๊ฑธ๋ ธ๋‹ค. - - - -ํ•˜์ง€๋งŒ ์บ์‹ฑ ํ›„์—๋Š” 12ms๋กœ ๋‹จ์ถ•๋˜์—ˆ๋‹ค. - - - -๊ทธ๋Ÿฐ๋ฐ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉด, class ์ •๋ณด์— ๋Œ€ํ•œ field๊ฐ€ json์— ์ถ”๊ฐ€๋˜๋Š” ํ˜„์ƒ์ด ์žˆ์–ด์„œ ์ถ”๊ฐ€ ๋ณด๋ฅ˜๋ฅผ ํ•˜์˜€๋‹ค. - -
- -#### ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ - -๋ชจ๋“  ์š”์ฒญ์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๋‹ค. ํŠนํžˆ, ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜, ๋ฒˆ์—ญ API ๊ฐ™์€ ๋‹ค๋ฅธ API์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ฑฐ๋‚˜, ํฌ๋กค๋ง ๋“ฑ์€ ๋งค์šฐ ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์ด๋‹ค. ์ด ๋ชจ๋“  ์ž‘์—…์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด, ์„ฑ๋Šฅ์— ๋งค์šฐ ์น˜๋ช…์ ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Async๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์‹ค์‹œํ•˜์˜€๋‹ค. - -์ด ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ Spring์—์„œ ThreadExecutor๋ฅผ ์ด์šฉํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ๊ธฐ๋ณธ Async Executor SimpleAsyncTaskExecutor์ธ๋ฐ, ์ด๋Š” ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„, ์„ฑ๋Šฅ ์ €ํ•˜, ์Šค์ผ€์ผ๋ง ๋ฌธ์ œ ๋“ฑ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด Thread Pool ๋ฐฉ์‹ Executor๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ์Šค๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ์„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํ–‰์‹œ๊ฐ„์ด ์งง์€ ๋งŽ์€ ๋Ÿ‰์˜ Task๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ถˆ๋ฆฌํ•˜๋‹ค. ๋ฆฌ์†Œ์Šค ์ธก๋ฉด์—์„œ๋Š” ๊ฐ ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ๋™์‹œ์— ๋งŽ์€ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์š”์ฒญ๋˜๋ฉด ๋งค๋ฒˆ ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ๋”ฐ๋ผ์„œ CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ฆฌ์†Œ์Šค์˜ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ณผ๋„ํ•˜๊ฒŒ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฉด์—์„œ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์†Œ๋ฉธ์‹œํ‚ค๋Š” ๋ฐ๋Š” ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋ฆฌ์†Œ์Šค๊ฐ€ ์†Œ์š”๋œ๋‹ค. ๊ฐ ์ž‘์—…๋งˆ๋‹ค ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ด๋Ÿฐ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๊ณ , ์ „์ฒด์ ์ธ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ผ์น  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, SimpleAsyncTaskExecutor๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜์— ๋Œ€ํ•œ ์ œํ•œ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋™์‹œ์— ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ •์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์—†๋Š” ์ˆ˜์ค€์œผ๋กœ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๊ณง OutOfMemoryError ๋“ฑ์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. - - - -๊ทธ๋ž˜์„œ, ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด Async์˜ Executor๋ฅผ ThreadPool๋ฐฉ์‹์˜ Executor๋กœ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋Š”, ์‚ฌ์šฉํ•  ์Šค๋ ˆ๋“œ ํ’€์— ์†ํ•œ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ˆ˜์ธ corepoolsize, corepoolsize๊ฐ€ ๊ฐ€๋“ ์ฐฌ ์ƒํƒœ์—์„œ ๋”์ด์ƒ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ• ๋•Œ, ๋Œ€๊ธฐํ•˜๋Š” ์žฅ์†Œ ํฌ๊ธฐ์ธ queuecapacity, ์Šค๋ ˆ๋“œ ํ’€์ด ํ™•์žฅ๋  ์ˆ˜ ์žˆ๋Š” ์Šค๋ ˆ๋“œ์˜ ์ƒํ•œ์„ , ์ฆ‰ ์Šค๋ ˆ๋“œ ์ˆ˜์˜ ์ƒํ•œ์„ ์ธ maxpoolsize๋ฅผ ์กฐ์ •ํ•˜์—ฌ ThreadPool๋ฐฉ์‹์˜ Async Executor๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค. - -#### Race Condition ํ•ด๊ฒฐ - -ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ถ”์ฒœ / ์ถ”์ฒœ ํ•ด์ œ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ถ”์ฒœ์ˆ˜, ์กฐํšŒ์ˆ˜์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ํ•˜๋‚˜์˜ ํ•„๋“œ์— ๋Œ€ํ•ด ์ˆ˜๋งŽ์€ ํŠธ๋žœ์ ์…˜ ์š”์ฒญ์ด ์˜ค๊ฐ€๋Š” ๊ธฐ๋Šฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒฝ์šฐ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ Race Condition, ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ Redis๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. Redis๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ์ž์›์— ๋Œ€ํ•œ Race Condition ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. Redis์— ์š”์ฒญ์„ ๋ณด๋‚ผ countTemplate๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ด์„œ Redis์— ๊ฐ์ฒด์™€ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ณ  ์ผ์ •์‹œ๊ฐ„๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ Cache์— ์ €์žฅ๋œ ๋‚ด์šฉ์„ DB์— ๋ฐ˜์˜์‹œํ‚ค๋„๋ก ํ•˜์˜€๋‹ค. ์ด๋ ‡๊ฒŒ Redis์— ์ €์žฅ๋œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ 30์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ DB์— ๋ฐ˜์˜๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. - -ํ˜„์žฌ ๋ฐฉ๋ฒ•๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ๋‚ด๊ฐ€ ์ถ”์ฒœํ•œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๊ฐ€ ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์–ด ๋ณด์—ฌ์ง€์ง€๋Š” ์•Š๋Š”๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ฌ๋•Œ๋Š” ์บ์‹œ์—์„œ ์ฐพ๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ถ”ํ›„์—๋Š” ์บ์‹œ์—์„œ ๊ฐ’์„ ์ฐพ๋„๋ก ๊ตฌํ˜„ํ•˜๊ณ  Redis Lock๊ณผ Redis Trasaction์„ ์‚ฌ์šฉํ•œ ๊ด€๋ฆฌ๋‚˜ RDS์˜ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋ฅผ ์กฐ์ ˆํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๊ณ ๋ คํ•ด๋ด์•ผ ํ•œ๋‹ค. - -#### N+1 ๋ฌธ์ œ - -์—ฌ๋Ÿฌ ์ฐธ์กฐ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ด๋“ค์„ Joinํ•˜์—ฌ ํ˜ธ์ถœํ•˜๋Š” ์ƒํ™ฉ์ด ๋งŽ์ด ์žˆ์—ˆ์œผ๋ฉฐ ์ด๋•Œ N+1๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Fetch Join์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Projection ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์˜ Inner Join๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ฒฝ์šฐ์˜ N+1์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋ชจ์ƒ‰ํ•˜์—ฌ ์ ์šฉํ•˜์˜€๋‹ค. ์ด๋กœ ์ธํ•ด ์„œ๋ฒ„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ์„ฑ๋Šฅํ–ฅ์ƒ์ด ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ ์‹œํ–‰ ์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ฐฉ์‹์€ Projection์„ ์‚ฌ์šฉํ•˜์—ฌ DTO๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ JPA ๊ฒ€์ƒ‰์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋Š”๋ฐ ์ด ๊ฒฝ์šฐ Fetch Join์ด๋‚˜ EntityGraph๋กœ๋Š” ํ•ด๊ฒฐ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. @Projection์„ ์‚ฌ์šฉํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ๋„ ์žˆ์œผ๋‚˜ ์ข€ ๋” ๊ณ ๋ฏผํ•ด๋ณธ ๊ฒฐ๊ณผ Join์˜ ๋ชฉ์ ์—์„œ N+1๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•  ํ•„๋“œ๊ฐ’์ด ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด InnerJoin์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ํ•ด๊ฒฐ๋˜๋Š” ๋ฒ”์ฃผ์˜€๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜๋‹ค. - -### ์•ˆ์ •์„ฑ ๊ณ ๋ ค ์‚ฌํ•ญ - -
- -#### ์‚ฌ์šฉ์ž ํŠธ๋ž˜ํ”ฝ ์ œํ•œ - - - - - -์‹ค์ œ๋กœ ์œ„์— ์‚ฌ์ง„์ฒ˜๋Ÿผ - - -#### ์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ํŒŒ์ผ ์šฉ๋Ÿ‰ ์ œํ•œ - -
- -### ๋ณด์•ˆ์  ๊ณ ๋ ค ์‚ฌํ•ญ - -
- -#### ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” - -#### JWT๋ฅผ ํ†ตํ•œ Authentication - -#### Token ํƒˆ์ทจ ์ƒํ™ฉ ์˜ˆ๋ฐฉ - -#### AccessToken Ban - -#### HMAC - -#### ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ ์šฉ - -
- -### ํ…Œ์ŠคํŠธ ๊ณ ๋ ค ์‚ฌํ•ญ - -#### Test Container - -Test Containers๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ž๋ฐ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, Docker ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ๋ฉ”์‹œ์ง• ํ, ์›น ์„œ๋ฒ„ ๋“ฑ์˜ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์†์‰ฝ๊ฒŒ ์„ค์ •ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ…Œ์ŠคํŠธ์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ์žฌํ˜„์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค. ์ฃผ์š” ํŠน์ง•์œผ๋กœ๋Š” ์ฝ”๋“œ ๋‚ด์—์„œ ์ง์ ‘ Docker ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” API ์ œ๊ณต, ๋‹ค์–‘ํ•œ ํ”„๋ฆฌ์…‹๊ณผ ์œ ์—ฐํ•œ ๊ตฌ์„ฑ ์˜ต์…˜, ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ ํ•„์š”ํ•œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ž๋™์œผ๋กœ ์‹œ์ž‘ํ•˜๊ณ  ์ข…๋ฃŒํ•˜๋Š” ๊ธฐ๋Šฅ, ๊ฐ์ข… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(MySQL, PostgreSQL ๋“ฑ)์™€์˜ ํ†ตํ•ฉ ์ง€์›, ๊ทธ๋ฆฌ๊ณ  ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ™•์žฅ์„ฑ ๋“ฑ์ด ์žˆ๋‹ค. Test Containers๋Š” JUnit๊ณผ ๊ฐ™์€ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์™€๋„ ํ†ตํ•ฉ๋˜์–ด, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์‰ฝ๊ณ  ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ๋„ ์žˆ๋‹ค. - -์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ ์„œ๋น„์Šคํ•˜๋Š” RDS๋ฅผ ๊ฐ€์ง€๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค๋ฉด, ๋งค์šฐ ์œ„ํ—˜ํ•  ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Test Container๋ฅผ ์ด์šฉํ•˜์—ฌ ๋…๋ฆฝ๋œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค. - -#### Jacoco - -JaCoCo๋Š” ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์ธก์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์˜คํ”ˆ ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์†Œ์Šค ์ฝ”๋“œ์—์„œ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ๋ถ€๋ถ„์„ ์‹คํ–‰ํ•˜๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ง€ํ‘œ๋กœ, ์†Œํ”„ํŠธ์›จ์–ด ํ…Œ์ŠคํŠธ์˜ ํ’ˆ์งˆ์„ ํ‰๊ฐ€ํ•˜๋Š” ์ค‘์š”ํ•œ ์š”์†Œ์ด๋‹ค. JaCoCo๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ํ…Œ์ŠคํŠธ์˜ ํšจ๊ณผ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜์—ฌ ๋ณด๋‹ค ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค. - -Jacoco๋ฅผ ์ด์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์ธก์ •ํ•˜๊ณ , ์ด๋ฅผ html๋กœ ์ถ”์ถœํ•ด์„œ ํŒ€์›๋“ค์ด ์‰ฝ๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํƒํ•˜์˜€๋‹ค. - -#### Apache Jmeter - -Apache JMeter๋Š” ์ž๋ฐ” ๊ธฐ๋ฐ˜์˜ ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ์–ด๋กœ, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ๋‹ค์–‘ํ•œ ์„œ๋น„์Šค์˜ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์™€ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. JMeter๋Š” ์ดˆ๊ธฐ์—๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœ๋˜์—ˆ์œผ๋‚˜, ํ˜„์žฌ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, FTP ์„œ๋ฒ„, ์›น ์„œ๋น„์Šค, JMS, LDAP, SMTP, POP3, IMAP ์„œ๋ฒ„ ๋“ฑ ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ ๋˜ํ•œ ์ง€์›ํ•œ๋‹ค. - -ํ”„๋กœ์ ํŠธ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ…Œ์ŠคํŠธ์™€ ๊ฐ™์ด ๋Œ€๊ทœ๋ชจ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. - -#### JUnits - -JUnit์€ ์ž๋ฐ” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์œ„ํ•œ ์˜คํ”ˆ ์†Œ์Šค ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์†Œํ”„ํŠธ์›จ์–ด ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ํ™”ํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. JUnit์€ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(TDD)์„ ์ด‰์ง„ํ•˜๋ฉฐ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•˜๋‹ค. ์ฃผ์š” ๊ธฐ๋Šฅ์œผ๋กœ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ ๊ฐ„ํŽธํ•œ ํ…Œ์ŠคํŠธ ์ •์˜, Test Suite์„ ํ†ตํ•œ ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ์˜ ๊ทธ๋ฃนํ™”, ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๊ฒ€์ฆ, ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ์˜ˆ์™ธ ์ƒํ™ฉ ์ฒ˜๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋ฆฌํฌํŠธ ๊ธฐ๋Šฅ ๋“ฑ์ด ์žˆ๋‹ค. JUnit์€ ๋ชจ๋“ˆํ™”๋œ ์ฝ”๋“œ ํ…Œ์ŠคํŠธ๋ฅผ ์žฅ๋ คํ•˜๋ฉฐ, ์ง€์†์  ํ†ตํ•ฉ(CI) ๋„๊ตฌ์™€์˜ ํ†ตํ•ฉ์„ ํ†ตํ•ด ์ž๋™ํ™”๋œ ๋นŒ๋“œ ํ”„๋กœ์„ธ์Šค์—์„œ๋„ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ค. ์ง๊ด€์ ์ด๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด API ๋•๋ถ„์— ๊ฐœ๋ฐœ์ž๋“ค์ด ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค. - -
- -### ํ˜„์‹ค์  ์ œํ•œ ๋ฐ ๊ฐœ์„  ํ•„์š” ์‚ฌํ•ญ - -
- -#### ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ฐœ์„  - -ํ˜„์žฌ ๊ฒŒ์‹œ๋ฌผ ๋ฐ ๊ณต์ง€์‚ฌํ•ญ ๊ฒ€์ƒ‰์€ ์ œ๋ชฉ์œผ๋กœ๋งŒ ๊ฒ€์ƒ‰๋œ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ณด๋‹ค ์ œ๋ชฉ + ๋‚ด์šฉ์œผ๋กœ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ SQL Like ์—ฐ์‚ฐ์ž๋กœ ๋‚ด์šฉ๊นŒ์ง€ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜๊ธด ํ•˜๋‚˜, Like ์—ฐ์‚ฐ์ž๋Š” ์„ ํ˜• ์—ฐ์‚ฐ์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ElasticSearch๋ฅผ ์ด์šฉํ•ด์„œ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ElasticSearch๋Š” ๋ถ„์‚ฐ ๊ฒ€์ƒ‰ ๋ฐ ๋ถ„์„ ์—”์ง„์œผ๋กœ, ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์—์„œ ๋น ๋ฅธ ์ „์ฒด ํ…์ŠคํŠธ ๊ฒ€์ƒ‰์„ ์ง€์›ํ•œ๋‹ค. ์ถ”ํ›„, ElasticSearch๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ RDB์— ์ €์žฅ๋œ ๊ธ€๋“ค์„ ์—ญ์ƒ‰์ธํ•˜์—ฌ ๊ฑฐ์˜ ์‹ค์‹œ๊ฐ„์— ๊ฐ€๊น๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋„๋ก ๊ฐœ์„ ํ•  ๊ฒƒ์ด๋‹ค. - -
- -#### ์ธ์Šคํ„ด์Šค ์„ฑ๋Šฅ์˜ ํ•œ๊ณ„ - - - -ํ˜„์žฌ ์šฐ๋ฆฌ์„œ๋ฒ„๋Š” Free Tier๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๋Š” ํŠธ๋ž˜ํ”ฝ์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ˆ˜์ง์  ํ™•์žฅ ๋˜๋Š” ์ˆ˜ํ‰์  ํ™•์žฅ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ Free Tier ์„ฑ๋Šฅ์ด ์•„๋‹Œ ๋” ์ข‹์€ EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๊ฐœ์˜ EC2๋ฅผ ๋งŒ๋“ค์–ด Load Balancing์„ ์ ์šฉํ•˜์—ฌ Scale Out์ ์ธ ํ™•์žฅ์„ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ๋Š” ๋น„์šฉ์ ์ธ ๋ฌธ์ œ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด๋‹ค. - -
- -#### ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - -ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Git Actions๋ฅผ ์ด์šฉํ•œ CI/CD๋ฅผ ์ ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ ๋ฐฉ์‹์€ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ๋ฐฐํฌ๋  ๋•Œ ๋ฐ˜๋“œ์‹œ 1~2๋ถ„์ •๋„ ์„œ๋ฒ„๊ฐ€ ์ค‘๋‹จ๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ์ด๋Š”, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค. - -
- - - -
- -๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹์œผ๋กœ๋Š” Rolling, Canary, Blue-Green ๋ฐฐํฌ ๋ฐฉ์‹ ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ, ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•œ๋‹ค๋ฉด Blue-Green ๋ฐฐํฌ ๋ฐฉ์‹์„ ์ ์šฉํ•  ๊ฒƒ์ด๋‹ค. Blue-Green์ด๋ž€ ๋‘ ๊ฐœ์˜ ๋™์ผํ•œ ํ™˜๊ฒฝ์ธ "๋ธ”๋ฃจ"์™€ "๊ทธ๋ฆฐ"์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ๋กค๋ฐฑํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๋ธ”๋ฃจ ๊ทธ๋ฆฐ ๋ฐฐํฌ ๋ฐฉ์‹์€ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹์ด๋ฉฐ, ์นด๋‚˜๋ฆฌ์— ๋น„ํ•ด์„œ ๊ตฌํ˜„๋„ ๊ฐ„๋‹จํ•˜๊ณ , Rolling ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ •์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์ถ”ํ›„ Blue-Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•  ๊ณ„ํš์ด๋‹ค. - -
- -#### ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ - -์„œ๋ฒ„๋ฅผ ๋‹จ์ผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด, ๋งŽ์€ ํŠธ๋ž˜ํ”ฝ์ด ๋ชฐ๋ ธ์„ ๋•Œ ๊ฐ๋‹นํ•˜๊ธฐ ํž˜๋“ค ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์„œ๋ฒ„๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋งŒ๋“ค๊ณ , Load Balacningํ•˜๋Š”๊ฒŒ ์ข‹์€ ์„ ํƒ์ง€์ผ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ํ•˜๊ธฐ์—” ๋น„์šฉ์ ์ธ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด ์–ด๋ ต๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์—ฌ๋Ÿฌ๊ฐœ์˜ EC2๋ฅผ ๋„์›Œ์•ผ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ์–ด์ฉ” ์ˆ˜ ์—†์ด ๋‹จ์ผ ์„œ๋ฒ„๋กœ ์šด์˜ํ•˜๊ฒŒ ๋˜์—ˆ์ง€๋งŒ, ์ถ”ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ์ ์šฉํ•  ๊ณ„ํš์ด๋‹ค. - -
- -#### ์บ์‹ฑ - -์ด๋ฏธ์ง€๋ฅผ ๋ฐ›์•„์˜ค๋Š” S3๋„ ์บ์‹ฑ์„ ๋ฌด์กฐ๊ฑด ํ•ด์•ผํ•œ๋‹ค. ์•„๋‹ˆ๋ฉด S3 ๋น„์šฉ์ด ๊ต‰์žฅํžˆ ๋งŽ์•„์งˆ ๊ฒƒ์ด๊ณ  ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ AWS CloudFront๊ฐ™์€ CDN ์„œ๋ฒ„๋ฅผ ๋‘๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€๋ฐ, ์ด ๋ถ€๋ถ„์€ ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•ด์„œ ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค. - -
- -#### ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น… ๊ฐ•ํ™” - - - -์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ „๋ฐ˜์ ์ธ ๋กœ๊น…์„ ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค. ํ˜„์žฌ ๋กœ๊น…์€ Nginx ๋‹จ์—์„œ ํŒŒ์ผ ์ €์žฅ์œผ๋กœ ํ•œ๋ฒˆ, API Gateway๋‹จ์—์„œ ์š”์ฒญ url, body, Authorization์œผ๋กœ ํ•œ๋ฒˆ, ๊ฐ MSA ์„œ๋ฒ„ ๋‹จ์—์„œ ํ•œ๋ฒˆ ์ด๋ฃจ์–ด์ง„๋‹ค. ํ•˜์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์—์„œ์˜ ๋กœ๊ทธ๋Š” ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ปจํ…Œ์ด๋„ˆ์— ์ ‘๊ทผํ•ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ์ฐพ์•„๋ด์•ผํ•œ๋‹ค. ๋˜ํ•œ, MSA ๊ตฌ์กฐ๋‹ค ๋ณด๋‹ˆ๊น, ๊ฐ๊ฐ ์ปจํ…Œ์ด๋„ˆ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ ๋ด์•ผํ•œ๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฐ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํŒŒ์•…ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์„ ๋” ์จ์„œ ์„œ๋น„์Šค์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Kafka + ELK Stack์„ ํ†ตํ•˜์—ฌ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ELK Stack์œผ๋กœ ์ „์†กํ•˜๊ณ , ์ด๋ฅผ ๋ถ„์„ํ•œ ํ›„, ๋Œ€์‹œ๋ณด๋“œ ํ˜•ํƒœ์˜ ์‹œ๊ฐ์ ์ธ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์‹œ์Šคํ…œ์ด ๊ตฌ์ถ•๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. - - - -๋˜ํ•œ, ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•  ๋•Œ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์„ฑ๋Šฅ ๊ด€๋ฆฌ๋Š” ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค. ์šฐ๋ฆฌ๊ฐ€ 24์‹œ๊ฐ„ 365์ผ ์ปดํ“จํ„ฐ ์•ž์— ์ƒ์ฃผํ•˜์—ฌ ์„œ๋ฒ„ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Prometheus + Grafana๋‚˜ Datadog ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•˜๋Š” ํ™˜๊ฒฝ์€ ํ•„์ˆ˜์ ์ด๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด๋‘๋ฉด ์„œ๋ฒ„์˜ CPU๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ธ‰์ฆ ํ–ˆ์„ ๋•Œ, ์šฐ๋ฆฌ ํŒ€ Slack์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ „์†กํ•˜๊ณ  ๋Œ€์‹œ๋ณด๋“œ๋กœ ํ˜„ํ™ฉ์„ ์‰ฝ๊ฒŒ ํ˜„ํ™ฉ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. - -
- -#### ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜ - - - -ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” MSA ๊ตฌ์กฐ์— ๊ฐ€๊นŒ์šด ํ˜•ํƒœ์—ฌ์„œ ์ˆ˜๋งŽ์€ Container๋“ค์ด ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค. ๋Œ์•„๊ฐ€๋Š” Container๋งŒ ์„ธ๋ณด๋”๋ผ๋„, Redis, Spring, Spring Cloud Gateway, Ruby On Rails, Nginx, Certbot, FastAPI ๋ฒŒ์จ 7๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์— ์•ž์„œ ์–ธ๊ธ‰ํ•œ Kafka, Grafana, Prometheus, DB Replication, ELK Stack ๋“ฑ ๊นŒ์ง€ ์ ์šฉํ•˜๋ฉด ์ˆ˜๋งŽ์€ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋Œ์•„๊ฐˆ ๊ฒƒ์ด๋‹ค. ๊ฑฐ๊ธฐ์— ๋กœ๋“œ ๋ฐธ๋ฆฐ์„ฑ๊นŒ์ง€ ์ ์šฉํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ด ์ปจํ…Œ์ด๋„ˆ๋“ค์„ ํ•œ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋ฌด๋ฆฌ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, EKS, Docker Swarm, Kubernetes๊ฐ™์€ ์ž๋™์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์žฅ์•  ๋ณต๊ตฌ๋ฅผ ๋„์™€์ฃผ๋Š” ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค. - - diff --git a/back/nginx/Dockerfile b/back/nginx/Dockerfile index af622f1c49..a8fe784969 100644 --- a/back/nginx/Dockerfile +++ b/back/nginx/Dockerfile @@ -1,3 +1,6 @@ FROM nginx + +#COPY nginx.conf /etc/nginx/nginx.conf COPY default.conf.template /etc/nginx/conf.d/default.conf.template + ENTRYPOINT ["/bin/bash", "-c", "envsubst '${SERVER_NAME}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"] diff --git a/back/nginx/nginx.conf b/back/nginx/nginx.conf new file mode 100644 index 0000000000..ff2718a376 --- /dev/null +++ b/back/nginx/nginx.conf @@ -0,0 +1,25 @@ +user nginx; +worker_processes auto; + +worker_rlimit_nofile 8192; + +events { + worker_connections 4096; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + + keepalive_timeout 65; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/front/FRONT.md b/front/FRONT.md new file mode 100644 index 0000000000..44121b5e16 --- /dev/null +++ b/front/FRONT.md @@ -0,0 +1,121 @@ +# ๐Ÿ“ฑ์™ธ๊ตญ๋ฏผ ์•ฑ ์„ธ๋ถ€ ํ™”๋ฉด + +![์Šฌ๋ผ์ด๋“œ 1](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/72da3858-516e-4471-a9c8-f86f9283fdff) + +| ![์Šฌ๋ผ์ด๋“œ2](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/2f1bf4df-afe8-4930-bd73-05cee1414f9b) | ![์Šฌ๋ผ์ด๋“œ3](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/92e8b7e8-2c4c-44a7-abfa-90fa4f28aeab) | +| ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| ![์Šฌ๋ผ์ด๋“œ4](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/95f965e4-47c3-41c2-a2c4-6f208d3a2c60) | ![์Šฌ๋ผ์ด๋“œ9](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/da778960-cf69-4480-bbfb-35073f9fae1d) | +| ![์Šฌ๋ผ์ด๋“œ5](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/0c1997ae-ffd3-46d3-ac67-6e248c8f6928) | ![์Šฌ๋ผ์ด๋“œ6](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/76a55bab-0e96-4214-ae63-909a57db2d59) | +| ![์Šฌ๋ผ์ด๋“œ7](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/c95531fe-2463-4bd1-a16c-7215898ab63a) | ![์Šฌ๋ผ์ด๋“œ8](https://github.com/kookmin-sw/capstone-2024-30/assets/55120784/4efeb2be-4745-439f-a722-b477e2cd333d) | + +## ์‚ฌ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ + +### ํ†ต์‹  ๊ด€๋ จ + +์•ฑ ๋‚ด์—์„œ ๋ฐฑ์—”๋“œ์™€์˜ ํ†ต์‹ ์„ ๋•๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +#### http (1.2.1) + +http๋Š” ํ”Œ๋Ÿฌํ„ฐ ์•ฑ์—์„œ HTTP์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. REST API์™€ ํ†ต์‹ ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ์˜ ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +#### http_parser (4.0.2) + +http ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ํŒŒ์‹ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„์™€์˜ http ํ†ต์‹ ์„ ์ง„ํ–‰ํ•  ๋•Œ http ์‘๋‹ต์„ ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ๋ณด์•ˆ ๊ด€๋ จ + +์•ฑ ๋‚ด์—์„œ ๋ณด์•ˆ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +#### flutter_dotenv (5.1.0) + +ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋กœ๋“œํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. .env ํŒŒ์ผ์„ ํ†ตํ•ด ์—”๋“œํฌ์ธํŠธ, ๋น„๋ฐ€ํ‚ค ๋“ฑ์„ ๊ด€๋ฆฌํ•˜์—ฌ ์ฝ”๋“œ ๋‚ด์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +#### crypto (3.0.3) + +์•”ํ˜ธํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ํ•ด์‹œ, HMAC, SHA-256 ๋“ฑ์˜ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ Access Token, Refresh Token, UUID ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### flutter_secure_storage (9.0.0) + +ํ”Œ๋Ÿฌํ„ฐ ๋‚ด ์•ฑ ๋‚ด๋ถ€์—์„œ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์•”ํ˜ธํ™”๋œ ์ €์žฅ์†Œ๋ฅผ ์ œ๊ณตํ•˜๊ณ , ๋น„๋ฐ€๋ฒˆํ˜ธ, ํ† ํฐ ๋“ฑ์˜ ๋ณด์•ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ OS๋ณ„๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ๋ณด์•ˆ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ Android ๋ฃจํŒ… ๋˜๋Š” iOS ํƒˆ์˜ฅ์„ ํ•˜๋”๋ผ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +### ๊ณ„์ • ๊ด€๋ จ + +์•ฑ ๋‚ด์—์„œ ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ์„ ์ง„ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +#### firebase_core (2.27.1) + +ํ”Œ๋Ÿฌํ„ฐ ์•ฑ์—์„œ ํŒŒ์ด์–ด๋ฒ ์ด์Šค ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +#### firebase_auth (4.17.9) + +ํ”Œ๋Ÿฌํ„ฐ ์•ฑ์—์„œ ํŒŒ์ด์–ด๋ฒ ์ด์Šค ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +### ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ด€๋ จ + +์•ฑ ๋‚ด์—์„œ ์ฃผ์š” ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +#### easy_localization (3.0.5) + +์•ฑ์— ์žˆ๋Š” ์–ธ์–ด๋ฅผ ๋‹ค๊ตญ์–ด๋กœ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. json ํŒŒ์ผ์„ ํ†ตํ•ด ๋ฒˆ์—ญ ๋ฌธ์ž์—ด์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์‚ฌ์šฉ์ž์˜ ์–ธ์–ด ์„ค์ •์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ๋ฒˆ์—ญ๋œ ๋ฌธ์ž์—ด์„ ํ‘œ์‹œํ•ด์ค๋‹ˆ๋‹ค. + +#### restart_app (1.2.1) + +์•ฑ์„ ์ข…๋ฃŒํ•˜๊ณ  ๋‹ค์‹œ ์‹œ์ž‘ํ•  ๋•Œ ์ด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœํ•˜์—ฌ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ธ์–ด๋ฅผ ์žฌ์„ค์ •ํ•  ๋•Œ ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•ด์•ผ ์–ธ์–ด๊ฐ€ ์ ์šฉ๋˜๋Š”๋ฐ ์ด ๋•Œ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +#### flutter_widget_from_html (0.14.11) + +HTML ๋ฌธ์ž์—ด์„ Flutter ์œ„์ ฏ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ณต์ง€์‚ฌํ•ญ์„ HTML ํ˜•์‹์œผ๋กœ ํฌ๋กค๋งํ•˜์—ฌ ์•ฑ์— ๋ณด์—ฌ์ค„ ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### image_picker (1.1.0) + +์ด๋ฏธ์ง€๋ฅผ ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ์นด๋ฉ”๋ผ๋กœ ์ดฌ์˜ํ•˜์—ฌ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. QnA์˜ ๊ธ€ ์ž‘์„ฑ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### permission_handler (11.3.1) + +์•ฑ์—์„œ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ๊ถŒํ•œ(์นด๋ฉ”๋ผ, ๋…น์Œ ๋“ฑ)์„ ์š”์ฒญํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๊ถŒํ•œ์„ ํ—ˆ์šฉํ•˜๊ฑฐ๋‚˜ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด์— ๋”ฐ๋ผ ๊ถŒํ•œ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +#### flutter_sound (9.2.3) + +์˜ค๋””์˜ค ๋…น์Œ ๋ฐ ์žฌ์ƒ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋ฐœ์Œํ‰๊ฐ€๋ฅผ ์œ„ํ•ด ์˜ค๋””์˜ค๋ฅผ ๋…น์Œํ•˜๊ณ , ์žฌ์ƒํ•˜๋ฉฐ, ์„œ๋ฒ„๋กœ ์ „์†กํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### audio_session (0.1.6) + +ํ”Œ๋Ÿฌํ„ฐ ์•ฑ ๋‚ด๋ถ€์—์„œ ์˜ค๋””์˜ค ์„ค์ • ๋ฐ ์ œ์–ด๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๋…น์Œ์„ ์ง„ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋ฑ ๊ด€๋ฆฌ๋ฅผ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. + +#### record (5.0.5) + +์˜ค๋””์˜ค ๋…น์Œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐœ์Œ ์—ฐ์Šต์—์„œ ๋ฐœ์Œ์„ ๋…น์Œํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### path_provider (2.1.3) + +ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ฒฝ๋กœ ๊ด€๋ฆฌ ๋ฐ ํŽธ์ง‘์„ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. ๋ฐœ์Œ ํ‰๊ฐ€์—์„œ ์Œ์„ฑ์„ ๋…น์Œํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. + +#### audio_players (6.0.0) + +์˜ค๋””์˜ค ํŒŒ์ผ ์žฌ์ƒ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์žฌ์ƒ, ์ผ์‹œ ์ •์ง€, ์ค‘์ง€ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐœ์Œ ์—ฐ์Šต์—์„œ ๋‚˜์˜ ๋ฐœ์Œ์„ ์žฌ์ƒํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### photo_view (0.15.0) + +์ด๋ฏธ์ง€๋ฅผ ํ™•๋Œ€/์ถ•์†Œํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์ ฏ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ™ˆ ํ™”๋ฉด์˜ ๊ธฐํƒ€ ์ •๋ณด์—์„œ ํ•™๊ต ์ง€๋„, ์‹œ์„ค ์ •๋ณด๋ฅผ ํ™•์ธํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +### ๊ธฐํƒ€ + +#### go_router (13.2.1) + +ํ”Œ๋Ÿฌํ„ฐ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŽ˜์ด์ง€ ๊ฐ„์˜ ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. url๊ฒฝ๋กœ ๊ธฐ๋ฐ˜์˜ ๋ผ์šฐํŒ…์„ ์ง€์›ํ•˜๊ณ , ํŽ˜์ด์ง€ ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋™์  ๋ผ์šฐํŒ… ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +#### flutter_native_splash (2.4.0) + +์•ฑ์„ ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋ฉด ๋“ฑ์žฅํ•˜๋Š” Splash Screen์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ Android์˜ ๊ฒฝ์šฐ Android 12 ๋ฒ„์ „ ์ดํ›„๋กœ๋Š” Splash Screen ๊ธฐ๋ณธ๊ฐ’์ด ์„ค์ •๋˜์–ด์žˆ์–ด Android ๋ฒ„์ „์— ๋”ฐ๋ผ Splash Screen์„ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด์•ผํ•˜๋Š”๋ฐ ์ด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +#### flutter_screenutil (5.9.0) + +์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด ํฌ๊ธฐ์— ๋”ฐ๋ผ UI ์š”์†Œ์˜ ํฌ๊ธฐ์™€ ๋ฐฐ์น˜๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜์—ฌ ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๋™์ผํ•œ ๋””์ž์ธ์˜ ํ™”๋ฉด์„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค. + +#### fluttertoast (8.2.4) + +๊ฐ„๋‹จํ•œ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•ด์ค๋‹ˆ๋‹ค. ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์„ ๋•Œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +#### country_picker (2.0.25) + +๊ตญ๊ฐ€๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•  ๋•Œ ๊ตญ๊ฐ€ ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. diff --git a/front/capstone_front/README.md b/front/capstone_front/README.md deleted file mode 100644 index 4990cf3de5..0000000000 --- a/front/capstone_front/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# capstone_front - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/front/capstone_front/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/front/capstone_front/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/front/capstone_front/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/front/test.txt b/front/test.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/index.md b/index.md index 8cf48efe37..4f77d7240a 100644 --- a/index.md +++ b/index.md @@ -1,28 +1,176 @@ -## 0. ์ค‘๊ฐ„๋ฐœํ‘œ ์ž๋ฃŒ +# **๐Ÿ’ป ์บก์Šคํ†ค 30์กฐ ๊ฒฐ๊ณผ๋ฌผ ์†Œ๊ฐœ** -[https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link](https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link) +
+ +[![Typing SVG](https://readme-typing-svg.demolab.com?font=Jua&size=25&pause=1000&color=6D2FF4&random=false&width=435&lines=%F0%9F%91%8B+%EC%99%B8%EA%B5%AD%EC%9D%B8+%EC%9C%A0%ED%95%99%EC%83%9D%EB%93%A4%EC%9D%84+%EC%9C%84%ED%95%9C+%EC%95%B1%2C+%EC%99%B8%EA%B5%AD%EB%AF%BC)](https://git.io/typing-svg) + +![แ„Žแ…ฌแ„Œแ…ฉแ†ผ แ„‡แ…กแ†ฏแ„‘แ…ญแ„Œแ…กแ„…แ…ญ](https://github.com/kookmin-sw/capstone-2024-30/assets/52407470/20fc41c1-8a22-4c90-a1f1-8539dea92ed1) + +
+ + [์ค‘๊ฐ„๋ฐœํ‘œ ์ž๋ฃŒ ๋ฐ ๋ณด๊ณ ์„œ] + +
+ + [์ตœ์ข…๋ฐœํ‘œ ์ž๋ฃŒ&ํฌ์Šคํ„ฐ ๋ฐ ๋ณด๊ณ ์„œ] + +
+ + [Github] + +
+ `ํŒŒํŠธ๋ณ„ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ฐ ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ์˜ ๋งˆํฌ๋‹ค์šด ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค` + + + + + + + + + + + +
+ ํ”„๋ก ํŠธ + + ๋ฐฑ + + AI +
+ FRONT.md + + BACK.md + + AI.md +
+

-## 1. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ +
+

+ +

+
+ Play ์Šคํ† ์–ด ๋‹ค์šด๋กœ๋“œ +
-์ด ํ”„๋กœ์ ํŠธ๋Š” ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์—์„œ ๊ณต๋ถ€ํ•˜๋Š” ์™ธ๊ตญ์ธ ์œ ํ•™์ƒ๋“ค์„ ์œ„ํ•œ ์ข…ํ•ฉ์ ์ธ ์•ฑ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•ฑ์€ ํ•™์ƒ๋“ค์ด ์บ ํผ์Šค ์ƒํ™œ์— ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ์ •๋ณด์™€ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +
+ +## **1. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ**
-## 2. Abstract +์ด ํ”„๋กœ์ ํŠธ๋Š” ๊ตญ๋ฏผ๋Œ€ํ•™๊ต ์œ ํ•™์ƒ๋“ค์ด ๊ฒช๋Š” ์–ธ์–ด์ , ๋ฌธํ™”์  ๋ถˆํŽธํ•จ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” ์•ฑ์—์„œ ์œ ํ•™์ƒ๋“ค์ด ์บ ํผ์Šค ์ƒํ™œ์— ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ์ •๋ณด์™€ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +
+ +## **2. Abstract** + +
This project aims to develop a comprehensive app service for international students studying at Kookmin University. The app provides a variety of information and services to help students quickly adapt to campus life.
-## 3. ์†Œ๊ฐœ ์˜์ƒ +## **3. ์‹œ์—ฐ ์˜์ƒ** + +
+ + + +
+ +## **4. ํ”„๋กœ์ ํŠธ ๊ธฐ๋Šฅ** + +
+ +### 1๏ธโƒฃ ๋ฒˆ์—ญ๋œ ๊ณต์ง€์‚ฌํ•ญ / ํ•™์‹ / ํ•™๊ต์ •๋ณด ์ œ๊ณต + +
+ +๊ตญ๋ฏผ๋Œ€ํ•™๊ต์—์„œ๋Š” ๊ณต์ง€์‚ฌํ•ญ, ํ•™์‹, ํ•™๊ต์ •๋ณด์˜ ๋ฒˆ์—ญ์„ ์ž˜ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด์—๋”ฐ๋ผ ์™ธ๊ตญ์ธ ์œ ํ•™์ƒ๋“ค์€ ๋งค๋ฒˆ ๋ฒˆ์—ญ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•™๊ต์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์–ป๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ณด์˜ ์ ‘๊ทผ์„ฑ์ด ๋‚ฎ์Šต๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ ์„œ๋น„์Šค๋Š” ์„ค์ •ํ•œ ์–ธ์–ด์— ๋งž์ถฐ์„œ ๊ณต์ง€์‚ฌํ•ญ/ํ•™์‹/ํ•™๊ต์ •๋ณด ๋ฒˆ์—ญ๋ณธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +
-ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœํ•˜๋Š” ์˜์ƒ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š” +|๋ฒˆ์—ญ๋œ ๊ณต์ง€์‚ฌํ•ญ|๊ณต์ง€์‚ฌํ•ญ ๋””ํ…Œ์ผ|๋ฒˆ์—ญ๋œ ํ•™์‹์ •๋ณด| +|------|---|---| +||||
-## 4. ํŒ€์› ์†Œ๊ฐœ +### 2๏ธโƒฃ ์ฑ—๋ด‡ ๊ธฐ๋Šฅ + +
+ +๊ตญ๋ฏผ๋Œ€ํ•™๊ต์—์„œ๋Š” ON๊ตญ๋ฏผ ์ฑ—๋ด‡ "์ฟ ๋ฏผ์ด"๋ฅผ ์„œ๋น„์Šคํ•˜๊ณ ์žˆ์œผ๋‚˜, ์„ฑ๋Šฅ์ด ๋งค์šฐ ํ˜•ํŽธ์—†์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์งˆ๋ฌธ์—๋„ ๋™๋ฌธ์„œ๋‹ต์„ ํ•˜๊ฑฐ๋‚˜, ์˜์–ด๋กœ ์งˆ๋ฌธํ–ˆ๋Š”๋ฐ ํ•œ๊ธ€๋กœ ๋‹ต๋ณ€ํ•˜๋Š” ๋“ฑ ์ „ํ˜€ ์ฑ—๋ด‡์œผ๋กœ์„œ์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ RAG์™€ LLM์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตญ๋ฏผ๋Œ€ํ•™๊ต์— ํŠนํ™”๋œ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•˜๊ณ  ๋‹ค๊ตญ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” "KuKu" ์ฑ—๋ด‡์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +
+ +|๊ตญ๋ฏผ๋Œ€ ๊ด€๋ จ ์งˆ๋ฌธ|๋‹ค๊ตญ์–ด ์ง€์›|์ผ์ƒ ๋Œ€ํ™”| +|------|---|---| +|||| + +
+ +### 3๏ธโƒฃ ๋ฐœ์Œ ๊ต์ • ๊ธฐ๋Šฅ + +
+ +๋งŽ์€ ์™ธ๊ตญ์ธ๋“ค์€ ํ•œ๊ตญ์— ์™€์„œ ์–ธ์–ด ๋ฌธ์ œ๋กœ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ํ•™๊ต ์ƒํ™œ์„ ํ•˜๋‹ค๋ณด๋ฉด ๋ฐœํ‘œ๋ฅผ ํ•˜๊ฑฐ๋‚˜ ์ผ์ƒ์ƒํ™œ์—์„œ ์˜์‚ฌ์†Œํ†ต์„ ํ•ด์•ผํ•  ๋•Œ, ๋ณธ์ธ์˜ ๋ฐœ์Œ์ด ์ •ํ™•ํ•œ์ง€ ํ™•์ธํ•  ๋ฐฉ๋ฒ•์ด ์—†์–ด์„œ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ ์ž์‹ ์˜ ๋ฐœํ‘œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋„ฃ์–ด์„œ ๋ฐœ์Œ ํ‰๊ฐ€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ํ•œ๊ตญ์˜ ์ผ์ƒ์ƒํ™œ์—์„œ ๋งŽ์ด ์“ฐ์ด๋Š” ์—ฌ๋Ÿฌ ํ‘œํ˜„๋“ค์„ ์—ฐ์Šตํ•  ์ˆ˜ ์žˆ๋„๋กํ•˜์—ฌ ํ•œ๊ตญ ์œ ํ•™์ƒํ™œ์„ ๋•๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +
+ +|์ผ์ƒ์ƒํ™œ์— ์ž์ฃผ์“ฐ๋Š” ์˜ˆ๋ฌธ ์ง€์›|๋ฌธ์žฅ ์ปค์Šคํ…€ ์ง€์›|๋ฐœ์Œ ํ‰๊ฐ€ ์ œ๊ณต| +|------|---|---| +|||| + +
+ +### 4๏ธโƒฃ ํ—ฌํผ ๋งค์นญ ๊ธฐ๋Šฅ + +
+ +๋งŽ์€ ์™ธ๊ตญ์ธ๋“ค์ด ๋‚ฏ์„  ๋•…์— ์™”์„ ๋•Œ ๋„์›€์„ ๋ฐ›์„ ์‚ฌ๋žŒ์ด ์—†์–ด์„œ ๋งค์šฐ ํž˜๋“ค์–ดํ•ฉ๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ ์™ธ๊ตญ์ธ๋“ค์„ ๋„์šธ ์ˆ˜ ์žˆ๋„๋ก ํ—ฌํผ ๋งค์นญ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•œ๊ตญ์ธ or ์˜ค๋žœ ์œ ํ•™์ƒํ™œ์„ ํ•˜์—ฌ ํ•œ๊ตญ ์ƒํ™œ์— ์ต์ˆ™ํ•ด์ง„ ์™ธ๊ตญ์ธ ํ—ฌํผ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +
+ +|ํ—ฌํผ ๋ฐ ํ—ฌํ”ผ ๊ฒŒ์‹œํŒ|๋””ํ…Œ์ผ|์ฑ„ํŒ…| +|------|---|---| +|||| + +
+ +### 5๏ธโƒฃ Q&A์™€ FAQ ๊ธฐ๋Šฅ + +
+ +์œ ํ•™์ƒ๋“ค์ด ํ•œ๊ตญ์ƒํ™œ์—์„œ ๊ถ๊ธˆํ•œ ๊ฒƒ์„ ๋ฌผ์–ด๋ณผ๋งŒํ•œ ๊ณณ์ด ๋งˆ๋•…์น˜ ์•Š๊ณ , ON๊ตญ๋ฏผ์— ์žˆ๋Š” FAQ์˜ ์กด์žฌ๋ฅผ ์•Œ๊ธฐ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด FAQ ๋˜ํ•œ ๋ฒˆ์—ญ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ, ์™ธ๊ตญ๋ฏผ์€ Q&A ๊ฒŒ์‹œํŒ๊ณผ ๋‹ค๊ตญ์–ด๋กœ ๋ฒˆ์—ญ๋œ FAQ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +
+ +|Q&A ๊ฒŒ์‹œํŒ|๋””ํ…Œ์ผ|FAQ ์กฐํšŒ| +|------|---|---| +|||| + +
+ +## **5. ํŒ€์› ์†Œ๊ฐœ** + +
@@ -31,9 +179,9 @@ This project aims to develop a comprehensive app service for international stude - - - + + + @@ -54,9 +202,9 @@ This project aims to develop a comprehensive app service for international stude - - - + + + @@ -72,68 +220,98 @@ This project aims to develop a comprehensive app service for international stude
-## 5. ๊ธฐ์ˆ ์Šคํƒ +## **6. ๊ธฐ์ˆ ์Šคํƒ** -### Frontend +
-
- - -
+### **๐Ÿ›  Frontend** -### Backend +| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Framework | | +| Database | REDRED | +| Programming Language | | +| Device | | -
- - - -
+
-### Tools +### **๐Ÿ’พ Backend** -
- - - - - -
+| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Framework | REDREDREDRED | +| Database | REDRED | +| Programming Language | REDRED | +| Test | REDRED | +| Deploy | REDREDREDREDREDREDRED | +| CI/CD | RED | +| ETC | REDREDRED | + +
+ +### **๐Ÿ“ป AI** + +| ์—ญํ•  | ์ข…๋ฅ˜ | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Programming Language | RED | +| Development | REDRED | +| Technology | REDRED | +| Test | | +| Server | | + +
+ +### ๐Ÿ”จ Tools + +| ์—ญํ•  | ์ข…๋ฅ˜ | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Version Control | | +| Cooperation | REDREDRED | +| Test | | + +

-## 6. ์‹œ์Šคํ…œ ๊ตฌ์กฐ +## **7. ์‹œ์Šคํ…œ ๊ตฌ์กฐ**
- -## 7. ์‚ฌ์šฉ๋ฒ• +### **๐Ÿ’ป ์„œ๋น„์Šค ์•„ํ‚คํƒ์ฒ˜** -### Backend +
+ -`.env.example`์„ ๋ฐ”ํƒ•์œผ๋กœ `.env`๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ +### **๐Ÿค– ์ฑ—๋ด‡ ์•„ํ‚คํ…์ฒ˜** -``` -chmod +x ./gradlew -./gradlew -``` +
+ -๋˜๋Š” +### **๐Ÿ“‚ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ** ``` -gradle clean build -``` +โ”œโ”€โ”€ ๐Ÿ“‚.github -๋ฅผ ํ†ตํ•ด ์‹คํ–‰ .jar ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ํŒŒ์ผ์„ ์ƒ์„ฑํ–ˆ์œผ๋ฉด +โ”œโ”€โ”€ ๐Ÿ“‚front ๐Ÿ—‚ ํ”„๋ก ํŠธ ์•ฑ ์†Œ์Šค (Flutter) +โ”œโ”€โ”€ ๐Ÿ“‚back-gateway ๐Ÿ—‚ ๋ฐฑ์—”๋“œ Api Gateway (Spring Cloud Gateway) + +โ”œโ”€โ”€ ๐Ÿ“‚back ๐Ÿ—‚ ๋ฐฑ์—”๋“œ ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„ ์†Œ์Šค(Spring Boot) + +โ”œโ”€โ”€ ๐Ÿ“‚back-chat ๐Ÿ—‚ ๋ฐฑ์—”๋“œ ์ฑ„ํŒ… ์„œ๋ฒ„ ์†Œ์Šค (Ruby on Rails) + +โ”œโ”€โ”€ ๐Ÿ“‚ai ๐Ÿ—‚ KuKu ์ฑ„ํŒ… ๋ด‡ ์†Œ์Šค + +โ””โ”€โ”€ ๐Ÿ“•Readme.md ``` -docker-compose up --build -d -``` -๋ฅผ ํ†ตํ•ด docker compose build๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ ์‹คํ–‰ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. +
+ +## **8. ์‚ฌ์šฉ๋ฒ•**
-### Frontend +### **Frontend** #### 1. ํ”Œ๋Ÿฌํ„ฐ ์„ค์น˜ @@ -161,35 +339,41 @@ flutter run
-### AI +### **Backend** -### Chat bot `KUKU` ์†Œ๊ฐœ - -๊ตญ๋ฏผ๋Œ€์— ๊ด€ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๋ฌผ์–ด๋ณด์„ธ์š” ! -๊ตญ๋ฏผ๋Œ€ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์ฑ—๋ด‡ <์ฟ ์ฟ >์ž…๋‹ˆ๋‹ค. +`.env.example`์„ ๋ฐ”ํƒ•์œผ๋กœ `.env`๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ -### ํ•™์Šตํ•œ ๋ฐ์ดํ„ฐ +``` +docker-compose up -d +``` -- [๊ตญ๋ฏผ๋Œ€ 2023 ์š”๋žŒ PDF](https://www.kookmin.ac.kr/comm/cmfile/thumbnail2.do?encSvrFileNm=223d2bdfdbb4df30ad85271267bd6e6a0a913159736ab9843bb76fc00eeeb5ddb1e88f35002b9cf1b749bfe96b751f16b8be21ad5273d348a74b10a57513dd4540bbcb178d3151db4d507c693a1f7ef9&encFileGrpSeq=8e8e9041def64eb5f7f8c21154bcff06&encFileSeq=cf9f1626435aafc6e0e182b36c8e23d9) -- [2023~2024.03.28 ๊ตญ๋ฏผ๋Œ€ ์ „์ฒด ๊ณต์ง€์‚ฌํ•ญ](https://www.kookmin.ac.kr/user/kmuNews/notice/index.do) +๋ฅผ ํ†ตํ•ด docker compose๋ฅผ ํ†ตํ•˜์—ฌ ์‹คํ–‰ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋Š” ๋ชจ๋‘ Dockerhub์— ์—…๋กœ๋“œ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. -### ์‹คํ–‰ ๋ฐฉ๋ฒ• +
-2024_03_30 ์•„๋‚˜์ฝ˜๋‹ค ํ™˜๊ฒฝ python 3.8 ์—์„œ ์‹คํ–‰ ํ™•์ธ +### **AI** -1. /ai ๊ฒฝ๋กœ๋กœ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์ด๋™ -2. ํŒจํ‚ค์ง€ ์„ค์น˜ `pip install -r requirements.txt` -3. /ai ๊ฒฝ๋กœ์— .env ํŒŒ์ผ ์ƒ์„ฑ (OPENAI_API_KEY = 'your_api') -4. `python run_chatbot.py` +1. `git clone https://github.com/kookmin-sw/capstone-2024-30.git` +2. `cd YOUR PATH/ai/` +3. `pip install -r requirements.txt` +4. ๋ฒกํ„ฐ ์ €์žฅ์†Œ FAISS ํด๋”๋ฅผ `/ai` ์— ์œ„์น˜ [๋‹ค์šด๋กœ๋“œ ๋งํฌ](https://drive.google.com/file/d/1-U5X_xRg0PLITrDWDNMeZK_NAP5IIwsL/view?usp=sharing) +5. `python run_chatbot.py` -### TEST +
-![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/6b7c8514-35dd-41bb-9815-56a182a7ccfb) +## **9. ๊ธฐํƒ€** -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/e37af8f8-a35b-4808-a357-aff5aebb8d88) +### **์ฑ—๋ด‡ ์ฃผ์š” ์ˆ˜์ง‘ ๋ฐ์ดํ„ฐ ์ถœ์ฒ˜** -![image](https://github.com/kookmin-sw/capstone-2024-30/assets/54922676/d7b5cd40-43b7-4cca-8be8-6fff257ed303) +- [๊ตญ๋ฏผ๋Œ€ 2023 ์š”๋žŒ PDF](https://www.kookmin.ac.kr/comm/cmfile/thumbnail2.do?encSvrFileNm=223d2bdfdbb4df30ad85271267bd6e6a0a913159736ab9843bb76fc00eeeb5ddb1e88f35002b9cf1b749bfe96b751f16b8be21ad5273d348a74b10a57513dd4540bbcb178d3151db4d507c693a1f7ef9&encFileGrpSeq=8e8e9041def64eb5f7f8c21154bcff06&encFileSeq=cf9f1626435aafc6e0e182b36c8e23d9) +- [๊ตญ๋ฏผ๋Œ€ ์ „์ฒด ๊ณต์ง€์‚ฌํ•ญ](https://www.kookmin.ac.kr/user/kmuNews/notice/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ๋Œ€ํ•™์†Œ๊ฐœ](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ๋Œ€ํ•™์ƒํ™œ](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ํ™ˆํŽ˜์ด์ง€ ํ•™์‚ฌ์•ˆ๋‚ด](https://www.kookmin.ac.kr/comm/menu/user/dcc49bd0de0a82d918456e893d7bb02e/content/index.do) +- [๊ตญ๋ฏผ๋Œ€ ์†Œํ”„ํŠธ์›จ์–ด์œตํ•ฉ๋Œ€ํ•™](https://cs.kookmin.ac.kr/intro/intro) +- [๊ตญ๋ฏผ๋Œ€ ์†Œํ”„ํŠธ์›จ์–ด์œตํ•ฉ๋Œ€ํ•™ ๊ณต์ง€์‚ฌํ•ญ](https://cs.kookmin.ac.kr/news/notice/) +- ๊ทธ ์™ธ ๋„ค์ด๋ฒ„ ๋ฐ ๊ตฌ๊ธ€ ๊ธฐํƒ€ ์ˆ˜์ง‘ ๋ฐ์ดํ„ฐ -## 8. ๊ธฐํƒ€ +### **Metrics** -
+[RAGAS](https://docs.ragas.io/en/stable/concepts/metrics/index.html)
์ตœ์ง€ํ›ˆ๊น€๋ฏผ์ œ์กฐํ˜„์ง„์ตœ์ง€ํ›ˆ๊น€๋ฏผ์ œ์กฐํ˜„์ง„
****1683
์ฑ„์›์ฐฌ๊น€ํ˜œ์„ฑ์ตœ์˜๋ฝ์ฑ„์›์ฐฌ๊น€ํ˜œ์„ฑ์ตœ์˜๋ฝ
****1676