[๊ฐœ๋…์ •๋ฆฌ] Streamlit๐Ÿ‘‘ ์†Œ๊ฐœ ๋ฐ ํ™œ์šฉ ๊ฐ€์ด๋“œ

Posted by Euisuk's Dev Log on March 9, 2025

[๊ฐœ๋…์ •๋ฆฌ] Streamlit๐Ÿ‘‘ ์†Œ๊ฐœ ๋ฐ ํ™œ์šฉ ๊ฐ€์ด๋“œ

์›๋ณธ ๊ฒŒ์‹œ๊ธ€: https://velog.io/@euisuk-chung/Streamlit-์†Œ๊ฐœ-๋ฐ-ํ™œ์šฉ-๊ฐ€์ด๋“œ

1. Streamlit์ด๋ž€?

Streamlit์€ Python ๊ธฐ๋ฐ˜์˜ ์˜คํ”ˆ์†Œ์Šค ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฐ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌํ•˜๊ณ  ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

  • Flask, Django ๊ฐ™์€ ์ „ํ†ต์ ์ธ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋กœ ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์˜ ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ์„ ์‰ฝ๊ฒŒ ์‹œ๊ฐํ™”ํ•˜๊ณ , ์›น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“บ ๊ณต์‹ ์†Œ๊ฐœ ์˜์ƒ: Streamlit Crash Course


2. Streamlit์˜ ์ฃผ์š” ํŠน์ง•

  • ๊ฐ„ํŽธํ•œ ๊ตฌํ˜„:

    • ๋ช‡ ์ค„์˜ Python ์ฝ”๋“œ๋งŒ์œผ๋กœ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ.
  • ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ ๋ถˆํ•„์š”:

    • ๋ณ„๋„์˜ API ์„œ๋ฒ„ ๊ตฌ์ถ• ์—†์ด Python ์ฝ”๋“œ๋งŒ์œผ๋กœ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌํ˜„ ๊ฐ€๋Šฅ.
  • ์ž๋™ ๋ฆฌ๋ Œ๋”๋ง:

    • UI ์š”์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ž๋™์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—…๋ฐ์ดํŠธ๋จ.
  • ๋ฐฐํฌ ๊ธฐ๋Šฅ ์ง€์›:

    • Streamlit Cloud๋ฅผ ์ด์šฉํ•ด ์‰ฝ๊ฒŒ ๋ฐฐํฌ ๊ฐ€๋Šฅ.
  • ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ UI ์ œ๊ณต:

    • ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์ ฏ(๋ฒ„ํŠผ, ์Šฌ๋ผ์ด๋”, ์ฒดํฌ๋ฐ•์Šค ๋“ฑ) ์ง€์›.
  • ๋‹ค์–‘ํ•œ ์ฐจํŠธ ๋ฐ ์ง€๋„ ์ง€์›:

    • ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ์ฐจํŠธ ๋ฐ ์ง€๋„ ์œ„์ ฏ์„ ๊ธฐ๋ณธ ์ œ๊ณต.
  • ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐ ํ™•์žฅ ๊ธฐ๋Šฅ ์ง€์›:

    • ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์‚ฌ์šฉ์ž ์ •์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ๋Šฅ ํ™•์žฅ์ด ๊ฐ€๋Šฅ.

3. Streamlit ์„ค์น˜ ๋ฐ ๊ธฐ๋ณธ ์‹คํ–‰

๐Ÿ“Œ 3.1 ์„ค์น˜

1
pip install streamlit
  • ์„ค์น˜ ์™„๋ฃŒ ํ›„ ๊ธฐ๋ณธ ๋ฐ๋ชจ ์‹คํ–‰:
