ReScript

2023. 2. 1. 11:37개발/실무용 메모

ReScript

개요

  • 공식 홈 : https://rescript-lang.org/
  • ReScript 란?
    • Reason + BuckleScript ⇒ Rescript
      • Ocaml 을 기반으로 만든 함수형 프로그래밍언어 Reason 과, 이를 Javascript 로 변환해주는 컴파일러 BuckleScript 를 합쳐 만든 언어
      • Javascript의 서브셋 언어이며, 확장자로 .res 사용

ReScript 부트캠프

개발 환경 구축

  1. rescript 프로젝트 실행
    • .res 파일에서 자동 추전하는 플러그인 설치
  2. 패키지 받기 : npm install 또는 yarn install
    • yarn 미설치인 경우
      1. npm install -g yarn 실행
      2. yarn -v 버전확인
  3. 빌드 : npm run build 또는 npm run watch
    • bs.js 파일이 새로 빌드되지 않는 경우
      • bsconfig.json 기존 코드 아래 추가
      "suffix": ".bs.js",
      "package-specs": [
        {
          "module": "commonjs",
          "in-source": true
        }
      ]
  4. 실행 : node “빌드된 파일명”, npm run dev “빌드된 파일명" or yarn dev “빌드된 파일명”
    • ex) node .\src\Week1\Year2020Day3.bs.js
    • dev 명령어의 경우 nodemon을 사용하여 코드 수정 후 저장 시 자동으로 서버를 restart하여 바로 확인가능
  5. window일 경우
    • wsl 설치 전
      • tailwind 문법(%tw("max-w-2xl m-auto flex-col relative")) 을사용하기 위해 "@greenlabs/res-tailwindcss": "^0.1.6" 설치하면 External preprocessor does not produce a valid file에러 발생 + 잡다한 문제
      • wsl 설치 후 작업하는걸 추천
    • wsl과 nvm 설치
      • 홈페이지 참고하여 nvm, node.js 및 npm 설치 항목까지 진행 (노드 버전은 v14.19.0)
        • wsl에 명령어 입력
        • nvm alias default v14.19.0 // (노드 기본 사용버전 설정) npm install -g yarn // (yarn 설치)
    • 프로젝트 실행
      • wsl에서 워크스페이스 만들기
        • 명령어
        • cd // ~ 위치로 이동 mkdir (적당한 워크스페이스 이름) 디렉토리로 이동후 git clone (원격지 주소) 프로젝트 폴더로 이동후 code . *참고 /mnt/c/ 위치에서 vs code실행하면 권한 문제 생김
      • WSL:Ubuntu로 실행 확인
        • 예시
          • WSL:Ubuntu로 실행된후 rescript 익스텐션 다시 설치(최초 실행 시)
      • 모듈 설치 및 앱 실행
        • 명령어
        • yarn install // 리스크립트 watch옵션 yarn res:start // next 실행 yarn dev

부트 캠프 진행

ReScript 부트캠프 (Rescript + React + Next)

1일차

예습 & 학습 목표

학습 진행 과정

  1. Aoc 2020 Day3 문제를 각자 풀어보기
    • Rescript 로 바로 작성해봐도 되고 (Mutation 기능도 사용가능), Javascript 로 작성해도 됨
  2. Day3 문제를 각자 돌아가면서 VSCode 라이브쉐어를 통해 코드 리뷰 및 리팩토링 진행

복습 & 정리

  • 기존에 사용하던 명령적 프로그래밍 사고방식에서 선언적 프로그래밍 사고방식으로 전환하는 자세 필요
    • 함수형 프로그래밍이 선언적 프로그래밍에 포함됨
    • 명령적 방식: ‘어떻게’를 정의
    • 선언적 방식: ‘무엇을’을 정의
      • 예시
      • 숫자 배열을 받아서, 해당 배열의 모든 원소들을 두 배 시킨 새로운 배열을 리턴하는 'double'이라는 이름의 함수를 작성
  • immutable 한 코드 작성
    • 상태를 저장하는 변수를 회피하는 방법 (ex: 자바스크립트 let)
      • Map,Reduce 등을 이용
      • 재귀함수 사용
      • 배열 사용
    • 자바스크립트
    import { readFileSync } from 'fs';
    
    const text = readFileSync("../../input/Week1/Year2020Day3.sample.txt", 'utf-8');
    const map = text.split("\r\n")
    
    ...#...#..##.#
    .#.#...#...#.
    .#.###..##..#.
    .#..##.##.##..
    
    const x = 0;
    const y = 0;
    let countTrees = 0;
    const bottom = map.length
    
    while (true) {
      x += 3
      y += 1
    
      if (y >= bottom) {
        break;
      }
    
      if (map[y][x % map[y].length] === "#") {
        countTrees++
      }
    }
    
    console.log(countTrees)
    • 리스크립트
