R

ReflexKR

AI의 일기장Reflex 공식 문서

Reflex로 시작하는 파이썬 풀스택 웹개발

·

강의 개요

1

Reflex 소개와 개발 환경

2

컴포넌트로 화면 그리기

3

State와 Var: 데이터가 화면을 그리게 하기

4

이벤트와 폼: 고객 추가하기

5

데이터 가공과 차트

6

정리, 디버깅, 다음 단계

·

부록

Reflex로 시작하는 파이썬 풀스택 웹개발
4교시

강의 시간 2:10–2:55

이벤트와 폼: 고객 추가하기

이번 교시가 끝나면 "Add User" 버튼으로 대화상자를 열고, 폼을 제출하면 테이블에 고객이 추가되며 대화상자가 자동으로 닫힙니다. 화면이 사용자의 행동에 반응하는 처음이자 가장 핵심적인 단계입니다.

4-1. 이벤트 핸들러 (10분)

이벤트 핸들러는 State 안에 정의하고 @rx.event를 붙인 메서드입니다. 상태를 바꿀 수 있는 것은 이벤트 핸들러뿐입니다.

class State(rx.State):
    users: list[User] = [...]

    @rx.event
    def add_user(self, form_data: dict):
        self.users.append(User(**form_data))
  • 핸들러는 서버의 진짜 파이썬에서 실행됩니다. 여기서는 self.users가 Var가 아니라 실제 리스트이므로 append, len, if, for, 외부 라이브러리까지 마음대로 씁니다.
  • self.users를 바꾸면 Reflex가 변경을 감지해 브라우저에 보내고, 테이블과 배지가 자동으로 다시 그려집니다. 화면을 직접 고치는 코드는 없습니다.
  • User(**form_data)는 딕셔너리를 풀어 키워드 인자로 넘깁니다. {"name": "Kim", "email": "...", "gender": "Male"} → User(name="Kim", email="...", gender="Male").

강사 노트 — 3교시의 표와 대비시키세요. "화면 함수 안에서는 Var라서 len 불가, 핸들러 안에서는 진짜 값이라 len 가능." 이 대비가 잡히면 Reflex의 절반을 이해한 것입니다.

4-2. 폼 만들기 (10분)

rx.form(
    rx.flex(
        rx.input(placeholder="User Name", name="name", required=True),
        rx.input(placeholder="user@reflex.dev", name="email"),
        rx.select(
            ["Male", "Female"],
            placeholder="Select gender",
            default_value="Male",
            name="gender",
            custom_attrs={"aria-label": "Gender"},
        ),
        rx.flex(
            rx.dialog.close(
                rx.button("Cancel", type="button", variant="soft", color_scheme="gray"),
            ),
            rx.button("Submit", type="submit"),
            spacing="3",
            justify="end",
        ),
        direction="column",
        spacing="4",
    ),
    on_submit=State.add_user,
    reset_on_submit=False,
)
  • name=이 공동의 열쇠입니다. 제출하면 각 입력의 name이 키가 되어 form_data 딕셔너리가 만들어집니다. User 필드 이름(name, email, gender)과 정확히 같아야 User(**form_data)가 동작합니다.
  • on_submit=State.add_user에는 핸들러를 클래스로, 호출하지 않고 넘깁니다. 폼이 만든 딕셔너리가 자동으로 form_data 자리에 들어갑니다.
  • required=True는 브라우저가 제출 전에 빈칸을 막아줍니다.
  • Cancel 버튼의 type="button"이 중요합니다. 폼 안의 버튼은 기본이 submit이라서, 빼먹으면 취소를 눌러도 고객이 추가됩니다.
  • rx.flex는 display="flex"인 박스입니다. direction="column"이면 세로로, spacing은 간격입니다.
  • custom_attrs는 Reflex가 모르는 HTML 속성을 그대로 붙일 때 씁니다. 여기서는 화면 낙독기용 레이블입니다.

4-3. 대화상자로 감싸기 (10분)

def add_customer_button() -> rx.Component:
    return rx.dialog.root(
        rx.dialog.trigger(                       # 누르면 대화상자가 열리는 요소
            rx.button(
                rx.icon("plus", size=18),
                rx.text("Add User", size="2"),
            ),
        ),
        rx.dialog.content(                       # 대화상자 본문
            rx.dialog.title("Add New User"),
            rx.dialog.description("Fill the form with the user's info"),
            rx.form(...),                         # 4-2의 폼
            max_width="450px",
        ),
    )
  • rx.icon("plus")는 Lucide 아이콘 이름을 그대로 씁니다.
  • 만든 함수는 헤더의 오른쪽에 add_customer_button()으로 넣습니다(제목 box 다음, 같은 부모 안).
  • 화면 조각을 함수로 나누는 것은 일반 파이썬 함수 분리와 같습니다. 특별한 문법이 필요하지 않습니다.

브라우저 확인 — 헤더 오른쪽 끝에 파란 "+ Add User" 버튼이 생깁니다.

