์ด์ ์ ์๊ฐํ๋ Clickhouse์์ ์ค์๊ฐ ๋ฐ์ดํฐ ๋ถ์์ ์ํด
๋์๋ณด๋๋ฅผ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ์์ฑํ ์ ์๋ Streamlit ์๋น์ค์ ๋ํด ์๊ฐํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ค์ ํ์ ์์ Streamlit์ผ๋ก ์๋น์ค๋ฅผ ๊ตฌ์ถํ๋ ํ๊ธฐ๋ ์งง๊ฒ ๊ณต์ ํฉ๋๋ค.
๋ฐ์ดํฐ ๋ถ์ ํ๊ฒฝ ๊ตฌ์ถ - 05. Clickhouse ์ค์๊ฐ ๋ฐ์ดํฐ ๋ถ์ ํ ์ด๋ธ ์์ฑํ๊ธฐ
๐ ์ด ๊ธ์์ ๋ค๋ฃฐ ๋ด์ฉ1๏ธโฃ Kafka์ ์ฐ๊ฒฐ๋ ClickHouse ํ ์ด๋ธ ์์ฑ (ํ๊ฒฝ์ค์ ํฌํจ)2๏ธโฃ ์ค์ต์ฉ ๋ฐ์ดํฐ์ kafka produce3๏ธโฃ Kafka ํ ์ด๋ธ์์ ๋ฐ์ดํฐ ์ ํ์ธ ๋ฐฉ๋ฒ1. Kafka์ ์ฐ๊ฒฐ๋ ClickHouse ํ ์ด๋ธ ์
jongwho.tistory.com
๐ ๏ธ Streamlit ์ค์น ๋ฐ ์ฐจํธ ์์ฑ
์๋ ๋ช ๋ น์ด๋ฉด streamlit ์ค์น๊ฐ ์๋ฃ๋ฉ๋๋ค.
pip install streamlit
๊ทธ ์ดํ์ streamlit ์๋ฒ๋ฅผ ์คํํฉ๋๋ค. ์๋ ๋ช ๋ น์ด๋ก tutorial ํ์ด์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
streamlit hello

