Streamlit의 기본 개념과 예제

Streamlit의 기본 개념과 예제

다음 링크에 있는 Streamlit 공식 문서 중 기본 개념 부분(Basic concepts)을 기반으로 정리했습니다. 이후 나머지 부분도 정리할 예정입니다.

https://docs.streamlit.io/get-started/fundamentals

최초작성 2025. 6. 25

Streamlit 개발환경 만들기와 실행하는 방법은 다음 포스트를 먼저 참고하세요.

Streamlit을 간단히 사용해보기

https://webnautes.kr/streamliteul-gandanhi-sayonghaebogi/ 

패키지 설치

streamlit 패키지를 설치해야 합니다.

pip install streamlit

miniconda 개발 환경을 만들어서 진행했습니다.

Visual Studio Code와 Miniconda를 사용한 Python 개발 환경 만들기( Windows, Ubuntu, WSL2)https://webnautes.kr/visual-studio-codewa-minicondareul-sayonghan-python-gaebal-hwangyeong-mandeulgi-windows-ubuntu-wsl2/

Streamlit 코드 실행 방법

Streamlit을 사용한 파이썬 코드는 다음 명령을 사용하여 실행할 수 있습니다.

streamlit run your_script.py 

위와 같이 실행하면 로컬 스트림릿 서버가 실행된후 기본 웹 브라우저의 새 탭에서 앱이 열립니다. 앱은 차트, 텍스트, 위젯, 표 등을 그릴 수 있는 캔버스입니다. 예를 들어 st.text를 사용하여 앱에 텍스트를 출력하고 st.line_chart를 사용하여 앱에 꺽은선 그래프를 그릴 수 있습니다.

파이썬 스크립트에 사용자 정의 인수를 전달할 때는 두 개의 대시(--) 뒤에 인수를 전달해야 합니다. 그렇지 않으면 인수가 Streamlit 자체에 대한 인수로 해석됩니다.

streamlit run your_script.py -- script args

Streamlit을 실행하는 또 다른 방법은 Python 모듈로 실행하는 것입니다. 이 방법은 Streamlit과 함께 작동하도록 PyCharm과 같은 IDE를 구성할 때 유용할 수 있습니다:

python -m streamlit run your_script.py

개발 흐름

스트림릿 앱을 개발할 때는 에디터와 브라우저 창을 나란히 배치하여 코드와 앱을 동시에 볼 수 있도록 하는 것이 좋다하여 다음처럼 크롬과 Visual Studio Code를 나란히 배치하여 진행해봤습니다.

코드를 수정후 저장하면 다음처럼 웹브라우저 오른쪽 상단에 보입니다. 파일이 변경되었다고 표시되는데 파일 저장후 Rerun을 매번 클릭하거나 파일 저장시 자동으로 재시작되도록 Always rerun을 클릭합니다.

Always rerun을 클릭 후에는 파일을 저장할때마다 웹브라우저가 갱신됩니다.

Streamlit 버전 1.10.0 이상부터는 Linux 배포판의 루트 디렉토리에서 Streamlit 앱을 실행할 수 없습니다. 루트 디렉토리에서 Streamlit 앱을 실행하려고 하면 Streamlit에서 FileNotFoundError를 발생시킵니다.

데이터 흐름

Streamlit의 아키텍처를 사용하면 일반 Python 스크립트를 작성하는 것과 동일한 방식으로 앱을 작성할 수 있습니다. 이를 위해 Streamlit 앱에는 고유한 데이터 흐름이 있습니다.

일반적인 웹 앱은 특정 이벤트가 발생하면 해당 함수만 실행되지만 Streamlit은 이벤트가 발생하면 전체 코드가 처음부터 다시 실행됩니다. 즉 화면에서 무언가를 업데이트해야 할 때마다 Streamlit은 전체 Python 스크립트를 처음부터 끝까지 다시 실행합니다.

전체 파이썬 코드를 다시 실행하는 상황은 다음 두 가지입니다.

  • 앱의 소소 코드를 수정후 저장할 때
  • 사용자가 앱의 위젯과 상호작용할 때. 예를 들어 버튼 클릭시

사용자가 슬라이더를 조작할 수 있는 간단한 예제를 살펴봅니다.

import streamlit as st

 
# 슬라이더를 움직일때 마다 코드 처음부터 다시 실행됩니다.
print("이 코드는 매번 실행됩니다")

x = 10  # 매번 변수 x가 다시 선언됩니다.

