검색
검색
공개 노트 검색
회원가입로그인

[스트림릿 앱만들기] 톰소여의 모험

reasonofmoon_a_vivid_highly_detailed_illustration_of_chibi_Tom__c6d4d52d-2641-40af-8d77-3367496a1e43

아직 완성된 앱은 아니지만, 앱을 개발하여 배포하는 과정까지의 과정에서 필요한 지식을 습득하고 훈련하기에 좋은 프로젝트였습니다. 아래는 미완성된 앱이지만, 링크를 달아두었습니다.

달빛 단어 포섭 앱


아래는 톰 소여 이야기를 입혀서 교안으로 만들어 본 것입니다.

챕터 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.wordsst.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)
  • 관련 링크: 이 링크들은 다른 마법 세계로 가는 포탈이에요. 여기서 더 많은 정보를 찾고, 연결될 수 있어요.


이렇게 톰과 친구들은 마법 무대를 준비하고, 재료를 모으고, 마법 공연을 시작하고, 관객들에게 사용법을 설명했어요. 이 모든 과정을 통해 우리는 코딩의 힘을 배웠고, 흥미진진한 모험을 함께 했어요!

reasonofmoon_a_vivid_highly_detailed_illustration_of_chibi_Tom__b96912aa-88ec-4423-8647-887cb9a3b513

조회수 : 167
heart
공유하기
카카오로 공유하기
페이스북 공유하기
트위터로 공유하기
url 복사하기
T
페이지 기반 대답
AI Chat