Skip to content

Latest commit

ย 

History

History
237 lines (164 loc) ยท 9.35 KB

README.md

File metadata and controls

237 lines (164 loc) ยท 9.35 KB
+ Pynecone์„ ์ฐพ๊ณ  ๊ณ„์‹ ๊ฐ€์š”? ์ด๊ณณ์ด ๋งž์Šต๋‹ˆ๋‹ค. Pynecone์€ ์ด์ œ Reflex๋กœ ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค. +
Reflex Logo Reflex Logo

โœจ ์ˆœ์ˆ˜ Python์œผ๋กœ ๊ณ ์„ฑ๋Šฅ ์‚ฌ์šฉ์ž ์ •์˜ ์›น์•ฑ์„ ๋งŒ๋“ค์–ด ๋ณด์„ธ์š”. ๋ช‡ ์ดˆ๋งŒ์— ๋ฐฐํฌ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. โœจ

PyPI version tests versions Documentaiton Discord


โš™๏ธ ์„ค์น˜

ํ„ฐ๋ฏธ๋„์„ ์—ด๊ณ  ์‹คํ–‰ํ•˜์„ธ์š”. (Python 3.8+ ํ•„์š”):

pip install reflex

๐Ÿฅณ ์ฒซ ์•ฑ ๋งŒ๋“ค๊ธฐ

reflex๋ฅผ ์„ค์น˜ํ•˜๋ฉด, reflex ๋ช…๋ น์–ด ๋ผ์ธ ๋„๊ตฌ๋„ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค.

์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์„ค์น˜๊ฐ€ ์„ฑ๊ณต์ ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. (my_app_name์„ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.):

mkdir my_app_name
cd my_app_name
reflex init

์ด ๋ช…๋ น์–ด๋Š” ์ƒˆ ๋””๋ ‰ํ† ๋ฆฌ์— ํ…œํ”Œ๋ฆฟ ์•ฑ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ ์ด ์•ฑ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

reflex run

http://localhost:3000 ์—์„œ ์•ฑ์ด ์‹คํ–‰ ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ my_app_name/my_app_name.py์—์„œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Reflex๋Š” ๋น ๋ฅธ ์ƒˆ๋กœ๊ณ ์นจ์„ ์ง€์›ํ•˜๋ฏ€๋กœ ์ฝ”๋“œ๋ฅผ ์ €์žฅํ•  ๋•Œ๋งˆ๋‹ค ์ฆ‰์‹œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿซง ์˜ˆ์‹œ ์•ฑ

์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค: DALLยทE๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ UI๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด OpenAI API๋ฅผ ํ˜ธ์ถœํ–ˆ์ง€๋งŒ, ์ด๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰๋˜๋Š” ML ๋ชจ๋ธ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ย 

A frontend wrapper for DALLยทE, shown in the process of generating an image.

ย 

์ด๊ฒƒ์ด ์™„์„ฑ๋œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด ๋ชจ๋“  ๊ฒƒ์€ ํ•˜๋‚˜์˜ Python ํŒŒ์ผ์—์„œ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค!

import reflex as rx
import openai

openai.api_key = "YOUR_API_KEY"

class State(rx.State):
    """The app state."""
    prompt = ""
    image_url = ""
    processing = False
    complete = False

    def get_image(self):
        """Get the image from the prompt."""
        if self.prompt == "":
            return rx.window_alert("Prompt Empty")

        self.processing, self.complete = True, False
        yield
        response = openai.Image.create(prompt=self.prompt, n=1, size="1024x1024")
        self.image_url = response["data"][0]["url"]
        self.processing, self.complete = False, True
        

def index():
    return rx.center(
        rx.vstack(
            rx.heading("DALLยทE"),
            rx.input(placeholder="Enter a prompt", on_blur=State.set_prompt),
            rx.button(
                "Generate Image",
                on_click=State.get_image,
                is_loading=State.processing,
                width="100%",
            ),
            rx.cond(
                State.complete,
                     rx.image(
                         src=State.image_url,
                         height="25em",
                         width="25em",
                    )
            ),
            padding="2em",
            shadow="lg",
            border_radius="lg",
        ),
        width="100%",
        height="100vh",
    )

# Add state and page to the app.
app = rx.App()
app.add_page(index, title="reflex:DALLยทE")

ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Reflex UI

UI๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค.

def index():
    return rx.center(
        ...
    )

index ํ•จ์ˆ˜๋Š” ์•ฑ์˜ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

center, vstack, input, button๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋“ค์€ ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์„œ๋กœ ์ค‘์ฒฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ‚ค์›Œ๋“œ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CSS์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Reflex๋Š” ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ 60๊ฐœ ์ด์ƒ์˜ ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ž์‹ ๋งŒ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋„ ์‰ฝ์Šต๋‹ˆ๋‹ค.

State

Reflex๋Š” UI๋ฅผ State ํ•จ์ˆ˜๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.

class State(rx.State):
    """The app state."""
    prompt = ""
    image_url = ""
    processing = False
    complete = False