1
streamlit hello
  • ์œ„ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ธฐ๋ณธ ๋ฐ๋ชจ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €(http://localhost:8501/) ์—์„œ ์—ด๋ฆฝ๋‹ˆ๋‹ค.

ํ•ด๋‹น demo์—๋Š” ์ƒ˜ํ”Œ ๋Œ€์‹œ๋ณด๋“œ ๋ฐ ๊ตฌํ˜„ ์ƒ˜ํ”Œ ์ฝ”๋“œ snippet์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

  • DataFrame demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@st.cache_data
def get_UN_data():
    AWS_BUCKET_URL = "https://streamlit-demo-data.s3-us-west-2.amazonaws.com"
    df = pd.read_csv(AWS_BUCKET_URL + "/agri.csv.gz")
    return df.set_index("Region")

try:
    df = get_UN_data()
    countries = st.multiselect(
        "Choose countries", list(df.index), ["China", "United States of America"]
    )
    if not countries:
        st.error("Please select at least one country.")
    else:
        data = df.loc[countries]
        data /= 1000000.0
        st.subheader("Gross agricultural production ($B)")
        st.dataframe(data.sort_index())

        data = data.T.reset_index()
        data = pd.melt(data, id_vars=["index"]).rename(
            columns={"index": "year", "value": "Gross Agricultural Product ($B)"}
        )
        chart = (
            alt.Chart(data)
            .mark_area(opacity=0.3)
            .encode(
                x="year:T",
                y=alt.Y("Gross Agricultural Product ($B):Q", stack=None),
                color="Region:N",
            )
        )
        st.altair_chart(chart, use_container_width=True)
except URLError as e:
    st.error(f"This demo requires internet access. Connection error: {e.reason}")
  • Plotting demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
progress_bar = st.sidebar.progress(0)
status_text = st.sidebar.empty()
last_rows = np.random.randn(1, 1)
chart = st.line_chart(last_rows)

for i in range(1, 101):
    new_rows = last_rows[-1, :] + np.random.randn(5, 1).cumsum(axis=0)
    status_text.text(f"{i}% complete")
    chart.add_rows(new_rows)
    progress_bar.progress(i)
    last_rows = new_rows
    time.sleep(0.05)

progress_bar.empty()

# Streamlit widgets automatically run the script from top to bottom. Since
# this button is not connected to any other logic, it just causes a plain
# rerun.
st.button("Rerun")
  • Mapping demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@st.cache_data
def from_data_file(filename):
    url = (
        "https://raw.githubusercontent.com/streamlit/"
        "example-data/master/hello/v1/%s" % filename
    )
    return pd.read_json(url)

try:
    ALL_LAYERS = {
        "Bike rentals": pdk.Layer(
            "HexagonLayer",
            data=from_data_file("bike_rental_stats.json"),
            get_position=["lon", "lat"],
            radius=200,
            elevation_scale=4,
            elevation_range=[0, 1000],
            extruded=True,
        ),
        "Bart stop exits": pdk.Layer(
            "ScatterplotLayer",
            data=from_data_file("bart_stop_stats.json"),
            get_position=["lon", "lat"],
            get_color=[200, 30, 0, 160],
            get_radius="[exits]",
            radius_scale=0.05,
        ),
        "Bart stop names": pdk.Layer(
            "TextLayer",
            data=from_data_file("bart_stop_stats.json"),
            get_position=["lon", "lat"],
            get_text="name",
            get_color=[0, 0, 0, 200],
            get_size=10,
            get_alignment_baseline="'bottom'",
        ),
        "Outbound flow": pdk.Layer(
            "ArcLayer",
            data=from_data_file("bart_path_stats.json"),
            get_source_position=["lon", "lat"],
            get_target_position=["lon2", "lat2"],
            get_source_color=[200, 30, 0, 160],
            get_target_color=[200, 30, 0, 160],
            auto_highlight=True,
            width_scale=0.0001,
            get_width="outbound",
            width_min_pixels=3,
            width_max_pixels=30,
        ),
    }
    st.sidebar.subheader("Map layers")
    selected_layers = [
        layer
        for layer_name, layer in ALL_LAYERS.items()
        if st.sidebar.checkbox(layer_name, True)
    ]
    if selected_layers:
        st.pydeck_chart(
            pdk.Deck(
                map_style=None,
                initial_view_state={
                    "latitude": 37.76,
                    "longitude": -122.4,
                    "zoom": 11,
                    "pitch": 50,
                },
                layers=selected_layers,
            )
        )
    else:
        st.error("Please choose at least one layer above.")
except URLError as e:
    st.error(
        """
        **This demo requires internet access.**
        Connection error: %s
    """
        % e.reason
    )
  • Animation demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Interactive Streamlit elements, like these sliders, return their value.
# This gives you an extremely simple interaction model.
iterations = st.sidebar.slider("Level of detail", 2, 20, 10, 1)
separation = st.sidebar.slider("Separation", 0.7, 2.0, 0.7885)

# Non-interactive elements return a placeholder to their location
# in the app. Here we're storing progress_bar to update it later.
progress_bar = st.sidebar.progress(0)

# These two elements will be filled in later, so we create a placeholder
# for them using st.empty()
frame_text = st.sidebar.empty()
image = st.empty()

m, n, s = 960, 640, 400
x = np.linspace(-m / s, m / s, num=m).reshape((1, m))
y = np.linspace(-n / s, n / s, num=n).reshape((n, 1))

for frame_num, a in enumerate(np.linspace(0.0, 4 * np.pi, 100)):
    # Here were setting value for these two elements.
    progress_bar.progress(frame_num)
    frame_text.text("Frame %i/100" % (frame_num + 1))

    # Performing some fractal wizardry.
    c = separation * np.exp(1j * a)
    Z = np.tile(x, (n, 1)) + 1j * np.tile(y, (1, m))
    C = np.full((n, m), c)
    M: Any = np.full((n, m), True, dtype=bool)
    N = np.zeros((n, m))

    for i in range(iterations):
        Z[M] = Z[M] * Z[M] + C[M]
        M[np.abs(Z) > 2] = False
        N[M] = i

    # Update the image placeholder by calling the image() function on it.
    image.image(1.0 - (N / N.max()), use_container_width=True)

# We clear elements by calling empty on them.
progress_bar.empty()
frame_text.empty()

# Streamlit widgets automatically run the script from top to bottom. Since
# this button is not connected to any other logic, it just causes a plain
# rerun.
st.button("Rerun")

๐Ÿ“Œ 3.2 ์ฒซ ๋ฒˆ์งธ Streamlit ์•ฑ ์‹คํ–‰

hello_streamlit.py

1
2
3
4
import streamlit as st

st.title("Hello Streamlit! ๐Ÿš€")
st.write("์ด๊ฒƒ์€ Streamlit์œผ๋กœ ๋งŒ๋“  ๊ฐ„๋‹จํ•œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค.")
  • ์‹คํ–‰ ๋ช…๋ น์–ด:
1
streamlit run hello_streamlit.py
  • streamlit run์œผ๋กœ ํŒŒ์ด์ฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜์—ฌ http://localhost:8501/์—์„œ Streamlit์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


4. Streamlit ์ฃผ์š” API ์†Œ๊ฐœ

๐Ÿ“Š API DEMO ์ถœ๋ ฅ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋Š” ์•„๋ž˜ DataCamp์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ 4.1 ํ…์ŠคํŠธ ์ถœ๋ ฅ

  • Streamlit์€ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ…์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Image Source: https://www.datacamp.com/tutorial/streamlit

  1. st.write()
  • ์„ค๋ช…: ๋ฌธ์ž์—ด, ์ˆซ์ž, ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„, ๋ฆฌ์ŠคํŠธ, ๊ฐ์ฒด ๋“ฑ์„ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์šฉ ์ถœ๋ ฅ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
2
  st.write("์•ˆ๋…•ํ•˜์„ธ์š”, Streamlit์ž…๋‹ˆ๋‹ค!")
  st.write(12345)

Image Source: https://www.datacamp.com/tutorial/streamlit

  1. st.title()
  • ์„ค๋ช…: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ œ๋ชฉ์„ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํฐ ๊ธ€์”จ ํฌ๊ธฐ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:

    1
    
    st.title("Streamlit ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜")
    
  1. st.header()
  • ์„ค๋ช…: ํ—ค๋”(ํฐ ์ œ๋ชฉ) ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜๋กœ, st.title()๋ณด๋‹ค ํฌ๊ธฐ๊ฐ€ ์ž‘์ง€๋งŒ, ์ฃผ์š” ์„น์…˜์„ ๋‚˜๋ˆ„๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
  st.header("์ด๊ฒƒ์€ ํ—ค๋”์ž…๋‹ˆ๋‹ค")
  1. st.subheader()
  • ์„ค๋ช…: ์„œ๋ธŒํ—ค๋”(์†Œ์ œ๋ชฉ) ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜๋กœ, st.header()๋ณด๋‹ค ์ž‘์€ ํฌ๊ธฐ์˜ ์ œ๋ชฉ์ž…๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
  st.subheader("์ด๊ฒƒ์€ ์„œ๋ธŒํ—ค๋”์ž…๋‹ˆ๋‹ค")
  1. st.markdown()
  • ์„ค๋ช…: Markdown ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ๋ฅผ ํฌ๋งทํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
2
  st.markdown("# ํฐ ์ œ๋ชฉ (Markdown)")
  st.markdown("**๊ตต์€ ๊ธ€์”จ**์™€ *์ดํƒค๋ฆญ์ฒด* ์‚ฌ์šฉ ๊ฐ€๋Šฅ")
  1. st.caption()
  • ์„ค๋ช…: ์„ค๋ช…์ด๋‚˜ ์ฃผ์„์„ ๋‹ค๋Š” ์บก์…˜์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ์ž‘์€ ๊ธ€์”จ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
  st.caption("์ด๊ฒƒ์€ ์บก์…˜(์„ค๋ช…)์ž…๋‹ˆ๋‹ค.")
  1. st.code()
  • ์„ค๋ช…: Python ์ฝ”๋“œ ๋˜๋Š” ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด ์ฝ”๋“œ๋ฅผ ํ•˜์ด๋ผ์ดํŒ…๋œ ์ฝ”๋“œ ๋ธ”๋ก์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
2
3
4
  st.code("""
          def hello():
              print("Hello, Streamlit!")
          """, language="python")
  1. st.latex()
  • ์„ค๋ช…: LaTeX ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์‹์„ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์˜ˆ์‹œ:
1
  st.latex(r"E = mc^2")

๐Ÿ“Œ 4.2 ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฐ›๊ธฐ

  • Streamlit์€ ๋‹ค์–‘ํ•œ ์ž…๋ ฅ ์œ„์ ฏ์„ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Image Source: https://www.datacamp.com/tutorial/streamlit

1. ํ…์ŠคํŠธ ์ž…๋ ฅ (st.text_input())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ ์ƒ์ž๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  name = st.text_input("์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”")
  st.write(f"์ž…๋ ฅ๋œ ์ด๋ฆ„: {name}")

2. ์ˆซ์ž ์ž…๋ ฅ (st.number_input())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  age = st.number_input("๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”", min_value=0, max_value=100, step=1)
  st.write(f"์ž…๋ ฅ๋œ ๋‚˜์ด: {age}")

3. ๋‚ ์งœ ์ž…๋ ฅ (st.date_input())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ๋‚ ์งœ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  selected_date = st.date_input("๋‚ ์งœ ์„ ํƒ")
  st.write(f"์„ ํƒํ•œ ๋‚ ์งœ: {selected_date}")

4. ์‹œ๊ฐ„ ์ž…๋ ฅ (st.time_input())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ๊ฐ„์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  selected_time = st.time_input("์‹œ๊ฐ„ ์„ ํƒ")
  st.write(f"์„ ํƒํ•œ ์‹œ๊ฐ„: {selected_time}")

5. ์—ฌ๋Ÿฌ ์ค„ ์ž…๋ ฅ (st.text_area())

  • ์„ค๋ช…: ์—ฌ๋Ÿฌ ์ค„์˜ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ ํ•„๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  message = st.text_area("๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”")
  st.write(f"์ž…๋ ฅ๋œ ๋ฉ”์‹œ์ง€:\n{message}")

6. ํŒŒ์ผ ์—…๋กœ๋“œ (st.file_uploader())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
  uploaded_file = st.file_uploader("ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”")
  if uploaded_file is not None:
      st.write("์—…๋กœ๋“œ๋œ ํŒŒ์ผ:", uploaded_file.name)

7. ์ƒ‰์ƒ ์„ ํƒ (st.color_picker())

  • ์„ค๋ช…: ์ƒ‰์ƒ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  color = st.color_picker("์ƒ‰์ƒ์„ ์„ ํƒํ•˜์„ธ์š”", <"#00f900")
  st.write(f"์„ ํƒํ•œ ์ƒ‰์ƒ: {color}")

๐Ÿ“Œ 4.3 ๋ฒ„ํŠผ ๋ฐ ์„ ํƒํ˜• ์ž…๋ ฅ

  • Streamlit์€ ๋‹ค์–‘ํ•œ ๋ฒ„ํŠผ ๋ฐ ์„ ํƒํ˜• ์ž…๋ ฅ ์œ„์ ฏ์„ ์ œ๊ณตํ•˜์—ฌ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ UI๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Image Source: https://www.datacamp.com/tutorial/streamlit

1. ๋ฒ„ํŠผ (st.button())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํŠน์ • ๋™์ž‘์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  if st.button("ํด๋ฆญํ•˜์„ธ์š”"):
      st.write("๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")

2. ์ฒดํฌ๋ฐ•์Šค (st.checkbox())

  • ์„ค๋ช…: ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
  agree = st.checkbox("๋™์˜ํ•ฉ๋‹ˆ๋‹ค")
  if agree:
      st.write("๋™์˜ํ•˜์…จ์Šต๋‹ˆ๋‹ค!")

3. ๋ผ๋””์˜ค ๋ฒ„ํŠผ (st.radio())

  • ์„ค๋ช…: ์—ฌ๋Ÿฌ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  selected_option = st.radio("์˜ต์…˜์„ ์„ ํƒํ•˜์„ธ์š”", ("์˜ต์…˜ 1", "์˜ต์…˜ 2", "์˜ต์…˜ 3"))
  st.write(f"์„ ํƒ๋œ ์˜ต์…˜: {selected_option}")

4. ๋“œ๋กญ๋‹ค์šด ์„ ํƒ (st.selectbox())

  • ์„ค๋ช…: ๋“œ๋กญ๋‹ค์šด ๋ฆฌ์ŠคํŠธ์—์„œ ํ•˜๋‚˜์˜ ์˜ต์…˜์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  fruit = st.selectbox("๊ณผ์ผ์„ ์„ ํƒํ•˜์„ธ์š”", ["์‚ฌ๊ณผ", "๋ฐ”๋‚˜๋‚˜", "์˜ค๋ Œ์ง€"])
  st.write(f"์„ ํƒํ•œ ๊ณผ์ผ: {fruit}")

5. ๋‹ค์ค‘ ์„ ํƒ (st.multiselect())

  • ์„ค๋ช…: ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์˜ต์…˜์„ ๋™์‹œ์— ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  planets = st.multiselect("ํ–‰์„ฑ์„ ์„ ํƒํ•˜์„ธ์š”", ["๋ชฉ์„ฑ", "ํ™”์„ฑ", "ํ•ด์™•์„ฑ"])
  st.write(f"์„ ํƒํ•œ ํ–‰์„ฑ: {planets}")

6. ์Šฌ๋ผ์ด๋” (st.slider())

  • ์„ค๋ช…: ์Šฌ๋ผ์ด๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆซ์ž ๊ฐ’์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  number = st.slider("์ˆซ์ž๋ฅผ ์„ ํƒํ•˜์„ธ์š”", 0, 50)
  st.write(f"์„ ํƒ๋œ ์ˆซ์ž: {number}")

7. ์„ ํƒ ์Šฌ๋ผ์ด๋” (st.select_slider())

  • ์„ค๋ช…: ๋ฆฌ์ŠคํŠธ์—์„œ ์ •ํ•ด์ง„ ๊ฐ’์„ ์„ ํƒํ•˜๋Š” ์Šฌ๋ผ์ด๋”์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  rating = st.select_slider("ํ‰๊ฐ€๋ฅผ ์„ ํƒํ•˜์„ธ์š”", ["๋‚˜์จ", "๋ณดํ†ต", "์ข‹์Œ", "์ตœ๊ณ "])
  st.write(f"์„ ํƒํ•œ ํ‰๊ฐ€: {rating}")

๐Ÿ“Œ 4.4 ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ

  • Streamlit์„ ํ™œ์šฉํ•˜๋ฉด ์ด๋ฏธ์ง€, ๋น„๋””์˜ค, ์˜ค๋””์˜ค ํŒŒ์ผ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„, JSON, ์ฃผ์š” ์ง€ํ‘œ(Metrics) ๋“ฑ์„ ์‰ฝ๊ฒŒ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์ด๋ฏธ์ง€, ๋น„๋””์˜ค ๋ฐ ์˜ค๋””์˜ค ํŒŒ์ผ ์ถœ๋ ฅ

  • Streamlit์—์„œ๋Š” ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋งŒ์œผ๋กœ ๋‹ค์–‘ํ•œ ๋ฏธ๋””์–ด ํŒŒ์ผ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ์ด๋ฏธ์ง€ ์ถœ๋ ฅ (st.image())

  • ์„ค๋ช…: ์ด๋ฏธ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. PIL.Image ๊ฐ์ฒด ๋˜๋Š” ์ด๋ฏธ์ง€ URL, ๋กœ์ปฌ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
  import streamlit as st
  from PIL import Image

  image = Image.open("example.jpg")  -  ๋กœ์ปฌ ์ด๋ฏธ์ง€ ํŒŒ์ผ
  st.image(image, caption="์˜ˆ์ œ ์ด๋ฏธ์ง€", use_column_width=True)

(2) ์˜ค๋””์˜ค ์ถœ๋ ฅ (st.audio())

  • ์„ค๋ช…: ์˜ค๋””์˜ค ํŒŒ์ผ์„ ์›น์—์„œ ์žฌ์ƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.audio("example.mp3")

(3) ๋น„๋””์˜ค ์ถœ๋ ฅ (st.video())

  • ์„ค๋ช…: ๋น„๋””์˜ค ํŒŒ์ผ์„ ์›น์—์„œ ์žฌ์ƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.video("example.mp4")

2. ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„ ์ถœ๋ ฅ

  • Streamlit์€ pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ์—ฌ๋Ÿฌ ๋ฐฉ์‹์œผ๋กœ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ (st.write())

  • ์„ค๋ช…: st.write()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.write(df)

(2) ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„ (st.dataframe())

  • ์„ค๋ช…: st.dataframe()์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํฌ๋กค ๋ฐ ์ •๋ ฌ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.dataframe(df)

(3) ์ •์  ํ…Œ์ด๋ธ” (st.table())

  • ์„ค๋ช…: st.table()์€ ์ •์ ์ธ ํ…Œ์ด๋ธ” ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ํ•„์š” ์—†์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.table(df)

3. JSON ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ (st.json())

  • ์„ค๋ช…: JSON ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์ธต์ ์œผ๋กœ ์ •๋ฆฌ๋œ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
  st.json({
      '์ด๋ฆ„': '์ด์‹œํ˜„',
      '๋‚˜์ด': 24,
      '๊ฑฐ์ฃผ์ง€': '๊ด‘์ฃผ'
  })

4. ์ฃผ์š” ์ง€ํ‘œ ๋ฐ ํ†ต๊ณ„ ์ถœ๋ ฅ (st.metric())

  • ์„ค๋ช…: ํŠน์ • ์ง€ํ‘œ(metric) ๋ฅผ ๊ฐ•์กฐํ•ด์„œ ๋ณด์—ฌ์ค„ ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. delta ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ๋ณ€ํ™”๋Ÿ‰์„ ํ•จ๊ป˜ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ(์ง€ํ‘œ ์ถœ๋ ฅ)

1
2
st.metric(label="LG์ „์ž", value="78,000์›", delta="2.12%")
st.metric(label="ํ˜„๋Œ€์ฐจ", value="150,000์›", delta="-1.25%")

๐Ÿ“Œ 4.5 ์ฐจํŠธ ๋ฐ ์ง€๋„ ์ถœ๋ ฅ

  • ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋”์šฑ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ์ธ์‚ฌ์ดํŠธ๋ฅผ ์–ป๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. Streamlit์„ ํ™œ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋กœ ๋‹ค์–‘ํ•œ ์ฐจํŠธ์™€ ์ง€๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์ฐจํŠธ ์ถœ๋ ฅ

  • Streamlit์€ ๋‹ค์–‘ํ•œ ์ฐจํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ง€์›ํ•˜๋ฉฐ, matplotlib, pandas, Altair, Graphviz ๋“ฑ์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) Matplotlib ๊ทธ๋ž˜ํ”„ (st.pyplot())

  • ์„ค๋ช…: matplotlib.pyplot์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•œ ๊ทธ๋ž˜ํ”„๋ฅผ Streamlit ์•ฑ์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • Matplotlib์€ Python์—์„œ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์‹œ๊ฐํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ์„ธ๋ฐ€ํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ํžˆ์Šคํ† ๊ทธ๋žจ, ์‚ฐ์ ๋„, ์„  ๊ทธ๋ž˜ํ”„, ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ทธ๋ž˜ํ”„๋ฅผ ์ง์ ‘ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
