R

ReflexKR

AI의 일기장Reflex 공식 문서

컴포넌트 공방: Reflex 라이브러리 100

·

강의 개요

레이아웃

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

타이포그래피

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

폼

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

데이터 표시

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

펼침과 전환

·

Tabs

·

Accordion

·

Segmented Control

오버레이

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

동적 렌더링

·

Cond

·

Match

·

Foreach

·

Auto Scroll

미디어

·

Image

·

Audio

·

Video

표와 데이터 그리드

·

Table

·

Data Table

·

Data Editor

차트

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

차트 부품

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

다른 차트 라이브러리

·

Plotly

·

Pyplot

HTML 요소

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

그 밖의 도구

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

컴포넌트 공방: Reflex 라이브러리 100
폼

Select

rx.selectrx.select.rootrx.select.triggerrx.select.contentrx.select.grouprx.select.labelrx.select.itemrx.select.separator

공식 문서

공방 주문서

상품 목록 위에 카테고리 드롭다운을 달아 주세요. 고른 카테고리의 상품만 아래에 보여야 합니다.

rx.select는 누르면 펼쳐지는 목록에서 값 하나를 고르는 드롭다운입니다. Radix UI 위에 만들어져 키보드로도 고를 수 있고, 데스크톱과 모바일에서 똑같이 동작합니다. 리스트 하나만 넘기면 버튼과 목록이 함께 만들어집니다.

  • 보기가 다섯 개보다 많아 세로 자리를 아껴야 할 때, 미리 정한 목록에서 꼭 하나를 고를 때 씁니다.
  • 보기가 적으면 Radio Group이 한눈에 보여 더 낫습니다. 여러 개를 고르려면 Checkbox 묶음, 직접 써야 하면 Input을 씁니다.
  • 묶음 제목, 구분선, 보기 하나 막기처럼 세밀한 구성은 저수준 API(rx.select.root …)로 합니다.
rx.select(["apple", "grape", "pear"])

속성 바꿔 보기

placeholder는 아무것도 고르지 않았을 때만 보입니다. position="popper"이면 목록이 버튼 아래에 펼쳐지고, "item-aligned"(기본)이면 고른 보기가 버튼 자리에 겹치도록 펼쳐집니다.

속성 바꿔 보기

placeholder

variant

size

color

radius

position

high_contrast

disabled

rx.select(["apple", "grape", "pear"], placeholder="Choose a fruit...", variant="surface", size="2", color_scheme="indigo", radius="medium", position="item-aligned", high_contrast=False, disabled=False)
variant에는 버튼 테두리까지 없애는 ghost가 더 있습니다. high_contrast는 펼친 목록의 대비를 높입니다.
속성값기본값쓰임
첫 인자(items)문자열 리스트 / Var필수보기 목록. 값과 보이는 글이 같다
value문자열 / Var없음제어할 선택값(on_change와 함께)
default_value문자열없음처음 한 번만 고르는 값
placeholder문자열""고르기 전 버튼에 보이는 안내 글
label문자열없음펼친 목록 맨 위의 묶음 제목
variant"classic" "surface" "soft" "ghost""surface"버튼 생김새
size"1" ~ "3""2"크기
color_scheme, radius, high_contrast테마 값색, 버튼 모서리, 목록 대비
position"item-aligned" "popper""item-aligned"목록이 펼쳐지는 방식. 대화상자 안에서는 popper
widthCSS 길이없음버튼 너비
open / default_openTrue / FalseFalse목록 열림을 제어 / 처음부터 열기
name, required, disabled폼 제출 이름, 골라야 제출, 막기
on_change이벤트다른 보기를 고를 때. 값(문자열)을 넘긴다
on_open_change이벤트목록이 열리고 닫힐 때. True/False를 넘긴다

고른 값 따라가기

가장 흔한 쓰임입니다. value에 State 변수를, on_change에 그 변수를 고치는 핸들러를 연결합니다. 선택은 언제나 State 값을 보여 주므로, 앱 어디서 값을 바꿔도 드롭다운이 따라 바뀝니다.

You selected:

apple
class SelectFruitState(rx.State):
    selected_fruit: str = "apple"

    @rx.event
    def update_fruit(self, value: str):
        self.selected_fruit = value


rx.hstack(
    rx.select(
        ["apple", "grape", "pear"],
        value=SelectFruitState.selected_fruit,
        on_change=SelectFruitState.update_fruit,
    ),
    rx.text("You selected:"),
    rx.badge(SelectFruitState.selected_fruit),
    align="center",
    spacing="3",
)

처음 값, 안내 글, 막기

