Feliz: Feliz์™€ ๋‚˜์˜ ์—ฌํ–‰ | Fable.React์™€ Feliz์˜ ๋น„๊ต

์— ๋งŒ๋“  2020๋…„ 04์›” 07์ผ  ยท  23์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: Zaid-Ajaj/Feliz

์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ,

์ด ๋ฌธ์ œ๋Š” Feliz ๋ฐ Feliz.Bulma์™€์˜ ๊ฒฝํ—˜์„ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ํ”ผ๋“œ๋ฐฑ์€ Fable Repl์„ Fable.React + Fulma ์—์„œ Feliz + Feliz.Bulma ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ์„น์…˜์—์„œ๋Š” Feliz๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ข‹์•„ํ•˜๊ฑฐ๋‚˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋˜ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” Fable.React์™€ Fulma์— ๋Œ€ํ•ด ๋น„์Šทํ•œ ์ผ์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๋“ค์€ ์ข‹์€ ๋ถ€๋ถ„๊ณผ ๋œ ์ข‹์€ ๋ถ€๋ถ„์—์„œ ๋ฉด์ œ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‘ ๋ฒˆ์งธ ์‹œ๊ฐ„์— Feliz์™€ Feliz.Bulma์™€์˜ ๊ฒฝํ—˜์ด ์–ด๋• ๋Š”์ง€ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ชฉํ‘œ๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋‚ด ๊ด€์ ์ด ์–ด๋–ป๊ฒŒ ๋ณ€ํ–ˆ๊ณ  ๊ทธ ์ด์œ ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ค‘์š”ํ•œ

๋‚˜๋Š” ๋‚ด๊ฐ€ ๋ถ„์„ํ•˜๋Š” ์ฃผ์ œ๊ฐ€ ๋ฏผ๊ฐํ•˜๊ณ  ๋‚˜๋ฅผ ๋‘๋ ต๊ฒŒ ํ•˜๋Š” ๋Œ€์ƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜๊ฒฌ ์„น์…˜์„ ๊ธ์ •์ ์œผ๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋‚ด์šฉ์˜ ํ…Œ์ด๋ธ”

๋ถ„๋ฅ˜ ์‹œ์Šคํ…œ

ํ”ผ๋“œ๋ฐฑ์„ ์ •๋ฆฌํ•  ๋ฐฉ๋ฒ•์„ ์ฐพ์œผ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ๊ณ ์ „์ ์ธ "์žฅ๋‹จ์ "์ด ๋„ˆ๋ฌด ์ œํ•œ์ ์ด๊ณ  ๊ณต๊ฒฉ์ ์œผ๋กœ ๋Š๊ปด์กŒ์Šต๋‹ˆ๋‹ค.

๋Œ€์‹  ๊ธฐํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  • โœ”๏ธ ๋‚ด๊ฐ€ ์ข‹์•„ํ•˜๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค
  • โš ๏ธ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ๋‚ด๊ฐ€ ์ต์ˆ™ํ•œ ๊ฒƒ๊ณผ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ ๊ฒƒ

    • ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

    • ์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ์˜ํ•ด์•ผ ํ•˜๋Š” ์˜์—ญ

  • โ„น๋Š” ์ฒ˜์Œ์—๋Š” ๋ช…ํ™•ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ Feliz๋กœ ์ธํ•ด ๋ฌธ์ œ์™€ ์ง์ ‘์ ์ธ ๊ด€๋ จ์ด ์—†๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

๋™์ผํ•œ ํ•ญ๋ชฉ์— ์—ฌ๋Ÿฌ ๊ธฐํ˜ธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

ํŽ ๋ฆฌ์ฆˆ


โœ”๏ธ Feliz๋Š” Fable.React ์œ„์— Feliz๊ฐ€ ๋ ˆ์ด์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— Feliz์™€ Fable.React๋ฅผ ํ˜ผํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โœ”๏ธ ์ฝ”๋“œ๋Š” Fable.React์— ๋น„ํ•ด ๋“ค์—ฌ์“ฐ๊ธฐ๊ฐ€ ๋” ํŽธํ•ฉ๋‹ˆ๋‹ค. ๋ชฉ๋ก์ด ํ•˜๋‚˜๋งŒ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ‘œ๋ฅผ ๋”ฐ๋ผ๊ฐ€์‹œ๋ฉด ์‰ฝ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

**Fable.React + ํ’€๋งˆ**

div [ ]
    [ Hero.hero [ Hero.IsFullHeight ]
        [ Hero.body [ ]
            [ Container.container [ ]
                [ img [ Src "img/fable-ionide.png"
                        Style [ Display DisplayOptions.Block
                                Width "auto"
                                Margin "auto" ] ]
                  br [ ]
                  Heading.h3 [ Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
                    [ str "Fable REPL" ]
                  Heading.p [ Heading.IsSubtitle
                              Heading.Is5
                              Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
                    [ str "is only available on desktop" ] ] ] ] ]
**Feliz + Feliz.Bulma**
Html.div [
    Bulma.hero [
        hero.isFullHeight
        prop.children [
            Bulma.heroBody [
                Bulma.container [
                    Html.img [
                        prop.src "img/fable-ionide.png"
                        prop.style [
                            style.display.block
                            style.width length.auto
                            style.margin length.auto
                        ]
                    ]

                    Html.br [ ]

                    Bulma.title3 [
                        text.hasTextCentered
                        prop.text "Fable REPL"
                    ]

                    Bulma.subtitle5 [
                        text.hasTextCentered
                        prop.text "is only available on desktop"
                    ]
                ]
            ]
        ]
    ]
]


โœ”๏ธ ์ด์ „ ํฌ์ธํŠธ ๋•๋ถ„์— ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

feliz_move_code_demo


โœ”๏ธ DOM ์†์„ฑ์„ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ํ˜•์‹์˜ API


โœ”๏ธ ICSSUnits ๋ฅผ ํ†ตํ•œ CSS ๋ฐ CSS ๋‹จ์œ„์— ๋Œ€ํ•œ ๊ฐ•๋ ฅํ•œ ํ˜•์‹์˜ API

style.marginLeft (length.em 0.5)
style.width (length.percent (model.PanelSplitRatio * 100.))

โœ”๏ธ ์ปจํ…์ŠคํŠธ๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š์œผ๋ฉฐ ๋Œ€๋ถ€๋ถ„์˜ ํ•ญ๋ชฉ์ด Html.* ๋˜๋Š” Prop.* ๋ฏธ๋งŒ์ž…๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ Feliz๋Š” ์ฝ”๋“œ์—์„œ ๋…ธ์ด์ฆˆ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

Html.span "Fable REPL"

// instead of

// Feliz verbose version
span [
    str "Fable REPL"
]

// Fable.React

span [ ]
    [ str "Fable REPL" ]

// --------------------

Bulma.title4 "Fable REPL"

// instead of

// Feliz.Bulma verbose version
Bulma.title4 [
    prop.text "Fable REPL"
]

// Fable.React
Headings.h4 [ ]
    [ str "Fable REPL" ]
๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ "์ฝ์„ ์ˆ˜" ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋•Œ๋•Œ๋กœ ์™ธ๋ถ€ ๋งฅ๋ฝ์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ํ•œ ๋ฐœ ๋ฌผ๋Ÿฌ์„œ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋˜ํ•œ ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ๋œ "์ผ๊ด€๋˜๊ฒŒ" ๋งŒ๋“ญ๋‹ˆ๋‹ค.
Html.tr [
    Html.th "Steps"
    Html.th [
        prop.className "has-text-right"
        prop.text "ms"
    ]
]


โš ๏ธ (์•„์ง) SSR์€ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค


โš ๏ธ ๋ฉ”์†Œ๋“œ/์†์„ฑ ๊ณผ๋ถ€ํ•˜๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๊ฒƒ์€ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์ด๊ฒƒ์€ ์•„๋งˆ๋„ ๋‚ด๊ฐ€ ๋ชจ๋ฅด๋Š” Ionide์˜ ํ•œ๊ณ„์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค)

๋‚˜๋Š”ํ•ด์•ผํ–ˆ๋‹ค :

  • ์ง์ ‘ ์‹คํ—˜ํ•˜๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด float๊ฐ€ ์žˆ์„ ๋•Œ prop.text 2.0 ๊ฐ€ ์ง€์›๋˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
  • Feliz ์†Œ์Šค ์ฝ”๋“œ ์ง์ ‘ ๋ณด๊ธฐ

โš ๏ธ ๋‘˜ ์ด์ƒ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝœ๋ฐฑ ์‚ฌ์šฉ์„ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“œ์„ธ์š”. Fable.REPL์—์„œ๋Š” System.Func<_,_,_> ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ธ์ปค๋ฆฌ๋ฅผ ๊ฐ•์ œ ์‹คํ–‰ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>) = Interop.mkAttr "editorDidMount" f
ํ˜ธ์ถœ ์ธก์—์„œ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
let private registerCompileCommand dispatch =
    System.Func<_,_,_>(
        fun (editor : Monaco.Editor.IStandaloneCodeEditor) (monacoModule : Monaco.IExports) ->
            let triggerCompile () = StartCompile None |> dispatch
            editor.addCommand(monacoModule.KeyMod.Alt ||| int Monaco.KeyCode.Enter, triggerCompile, "") |> ignore
            editor.addCommand(monacoModule.KeyMod.CtrlCmd ||| int Monaco.KeyCode.KEY_S, triggerCompile, "") |> ignore
    )


โœ”๏ธ โš ๏ธ Feliz๋Š” ์‚ฌ์šฉ์ž์˜ ์‚ถ์„ ์กฐ๊ธˆ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๊ณผ๋ถ€ํ•˜๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ๋น„์šฉ์ด ๋“ญ๋‹ˆ๋‹ค.

prop.onChange ๊ฒฝ์šฐ ๋“ฃ๊ณ  ์‹ถ์€ ํ•ญ๋ชฉ์— ๋”ฐ๋ผ 6๊ฐœ์˜ ์˜ค๋ฒ„๋กœ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • static member inline onChange (handler: bool -> unit)
  • static member inline onChange (handler: Event -> unit)
  • static member inline onChange (handler: File -> unit)
  • static member inline onChange (handler: File list -> unit)
  • static member inline onChange (handler: string -> unit)
  • static member inline onCheckedChange (handler: bool -> unit)

์ด๊ฒƒ์€ ์‚ฌ๋žŒ๋“ค์ด "no" ์žฌ๋ฏธ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€๋งŒ ์ƒ๋Œ€๋ฐฉ์€ ๋ช…์‹œ์ ์œผ๋กœ ์›ํ•˜๋Š” ์ด๋ฒคํŠธ ์œ ํ˜•์„ ๋งํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// Feliz
prop.onChange (fun (e : Types.Event) -> e.Value |> ChangeGistToken |> dispatch)

// Fable.React
prop.onChange (fun e -> e.Value |> ChangeGistToken |> dispatch)

โœ”๏ธ โš ๏ธ Feliz ์ƒํƒœ๊ณ„๋Š” ๋Œ€๋ถ€๋ถ„ ์œ ํ˜• ์•ˆ์ „ํ•˜์ง€๋งŒ ์ž˜๋ชป๋œ ์ฝ”๋“œ ์ž‘์„ฑ์„ ๋ฐฉ์ง€ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. Feliz์™€ Feliz.Bulma์™€ ๊ฐ™์€ ํ™•์žฅ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์†์„ฑ์„ ์‰ฝ๊ฒŒ ํ˜ผํ•ฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

์ด ์ฝ”๋“œ๋Š” Feliz์™€ F# ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๊ด€์ ์—์„œ ๋ณผ ๋•Œ ๊ดœ์ฐฎ์€ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์˜ˆ์ƒํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

Html.p [
    text.isUppercase
    text.isItalic
    color.hasTextSuccess
    prop.text "Hello Feliz"
]
`text.isUppsercase`, `text.isItalic` ๋ฐ `color.hasTextSuccess`๋Š” ๋ชจ๋‘ `ClassName "my-css-class`์™€ ๊ฐ™์€ ๊ฒƒ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ React์—์„œ๋Š” ๋งˆ์ง€๋ง‰ ํ•˜๋‚˜๋งŒ ํšจ๊ณผ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ์ฝ”๋“œ๋Š” ์ƒ์„ฑํ•˜๋‹ค:
<p class="has-text-success">Hello Feliz<\p>
๋Œ€์‹ ์—
<p class="is-uppercase is-italic has-text-success>Hello Feliz</p>
์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์€ Feliz.Bulma์—์„œ ์ œ๊ณตํ•˜๋Š” `++`๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
open Feliz.Bulma.Operators

Html.p [
    text.isUppercase
    ++ text.isItalic
    ++ color.hasTextSuccess
    prop.text "Hello Feliz"
]
์ด๊ฒƒ์€ ํ˜ธํ™˜๋˜๋Š” ๊ฒฝ์šฐ "Feliz ์š”์†Œ"์— ๋‹ค๋ฅธ Feliz ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์˜ค๋Š” ๋™์ž‘์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ์ „ํžˆ ์ข‹์€ ๊ฐ€๋Šฅ์„ฑ์ž…๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์€ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์กฐ์‹ฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.


โ„น ์ฒ˜์Œ์—๋Š” Feliz๊ฐ€ Fable.React๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์„คํƒ• ev.Value ๊ตฌ๋ฌธ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Fable.React.Extension ๋Š” ๊ตฌ๋ฌธ ์„คํƒ•์„ ํ˜ธ์ŠคํŒ…ํ•˜๋ฏ€๋กœ ๋ชจ๋“  Fable.React ํ•จ์ˆ˜๋กœ ์ปจํ…์ŠคํŠธ๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๋„๋ก ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

open Feliz
open Fable.React.Extensions

โ„น ๊ตฌ๋ฌธ์€ HTML ์‚ฌ๊ณ  ๋ฐฉ์‹์„ ์žฌํ˜„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Feliz๋Š” HTML๋ณด๋‹ค React API ์œ„์— ์žˆ๋Š” ๊ตฌ๋ฌธ ์„คํƒ•์ž…๋‹ˆ๋‹ค. Feliz๋Š” children ๋„ ์†์„ฑ์ด๊ธฐ ๋•Œ๋ฌธ์— ์†์„ฑ์˜ ๊ด€์ ์—์„œ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.


ํŽ ๋ฆฌ์ฆˆ.๋ถˆ๋งˆ

โœ”๏ธ Feliz์™€ ์ž˜ ํ†ตํ•ฉ๋จ


โœ”๏ธ Fulma์— ๋น„ํ•ด ๋“ค์—ฌ์“ฐ๊ธฐ ์šฉ์ด

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