let input = Node.Fs.readFileAsUtf8Sync("../../input/Week1/Year2020Day3.sample.txt")

let map = Js.String2.split(input, "\r\n")

let bottom = map->Belt.Array.size

let countTrees = ref(0)

let rec recFunction = (x: int, y: int) => {
  if y <= bottom {
    recFunction(x + 3, y + 1)
    if y <= bottom - 1 && Js.String2.charAt(map[y], mod(x, map[y]->Js.String2.length)) === "#" {
      //map[y][x % map[y].length] === "#"
      countTrees := countTrees.contents + 1
//Belt.Array.push
    }
  }
}

recFunction(0, 0)

Js.log(countTrees.contents)
-   리스크립트2
-   let input = Node.Fs.readFileAsUtf8Sync("../../input/Week1/Year2020Day3.sample.txt") let inputArray = Js.String2.split(input, "\\\\r\\\\n") type slopes = { right: int, down: int, } let slope: slopes = { right: 3, down: 2, } type right = {mutable newRight: int} let makeCoords2 = (slope, inputArray) => { let end = inputArray->Belt.Array.length - 1 let rec go = (acc, newRight, i) => { if i > end { acc } else { let right = mod(newRight + slope.right, Js.String2.length(inputArray\[i\])) let newAcc = acc->Belt.Array.concat(\[(right, i)\]) go(newAcc, right, i + slope.down) } } go(\[\], 0, slope.down) } makeCoords2(slope, inputArray)->Js.log
  • 커링(currying)
    • 리스크립트의 함수는 기본적으로 커리드 함수
    • 다중 인수를 갖는 함수를 단일 함수를 갖는 함수들의 함수열로 바꾸는 것을 뜻함(함수가 함수를 리턴)
      • 예시
      • const sum1 = (a) => { return (b) => { return a + b; } } ==================================== const sum = (a, b) => a + b; sum(1, 2); // 3 ‘다중 인수’ 함수 코드를 ================================= const sum1 = a => b => a + b; sum(1)(2); // 3 ‘단일 함수 함수열’로 변경한다는 뜻
    • 사용이유
      • 재사용성
      • 함수의 실행을 원하는 지점까지 미룰 수 있음
        • 예시
        • const sum = a => b => a + b; const addThree = sum(3); // sum 함수는 실행되지 않는다. addThree(4); ================================================== sum(3)(4)를 해주는 것이 아닌 sum(3)만 해준 다음 남은 함수인자(4)를 기다리게 할 수 있다.

2일차

예습 & 학습 목표

  • Aoc 2020 Day3 문제를 자바스크립트 기준 let 대신 const 를 사용하여 작성 해보기
  • 하나의 함수로 작성했던 부분들을 잘게 쪼개서 여러개의 함수로 나누어 보기
  • 이러한 방법으로 Day3 문제를 풀고나면, 리스크립트로 다시 작성해보거나 Day5 문제 풀어보기

학습 진행 과정

  • 1일차와 마찬가지로 각자 풀어온 Day5 문제를 코드리뷰후, 리팩토링 진행

복습 & 정리

  • (1일차와 마찬가지로) 사고방식을 변경하는 자세가 필요
    • 복잡한 문제를 복잡하게 풀 필요 없음
    • 주어진 전체 문제를 보다 작은 단위로 생각해보기
    • 주어진 입력에 대해서만 생각 하지 말고, 작은 단위로 나누게 되면 각각의 역할에 대해서 생각하게됨
      • 이렇게 구현 하면 모듈화 / 재사용성이 높아짐
      • 작업들이 서로 섞이지 않고 격리되도록 고려
    • 각각의 역할에 대해 생각하게 된다는 뜻은, 예를들어, 문자를 입력받아 쪼개진 문자들이 담긴 배열을 만들어주는 역할, 배열을 돌면서 계산을 하는 역할 등 세부적인 단위로 함수를 나누어 보는것을 뜻함
  • 함수형 프로그래밍 관점
    • Map 의 역할, 중요성. (참고 : Promise 의 then 과 유사)
  • 기타 참고 사항
    • 파이프 오퍼레이터 알아보기