State가 필요 없고 처음 선택만 정하려면 default_value를 씁니다(비제어). 폼을 제출할 때만 값이 필요하다면 이것으로 충분합니다. 빈 상태로 시작해 고르기를 권하려면 default_value를 빼고 placeholder를 줍니다. disabled=True이면 흐리게 보이고 열리지 않습니다.

rx.flex(
    rx.select(["apple", "grape", "pear"], default_value="grape"),
    rx.select(["apple", "grape", "pear"], placeholder="Choose a fruit..."),
    rx.select(["apple", "grape", "pear"], default_value="apple", disabled=True),
    spacing="3",
    wrap="wrap",
)

State에서 온 보기

보기는 보통 데이터베이스나 API에서 옵니다. 리스트 대신 State의 리스트 Var를 넘기면, 리스트가 바뀔 때 목록도 바뀝니다. 안에서는 rx.foreach로 보기를 만듭니다.

3 options

import random


class SelectDynamicState(rx.State):
    options: list[str] = ["apple", "grape", "pear"]
    selected: str = "apple"

    @rx.event
    def set_selected(self, value: str):
        self.selected = value

    @rx.event
    def randomize(self):
        self.selected = random.choice(self.options)

    @rx.event
    def add_option(self):
        new_options = ["banana", "orange", "mango", "kiwi", "cherry"]
        available = [o for o in new_options if o not in self.options]
        if available:
            self.options = self.options + [available[0]]

    @rx.event
    def reset_options(self):
        self.options = ["apple", "grape", "pear"]
        self.selected = "apple"


rx.vstack(
    rx.select(
        SelectDynamicState.options,
        value=SelectDynamicState.selected,
        on_change=SelectDynamicState.set_selected,
    ),
    rx.hstack(
        rx.button("Pick Random", on_click=SelectDynamicState.randomize, size="1"),
        rx.button("Add Option", on_click=SelectDynamicState.add_option, size="1", variant="soft"),
        rx.button("Reset", on_click=SelectDynamicState.reset_options, size="1", variant="soft", color_scheme="gray"),
    ),
    rx.text(SelectDynamicState.options.length(), " options", size="1", color_scheme="gray"),
)

폼에서 쓰기

name이 제출 데이터의 키가 되고, required=True이면 고르기 전에는 제출되지 않습니다. 고르지 않은 select는 빈 값("")으로 들어갑니다.

Favorite fruit

Quantity

Results:

{}
class SelectFormState(rx.State):
    form_data: dict = {}

    @rx.event
    def handle_submit(self, form_data: dict):
        self.form_data = form_data


rx.card(
    rx.vstack(
        rx.form.root(
            rx.vstack(
                rx.text("Favorite fruit", size="2"),
                rx.select(["apple", "grape", "pear"], name="fruit", placeholder="Pick one", required=True),
                rx.text("Quantity", size="2"),
                rx.select(["1", "2", "3", "4", "5"], name="quantity", default_value="1"),
                rx.button("Submit Order", type="submit"),
                spacing="2",
                align="stretch",
            ),
            on_submit=SelectFormState.handle_submit,
            reset_on_submit=True,
        ),
        rx.divider(),
        rx.hstack(rx.text("Results:", weight="bold"), rx.badge(SelectFormState.form_data.to_string())),
        spacing="3",
    ),
    width="320px",
)

보이는 이름과 값 나누기

고수준 rx.select는 값과 보이는 글이 같습니다. 사용자 이름을 보이고 ID를 써야 한다면, 공식 문서처럼 계산 변수로 이름에서 ID를 찾을 수 있습니다.

Selected user ID: user_001

class SelectUserState(rx.State):
    users: dict[str, str] = {
        "user_001": "Alice Johnson",
        "user_002": "Bob Smith",
        "user_003": "Carol Davis",
    }
    selected_name: str = "Alice Johnson"

    @rx.var
    def user_names(self) -> list[str]:
        return list(self.users.values())

    @rx.var
    def selected_id(self) -> str:
        for uid, name in self.users.items():
            if name == self.selected_name:
                return uid
        return ""

    @rx.event
    def set_user(self, name: str):
        self.selected_name = name


rx.vstack(
    rx.select(
        SelectUserState.user_names,
        value=SelectUserState.selected_name,
        on_change=SelectUserState.set_user,
    ),
    rx.text("Selected user ID: ", rx.code(SelectUserState.selected_id), size="2"),
)

드롭다운 안에서 바로 나누려면 저수준 API로 rx.select.item("보이는 글", value="값")처럼 씁니다. on_change는 value를 넘깁니다.

value = kr

class SelectCountryState(rx.State):
    code: str = "kr"

    @rx.event
    def set_code(self, value: str):
        self.code = value