// Fulma
Card.card [ ]
    [ Card.header [ Common.Props [ OnClick (fun _ -> ToggleWidget title |> dispatch ) ] ]
        [ Card.Header.title [ ]
            [ Icon.icon [ Icon.Props [ Style [ MarginRight ".5em" ] ] ]
                [ Fa.i [ icon; Fa.Size Fa.FaLarge ] [] ]
                str title ]
            Card.Header.icon [ ]
            [ Icon.icon [ ] [ Fa.i [ headerIcon ; Fa.Size Fa.FaLarge ] [] ] ] ]
        ofOption content ]

// Feliz.Bulma
Bulma.card [
    Bulma.cardHeader [
        prop.onClick (fun _ -> ToggleWidget title |> dispatch )
        prop.children [
            Bulma.cardHeaderTitle [
                Bulma.icon [
                    prop.style [
                        style.marginRight (length.em 0.5)
                    ]
                    prop.children [
                        Fa.i [ icon; Fa.Size Fa.FaLarge ] []
                    ]
                ]
                Html.text title
            ]

            Bulma.cardHeaderIcon [
                Bulma.icon [
                    Fa.i [ headerIcon ; Fa.Size Fa.FaLarge ] []
                ]
            ]
        ]
    ]

    Html.ofOption content
]


โœ”๏ธ Bulma ์ ‘๋‘์–ด ๋•๋ถ„์— Bulma ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‰ฝ๊ฒŒ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ ์†์„ฑ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ปจํ…์ŠคํŠธ button.* , help.* , columns.* ์˜ค์—ผ์‹œํ‚ต๋‹ˆ๋‹ค.

์ด๋ก ์ ์œผ๋กœ ์‚ฌ๋žŒ๋“ค์€ ํ•˜๋‚˜์˜ CSS ํ”„๋ ˆ์ž„์›Œํฌ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ button.* , columns.* ๋“ฑ๊ณผ ๊ฐ™์€ ์ผ๋ฐ˜ ์†์„ฑ์— ๋Œ€ํ•ด ์ถฉ๋Œ์ด ์—†์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.


โœ”๏ธ ๊ตฌ์„ฑ ์š”์†Œ ๊ณ„์ธต ๊ตฌ์กฐ๋Š” ํŒŒ์•…ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

Bulma.card > Bulma.cardHeader > Bulma.cardHeaderTitle

์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

Bulma.card [
    Bulma.cardHeader [
        prop.onClick (fun _ -> ToggleWidget title |> dispatch )
        prop.children [
            Bulma.cardHeaderTitle [
                // ...
            ]

            Bulma.cardHeaderIcon [
                // ...
            ]
        ]
    ]
]


โš ๏ธ ๊ทธ๋Ÿฌ๋‚˜ ์ผ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋™์ผํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Bulma.passwordInput ๋Œ€์‹  Bulma.inputPassword

์ด ๊ฒฝ์šฐ ๋™์ผํ•œ "์ ‘๋‘์‚ฌ"๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์œ ํ˜•์˜ ์ž…๋ ฅ์„ ์‰ฝ๊ฒŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ Feliz.Bulma๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌ์„ฑ ์š”์†Œ ๋™์ž‘์„ ์‰ฝ๊ฒŒ ํ˜ผํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

Bulma.button [
    // Properties specific to a button coming from Feliz.Bulma
    button.isOutlined
    // Properties specific to a tooltip coming from Feliz.Bulma
    tooltip.hasTooltipRight
    tooltip.text tooltipText
    // General properties coming from Feliz
    prop.disabled isCompiling
    prop.onClick (fun _ -> dispatch msg)
    prop.children [
        Bulma.icon [
            icon.isLarge
            prop.children faIcon
        ]
    ]
]
๋ณด์‹œ๋‹ค์‹œํ”ผ ๊ธฐ์กด ๊ตฌ์„ฑ ์š”์†Œ์— ์ƒˆ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋ฒ„ํŠผ์— ๋™์ž‘ ํˆดํŒ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๋˜ํ•œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž˜๋ชป๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
Html.select [
    select.isFullwidth
]
๋Œ€์‹ ์—
Bulma.select [
    select.isFullwidth
]
Fulma๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ถ„๋ฆฌ์— ๋Œ€ํ•ด ๋” ์—„๊ฒฉํ•˜๋ฉฐ `CustomClass` ์†Œํ’ˆ์„ ํ†ตํ•ด CSS ํด๋ž˜์Šค๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š” ํ•œ ๋™์ž‘์„ ํ˜ผํ•ฉํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ Feliz.Bulma๋Š” ์ถœ๋ ฅํ•˜๋ ค๋Š” โ€‹โ€‹HTML ์š”์†Œ๋ฅผ ์ œ์–ดํ•˜๋Š” โ€‹โ€‹๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด Bulma.field ๋ฅผ ์ƒ์„ฑํ•˜๋Š” div ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋•Œ๋กœ๋Š” p ์š”์†Œ๊ฐ€ ์ถœ๋ ฅ์œผ๋กœ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ Feliz.Bulma๋Š” Bulma์— ๋Œ€ํ•œ ์ •ํ™•ํ•œ ๋งคํ•‘์ž…๋‹ˆ๋‹ค.

๋‚ ์”ฌํ•ด์„œ ์ข‹๋„ค์š”.

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ์„ ์˜๋ฏธํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

  • Bulma์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ์•Œ๊ณ  ์žˆ๊ฑฐ๋‚˜ Bulma ๋ฌธ์„œ๋ฅผ ์—ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‹œ๋„๋Ÿฌ์šด ์ฝ”๋“œ๋ฅผ ํ”ผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, Bulma.tabs ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์•ˆ๋‚ด๋ฅผ ๋ฐ›์ง€ ์•Š๊ณ  tabs ๋Š” ul ๋‹ค์Œ์— li ์™€ a ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค a .

์ฝ”๋“œ๋ฅผ ๋ณด๋ ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์„ธ์š”

// Feliz + Feliz.Bulma
Bulma.tabs [
    tabs.isCentered
    tabs.isMedium
    tabs.isToggle
    prop.children [
        Html.ul [
            Html.li [
                if (activeTab = CodeTab.FSharp) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.FSharp |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "F#"
                    ]
                ]
            ]

            Html.li [
                if (activeTab = CodeTab.Html) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.Html |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "HTML"
                    ]
                ]
            ]

            Html.li [
                if (activeTab = CodeTab.Css) then
                    prop.className "is-active"
                prop.onClick (fun _ -> SetCodeTab CodeTab.Css |> dispatch)
                prop.children [
                    Html.a [
                        prop.text "CSS"
                    ]
                ]
            ]
        ]
    ]
]
Fulma๋Š” ์‚ฌ์šฉ์ž๊ฐ€ "Bulma ๊ตฌ์„ฑ ์š”์†Œ"์˜ ๊ด€์ ์—์„œ ๋” ๋งŽ์ด ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
// Fable.React + Fulma
    Tabs.tabs [ Tabs.IsCentered
                Tabs.Size Size.IsMedium
                Tabs.IsToggle ]
        [ Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.FSharp)
                     Tabs.Tab.Props [
                        OnClick (fun _ -> SetCodeTab CodeTab.FSharp |> dispatch)
                     ] ]
            [ a [ ] [ str "F#" ] ]
          Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.Html)
                     Tabs.Tab.Props [
                         OnClick (fun _ -> SetCodeTab CodeTab.Html |> dispatch)
                     ] ]
            [ a [ ] [ str "HTML" ] ]
          Tabs.tab [ Tabs.Tab.IsActive (activeTab = CodeTab.Css)
                     Tabs.Tab.Props [
                         OnClick (fun _ -> SetCodeTab CodeTab.Css |> dispatch)
                     ] ]
            [ a [ ] [ str "CSS" ] ] ]

๋ฉ”๋ชจ:

Fulma๋Š” tab ๋ฅผ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  Tabs.Tab.* ํŠน์ • ๋ž˜ํผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Fulma๋Š” ์—ฌ์ „ํžˆ a ์š”์†Œ๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ผ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ฒฝ์šฐ).


โœ”๏ธ โš ๏ธ Feliz.Bulma๋Š” ์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฒˆ๋“ค ํฌ๊ธฐ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์ด ์ž‘์ง€๋งŒ ํ”„๋กœ์ ํŠธ ํฌ๊ธฐ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋” ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค

Fulma๋Š” Bulma ํด๋ž˜์Šค๋ฅผ ๋ชจ๋ธ๋งํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ DU๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. [Common.fs](https://github.com/Fulma/Fulma/blob/2f99474cd6c793776001d07da009f7211be2f30c/src/Fulma/Common.fs)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ์—๋Š” ๊ณ ์œ ํ•œ DU๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” Fulma๊ฐ€ DU๋ฅผ ํด๋ž˜์Šค๋กœ ๋ณ€ํ™˜ํ•˜๋Š” 'parseOptions' ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Feliz.Bulma๋Š” Bulma ํด๋ž˜์Šค ์œ„์— DSL์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ์ถœ๋ ฅํ•จ์œผ๋กœ์จ ๋ณด๋‹ค ์ง์ ‘์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ทจํ•ฉ๋‹ˆ๋‹ค.

// Fulma
Column.column
    [
        Column.Width (Screen.Desktop, Column.IsHalf)
        Column.Width (Screen.Mobile, Column.IsFull)
    ]
    [
        // ...
    ]

// Feliz
Bulma.column [
    column.isHalfDesktop
    column.isFullMobile
    prop.children [
        // ...
    ]
]
ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ณ  ์†์„ฑ ๋ชฉ๋ก์„ ์กฐ์ž‘ํ•œ ๋•๋ถ„์— Feliz.Bulma๋Š” Fulma์— ์ถ”๊ฐ€๋œ ๋ชจ๋“  DU ๋ฐ ์ฝ”๋“œ์— ๋Œ€ํ•œ ํฐ ๋น„์šฉ์ด ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋“  ํด๋ž˜์Šค์— "๊ฐ€์ž…"ํ•˜๋ ค๋ฉด ์—ฌ์ „ํžˆ ์ถ”๊ฐ€ ํŒจ์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ [Feliz.Bulma.ElementBuilders.Helpers ๋ชจ๋“ˆ](https://github.com/Dzoukr/Feliz.Bulma/blob/3ecbba1579d2a26281f24e6a6664b5d9c5222603/src/Feliz.Bulma/ElementBuilder)์„ ํ†ตํ•ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ•จ์ˆ˜๋Š” ์ธ๋ผ์ธ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ์ ํŠธ๊ฐ€ ํด์ˆ˜๋ก ์ฝ”๋“œ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ๋„ ์ปค์ง‘๋‹ˆ๋‹ค.


โš ๏ธ ์ƒˆ๋กœ์šด ์ƒ‰์ƒ ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Feliz.Bulma์—์„œ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ๋Š” button.isWarning , help.isWarning ๋“ฑ๊ณผ ๊ฐ™์€ ์ž์ฒด ์ƒ‰์ƒ ๊ตฌํ˜„์„ ๋ณด์œ ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ƒ‰์ƒ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด button.isMyNewColor , help.isMyNewColor ๋ชจ๋‘๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Fulma์—์„œ๋Š” ๋ชจ๋‘ ๊ฐ™์€ ์ƒ‰์ƒ ์œ ํ˜•์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์„œ ์ฐธ์กฐ


์šฐํ™”.๋ฐ˜์‘

โœ”๏ธ ์†์„ฑ ๋ชฉ๋ก๊ณผ ์ž์‹ ๋ชฉ๋ก์œผ๋กœ ์ƒ๊ฐํ•  ๋•Œ HTML ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.


โš ๏ธ ์ด์ค‘ ๋ชฉ๋ก์„ ๊ตฌ์„ฑํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋“ค์—ฌ์“ฐ๊ธฐ ๊ทœ์น™์„ ์ •ํ•˜๊ธฐ ์–ด๋ ต๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ์„ค๋ช…์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์„ธ์š”

์˜ˆ์‹œ:

// When I want to put an empty div
div [ ] [ ]

// When I want to put a div with a single property
div [ ClassName "button" ] [ ]
// or
div [ ClassName "button" ]
    [ ]

// When I want to put a div with several property
div [ ClassName "button"; Id "my-button" ]
    [ ]
// yes but what if one of my property is not that simple?
div
    [
        ClassName "button"
        OnClick (fun _ ->
            // do something
        )
    ]
    [ ]

// It's also possible to have consistent indentation if you do something like that
div [ ] [
    // Children 1
    // Children 2
]

// But if you have non simple property you still have problems, I don't personally don't find it easy to read
// I am not sure if that's how people would write it because I don't use this format personally
div [
    ClassName "button"
    OnClick (fun _ ->
        // do something
    )
] [
    div [ ] [
        str "Children 1"
    ]
]

// and so on
์†์„ฑ ๋ชฉ๋ก์—์„œ ๋ช‡ ๊ฐ€์ง€ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ๋ช‡ ๊ฐ€์ง€ ๊ฐ€๋Šฅํ•œ ๊ตฌ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ ์ผ๊ด€๋œ ๊ตฌ๋ฌธ์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
div
    [
        // Property 1 ...
        // Property 2 ...
        // Property 3 ...
    ]
    [
        div
            [
                // Property 1 ...
                // Property 2 ...
                // Property 3 ...
            ]
            [
                // Children 1 ...
                // Children 2 ...
                // Children 3 ...
            ]
        div
            [
                // Property 1 ...
                // Property 2 ...
                // Property 3 ...
            ]
            [
                // Children 1 ...
                // Children 2 ...
                // Children 3 ...
            ]
    ]

// So for an empty element
div
    [

    ]
    [

    ]

// Awesome... ๐Ÿ™„
Feliz์™€์˜ ๋น„๊ต๋ฅผ ์œ„ํ•ด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
// Empty div
Html.div [ ]

// Non empty div
Htmldiv [
    // Property 1
    // Property 2
    // Property 3
    prop.children [
        Html.div [
            // Property 1
            // Property 2
            // Property 3
        ]

        Html.div [
            // Property 1
            // Property 2
            // Property 3
            // Complexe property
            OnClick (fun _ ->
                // Do something
            )
        ]
    ]
]
๊ทธ๋ž˜์„œ ์ง€๊ธˆ์€ 2๊ฐœ์˜ ๊ฒฝ์šฐ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ Feliz ์ฝ”๋“œ๋ฅผ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๋‹จ์ผ ๋ฐฉ๋ฒ•์„ ์ •๋ง๋กœ ์›ํ•œ๋‹ค๋ฉด ๋„ˆ๋ฌด ๋งŽ์€ ๋…ธ์ด์ฆˆ ์—†์ด ๋นˆ ๋ฒ„์ „์„ ์—ฌ๋Ÿฌ ์ค„์— ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.


โš ๏ธ ๋Œ€๋ถ€๋ถ„์˜ API๋Š” ์ž…๋ ฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

type HTMLAttr =
    | DefaultChecked of bool
    | DefaultValue of obj
    | Accept of string
    | AcceptCharset of string
    | AccessKey of string
    | Action of string
    | AllowFullScreen of bool
    | AllowTransparency of bool

โœ”๏ธ โš ๏ธ ์ผ๋ถ€ API๊ฐ€ ์ž…๋ ฅ๋˜์—ˆ์ง€๋งŒ ์ „๋ถ€๋Š” ์•„๋‹ˆ๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


โœ”๏ธ SSR์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.


ํ’€๋งˆ

โœ”๏ธ ์œ ํ˜• ์•ˆ์ „ API

Modifier.TextAlignment (Screen.All, TextAlignment.Centered)

Button.button [ Button.Color IsWhite ]
    [ str "White" ]

โœ”๏ธ Fulma API๋Š” ๊ตฌ์„ฑ ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ฉด Intellisense๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โœ”๏ธ Fulma๋Š” ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋„๋ก ๊ฐ•์š”ํ•ฉ๋‹ˆ๋‹ค.


โœ”๏ธ IsCustomColor ๋•๋ถ„์— ์ง€์›๋˜๋Š” ์ƒ‰์ƒ์„ ์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// All you need to add `custom-purple` support to all your components is this line
let isCustomPurple = IsCustomColor "custom-purple"

โœ”๏ธ โš ๏ธ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์˜ˆ์ œ๊ฐ€ ํฌํ•จ๋œ ์ข‹์€ ๋ฌธ์„œ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ์— ๋™์ผํ•œ ์ˆ˜์ค€์˜ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.


โœ”๏ธ โš ๏ธ Fulma๊ฐ€ ๋ฒˆ๋“ค ํฌ๊ธฐ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์€ "์•ˆ์ •์ "์ž…๋‹ˆ๋‹ค. ์ด ์ ์€ Feliz.Bulma ์„น์…˜์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


โš ๏ธ ์‚ฌ๋žŒ๋“ค์€ "ํŠน๋ณ„ ๋„์šฐ๋ฏธ"๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ์ปค์Šคํ…€ ํด๋ž˜์Šค
  • ์†Œํ’ˆ
  • ์ˆ˜์ •์ž

โš ๏ธ Fulma ์ฝ”๋“œ๋Š” ๊ฐ•๋ ฅํ•œ ํ˜•์‹์˜ DSL๋กœ ์ธํ•ด ์ฝ”๋“œ์— ๋…ธ์ด์ฆˆ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋ณด๋ ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค.

// Fulma
Heading.p [ Heading.IsSubtitle
            Heading.Is5
            Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
    [ str "is only available on desktop" ]

// Feliz.Bulma
Bulma.subtitle5 [
    text.hasTextCentered
    prop.text "is only available on desktop"
]


โš ๏ธ Fulma ์ฝ”๋“œ๋Š” Props , Modifiers ๋ฅผ ๊ฒฐํ•ฉํ•  ๋•Œ ์ค‘์ฒฉ๋œ ๋ชฉ๋ก์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํฌ๋งทํ•˜๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


์ €๋Š” 2-3์ฃผ ๋™์•ˆ ์ด ๋ถ„์„์„ ์ง„ํ–‰ํ–ˆ์œผ๋ฉฐ ์—ฌ์ „ํžˆ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹œ๊ฐ„์ด ๋งŽ์ด ๊ฑธ๋ฆฌ๊ณ  ์ƒํ™ฉ์„ ๋” ์ž˜ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ข…๋ฃŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Fable.React์™€ Fulma์— ๋Œ€ํ•ด ํŠนํžˆ ๋‚˜์—ดํ•ด์•ผ ํ•  ์ข‹์€ ์ ์ด๋‚˜ ๋ฌธ์ œ๊ฐ€ ๋” ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๊ฒƒ์ด ์ œ๊ฐ€ ๊ฐ€์žฅ ์–ด๋ ค์šด ๋ถ€๋ถ„์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. 3๋…„ ๋„˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ๋””์ž์ธํ•˜๋‹ค ๋ณด๋‹ˆ ๊ฐ๊ด€์ ์ธ ์‹œ์„ ์„ ๊ฐ–๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋ณ€ํ™˜์ด ์–ด๋ ค์› ์Šต๋‹ˆ๊นŒ?

Fable.React์—์„œ Feliz๋กœ์˜ ๋ณ€ํ™˜์€ ์ถฉ๋ถ„ํžˆ ์‰ฌ์› ์Šต๋‹ˆ๋‹ค. Feliz๋Š” Fable.React์™€ ํ˜ธํ™˜๋˜๋ฏ€๋กœ ํŒŒ์ผ๋ณ„ ํŒŒ์ผ, ๊ตฌ์„ฑ์š”์†Œ๋ณ„ ๊ตฌ์„ฑ์š”์†Œ ๋“ฑ์„ ์ ์ง„์ ์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  open Fable.React ๋ฐ open Fable.React.Props ์ง€์นจ์„ ์ œ๊ฑฐํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๋ณ€ํ™˜ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ข…์†์„ฑ์œผ๋กœ Fulma๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌผ๋ก  ์ผ๋ถ€ ํŒŒ์ผ์„ ์žŠ์–ด๋ฒ„๋ ค ์ปดํŒŒ์ผ๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•œ ๋‹ค์Œ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ ์—์„œ ๋ณ€ํ™˜์„ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ๋ณ‘ํ•ฉ ์š”์ฒญ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Feliz์™€ ๋‚˜์˜ ์—ฌํ–‰

Feliz๋ฅผ ์ฒ˜์Œ ๋ฐœ๊ฒฌํ–ˆ์„ ๋•Œ "HTML/JSX ๋ฐฉ์‹"์„ ๋”ฐ๋ฅด์ง€ ์•Š์•˜๊ณ  ๋‚ด๊ฐ€ ๋งŽ์€ ๋…ธ๋ ฅ์„ ํˆฌ์žํ•œ ๊ฒƒ์— ๋Œ€ํ•œ ๋Œ€์ฒด ํ”„๋กœ์ ํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฉ์–ด์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ํ˜์‹ ์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ๋ฅผ ์„ฑ์žฅ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ทธ ๋•๋ถ„์— ์˜ค๋Š˜๋‚  Elmish๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๊ธฐ์–ตํ•œ ๋˜ ๋‹ค๋ฅธ ์‚ฌ์‹ค์€ JSX๊ฐ€ "์‹ค์ œ React API"๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ React API๋Š” React.createElement ์ž…๋‹ˆ๋‹ค. JSX๋Š” ๊ทธ ์œ„์— ๊ตฌ๋ฌธ ์„คํƒ•์ด๋ฉฐ Fable.React ๋ฐ Feliz๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‚˜๋ฅผ ์œ„ํ•ด ์•ž์œผ๋กœ ๋‚˜์•„๊ฐ€๊ณ  ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค.

Feliz๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด Fable REPL์ธ ์ค‘๊ฐ„ ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. Fable REPL์€ ๋ณต์žกํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์•„๋‹ˆ์ง€๋งŒ Fable๊ณผ React์˜ ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฐ์ • ๋•๋ถ„์— ๋‚ด๊ฐ€ ํ…Œ์ŠคํŠธํ•œ ๊ฒƒ๋“ค์˜ ์ „์ฒด ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ํŽ ๋ฆฌ์ฆˆ
  • ํŽ ๋ฆฌ์ฆˆ.๋ถˆ๋งˆ
  • Feliz์™€ Fable.React๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ž˜ ํ˜ธํ™˜๋˜๋Š”์ง€
  • Fable.React์—์„œ Feliz๋กœ์˜ ์ „ํ™˜์ด ์–ผ๋งˆ๋‚˜ ์‰ฌ์šด์ง€
  • Feliz์—์„œ DOM์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ํ˜ธ์ถœ๊ณผ ํ•จ๊ป˜ ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ ์‚ฌ์šฉ
  • JavaScript Feliz ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑ๋œ ์‚ฌ์†Œํ•œ ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑ ์š”์†Œ์™€ ๋ฐ”์ธ๋”ฉ ๋ฐ ์ƒํ˜ธ ์šด์šฉ์„ฑ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•
  • Fulma์—์„œ Feliz.Bulma๋กœ์˜ ์ „ํ™˜์„ ํ†ตํ•ด Feliz ์ƒํƒœ๊ณ„์—์„œ Fulma์™€ ๋™๋“ฑํ•œ ๊ธฐ๋Šฅ์ด ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ€ํ™˜์„ ์‹œ์ž‘ํ•  ๋•Œ ์ฝ”๋“œ ํ˜•์‹์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์‰ฌ์šด์ง€ ๋งค์šฐ ๋งŒ์กฑํ–ˆ์Šต๋‹ˆ๋‹ค. Feliz ๊ธฐ๋ฐ˜ ๋ทฐ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋ฉด ์ •๋ง ์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์ปค์งˆ์ˆ˜๋ก ์ฝ”๋“œ ํ˜•์‹์ด ๋” ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ค ์ค‘๊ฐ„์— ๋ญ”๊ฐ€ ์งœ์ฆ์ด ๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋Š”๋ฐ ์ฐพ์„ ์ˆ˜๊ฐ€ ์—†์—ˆ์–ด์š”.

Fable REPL์„ Feliz๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์„ ์™„๋ฃŒํ–ˆ๋Š”๋ฐ ์‚ฌ์šฉ ๊ฒฝํ—˜์ด ๋งˆ์Œ์— ๋“œ๋Š”์ง€ ์•„๋‹Œ์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ์„œ์•ผ ๋ถ„์„์„ ๋งˆ๋ฌด๋ฆฌํ•˜๋ ค๊ณ  ํ•˜๊ณ , ๋ฌด์—‡์ด ๋‚˜๋ฅผ ๊ฐ€๋กœ๋ง‰๋Š”์ง€ ์ดํ•ดํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Feliz์™€ ๊ทธ๋‹ค์ง€ ๊ด€๋ จ์ด ์—†์ง€๋งŒ Feliz.Bulma์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” 2.5๋…„ ์ด์ƒ Fulma๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ๊ฐœ์„ ํ•ด ์™”๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. Fulma๋Š” ์ €์˜ ๊ฐ€์žฅ ํฐ ํ”„๋กœ์ ํŠธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ํŠนํžˆ ๋ชจ๋“  ๊ฒƒ์ด ํ˜•์‹ํ™”๋˜๊ณ  ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ด€์ ์—์„œ ์ƒ๊ฐํ•˜๊ฒŒ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค.

Feliz.Bulma๋Š” ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ทจํ•˜๊ณ  Fulma๋ณด๋‹ค ์ Š์Šต๋‹ˆ๋‹ค. ๋งŒ 5๊ฐœ์›”์ž…๋‹ˆ๋‹ค.

๋งํ–ˆ๋‹ค ๋ชจ๋‘์™€ ํ•จ๊ป˜, ๋‚˜๋Š” ๋‚ด ๊ฒฐ๋ก ์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค ์ง€๊ธˆ ์‹œ๊ฐ„์ด ์ƒ๊ฐ :)

๊ฒฐ๋ก 

ํŽ ๋ฆฌ์ฆˆ์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ ์‹ฑ๊ธ€๋ฆฌ์ŠคํŠธ ์Šคํƒ€์ผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‚ด๊ฐ€ ๋งŽ์ด ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ด์ง€๋งŒ ๋‚ด ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ํฌ๋งทํ•  ์ˆ˜ ์žˆ๊ณ  ๋‹ค์Œ ์ค„์„ ์–ด๋””์„œ ์‹œ์ž‘ํ•ด์•ผ ํ•˜๋Š”์ง€ ์ƒ๊ฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด ํฐ ์žฅ์ ์ž…๋‹ˆ๋‹ค.

๋‘˜์งธ, ์œ ํ˜• ์•ˆ์ „ API๋Š” ํ•„์ˆ˜ํ’ˆ์ž…๋‹ˆ๋‹ค. ๋™๋“ฑํ•œ CSS๋ณด๋‹ค ๋” ์žฅํ™ฉํ•˜์ง€๋งŒ ์ข‹์€ ๊ธฐ๋Šฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์œผ๋กœ ๋ฉ‹์ง„ ๊ฒƒ์„ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๊ฒƒ์ด ์ง€์›๋˜๋Š”์ง€ ๋งํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ํ•ญ์ƒ ๊ทธ๊ฒƒ์„ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” PR์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :).

Feliz.Bulma๋Š” Feliz๋ฅผ ํ™•์žฅํ•˜๋Š” ์œ ๋งํ•œ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ์ง€๋งŒ ์•„์ง Fulma์™€ ์ง์ ‘์ ์œผ๋กœ ๋™๋“ฑํ•  ๋งŒํผ ์„ฑ์ˆ™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ @Dzoukr๋กœ ๋” ํƒ๊ตฌํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ด๋ฉฐ Feliz.Bulma์˜ ๋‹จ์ˆœํ•จ๊ณผ Fulma์˜ ์™„์ „ํ•˜์ง€๋งŒ ์žฅํ™ฉํ•œ ๊ฒƒ ์‚ฌ์ด์—์„œ ์ค‘๊ฐ„ ์ง€์ ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

3๋…„ ์ „, 2017๋…„ 3์›” 8์ผ , Tomas, Eugene๊ณผ ์ €๋Š” Fable.Arch๋ฅผ Elmish์™€ ๋ณ‘ํ•ฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ๋กœ์„œ ์˜ค๋Š˜๋‚  ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋†€๋ผ์šด ์ƒํƒœ๊ณ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ํ˜„์žฌ Feliz์™€ Fable.React์™€ ๋น„์Šทํ•œ ์‹œ๋Œ€๋ฅผ ์‚ด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Feliz๊ฐ€ Fable.React๋ณด๋‹ค ๋” ๋งŽ์€ ๊ฒฌ์ธ๋ ฅ์„ ์–ป์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๋‘˜ ๋‹ค ํ•จ๊ป˜ ๊ณต์กดํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๋‘˜ ๋‹ค ์ž˜ ์ž‘๋™ํ•˜๋ฏ€๋กœ ์‚ฌ๋žŒ๋“ค์ด ์„ ํ˜ธํ•˜๋Š” ๊ฒƒ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ ๋ถ„์„์„ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋Œ“๊ธ€ ์„น์…˜์—์„œ ๊ธฐ๊บผ์ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. :)

awesome blog

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š” @MangelMaxime ์ž…๋‹ˆ๋‹ค .