7
8
  import streamlit as st
  import matplotlib.pyplot as plt
  import numpy as np

  rand = np.random.normal(1, 2, size=20)  # ์ •๊ทœ๋ถ„ํฌ ๋‚œ์ˆ˜ ์ƒ์„ฑ
  fig, ax = plt.subplots()
  ax.hist(rand, bins=15)  # ํžˆ์Šคํ† ๊ทธ๋žจ ๊ทธ๋ฆฌ๊ธฐ
  st.pyplot(fig)

Image Source: https://www.datacamp.com/tutorial/streamlit

(2) ์„ ํ˜• ์ฐจํŠธ (st.line_chart())

  • ์„ค๋ช…: Pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ์ด์šฉํ•˜์—ฌ ์„ ํ˜• ์ฐจํŠธ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.

    • ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ณ€ํ™”๋ฅผ ์‹œ๊ฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ, ์ฃผ๊ฐ€ ๋ณ€๋™, ํŠธ๋ Œ๋“œ ๋ถ„์„ ๋“ฑ์—์„œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
  import streamlit as st
  import pandas as pd
  import numpy as np

  df = pd.DataFrame(np.random.randn(10, 2), columns=['x', 'y'])
  st.line_chart(df)

Image Source: https://www.datacamp.com/tutorial/streamlit