state๋Š” ์•ฑ์—์„œ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ณ€์ˆ˜(vars๋กœ ๋ถˆ๋ฆผ)์™€ ์ด๋Ÿฌํ•œ ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ state๋Š” prompt์™€ image_url๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ processing๊ณผ complete๋ผ๋Š” ๋ถˆ๋ฆฌ์–ธ ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ’๋“ค์€ ์›ํ˜• ์ง„ํ–‰๋ฅ ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ํ‘œ์‹œํ•  ๋•Œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Event Handlers

def get_image(self):
    """Get the image from the prompt."""
    if self.prompt == "":
        return rx.window_alert("Prompt Empty")

    self.processing, self.complete = True, False
    yield
    response = openai.Image.create(prompt=self.prompt, n=1, size="1024x1024")
    self.image_url = response["data"][0]["url"]
    self.processing, self.complete = False, True

State ๋‚ด์—์„œ, state vars๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” Reflex์—์„œ state๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ ํ…์ŠคํŠธ ์ƒ์ž์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ์ž ๋™์ž‘์— ์‘๋‹ตํ•˜์—ฌ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋™์ž‘์„ ์ด๋ฒคํŠธ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

DALLยทE. ์•ฑ์—๋Š” OpenAI API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” get_image ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ค‘๊ฐ„์— yield๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด UI๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด UI๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋์—์„œ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

Routing

๋งˆ์ง€๋ง‰์œผ๋กœ, ์•ฑ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

app = rx.App()

์•ฑ์˜ ๋ฃจํŠธ์—์„œ index ์ปดํฌ๋„ŒํŠธ๋กœ ํŽ˜์ด์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํŽ˜์ด์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ/๋ธŒ๋ผ์šฐ์ € ํƒญ์— ํ‘œ์‹œ๋  ์ œ๋ชฉ๋„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

app.add_page(index, title="DALL-E")

์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฉ€ํ‹ฐ ํŽ˜์ด์ง€ ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“‘ ์ž๋ฃŒ

๐Ÿ“‘ ๋ฌธ์„œ ย  | ย  ๐Ÿ—ž๏ธ ๋ธ”๋กœ๊ทธ ย  | ย  ๐Ÿ“ฑ ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ย  | ย  ๐Ÿ–ผ๏ธ ๊ฐค๋Ÿฌ๋ฆฌ ย  | ย  ๐Ÿ›ธ ๋ฐฐํฌ ย 

โœ… ์ƒํƒœ

Reflex๋Š” 2022๋…„ 12์›” Pynecone์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

2023๋…„ 7์›” ํ˜„์žฌ, ์šฐ๋ฆฌ๋Š” Public Beta ์ƒํƒœ์— ์žˆ์Šต๋‹ˆ๋‹ค.

  • โœ… Public Alpha: ๋ˆ„๊ตฌ๋‚˜ Reflex๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์ ๊ทน์ ์œผ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ”ถ Public Beta: ๋น„์ƒ์—…์  ์šฉ๋„๋กœ ์ถฉ๋ถ„ํžˆ ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค.
  • Public Hosting Beta: ์„ ํƒ์ ์œผ๋กœ, Reflex์—์„œ ์•ฑ์„ ๋ฐฐํฌํ•˜๊ณ  ํ˜ธ์ŠคํŒ…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!
  • Public : Reflex๋Š” ํ”„๋กœ๋•์…˜ ์ค€๋น„๋ฅผ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค.

Reflex๋Š” ๋งค์ฃผ ์ƒˆ๋กœ์šด ๋ฆด๋ฆฌ์ฆˆ์™€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค! ์ตœ์‹  ์ •๋ณด๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด โญ Star์™€ ๐Ÿ‘€ Watch๋ฅผ ๋ˆŒ๋Ÿฌ ์ด ์ €์žฅ์†Œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ์—ฌ

์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ๊ธฐ์—ฌ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ์•„๋ž˜๋Š” Reflex ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ฐธ์—ฌํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • Discord ์ฐธ์—ฌ: ์šฐ๋ฆฌ์˜ Discord๋Š” Reflex ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ๋„์›€์„ ๋ฐ›๊ณ  ๊ธฐ์—ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋…ผ์˜ํ•˜๋Š” ์ตœ๊ณ ์˜ ์žฅ์†Œ์ž…๋‹ˆ๋‹ค.
  • GitHub Discussions: ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ์ด๋‚˜ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฑฐ๋‚˜ ํ•ด๊ฒฐ์ด ํ•„์š”ํ•œ ๊ฒƒ๋“ค์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  • GitHub Issues: ์ด๊ฒƒ์€ ๋ฒ„๊ทธ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ํ›Œ๋ฅญํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ, ๊ธฐ์กด์˜ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  PR์„ ์ œ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋Šฅ๋ ฅ์ด๋‚˜ ๊ฒฝํ—˜์— ์ƒ๊ด€์—†์ด ์ ๊ทน์ ์œผ๋กœ ๊ธฐ์—ฌ์ž๋ฅผ ์ฐพ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

License

Reflex๋Š” ์˜คํ”ˆ์†Œ์Šค์ด๋ฉฐ Apache License 2.0๋กœ ๋ผ์ด์„ ์Šค๊ฐ€ ๋ถ€์—ฌ๋ฉ๋‹ˆ๋‹ค.