컴포넌트 공방: 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
rx.selectrx.select.rootrx.select.triggerrx.select.contentrx.select.grouprx.select.labelrx.select.itemrx.select.separator공식 문서
공방 주문서
상품 목록 위에 카테고리 드롭다운을 달아 주세요. 고른 카테고리의 상품만 아래에 보여야 합니다.
rx.select는 누르면 펼쳐지는 목록에서 값 하나를 고르는 드롭다운입니다. Radix UI 위에 만들어져 키보드로도 고를 수 있고, 데스크톱과 모바일에서 똑같이 동작합니다. 리스트 하나만 넘기면 버튼과 목록이 함께 만들어집니다.
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)| 속성 | 값 | 기본값 | 쓰임 |
|---|---|---|---|
첫 인자(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 |
width | CSS 길이 | 없음 | 버튼 너비 |
open / default_open | True / False | False | 목록 열림을 제어 / 처음부터 열기 |
name, required, disabled | 폼 제출 이름, 골라야 제출, 막기 | ||
on_change | 이벤트 | 다른 보기를 고를 때. 값(문자열)을 넘긴다 | |
on_open_change | 이벤트 | 목록이 열리고 닫힐 때. True/False를 넘긴다 |
가장 흔한 쓰임입니다. value에 State 변수를, on_change에 그 변수를 고치는 핸들러를 연결합니다. 선택은 언제나 State 값을 보여 주므로, 앱 어디서 값을 바꿔도 드롭다운이 따라 바뀝니다.
You selected:
appleclass 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",
)보기는 보통 데이터베이스나 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는 빈 값("")으로 들어갑니다.
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는 목록이 열릴 때 True, 닫힐 때 False를 넘깁니다. 열릴 때 데이터를 미리 불러오거나 횟수를 세는 데 씁니다.
Open count: 0
Closedclass 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",
),
)고수준 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",
)저수준 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은 펼친 목록 맨 위에 묶음 제목으로 보입니다.
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을 고쳐야 한다.
새 교시·새 강의 소식 받기
이 강의에 새 교시가 더해지거나 이어지는 강의가 열리면 이메일로 알려 드려요.
R
ReflexKR
눌러 보며 배우는 파이썬과 Reflex. 모든 예제는 Reflex 0.10.0에서 직접 실행해 확인했습니다.
ReflexKR은 배너 광고 없이 무료로 운영돼요. 도움이 되셨다면 커피 한 잔으로 응원해 주세요.
© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
질문과 답변
이 교시에서 궁금한 점을 남겨 주세요. 모든 언어 페이지의 질문이 함께 모입니다.