(3) ๋ง‰๋Œ€ ์ฐจํŠธ (st.bar_chart())

  • ์„ค๋ช…: Pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ์ด์šฉํ•˜์—ฌ ๋ง‰๋Œ€ ์ฐจํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋น„๊ต๋ฅผ ํ•  ๋•Œ ์ ์ ˆํ•œ ์ฐจํŠธ์ž…๋‹ˆ๋‹ค.
    • ๊ฐ ๋ฒ”์ฃผ์˜ ํฌ๊ธฐ ์ฐจ์ด๋ฅผ ๊ฐ•์กฐํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
  import streamlit as st
  import pandas as pd
  import numpy as np

  df = pd.DataFrame(np.random.randn(10, 2), columns=['x', 'y'])
  st.bar_chart(df)

Image Source: https://www.datacamp.com/tutorial/streamlit

(4) ์˜์—ญ ์ฐจํŠธ (st.area_chart())

  • ์„ค๋ช…: Pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ์ด์šฉํ•˜์—ฌ ์˜์—ญ ์ฐจํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • ์„ ํ˜• ์ฐจํŠธ์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, ๋ˆ„์ ๋œ ๊ฐ’์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ•์กฐํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ์ „์ฒด์ ์ธ ๋ณ€ํ™” ํ๋ฆ„๊ณผ ์ฐจ์ด๋ฅผ ๊ฐ•์กฐํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
  import streamlit as st
  import pandas as pd
  import numpy as np

  df = pd.DataFrame(np.random.randn(10, 2), columns=['x', 'y'])
  st.area_chart(df)