์šฐ์„ , ํ”„๋กœ์ ํŠธ์—์„œ Feliz๋ฅผ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ด‘๋ฒ”์œ„ํ•œ ๋ถ„์„์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ๋“ค์ธ ๊ฒƒ์— ๋Œ€ํ•ด ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋งŽ์€ Fable ์‚ฌ์šฉ์ž๊ฐ€ ๋งค์šฐ ์œ ์šฉํ•˜๊ณ  ์œ ์ตํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ํ”ผ๋“œ๋ฐฑ๊ณผ ๊ฐœ๋ฐฉ์„ฑ์— ์ •๋ง ๊ฐ์‚ฌํ•˜๋ฉฐ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ƒํƒœ๊ณ„์™€ ํ•จ๊ป˜ ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ƒ์˜ ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

์ƒํƒœ๊ณ„์—์„œ ์ผ์ข…์˜ ํŠน๋ณ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— Feliz.Bulma์— ๋Œ€ํ•ด ์ž ์‹œ ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ด๋ฏธ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋‹ต๋ณ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

onChange ๊ณผ๋ถ€ํ•˜

๋ช…์‹œ์ ์œผ๋กœ ์›ํ•˜๋Š” ์ด๋ฒคํŠธ ์œ ํ˜•์„ ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์‹ค, ๋‹น์‹ ์€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฉ”์‹œ์ง€ ์œ ํ˜•์—์„œ ์œ ์ถ”๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

prop.onChange (ChangeGistToken >> dispatch)

ChangeGistToken ์˜ ์œ ํ˜•์— ๋”ฐ๋ผ ์˜ฌ๋ฐ”๋ฅธ ์˜ค๋ฒ„๋กœ๋“œ๊ฐ€ ์œ ์ถ”๋ฉ๋‹ˆ๋‹ค. DU ์ผ€์ด์Šค๊ฐ€ ChangeGistToken of string ์ด๋ฉด string -> unit ์˜ ๊ณผ๋ถ€ํ•˜๊ฐ€ ์„ ํƒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ž…๋ ฅ ์œ ํ˜•์ด file ์ธ ๊ฒฝ์šฐ FileSelected of File ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๊ณผ๋ถ€ํ•˜ File -> unit ๊ฐ€ ์œ ์ถ”๋˜๋Š” ์‹์ž…๋‹ˆ๋‹ค. ev.Value ์—์„œ Fable.React ev.Value ์˜ ๊ตฌ๋ฌธ ์„คํƒ•์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

onChange ๊ฐ€ ์ด๋ฏธ ๋ถ€์šธ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ prop.onCheckedChange๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์ค‘ ์ธ์ˆ˜ ์ฝœ๋ฐฑ

@Shmew ๋Š” ์ด๋ฏธ ์ด ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์—ˆ์œผ๋ฉฐ ์‹ค์ œ๋กœ ์ด๊ฒƒ์ด ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์†์„ฑ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Feliz ํ™•์žฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์†์„ฑ์„ ์„ค์ •ํ•˜๊ธฐ ์ „์— ๋ณ€ํ™˜ ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

type myExtension = 
    static member inline extenstionProperty value = 
        // transform value here
        let transformedValue = doWeirdStuffWith value
        Interop.mkAttr "extenstionProperty" transformedValue

์ด ๋…ผ๋ฆฌ์— ๋”ฐ๋ผ ๋‚ด๋ถ€์ ์œผ๋กœ 'A -> 'B -> unit ๋ฅผ Func<'A, 'B, unit> ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = 
        let transformedFunction = System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>(fun editor exported -> f editor exported)
        Interop.mkAttr "editorDidMount" transformedFunction

