Update streamlit_app.py
Browse files- streamlit_app.py +51 -241
streamlit_app.py
CHANGED
|
@@ -1,248 +1,58 @@
|
|
| 1 |
-
# ๋คํธ์ํฌ ์๋ฌ
|
| 2 |
import streamlit as st
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import numpy as np
|
| 5 |
-
from rank_bm25 import BM25Okapi
|
| 6 |
-
import pickle
|
| 7 |
-
import json
|
| 8 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
#
|
| 11 |
-
st.
|
| 12 |
-
page_title="์ฌ๋ก๋ ํฐ ์ง๋ฅํ ๊ฒ์",
|
| 13 |
-
page_icon="๐",
|
| 14 |
-
layout="wide"
|
| 15 |
-
)
|
| 16 |
|
| 17 |
-
|
| 18 |
-
DATA_PATH = "."
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
self.bm25 = None
|
| 24 |
-
self.processed_data = None
|
| 25 |
-
self.metadata = {}
|
| 26 |
-
self.system_loaded = False
|
| 27 |
-
|
| 28 |
-
@st.cache_resource
|
| 29 |
-
def load_system(_self):
|
| 30 |
-
"""์์คํ
๋ก๋ (HF Spaces ์ต์ ํ)"""
|
| 31 |
-
try:
|
| 32 |
-
# ํ์ผ ์กด์ฌ ํ์ธ
|
| 33 |
-
required_files = [
|
| 34 |
-
'embeddings.npy',
|
| 35 |
-
'bm25_index.pkl',
|
| 36 |
-
'processed_data.pkl',
|
| 37 |
-
'system_metadata.json'
|
| 38 |
-
]
|
| 39 |
-
|
| 40 |
-
missing_files = []
|
| 41 |
-
for file in required_files:
|
| 42 |
-
if not os.path.exists(os.path.join(DATA_PATH, file)):
|
| 43 |
-
missing_files.append(file)
|
| 44 |
-
|
| 45 |
-
if missing_files:
|
| 46 |
-
st.error(f"ํ์ํ ํ์ผ์ด ์์ต๋๋ค: {missing_files}")
|
| 47 |
-
return False
|
| 48 |
-
|
| 49 |
-
# ์๋ฒ ๋ฉ ๋ก๋ (์ฌ์ฉ์ ์ํ์ง๋ง ๋ก๋๋ ํจ)
|
| 50 |
-
_self.embeddings = np.load(f"{DATA_PATH}/embeddings.npy")
|
| 51 |
-
|
| 52 |
-
# BM25 ์ธ๋ฑ์ค ๋ก๋
|
| 53 |
-
with open(f"{DATA_PATH}/bm25_index.pkl", 'rb') as f:
|
| 54 |
-
_self.bm25 = pickle.load(f)
|
| 55 |
-
|
| 56 |
-
# ๋ฐ์ดํฐ ๋ก๋
|
| 57 |
-
with open(f"{DATA_PATH}/processed_data.pkl", 'rb') as f:
|
| 58 |
-
_self.processed_data = pickle.load(f)
|
| 59 |
-
|
| 60 |
-
# ๋ฉํ๋ฐ์ดํฐ ๋ก๋
|
| 61 |
-
with open(f"{DATA_PATH}/system_metadata.json", 'r') as f:
|
| 62 |
-
_self.metadata = json.load(f)
|
| 63 |
-
|
| 64 |
-
_self.system_loaded = True
|
| 65 |
-
st.success("โ
์์คํ
๋ก๋ ์๋ฃ!")
|
| 66 |
-
return True
|
| 67 |
-
|
| 68 |
-
except Exception as e:
|
| 69 |
-
st.error(f"์์คํ
๋ก๋ ์คํจ: {e}")
|
| 70 |
-
return False
|
| 71 |
-
|
| 72 |
-
def tokenize_korean(self, text):
|
| 73 |
-
import re
|
| 74 |
-
tokens = re.findall(r'\b\w+\b', str(text).lower())
|
| 75 |
-
return [token for token in tokens if len(token) > 1]
|
| 76 |
-
|
| 77 |
-
def search(self, query, top_k=10):
|
| 78 |
-
"""BM25 ๊ธฐ๋ฐ ๊ฒ์ (๋คํธ์ํฌ ํธ์ถ ์์)"""
|
| 79 |
-
if not self.system_loaded:
|
| 80 |
-
return []
|
| 81 |
-
|
| 82 |
-
# BM25 ๊ฒ์๋ง ์ฌ์ฉ (API ํธ์ถ ์ ๊ฑฐ)
|
| 83 |
-
tokenized_query = self.tokenize_korean(query)
|
| 84 |
-
bm25_scores = self.bm25.get_scores(tokenized_query)
|
| 85 |
-
|
| 86 |
-
# ์์ ๊ฒฐ๊ณผ ์ ํ
|
| 87 |
-
top_indices = np.argsort(bm25_scores)[::-1][:top_k]
|
| 88 |
-
|
| 89 |
-
results = []
|
| 90 |
-
for rank, idx in enumerate(top_indices, 1):
|
| 91 |
-
if bm25_scores[idx] > 0: # ๊ด๋ จ์ฑ ์๋ ๊ฒฐ๊ณผ๋ง
|
| 92 |
-
row = self.processed_data.iloc[idx]
|
| 93 |
-
results.append({
|
| 94 |
-
'rank': rank,
|
| 95 |
-
'title': row.get('h3_title', ''),
|
| 96 |
-
'date': row.get('date', ''),
|
| 97 |
-
'newsletter': row.get('h1_title', ''),
|
| 98 |
-
'entities': row.get('entities', ''),
|
| 99 |
-
'events': row.get('events', ''),
|
| 100 |
-
'content': row.get('h3_content_text', ''),
|
| 101 |
-
'score': float(bm25_scores[idx])
|
| 102 |
-
})
|
| 103 |
-
|
| 104 |
-
return results
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
st.markdown("**2๋
๊ฐ์ ์ฌ๋ก๋ ํฐ ์์นด์ด๋ธ๋ฅผ ๊ฒ์ํ์ธ์!**")
|
| 110 |
-
st.info("๐ก ํค์๋ ๊ธฐ๋ฐ BM25 ๊ฒ์์ ์ฌ์ฉํฉ๋๋ค (API ํค ๋ถํ์)")
|
| 111 |
-
|
| 112 |
-
# ์์คํ
์ด๊ธฐํ
|
| 113 |
-
search_system = HFSlowletterSearch()
|
| 114 |
-
|
| 115 |
-
# ์์คํ
๋ก๋
|
| 116 |
-
with st.spinner("๐ ์์คํ
๋ก๋ฉ ์ค..."):
|
| 117 |
-
system_loaded = search_system.load_system()
|
| 118 |
-
|
| 119 |
-
if system_loaded:
|
| 120 |
-
# ์์คํ
์ ๋ณด
|
| 121 |
-
col1, col2, col3 = st.columns(3)
|
| 122 |
-
with col1:
|
| 123 |
-
st.metric("๐ ์ด ๋ฌธ์", f"{search_system.metadata.get('total_documents', 0):,}๊ฐ")
|
| 124 |
-
with col2:
|
| 125 |
-
st.metric("๐
์์ฑ์ผ", search_system.metadata.get('created_at', 'Unknown')[:10])
|
| 126 |
-
with col3:
|
| 127 |
-
st.metric("๐ ์
๋ฐ์ดํธ", search_system.metadata.get('last_updated', 'Unknown')[:10])
|
| 128 |
-
|
| 129 |
-
st.divider()
|
| 130 |
-
|
| 131 |
-
# ๋น ๋ฅธ ๊ฒ์ ๋ฒํผ
|
| 132 |
-
st.subheader("๐ ๋น ๋ฅธ ๊ฒ์")
|
| 133 |
-
|
| 134 |
-
quick_queries = [
|
| 135 |
-
"์ค์์ด ๋ํต๋ น",
|
| 136 |
-
"์ด์ฌ๋ช
๋ํต๋ น",
|
| 137 |
-
"๋ช
ํ๊ท ์ฌ๊ฑด",
|
| 138 |
-
"ํํต",
|
| 139 |
-
"๊ฒฝ์ ์ ์ฑ
",
|
| 140 |
-
"๋ถํ"
|
| 141 |
-
]
|
| 142 |
-
|
| 143 |
-
cols = st.columns(3)
|
| 144 |
-
selected_query = None
|
| 145 |
-
|
| 146 |
-
for i, query in enumerate(quick_queries):
|
| 147 |
-
col = cols[i % 3]
|
| 148 |
-
if col.button(query, key=f"quick_{i}"):
|
| 149 |
-
selected_query = query
|
| 150 |
-
|
| 151 |
-
# ๊ฒ์์ฐฝ
|
| 152 |
-
search_query = st.text_input(
|
| 153 |
-
"๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์:",
|
| 154 |
-
value=selected_query or "",
|
| 155 |
-
placeholder="์: ์ค์์ด ๋ํต๋ น ์ต๊ทผ ๋
ผ๋"
|
| 156 |
-
)
|
| 157 |
-
|
| 158 |
-
# ๊ฒ์ ์ค์
|
| 159 |
-
col1, col2 = st.columns([3, 1])
|
| 160 |
-
with col2:
|
| 161 |
-
top_k = st.selectbox("๊ฒฐ๊ณผ ๊ฐ์", [5, 10, 20, 30], index=1)
|
| 162 |
-
|
| 163 |
-
# ๊ฒ์ ์คํ
|
| 164 |
-
if search_query:
|
| 165 |
-
with st.spinner("๐ ๊ฒ์ ์ค..."):
|
| 166 |
-
results = search_system.search(search_query, top_k=top_k)
|
| 167 |
-
|
| 168 |
-
if results:
|
| 169 |
-
st.success(f"๐ {len(results)}๊ฐ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์์ต๋๋ค!")
|
| 170 |
-
|
| 171 |
-
# ๊ฒฐ๊ณผ ํ์
|
| 172 |
-
for result in results:
|
| 173 |
-
with st.expander(
|
| 174 |
-
f"๐ {result['rank']}. {result['title']} ({result['date']})",
|
| 175 |
-
expanded=result['rank'] <= 3
|
| 176 |
-
):
|
| 177 |
-
col1, col2 = st.columns([3, 1])
|
| 178 |
-
|
| 179 |
-
with col1:
|
| 180 |
-
if result['entities']:
|
| 181 |
-
entities = result['entities'].split(';')[:5]
|
| 182 |
-
entities = [e.strip() for e in entities if e.strip()]
|
| 183 |
-
if entities:
|
| 184 |
-
st.write("**๐ฅ ๊ด๋ จ ์ธ๋ฌผ/๊ธฐ๊ด:**", ", ".join(entities))
|
| 185 |
-
|
| 186 |
-
if result['events']:
|
| 187 |
-
events = result['events'].split(';')[:3]
|
| 188 |
-
events = [e.strip() for e in events if e.strip()]
|
| 189 |
-
if events:
|
| 190 |
-
st.write("**๐ ์ฃผ์ ์ฌ๊ฑด:**", ", ".join(events))
|
| 191 |
-
|
| 192 |
-
if result['content']:
|
| 193 |
-
st.write("**๐ ๋ด์ฉ:**")
|
| 194 |
-
content = result['content']
|
| 195 |
-
if len(content) > 300:
|
| 196 |
-
st.write(content[:300] + "...")
|
| 197 |
-
else:
|
| 198 |
-
st.write(content)
|
| 199 |
-
|
| 200 |
-
with col2:
|
| 201 |
-
st.metric("๊ฒ์ ์ ์", f"{result['score']:.2f}")
|
| 202 |
-
if result['newsletter']:
|
| 203 |
-
st.write(f"**๐** {result['newsletter']}")
|
| 204 |
-
else:
|
| 205 |
-
st.warning("๐ ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค. ๋ค๋ฅธ ํค์๋๋ก ์๋ํด๋ณด์ธ์.")
|
| 206 |
-
st.info("๐ก ํ: ๋ ๊ฐ๋จํ ํค์๋๋ก ๊ฒ์ํด๋ณด์ธ์ (์: '์ค์์ด', '์ด์ฌ๋ช
', 'ํํต')")
|
| 207 |
-
|
| 208 |
-
# ์ฌ์ฉ๋ฒ ์๋ด
|
| 209 |
-
with st.expander("๐ก ์ฌ์ฉ๋ฒ ์๋ด"):
|
| 210 |
-
st.markdown("""
|
| 211 |
-
### ๐ ๊ฒ์ ํ
|
| 212 |
-
- **์ธ๋ฌผ๋ช
**: "์ค์์ด", "์ด์ฌ๋ช
", "๋ช
ํ๊ท "
|
| 213 |
-
- **์ฌ๊ฑด๋ช
**: "ํํต", "ํน๊ฒ", "๋๋ก "
|
| 214 |
-
- **์ฃผ์ **: "๊ฒฝ์ ์ ์ฑ
", "๋ถํ ๊ด๋ จ"
|
| 215 |
-
- **๋ณตํฉ ๊ฒ์**: "์ค์์ด ํํต ๊ฐ๋ฅ์ฑ"
|
| 216 |
-
|
| 217 |
-
### โก ์์คํ
์ ๋ณด
|
| 218 |
-
- **๊ฒ์ ๋ฐฉ์**: BM25 ํค์๋ ๋งค์นญ
|
| 219 |
-
- **๋ฐ์ดํฐ ๋ฒ์**: 2023๋
4์ ~ 2025๋
7์
|
| 220 |
-
- **์ด ๋ฌธ์ ์**: 13,000+ ๊ฐ ์ฌ๋ก๋ ํฐ ๏ฟฝ๏ฟฝ๏ฟฝ์ฌ
|
| 221 |
-
- **์
๋ฐ์ดํธ**: ์ ๊ธฐ์ ์ผ๋ก ์๋ก์ด ๊ธฐ์ฌ ์ถ๊ฐ
|
| 222 |
-
|
| 223 |
-
### ๐ฏ ๊ธฐ๋ฅ
|
| 224 |
-
- ๐ **์ ํํ ํค์๋ ๋งค์นญ**: ์
๋ ฅํ ๋จ์ด์ ์ ํํ ์ผ์นํ๋ ๊ธฐ์ฌ ์ฐ์
|
| 225 |
-
- ๐ **๊ด๋ จ๋ ์ ์ ๋ ฌ**: ๊ฐ์ฅ ๊ด๋ จ์ฑ ๋์ ๊ฒฐ๊ณผ ๋จผ์ ํ์
|
| 226 |
-
- ๐
**์๊ฐ์ ์ถ์ **: ์ฌ๊ฑด์ ์๊ฐ์ ํ๋ฆ ํ์
๊ฐ๋ฅ
|
| 227 |
-
- ๐ฅ **์ธ๋ฌผ/์ฌ๊ฑด ์ฐ๊ฒฐ**: ๊ด๋ จ ์ธ๋ฌผ๊ณผ ์ฌ๊ฑด ์๋ ํ์
|
| 228 |
-
|
| 229 |
-
---
|
| 230 |
-
๐ป **๊ธฐ์ ์คํ**: BM25 + Contextual Retrieval | ๐๏ธ **ํ๋ซํผ**: Hugging Face Spaces
|
| 231 |
-
""")
|
| 232 |
-
|
| 233 |
-
else:
|
| 234 |
-
st.error("โ ์์คํ
์ ๋ก๋ํ ์ ์์ต๋๋ค.")
|
| 235 |
-
st.info("๐ง ํ์ํ ๋ฐ์ดํฐ ํ์ผ๋ค์ด ์
๋ก๋๋์ง ์์์ ์ ์์ต๋๋ค.")
|
| 236 |
-
|
| 237 |
-
# ๋๋ฒ๊น
์ ๋ณด
|
| 238 |
-
with st.expander("๐ ๋๋ฒ๊น
์ ๋ณด"):
|
| 239 |
-
st.write("ํ์ํ ํ์ผ๋ค:")
|
| 240 |
-
files_status = {}
|
| 241 |
-
for file in ['embeddings.npy', 'bm25_index.pkl', 'processed_data.pkl', 'system_metadata.json']:
|
| 242 |
-
exists = os.path.exists(file)
|
| 243 |
-
files_status[file] = "โ
" if exists else "โ"
|
| 244 |
-
|
| 245 |
-
st.json(files_status)
|
| 246 |
|
| 247 |
-
|
| 248 |
-
|
|
|
|
|
|
| 1 |
+
# ์ต์ ํ
์คํธ ์ฑ - ๋คํธ์ํฌ ์๋ฌ ๋๋ฒ๊น
์ฉ
|
| 2 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import os
|
| 4 |
+
import json
|
| 5 |
+
|
| 6 |
+
st.title("๐ ์ฌ๋ก๋ ํฐ ๊ฒ์ - ๋๋ฒ๊น
")
|
| 7 |
+
|
| 8 |
+
st.write("## ๐ ํ์ผ ํ์ธ")
|
| 9 |
+
|
| 10 |
+
# ํ์ฌ ๋๋ ํ ๋ฆฌ ํ์ผ ๋ชฉ๋ก
|
| 11 |
+
files = os.listdir(".")
|
| 12 |
+
st.write("ํ์ฌ ๋๋ ํ ๋ฆฌ ํ์ผ๋ค:")
|
| 13 |
+
for file in files:
|
| 14 |
+
size = os.path.getsize(file) if os.path.isfile(file) else "ํด๋"
|
| 15 |
+
st.write(f"- {file}: {size}")
|
| 16 |
+
|
| 17 |
+
st.write("## ๐ ์์คํ
ํ์ผ ํ
์คํธ")
|
| 18 |
+
|
| 19 |
+
# ๊ฐ ํ์ผ ๊ฐ๋ณ ํ
์คํธ
|
| 20 |
+
test_results = {}
|
| 21 |
+
|
| 22 |
+
# 1. ๋ฉํ๋ฐ์ดํฐ ํ์ผ ํ
์คํธ
|
| 23 |
+
try:
|
| 24 |
+
with open("system_metadata.json", 'r') as f:
|
| 25 |
+
metadata = json.load(f)
|
| 26 |
+
test_results["system_metadata.json"] = f"โ
์ฑ๊ณต - {len(metadata)} ๊ฐ ํญ๋ชฉ"
|
| 27 |
+
st.json(metadata)
|
| 28 |
+
except Exception as e:
|
| 29 |
+
test_results["system_metadata.json"] = f"โ ์คํจ: {e}"
|
| 30 |
+
|
| 31 |
+
# 2. ๋ค๋ฅธ ํ์ผ๋ค ํฌ๊ธฐ๋ง ํ์ธ
|
| 32 |
+
for file in ["embeddings.npy", "bm25_index.pkl", "processed_data.pkl"]:
|
| 33 |
+
try:
|
| 34 |
+
size = os.path.getsize(file)
|
| 35 |
+
test_results[file] = f"โ
ํ์ผ ์กด์ฌ - {size/1024/1024:.1f}MB"
|
| 36 |
+
except Exception as e:
|
| 37 |
+
test_results[file] = f"โ ํ์ผ ์์: {e}"
|
| 38 |
+
|
| 39 |
+
st.write("## ๐ ํ
์คํธ ๊ฒฐ๊ณผ")
|
| 40 |
+
for file, result in test_results.items():
|
| 41 |
+
st.write(f"**{file}**: {result}")
|
| 42 |
|
| 43 |
+
# ๊ฐ๋จํ ๊ฒ์ ์ธํฐํ์ด์ค (ํ์ผ ๋ก๋ฉ ์์ด)
|
| 44 |
+
st.write("## ๐งช ๊ธฐ๋ณธ ๊ธฐ๋ฅ ํ
์คํธ")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
search_query = st.text_input("ํ
์คํธ ๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์:")
|
|
|
|
| 47 |
|
| 48 |
+
if search_query:
|
| 49 |
+
st.success(f"์
๋ ฅ๋ฐ์ ๊ฒ์์ด: {search_query}")
|
| 50 |
+
st.info("ํ์ผ ๋ก๋ฉ์ ๋ค์ ๋จ๊ณ์์ ํ
์คํธํฉ๋๋ค.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
+
# ์์คํ
์ ๋ณด
|
| 53 |
+
st.write("## โ๏ธ ์์คํ
์ ๋ณด")
|
| 54 |
+
st.write(f"Python ๋ฒ์ : {st.__version__}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
# ๋คํธ์ํฌ ํ
์คํธ ์ ๊ฑฐ
|
| 57 |
+
st.write("## โ
๋คํธ์ํฌ ํธ์ถ ์์")
|
| 58 |
+
st.success("์ด ์ฑ์ ์ด๋ค ์ธ๋ถ ๋คํธ์ํฌ ํธ์ถ๋ ํ์ง ์์ต๋๋ค.")
|