Image Source: https://www.datacamp.com/tutorial/streamlit

(5) Altair ์ฐจํŠธ (st.altair_chart())

  • ์„ค๋ช…: Altair๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ์ฐจํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋ฐ ์ƒํ˜ธ์ž‘์šฉ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฐ์ ๋„, ๋ฒ„๋ธ” ์ฐจํŠธ ๋“ฑ ๋‹ค์ฐจ์› ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐํ™”ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
7
8
9
10
  import streamlit as st
  import pandas as pd
  import numpy as np
  import altair as alt

  df = pd.DataFrame(np.random.randn(500, 3), columns=['x', 'y', 'z'])
  chart = alt.Chart(df).mark_circle().encode(
      x='x', y='y', size='z', color='z', tooltip=['x', 'y', 'z']
  )
  st.altair_chart(chart, use_container_width=True)

Image Source: https://www.datacamp.com/tutorial/streamlit

(6) ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ ์‹œ๊ฐํ™” (st.graphviz_chart())

  • ์„ค๋ช…: graphviz๋ฅผ ์ด์šฉํ•ด ๋…ธ๋“œ ๋ฐ ์—ฃ์ง€ ๊ธฐ๋ฐ˜ ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ๋…ธ๋“œ์™€ ์—ฃ์ง€ ๊ธฐ๋ฐ˜ ๋„คํŠธ์›Œํฌ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ๊ณ„์ธต ๊ตฌ์กฐ, ํ”„๋กœ์„ธ์Šค ํ๋ฆ„๋„, ๊ด€๊ณ„ ๋ชจ๋ธ๋ง ๋“ฑ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
7
8
9
10
11
  import streamlit as st
  import graphviz

  st.graphviz_chart('''
      digraph {
          Big_shark -> Tuna
          Tuna -> Mackerel
          Mackerel -> Small_fishes
          Small_fishes -> Shrimp
      }
  ''')

Image Source: https://www.datacamp.com/tutorial/streamlit

2. ์ง€๋„ ์ถœ๋ ฅ

  • Streamlit์€ ๊ฐ„๋‹จํ•œ st.map() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง€๋„๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, latitude(์œ„๋„) ๋ฐ longitude(๊ฒฝ๋„) ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๋„ ์ถœ๋ ฅ (st.map())

  • ์„ค๋ช…: Pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์— ์œ„๋„(lat)์™€ ๊ฒฝ๋„(lon) ๊ฐ’์„ ํฌํ•จํ•˜์—ฌ ์ง€๋„๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ์ง€๋ฆฌ์  ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ง€๋„ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ์œ„๋„(lat)์™€ ๊ฒฝ๋„(lon) ๊ฐ’์ด ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:

1
2
3
4
5
6
7
8
9
  import pandas as pd
  import numpy as np
  import streamlit as st

  df = pd.DataFrame(
      np.random.randn(500, 2) / [50, 50] + [37.76, -122.4],  # ์ƒŒํ”„๋ž€์‹œ์Šค์ฝ” ์ฃผ๋ณ€ ๋žœ๋ค ์ขŒํ‘œ ์ƒ์„ฑ
      columns=['lat', 'lon']
  )
  st.map(df)

