[Service] Streamlit
앞서 프로토타입 등을 빠르게 구현할 때 사용할 수 있는 라이브러리로 Streamlit 이 있다고 했다. 이에 대해 알아보자.
Streamlit
- 다른 웹 서비스 구현 라이브러리인 Voila 는 노트북에서 쉽게 프로토타입을 만들 수 있지만, 대시보드처럼 레이아웃을 잡기 어렵다는 단점이 있다.
- 또한 JavaScript, React, Vue 등을 사용해 프로토타입을 만드는 것은 시간 효율적이지 않다.
- 처음부터 HTML/JavaScript + Flask/FastAPI 가 아닌 기존 코드를 조금만 수정해서 웹 서비스를 만드는 것이 목표일 때, Streamlit 을 사용할 수 있다.
-
아래 이미지는 데이터 분석가(또는 데이터 사이언티스트)가 웹 서비스를 만드는 Flow 에 대해 나타내고 있다.

-
데이터 Product 가 중요하다면 프론트엔드/PM 조직과 협업한다. 단 프론트엔드/PM 조직은 다른 웹도 만들고 있어 빠르게 이터레이션 하기 어렵다.
Step 1 에서 requirements 는 라이브러리가 아니라 요구사항을 뜻한다. -
Streamlit 은 다른 조직의 도움 없이 빠르게 웹 서비스를 만들 수 있는 장점이 있다.

- 여기서 유념해야 할 것은, 모든 것은 프로젝트의 요구조건에 따라 다르고, 프로젝트 설계에 따라 다르다는 점이다.
- Streamlit 의 대안으로는 아래와 같은 도구들이 있다.
- R 의 Shiny
- Flask, Fast API : 백엔드를 직접 구성 + 프론트엔드 작업도 진행
- Dash : 제일 기능이 풍부한 Python 대시보드 라이브러리
-
Voila : jupyter notebook 을 바로 시각화할 수 있으나, UX 나 레이아웃을 잡기에는 까다롭다.

- Streamlit 은 공식문서를 제공한다. Streamlit Docs에 들어가보면 확인할 수 있다.
- Streamlit 의 장점은 아래와 같다.
- 파이썬 스크립트 코드를 조금만 수정하면 웹을 띄울 수 있다.
- 백엔드 개발이나 HTTP 요청을 구현하지 않아도 된다.
- 다양한 Component 제공해 대시보드 UI 구성할 수 있다.
- Streamlit Cloud 도 존재하기 때문에 쉽게 배포할 수 있다. 단, Community Plan 은 Public Repo 만 가능하다.
- 화면 녹화 기능(Record)이 존재한다. 이를 github README.md 에 올릴 수 있다.
- 굉장히 적은 코드로 프론트엔드 기능을 할 수 있다.
-
아래와 같이 CLI 로
streamlit run streamlit-basic.py명령어만 입력하면 바로 확인해볼 수 있다.
Streamlit Component
- Streamlit API Document 에는 굉장히 많은 Component 가 존재한다. Streamlit cheat sheet를 참고하면 확인할 수 있다.
- 아래는 Streamlit 에서 제공하는 대표적인 component 들이며, 코드와 웹에서 보이는 결과를 보여주는 이미지를 보자.
Title, Header, subheader, Write
| CODE | RESULT |
|---|---|
|
|
버튼
| CODE | RESULT |
|---|---|
|
|
체크박스
valueargument 에 인자를 넘겨주면 default 값을 설정할 수 있다.
| CODE | RESULT |
|---|---|
|
|
Pandas Dataframe, Markdown
st.write: 보여줄 수 있는 것이면 어떤 것이든 보여준다.st.dataframe: Interactive 한 Dataframe, 컬럼 클릭이 가능하고 정렬도 가능하다.st.table: Static 한 Dataframe
| CODE | RESULT |
|---|---|
|
|
강조
| CODE | RESULT |
|---|---|
|
|
Metric, Json
- 지표 등을 작성할 수 있다.
| CODE | RESULT |
|---|---|
|
|
Line Chart
| CODE | RESULT |
|---|---|
|
|
Map Chart
| CODE | RESULT |
|---|---|
|
|
-
그 외에 plotly chart 등도 쉽게 사용할 수 있다. 자세한 것은 Streamlit Docs 의 Chart elements 를 확인해보자.

