theme | title | titleTemplate | highlighter | lineNumbers | aspectRatio | fonts | download | favicon | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
geist |
ReScript 같이 해요 |
%s - Miryang |
prism |
true |
4/3 |
|
true |
FEConf 2022
- 그린랩스 프론트엔드 엔지니어
- 여행을 좋아합니다.
- 지방 거주 재택러.
- 함수형 프로그래밍 입문자.
- 개발 블로거. miryang.dev (게스트북 남겨주세요 🫶🏻)
- 가볍게 알아보기
- 작게 좋았던 점
- 크게 좋았던 점
- 아직도 망설인다면
- 아쉬웠던 점
- 프론트엔드 비기너이신 분
- 새로운 언어를 가볍게 알아보고 싶으신 분
- 함수형 프로그래밍 언어에 관심 있으신 분
- 강력한 타입 언어를 사용해 보고 싶으신 분
- ReScript 도입을 망설이고 있으셨던 분
- JavaScript 개발자에게 친숙한 구문을 제공
- 모든 JavaScript 라이브러리를 ReScript와 함께 사용 가능
프로그래밍 언어 문법은 금방 익힌다는 자신감 Down
달랐던 점 | 설명 |
---|---|
😅 let만 있다. | const와 비슷한 불변 변수 선언 |
😂 화살표를 사용한다. | -> 파이프 연산자, a(b)를 b->a |
😫 return이 없다. | 마지막 라인은 암묵적 반환 |
😰 import & export가 없다. | 모든 모듈은 내보내진다 |
🤯 타입 어노테이션 없이도 타입이 있다. | 타입 추론 시스템 |
작은 따옴표 vs 큰 따옴표 / 탭 사이즈 2 vs 4 / 세미콜론 붙이기 vs 말기
validateAge(getAge(parseData(person)))
person -> parseData -> getAge -> validateAge
person
-> parseData
-> getAge
-> validateAge
파이프 연산자 ->
책 읽듯이 한 방향으로 읽기
export { default as Button } from "./Button";
export { default as Card } from "./Card";
export { default as Modal } from "./Modal";
export { default as Toast } from "./Toast";
export { default as Layout } from "./Layout";
export { default as Footer } from "./Footer";
export { default as Header } from "./Header";
export { default as Container } from "./Container";
export { default as Toc } from "./Toc";
export { default as Title } from "./Title";
- 모든 모듈은 내보내진다.
- 인터페이스 파일을 사용하면 내보내고 싶은 모듈만 지정할 수도 있다.
// TestComponent.res
module Button = {
@react.component
let make = () =>
<button> {`click`->React.string} </button>
}
// Generated by ReScript, TestComponent.js
function Playground$Button(Props) {
return React.createElement("button", undefined, "click");
}
var Button = { make: Playground$Button };
exports.Button = Button;
import Container from '../components/Container';
import Button from '../components/Button';
import Button form '../어디더라?';
import할 파일의 위치를 몰라도, import 한 파일의 위치가 바뀌어도
코드를 변경하지 않아도 된다.
// TestComponent.res
module Button = {
@react.component
let make = () =>
<button> {`click`->React.string} </button>
}
// TestPage.res
<TestComponent.Button />
let add1 = (a, b) => a + b
let add2 = (a, b) => a ++ b
let add3 = (a, b) => a +. b
(int, int) => int
(string, string) => string
(float, float) => float
값의 형태가 맞는 레코드 타입 선언을 찾는다. data는 person 타입으로 추론된다.
Red 또는 Blue 또는 Yellow 표현한다.
type color = Red | Blue | Yellow
let myColor = Red
배리언트 생성자는 추가 값을 가질 수 있다.
type result = Pending | Success | Fail
type result = Pending | Success({data: string}) | Fail
구조 분해를 하고, 각각 분해된 결과의 오른 편에 작성된 코드가 실행된다.
type sns = Facebook(string) | Twitter(string) | None
let name = switch data {
| Facebook(name) => name
| Twitter(name) => name
| None => ""
}
누락 된 패턴이 있는지 컴파일 시점에 검사한다.
<button onClick={() => router.push('/')}>
Home으로
</button>
<button onClick={() => router.push('/post')}>
Post로
</button>
<button onClick={() => router.push('/post?id=123')}>
id와 함께 Post로
</button>
type page = Home | Post
let toString = page => {
switch page {
| Home => "/"
| Post => "/post"
}
}
<button onClick={_=> router->Next.Router.push(Route.toString(Home))}>
{`Home으로`->React.string}
</button>
<button onClick={_=> router->Next.Router.push(Route.toString(Post))}>
{`Post로`->React.string}
</button>
type page = Home | Post({id: string})
let toString = page => {
switch page {
| Home => "/"
| Post({id}) => {...}
}
}
...
<button onClick={_=>
router->Next.Router.push(Route.toString(Post{id: 123}))}>
{`id와 함께 Post로`->React.string}
</button>
// 컴파일 에러 발생
<button onClick={_=> router->Next.Router.push(Route.toString(Post))}>
{`Post로`->React.string}
</button>
Uncaught TypeError: Cannot read properties of null (reading 'value')
I call it my billion-dollar mistake.
It was the invention of the null reference in 1965
- Tony Hoare -
It was the invention of the null reference in 1965
- Tony Hoare -
- ReScript에 null 또는 undefined에 대한 개념이 없습니다.
- option 타입은 배리언트입니다.
type option<'a> = None | Some('a)
let 계세요? = 없음 | 사람(정미량)
[URL 생성] 요청
요청 중...
[URL 생성] 완료
miryang.dev
[URL 생성] 실패
요청 실패
type result_t = Pending | Success(string) | Fail
@react.component
let default = () => {
let (result, setResult) = React.useState(_ => Pending)
let handleClick = e => {
e->ReactEvent.Synthetic.preventDefault
let response {
| Some(res) => setResult(_ => Success(res))
| None => setResult(_ => Fail)
}
}
<>
<button onClick={handleClick}> {`URL 생성` -> React.string} </button>
{
switch result {
| Pending => {`요청 중...` -> React.string}
| Fail => {`요청 실패` -> React.string}
| Success(url) => {url -> React.string}
}
}
</>
}
type result_t = Pending | Success(string) | Fail
@react.component
let default = () => {
let (result, setResult) = React.useState(_ => Pending)
let handleClick = e => {
e->ReactEvent.Synthetic.preventDefault
let response {
| Some(res) => setResult(_ => Success(res))
| None => setResult(_ => Fail)
}
}
<>
<button onClick={handleClick}> {`URL 생성` -> React.string} </button>
{
switch result {
| Pending => {`요청 중...` -> React.string}
| Fail => {`요청 실패` -> React.string}
| Success(url) => {url -> React.string}
}
}
</>
}
type result_t = Pending | Success(string) | Fail
@react.component
let default = () => {
let (result, setResult) = React.useState(_ => Pending)
let handleClick = e => {
e->ReactEvent.Synthetic.preventDefault
let response {
| Some(res) => setResult(_ => Success(res))
| None => setResult(_ => Fail)
}
}
<>
<button onClick={handleClick}> {`URL 생성` -> React.string} </button>
{
switch result {
| Pending => {`요청 중...` -> React.string}
| Fail => {`요청 실패` -> React.string}
| Success(url) => {url -> React.string}
}
}
</>
}
ReScript를 도입하고 싶은데
프로젝트를 폭파하고 새로 시작하기에
리스크가 너무 큽니다.
원활한 통합이 가능
프로젝트를 폭파하고 새로 시작하기에
리스크가 너무 큽니다.
x
ReScript를 부분적으로 적용하면서 원활한 통합이 가능
raw JavaScript 코드를 작성할 수 있다.
let add = %raw(`
function(a, b) {
console.log("hello from raw JavaScript!");
return a + b
}
`)
Js.log(add(1, 2))
- 타입 어노테이션이 없으면 추론이 된다.
- 타입 어노테이션을 줘서 타입 안전하게 할 수도 있다.
// ts-belt/src/Option/Option.res
%comment("Returns `Some(value)` if the provided value is not falsy, otherwise, returns `None`.")
@gentype
let fromFalsy = value => value ? Some(value) : None
↓
// ts-belt/src/Option/Option.ts
export declare type ExtractValue<T> = Exclude<T, null | undefined>;
export declare type Option<A> = A | null | undefined;
export declare const Some: <A>(value: NonNullable<A>) => Option<A>;
export declare const None: Option<never>;
export declare function fromFalsy<A>(value: A): Option<ExtractValue<A>>;
TypeScript & ReScript & JavaScript 3가지 언어를 동시에 사용할 수 있다.
Next.js의 Head 컴포넌트 바인딩 코드
// https://github.com/MoOx/rescript-next
module Head = {
@module("next/head") @react.component
external make: (~children: React.element) => React.element = "defalut"
}
Web api의 addEventListener, removeEventListener 바인딩 코드
@val @scope("document")
external addEventListener: (string, t => unit) => unit = "addEventListener"
@val @scope("document")
external removeEventListener: (string, t => unit) => unit = "removeEventListener"
stackoverflow 또는 검색으로는 도움 받기 힘들다
ReScript Forum 을 이용합니다.
- 그래프큐엘 클라이언트 라이브러리
module Query = %relay(`
query IndexQuery {
user {
name
age
}
}
`)
type props = { data: IndexQuery_graphql.Types.response}
npm install rescript --save-dev
- https://unsplash.com/photos/z8kriatLFdA
- https://www.youtube.com/watch?v=WN5UfUjVTNE
- https://www.youtube.com/watch?v=tn0zt7U7roY
- https://www.youtube.com/watch?v=dr1PskGSdU4
- https://www.youtube.com/watch?v=ZY4n7B0pZFw
- https://www.youtube.com/watch?v=XWgL51JSbGI
- https://www.youtube.com/watch?v=kAWP0laFz6M
- https://green-labs.github.io/why-rescript
- https://rescript-lang.org