Image Source: https://www.datacamp.com/tutorial/streamlit


๐Ÿ“Œ 4.6. ์ƒํƒœ ๋ฉ”์‹œ์ง€ ๋ฐ ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ

  • Streamlit์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ง„ํ–‰๋ฅ  ๋ฐ”, ์Šคํ”ผ๋„ˆ(๋กœ๋”ฉ ํ‘œ์‹œ), ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ๋“ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์ง„ํ–‰๋ฅ  ๋ฐ ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ

Image Source: https://www.datacamp.com/tutorial/streamlit

(1) ์ง„ํ–‰๋ฅ  ๋ฐ” (st.progress())

  • ์„ค๋ช…: ํ”„๋กœ์„ธ์Šค ์ง„ํ–‰ ์ƒํ™ฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ง„ํ–‰๋ฅ  ๋ฐ”(progress bar) ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
6
7
8
  import streamlit as st
  import time  

  progress_bar = st.progress(0)  # ์ดˆ๊ธฐ๊ฐ’ 0

  for percent in range(0, 101, 10):
      time.sleep(0.1)  # ํ”„๋กœ์„ธ์Šค ์ง„ํ–‰ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
      progress_bar.progress(percent)  # ์ง„ํ–‰๋ฅ  ์—…๋ฐ์ดํŠธ

(2) ์Šคํ”ผ๋„ˆ (st.spinner())

  • ์„ค๋ช…: ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ์ž‘์—… ์ค‘ ๋กœ๋”ฉ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
6
  import streamlit as st
  import time  

  with st.spinner("์ž ์‹œ๋งŒ ๊ธฐ๋‹ค๋ ค ์ฃผ์„ธ์š”..."):
      time.sleep(5)  # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  st.success("์ž‘์—… ์™„๋ฃŒ!")