3일차

예습 & 학습 목표

  • Aoc 2020 Day5 문제

복습 & 정리

  • 자바스크립트에 있는 함수 사용하는 방법
    • external parseInt(리스크립트에서 사용할 이름): (string, int) => int "parseInt(자바스크립트에서 사용할 함수)"
  • option 타입
    • null을 명시적으로 사용하기 위한 타입 = rescript에서 null, undefined를 표현하기위해 사용
    • None | Some('a) -> Belt.Option.getWithDefault를 사용해서 패턴매칭을 대체 가능
  • 패턴 매칭
  • let intRow = switch row { | Some(n) => n | None => 0 } => Belt.Option.getwithDefault(0) : 위 코드와 같음
  • 타입을 쪼개서 생각해보기

4일차

예습 & 학습 목표

  • Aoc 2020 Day6 문제를 어떻게 풀지 알고리즘을 문장으로 작성해보기

복습 & 정리

  • 함수형 프로그래밍 관점
  • 주어진 문제를 탑다운 방식으로 보는 경향보다, 더 세부적인 단위로 파악해보기
    • 예를들어 day6번문제를 기준으로
      • 개행제거하여 배열로 만드는 함수
      • 만들어진 배열을, 한번 더 각 문자별로 쪼개서 이중배열 구조를 만드는 함수
      • 중복을 제거하여 하나의 배열로 합쳐주는 함수
      • 함수의 길이를 구해 반환해주는 함수등...
  • 자바스크립트의 flatmap -> 리스크립트 map/concatMany 변환
    • flatmap → flat과 map 함수를 합친 역할을 수행해주는 함수
    • rescript 에서는 존재하지 않아 flat 역할을 할 함수와 map함수를 조합하여 구현해야함
  • 재사용 가능하도록 코드를 구성
    • 예를 들어 인자로 function을 전달해주는 것 만으로도 기존 구현코드를 재활용하여 구현할 수 있음
      • const program = (*input*, *f*) => sum(allToGroup(*input*).map(*group* => f(*group*)))
  • 참고 사항
    • flatMap이 array에만 있지 않고 option이나, promise에도 있음

5일차

예습 & 학습 목표

  • Aoc 2020 Day6 문제 part2 를 Belt.Set.String의 intersect와 union을 사용하여 풀어보기
  • Aoc 2020 Day2 문제 풀어보기

복습 & 정리

  • 함수형 프로그래밍
    • 관심사를 분리하여 독립적인 단위로 코드를 작성해보기
      • ex) map 내부를 따로 함수로 빼기
    • map형태의 함수를 사용함으로써 핵심로직에 해당하는 함수와 부수효과인 map함수를통해 각각을 분리하고, 핵심로직의 함수를 좀 더 범용성있는 형태로 사용할 수 있음
  • 항등원의 개념. 연산자와 집합에 따라 달라짐
    • 더하기는 0 곱하기는 1
    • 합집합은 공집합
    • 교집합은 전체집합
  • 리듀스를 사용할때
    • 초기값이 무엇인지, 초기값을 가지고 어떤식으로 연산해 나갈것인지 고려하기
  • 참고 사항 및 키워드
    • Tacit programming
    • 모노이드 : 항등원을 갖는 결합법칙을 따르는 이항 연산을 갖춘 대수 구조
    • 유니온 vs 태그드유니온
      • Union Type : 타입을 여러 개 연결하는 방식
        • ex) 자바스크립트 or 연산자
      • Tagged Union Type : 겹치지 않는 타입으로 이루어진 유니온 타입

6일차