Radio Button, Select Box
| CODE | RESULT |
|---|---|
|
|
Multi Select Box
| CODE | RESULT |
|---|---|
|
|
Slider
| CODE | RESULT |
|---|---|
|
|
input box, caption, code, latex
| CODE | RESULT |
|---|---|
|
|
sidebar
-
st.sidebar.button(”hi”)와 같이 사용한다. Sidebar 에는 파라미터를 지정하거나 암호를 설정할 수 있다.
columns
| CODE | RESULT |
|---|---|
|
|
Expander
| CODE | RESULT |
|---|---|
|
|
Spinner
| CODE | RESULT |
|---|---|
|
|
Ballons
st.balloons()을 사용하면 웹 화면에 풍선이 나오는 이쁜(?) 효과를 발생시킨다.
Status Box
| CODE | RESULT |
|---|---|
|
|
Form
| CODE | RESULT |
|---|---|
|
|
file uploader
| CODE | RESULT |
|---|---|
|
|
Streamlit Data Flow
- Streamlit 의 화면에서 무언가 업데이트 되면, 전체 Streamlit 코드가 다시 실행되는 구조다.
- 코드가 수정되는 경우
- 사용자가 Streamlit 의 위젯과 상호작용 하는 경우(버튼 클릭, 입력 상자에 텍스트 입력 등)
- streamlit 의 data flow 로 인해 매번 코드가 재실행되며 중복 이벤트를 할 수 없다. 따라서 Global Variable 처럼 서로 공유할 수 있는 변수가 필요하다.
Seesion State
- streamlit 0.84 버전에서 이러한 역할을 하는 Session State 가 개발되었다.
st.session_state.{seesion_state_value}와 같이 사용하며,session_state_value에 저장해서 활용하는 방식이다. 이를 Global Variable 처럼 쓸 수 있다.-
만약 session state 가 없다면 아래와 같이 코드를 짤 수 있다. 아래 코드는
Increment를 누르면 1 이 증가하고Decrement를 누르면 1 이 감소하는 예제다.import streamlit as st import pandas as pd import numpy as np import time st.title('Counter Example without session state') count_value = 0 increment = st.button('Increment') if increment: count_value += 1 decrement = st.buttom('Decrement') if decrement: count_value -= 1 st.write('Count = ', count_value) - 그러나 increment 버튼을 아무리 눌러도 계속 결과는 1 이다.
- 한 번 버튼을 누르면 재실행이 되어 count value 가 계속 0 으로 초기화 되기 때문이다. 즉 한 번 버튼을 누를 때마다 코드 전체가 재실행되는 것이다.
-
이제 session state 가 있다면 아래와 같다.
import streamlit as st import pandas as pd import numpy as np import time st.title('Counter Example without session state') # count session state 에 init if 'count' not in st.session_state: st.session_state.count = 0 # increment 버튼이 클릭되면 session state 의 count 에 1 을 더함 increment = st.button('Increment') if increment: st.session_state.count += 1 # decrement 버튼이 클릭되면 session state 의 count 에 1 을 뺌 decrement = st.buttom('Decrement') if decrement: st.session_state.count -= 1 st.write('Count = ', st.session_state.count) - 이제는 코드가 재실행이 되어도
st.session_state의 값은 변하지 않아, 버튼을 누른 만큼 count 가 됨을 확인할 수 있다. - 따라서 계속 반복해서 사용하고 싶은 변수가 있으면
session_state에 저장해주는 것이 중요하다. session_state를 사용할 때는 처음에 default value 변수가 있는지 확인하고 없으면 init 해준다. 만약 위 코드에서 if 문 없이st.session_state.count = 0으로 바로 할당했다면 계속 재실행 되어 무조건 0 으로 남게 된다. 따라서 처음에st.session_state에 해당 변수가 없을 때만 0 으로 init 한 것이다.- 이후 특정 조건 충족 시
session_state에 저장한 변수가 증가하도록 만들면 된다. - 이와 관련해서 Session State 에 대해 잘 정리한 Streamlit blog 글을 보자.
Callback
st.session_state와on_change옵션을 같이 사용하여 Callback 함수를 사용할 수 있다.- 프로그래밍에서 callback 이란 특정한 조건을 만족하거나 이벤트 발생 시 특정 함수를 호출하는 패턴을 의미한다. 이는 트리거와 유사한 개념이다.
-
callback 함수는 GUI 프로그래밍이나 웹 애플리케이션에서 사용자의 입력(버튼 클릭, 키 입력, 마우스 이벤트 등)이나 시스템 이벤트 처리에 사용된다.
st.title("가고 싶은 휴양지를 선택하세요") # 세션상태 초기화 if 'lang' not in st.session_state: st.session_state['lang']='미국' # 콜백함수 정의 def button_callback(selected_lang): st.session_state['lang'] = selected_lang # 라디오 버튼 생성 radio_options=['미국', '영국', '프랑스', '독일'] radio_selected = st.radio("어느 곳으로 떠나시겠습니까?", radio_options) # 기본 버튼: on_click 에 콜백 함수를 지정, args 에 콜백 함수에 대한 인수 지정 clicked = st.button('선택', on_click=button_callback, args=[radio_selected]) # 콜백함수를 실행한 결과 출력 st.write(f"{st.session_state['lang']}에 가는 것을 선택했습니다.") - 만약 버튼 없이
radio_selected를 고르자마자st.session_state['lang']을 바꿔버리면, 사용자가 선택만 해도 값이 바뀌게 된다. 그러나 “진짜 바꿀지 말지” 는 버튼 클릭으로 결정해야 한다. - 따라서 버튼을 눌렀을 때만
st.session_state를 바꾸도록 콜백 함수를 버튼에 연결하는 것이다. - 이처럼 콜백 함수 쓰는 이유는 “버튼을 눌렀을 때만 상태를 바꿔서, 사용자의 명확한 ‘확정’ 행동을 기다리기 위해” 사용하는 것이다.
- 위 코드에서는
st.button을 누를 때만 true 가 되고, 콜백(on_click)을 연결하면 누른 순간 자동으로 실행된다. - 이러한 “선택 후 확정” 은 웹앱에서 기본적이고 좋은 습관이다.
- 간단한 앱에서는 버튼을 클릭했는지 직접 if 문으로 체크하는
if clicked:로도 충분히 가능하지만, 복잡한 인터랙션(여러 버튼/액션)에서는on_click=callback을 쓰는 게 관리가 편하다.
Cache
- Streamlit 은 매번 다시 실행하는 특성 때문에 데이터도 매번 다시 읽게 된다. 이런 경우
@st.cache데코레이터를 사용해서 캐싱하면 좋다. -
아래 코드와 같이 Streamlit Cache 를 뜻하는
@st.cache를 데코레이터로 함수 위에 붙여준다.DATE_COLUMN = 'data/time' DATA_URL = 'https://s3-us-west-2.amazonaws.com/streamlit-demo-data/uber-raw-data-sep14.csv.gz' @st.cache def load_data(nrows): data = pd.read_csv(DATA_URL, nrows=nrows) lowercase = lambda x: str(x).lower() data.rename(lowercase, axis='columns', inplace=True) data[DATA_COLUMN] = pd.to_datetime(data[DATE_COLUMN]) return data - 캐싱은 성능을 위해 메모리 등에 저장하는 행위다. 데이터를 읽는 함수를 만들고 위처럼
@st.cache데코레이터를 적용하게 되면, 다시 실행이 되더라도 캐싱이 되어 더 빠르게 실행된다.



















댓글 남기기