์ด๊ฒƒ์€ @Shmew์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋ช…์‹œ์ ์ธ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค(๋ฐ”์ธ๋”ฉ์— ๋Œ€ํ•ด ์•Œ๊ณ  ์žˆ๋“ฏ์ด, ๋ฐ”์ธ๋”ฉ์„ ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค

Feliz ์ƒํƒœ๊ณ„์˜ ์œ ํ˜• ์•ˆ์ „

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ @Shmew : ์ ์ ˆํ•œ Feliz ํ™•์žฅ์€ ์ž˜๋ชป ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ์†์„ฑ ์œ ํ˜•์„ ์ œํ•œํ•˜๋ฉด ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Feliz.Recharts์ฒ˜๋Ÿผ prop ์— ์žˆ๋Š” ๊ธฐ์กด ์†์„ฑ๊ณผ์˜ ์—ญํ˜ธํ™˜์„ฑ์„ ํ—ˆ์šฉํ•˜๋Š” IReactProperty ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์†์„ฑ์„ ๋นŒ๋“œํ•˜๊ฑฐ๋‚˜ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋ณด๋‹ค ์ „๋ฌธํ™”๋œ ์œ ํ˜• ISpecialiedProperty ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด IReactProperty ์—์„œ ์ผ๋ถ€ ์†์„ฑ์˜ ์ด๋ฆ„์„ ๋ณต์ œํ•˜๋Š” ๋™์•ˆ ์†์„ฑ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ:

type felizExtension = 
    static member inline specializedProp (value: string) : ISpecializedProperty = unbox (prop.text value) 

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ Feliz.AntDesign์œผ๋กœ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๊ธฐ ์—์„œ button ์ง„์ž…์ ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ผ๋ถ€ ์†์„ฑ์„ ๋ณต์ œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(๋‚˜์ค‘์— ์†์„ฑ์„ ์—„๊ฒฉํ•œ ์œ ํ˜•์œผ๋กœ ๋” ์ „๋ฌธํ™”ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ฒ˜์Œ์—๋Š” , ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์€ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์œ„์น˜์ž…๋‹ˆ๋‹ค). Feliz.MaterialUI๋Š” ์†์„ฑ์„ ์ „๋ฌธํ™”ํ•˜๊ธฐ๋ณด๋‹ค IReactProperty ๋ฅผ ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•ด ์œ ์‚ฌํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Feliz.Bulma์˜ ๊ฒฝ์šฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

Feliz.Bulma ์‚ฌ๊ฑด

Feliz.Bulma๊ฐ€ ๊ตฌํ˜„๋˜๊ธฐ ์ „์— CSS ์ „์šฉ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์œ„ํ•œ Feliz ํ™•์žฅ์„ ๊ตฌ์ถ• ํ•  ํ•„์š” ๊ฐ€ yield ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ TypedCssClasses ์™€ ๊ฒฐํ•ฉํ•˜๋ฉด CSS ์ „์šฉ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„ ๋ฐ ์œ ์ง€ ๊ด€๋ฆฌ ๋น„์šฉ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ง์ ‘ ๋ฌธ์„œ ์›น์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ•  ํ•„์š” ์—†์ด ํƒ€์ดํ•‘๋œ ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํ”„๋กœ์ ํŠธ ์ž์ฒด์— ๋Œ€ํ•œ ์ด๋ฏธ ์ •๋ง ์ข‹์€ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

๊ทธ๋Ÿฌ๋‚˜ @Dzoukr์ด Feliz.Bulma๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ๋ณธ ํ›„์—๋Š” ์ดํ•ด๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Bulma์˜ ํ•œ ๋ชจ๋“ˆ์—์„œ CSS ๊ตฌ์„ฑ ์š”์†Œ์— ํŠน์ • "์ง„์ž…์ "์„ ๋„์ž…ํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ๋‹จ์ˆœํ™”ํ•˜์—ฌ Bulma.button ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค Bulma.card ๋“ฑ. ์‚ฌ์šฉํ•˜๊ธฐ ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค. ์ˆ˜์ •์ž์— ๊ด€ํ•ด์„œ๋Š” Roman์˜ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋”ฐ๋ฅด๊ธฐ ์‰ฝ๊ณ  ์ž‘์—…ํ•˜๊ธฐ ์‰ฝ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ๋ง์”€ํ•˜์‹  ๊ฒƒ์ฒ˜๋Ÿผ ๋•Œ๋กœ๋Š” ํฅ๋ฏธ๋กœ์šด ๊ฒฐ๊ณผ๋ฅผ ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์‹คํžˆ ๊ทธ ๋ถ€์„œ์—์„œ ๋” ๊ฐœ์„ ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์œผ๋ฉฐ ์–ด์จŒ๋“  Roman์€ Feliz์˜ ๋น„์ „์„ ๋”ฐ๋ผ ๋ฐฐ์šฐ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํฐ ์—ญํ• ์„ ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

Feliz์˜ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ

ํ˜„์žฌ Fable.React ๋ฐ Feliz ์˜ ํฐ ์ฐจ์ด์ ์„ ์ง€์ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐ ๊ด€๋ฆฌ์ž…๋‹ˆ๋‹ค. Feliz ๋ฒ„์ „์€ ๋ชจ๋“  ์ƒํƒœ๊ณ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Femto์™€ ํ˜ธํ™˜๋˜๋ฏ€๋กœ ํ•„์š”ํ•œ npm ํŒจํ‚ค์ง€๋ฅผ ์ˆ˜๋™์œผ๋กœ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ํ˜ธํ™˜๋˜๋Š” ๋ฒ„์ „์„ ์•Œ์•„์•ผ ํ•˜๋Š” ์‚ฌ์šฉ์ž์˜ ๋ถ€๋‹ด์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ชจ๋“  ํŒŒ์ƒ Fable.React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์‹ฌ์ง€์–ด ๊ทธ๋ ‡์ง€ ์•Š์€ Fable.React ์ž์ฒด์™€๋„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

ํ–ฅํ›„ ๊ณ„ํš

  • Feliz ์Šคํƒ€์ผ ํ™•์žฅ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ค€์ˆ˜ํ•ด์•ผ ํ•  ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๋ฌธ์„œ ์ž‘์„ฑ(๋”์šฑ).
  • Feliz ์ž์ฒด๋ฅผ ์œ„ํ•œ Feliz ๋ถ„์„๊ธฐ ๊ตฌ์ถ•

๋ชจ๋“  23 ๋Œ“๊ธ€

ํ›Œ๋ฅญํ•œ ๋ถ„์„์ž…๋‹ˆ๋‹ค.

๊ฐœ์ธ์ ์œผ๋กœ ์ด์ค‘ ๋ชฉ๋ก ํ˜•์‹์ด ๋•Œ๋•Œ๋กœ ๊ณ ํ†ต์Šค๋Ÿฝ๊ธด ํ•˜์ง€๋งŒ Fable.React ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๊ธฐ๋ณธ html์˜ ๊ตฌ์กฐ๋ฅผ ๋” ์ž˜ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋Š๋‚๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ div [.. , Html.div [.... (etc)๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋ณ„๊ฑฐ ์•„๋‹Œ๊ฑฐ ๊ฐ™์ง€๋งŒ ๋‚ด ๋‡Œ๋Š” Fable.React๋ฅผ ๋” ์‰ฝ๊ฒŒ ํŒŒ์‹ฑํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

@MangelMaxime ์ž˜ ์“ฐ์…จ ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ์ง€์ ์„ ๋งŽ์ด

๋‚ด๊ฐ€ ์ˆ˜์ง‘ํ•œ ๊ฒƒ์—์„œ ์ด๊ฒƒ์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ๋Œ€๋ถ€๋ถ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ™•์žฅ๋˜๋Š” ๋ฐฉ์‹๊ณผ ์ตœ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์— ๊ด€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Feliz ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๊ฐœ๋ฐœ์ž๊ฐ€ ๋” ๋งŽ์€ ์ž‘์—…์„ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•˜์ง€๋งŒ ๊ฒฐ๊ณผ API๊ฐ€ ์‚ฌ์šฉํ•˜๊ธฐ์— ๋„ˆ๋ฌด ์ข‹์„ ๋•Œ ๋…ธ๋ ฅํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” Feliz ๋Œ€ํ•ด ์ƒ๋‹นํ•œ ์ˆ˜์˜ ํ™•์žฅ์„ ์ž‘์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ๋งŽ์€ ๋ฌธ์ œ๋ฅผ ์™„ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ํ†ต์ฐฐ๋ ฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ณผ๋ถ€ํ•˜

๋‚˜๋Š” Visual Studio๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์–ด๋–ค ์˜ค๋ฒ„๋กœ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ฐพ๋Š” ๋ฐ ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ด๊ฒƒ์ด ionide์˜ ๋์—์„œ ๊ฐœ์„ ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

prop.text toString ๋ฉค๋ฒ„๊ฐ€ ํ•„์š”ํ•œ ๋‹จ์ผ ์ธ๋ผ์ธ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  ๋ชจ๋“  ์œ ํ˜•์„ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ฝœ๋ฐฑ:

๋Œ€์‹ ์—:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>) = Interop.mkAttr "editorDidMount" f

์“ฐ๋‹ค:

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = Interop.mkAttr "editorDidMount" (Func<_,_,_> f)

๊ทธ๋Ÿฐ ๋‹ค์Œ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์˜ˆ์ƒํ•œ ๋Œ€๋กœ ์†์„ฑ์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž˜๋ชป๋œ ์ฝ”๋“œ ์ž‘์„ฑ

๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ์ผ๋ถ€์— ์žˆ๋Š” ๋งŒํผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด๊ฒƒ์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณธ ์ ์ด ์—†์œผ๋ฉฐ Html ํ™•์žฅํ•  ๋•Œ ์™„์ „ํžˆ ์ ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‚ด ๊ฐ€์žฅ ํฐ Feliz ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ •์˜ํ•œ ์†์„ฑ์— ์ถ”๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Feliz.Plotly ์œ ํšจํ•˜์ง€ ์•Š์€ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์œ ์ผํ•œ ๋‹จ์ ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๊ฝค ๋งŽ์€ ์ž‘์—…์ด ์†Œ์š”๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


ํ™•์žฅํ•˜๋ ค๋ฉด ํด๋ฆญ

[<AutoOpen;EditorBrowsable(EditorBrowsableState.Never)>]
module Types =
  type IPlotProperty = interface end
  type IModeBarButtonsProperty = interface end
  type IAaxisProperty = interface end
  type IAggregateProperty = interface end
  type IAggregationProperty = interface end
  type IAggregationsProperty = interface end
  type IAngularaxisProperty = interface end
  type IAnimationProperty = interface end
  type IAnnotationProperty = interface end
  type IAnnotationsProperty = interface end
  type IAreaProperty = interface end
  type IAspectratioProperty = interface end
  type IAxisProperty = interface end
  type IBarProperty = interface end
  type IBarpolarProperty = interface end
  type IBaxisProperty = interface end
  type IBorderProperty = interface end
  type IBoxProperty = interface end
  type IButtonProperty = interface end
  type ICameraProperty = interface end
  type ICandlestickProperty = interface end
  type ICapsProperty = interface end
  type ICarpetProperty = interface end
  type ICaxisProperty = interface end
  type ICellsProperty = interface end
  type ICenterProperty = interface end
  type IChoroplethProperty = interface end
  type IChoroplethmapboxProperty = interface end
  type ICircleProperty = interface end
  type IColoraxisProperty = interface end
  type IColorbarProperty = interface end
  type IColorscaleProperty = interface end
  type IColorscalesProperty = interface end
  type IConcentrationscalesProperty = interface end
  type IConeProperty = interface end
  type IConfigProperty = interface end
  type IConnectorProperty = interface end
  type IContourProperty = interface end
  type IContourcarpetProperty = interface end
  type IContoursProperty = interface end
  type ICumulativeProperty = interface end
  type ICurrentvalueProperty = interface end
  type IDecreasingProperty = interface end
  type IDeltaProperty = interface end
  type IDensitymapboxProperty = interface end
  type IDiagonalProperty = interface end
  type IDimensionProperty = interface end
  type IDimensionsProperty = interface end
  type IDomainProperty = interface end
  type IEditsProperty = interface end
  type IErrorXProperty = interface end
  type IErrorYProperty = interface end
  type IErrorZProperty = interface end
  type IEyeProperty = interface end
  type IFillProperty = interface end
  type IFilterProperty = interface end
  type IFontProperty = interface end
  type IFrameProperty = interface end
  type IFramesEntryProperty = interface end
  type IFramesProperty = interface end
  type IFunnelProperty = interface end
  type IFunnelareaProperty = interface end
  type IGaugeProperty = interface end
  type IGeoProperty = interface end
  type IGradientProperty = interface end
  type IGridProperty = interface end
  type IGroupbyProperty = interface end
  type IHeaderProperty = interface end
  type IHeatmapProperty = interface end
  type IHeatmapglProperty = interface end
  type IHistogram2dProperty = interface end
  type IHistogram2dcontourProperty = interface end
  type IHistogramProperty = interface end
  type IHoverlabelProperty = interface end
  type IImageProperty = interface end
  type IImagesProperty = interface end
  type IIncreasingProperty = interface end
  type IIndicatorProperty = interface end
  type IInsidetextfontProperty = interface end
  type IIsosurfaceProperty = interface end
  type ILabelfontProperty = interface end
  type ILataxisProperty = interface end
  type ILayerProperty = interface end
  type ILayersProperty = interface end
  type ILayoutProperty = interface end
  type ILeafProperty = interface end
  type ILegendProperty = interface end
  type ILightingProperty = interface end
  type ILightpositionProperty = interface end
  type ILineProperty = interface end
  type ILinkProperty = interface end
  type ILonaxisProperty = interface end
  type IMapboxProperty = interface end
  type IMarginProperty = interface end
  type IMarkerProperty = interface end
  type IMeanlineProperty = interface end
  type IMesh3dProperty = interface end
  type IModebarProperty = interface end
  type INodeProperty = interface end
  type INumberProperty = interface end
  type IOhlcProperty = interface end
  type IOutsidetextfontProperty = interface end
  type IPadProperty = interface end
  type IParcatsProperty = interface end
  type IParcoordsProperty = interface end
  type IPathbarProperty = interface end
  type IPieProperty = interface end
  type IPointcloudProperty = interface end
  type IPolarProperty = interface end
  type IProjectProperty = interface end
  type IProjectionProperty = interface end
  type IRadialaxisProperty = interface end
  type IRangebreakProperty = interface end
  type IRangebreaksProperty = interface end
  type IRangefontProperty = interface end
  type IRangeselectorProperty = interface end
  type IRangesliderProperty = interface end
  type IRotationProperty = interface end
  type ISankeyProperty = interface end
  type IScatter3dProperty = interface end
  type IScatterProperty = interface end
  type IScattercarpetProperty = interface end
  type IScattergeoProperty = interface end
  type IScatterglProperty = interface end
  type IScattermapboxProperty = interface end
  type IScatterpolarProperty = interface end
  type IScatterpolarglProperty = interface end
  type IScatterternaryProperty = interface end
  type ISceneProperty = interface end
  type ISelectedProperty = interface end
  type IShapeProperty = interface end
  type IShapesProperty = interface end
  type ISlicesProperty = interface end
  type ISliderProperty = interface end
  type ISlidersProperty = interface end
  type ISortProperty = interface end
  type ISpaceframeProperty = interface end
  type ISplomProperty = interface end
  type IStartsProperty = interface end
  type IStepProperty = interface end
  type IStepsProperty = interface end
  type IStreamProperty = interface end
  type IStreamtubeProperty = interface end
  type IStyleProperty = interface end
  type IStylesProperty = interface end
  type ISunburstProperty = interface end
  type ISurfaceProperty = interface end
  type ISymbolProperty = interface end
  type ITableProperty = interface end
  type ITernaryProperty = interface end
  type ITextfontProperty = interface end
  type IThresholdProperty = interface end
  type ITickfontProperty = interface end
  type ITickformatstopProperty = interface end
  type ITickformatstopsProperty = interface end
  type ITilingProperty = interface end
  type ITitleProperty = interface end
  type ITotalsProperty = interface end
  type ITracesProperty = interface end
  type ITransformsProperty = interface end
  type ITransitionProperty = interface end
  type ITreemapProperty = interface end
  type IUniformtextProperty = interface end
  type IUnselectedProperty = interface end
  type IUpProperty = interface end
  type IUpdatemenuProperty = interface end
  type IUpdatemenusProperty = interface end
  type IViolinProperty = interface end
  type IVolumeProperty = interface end
  type IWaterfallProperty = interface end
  type IXProperty = interface end
  type IXaxisProperty = interface end
  type IXbinsProperty = interface end
  type IYProperty = interface end
  type IYaxisProperty = interface end
  type IYbinsProperty = interface end
  type IZProperty = interface end
  type IZaxisProperty = interface end
  type IButtonsProperty = inherit IModeBarButtonsProperty
  type IMeasureProperty = interface end
  type ITemplateProperty = interface end

๋น„์Šทํ•œ ๋งฅ๋ฝ์—์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ์—์„œ ์›ํ•˜๋Š” ๊ฒฝ์šฐ ์†์„ฑ์„ "์ž๋™์œผ๋กœ" ๊ฒฐํ•ฉํ•˜๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์‹ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ํŠธ๋ฆญ์€ ํ•ด๋‹น ํ•ญ๋ชฉ์„ IReactProperty ์™€ ๋‹ค๋ฅธ ์œ ํ˜•์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Feliz.Plotly ์—์„œ๋Š” ์ฃผ์–ด์ง„ ๊ตฌ์„ฑ์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์ˆ˜์ •/์ˆ˜์ง‘/๋ณ€๊ฒฝํ•˜์—ฌ ์‹ค์ œ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์ ์ ˆํ•˜๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š” ๋งŽ์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ์กด์žฌํ•˜๋Š” ๊ฒƒ๋“ค์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ค ๋ฉด์—์„œ๋Š” ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

"Feliz" ๋ฐฉ์‹์œผ๋กœ ์ž์‹ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋” ๋‚˜์€ ๋ฌธ์„œ๋กœ ๋‹น์‹ ์ด ๊ฒช์—ˆ๋˜ ๋งŽ์€ ๊ณ ํ†ต์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์•„๋งˆ๋„ @Zaid-Ajaj์™€ ๋‚˜๋Š” ๋ฏธ๋ž˜์— ๊ทธ ์ผ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@l3m ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

๊ฐœ์ธ์ ์œผ๋กœ ์ด์ค‘ ๋ชฉ๋ก ํ˜•์‹์ด ๋•Œ๋•Œ๋กœ ๊ณ ํ†ต์Šค๋Ÿฝ๊ธด ํ•˜์ง€๋งŒ Fable.React ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๊ธฐ๋ณธ html์˜ ๊ตฌ์กฐ๋ฅผ ๋” ์ž˜ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋Š๋‚๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ div [.. , Html.div [.... (etc)๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋ณ„๊ฑฐ ์•„๋‹Œ๊ฑฐ ๊ฐ™์ง€๋งŒ ๋‚ด ๋‡Œ๋Š” Fable.React๋ฅผ ๋” ์‰ฝ๊ฒŒ ํŒŒ์‹ฑํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ๋ฐฉ๋ฒ•์„ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ์‰ฌ์šด ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ œ ๊ฒฝ์šฐ์—๋Š” ์ฝ”๋“œ๋ฅผ HTML๋กœ ๋ณด์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค๊ณ  ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ด ์ „ํ™˜ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

F# 5์™€ "์ •์  ํด๋ž˜์Šค ์—ด๊ธฐ" ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด Feliz๊ฐ€ Html ํด๋ž˜์Šค๋ฅผ ์—ฌ๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ Html.div ๋ฐ div ๊ตฌ๋ฌธ์„ ๋ชจ๋‘ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

์˜๊ฒฌ์„ ์ฃผ์‹  @Shmew ์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ Feliz์— ์—ฌ๋Ÿฌ ์œ ํ˜•์˜ "๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ"๊ฐ€ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์žŠ์–ด๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

"์ž˜๋ชป๋œ ์ฝ”๋“œ ์ž‘์„ฑ"์— ๋Œ€ํ•ด ์˜ˆ, ํ•˜์ง€๋งŒ ์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” Props ์™€ ๊ฐ™์€ ํŠน์ˆ˜ ๋„์šฐ๋ฏธ๋ฅผ ํ†ตํ•ด ํ‘œ์ค€ ์†์„ฑ์„ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” Fulma ์ƒํ™ฉ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด IReactProperty ์œ ํ˜•์„ "ํ™•์žฅ"ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Props . ๊ทธ ์ด์œ ๋Š” Bulma "๊ตฌ์„ฑ ์š”์†Œ"๊ฐ€ DOM ์š”์†Œ์— ๋Œ€ํ•œ ์ „๋ฌธํ™”์— ๋ถˆ๊ณผํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

HTML ์š”์†Œ์— ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ์ฝ”๋“œ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

Bulma.button ๋Š” <div class="button">

"์‹ค์ œ" ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฒฝ์šฐ(์ฃ„์†กํ•˜์ง€๋งŒ ์ด๋ฆ„์„ ๋‹ค๋ฅด๊ฒŒ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค) ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์€ ์‹ค์ œ๋กœ ์˜ฌ๋ฐ”๋ฅธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Fable.ReactLeaflet ์—์„œ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ์˜ ํŠน์ • ์†์„ฑ์„ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋ชจ๋“  ํ‘œ์ค€ HTML ์†์„ฑ์„ ์ง€์›ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

"์ฝœ๋ฐฑ"์— ๋Œ€ํ•œ ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์— ๋Œ€ํ•ด์„œ๋Š” ์ €์—๊ฒŒ ํšจ๊ณผ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Fable์ด ์ „๋‹ฌํ•œ ํ•จ์ˆ˜์—๋Š” ์นด๋ ˆ ์ธ์ˆ˜๊ฐ€ ์žˆ์—ˆ๊ณ  myFunction(arg1)(arg2) ์™€ ๊ฐ™์€ ๊ฒƒ์„ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด System.Func ๋ฅผ ํ†ตํ•ด uncurried ๋ฒ„์ „์„ ๊ฐ•์ œ ์‹คํ–‰ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ž‘๋™ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•ˆ๋‹ค๋ฉด https://github.com/fable-compiler/repl/pull/108/files ์—์„œ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด PR์„ ๋ณด๋‚ด๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” @MangelMaxime ์ž…๋‹ˆ๋‹ค .

์šฐ์„ , ํ”„๋กœ์ ํŠธ์—์„œ Feliz๋ฅผ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ด‘๋ฒ”์œ„ํ•œ ๋ถ„์„์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ๋“ค์ธ ๊ฒƒ์— ๋Œ€ํ•ด ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋งŽ์€ Fable ์‚ฌ์šฉ์ž๊ฐ€ ๋งค์šฐ ์œ ์šฉํ•˜๊ณ  ์œ ์ตํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ํ”ผ๋“œ๋ฐฑ๊ณผ ๊ฐœ๋ฐฉ์„ฑ์— ์ •๋ง ๊ฐ์‚ฌํ•˜๋ฉฐ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ƒํƒœ๊ณ„์™€ ํ•จ๊ป˜ ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ƒ์˜ ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

์ƒํƒœ๊ณ„์—์„œ ์ผ์ข…์˜ ํŠน๋ณ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— Feliz.Bulma์— ๋Œ€ํ•ด ์ž ์‹œ ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ด๋ฏธ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋‹ต๋ณ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

onChange ๊ณผ๋ถ€ํ•˜

๋ช…์‹œ์ ์œผ๋กœ ์›ํ•˜๋Š” ์ด๋ฒคํŠธ ์œ ํ˜•์„ ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์‹ค, ๋‹น์‹ ์€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฉ”์‹œ์ง€ ์œ ํ˜•์—์„œ ์œ ์ถ”๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

prop.onChange (ChangeGistToken >> dispatch)

ChangeGistToken ์˜ ์œ ํ˜•์— ๋”ฐ๋ผ ์˜ฌ๋ฐ”๋ฅธ ์˜ค๋ฒ„๋กœ๋“œ๊ฐ€ ์œ ์ถ”๋ฉ๋‹ˆ๋‹ค. DU ์ผ€์ด์Šค๊ฐ€ ChangeGistToken of string ์ด๋ฉด string -> unit ์˜ ๊ณผ๋ถ€ํ•˜๊ฐ€ ์„ ํƒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ž…๋ ฅ ์œ ํ˜•์ด file ์ธ ๊ฒฝ์šฐ FileSelected of File ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๊ณผ๋ถ€ํ•˜ File -> unit ๊ฐ€ ์œ ์ถ”๋˜๋Š” ์‹์ž…๋‹ˆ๋‹ค. ev.Value ์—์„œ Fable.React ev.Value ์˜ ๊ตฌ๋ฌธ ์„คํƒ•์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

onChange ๊ฐ€ ์ด๋ฏธ ๋ถ€์šธ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ prop.onCheckedChange๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์ค‘ ์ธ์ˆ˜ ์ฝœ๋ฐฑ

@Shmew ๋Š” ์ด๋ฏธ ์ด ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์—ˆ์œผ๋ฉฐ ์‹ค์ œ๋กœ ์ด๊ฒƒ์ด ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์†์„ฑ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Feliz ํ™•์žฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์†์„ฑ์„ ์„ค์ •ํ•˜๊ธฐ ์ „์— ๋ณ€ํ™˜ ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

type myExtension = 
    static member inline extenstionProperty value = 
        // transform value here
        let transformedValue = doWeirdStuffWith value
        Interop.mkAttr "extenstionProperty" transformedValue

์ด ๋…ผ๋ฆฌ์— ๋”ฐ๋ผ ๋‚ด๋ถ€์ ์œผ๋กœ 'A -> 'B -> unit ๋ฅผ Func<'A, 'B, unit> ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[<Erase>]
type editor =
    /// <summary>Triggered when the Editor has been mounted</summary>
    static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = 
        let transformedFunction = System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>(fun editor exported -> f editor exported)
        Interop.mkAttr "editorDidMount" transformedFunction

์ด๊ฒƒ์€ @Shmew์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋ช…์‹œ์ ์ธ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค(๋ฐ”์ธ๋”ฉ์— ๋Œ€ํ•ด ์•Œ๊ณ  ์žˆ๋“ฏ์ด, ๋ฐ”์ธ๋”ฉ์„ ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค

Feliz ์ƒํƒœ๊ณ„์˜ ์œ ํ˜• ์•ˆ์ „

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ @Shmew : ์ ์ ˆํ•œ Feliz ํ™•์žฅ์€ ์ž˜๋ชป ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ์†์„ฑ ์œ ํ˜•์„ ์ œํ•œํ•˜๋ฉด ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Feliz.Recharts์ฒ˜๋Ÿผ prop ์— ์žˆ๋Š” ๊ธฐ์กด ์†์„ฑ๊ณผ์˜ ์—ญํ˜ธํ™˜์„ฑ์„ ํ—ˆ์šฉํ•˜๋Š” IReactProperty ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์†์„ฑ์„ ๋นŒ๋“œํ•˜๊ฑฐ๋‚˜ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋ณด๋‹ค ์ „๋ฌธํ™”๋œ ์œ ํ˜• ISpecialiedProperty ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด IReactProperty ์—์„œ ์ผ๋ถ€ ์†์„ฑ์˜ ์ด๋ฆ„์„ ๋ณต์ œํ•˜๋Š” ๋™์•ˆ ์†์„ฑ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ:

type felizExtension = 
    static member inline specializedProp (value: string) : ISpecializedProperty = unbox (prop.text value) 

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ Feliz.AntDesign์œผ๋กœ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๊ธฐ ์—์„œ button ์ง„์ž…์ ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ผ๋ถ€ ์†์„ฑ์„ ๋ณต์ œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(๋‚˜์ค‘์— ์†์„ฑ์„ ์—„๊ฒฉํ•œ ์œ ํ˜•์œผ๋กœ ๋” ์ „๋ฌธํ™”ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ฒ˜์Œ์—๋Š” , ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์€ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์œ„์น˜์ž…๋‹ˆ๋‹ค). Feliz.MaterialUI๋Š” ์†์„ฑ์„ ์ „๋ฌธํ™”ํ•˜๊ธฐ๋ณด๋‹ค IReactProperty ๋ฅผ ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•ด ์œ ์‚ฌํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Feliz.Bulma์˜ ๊ฒฝ์šฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

Feliz.Bulma ์‚ฌ๊ฑด

Feliz.Bulma๊ฐ€ ๊ตฌํ˜„๋˜๊ธฐ ์ „์— CSS ์ „์šฉ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์œ„ํ•œ Feliz ํ™•์žฅ์„ ๊ตฌ์ถ• ํ•  ํ•„์š” ๊ฐ€ yield ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ TypedCssClasses ์™€ ๊ฒฐํ•ฉํ•˜๋ฉด CSS ์ „์šฉ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„ ๋ฐ ์œ ์ง€ ๊ด€๋ฆฌ ๋น„์šฉ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ง์ ‘ ๋ฌธ์„œ ์›น์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ•  ํ•„์š” ์—†์ด ํƒ€์ดํ•‘๋œ ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํ”„๋กœ์ ํŠธ ์ž์ฒด์— ๋Œ€ํ•œ ์ด๋ฏธ ์ •๋ง ์ข‹์€ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

๊ทธ๋Ÿฌ๋‚˜ @Dzoukr์ด Feliz.Bulma๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ๋ณธ ํ›„์—๋Š” ์ดํ•ด๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Bulma์˜ ํ•œ ๋ชจ๋“ˆ์—์„œ CSS ๊ตฌ์„ฑ ์š”์†Œ์— ํŠน์ • "์ง„์ž…์ "์„ ๋„์ž…ํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ๋‹จ์ˆœํ™”ํ•˜์—ฌ Bulma.button ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค Bulma.card ๋“ฑ. ์‚ฌ์šฉํ•˜๊ธฐ ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค. ์ˆ˜์ •์ž์— ๊ด€ํ•ด์„œ๋Š” Roman์˜ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋”ฐ๋ฅด๊ธฐ ์‰ฝ๊ณ  ์ž‘์—…ํ•˜๊ธฐ ์‰ฝ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ๋ง์”€ํ•˜์‹  ๊ฒƒ์ฒ˜๋Ÿผ ๋•Œ๋กœ๋Š” ํฅ๋ฏธ๋กœ์šด ๊ฒฐ๊ณผ๋ฅผ ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์‹คํžˆ ๊ทธ ๋ถ€์„œ์—์„œ ๋” ๊ฐœ์„ ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์œผ๋ฉฐ ์–ด์จŒ๋“  Roman์€ Feliz์˜ ๋น„์ „์„ ๋”ฐ๋ผ ๋ฐฐ์šฐ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํฐ ์—ญํ• ์„ ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

Feliz์˜ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ

ํ˜„์žฌ Fable.React ๋ฐ Feliz ์˜ ํฐ ์ฐจ์ด์ ์„ ์ง€์ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐ ๊ด€๋ฆฌ์ž…๋‹ˆ๋‹ค. Feliz ๋ฒ„์ „์€ ๋ชจ๋“  ์ƒํƒœ๊ณ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Femto์™€ ํ˜ธํ™˜๋˜๋ฏ€๋กœ ํ•„์š”ํ•œ npm ํŒจํ‚ค์ง€๋ฅผ ์ˆ˜๋™์œผ๋กœ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ํ˜ธํ™˜๋˜๋Š” ๋ฒ„์ „์„ ์•Œ์•„์•ผ ํ•˜๋Š” ์‚ฌ์šฉ์ž์˜ ๋ถ€๋‹ด์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ชจ๋“  ํŒŒ์ƒ Fable.React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์‹ฌ์ง€์–ด ๊ทธ๋ ‡์ง€ ์•Š์€ Fable.React ์ž์ฒด์™€๋„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

ํ–ฅํ›„ ๊ณ„ํš

  • Feliz ์Šคํƒ€์ผ ํ™•์žฅ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ค€์ˆ˜ํ•ด์•ผ ํ•  ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๋ฌธ์„œ ์ž‘์„ฑ(๋”์šฑ).
  • Feliz ์ž์ฒด๋ฅผ ์œ„ํ•œ Feliz ๋ถ„์„๊ธฐ ๊ตฌ์ถ•

์‚ฌ์‹ค, ๋‹น์‹ ์€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฉ”์‹œ์ง€ ์œ ํ˜•์—์„œ ์œ ์ถ”๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

prop.onChange (ChangeGistToken >> dispatch)

์‚ฌ์‹ค, ๋‚˜๋Š”์ด ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค์ค‘ ์ธ์ˆ˜ ์ฝœ๋ฐฑ

@Shmew ๋Š” ์ด๋ฏธ ์ด ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์—ˆ์œผ๋ฉฐ ์‹ค์ œ๋กœ ์ด๊ฒƒ์ด ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์†์„ฑ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. @Shmew์—์„œ ์ œ๊ณตํ•œ ์Šค๋‹ˆํŽซ์˜ ๋์„ ๋ณด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ์„œ๋ช… ์„ ์–ธ์„ ๋ณด์•˜๊ณ  ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

๋‘˜ ๋‹ค ๋งž์Šต๋‹ˆ๋‹ค. ์†์„ฑ์— ๊ฐ’์„ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Feliz.Bulma ์‚ฌ๊ฑด

๋„ค, TypedCssClasses๋Š” ๊ดœ์ฐฎ์ง€๋งŒ Fulma๋‚˜ Feliz.Bulma์™€ ๋น„๊ตํ•˜๋ฉด ๊ฐ™์€ ์ˆ˜์ค€์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. CSS ํŒŒ์ผ์— ๋ชจ๋“  ๊ฒƒ์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ์ ์€ ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ ๋””์Šคํฌ์— ํŒŒ์ผ์ด ํ•ญ์ƒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ TypedCssClasses๊ฐ€ ์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” Roman์ด Feliz์˜ ๋น„์ „์„ ๋”ฐ๋ผ ๋ฐฐ์šฐ๊ธฐ ์‰ฝ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์„ ๋งŒ๋“œ๋Š” ๋ฐ ํฐ ์—ญํ• ์„ ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋™์˜ํ•ฉ๋‹ˆ๋‹ค. Fulma์—์„œ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…์„ ์•ฝ๊ฐ„ ์ถ”๊ฐ€ํ•˜์—ฌ ์œ ์‚ฌํ•˜๊ฒŒ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ–ˆ๋“ฏ์ด ์šฐ๋ฆฌ๋Š” ๋‘˜ ์‚ฌ์ด์˜ ์˜ฌ๋ฐ”๋ฅธ ์ค‘๊ฐ„ ์ง€์ ์„ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์ง€๊ธˆ ์—ผ๋‘์— ๋‘๊ณ  ์žˆ๋Š” ๋ˆ„๋ฝ๋œ ๊ธฐ๋Šฅ์€ ์ถœ๋ ฅ ์š”์†Œ๋ฅผ ์ œ์–ดํ•˜๊ณ  ์ง€์›๋˜๋Š” ์ƒ‰์ƒ ์„ธํŠธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๊ทธ๊ฒƒ์— ๋งŽ์€ ๋ณต์žก์„ฑ์„ ๋”ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@Zaid-Ajaj๋Š” ํ”ผ๋“œ๋ฐฑ๊ณผ ํŠนํžˆ Feliz ์ž ์žฌ๋ ฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์„ ์•Œ๋ ค์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.
๋‚˜๋Š” Feliz ๋ฌธ์„œ๋ฅผ ์‹ค์ œ๋กœ ์ฝ์ง€ ์•Š์•˜์Œ์„ ์ธ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์„ ๋” ์•Œ๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. :)

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค @MangelMaxime - ํ›Œ๋ฅญํ•œ ๋น„๊ต์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ์–ธ๊ธ‰ํ•˜์ง€ ์•Š์€ Fantomas์—๊ฒŒ ๋‹น์‹ ๋„ Fable.React ๋˜๋Š” ํŽ ๋ฆฌ ์ฆˆ์™€ Fantomas ์ž‘์—… ํ•  ๊ธฐํšŒ๊ฐ€ ์žˆ์—ˆ๋‚˜์š” - ์•„๋ฌด ๊ณณ์ด๋‚˜ ์ฝ”๋“œ์˜ ์„œ์‹์„ ์ž๋™ ํ•˜์‹œ๋‚˜์š”?
Fantomas๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Fable ํ”„๋กœ์ ํŠธ ์ค‘ ํ•˜๋‚˜์—์„œ Ionide์—์„œ ํŒŒ์ผ ์ €์žฅ ์‹œ ์ž๋™ ํฌ๋งท์„ ์ผฐ๊ณ  ์ด๊ฒƒ์ด ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ์— ๋„์›€์ด ๋œ๋‹ค๊ณ  ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜๋™ ํฌ๋งท์— ๋Œ€ํ•ด ์ „ํ˜€ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค!
Fantomas๊ฐ€ ์ด์ œ ์ถ”์ง„๋ ฅ์„ ์–ป๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ Fable ๋ฐ React/Feliz์™€ ํ•จ๊ป˜ ๋” ๋„๋ฆฌ ์ฑ„ํƒ๋˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค.

@theimowski ๋‚˜๋Š”

๋Œ€๋ถ€๋ถ„ ๋‚ด ์ฝ”๋“œ๋ฅผ ์—‰๋ง์œผ๋กœ ๋งŒ๋“ค๊ณ  ์ฝ๊ธฐ๊ฐ€ ์ •๋ง ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚ด ๊ฒฝํ—˜์— ๋”ฐ๋ฅด๋ฉด Fable.React ๋ฐ Feliz์™€ ์ž˜ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ Fantomas์˜ ๊ธฐ๋ณธ๊ฐ’์ด ๋งˆ์Œ์— ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๋‚ด ์ฝ”๋“œ๋ฅผ ๋“ค์—ฌ์“ฐ๊ธฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜๋ฉด ๋‹ค์‹œ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ repo๋ฅผ ์‚ดํŽด๋ณธ ๊ฒฐ๊ณผ Fantomas๊ฐ€ ์ปจํ…์ŠคํŠธ ํฌ๊ธฐ์— ๋”ฐ๋ผ ์ฝ”๋“œ๋ฅผ ํ˜•์‹ํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋ถ€๋ถ„ ๋‚ด ์š”๊ตฌ์— ๋งž์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด

type Test =
    {
        Prop1 : string
    }

ํ•œ ์ค„์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์œ ๋งŒ์œผ๋กœ type Test = { Prop1 : string } ๊ฐ€ ๋˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ธฐ๋ก์ด ์ข‹์€ ์˜ˆ๊ฐ€ ์•„๋‹ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ œ ๋ง์˜ ์˜๋ฏธ๋ฅผ ์ดํ•ดํ•ด์ฃผ์…จ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค. :)

@theimowski ๋ฐ˜๊ฐ‘ ์Šต๋‹ˆ๋‹ค. ์ €๋Š” Fable์ด ์•„๋‹Œ ํ”„๋กœ์ ํŠธ์—์„œ ํŒฌํ† ๋งˆ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๋ฒˆ ๋” ํ•ด๋ณด๊ณ  ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ๋ด์•ผ๊ฒ ๋„ค์š”.

@MangelMaxime ์ž‘์—…์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์–ด์ฉ์ง€ ๋‹น์‹ ์˜ Fulma ํ”„๋กœ์ ํŠธ๊ฐ€ ์šฐํ™”์˜ ์„ธ๊ณ„๋กœ ๋‚˜๋ฅผ ๊ณต๊ฒฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜๋Š” ์—ฌํ–‰์„ ์ •๋ง ์ฆ๊ธด๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ ๋‹จ์ผ ๋ชฉ๋ก์„ ์ข‹์•„ํ•˜์ง€๋งŒ ํด๋ž˜์Šค ๋ฉค๋ฒ„ ์žฌ์ •์˜๋ณด๋‹ค DU๋„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋กœ๋Š” DU๊ฐ€ ์žฌ์ •์˜๋ฅผ ์ง€์›ํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ •๋ง ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๋ฏธ Fable.React๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋‹จ์ผ ๋ชฉ๋ก์„ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— > ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

let myView =
    div </> [
        Classes [ ""; ""; "" ]
        Children [
              ...
        ]
    ]

์—ฐ์‚ฐ์ž๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ณ  DU๋„ ํ™œ์šฉ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋Œ€๋žต์ ์ธ ๊ตฌํ˜„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Zaid-Ajaj ํ›Œ๋ฅญํ•œ ์ž‘์—…์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ผ๋ถ€ ํ”„๋กœ์ ํŠธ๋ฅผ Feliz ๋ฐ Feliz.Material๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”!

์•ˆ๋…•ํ•˜์„ธ์š” @MangelMaxime ์ž…๋‹ˆ๋‹ค .
์šฐ์„  ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์†”์งํžˆ ๋‚˜๋Š” ๋‚ด๊ฐ€ ์ฃผ๋กœ dogfooding ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค๊ณ  ๋ถ€์ž‘์šฉ์œผ๋กœ ๊ฒŒ์‹œํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๊นŠ์ด ๋“ค์—ฌ๋‹ค๋ณผ ๊ฒƒ์ด๋ผ๊ณ  ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. "์ด๋ด, @Zaid-Ajaj๊ฐ€ Feliz๋ฅผ ๋””์ž์ธํ•œ ๋ฐฉ๋ฒ•์ด ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. Bulma๋„ ๋˜‘๊ฐ™์ด ํ•ฉ์‹œ๋‹ค"๋ผ๋Š” ์ƒ๊ฐ์ด ๋ถ€ํ’€์–ด ์˜ค๋ฅธ ๊ฒƒ์ด ์›ƒ๊ธฐ๋„ค์š”.

์ €๋Š” ์ด๋ฏธ Feliz.Bulma repo์— ์ƒˆ๋กœ์šด Issue๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ v2๋ฅผ ๋” ์ข‹๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ํ† ๋ก ํ•˜๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๋ฌธ์ œ์— ๋Œ€ํ•ด ํ•ฉ์˜๊ฐ€ ๋œ ๊ฒƒ ๊ฐ™์œผ๋‹ˆ version2 ๋ถ„๊ธฐ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‹ค์Œ ์ฃผ์š” ๋ฆด๋ฆฌ์Šค์— ๋Œ€ํ•œ Zaid์˜ ๊ณ„ํš์„ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์€ ์ตœ์‹  ์ฃผ์š” ๋ฒ„์ „ ๊ฐ„์— API๋ฅผ 100% ํ˜ธํ™˜๋˜๋„๋ก ์œ ์ง€ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด์จŒ๋“ , ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค-ํ›Œ๋ฅญํ•œ ๋ถ„์„!

์œ„ํ—˜์—์„œ ์ด์ „ ๋…ผ์˜๋ฅผ ์žฌ๊ฐœํ•œ๋‹ค (!) ๋ฏธ์•ˆ ๋‚œ ๊ทธ๋ƒฅ ๋“ค์—ฌ ์“ฐ๊ธฐ ๋…ผ์Ÿ์— ๋‚ด ๋‘ ์„ผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์—ˆ -์ด (๋‹ค์†Œ)๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ข‹์€ ์ผ๊ด€๋œ identation์„ ํ—ˆ์šฉ ์•Š๋Š” ๋”ฐ๋ผ ์Šคํƒ€์ผ๋ง๊ณผ Fable.React์— ๋Œ€ํ•ด ๋‹ค์Œ ์Šคํƒ€์ผ.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ํ•œ ๊ณณ์€ ์‚ฌ์†Œํ•œ ์Šคํƒ€์ผ๋ง์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ์˜ ๊ฒฝํ—˜์— ๋”ฐ๋ฅด๋ฉด ์ด๊ฒƒ์€ ์†Œ์ˆ˜์˜ ์š”์†Œ(์•„๋งˆ๋„ 10-20%)๋งŒ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

div [ ] [
    Hero.hero [ Hero.IsFullHeight ] [
        Hero.body [ ] [
            Container.container [ ] [
                img [ Src "img/fable-ionide.png"
                      Style [ Display DisplayOptions.Block
                              Width "auto"
                              Margin "auto" ]
                    ]
                br [ ]
                Heading.h3 [ Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
                    str "Fable REPL"
                ]
                Heading.p [ Heading.IsSubtitle
                            Heading.Is5
                            Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
                    str "is only available on desktop"
                ]
            ]
        ]
    ]
]

๋˜ํ•œ VS Code ๋ฐ Rainbow Brackets ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ธŒ๋ž˜ํ‚ท ๊ณผ๋ถ€ํ•˜๋ฅผ ํ”ผํ•˜๋Š” ๋ฐ ์‹ค์ œ๋กœ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณ ๋ คํ•ด์•ผ ํ•  ๋‹ค๋ฅธ ์‚ฌํ•ญ(์œ„์—์„œ ๋†“์ณค์„ ์ˆ˜ ์žˆ์Œ)์€ GiraffeViewEngine์ด Fable.React ์Šคํƒ€์ผ, ์ฆ‰ ์š”์†Œ attr-list children-list๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋‘˜ ์‚ฌ์ด๋ฅผ ์ „ํ™˜ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์˜ ๊ด€์ ์—์„œ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@Zaid-Ajaj๋Š” Femto์— ๋Œ€ํ•ด ์ข‹์€ ์ง€์ ์„ ํ•ฉ๋‹ˆ๋‹ค. Fable.React ์Šคํƒ€์ผ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ด๊ฒƒ์ด ๊ตฌํ˜„ ๋˜์ง€ ์•Š์€ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด Femto๊ฐ€ ๊ฒฐํ•ฉ๋œ Feliz ์Šคํƒ€์ผ์— ๊ณ ์œ ํ•œ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

Fable.React ์Šคํƒ€์ผ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ด๊ฒƒ์ด ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด Femto๊ฐ€ ๊ฒฐํ•ฉ๋œ Feliz ์Šคํƒ€์ผ์— ๊ณ ์œ ํ•œ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@isaacabraham

Fable.React์—์„œ ํŒŒ์ƒ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Femto ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ณ  ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜์ง€๋งŒ Fable.React ์ž์ฒด์—๋Š” react , react-dom ๋ฐ react-native ์˜ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ํ˜ผํ•ฉ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. . @MangelMaxime ์€ ํŒจํ‚ค์ง€ ๋ถ„ํ•  ์ž‘์—…์„ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ ์˜ˆ์ƒ๋ณด๋‹ค ํ›จ์”ฌ ๋” ๋งŽ์€ ์ž‘์—…์ด์—ˆ๊ณ  ์•„๋งˆ๋„ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋„์ž…ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Fable.React ์—์„œ ํŒŒ์ƒ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ์ด๊ฒƒ์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋Š” Fable.React๊ฐ€ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ข…์†์„ฑ์˜ npm ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ react ๋ฐ react-dom ํ˜ธํ™˜ ๋ฒ„์ „๋„ ํฌํ•จํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ( react ๋ฐ react-dom ์‹œ์ž‘ํ•˜๋Š” Feliz์™€ ๋‹ฌ๋ฆฌ Feliz ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์€ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.) ๋ถˆํ–‰ํžˆ๋„, ๋‚˜๋Š” ๊ทธ ์†”๋ฃจ์…˜์ด ๊ฝค ์˜ค๋žซ๋™์•ˆ ์ฃผ๋ณ€์— ์žˆ์—ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ƒํ™ฉ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์ฃผ๋„๊ถŒ์„ ์žก์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•„์ง ๋ณด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ˜•์‹ ์ง€์ •๊ณผ ๊ด€๋ จํ•˜์—ฌ: Fantomas๋Š” ์•„์ง DSL์„ ํ•ฉ๋ฆฌ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ํ˜•์‹ํ™”ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๋ชฉ๋ก ํ˜•์‹ ์ง€์ •์„ ๊ตฌ์„ฑ ๊ฐ€๋Šฅ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๋ฉ‹์ง„ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ React DSL์„ ํ˜•์‹ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ผ์ข…์˜ "Feliz-mode" ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ๋Œ€)

@Zaid-Ajaj React npm ์ข…์†์„ฑ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์ œ๋Š” Femto๋ฅผ ์ค‘๋‹จํ•˜์ง€ ์•Š๊ณ  react ๋Œ€์‹  preact๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

@l3m ์‚ฌ์‹ค, ํ•˜์ง€๋งŒ ์ด ๊ฒฝ์šฐ์—๋Š” Femto๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ด ๊ฒฝ์šฐ๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ญ”๊ฐ€๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ์–ด์„œ Femto repo์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(์ง€๊ธˆ ํ™•์ธํ•  ์‹œ๊ฐ„์ด ์—†์–ด ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค ^^)

@l3m Preact๋Š” Femto npm ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ์ƒ๊ด€์—†์ด React ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์žˆ๋Š” ๊ฒฝ์šฐ Switching to Preact ์˜ ์ง€์นจ์— ๋”ฐ๋ผ ๋ชจ๋“ˆ ๊ฐ€์ ธ์˜ค๊ธฐ ๋ณ„์นญ๋งŒ ๊ตฌ์„ฑํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ˆœ์ˆ˜ํ•œ Preact ๋ฐ”์ธ๋”ฉ(100% non-React ์ฝ”๋“œ)์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์€ Feliz์˜ ๋ชฉํ‘œ๊ฐ€ ์•„๋‹ˆ๋ฉฐ ์•„๋งˆ๋„ Preact ์ „์šฉ ๋ฐ”์ธ๋”ฉ์ด ํ™•์žฅ๋˜๊ณ  ํ™•์žฅ๋  ๋…๋ฆฝํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Feliz.Preact์— ์˜์กดํ•˜์ง€ ์•Š๋Š” Feliz.Preact)๋กœ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. preact/compat ์—์„œ ๋ฒ—์–ด๋‚˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ

@Zaid-Ajaj ์ €๋Š” ์ง€๊ธˆ Elmish ์•ฑ์—์„œ preact๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. Femto๊ฐ€ React deps์™€ ํ•จ๊ป˜ nuget์„ ๋ณด๋Š” ๊ฒฝ์šฐ packages.json์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ์กฐ์‹ฌ์Šค๋Ÿฌ์› ๊ณ  ์ด๋ฅผ ๊ฐ•์กฐํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ๋” ์ข‹์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ preact/compat ๋Š” ์ตœ์‹  preact์—์„œ ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ด์ œ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

์œ„ํ—˜์„ ๋ฌด๋ฆ…์“ฐ๊ณ  ์˜ค๋ž˜๋œ ํ† ๋ก ์„ ๋‹ค์‹œ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค!). ์ €๋Š” ๋“ค์—ฌ์“ฐ๊ธฐ ํ† ๋ก ์— 2์„ผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Fable.React์— ๋Œ€ํ•ด ํ›Œ๋ฅญํ•˜๊ณ  ์ผ๊ด€๋œ ์‹๋ณ„ ๋ฐ ํŒ”๋กœ์šฐ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” _does_ ๋”ฐ๋ฅด๋Š” ์Šคํƒ€์ผ์ž…๋‹ˆ๋‹ค. ์Šคํƒ€์ผ.