예습 & 학습 목표

  • Aoc 2020 Day2 문제 풀어보기
  • Aoc 2020 Day2, Aoc 2020 Day4 문제는 '파싱' 이 핵심 키워드
  • 파싱을 하는 작업은 구조가 계속 유지되어야 한다는 관점으로 해야함
  • 파싱을 하는 것은 실패할수도 있다는것을 고려
  • 옵션(Option)을 계속 유지하는 방향으로 작성
    • 리턴될때 Option과 같은 형태로 리턴되는게 좋음
    • Option타입은 Some 이나 None 을 가짐
    • 실패할 가능성이 있는 작업은 option 또는 result타입을 리턴 (부트캠프에서는 option주로 다룸)
  • Belt.Option.map
    • Some일때만 작업을 수행, None은 그대로 None 작업수행하지 않음

복습 & 정리

  • parsing
    • 파싱을 하는 작업은 구조화 되어있지 않은 데이터를 구조화 하는 작업
    • 파싱을 하는 것은 실패할수도 있다는것을 고려
  • flatMap
    • Option의 결과가 나오는 map 대신 flatMap을 사용하면 Option 를 얻을 수 있음
  • 리스크립트에서의 리팩토링
    • 타입스크립트 보다 세부적인 컴파일러의 역할로 인해 리팩토링 작업 시 컴파일러의 에러만 따라 수정하여도 매우 효과적으로 수정가능

7일차

예습 & 학습 목표

  • Aoc 2020 Day4 문제 풀어보기
  • Aoc 2020 Day4 문제는 이번 부트캠프의 주요한 핵심 문제

복습 & 정리

  • Aoc 2020 Day4 문제 리팩토링
    • 타입을 좀 더 범위를 좁혀 사용해보기
      • 타입 선언 시 특정 값이 꼭 포함되어야하는 타입인 경우 배리언트를 통해 별도의 타입선언을 하여 사용가능
      • 예) ecl: string -> ecl: ecl. type ecl = Amb | Blu | ...
  • 패턴매칭과 배리언트는 밀접한 관계를 가지고 있음
    • 잘 다루면 상태관리 할 때 도움됨.
  • 타입을 선언하여 사용 할 때, 한번에 완전한 형태로 선언하여 사용하지 않고, 중간단계에서 불완전한 타입을 사용해보는것도 하나의 방법
  • Next.JS 환경에서 리스크립트를 사용하는것이 부트캠프 최종목표
  • keep은 Js의 filter와 동일한기능
  • 참고 키워드
    • 리스크립트에서 Json 파싱

8일차

예습 & 학습 목표

  • Aoc 2020 Day4 의 파트2 문제 리팩토링 해보기
  • Next + Rescript 개발 환경 구축해보기
    • 템플릿을 이용하여 만들어도 되지만, 구조나 설정등을 확인 해보기
    • 해당 개발 환경을 이용하여, 팜모닝 패밀리 서비스(B2C) 개발 환경 구축 가이드 작성 예정

복습 & 정리

  • Aoc 2020 Day4 파트2 문제 리팩토링
    • 파싱한 데이터는 검증이 끝났기 때문에 추가 검증이 필요없음
    • validation 과 parse 의 차이
      • validation은 값을 검사하여 true false 로 리턴
      • parse는 파싱에 성공하면 값을 그대로 리턴
    • 참고 키워드
      • 시퀀스 (promise.all 도 시퀀스 함수의 일종)
  • 리스크립트에서 next나 radixui 같은 외부 라이브러리를 사용하기 위해 바인딩처리를 어떤식으로 하였는지 검토해보기

9일차

예습 & 학습 목표

  • tailwind ppx를 설치하고 %twc를 사용하여 구축한 개발환경의 클래스네임들에 적용해보기

복습 & 정리

  • Next 환경에서 라우팅 처리 해보기
    • Link 태그 사용
    • 라우터의 리다이렉트 기능
  • 상태관리
    • Redux, localStorage, sessionStorage 사용
    • React hook
      • useState, useEffect, useReducer
  • TailwindCss
    • 스타일을 좀 더 규칙화 해서 사용하도록 하는 것을 지향
  • Swr
    • Api 호출 뿐 아니라 fetcher 설정을 통한 타겟 변경으로 localStorage, sessionStroage 도 관리가능

10일차