st.slider("슬라이더", 0, 100# 슬라이더도 매번 다시 선언됩니다.

x=x+1 # 변수 x에 1을 더해줍니다.

print("이 코드도 매번 실행됩니다", x) # 변수 x의 값을 출력해봅니다   

실행해보면 슬라이더를 움직일 때마다 파이썬 전체 코드가 다시 실행되어 매번 변수 x는 10으로 다시 선언되기 때문에 변수 x에 1을 더한 11이 항상 출력되는 것을 볼 수 있습니다.

이 코드는 매번 실행됩니다
이 코드도 매번 실행됩니다 11
이 코드는 매번 실행됩니다
이 코드도 매번 실행됩니다 11
이 코드는 매번 실행됩니다
이 코드도 매번 실행됩니다 11
이 코드는 매번 실행됩니다
이 코드도 매번 실행됩니다 11

슬라이더를 움직일때 마다 파이썬 전체 코드가 다시 실행되어 비효율적일 수 있습니다. 이 문제는 뒤에서 살펴볼 캐싱으로 해결가능합니다.

콜백

콜백 button_callback이 on_click 매개변수를 통해 위젯에 전달될 때마다 콜백은 항상 스크립트의 나머지 부분보다 먼저 실행됩니다.

다음 예제를 통해 살펴보겠습니다.

import streamlit as st

def button_callback():
    st.session_state.callback_executed = True
    print("🔥 콜백 함수가 실행되었습니다!")
    st.session_state.callback_count = st.session_state.get('callback_count', 0) + 1

print("📝 메인 스크립트 시작")

st.title("콜백 실행 순서 예시")

if st.button("클릭하세요!", on_click=button_callback):
    print("💡 버튼이 클릭되었습니다 (메인 스크립트)")


if st.session_state.get('callback_executed'):
    st.success(f"콜백이 {st.session_state.get('callback_count', 0)}번 실행되었습니다!")


print("✅ 메인 스크립트 끝")

클릭하세요 버튼을 클릭할 때마다 콜백이 몇번 실행되었는지가 출력됩니다.

다음 실행결과를 보면 콜백 함수는 항상 메인 스크립트보다 먼저 실행됩니다콜백에서 st.session_state.callback_count 값을 수정하면, 메인 스크립트에서 바로 그 값을 가져와 사용할 수 있습니다이를 통해 위젯 상호작용에 즉시 반응하는 로직을 구현할 수 있습니다

🔥 콜백 함수가 실행되었습니다!
📝 메인 스크립트 시작
💡 버튼이 클릭되었습니다 (메인 스크립트)
✅ 메인 스크립트 끝

캐싱

캐시를 사용하지 않는 코드입니다. 슬라이더를 움직일 때마다 expensive_calculation 함수가 실행되어 3초를 대기하도록 만들었기 때문에 슬라이더값이 변경될때까지 좀 시간이 걸립니다.

import streamlit as st
import time

def expensive_calculation():
    time.sleep(3# 3초 걸리는 무거운 계산
    return "계산 완료!"

st.title("느린 앱")
result = expensive_calculation()  # 매번 3초씩 기다림
st.write(result)

slider_value = st.slider("값 조정", 0, 100)
st.write(f"슬라이더 값: {slider_value}")

실행결과입니다.

캐시를 사용하면 속도를 개선할 수 있습니다. 캐시 데코레이터인 @st.cache_data를 사용하여 함수의 실행 결과를 메모리에 저장하여 앱 속도를 향상시킵니다.

동작 방식:

  • 첫 번째 실행: expensive_calculation() 함수가 실행되어 3초 대기 후 결과를 메모리에 저장합니다.
  • 이후 실행: 함수를 다시 실행하지 않고 캐시된 결과를 즉시 반환합니다. (대기시간 0초)

실제 효과: 슬라이더를 조작할 때마다 Streamlit이 전체 코드를 재실행하지만, 캐시 덕분에 expensive_calculation() 함수는 최초에만 3초 대기하고 이후부턴 3초씩 기다리지 않고 즉시 완료됩니다.

import streamlit as st
import time

@st.cache_data  # 🚀 캐시 데코레이터
def expensive_calculation():
    time.sleep(3# 3초 걸리는 무거운 계산
    return "계산 완료!"

st.title("빠른 앱")
result = expensive_calculation()  # 첫 번째만 3초, 이후는 즉시!
st.write(result)

slider_value = st.slider("값 조정", 0, 100)
st.write(f"슬라이더 값: {slider_value}")

데이터 표시 및 스타일 지정

스트림릿 앱에서 데이터(표, 배열, 데이터 프레임)를 표시하는 방법에는 몇 가지가 있습니다. 아래에서는 텍스트부터 표까지 무엇이든 작성하는 데 사용할 수 있는 magic과 st.write()에 대해 소개합니다.

magic 사용

Streamlit 메서드를 호출하지 않고도 앱에 쓸 수 있습니다. Streamlit은 “magic 명령”을 지원하므로 st.write()를 사용하지 않고 변수 이름만 적어도 화면에 출력됩니다.

import streamlit as st
import pandas as pd

df = pd.DataFrame({
  'first column': [1, 2, 3, 4],
  'second column': [10, 20, 30, 40]
})

df

위 코드 실행결과 입니다. df만 적었을 뿐인데도 데이터프레임이 출력되었습니다. 왜냐하면 변수나 리터럴 값을 발견할 때마다 st.write()를 사용하여 자동으로 앱에 해당 값을 출력하기 때문입니다.

데이터 프레임 쓰기

텍스트, 데이터, Matplotlib 도형, Altair 차트 등 거의 모든 것을 st.write()에 전달할 수 있습니다. Streamlit이 알아서 화면에 출력해줍니다.

import streamlit as st
import pandas as pd

st.write('테스트입니다.')
st.write(pd.DataFrame({
    'first column': [1, 2, 3, 4],
    'second column': [10, 20, 30, 40]
}))

실행결과입니다. st.write가 텍스트와 데이터 프레임을 알아서 출력해주었습니다.

st.write() 함수가 알아서 출력해주지만 추가 기능을 제공해주는 st.dataframe()와 st.table() 함수도 준비 되어 있습니다.

예를 들어 데이터 프레임을 만들고 판다스 스타일러 객체를 사용하여 서식을 변경해 보겠습니다. 이 예제에서는 무작위 샘플을 생성하기 위해 Numpy를 사용하고 대화형 테이블을 그리기 위해 st.dataframe() 메서드를 사용합니다.

판다스 스타일러 객체를 사용하여 대화형 테이블의 일부 요소를 강조 표시하는 첫 번째 예제를 확장해 보겠습니다.

import streamlit as st
import numpy as np
import pandas as pd

dataframe = pd.DataFrame(
    np.random.randn(10, 20),
    columns=('col %d' % i for i in range(20)))

st.dataframe(dataframe.style.highlight_max(axis=0))

실행해보면 컬럼의 최대값을 노란색 배경으로 하이라이트해줍니다.

st.table 함수는 생성된 데이터프레임을 고정된 표 형태로 화면에 출력합니다. 즉, 모든 데이터를 한 번에 표시하기 때문에 스크롤이 되지 않으며 읽기 전용입니다.

import streamlit as st
import numpy as np
import pandas as pd

dataframe = pd.DataFrame(
    np.random.randn(10, 20),
    columns=('col %d' % i for i in range(20)))
st.table(dataframe)

실행결과입니다.

차트와 지도 그리기

Streamlit은 Matplotlib, Altair, deck.gl 등과 같은 여러가지 데이터 차트 그리기를 지원합니다.

꺾은선 그래프 그리기

st.line_chart()를 사용하여 앱에 꺽은선 그래프를 쉽게 추가할 수 있습니다. Numpy를 사용하여 무작위 샘플을 생성한 다음 차트로 만들어 보겠습니다.

import streamlit as st
import numpy as np
import pandas as pd

chart_data = pd.DataFrame(
    np.random.randn(20, 3),
    columns=['a', 'b', 'c'])

st.line_chart(chart_data)

지도 그리기

st.map()을 사용하면 데이터 요소를 지도에 표시할 수 있습니다. Numpy를 사용하여 몇 가지 샘플 데이터를 생성하고 샌프란시스코 지도에 그려 보겠습니다.

import streamlit as st
import numpy as np
import pandas as pd

map_data = pd.DataFrame(
    np.random.randn(1000, 2) / [50, 50] + [37.76, -122.4],
    columns=['lat', 'lon'])

st.map(map_data)

위젯

Streamlit의 장점 중 하나는 위젯을 일반 변수처럼 취급할 수 있다는 점입니다. 이는 복잡한 콜백 함수나 이벤트 처리 없이도 직관적인 코드 작성을 가능하게 합니다.

슬라이더를 예로 들어 간단히 살펴보겠습니다.

사용자가 슬라이더를 조작하면 페이지가 자동으로 다시 실행되어 변경된 값이 즉시 화면에 반영됩니다.

import streamlit as st

x = st.slider('x'# 슬라이더 위젯입니다.

# 슬라이더 위젯의 값을 변수 참조로 바로 가져와 출력합니다.
st.write(x, '의 제곱은 ', x * x)

실행후, 슬라이더를 이동하여 얻은 값을 사용하여 바로 아래에 문자열 출력시 숫자와 숫자의 제곱을 출력하기 위해 사용합니다.

체크박스를 사용하여 데이터프레임을 표시/숨기기

체크박스를 사용하여 데이터프레임의 표시 여부를 결정하는 예제코드입니다.

import streamlit as st
import numpy as np
import pandas as pd

# 체크박스가 체크되어 있는지 검사합니다.
if st.checkbox('Show dataframe'):

    # 데이터프레임을 화면에 출력합니다.
    chart_data = pd.DataFrame(
      np.random.randn(20, 3),
      columns=['a', 'b', 'c'])

    chart_data

실행하면 체크박스가 보입니다.

체크 박스를 체크하면 데이터프레임이 보여지고 다시 체크해제하면 데이터 프레임이 숨겨집니다.

selectbox 사용하여 데이터프레임의 행을 필터링링

st.selectbox를 사용하여 선택한 값에 해당되는 데이터프레임의 행을 보여주는 예제코드입니다.

import streamlit as st
import pandas as pd

# 나라와 수도 데이터프레임
df = pd.DataFrame({
  'country': ['대한민국', '일본', '중국', '미국', '프랑스', '독일'],
  'capital': ['서울', '도쿄', '베이징', '워싱턴D.C.', '파리', '베를린'],
})

# 나라 이름을 선택 옵션으로 제공
selected_country = st.selectbox(
  '나라를 선택하세요:',
  df['country'# country 컬럼의 모든 값이 드롭다운 옵션으로 표시됨
)

# 선택된 나라에 해당하는 행 필터링
filtered_df = df[df['country'] == selected_country]
st.write(f"**{selected_country}**의 정보:")
st.write(filtered_df)

# 선택된 나라의 수도만 따로 표시
selected_capital = filtered_df['capital'].iloc[0]
st.write(f"{selected_country}의 수도는 **{selected_capital}**입니다.")

selectbox에서 선택한 나라에 해당하는 데이터프레임의 행을 필터링해서 출력해줍니다.

레이아웃

st.sidebar를 사용하여 왼쪽에 위치한 사이드바에 위젯을 구성해봅니다.

예를 들어 사이드바에 선택 상자와 슬라이더를 추가하려는 경우 st.slider 및 st.selectbox 대신 st.sidebar.slider 및 st.sidebar.selectbox를 사용합니다:

import streamlit as st

# 사이드바에 선택 상자 추가
add_selectbox = st.sidebar.selectbox(
    'How would you like to be contacted?',
    ('Email', 'Home phone', 'Mobile phone')
)

# 사이드바에 슬라이더 추가
add_slider = st.sidebar.slider(
    'Select a range of values',
    0.0, 100.0, (25.0, 75.0)
)

실행결과입니다.

st.columns를 사용하면 위젯을 나란히 배치할 수 있습니다.

import streamlit as st


left_column, right_column = st.columns(2)

# 왼쪽 컬럼에 버튼을 배치합니다.
left_column.button('Press me!')

# 오른쪽 컬럼에 라디오를 배치합니다.
with right_column:
    chosen = st.radio(
        'Sorting hat',
        ("Gryffindor", "Ravenclaw", "Hufflepuff", "Slytherin"))
    st.write(f"You are in {chosen} house!")

실행결과 버튼과 라디오가 가로 방향으로 나란히 배치되었습니다.

st.expander를 사용하여 콘텐츠를 숨길 수 있습니다.

import streamlit as st


st.title("데이터 분석 대시보드")

# FAQ 섹션
with st.expander("자주 묻는 질문 (FAQ)"):
    st.markdown("""
    **Q: 데이터는 어떻게 업데이트되나요?**
    A: 매일 자동으로 업데이트됩니다.
   
    **Q: 데이터 출처는 어디인가요?**
    A: 공공 API를 통해 수집됩니다.
    """)`

실행하면 처음에는 자세한 내용이 숨겨져 있고

자주 묻는 질문을 클릭하면 상세내용이 보입니다.

진행 상황 표시

st.progress()를 사용하여 진행 상태를 표시할 수 있습니다.

import streamlit as st
import time

latest_iteration = st.empty()
bar = st.progress(0)

for i in range(100):

  latest_iteration.text(f'Iteration {i+1}')
  bar.progress(i + 1)
  time.sleep(0.1)

실행하면 프로그레스바가 표시되면서 100까지 이동합니다.