๋‚˜๋Š” Isaac์˜ ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋“ค์—ฌ์“ฐ๊ณ  ์žˆ์œผ๋ฉฐ ๊ฝค ์ฝ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
์ผ์„ ํ•˜๋Š” ๋‹จ์ผ ๋ชฉ๋ก ๋ฐฉ์‹๊ณผ ๋น„๊ตํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์›๋ณธ ๊ฒŒ์‹œ๋ฌผ์˜ ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” ๋” ๋‚˜์€ ๋น„๊ต๋ฅผ ์œ„ํ•ด ๊ทธ์˜ ์ฝ”๋“œ์™€ ์œ ์‚ฌํ•œ ๊ฒƒ์„ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

F# 5์™€ "์ •์  ํด๋ž˜์Šค ์—ด๊ธฐ" ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด Feliz๊ฐ€ Html ํด๋ž˜์Šค๋ฅผ ์—ฌ๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ Html.div ๋ฐ div ๊ตฌ๋ฌธ์„ ๋ชจ๋‘ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

์ •๋ง ๋ฉ‹์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ด ์ˆ˜ ์žˆ๋„๋ก Html ํด๋ž˜์Šค๋ฅผ ์ •์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@fc1943s Html , prop ๋ฐ style ๋Š” ๋ชจ๋‘ ์ •์  ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ F# 5์—์„œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ์ •์  ํด๋ž˜์Šค ๋‚ด์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ฒ€์ƒ‰ ์ง„์ž…์  ๋ฐ ๊ทธ๋ฃนํ™” ์ง€์ ์ด ์ „์ฒด ์ง€์ ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ด์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ •์  ํด๋ž˜์Šค๋ฅผ ์—ด๋ฉด ๊ฒ€์ƒ‰์ด ์–ด๋–ป๊ฒŒ ๋ฐฉํ•ด๋ฉ๋‹ˆ๊นŒ? ์—ฌ์ „ํžˆ Feliz.Html์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@MaxWilsonMS "์ตœ์ข…" ์ฝ”๋“œ๋ฅผ ์ถœ๋ ฅํ•˜๋ ค๋ฉด ์ถ”๊ฐ€ ๋‹จ๊ณ„๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Feliz.Html.div ์“ฐ๊ณ  ์ด์ƒํ•ด ๋ณด์ด๋Š” Feliz.Html. ๋ถ€๋ถ„์„ โ€‹โ€‹์ง€์›๋‹ˆ๋‹ค.