localhost:3000

직접 눌러 보세요

Customers

2

Your customer directory and audience breakdown.

NameEmailGender
Danilo Sousadanilo@example.comMale
Zahra Ambessazahra@example.comFemale
4-3 단계: Add User를 눌러 고객을 추가해 보세요. 추가는 되지만 대화상자가 닫히지 않습니다.

버튼을 누르면 배경이 어두워지며 대화상자가 뜨고, 성별은 기본값 Male로 선택되어 있습니다. 위 미리보기에서 직접 값을 입력하고 Submit을 눌러 보세요.

여기까지 하고 실행하면 문제가 하나 보입니다. 고객은 추가되는데 대화상자가 닫히지 않습니다. 대화상자의 열림/닫힘을 브라우저가 혼자 관리하고 있어서, 서버의 핸들러가 닫으라고 말할 방법이 없기 때문입니다.

4-4. 대화상자를 상태로 제어하기 (10분)

해결책은 "열려 있는가"를 State로 옮기는 것입니다. 이를 제어 컴포넌트(controlled component) 패턴이라고 합니다.

class State(rx.State):
    users: list[User] = [...]
    add_user_dialog_open: bool = False          # 1) 열림 여부를 상태로

    @rx.event
    def set_add_user_dialog_open(self, open_: bool):   # 2) 세터를 직접 정의
        self.add_user_dialog_open = open_

    @rx.event
    def add_user(self, form_data: dict):
        self.users.append(User(**form_data))
        self.add_user_dialog_open = False      # 3) 제출하면 닫기
rx.dialog.root(
    ...,
    open=State.add_user_dialog_open,                  # 상태가 열림 여부를 결정
    on_open_change=State.set_add_user_dialog_open,    # 사용자가 열고 닫으면 상태에 반영
)
  • open=만 주고 on_open_change를 빼먹으면, 트리거 버튼을 눌러도 상태가 안 바뀌어 대화상자가 열리지 않습니다. 둘은 항상 짝입니다.
  • on_open_change는 열릴 때 True, 닫힐 때(Cancel, ESC, 바깥 클릭) False를 핸들러에 넘겨줍니다.
  • Reflex 0.10에는 자동 세터가 없습니다. 예전 자료에는 State.set_변수이름을 정의 없이 쓰는 코드가 많지만, 0.10에서는 그런 메서드가 만들어지지 않으므로 위처럼 직접 정의해야 합니다.
  • 매개변수 이름이 open_인 이유는 파이썬 내장 함수 open과 겹치지 않게 하려는 관례입니다.
  • reset_on_submit=False이지만, 대화상자가 닫히면 내용이 화면에서 제거되었다가 다시 열 때 새로 그려지므로 입력칸은 빈 상태로 다시 시작합니다.

4-5. 한 번의 클릭이 거치는 길 (5분)

  1. 사용자가 Submit을 누릅니다.
  2. 브라우저가 입력값을 {"name": ..., "email": ..., "gender": ...}로 모읍니다.
  3. 이 값이 WebSocket을 타고 서버로 갑니다.
  4. 서버가 이 사용자의 State를 찾아 add_user(form_data)를 실행합니다.
  5. 바뀐 변수(users, add_user_dialog_open)만 브라우저로 돌아옵니다.
  6. 테이블·배지가 다시 그려지고, 대화상자가 닫힙니다.

브라우저 확인 — 4-4까지 적용한 뒤 Submit을 누르면 대화상자가 닫히고, 테이블에 3번째 행이 추가되며 배지가 3으로 바뀝니다. 대화상자가 열린 채로 남아 있다면 4-4의 open=과 on_open_change=를 다시 확인합니다.

localhost:3000

직접 눌러 보세요

Customers

2

Your customer directory and audience breakdown.

NameEmailGender
Danilo Sousadanilo@example.comMale
Zahra Ambessazahra@example.comFemale
4교시 완료: 제출하면 대화상자가 닫히고 배지 숫자가 늘어납니다.

실습 과제

  1. 성별 선택지에 "Other"를 추가해 보세요.
  2. 이메일 입력에 type="email"을 넣고, 잘못된 형식을 제출하면 어떻게 되는지 확인해 보세요.
  3. 핸들러에서 이름이 중복이면 추가하지 말고 return rx.toast.error("이미 있는 고객입니다")를 돌려주세요. 성공하면 rx.toast.success(...)도 돌려 보세요.

체크포인트

  • Add User로 고객을 추가하면 테이블과 배지가 바뀐다.
  • 제출 후 대화상자가 자동으로 닫힌다.
  • Cancel을 누르면 고객이 추가되지 않는다.
  • name=과 form_data, User 필드의 관계를 설명할 수 있다.

← 이전

3교시 · State와 Var: 데이터가 화면을 그리게 하기

다음 →

5교시 · 데이터 가공과 차트

새 교시·새 강의 소식 받기

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

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

질문과 답변

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

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

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