streamlit์์ ์ ๊ฐ ์ฃผ๋ก ์ฌ์ฉํ๋ ์ฐจํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ altair_chart ์์ต๋๋ค(https://docs.streamlit.io/develop/api-reference/charts/st.altair_chart). ์ด ์ฐจํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ฐจํธ ๋ด์์ ์ถ๊ฐ ์ก์ (์: ๋ฒํผ ํด๋ฆญ ๋ฑ)์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ธ๋ฐ์. ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ง๋ ์ฑ ์์์ ๋๋ค.


์ค์ ์ดํ๋ฆฌ์ผ์ด์ ๋์ ์์
๐ ๏ธ Streamlit ์ฐจํธ ์์ฑ ์ฝ๋ ์์
import streamlit as st
import pandas as pd
import numpy as np
import altair as alt
# ์ฐจํธ ์ ํ ํค ์ด๊ธฐํ
chart_key = "alt_chart"
if "select_state" not in st.session_state:
st.session_state["select_state"] = False
# ์ฐ๋๋ณ ๋ฐ์ดํฐ ์์ฑ
years = list(range(2020, 2025))
sales_data = [100, 200, 150, 300, 250] # ์์ ๋ฐ์ดํฐ
annual_data = pd.DataFrame({"year": years, "annual_sales": sales_data})
# State ๋ณ๊ฒฝ ํจ์
def change_state():
st.session_state["select_state"] = True
# ์๋ณ ๋ฐ์ดํฐ ์์ฑ ํจ์
def generate_monthly_data(selected_year):
np.random.seed(selected_year) # ์ฐ๋๋ณ ๊ณ ์ ๋ฐ์ดํฐ ์์ฑ
return pd.DataFrame(
{
"year": selected_year,
"month": range(1, 13),
"monthly_sales": np.random.randint(50, 300, 12),
}
)
# Altair ์ ํ ์ค์
selection = alt.selection_point(
fields=["year"], # ์นดํ
๊ณ ๋ฆฌ ํ๋ ๊ธฐ์ค ์ ํ
on="click", # ํด๋ฆญ ์ด๋ฒคํธ ์ฌ์ฉ
empty=False, # ํญ์ ํ๋์ ์ ํ ์ ์ง
)
# ๋ฐ ์ฐจํธ ์์ฑ
chart = (
alt.Chart(annual_data)
.mark_bar()
.encode(
x=alt.X("year:O", title="์ฐ๋"), # ์์ํ(O) ๋ฐ์ดํฐ๋ก ์ฒ๋ฆฌ
y=alt.Y("annual_sales:Q", title="์ฐ๊ฐ ๋งค์ถ"),
color=alt.condition(selection, alt.value("#4A90E2"), alt.value("#D3D3D3")),
)
.interactive()
.add_params(selection)
) # ์ ํ ํ๋ผ๋ฏธํฐ ์ถ๊ฐ
# ์ฐจํธ ํ์ (ํค ์ง์ ํ์)
chart_event = st.altair_chart(
chart,
key=chart_key,
on_select=change_state, # ์ ํ์ ์ฑ ๋ฆฌ๋ก๋
)
if st.session_state["select_state"]:
# ์ ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
if chart_event and chart_event.get("selection", {}).get("param_1"):
selected_year = chart_event["selection"]["param_1"][0]["year"]
monthly_data = generate_monthly_data(selected_year)
st.dataframe(monthly_data, hide_index=True)
else:
st.write("์ฐจํธ์ ๋ฐ๋ฅผ ํด๋ฆญํ๋ฉด ํด๋น ๋ฐ์ดํฐ๊ฐ ํ์๋ฉ๋๋ค.")
์์ ๊ฐ์ด streamlit์ ์ฌ์ฉํ๋ฉด ๋น ๋ฅด๊ฒ ์ฐจํธ ๋ฐ ๋์๋ณด๋๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
๐ ์ฌ๋ด ์๋น์ค์์ Streamlit์ ์ฌ์ฉํ ์งง์ ํ๊ธฐ
POC์ ๋น ๋ฅธ ์๊ฐ ๋ด์ ๋์๋ณด๋ ๊ตฌ์ฑ๊ณผ ๋ฐ์ดํฐ ๋ถ์ ๋ ํฌํธ๋ฅผ ์คํํ๋ ์๋น์ค๋ฅผ ๋ง๋๋๋ฐ ๊ฐ์ฅ ์ต๊ณ ์ ์ฑ
๐ ํ์ง๋ง Streamlit์ ํ๊ณ๋ ๋ช ํํฉ๋๋ค.
โ ์ฅ์
์ฌ๋ด์์ ๋ด๋ถ์ ์ผ๋ก ๋ฐ์ดํฐ ๋ ํฌํธ ํ๊ฒฝ์ ๋ง๋ค๊ธฐ ์ํด ์ฌ์ฉํ์ต๋๋ค. ์๊ฐ์ด ์ ๋ง ์ด๋ฐํ๊ณ ๋น ๋ฅด๊ฒ ์๋น์ค๋ฅผ ์ฌ๋ฆด ์ ์๋ ํ๋ ์์ํฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ๋ด์์ Snowflake๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ธฐ์ ๋ถ์๊ฐ์ ํ์ ํ๊ธฐ ์ข์ ํด์ ์ ํํด์ผ ํ์ต๋๋ค.
Snowflake๋ ์์ฒด์ ์ผ๋ก Streamlit ์ฑ์ ๋ด์ฅํ๊ณ ์์ด ๋ถ์๊ฐ๊ฐ ์ ๊ทผํ๊ธฐ ์ฌ์ ๊ณ ๋ํ Python ๊ธฐ๋ฐ์ ํ๋ ์์ํฌ์๊ธฐ์ ํ์ต ๊ณก์ ์ด ๊ฐํ๋ฅด์ง ์์์ต๋๋ค.
โ ๏ธ ๋จ์
Streamlit ์์ฒด์ ์ผ๋ก ์ธ์ฆ์ด ์์ต๋๋ค. ์ด์ AWS ์ธํ๋ผ์ Streamlit ์๋ฒ๋ฅผ ์ฌ๋ฆฌ๋ฉด์ Cognito์ ALB ์กฐํฉ์ผ๋ก ์ด๋ฅผ ํด๊ฒฐํ์ต๋๋ค. ๋ํ Streamlit ์ฑ ์์ฒด๋ Websocket ๋ฐฉ์์ผ๋ก ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ณ์ ์คํ๋๋ ๊ตฌ์กฐ์ ๋๋ค. ์ฆ ์น ๋ธ๋ผ์ฐ์ ์ Streamlit ์ฑ์ ์ง์ ๋๋๋งํ์ฌ ์ฐจํธ ๋ฐ ์ ๋๋ฉ์ด์ ์ ๋ณด์ฌ์ฃผ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ธ๋ฐ์, ํด๋น ํน์ง์ด ๋ฐ์ดํฐ ๋ถ์ ๊ตฌ์กฐ์์๋ ๋จ์ ์ด ๋์์ต๋๋ค(100๋ง๊ฑด ์ด์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๊ฑฐ๋ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ ๋).
์ข ํฉ์ ์ผ๋ก ์ผ๋ฐ ์ฌ์ฉ์(์ฌ๋ด์์๋ ์ ๋ฌธ์ ์ธ ๋ฐ์ดํฐ ๋ถ์๊ฐ๊ฐ ์๋) ๊ด์ ์์ ๋ฐ์ดํฐ ๋ถ์์์ ํ์ํ ๋ฐ์ดํฐ ๊ฑด ์๊ฐ ๋ง์ง ์๋ค๋ฉด Streamlit์ ์ ํํ์ฌ ๋น ๋ฅด๊ฒ MVP๋ฅผ ๋ง๋ค์ด์ ํ์์๊ฒ ํผ๋๋ฐฑ ๋ฐ๊ณ ๊ทธ๊ฑธ ๋ค์ ๋ฐ์ํ๋ ๋ฃจํ๋ฅผ ์์ฑํ๊ธฐ๋ ์ข์ ์ฑ์ผ๋ก ์๊ฐ๋ฉ๋๋ค.
๐ ๋ค์ ๊ธ์์๋ Streamlit์ clickhouse๋ฅผ ์ฐ๊ฒฐํ์ฌ ์ค์๊ฐ ๋์๋ณด๋๋ฅผ ๊ตฌ์ฑํ๋ ์ฑ์ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.