์ข‹์€ ์ ์€ ์‚ฌ๋žŒ๋“ค์ด ๊ธฐ๋ณธ ์„ค์ •์— ๋”ฐ๋ผ ์ •์  ํด๋ž˜์Šค๋ฅผ ์—ด๊ฑฐ๋‚˜ ์—ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. :)

์œ„์—์„œ ๋งํ•œ ๋‚ด์šฉ์„ ๋ฐ˜๋ณตํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์œ ์šฉํ•œ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ์™„์ „ํ•œ ์ดˆ๋ณด์ž๋กœ์„œ์˜ ๊ฒฝํ—˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋‚˜๋Š” ๋“ค์—ฌ์“ฐ๊ธฐ์— Feliz ์ ‘๊ทผ ๋ฐฉ์‹์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” Isaac์ด ์ œ์•ˆํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋“ค์—ฌ์“ฐ๊ธฐ๊ฐ€ ๋ชจ๋‘ 2/4๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— "๋•Œ๋กœ๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค". Feliz์™€ ํ•จ๊ป˜๋ผ๋ฉด ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•  ์‹œ๊ฐ„์ด ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค.

  • ๋‚ด๊ฐ€ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๋ฅผ ๋งŒ๋“ค๊ณ  ์žˆ๋‹ค๋ฉด ์ง€๊ธˆ์€ Fable.React ๋ฐฉ์‹์œผ๋กœ ํ–ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” "Feliz ๋ฐฉ์‹"์œผ๋กœ ๊ฑด๋ฌผ์„ ์ง“๋Š” ๊ฒƒ์„ ๋ณด๊ณ  ์ฆ‰์‹œ ๋„๋ง์ณค์Šต๋‹ˆ๋‹ค(์–ธ์  ๊ฐ€ ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค!).

  • ๋ฌธ์„œ๊ฐ€ "์ด๊ฒƒ์ด ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•" ์ธก๋ฉด์— ๋ณด๋‹ค ์ •๊ธฐ์ ์œผ๋กœ ์šฐ์„  ์ˆœ์œ„๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค๋ฉด ์ •๋ง ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์†Œ์ˆ˜์˜ ์ž‘์„ฑ์ž์™€ ํ•จ๊ป˜ ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ CSS์—์„œ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•œ API ํ‘œ๋ฉด์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์ข…์ข… ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ๋ฐ›์•„๋“ค์ด๋Š” ๊ฒƒ์ด ๊ณต์ •ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์˜ˆ๋ฅผ ๋“ค์–ด). ๋”ฐ๋ผ์„œ ์ดˆ๋ณด์ž๊ฐ€ "fontSize"๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์—†์„ ๋•Œ ๋ง‰ํžˆ๊ฒŒ ๋œ๋‹ค๋ฉด "em"์— ์žˆ๊ฑฐ๋‚˜ "grid-template-rows"๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๊ฑฐ๋‚˜ "Screen.All" ์ธ์ˆ˜๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š”์ง€ ์ „ํ˜€ ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋ช‡ ๊ฐ€์ง€ ์ž ์žฌ์ ์ธ ์šฐ๋ ค๋ฅผ ์—†์• ๊ธฐ ์‹œ์ž‘ํ•˜๋Š” ์ผ๋ถ€ "์‚ถ์˜ ์งˆ" ๊ฐœ์„ ์ด ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค... ์˜ˆ๋ฅผ ๋“ค์–ด Feliz์—์„œ ์Šคํƒ€์ผ ์†Œํ’ˆ์˜ ๊ธด ์ˆ˜์ง ๋ชฉ๋ก์„ ๋ณผ ๋•Œ style.paddingHorizontal ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. style.paddingVertical . ๋‚˜๋Š” ๋‚ด ์ž์‹ ์˜ ๋„์šฐ๋ฏธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ๊ธฐ๋Šฅ์„ ์š”์ฒญํ•˜๋Š” ๊ณณ์ด ์•„๋‹ˆ๋ฉฐ ํ‘œ์ค€์ด ์•„๋‹Œ ๊ฒƒ์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ณ  ์‹ถ์€ ์š•๊ตฌ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

  • ๋‚˜๋Š” ์ƒˆ๋กœ์šด F#์—์„œ Html ์ •์  ํด๋ž˜์Šค๋ฅผ ์—ฌ๋Š” ์•„์ด๋””์–ด๋ฅผ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค. ๋น„ํŒ์ ์œผ๋กœ - ๋™๋ฃŒ๋“ค์ด ๋‚ด ์–ด๊นจ ๋„ˆ๋จธ๋กœ ๋ฐ”๋ผ๋ณผ ๋•Œ ๊ทธ๊ฒƒ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค - ๊ทธ๋ฆฌ๊ณ  ๋” ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์„ ์ฐธ์—ฌ์‹œํ‚ค๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์•…๋งˆ์˜ ์˜นํ˜ธ์ž ์—ญํ• ์„ ํ•˜๋ ค๋ฉด div ๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๋ฐ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ์ง€ ์•Š์œผ๋ฉฐ ์ด๋ฅผ ๋ฐœ๊ฒฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋งˆ๋ฒ•์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์„ค์ •์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ถ€๋”ชํž ๊ฐ€์žฅ ์ž‘์€ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๋‚˜์—๊ฒŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ, React, CSS ๋“ฑ์„ ์ˆจ๊ธฐ๋Š” ๊ฒƒ. ๊ทธ๋Ÿฌ๋‚˜ ์ผ๋ฐ˜์ ์œผ๋กœ API์˜ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ์„ฑ์€ ๋›ฐ์–ด๋‚ฉ๋‹ˆ๋‹ค.

  • ํŠนํžˆ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์ „ํ™˜/์• ๋‹ˆ๋ฉ”์ด์…˜์˜ ๊ฒฝ์šฐ Feliz ๋ฐฉ์‹์€ ๋งค์šฐ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์—…ํ•˜๊ธฐ์— ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค.

  • ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ๊ทธ๋ฆฌ๊ณ  ์•„๋งˆ๋„ ์ ‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ถ€๋”ช์น˜๋Š” ๋ฌธ์ œ๋Š” Elmish ์„ธ๊ณ„๊ฐ€ ๋๋‚˜๊ณ  React ์„ธ๊ณ„๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์œ„์น˜๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ด€์‹ฌ์‚ฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ React์— ๋Œ€ํ•œ ๊ฐ€์žฅ ํ๋ฆฟํ•œ ๋‹จ์„œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— Fable.React ๋˜๋Š” Feliz์—์„œ Function Component๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์ด ์ €๋ฅผ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด๊ฒƒ์„ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์€ ๋‚˜์˜ ์ฑ…์ž„์ด์ง€๋งŒ ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ํ˜„์žฌ Elmish์™€ React ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ด๋กœ ์ธํ•ด ์—…๋ฐ์ดํŠธ ์‹œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด์™€ ๊ฐ™์ด ์™„์ „ํžˆ ๋นŒ๋“œ๋œ ์•ฑ์˜ ์˜ˆ๋Š” ์ฐพ๊ธฐ ํž˜๋“ค๋‹ค. ๋•Œ๋กœ๋Š” Javascript๋กœ ํ”„๋กœ๋•์…˜ ๋“ฑ๊ธ‰์˜ React ์•ฑ์„ ๋จผ์ € ๋งŒ๋“ค๊ณ  Fable/Elmish๋กœ ์กธ์—…ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ธ์ƒ์„ ๋ฐ›๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋ฉด์ฑ… ์กฐํ•ญ: ์ €๋Š” Feliz ๋ฌธ์„œ๋ฅผ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์ฝ์ง€ ์•Š์•˜์œผ๋ฉฐ ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฒƒ์€ ์•„๋งˆ๋„ ์ œ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค!

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ณต์œ ํ•ด ์ฃผ์‹  ๋ชจ๋“  ๋ถ„๋“ค๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