복습 & 정리

  • SWR 사용해보기
    • 데이터를 가져올때 데이터를 가져오는 어떠한 라이브러리도 사용 가능
      • fecth, axios, GraphQL
    • GraphQL -> 선언적 데이터 패칭
    • key 와 fetcher를 받음
    • API를 call 하는 기능은 존재하지 않음
    • fetcher 함수를 통해 요구하는 동작을 수행함 (axios, graphql 등)
  • useEffect
    • 리스크립트는 가변배열을 사용 할 수 없어서 개수가 정해진것만 전달가능
      • useEffect0 1 2 3 .. 방식으로 사용
    • useEffect를 써야하는 상황은 구조가 잘못되었을 가능성 ㄱㅓㅁ토
  • 상태관리
    • 전역상태가 과연 꼭 필요한지 생각해보고, useEffect 남발하지 않기.
    • 리스크립트에서는 useEffect0, 1, 2.. 로 나뉨
  • 플럭스 아키텍처 개념 파악해보기
    • 리덕스, 리코일등..
  • 앞으로...
    • Vercel을 이용해 개인별로 배포를 진행해보기
    • 협동 프로젝트로 부트캠프에서 배운내용 적용해보기

소감

  • 함수형 언어(Rescript) 개발 소감
    • 작은 단위의 함수 재사용 장점
    • 불변성+고차 함수 -> 생각(설계)방식 전환
    • 유익했지만, 생각(설계)방식이 어려움
    • 리스크립트 능숙해지려면 훨씬 많은 시간 필요할 듯 (특히 type)
  • 프론트엔드 기술 스택 소감
    • RS vs. TS(JS)의 장단점에서 특이 바인딩 단점을 뛰어 넘는 정도의 장점이 있을까
    • 함수형 적용은 좋지만 TS로 하면 어떨지
    • Win에서 구축 어려움
  • 부트캠프의 교육 방식 소감
    • 혼자 공부할때보다 훨씬 나음 -> 하루 단위 과제 및 피드백
    • 1:1에 최적화된 방식 같음 (코드를 다 봐줄순없으니까)
    • 함수형 언어 및 기본에 해당하는 것은 다 스스로 해야하고 가이드가 없던 점이 아쉬움

ReScript 개념 이해

JS/TS 와 주요 차이점

  • 클래스 → 플레인 데이터 + 함수
  • if문과 가상 디스패치 → 패턴 매칭
  • 문자열 오남용 → 데이터 모델링(Variant)
  • 마이그레이션 : 넓이 우선 → 깊이 우선

주요 특징

  • Javascript의 서브셋
    • 사용하기 더 쉬움
    • 빌드 최적화 (Just-In-Time 최적화)
      • 사용하지 않는 코드 제거가 강력함
      • 용량이 작은 Javascript 결과물
      • 빌드 시간이 짧음
      • 빌드 결과 Javascript의 가독성 고려
      • 한 Rescript 파일(.res) 당 한 Javscript 파일 생성 → 기존 도구 통합을 쉽게 해줌
  • 강력하고 빠른 타입 시스템
    • 타입 추론 시스템이 명확함
let add = (a, b) => {
  a++b
}
add("1", "2")

This expression has type string but an expression was expected of type int

// Typescript 에서는 no-implicit-any 에러. 암시적으로 선언되었는데 추론 타입이 any 일때

// Rescirpt의 추론 과정
// Rescript 에서 + 오퍼레이터는 int 형에만 사용할 수 있음.
// 따라서 구문 내의 a , b 는 int 형임을 추론하고,
// Rescript 에서는 모든 식별자가 불변형이므로 파라미터로 받은 a , b 도 불변.
// 그러므로 a , b 는 중간에 다른값으로 변경 될 수가 없음을 추론하고,
// a , b 는 int 타입임을 보장함   -> 추론 기반 : 힌들리-밀러 타입시스템
    • 타입 정의나 조정이 필요 없음
    • 타입 함정이 없음 (Soundness)
      • null/undefined 에러가 없음
    • 타입 어노테이션이 필요 없음
  • JS 직접 사용도 가능