(3) ์ถ•ํ•˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ (st.balloons())

  • ์„ค๋ช…: ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์—…์„ ์™„๋ฃŒํ–ˆ์„ ๋•Œ ํ’์„  ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ํ‘œ์‹œํ•˜์—ฌ ์ถ•ํ•˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
  import streamlit as st

  st.success("์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
  st.balloons()  # ์ถ•ํ•˜ ์• ๋‹ˆ๋ฉ”์ด์…˜

2. ์ƒํƒœ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ

  • Streamlit์€ ์„ฑ๊ณต, ์˜ค๋ฅ˜, ๊ฒฝ๊ณ , ์ •๋ณด ๋ฉ”์‹œ์ง€๋ฅผ ์‰ฝ๊ฒŒ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Image Source: https://www.datacamp.com/tutorial/streamlit

(1) ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ (st.success())

  • ์„ค๋ช…: ์„ฑ๊ณต์ ์ธ ์ž‘์—… ์™„๋ฃŒ๋ฅผ ์•Œ๋ฆฌ๋Š” ๋…น์ƒ‰ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.success("์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค! ๋ชจ๋“  ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")

(2) ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ (st.error())

  • ์„ค๋ช…: ์—๋Ÿฌ ๋˜๋Š” ์‹คํŒจํ•œ ์ž‘์—…์„ ๊ฐ•์กฐํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.error("์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")

(3) ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ (st.warning())

  • ์„ค๋ช…: ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•œ ์ž‘์—…์„ ๊ฐ•์กฐํ•˜๋Š” ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.warning("์ด ์ž‘์—…์€ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")

(4) ์ •๋ณด ๋ฉ”์‹œ์ง€ (st.info())

  • ์„ค๋ช…: ์ผ๋ฐ˜์ ์ธ ์ •๋ณด๋‚˜ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.info("Streamlit์„ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์›น ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")

(5) ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ (st.exception())

  • ์„ค๋ช…: Python์˜ ์˜ˆ์™ธ(Exception) ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
  st.exception(RuntimeError("RuntimeError ์˜ˆ์™ธ ๋ฐœ์ƒ"))

  1. Streamlit Component

Streamlit์€ ๋‹ค์–‘ํ•œ UI ์š”์†Œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ณด๋‹ค ๊ตฌ์กฐ์ ์œผ๋กœ ์ •๋ฆฌํ•˜๊ณ  ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์žฅ์—์„œ๋Š” ์‚ฌ์ด๋“œ๋ฐ”, ์ปจํ…Œ์ด๋„ˆ, ์ปฌ๋Ÿผ, ํƒญ, ์ต์ŠคํŒฌ๋”(expander) ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ •๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ 5.1. Sidebar ์‚ฌ์šฉํ•˜๊ธฐ

Streamlit์˜ st.sidebar()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ™”๋ฉด์˜ ์™ผ์ชฝ์— ๊ณ ์ •๋œ ์‚ฌ์ด๋“œ๋ฐ”(Sidebar) ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ์‚ฌ์ด๋“œ๋ฐ” ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

  • ์„ค๋ช…: st.sidebar๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ฑ์˜ ์ฃผ์š” ์ฝ˜ํ…์ธ ๋ฅผ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๊ณ  ์˜ต์…˜, ์ž…๋ ฅ ํ•„๋“œ, ๋ฒ„ํŠผ ๋“ฑ์„ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
6
7
8
  import streamlit as st

  st.sidebar.title("์‚ฌ์ด๋“œ๋ฐ” ๋ฉ”๋‰ด")
  st.sidebar.markdown("์—ฌ๊ธฐ์— ์„ค์ •์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")

  # ์‚ฌ์ด๋“œ๋ฐ” ์š”์†Œ ์ถ”๊ฐ€
  option = st.sidebar.radio("์˜ต์…˜ ์„ ํƒ", ["์˜ต์…˜ A", "์˜ต์…˜ B", "์˜ต์…˜ C"])
  st.sidebar.button("์‚ฌ์ด๋“œ๋ฐ” ๋ฒ„ํŠผ")

๐Ÿ“Œ ์ฃผ์š” ํŠน์ง•

  • st.sidebar.title(), st.sidebar.markdown(), st.sidebar.radio() ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ด๋“œ๋ฐ” ๋‚ด UI ๊ตฌ์„ฑ ๊ฐ€๋Šฅ
  • ์ฃผ์š” ์ฝ˜ํ…์ธ ๋ฅผ ํ™”๋ฉด ์ค‘์•™์— ๋ฐฐ์น˜ํ•˜๋ฉด์„œ ์„ค์ •์„ ์‚ฌ์ด๋“œ๋ฐ”์— ๋„ฃ์–ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ

๐Ÿ“Œ 5.2. ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปฌ๋Ÿผ ํ™œ์šฉ

Streamlit์—์„œ๋Š” st.columns()์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปฌ๋Ÿผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ ˆ์ด์•„์›ƒ์„ ๋” ์ •๋ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ๋™์ผํ•œ ๋„ˆ๋น„์˜ ์ปฌ๋Ÿผ ์ƒ์„ฑ

  • ์„ค๋ช…: st.columns(n)์„ ์‚ฌ์šฉํ•˜๋ฉด n๊ฐœ์˜ ๋™์ผํ•œ ํฌ๊ธฐ์˜ ์ปฌ๋Ÿผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
  col1, col2 = st.columns(2)

  col1.write("์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ")
  col2.write("๋‘ ๋ฒˆ์งธ ์ปฌ๋Ÿผ")

(2) ๋‹ค์–‘ํ•œ ๋„ˆ๋น„์˜ ์ปฌ๋Ÿผ ์ƒ์„ฑ

  • ์„ค๋ช…: st.columns([๋น„์œจ1, ๋น„์œจ2, ...])์„ ์‚ฌ์šฉํ•˜๋ฉด ๋น„์œจ์„ ์ง€์ •ํ•˜์—ฌ ๊ฐ€๋ณ€์ ์ธ ๋„ˆ๋น„์˜ ์ปฌ๋Ÿผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
6
7
8
9
10
11
12
13
  col1, col2, col3 = st.columns([2, 6, 2])

  with col1:
      st.header('Col1')
      st.image('https://static.streamlit.io/examples/cat.jpg')

  with col2:
      st.header('Col2')
      st.image('https://static.streamlit.io/examples/dog.jpg')

  with col3:
      st.header('Col3')
      st.image('https://static.streamlit.io/examples/owl.jpg')

๐Ÿ“Œ ์ฃผ์š” ํŠน์ง•

  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ UI ์š”์†Œ๋ฅผ ๋ณ‘๋ ฌ ๋ฐฐ์น˜ ๊ฐ€๋Šฅ
  • ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ๋ฐ ๋น„๊ต ๋ถ„์„ ํ™”๋ฉด ๊ตฌ์„ฑ์— ์œ ์šฉ

๐Ÿ“Œ 5.3. ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉํ•˜๊ธฐ

Streamlit์—์„œ๋Š” st.container()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด์šฉ์„ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

  • ์„ค๋ช…: st.container() ์•ˆ์— ์š”์†Œ๋ฅผ ๋ฐฐ์น˜ํ•˜๋ฉด ๋…ผ๋ฆฌ์ ์ธ ๊ทธ๋ฃน์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  with st.container():
      st.write("์ด๊ฒƒ์€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์ž…๋‹ˆ๋‹ค.")

๐Ÿ“Œ ์ฃผ์š” ํŠน์ง•

  • ํŠน์ • ์ฝ˜ํ…์ธ ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ UI๋ฅผ ์ •๋ˆ
  • ๋ฐ˜๋ณต์ ์ธ UI ์š”์†Œ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•  ๋•Œ ์œ ์šฉ

๐Ÿ“Œ 5.4. Tab(ํƒญ) ํ™œ์šฉํ•˜๊ธฐ

Streamlit์˜ st.tabs()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ ํ™”๋ฉด์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํƒญ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ๊ธฐ๋ณธ์ ์ธ ํƒญ ์ƒ์„ฑ

  • ์„ค๋ช…: st.tabs(["ํƒญ ์ด๋ฆ„1", "ํƒญ ์ด๋ฆ„2", ...])์„ ์‚ฌ์šฉํ•˜์—ฌ ํƒญ์„ ์ƒ์„ฑํ•˜๊ณ , ๊ฐ ํƒญ์— ์ฝ˜ํ…์ธ ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
4
5
6
7
8
9
10
11
12
13
  tab1, tab2, tab3 = st.tabs(['Cat', 'Dog', 'Owl'])

  with tab1:
      st.header('๊ณ ์–‘์ด')
      st.image('https://static.streamlit.io/examples/cat.jpg')

  with tab2:
      st.header('๊ฐ•์•„์ง€')
      st.image('https://static.streamlit.io/examples/dog.jpg')

  with tab3:
      st.header('์˜ฌ๋นผ๋ฏธ')
      st.image('https://static.streamlit.io/examples/owl.jpg')

๐Ÿ“Œ ์ฃผ์š” ํŠน์ง•

  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ •๋ณด๋ฅผ ํ•œ ํ™”๋ฉด์—์„œ ๊ด€๋ฆฌํ•˜๋ฉด์„œ๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒ์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ
  • ๋ฉ€ํ‹ฐ ํŽ˜์ด์ง€ ๊ตฌ์กฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

๐Ÿ“Œ 5.5. Expander(ํŽผ์น˜๊ธฐ) ์‚ฌ์šฉํ•˜๊ธฐ

Streamlit์—์„œ๋Š” st.expander()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ์ ‘๊ณ  ํŽผ์น˜๋Š” UI๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ๊ธฐ๋ณธ์ ์ธ Expander ์‚ฌ์šฉ๋ฒ•

  • ์„ค๋ช…: st.expander("์ œ๋ชฉ")์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ ‘ํ˜€ ์žˆ๋Š” ๋ธ”๋ก์„ ๋งŒ๋“ค๊ณ , ํด๋ฆญ ์‹œ ๋‚ด์šฉ์„ ํŽผ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
  with st.expander("๋” ๋ณด๊ธฐ"):
      st.write("์ด๊ณณ์— ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")

(2) Expander ๋‚ด๋ถ€์— ์ด๋ฏธ์ง€ ๋ฐ ํ…์ŠคํŠธ ์ถ”๊ฐ€

  • ์„ค๋ช…: st.expander() ๋‚ด๋ถ€์— ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์š”์†Œ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์ œ:
1
2
3
  with st.expander("๊ณ ์–‘์ด ์ •๋ณด ๋ณด๊ธฐ"):
      st.write("์—ฌ๊ธฐ์—๋Š” ๊ณ ์–‘์ด์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.")
      st.image('https://static.streamlit.io/examples/cat.jpg', width=200)

๐Ÿ“Œ ์ฃผ์š” ํŠน์ง•

  • ๊ธด ๋‚ด์šฉ์„ ํ•œ ๋ฒˆ์— ํ‘œ์‹œํ•˜์ง€ ์•Š๊ณ , ํ•„์š”ํ•  ๋•Œ๋งŒ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ ๊ฐ€๋Šฅ
  • ์„ค๋ช…์„œ, ์ถ”๊ฐ€ ์ •๋ณด ์ œ๊ณต ์‹œ ์œ ์šฉ

6. Streamlit ๋กœ์ปฌ ์„œ๋ฒ„ ์‹คํ–‰ ๋ฐ ์™ธ๋ถ€ ์ ‘์† ์„ค์ •

(์ฐธ๊ณ ) ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด?

1
streamlit run app.py
  • ๊ธฐ๋ณธ์ ์œผ๋กœ localhost:8501 ์—์„œ ์‹คํ–‰๋จ.
  • localhost๋Š” 127.0.0.1๋กœ ๋งคํ•‘๋˜๋ฉฐ, ์™ธ๋ถ€์—์„œ๋Š” ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Œ.
  • ์ฆ‰, ๊ฐ™์€ PC์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ณ , ๋‹ค๋ฅธ ๋„คํŠธ์›Œํฌ ๊ธฐ๊ธฐ(์˜ˆ: ์Šค๋งˆํŠธํฐ, ๋‹ค๋ฅธ ์ปดํ“จํ„ฐ)์—์„œ๋Š” ์ ‘์†ํ•  ์ˆ˜ ์—†์Œ.

๐Ÿ“Œ 6.1 ํŠน์ • ํฌํŠธ์—์„œ ์‹คํ–‰

  • ๊ธฐ๋ณธ ํฌํŠธ(8501) ๋Œ€์‹  8080 ํฌํŠธ์—์„œ ์‹คํ–‰๋จ.
1
streamlit run app.py --server.port 8080

โœ… ์™œ ํŠน์ • ํฌํŠธ๋ฅผ ๋ณ€๊ฒฝํ• ๊นŒ?

  • ํฌํŠธ ์ถฉ๋Œ ๋ฐฉ์ง€: ์ด๋ฏธ 8501 ํฌํŠธ๋ฅผ ๋‹ค๋ฅธ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉ ์ค‘์ผ ์ˆ˜๋„ ์žˆ์Œ.
  • ๊ธฐ๋ณธ ๋ฐฉํ™”๋ฒฝ ์ •์ฑ… ์šฐํšŒ: ์ผ๋ถ€ ๋„คํŠธ์›Œํฌ ํ™˜๊ฒฝ์—์„œ๋Š” ํŠน์ • ํฌํŠธ(์˜ˆ: 8501)๊ฐ€ ์ฐจ๋‹จ๋  ์ˆ˜ ์žˆ์Œ.
  • ๊ณต์œ ๋œ ์„œ๋ฒ„์—์„œ ๋‹ค๋ฅธ ์„œ๋น„์Šค์™€ ๊ตฌ๋ถ„: ๋™์ผํ•œ ์„œ๋ฒ„์—์„œ ์—ฌ๋Ÿฌ Streamlit ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ ํฌํŠธ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด์•ผ ํ•จ.

๐Ÿ“Œ 6.2 ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ์—์„œ ์ ‘์† ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ

  • ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ๋‚ด ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ๋„ http://<์„œ๋ฒ„ IP>:8080์œผ๋กœ ์ ‘์† ๊ฐ€๋Šฅ
1
streamlit run app.py --server.address 0.0.0.0 --server.port 8080

โœ… ์—ฌ๊ธฐ์„œ 0.0.0.0์ด ๋ฌด์—‡์ธ๊ฐ€?

  • ๊ธฐ๋ณธ์ ์œผ๋กœ Streamlit์€ localhost(127.0.0.1)์—์„œ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ, ์™ธ๋ถ€์—์„œ๋Š” ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Œ.
  • 0.0.0.0์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ํ•ด๋‹น ์ปดํ“จํ„ฐ์˜ ๋ชจ๋“  ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ.
  • ์ฆ‰, ๊ฐ™์€ ๋„คํŠธ์›Œํฌ์˜ ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ๋„ http://<์„œ๋ฒ„์˜ IP>:8080์„ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅ.

7. Streamlit ๋ฐฐํฌํ•˜๊ธฐ

๐Ÿ“Œ 7.1 Streamlit Community Cloud ๋ฐฐํฌ

  1. GitHub์— ํ”„๋กœ์ ํŠธ ์—…๋กœ๋“œ
  2. Streamlit Cloud ์ ‘์†
  3. New app ๋ฒ„ํŠผ ํด๋ฆญ ํ›„ GitHub ์ €์žฅ์†Œ ์„ ํƒ
  4. ์ž๋™์œผ๋กœ ๋ฐฐํฌ๋จ (๋ฌด๋ฃŒ ๋ฐฐํฌ ๊ฐ€๋Šฅ)

๐Ÿ“Œ 7.2 Docker๋ฅผ ํ™œ์šฉํ•œ ๋ฐฐํฌ

1) Dockerfile ์ž‘์„ฑ

1
2
3
4
5
6
7
8
FROM python:3.9

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=8501"]

2) Docker ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰

1
2
docker build -t my-streamlit-app .
docker run -p 8501:8501 my-streamlit-app

๐Ÿš€ ์ด์ œ http://<์„œ๋ฒ„ IP>:8501์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ!


โ„น๏ธ Streamlit ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐ ํ•™์Šต ์ž๋ฃŒ

๐ŸŽฏ ์ •๋ฆฌ

  • Streamlit์€ ๊ฐ„๋‹จํ•œ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋งŒ์œผ๋กœ ์›น/์•ฑ ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์ตœ๊ณ ์˜ ๋„๊ตฌ
  • ๋ฐฐํฌ๋„ ์‰ฝ๊ณ , ๋กœ์ปฌ ์„œ๋ฒ„์—์„œ๋„ ์†์‰ฝ๊ฒŒ ์‹คํ–‰ ๊ฐ€๋Šฅ
  • ๋ฐ์ดํ„ฐ ๋ถ„์„, ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ๋ฐฐํฌ, ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ• ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ํ™œ์šฉ ๊ฐ€๋Šฅ

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘‘



-->