@drk-mtr ์ž…๋ ฅํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‚ด๊ฐ€ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๋ฅผ ๋งŒ๋“ค๊ณ  ์žˆ๋‹ค๋ฉด ์ง€๊ธˆ์€ Fable.React ๋ฐฉ์‹์œผ๋กœ ํ–ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” "Feliz ๋ฐฉ์‹"์œผ๋กœ ๊ฑด๋ฌผ์„ ์ง“๋Š” ๊ฒƒ์„ ๋ณด๊ณ  ์ฆ‰์‹œ ๋„๋ง์ณค์Šต๋‹ˆ๋‹ค(์–ธ์  ๊ฐ€ ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค!).

์˜ˆ, ์ฒ˜์Œ์—๋Š” ๊ฝค ์–ด๋ ค์›Œ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ์ต์ˆ™ํ•ด์ง€๋ฉด ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ƒ๋‹นํ•œ ์ดˆ๊ธฐ ๋น„์šฉ์ด ๋“ ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๋‹ค์ˆ˜์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ๊ฝค ์ˆ˜์šฉ ๊ฐ€๋Šฅํ•œ ์ ˆ์ถฉ์•ˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์šด ์ข‹๊ฒŒ๋„ ์ž‘์—… ์ž์ฒด๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋ฉฐ ์ผ๋ถ€ ๋ณต์‚ฌ + ๋ถ™์—ฌ๋„ฃ๊ธฐ๋Š” ๋งŽ์€ ์ž‘์—…์˜ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋˜๋Š” ์šฐ๋ฆฌ ์ค‘ ์ผ๋ถ€์ฒ˜๋Ÿผ ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์„ฑ๊ธฐ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ).

๋ฌธ์„œ๊ฐ€ "์ด๊ฒƒ์ด ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•" ์ธก๋ฉด์— ๋ณด๋‹ค ์ •๊ธฐ์ ์œผ๋กœ ์šฐ์„  ์ˆœ์œ„๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค๋ฉด ์ •๋ง ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์†Œ์ˆ˜์˜ ์ž‘์„ฑ์ž์™€ ํ•จ๊ป˜ ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ CSS์—์„œ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•œ API ํ‘œ๋ฉด์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์ข…์ข… ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ๋ฐ›์•„๋“ค์ด๋Š” ๊ฒƒ์ด ๊ณต์ •ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์˜ˆ๋ฅผ ๋“ค์–ด). ๋”ฐ๋ผ์„œ ์ดˆ๋ณด์ž๊ฐ€ "fontSize"๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์—†์„ ๋•Œ ๋ง‰ํžˆ๊ฒŒ ๋œ๋‹ค๋ฉด "em"์— ์žˆ๊ฑฐ๋‚˜ "grid-template-rows"๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๊ฑฐ๋‚˜ "Screen.All" ์ธ์ˆ˜๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š”์ง€ ์ „ํ˜€ ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹น์‹ ์ด ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ (์ด ์ปจํ…์ŠคํŠธ์—์„œ๋Š” ๋งํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค):

prop ๋ฐ style ์œ ํ˜• ๋ชจ๋‘์— custom ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ICssUnit ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. style.fontSize (length.em 1)

๋ฌธ์„œ ์—…๋ฐ์ดํŠธ๋Š” ์ถฉ๋ถ„ํžˆ ์‰ฌ์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ช‡ ๊ฐ€์ง€ ์ž ์žฌ์ ์ธ ์šฐ๋ ค๋ฅผ ์—†์• ๊ธฐ ์‹œ์ž‘ํ•˜๋Š” ์ผ๋ถ€ "์‚ถ์˜ ์งˆ" ๊ฐœ์„ ์ด ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค... ์˜ˆ๋ฅผ ๋“ค์–ด Feliz์—์„œ ์Šคํƒ€์ผ ์†Œํ’ˆ์˜ ๊ธด ์„ธ๋กœ ๋ชฉ๋ก์„ ๋ณผ ๋•Œ style.paddingHorizontal์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. style.padding์ˆ˜์ง. ๋‚˜๋Š” ๋‚ด ์ž์‹ ์˜ ๋„์šฐ๋ฏธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ๊ธฐ๋Šฅ์„ ์š”์ฒญํ•˜๋Š” ๊ณณ์ด ์•„๋‹ˆ๋ฉฐ ํ‘œ์ค€์ด ์•„๋‹Œ ๊ฒƒ์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ณ  ์‹ถ์€ ์š•๊ตฌ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ด๋“  ๋‚ด ์ฑ…์—์„œ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ด ๋ฐœ๊ฒฌํ•œ ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์— ๋Œ€ํ•œ ์ด์Šˆ/ํ™๋ณด๋ฅผ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค!

๋” ์ผ๋ฐ˜์ ์œผ๋กœ ๊ทธ๋ฆฌ๊ณ  ์•„๋งˆ๋„ ์ ‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ถ€๋”ช์น˜๋Š” ๋ฌธ์ œ๋Š” Elmish ์„ธ๊ณ„๊ฐ€ ๋๋‚˜๊ณ  React ์„ธ๊ณ„๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์œ„์น˜๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ด€์‹ฌ์‚ฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ React์— ๋Œ€ํ•œ ๊ฐ€์žฅ ํ๋ฆฟํ•œ ๋‹จ์„œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— Fable.React ๋˜๋Š” Feliz์—์„œ Function Component๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์ด ์ €๋ฅผ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด๊ฒƒ์„ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์€ ๋‚˜์˜ ์ฑ…์ž„์ด์ง€๋งŒ ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ํ˜„์žฌ Elmish์™€ React ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ด๋กœ ์ธํ•ด ์—…๋ฐ์ดํŠธ ์‹œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด์™€ ๊ฐ™์ด ์™„์ „ํžˆ ๋นŒ๋“œ๋œ ์•ฑ์˜ ์˜ˆ๋Š” ์ฐพ๊ธฐ ํž˜๋“ค๋‹ค. ๋•Œ๋กœ๋Š” Javascript๋กœ ํ”„๋กœ๋•์…˜ ๋“ฑ๊ธ‰์˜ React ์•ฑ์„ ๋จผ์ € ๋งŒ๋“ค๊ณ  Fable/Elmish๋กœ ์กธ์—…ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ธ์ƒ์„ ๋ฐ›๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋ฉด์ฑ… ์กฐํ•ญ: ์ €๋Š” Feliz ๋ฌธ์„œ๋ฅผ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์ฝ์ง€ ์•Š์•˜์œผ๋ฉฐ ์ •๋ง ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฒƒ์€ ์•„๋งˆ๋„ ์ œ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค!

Fable ์„ธ๊ณ„(Feliz ์ด์ „)์—์„œ์˜ ๋‚ด ๊ฒฝํ—˜์€ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ๊ฒƒ์„ ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑํ–ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(์˜ˆ: ๊ฐ .fs์— ๋Œ€ํ•œ ํ•˜๋‚˜์˜ ๊ตฌ์„ฑ์š”์†Œ). ๊ทธ ๊ฒฐ๊ณผ React ์‹œ์Šคํ…œ์ด ์ œ๊ณตํ•˜๋Š” ๋งŽ์€ ์ด์ ์„ ์žƒ์—ˆ์Šต๋‹ˆ๋‹ค. Feliz๋ฅผ ์‹œ์ž‘ํ•œ ์ดํ›„๋กœ ๋‚˜๋Š” React๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ๋” ๊นŠ์ด ํŒŒ๊ณ ๋“ค์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ต์ˆ™ํ•ด์ง€๋‹ˆ๊นŒ ๋” ์ด์ƒ ์˜์‹์ ์œผ๋กœ ์ƒ๊ฐํ•˜์ง€๋„ ์•Š๋Š”๋‹ค. ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ๋ชจ๋“  ๋‹จ์ผ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค.

๊ด€์šฉ์ ์œผ๋กœ React๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ง๋กœ ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด Elmish๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฐ๋ชจ ์•ฑ์—์„œ ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ/ํ›„ํฌ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ์ œํ•œํ•˜๋Š” ๊ฒƒ๋งŒ ํผ ๊ฐ„๋‹จํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. API๊ฐ€ ๊ฑฐ์˜ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— JS๋กœ ์ž‘์„ฑ๋œ ๋ชจ๋“  ๊ฒƒ์„ ๋งค์šฐ ์‰ฝ๊ฒŒ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Feliz๋กœ ์ž‘์„ฑ๋œ ์–‘์งˆ์˜ ์˜คํ”ˆ ์†Œ์Šค ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐ–๋Š” ๊ฒƒ์€ ํ™•์‹คํžˆ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ž ์‹œ ๋™์•ˆ #67์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ฒƒ์€ ์•„๋งˆ๋„ ๋ฌธ์„œ ์•ฑ ์ž์ฒด ๋˜๋Š” @cmeeren ์˜ Electron Demo ์ผ ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