14장. Streamlit 화면과 Docker 컨테이너
Streamlit 이 화면을 그리는 법
즉석 사진기는 단추를 누를 때마다 사진 한 장을 처음부터 새로 뽑습니다. Streamlit 도 사용자가 위젯을 건드릴 때마다 스크립트를 맨 위부터 다시 실행해 화면을 새로 그립니다. 그래서 코드는 위에서 아래로 읽는 보통 파이썬 스크립트처럼 쓰면 됩니다.
1번에서 사용자가 선택 상자를 바꾸거나 단추를 누릅니다. 2번에서 Streamlit 이 스크립트를 처음부터 다시 실행합니다. 3번에서 st.selectbox(...) 같은 줄이 위젯의 지금 값을 돌려줍니다. 4번에서 실행이 끝까지 가며 화면을 새로 그립니다. 여기서 즉석 사진 한 장이 다시 실행 한 번입니다. 사진과 달리 Streamlit 은 바뀐 부분만 화면에 고쳐 그려서, 깜빡이지 않습니다.
보통 웹 프로그램과 다른 점
| 견줄 것 | 보통 웹 프로그램 | Streamlit |
|---|---|---|
| 단추를 누르면 | 그 단추에 붙은 함수만 실행 | 스크립트 전체를 다시 실행 |
| 변수 | 계속 남아 있다 | 다시 실행할 때마다 처음부터 |
| 남겨야 할 값 | 변수에 둔다 | st.session_state 에 둔다 |
| 무거운 준비 | 한 번 해 둔다 | @st.cache_resource 로 한 번만 |
다시 실행을 흉내 내 보기
# Streamlit 을 쓰지 않고 "사건마다 처음부터 다시 실행"을 흉내 낸다
session_state = {} # 다시 실행해도 남는 곳
def script(clicked):
count = 0 # 보통 변수: 매번 0 으로 다시 시작한다
session_state.setdefault("count", 0) # 세션 상태: 처음 한 번만 0
if clicked:
count += 1
session_state["count"] += 1
return count, session_state["count"]
for clicked in [True, True, True]: # 단추를 세 번 누른다 = 세 번 다시 실행
print(script(clicked))코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
session_state = {} | 함수 바깥에 둔 빈 사전이다. 함수를 몇 번 불러도 사라지지 않아 Streamlit 의 st.session_state 역할을 흉내 낸다 |
def script(clicked): | 스크립트 한 번 실행을 함수 하나로 흉내 낸다. clicked 는 이번 실행에서 단추를 눌렀는지다 |
count = 0 | 함수 안의 보통 변수다. 함수를 부를 때마다 새로 0 이 된다 |
session_state.setdefault("count", 0) | set default(기본값 정하기)라는 뜻이다. "count" 가 아직 없을 때만 0 을 넣고, 있으면 그대로 둔다 |
session_state["count"] += 1 | 세션 상태에 든 값을 1 늘린다. 다음 실행에도 이 값이 남는다 |
이 코드가 뜻하는 것
게임의 저장 슬롯에 견줄 수 있습니다. 게임을 껐다 켜면 화면 위의 점수는 0 부터 다시 시작하지만, 저장 슬롯에 적어 둔 기록은 남아 있습니다. 여기서 껐다 켜기가 다시 실행, 화면 위의 점수가 보통 변수 count, 저장 슬롯이 session_state 입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
count = 0 | 실행마다 0 부터 센다 | 켤 때마다 0 점으로 시작하는 화면 점수 |
session_state.setdefault("count", 0) | 처음 한 번만 0 을 넣는다 | 저장 슬롯이 비었을 때만 새로 만든다 |
if clicked: 아래 두 줄 | 둘 다 1 씩 늘린다 | 점수도 올리고 저장 슬롯에도 적는다 |
for clicked in [True, True, True]: | 단추를 세 번 누른다 | 게임을 세 판 한다 |
결과 (1, 1), (1, 2), (1, 3) 에서 앞 숫자는 늘 1 이고 뒤 숫자만 1, 2, 3 으로 쌓입니다. 보통 변수는 다시 실행마다 처음으로 돌아가고, 세션 상태에 둔 값만 쌓인다는 뜻입니다. 비유와 다른 점은, Streamlit 의 세션 상태는 탭을 닫으면 사라진다는 것입니다.
작게 줄여서 눈으로 보기
st.button 처럼 "누른 그 실행에서만 True" 인 값을 흉내 냅니다. 단추를 누르고, 다른 칸을 두 번 바꾸고, 다시 단추를 누르는 순서는 설명을 위해 정한 것입니다.
# st.button 처럼 "누른 그 실행에서만 True" 인 값을 흉내 낸다(설명용)
session_state = {"pressed": 0}
def script(button):
if button: # 이번 실행에서 눌렀을 때만 True
session_state["pressed"] += 1 # 눌렀다는 사실은 세션 상태에 적는다
return button, session_state["pressed"]
for button in [True, False, False, True]: # 누름, 다른 칸 바꿈, 다른 칸 바꿈, 누름
print(script(button))(True, 1)
(False, 1)
(False, 1)
(True, 2)| 결과 | 읽는 법 |
|---|---|
(True, 1) | 단추를 누른 실행이다. 단추 값은 True 이고, 누른 횟수를 1 로 적었다 |
(False, 1) 두 줄 | 다른 칸을 바꿔 다시 실행됐다. 단추 값은 False 로 돌아갔지만 적어 둔 1 은 남았다 |
(True, 2) | 다시 누른 실행이다. 적어 둔 횟수가 2 가 됐다 |
단추 값만 믿으면 다른 칸을 건드리는 순간 "눌렀다"는 사실이 사라집니다. 그래서 남겨야 할 사실은 세션 상태에 적습니다.
주의할 점
- 다시 실행 때문에, 스크립트의 위쪽에 무거운 일(모델 불러오기, 큰 파일 읽기)을 그냥 두면 단추를 누를 때마다 다시 합니다. 캐시를 씁니다(다음다음 절).
- 위젯 값을 바꿀 때마다 다시 실행되는 것이 번거로우면,
st.form으로 칸들을 묶어 "제출" 단추를 누를 때만 다시 실행되게 합니다(실습 1).