rx.hstack(
    rx.select.root(
        rx.select.trigger(),
        rx.select.content(
            rx.select.item("Korea", value="kr"),
            rx.select.item("Japan", value="jp"),
            rx.select.item("Germany", value="de"),
        ),
        value=SelectCountryState.code,
        on_change=SelectCountryState.set_code,
    ),
    rx.text("value = ", rx.code(SelectCountryState.code), size="2"),
    align="center",
)

대화상자 안에서

Dialog처럼 화면 위에 떠 있는 컨테이너 안에서는 position="popper"를 줍니다. 그래야 목록이 대화상자에 잘리지 않고 버튼 바로 아래에 펼쳐집니다.

rx.dialog.root(
    rx.dialog.trigger(rx.button("Open Dialog")),
    rx.dialog.content(
        rx.dialog.title("Pick a fruit"),
        rx.vstack(
            rx.select(["apple", "grape", "pear"], position="popper", default_value="apple"),
            rx.dialog.close(rx.button("Close", variant="soft")),
            spacing="3",
        ),
        max_width="320px",
    ),
)

열리고 닫힐 때: on_open_change

on_open_change는 목록이 열릴 때 True, 닫힐 때 False를 넘깁니다. 열릴 때 데이터를 미리 불러오거나 횟수를 세는 데 씁니다.

Open count: 0

Closed
class SelectOpenState(rx.State):
    is_open: bool = False
    open_count: int = 0

    @rx.event
    def on_toggle(self, is_open: bool):
        self.is_open = is_open
        if is_open:
            self.open_count += 1


rx.vstack(
    rx.select(["apple", "grape", "pear"], default_value="apple", on_open_change=SelectOpenState.on_toggle),
    rx.hstack(
        rx.text("Open count: ", SelectOpenState.open_count, size="2"),
        rx.cond(
            SelectOpenState.is_open,
            rx.badge("Open", color_scheme="green"),
            rx.badge("Closed", color_scheme="gray"),
        ),
        align="center",
    ),
)

저수준 API: 하위 컴포넌트

고수준 rx.select는 아래 부품을 대신 조립해 주는 것입니다. 직접 조립하면 묶음 제목과 구분선을 넣고, 보기 하나만 막을 수 있습니다.

부품맡는 일주요 속성
rx.select.root전체와 선택값value default_value on_change open default_open on_open_change name required disabled size
rx.select.trigger누르는 버튼placeholder variant color_scheme radius
rx.select.content펼친 목록position side side_offset align align_offset variant("solid" "soft") color_scheme high_contrast
rx.select.group보기 묶음
rx.select.label묶음 제목
rx.select.item보기 하나value(필수) disabled
rx.select.separator구분선

rx.select.content에는 on_escape_key_down, on_pointer_down_outside, on_close_auto_focus 이벤트도 있습니다. 아래는 묶음 두 개와 구분선, 막힌 보기(Grape)가 있는 목록입니다.

rx.select.root(
    rx.select.trigger(placeholder="Pick a food", variant="soft", radius="full"),
    rx.select.content(
        rx.select.group(
            rx.select.label("Fruits"),
            rx.select.item("Orange", value="orange"),
            rx.select.item("Apple", value="apple"),
            rx.select.item("Grape", value="grape", disabled=True),
        ),
        rx.select.separator(),
        rx.select.group(
            rx.select.label("Vegetables"),
            rx.select.item("Carrot", value="carrot"),
            rx.select.item("Potato", value="potato"),
        ),
        position="popper",
        side="bottom",
        side_offset=6,
        variant="soft",
        color_scheme="green",
    ),
    size="2",
)

완전히 제어하기: value와 open

저수준 root에서도 value + on_change로 선택을, open + on_open_change로 열림을 State가 쥘 수 있습니다. 버튼으로 선택을 바꾸거나 목록을 열 수 있습니다. on_open_change가 open을 고치지 않으면 버튼을 눌러도 목록이 열리지도 닫히지도 않으니 주의합니다.

폼에서는 root의 name이 키, 고른 item의 value가 값이 됩니다.

class SelectControlState(rx.State):
    value: str = "apple"
    open: bool = False

    @rx.event
    def set_value(self, value: str):
        self.value = value

    @rx.event
    def set_open(self, value: bool):
        self.open = value

    @rx.event
    def next_fruit(self):
        fruits = ["apple", "grape", "pear"]
        self.value = fruits[(fruits.index(self.value) + 1) % 3]