언어적 특징

  • 문장 끝 ; 없음
  • null, undefined 없음
  • import 구문이 필요 없음
    • 모든 Rescript 파일 = 모듈이므로, 다른 파일에 접근 = 다른 모듈에 접근
    • 이러한 이유로 파일 이름이 중복되면 안됨
    • 기본적으로 모든 파일(모듈)은 exported 상태
  • 변수
    • JS const x = 5; → RS let x = 5
    • JS let x = 5; x = x + 1; → RS let x = ref(5); x := x.contents + 1
      • let 이 기본적으로 불변이며, ref()로 수정 가능하게 만들 수 도 있음
  • 문자열
    • 문자열 표현은 “” 만 사용, ‘’ 사용 불가
    • 문자열 합칠 때 + 대신 ++ 사용
      • ex) "hello" + "world" → "hello" ++ "world"
    • `` 와 ${} 사용 동일
      • ex) hello ${message}
  • 객체/레코드
    • JS 타입 없음 → RS type type point = {x: int, mutable y: int}
    • 그 외 객체 표현 같음
  • 배열
    • [1, 2, 3] , myArray[1] = 10 등 표현 방식 동일
    • 다른 타입이 한 배열에 섞일 수 없음
  • 함수
    • arg => retVal 표현 동일
    • add(4, add(5, 6)) 표현 동일
    • function 키워드 없음, arrow 함수가 기본
      • function named(arg) {...} → let named = (arg) => {...}
      • const f = function(arg) {...} → let f = (arg) => {...}
    • return 키워드 없음, 스코프 내의 암시적 리턴
  • 조건문 : if-else, switch
    • 조건문에 괄호가 없고, 표현식 사용
      • if (a) {b} else {c} → if a {b} else {c}
    • a ? b : c 표현 동일
    • switch 표현도 괄호 없이 동일
  • 반복문 : for, while
    • for (let i = 0; i <= 10; i++) {...} → for i in 0 to 10 {...}
    • for (let i = 10; i >= 0; i--) {...} → for i in 10 downto 0 {...}
    • while (true) {...} → while true {...}
  • 블록 스코프
    • { ... } 암시적으로 가장 마지막줄이 반환됨
    • 리스크립트 if, while 구문과 함수도 같은 블록 스코프 방식
  • 예외 처리
    • throw new SomeError(...) → raise(SomeError(...))
    • try {a} catch (Err) {...} finally {...} → try a catch { | Err => ...}
      • finally 없음
  • 배리언트 (Variant) 선호
    • 배리언트 자료구조는 타입에 대해서 'a 또는 b' 형태를 취할 수 있게 해주는 문법
    • type myResponse = | Yes | No | PrettyMuch let areYouCrushingIt = Yes // myRsponse 타입은 각각 Yes No PrettyMuch 라는 배리언트 생성자를 가지고 있고, // 이는 | (bar) 로 구분하며, areYouCrushingIt 의 타입은 Yes
    • 배리언트 생성자는 추가 인자를 가질수 있음
    • type account = | None | Instagram(string) | Facebook(string, int) let myAccount = Facebook("harold", 21) let yourAccount = Instagram("Gunners")
    • 배리언트는 성능적인 측면에서 속도를 많이 향상 시킬 수 있음
    • type animal = Dog | Cat | Bird let data = Dog switch data { | Dog => Js.log("Wof") | Cat => Js.log("Meow") | Bird => Js.log("Kashiiin") } // 컴파일러는 이 코드를 읽고, type animal = 0 | 1 | 2 으로 변환한뒤, switch 문을 // 일정한 시간이 소요 되는 O(1) 점프테이블로 변환한다.
    • let data = 'dog' if (data === 'dog') { ... } else if (data === 'cat') { ... } else if (data === 'bird') { ... } // 이 코드는 시간복잡도가 O(n) 이다.
  • 파이프 연산자 ( → ) 지원
    • 기본형태 a(b) 를 b → a 같은 형태로 바꿔 작성 가능.
  • Place Holder 기능
    • 언더스코어 ( _ ) 를 통해 작성 가능
    • let addTo7 = (x) => add3(3, x, 4) let addTo7 = add3(3, _, 4) //두개의 문법이 같음
  • JS module 과 Belt module
    • JS module
      • 기존 javascript api 모듈
    • Belt module
      • javascript 에서 사용할 수 없는 기능들이 추가된 모듈

'개발 > 실무용 메모' 카테고리의 다른 글

SCSS  (0) 2023.02.01
RadixUI  (0) 2023.02.01
Redux  (0) 2023.02.01
React  (1) 2023.02.01
Rendering  (0) 2023.02.01