[스트림릿 앱만들기] 톰소여의 모험
아직 완성된 앱은 아니지만, 앱을 개발하여 배포하는 과정까지의 과정에서 필요한 지식을 습득하고 훈련하기에 좋은 프로젝트였습니다. 아래는 미완성된 앱이지만, 링크를 달아두었습니다.
달빛 단어 포섭 앱
아래는 톰 소여 이야기를 입혀서 교안으로 만들어 본 것입니다.
챕터 1: 마법의 도구
섹션 1.1: 마법의 도구 가져오기
톰 소여와 친구들이 숲 속을 탐험하다가 마법의 책을 발견했어요. 이 책에는 다양한 마법 도구들이 들어있어요. 톰과 친구들이 이 마법 도구들을 어떻게 사용할 수 있을지 배워봅시다.
import streamlit as st
import base64
import requests
from bs4 import BeautifulSoup
import pandas as pd
from textblob import TextBlob
import streamlit as st
: 톰의 모험 일지를 작성하는 마법의 도구에요.import base64
: 마법의 비밀 언어를 해독하는 도구에요.import requests
: 멀리 있는 정보를 가져오는 마법의 부엉이에요.from bs4 import BeautifulSoup
: 필요한 정보를 찾는 마법의 돋보기에요.import pandas as pd
: 중요한 데이터를 정리해주는 마법의 장부에요.from textblob import TextBlob
: 텍스트를 이해하고 처리해주는 마법의 책이에요.
챕터 2: 마법 무대 준비하기
섹션 2.1: 무대 꾸미기
모든 마법 공연은 멋진 무대가 필요해요. 여기서는 우리의 마법 웹 페이지를 준비해볼 거에요.
st.set_page_config(
page_title="Darlbit Word Subsumption",
page_icon="📚"
)
st.set_page_config
: 우리 페이지에 제목과 아이콘을 설정해주는 마법이에요. 여기서는 "Darlbit Word Subsumption"이라는 제목과 책 아이콘 📚을 설정했어요.
섹션 2.2: 무대 장식하기
마법 공연은 특별한 효과가 필요하죠. 여기서는 CSS라는 마법을 사용해 페이지를 멋지게 꾸며볼 거에요.
st.markdown(
"""
<style>
body {
background-image: url('https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxlrcU%2FbtsyynNTUpV%2F645uH8YylBDseYgqvXXK5k%2Fimg.png');
background-size: cover;
}
h1 {
color: #4CAF50;
}
.link-box {
padding: 10px;
border: 2px solid #4CAF50;
border-radius: 5px;
margin: 5px;
background-color: #f9f9f9;
text-align: center;
}
img {
max-height: 200px;
}
.streamlit-expanderContent {
display: flex;
flex-direction: column;
align-items: center;
}
[data-testid="stExpander"] button {
display: none;
}
.streamlit-expanderHeader {
pointer-events: none;
}
[data-testid="stImage"] button {
display: none;
}
</style>
""",
unsafe_allow_html=True
)
st.markdown("""<style>...</style>""", unsafe_allow_html=True)
: 이 마법은 페이지의 배경을 멋진 이미지로 바꾸고, 색상을 바꾸고, 여러 요소들을 스타일링해요.
챕터 3: 마법 재료 준비하기
섹션 3.1: 재료 모으기
톰과 친구들은 마법을 실행하기 전에 필요한 재료들을 준비해요. 여기서는 단어와 삭제된 단어를 저장할 준비를 해요.
if 'words' not in st.session_state:
st.session_state.words = []
if 'deleted_words' not in st.session_state:
st.session_state.deleted_words = []
st.session_state.words
와st.session_state.deleted_words
: 이 마법 가방은 단어들과 삭제된 단어들을 저장해요. 가방이 아직 없다면, 새로 만들어줘요.
섹션 3.2: 마법 부리기
톰은 강력한 주문을 외워서 사전을 찾아내요. 그 주문이 바로 여기 있어요:
def fetch_word_details(word):
url = f"https://www.dictionary.com/browse/{word}"
response = requests.get(url)
if response.status_code == 200:
soup = BeautifulSoup(response.content, "html.parser")
try:
definition = soup.find("div", {"value": "1"}).text.strip()
except AttributeError:
definition = "Definition not found"
try:
ipa = soup.find("span", {"class": "pron-spell-content"}).text.strip()
except AttributeError:
ipa = None
synonyms, antonyms, example_sentence = "Not found", "Not found", "Not found"
try:
synonyms = ', '.join([syn.text for syn in soup.find_all("a", {"class": "css-1gyuw4i eh475bn0"})[:5]])
except AttributeError:
pass
try:
antonyms = ', '.join([ant.text for ant in soup.find_all("a", {"class": "css-lv3ht0 eh475bn0"})[:5]])
except AttributeError:
pass
try:
example_sentence = soup.find("div", {"class": "css-pnw38j e15kc6du6"}).text.strip()
except AttributeError:
pass
return definition, ipa, synonyms, antonyms, example_sentence
return "Definition not found", None, "Not found", "Not found", "Not found"
def fetch_word_details(word)
: 이 마법 주문은 사전 웹사이트에 가서 단어의 정의, 발음기호(IPA), 동의어, 반의어, 예문을 찾아요.url = f"https://www.dictionary.com/browse/{word}"
: 단어의 정보를 숨겨놓은 마법의 주소에요.response = requests.get(url)
: 우리의 마법 부엉이가 페이지를 가져와요.BeautifulSoup(response.content, "html.parser")
: 마법의 돋보기가 필요한 정보를 찾아줘요.try...except
: 이 보호 주문은 일부 정보가 없을 때 안전하게 넘어가게 해줘요.
챕터 4: 마법 공연 시작하기
섹션 4.1: 무대 세팅하기
이제 톰과 친구들이 마법 무대를 준비해요. 주요 내용과 사용법 안내를 위한 칼럼을 설정해요.
col1, col2 = st.columns([3, 1])
st.columns([3, 1])
: 두 개의 칼럼을 만들어요. 첫 번째 칼럼(col1)은 메인 콘텐츠를 위해 더 크고(3 부분), 두 번째 칼럼(col2)은 사용법 안내를 위해 더 작아요(1 부분).
섹션 4.2: 마법 공연하기
col1에서 메인 마법을 보여줘요:
with col1:
st.title("Darlbit Word Subsumption")
uploaded_file = st.file_uploader("CSV 파일 업로드", type=["csv"])
uploaded_text_file = st.file_uploader("텍스트 파일 업로드", type=["txt"])
if uploaded_file is not None:
df = pd.read_csv(uploaded_file)
st.write("업로드된 데이터")
st.dataframe(df)
required_columns = ['word', 'difficulty', 'topic', 'source', 'important']
for col in required_columns:
if col not in df.columns:
df[col] = 'Unknown' if col != 'important' else False
words = df['word'].tolist()
for new_word in words:
if new_word:
word_details, ipa, synonyms, antonyms, example_sentence = fetch_word_details(new_word)
new_word_entry = {
"word": new_word,
"part_of_speech": "Not found",
"example_sentence": example_sentence,
"synonyms": synonyms,
"antonyms": antonyms,
"image_url": "https://via.placeholder.com/150",
"difficulty": df.loc[df['word'] == new_word, 'difficulty'].values[0],
"topic": df.loc[df['word'] == new_word, 'topic'].values[0],
"source": df.loc[df['word'] == new_word, 'source'].values[0],
"important": df.loc[df['word'] == new_word, 'important'].values[0],
"definition": word_details,
"ipa": ipa
}
st.session_state.words.append(new_word_entry)
st.success("CSV 파일에서 단어 리스트가 성공적으로 추가되었습니다!")
if uploaded_text_file is not None:
text = uploaded_text_file.read().decode("utf-8")
st.text_area("업로드된 텍스트", text, height=200)
if st.button("입력"):
blob = TextBlob(text)
words = list(set(blob.words))
for new_word in words:
word_details, ipa, synonyms, antonyms, example_sentence = fetch_word_details(new_word)
new_word_entry = {
"word": new_word,
"part_of_speech": "Not found",
"example_sentence": example_sentence,
"synonyms": synonyms,
"antonyms": antonyms,
"image_url": "https://via.placeholder.com/150",
"difficulty": "Unknown",
"topic": "General",
"source": "Uploaded Text",
"important": False,
"definition": word_details,
"ipa": ipa
}
st.session_state.words.append(new_word_entry)
st.success("텍스트 파일에서 단어 리스트가 성공적으로 추가되었습니다!")
st.file_uploader
: 이 마법의 문을 통해 사용자들이 파일을 업로드할 수 있어요. 하나는 CSV 파일용, 하나는 텍스트 파일용이에요.if uploaded_file is not None
: CSV 파일이 업로드되면, 이를 열어서 데이터를 읽고, 무대에 보여줘요.required_columns
: CSV 파일에 필요한 재료들(열)이에요.fetch_word_details(new_word)
: 각 단어에 대해 주문을 사용해 세부 정보를 가져와요.st.session_state.words.append(new_word_entry)
: 단어 정보를 우리의 마법 가방(세션 상태)에 저장해요.st.success
: 모든 것이 잘 되면, 관객들에게 성공 메시지를 보여줘요.
섹션 4.3: 단어 필터링 및 표시
이제 필터를 추가하고 단어들을 테이블 형식으로 보여줘요:
selected_difficulty = st.selectbox('난이도로 필터', ['모두', '쉬움', '중간', '어려움'])
selected_topic = st.text_input('주제로 필터')
show_deleted = st.checkbox('삭제된 단어 보기')
def display_words(words):
data = []
for i, word_entry in enumerate(words):
word_info = {
"단어": word_entry["word"],
"품사": word_entry['part_of_speech'],
"예문": word_entry['example_sentence'],
"동의어": word_entry['synonyms'],
"반의어": word_entry['antonyms'],
"난이도": word_entry['difficulty'],
"주제": word_entry['topic'],
"예문 출처": word_entry['source'],
"정의": word_entry['definition'],
"발음기호": word_entry['ipa'] if word_entry['ipa'] else "없음",
"중요 단어": "🌟" if word_entry["important"] else ""
}
data.append(word_info)
st.image(word_entry["image_url"], caption=word_entry["word"])
df = pd.DataFrame(data)
st.dataframe(df)
csv = df.to_csv(index=False)
b64 = base64.b64encode(csv.encode()).decode()
href = f'<a href="data:file/csv;base64,{b64}" download="words.csv">CSV 파일 다운로드</a>'
st.markdown(href, unsafe_allow_html=True)
filtered_words = st.session_state.words
if selected_difficulty != '모두':
filtered_words = [word for word in filtered_words if word['difficulty'] == selected_difficulty]
if selected_topic:
filtered_words = [word for word in filtered_words if selected_topic.lower() in word['topic'].lower()]
display_words(filtered_words)
if show_deleted:
st.markdown("### 삭제된 단어")
display_words(st.session_state.deleted_words)
if st.button('모든 삭제된 단어 복원'):
st.session_state.words.extend(st.session_state.deleted_words)
st.session_state.deleted_words = []
st.experimental_rerun()
st.selectbox
,st.text_input
,st.checkbox
: 이 필터들은 관객들이 특정 단어들을 찾는 것을 도와줘요.display_words
함수: 이 마법 함수는 단어들을 테이블 형식으로 보여주고, 각 단어의 세부 정보와 이미지를 보여줘요. 또한 데이터를 다운로드할 수 있는 버튼도 있어요.filtered_words
: 난이도와 주제에 따라 단어들을 필터링해요.st.button('모든 삭제된 단어 복원')
: 이 버튼을 클릭하면 삭제된 모든 단어를 다시 복원해요.
챕터 5: 관객에게 사용법 설명하기
섹션 5.1: 사용법 안내
col2에서 우리의 마법 도구 사용법을 간단하게 안내해요:
with col2:
st.title("사용법 안내")
st.markdown(
"""
### Darlbit Word Subsumption 사용법
1. **CSV 파일 업로드**: CSV 파일을 업로드하여 단어 목록을 추가합니다.
- 필요한 열: `word`, `difficulty`, `topic`, `source`, `important`
2. **텍스트 파일 업로드**: 분석할 텍스트 파일을 업로드합니다.
3. **입력 버튼 클릭**: 텍스트를 분석하기 위해 '입력' 버튼을 클릭합니다.
4. **단어 목록 확인**: 업로드된 단어 목록을 테이블에서 확인할 수 있습니다.
5. **필터 사용**: 난이도와 주제로 단어 목록을 필터링합니다.
6. **삭제된 단어 복원**: 삭제된 단어를 복원할 수 있습니다.
7. **데이터 다운로드**: 단어 목록을 CSV 파일로 다운로드합니다.
### 예제 CSV 파일
아래 버튼을 클릭하여 예제 CSV 파일을 다운로드 받으세요.
"""
)
example_data = {
"word": ["cogitate", "perspicacious", "loquacious"],
"difficulty": ["어려움", "중간", "쉬움"],
"topic": ["thinking", "perception", "speaking"],
"source": ["example.com", "example.com", "example.com"],
"important": [True, False, True]
}
example_df = pd.DataFrame(example_data)
example_csv = example_df.to_csv(index=False)
b64_example = base64.b64encode(example_csv.encode()).decode()
example_href = f'<a href="data:file/csv;base64,{b64_example}" download="example_words.csv">예제 CSV 파일 다운로드</a>'
st.markdown(example_href, unsafe_allow_html=True)
st.markdown(
"""
### 데이터 저장 안내
앱을 종료하기 전에 단어 목록을 CSV 파일로 다운로드하여 데이터를 저장하세요.
다음 번에 이 파일을 업로드하여 이전 데이터를 불러올 수 있습니다.
"""
)
사용법 안내: 앱의 각 부분을 어떻게 사용하는지 설명해요. 이 설명을 통해 톰과 친구들은 마법 도구를 쉽게 사용할 수 있어요.
섹션 5.2: 관련 링크
마지막으로, 우리의 마법 도구와 관련된 링크들을 공유해요:
st.markdown("### 관련 링크")
link_data = [
("🌐", "사이트", "https://spiffy-fig-443.notion.site/Reason-of-Moon-c68af35321f24e418bec2b804adadf7a"),
("🐦", "Twitter", "https://twitter.com/reasonofmoon"),
("📷", "Instagram", "https://www.instagram.com/darlsam37"),
("▶️", "YouTube1", "https://www.youtube.com/@reasonofmoon"),
("▶️", "YouTube2", "https://www.youtube.com/@meta_prompt"),
("🌙", "Moonlang", "https://moonlang.com")
]
cols = st.columns(2)
for i, (emoji, name, url) in enumerate(link_data):
with cols[i % 2]:
st.markdown(f'<div class="link-box"><a href="{url}" target="_blank">{emoji} {name}</a></div>', unsafe_allow_html=True)
관련 링크: 이 링크들은 다른 마법 세계로 가는 포탈이에요. 여기서 더 많은 정보를 찾고, 연결될 수 있어요.
이렇게 톰과 친구들은 마법 무대를 준비하고, 재료를 모으고, 마법 공연을 시작하고, 관객들에게 사용법을 설명했어요. 이 모든 과정을 통해 우리는 코딩의 힘을 배웠고, 흥미진진한 모험을 함께 했어요!