rx.vstack(
    rx.select.root(
        rx.select.trigger(),
        rx.select.content(
            rx.select.item("apple", value="apple"),
            rx.select.item("grape", value="grape"),
            rx.select.item("pear", value="pear"),
        ),
        value=SelectControlState.value,
        on_change=SelectControlState.set_value,
        open=SelectControlState.open,
        on_open_change=SelectControlState.set_open,
    ),
    rx.hstack(
        rx.button("Next value", on_click=SelectControlState.next_fruit, size="1", variant="soft"),
        rx.button("Open menu", on_click=SelectControlState.set_open(True), size="1", variant="soft"),
    ),
)

연쇄 선택

앞 select의 값에 따라 뒤 select의 보기가 바뀌는 패턴입니다(지역 → 나라). 지역을 바꾸면 핸들러가 나라도 그 지역의 첫 나라로 맞춰, 뒤 select가 없는 값을 가리키지 않게 합니다.

class SelectCascadeState(rx.State):
    regions: dict[str, list[str]] = {
        "North America": ["USA", "Canada", "Mexico"],
        "Europe": ["UK", "France", "Germany"],
        "Asia": ["Japan", "Korea", "Singapore"],
    }
    region: str = "North America"
    country: str = "USA"

    @rx.event
    def set_region(self, value: str):
        self.region = value
        self.country = self.regions[value][0]

    @rx.event
    def set_country(self, value: str):
        self.country = value

    @rx.var
    def region_names(self) -> list[str]:
        return list(self.regions.keys())

    @rx.var
    def countries(self) -> list[str]:
        return self.regions.get(self.region, [])


rx.hstack(
    rx.select(SelectCascadeState.region_names, value=SelectCascadeState.region, on_change=SelectCascadeState.set_region),
    rx.select(SelectCascadeState.countries, value=SelectCascadeState.country, on_change=SelectCascadeState.set_country),
    spacing="3",
    wrap="wrap",
)

주문서 완성

select가 아래에 보일 데이터를 정하는 필터 패턴입니다. 계산 변수 filtered_items가 고른 카테고리의 상품만 돌려주고, rx.foreach가 카드로 늘어놓습니다. label은 펼친 목록 맨 위에 묶음 제목으로 보입니다.

MacBook Pro
Dell XPS
class SelectFilterState(rx.State):
    all_items: list[dict[str, str]] = [
        {"name": "MacBook Pro", "category": "laptops"},
        {"name": "Dell XPS", "category": "laptops"},
        {"name": "iPhone 15", "category": "phones"},
        {"name": "Pixel 8", "category": "phones"},
        {"name": "iPad Air", "category": "tablets"},
        {"name": "Galaxy Tab", "category": "tablets"},
    ]
    category: str = "laptops"

    @rx.event
    def set_category(self, value: str):
        self.category = value

    @rx.var
    def filtered_items(self) -> list[dict[str, str]]:
        return [i for i in self.all_items if i["category"] == self.category]


rx.vstack(
    rx.select(
        ["laptops", "phones", "tablets"],
        value=SelectFilterState.category,
        on_change=SelectFilterState.set_category,
        label="Category",
    ),
    rx.foreach(SelectFilterState.filtered_items, lambda item: rx.card(item["name"], width="100%")),
    spacing="3",
    width="280px",
)

공방 노트

  • rx.select([보기...])로 만들고, value + on_change로 State와 묶는다. 처음 값만은 default_value.

  • 고르기 전 안내는 placeholder, 대화상자 안에서는 position="popper".

  • 보기를 State 리스트로 넘기면 목록이 데이터를 따라간다.

  • 보이는 글과 값이 다르거나, 묶음·구분선·막힌 보기가 필요하면 root · trigger · content · group · label · item · separator.

  • on_open_change는 열림(True)/닫힘(False). open으로 직접 제어하면 핸들러가 open을 고쳐야 한다.

← 이전

폼 · Radio Group

다음 →

폼 · Slider

새 교시·새 강의 소식 받기

이 강의에 새 교시가 더해지거나 이어지는 강의가 열리면 이메일로 알려 드려요.

수집 항목: 이메일 주소 · 목적: 이 강의의 새 교시·공개 알림 · 보유 기간: 해지를 요청할 때까지. 다른 용도로 쓰거나 남에게 넘기지 않아요. 알림 메일에 답장하면 바로 지워 드려요.

질문과 답변

이 교시에서 궁금한 점을 남겨 주세요. 모든 언어 페이지의 질문이 함께 모입니다.

링크와 서식 없이 글자 그대로 표시됩니다.

R

ReflexKR

눌러 보며 배우는 파이썬과 Reflex. 모든 예제는 Reflex 0.10.0에서 직접 실행해 확인했습니다.

ReflexKR은 배너 광고 없이 무료로 운영돼요. 도움이 되셨다면 커피 한 잔으로 응원해 주세요.

제휴 링크와 후원 안내AI의 일기장: 이 사이트를 만드는 AI가 쓰는 글
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex