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
그 밖의 도구

Memo

rx.memorx.RestProprx.EMPTY_VAR_STR

공식 문서

공방 주문서

장바구니 숫자가 바뀔 때마다 옆의 상품 카드 목록까지 다시 그려지지 않도록, 카드만 따로 떼어 주세요.

@rx.memo는 함수를 메모이즈된 React 컴포넌트로 바꿉니다. 컴파일러는 그 함수를 따로 된 모듈로 내보내고, React의 memo가 선언된 속성이 바뀔 때만 다시 그립니다. 그리기가 무겁고, 상태 중 좁은 일부에만 기대는 부분 트리에 씁니다. 이 페이지의 @rx.memo 함수는 모두 예제 코드 위쪽(State 소스)에 정의되어 있습니다.

Count: 0

Memoized Component

This component only re-renders when props change.

class MemoDemoState(rx.State):
    count: int = 0

    @rx.event
    def increment(self):
        self.count += 1


@rx.memo
def memo_expensive_component(label: rx.Var[str]) -> rx.Component:
    return rx.vstack(
        rx.heading(label, as_="h2", size="4"),
        rx.text("This component only re-renders when props change."),
        rx.divider(),
    )


rx.vstack(
    rx.text("Count: ", MemoDemoState.count),
    rx.button("Increment", on_click=MemoDemoState.increment),
    memo_expensive_component(label="Memoized Component"),
)

memo_expensive_component는 label이 바뀔 때만 다시 그려집니다. Increment로 count를 올려도 이 컴포넌트는 다시 그려지지 않습니다.

State 값을 속성으로

rx.memo에는 눈에 보이는 속성이 없으므로, 실습대 대신 State로 움직이는 예제로 봅니다. 속성에는 State의 Var를 그대로 넘길 수 있고, 그 Var가 바뀌면 다시 그려집니다.

Hello, World

class MemoGreetState(rx.State):
    name: str = "World"

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


@rx.memo
def memo_greeting(name: rx.Var[str]) -> rx.Component:
    return rx.heading("Hello, " + name, as_="h2", size="5")


rx.vstack(
    memo_greeting(name=MemoGreetState.name),
    rx.input(value=MemoGreetState.name, on_change=MemoGreetState.change_name),
)

호출하는 자리에서 State를 속성에 묶어도 State가 페이지로 끌려 들어오지 않습니다. 컴파일러가 그 호출을 자동 생성 래퍼 컴포넌트로 옮기고, 래퍼가 필요한 State 훅을 가집니다. State가 바뀌면 래퍼가 다시 그려지지만, 속성 값이 실제로 바뀌지 않았다면 React의 memo가 거기서 멈춥니다. 무거운 컴포넌트에 꼭 필요한 Var만 넘기는 것이 요령입니다.

규칙

표시값기본값쓰임
rx.Var[T]str, int, TypedDict …없음일반 속성. 함수 안에서는 Var로 다룬다
rx.RestProp매개변수 하나까지없음선언하지 않은 나머지 속성을 그대로 넘긴다(JSX의 ...rest)
children: rx.Var[rx.Component]이름이 꼭 children없음호출하는 쪽의 자식 컴포넌트
기본값rx.EMPTY_VAR_STR rx.EMPTY_VAR_INT rx.EMPTY_VAR_COMPONENT없음기본값도 Var여야 한다. 빈 문자열·0·빈 컴포넌트
호출memo_fn(title="...")속성은 이름(키워드)으로만 넘긴다
wrapper=Var 또는 NoneReact의 memo감쌀 자바스크립트 함수

rx.foreach와 함께

목록의 항목마다 메모 컴포넌트를 그리려면 호출을 람다로 감싸고 속성을 키워드로 넘깁니다. 항목을 구별하는 key도 함께 넘깁니다. key가 실제 요소에 닿도록 rx.RestProp 매개변수를 선언해 반환할 컴포넌트에 펼쳐 넣습니다. key를 시그니처에 직접 선언하면 안 됩니다. 함수 안의 task는 dict가 아니라 Var라서 task["name"]처럼 꺼내 쓸 수는 있지만, 반복하거나 .keys() 같은 dict 메서드를 부를 수는 없습니다.

Write docs

Review PR

from typing import TypedDict


class MemoTask(TypedDict):
    id: str
    name: str


class MemoTaskState(rx.State):
    tasks: list[MemoTask] = [
        {"id": "1", "name": "Write docs"},
        {"id": "2", "name": "Review PR"},
    ]

    @rx.event
    def add_task(self):
        n = len(self.tasks) + 1
        self.tasks = [*self.tasks, {"id": str(n), "name": f"Task {n}"}][-6:]


@rx.memo
def memo_task_card(rest: rx.RestProp, *, task: rx.Var[MemoTask]) -> rx.Component:
    return rx.card(rx.text(task["name"]), rest)


rx.vstack(
    rx.foreach(
        MemoTaskState.tasks,
        lambda task: memo_task_card(task=task, key=task["id"]),
    ),
    rx.button("Add task", on_click=MemoTaskState.add_task, variant="soft"),
)

rx.RestProp으로 속성 넘기기

rx.RestProp은 선언하지 않은 속성을 받아 그대로 넘깁니다. 기본 컴포넌트에 모양만 입힌 얇은 포장을 만들 때, 속성을 하나하나 다시 선언하지 않아도 됩니다. rest는 속을 들여다볼 수 없는 값으로 보고, 쓸 컴포넌트에 위치 인자로 넘깁니다. on_click, id, title이 모두 rest를 타고 버튼에 닿습니다.

@rx.memo
def memo_pill_button(rest: rx.RestProp, *, label: rx.Var[str]) -> rx.Component:
    return rx.button(label, rest, radius="full", variant="surface")


rx.flex(
    memo_pill_button(label="Save", on_click=rx.toast("Saved"), id="memo_save"),
    memo_pill_button(label="Help", on_click=rx.toast("Help is on the way"), title="Opens help"),
    spacing="3",
)

.merge로 rest에 다른 dict를 합칠 수 있고, rest.get(...)으로 값을 꺼내 Var 연산을 할 수 있습니다. 다만 값은 컴파일할 때 알 수 없으므로 파이썬 if로 갈라 볼 수는 없습니다. Reflex 0.10에서 공식 문서의 예제를 그대로 쓰면 두 군데가 어긋납니다. rest는 브라우저에서 쓰는 자바스크립트 이름(className)을 담고 있어서 "class_name"으로는 꺼내지지 않고, rest.get(...)의 결과는 타입이 정해지지 않은 Var라 바로 + " ..."를 하면 오류가 납니다. 그래서 "className"으로 꺼내고 합치며, .to(str)로 문자열 Var로 바꾼 뒤 더합니다.

@rx.memo
def memo_merged_button(rest: rx.RestProp, *, label: rx.Var[str]) -> rx.Component:
    class_name = rest.get("className", "").to(str) + " font-bold tracking-wide"
    return rx.button(label, rest.merge({"className": class_name}))


rx.flex(
    memo_merged_button(label="Default"),
    memo_merged_button(label="Caller class", class_name="italic"),
    spacing="3",
)

한계: 빌드할 때 쓰이는 속성

rx.RestProp은 속성을 실행할 때 넘기므로, 대상 컴포넌트의 create()가 빌드할 때 쓰는 값은 실어 나르지 못합니다. rx.link의 is_external, rx.icon의 tag, 직접 만든 create()가 받는 키워드가 그렇습니다. rest로 보내면 그냥 속성이나 CSS가 되어 아무 효과가 없습니다. 이런 값은 따로 rx.Var[...] 매개변수로 받아 함수 본문에서 create()에 직접 넘깁니다.

P: Foo
class MemoPrefixText(rx.el.Span):
    @classmethod
    def create(cls, *children, prefix: rx.Var[str] | str = "", **props) -> rx.Component:
        return super().create(prefix, *children, **props)


# prefix is used by create(), so it is not passed through rest.
# take it as its own parameter and pass it to create() in the memo body.
@rx.memo
def memo_styled_text(rest: rx.RestProp, *, prefix: rx.Var[str]) -> rx.Component:
    return MemoPrefixText.create("Foo", rest, prefix=prefix)


memo_styled_text(prefix="P: ", class_name="italic")

자식 받기

이름이 children이고 rx.Var[rx.Component]로 표시한 매개변수가 호출하는 쪽의 자식을 받습니다. 자식은 위치 인자로, 나머지 속성은 키워드로 넘깁니다. note처럼 기본값이 rx.EMPTY_VAR_STR인 속성은 생략할 수 있습니다.

Memoized card

Body copy goes here.

With a note

note is optional

@rx.memo
def memo_panel(
    children: rx.Var[rx.Component],
    *,
    title: rx.Var[str],
    note: rx.Var[str] = rx.EMPTY_VAR_STR,
) -> rx.Component:
    return rx.box(
        rx.heading(title, as_="h3", size="3"),
        children,
        rx.text(note, size="1", color_scheme="gray"),
        padding="12px",
        border=f"1px solid {rx.color('gray', 6)}",
        border_radius="8px",
    )


rx.flex(
    memo_panel(rx.text("Body copy goes here."), title="Memoized card"),
    memo_panel(rx.button("Inside", size="1"), title="With a note", note="note is optional"),
    spacing="3",
    wrap="wrap",
)

컴포넌트 대신 Var 돌려주기

반환 표시를 rx.Var[T]로 하면 컴포넌트가 아니라 평범한 자바스크립트 함수가 만들어지고, 호출한 자리는 페이지에 끼워 쓸 수 있는 Var가 됩니다. 이런 메모의 본문은 컴파일할 때 한 번 실행되므로 Var 연산만 쓸 수 있습니다. 훅도, Var를 보고 하는 파이썬 분기도 안 됩니다.

USD: $100

class MemoPriceState(rx.State):
    amount: int = 100
    currency: str = "USD"

    @rx.event
    def raise_price(self):
        self.amount += 10


@rx.memo
def memo_format_price(amount: rx.Var[int], currency: rx.Var[str]) -> rx.Var[str]:
    return currency.to(str) + ": $" + amount.to(str)


rx.hstack(
    rx.text(memo_format_price(amount=MemoPriceState.amount, currency=MemoPriceState.currency), weight="bold"),
    rx.button("+10", on_click=MemoPriceState.raise_price, size="1", variant="soft"),
    align="center",
)

자바스크립트 래퍼 바꾸기

컴파일된 함수 컴포넌트는 기본으로 React의 memo로 감싸집니다. wrapper=에 다른 호출 가능한 Var를 주면 그것으로 감싸고, wrapper=None이면 감싸지 않은 함수 컴포넌트를 그대로 내보냅니다. 아래는 wrapper=None의 예입니다. 컴포넌트를 돌려주는 메모에만 쓸 수 있고, Var를 돌려주는 메모에 주면 거부됩니다.

@rx.memo(wrapper=None)
def memo_swatch(width: rx.Var[int], height: rx.Var[int]) -> rx.Component:
    return rx.box(width=width.to_string() + "px", height=height.to_string() + "px", background=rx.color("indigo", 9), border_radius="4px")


rx.flex(
    memo_swatch(width=40, height=20),
    memo_swatch(width=80, height=20),
    memo_swatch(width=120, height=20),
    spacing="2",
    align="center",
)

다른 라이브러리의 함수로 감쌀 때는 FunctionStringVar에 import를 실어 보냅니다. 아래 예는 mobx-react-lite 패키지가 필요해서 코드로만 보입니다.

observer = rx.vars.FunctionStringVar(
    "observer",
    _var_data=rx.vars.VarData(imports={"mobx-react-lite": "observer"}),
)


@rx.memo(wrapper=observer)          # wrap with mobx's observer instead of React's memo
def observed_panel(label: rx.Var[str]) -> rx.Component:
    return rx.text(label)

언제 쓰고 언제 말까

쓸 때: 그리기가 무겁고, 결과가 적은 수의 속성으로 정해지며, 자주 바뀌는 조상 때문에 쓸데없이 다시 그려질 때. 말 때: 가벼운 컴포넌트라 장부 관리가 더 비쌀 때, 또는 속성이 어차피 매번 바뀌어서 memo가 한 번도 건너뛰지 못할 때.

옛 rx.memo에서 옮기기

예전 rx.memo는 title: str처럼 평범한 타입 표시를 받았습니다. 지금은 rx.Var[...]가 필요합니다. 옛 이름 rx._x.memo는 새 rx.memo로 이어지고, 한 번 안내 문구를 찍습니다.

# before
@rx.memo
def card(title: str) -> rx.Component: ...


# now
@rx.memo
def card(title: rx.Var[str]) -> rx.Component: ...

API

인자타입쓰임
component_fnCallable[..., rx.Component | rx.Var]메모로 만들 함수. 매개변수는 모두 rx.Var[...] 또는 rx.RestProp
wrapperrx.Var | None감쌀 JS 함수. 기본은 React의 memo, None이면 감싸지 않는다. 컴포넌트를 돌려주는 메모에만

rx.memo(fn)처럼 바로 쓰거나 rx.memo(wrapper=...)(fn)처럼 키워드만 주어 데코레이터를 만듭니다.

주문서 완성

상품 카드를 @rx.memo로 떼어 냈습니다. 카드는 product만 받으므로, Add one으로 장바구니 숫자가 바뀌어도 카드는 다시 그려지지 않습니다. width="100%"는 rest를 타고 카드에 닿습니다.

0

Desk lamp

$39

Notebook

$6

Fountain pen

$58
from typing import TypedDict


class MemoProduct(TypedDict):
    id: str
    name: str
    price: str


class MemoShopState(rx.State):
    cart: int = 0
    products: list[MemoProduct] = [
        {"id": "p1", "name": "Desk lamp", "price": "$39"},
        {"id": "p2", "name": "Notebook", "price": "$6"},
        {"id": "p3", "name": "Fountain pen", "price": "$58"},
    ]

    @rx.event
    def add_one(self):
        self.cart += 1

    @rx.event
    def empty_cart(self):
        self.cart = 0


@rx.memo
def memo_product_card(rest: rx.RestProp, *, product: rx.Var[MemoProduct]) -> rx.Component:
    return rx.card(
        rx.flex(
            rx.icon("package", size=18),
            rx.text(product["name"], weight="bold"),
            rx.spacer(),
            rx.badge(product["price"], variant="soft"),
            spacing="3",
            align="center",
        ),
        rest,
    )


rx.vstack(
    rx.hstack(
        rx.badge(rx.icon("shopping-cart", size=14), MemoShopState.cart, size="2"),
        rx.button("Add one", on_click=MemoShopState.add_one, size="1"),
        rx.button("Empty", on_click=MemoShopState.empty_cart, size="1", variant="soft", color_scheme="gray"),
        align="center",
    ),
    rx.foreach(
        MemoShopState.products,
        lambda p: memo_product_card(product=p, key=p["id"], width="100%"),
    ),
    width="100%",
    max_width="380px",
)

공방 노트

  • @rx.memo는 선언한 속성이 바뀔 때만 다시 그리는 React 컴포넌트를 만든다.

  • 매개변수는 모두 rx.Var[T] 또는 rx.RestProp(하나까지). 자식은 children: rx.Var[rx.Component].

  • 호출은 키워드로. 기본값은 rx.EMPTY_VAR_STR 같은 Var. foreach에서는 key를 rest로.

  • rest는 실행할 때 넘기는 속성뿐. create()가 빌드 때 쓰는 값은 따로 매개변수로.

  • -> rx.Var[T]면 JS 함수가 된다. wrapper=None은 memo로 감싸지 않는다.

더 배우고 싶다면

제휴 링크

이 강의 다음에 이어서 보기 좋은 강의입니다. 제휴 링크라서 이 링크로 수강하면 ReflexKR에 수수료가 돌아가고, 수강료는 똑같습니다. 고르는 기준

처음하는 파이썬 백엔드와 웹기술 입문 (파이썬 중급, Flask)

잔재미코딩 DaveLee · 인프런 · ★4.9 · 수강평 195

처음하는 파이썬 백엔드 FastAPI 입문

잔재미코딩 DaveLee · 인프런 · ★4.8 · 수강평 63

← 이전

그 밖의 도구 · Script

새 교시·새 강의 소식 받기

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

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

질문과 답변

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

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

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