Streamlit 세션 스테이트와 콜백 — 리런 사이에 값 기억하기
Streamlit 세션 스테이트와 콜백 — 리런 사이에 값 기억하기
Streamlit은 상호작용할 때마다 스크립트를 위에서 아래로 다시 실행(리런)해요. 그런데 이 리런은 "깨끗한 상태"에서 일어나요 — 리런 사이에 변수는 공유되지 않죠. 그래서 단순히 count = 0을 만들고 버튼을 누르면 count += 1을 해도, 다음 리런에서 count는 다시 0으로 초기화돼요. 사용자가 몇 번 눌렀는지 셀 수 없는 거예요. 이 문제를 푸는 게 바로 **세션 스테이트(Session State)**예요.
출처: https://docs.streamlit.io/develop/concepts/architecture/session-state
세션이란
브라우저 탭 하나가 Streamlit 앱에 접속하면 그게 하나의 세션이 돼요. 세션 스테이트는 그 사용자 세션 안에서 리런 사이에 값을 공유하는 방법이에요. 멀티페이지 앱에서도 페이지 간에 유지돼요.
초기화와 읽기·쓰기
세션 스테이트 API는 파이썬 딕셔너리처럼 생겼어요. 초기화부터 확인해볼게요.
import streamlit as st
if 'key' not in st.session_state:
st.session_state['key'] = 'value'
# 딕셔너리 문법 외에 속성 문법도 지원해요
if 'key' not in st.session_state:
st.session_state.key = 'value'
값을 읽거나 바꾸는 것도 간단해요.
st.write(st.session_state.key) # 값 읽기
st.session_state.key = 'value2' # 속성으로 갱신
st.session_state['key'] = 'value2' # 딕셔너리처럼 갱신
초기화하지 않은 변수에 접근하면 예외가 나요. 그래서 항상 if 'count' not in st.session_state:로 먼저 있는지 확인하고 초기화하는 패턴을 써요.
예제: 카운터 앱에 세션 스테이트 적용
import streamlit as st
st.title('Counter Example')
if 'count' not in st.session_state:
st.session_state.count = 0
increment = st.button('Increment')
if increment:
st.session_state.count += 1
st.write('Count = ', st.session_state.count)
이제 버튼을 누를 때마다 count가 실제로 늘어나요.
콜백(Callback) 사용하기
콜백은 입력 위젯이 바뀔 때 호출되는 파이썬 함수예요. on_change(또는 on_click) 파라미터에 함수를 넘기고, args·kwargs로 인자를 전달할 수 있어요.
import streamlit as st
st.title('Counter Example using Callbacks')
if 'count' not in st.session_state:
st.session_state.count = 0
def increment_counter():
st.session_state.count += 1
st.button('Increment', on_click=increment_counter)
st.write('Count = ', st.session_state.count)
참고할 점
st.button과st.file_uploader는 세션 스테이트로 값을 설정할 수 없어요. 이 위젯들의 상태를 세션 스테이트 API로 지정하면StreamlitAPIException이 나요.- 세션 스테이트는 탭이 열려 있고 서버에 연결돼 있는 동안만 살아있어요. 탭을 닫으면 사라지고, 서버가 죽으면 초기화돼요. 영구 저장이 아니라는 점을 기억해두세요.
더 알아보기
세션 스테이트와 위젯 상태가 연결되는 원리, 직렬화 옵션(runner.enforceSerializableSessionState)은 공식 Session State 문서에서 더 자세히 볼 수 있어요.