uploaded_file=st.file_uploader("Choose a file",type=['png','jpg','jpeg'])
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 이 감소하는 예제다.
importstreamlitasstimportpandasaspdimportnumpyasnpimporttimest.title('Counter Example without session state')count_value=0increment=st.button('Increment')ifincrement:count_value+=1decrement=st.buttom('Decrement')ifdecrement:count_value-=1st.write('Count = ',count_value)
그러나 increment 버튼을 아무리 눌러도 계속 결과는 1 이다.
한 번 버튼을 누르면 재실행이 되어 count value 가 계속 0 으로 초기화 되기 때문이다. 즉 한 번 버튼을 누를 때마다 코드 전체가 재실행되는 것이다.
이제 session state 가 있다면 아래와 같다.
importstreamlitasstimportpandasaspdimportnumpyasnpimporttimest.title('Counter Example without session state')# count session state 에 init
if'count'notinst.session_state:st.session_state.count=0# increment 버튼이 클릭되면 session state 의 count 에 1 을 더함
increment=st.button('Increment')ifincrement:st.session_state.count+=1# decrement 버튼이 클릭되면 session state 의 count 에 1 을 뺌
decrement=st.buttom('Decrement')ifdecrement:st.session_state.count-=1st.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'notinst.session_state:st.session_state['lang']='미국'# 콜백함수 정의
defbutton_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 를 데코레이터로 함수 위에 붙여준다.
댓글 남기기