시작하며

무엇을 배울 것인가refactoring

변화 시키기 편하고 변할때 동작범위 밖의 요소가 바뀌어선 안됨
0. 원래의 코드

function createElement(type, props) {
  switch(type){
  	case 'h1' :
      return [document.createElement('h1')]
        .map(element => {
      	  Object
            .entries({...props, 'data-id': 'title'})
            .forEach([name, value] => element.setAttribute(name,value))
        return element;
      })[0];
    case 'div' :
      return [document.createElement('div')]
        .map(element => {
      	  Object
            .entries({...props, 'data-id': 'layout'})
            .forEach([name, value] => element.setAttribute(name,value))
        return element;
      })[0];
  }
}
  • 특정부분의 로직을 테스트하기 어렵다
  1. 1차 수정
function createH1(props) {
  return [document.createElement('h1')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'title'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}

function createDiv(props) {
  return [document.createElement('div')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'layout'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}
function createElement(type, props) {
  switch(type){
    case h1: return createH1(props)
    case div: return createDiv(props)
  }
}
  • 개별 로직을 테스트 할 수 있지만
  • createElement 전체를 테스트 하게 됨
  1. 2차 수정
function createH1(props) {
  return [document.createElement('h1')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'title'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}

function createDiv(props) {
  return [document.createElement('div')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'layout'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}

const creatorMap = {
  h1: createH1,
  div:createDiv,
};

function createElement(type, props) {
  return creatorMap[type](props)
}
  • 변화에 대한 영향범위를 많이 축소 시킴
  • 하지만 creatorMap에 의존하고 있음
  1. 3차 수정
function createH1(props) {
  return [document.createElement('h1')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'title'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}

function createDiv(props) {
  return [document.createElement('div')]
    .map(element => {
    Object
      .entries({...props, 'data-id': 'layout'})
      .forEach([name, value] => element.setAttribute(name,value))
    return element;
  })[0];
}

const creatorMap = {
  h1: createH1,
  div:createDiv,
};

const coupler = map 
  => (type, props) => map[type](props);
const createElement = coupler(creatorMap)
  • creatorMap의 종속성을 끊어냄

문제점

DOM

DOM API : html문서를 브라우저가 렌더링 하기 위해서 가공시켜 좋은 객체의 통칭

  • 다루기가 까다로움.
  • SPA Single-page application
    • 리로딩 때문에 깜박거림을 방지
    • 복잡도가 높아진다. 라이브오브젝트이기 때문. 참조가 불규칙함
    • .getElementByTagName(), .querySelectorAll()
      • html collection() 이용, NodeList() 이용

복잡도를 낮출 패턴

  • 모델-뷰(ui)-컨트롤러
    • 규모가 커졌을 때 복잡도를 해결하지 못함
  • react
    • 돔을 쓰지 않음
    • 패러다임을 많이 바꿈
    • ui를 위한 라이브러리

react

  • 선언적이다. Declarative
  • 효율적이다. Efficient
    • 가짜돔을 제공해서 돔과 인터렉션 최소화.
  • 유연하다. Flexible
    • 기존의 라이브러리와 프레임워크 가능

초기버전으로 코드분석해보기. 요즘 버전은 파악하기 어려움

개발 환경 세팅

  • bundler
    • webpack
    • babel
  • code editor
    • vs code

React 만들기

해결책 만들기

문자열을 다루지 말자! 훨씬 다루기 쉬운 객체로 만들어 보자
그런 프로그램이 react

webpack and babel: bundling

브라우저에 서빙하기 위해서는 번들링 과정이 필요하다.
여러개의 파일들을 하나의 파일을 만들어냄

빌드 할 수 있는 구조를 만들어보자

터미널 셋업시작!

  • npm init -y
    • 기본적인 패키지 만들기
    • package.json 생성
  • npm install webpack-cli --save-dev
    • 웹팩설치
    • 개발환경에서만 쓸 것 --save-dev
    • package-lock.json 생성
  • npm install webpack-dev-server
    • 확인하면서 개발할수 있도록 웹서버 생성해주기
  • npm install webpack-dev-server babel-loader @babel/core @babel/preset-env @babel/preset-react html-webpack-plugin --save-dev
    • 필요한 플러그인 설치
    • /node_modules 에 패키지 설치가 된다
  • webpack.config.js 파일 만들기
    • 노드파일. 노드문법으로 써야 한다
    • module.exports = {}
      • mode: '',
      • entry: '', 어떤 자바스크립트 파일로 시작할 것인지
      • output: {}, 출력 주로 여러가지 정보들이 필요해서 객체로 만듬
      • module: {}, entry의 입력값으로 transpile 한다
      • plugins: [] n개의 서프트웨어 설정해서 마지막 처리과정에서 쓰인다. console.log 라던가 주석등을 제거
    • devServer: {} 웹서버 띄우기
      • port: 9999
      • compress: true 압축해라
  • babel.config.json 파일 생성
  {
    "presets": ["@babel/preset-env"]
  }
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path');
module.export = {
  mode: 'development',
  entry: './app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
                 // (현재디렉토리, 출력디렉토리)
    filename: 'bundle.js'
  }, 
  
  devServer: {
    compress: true,
    port: 9999,
  },
  
  module: {
    rules: [ // 로더들을 지정하게 됨
      {
        test: /\.js$/, // 정규식. 바벨로더는 자바스크립트파일만 처리
        exclude: /node_modules/, // 특정 디렉토리 제외
        use: { // 설치한 바벨로더, 플러그인 셋팅
          loader: 'babel-loader', 
          options: {
            presets: ["@babel/preset-env","@babel/preset-react"]
          }
        } 
      }
    ]
  },
  
  plugins: [
    new HtmlWebpackPlugin() // 인스턴스로 함수 호출
  ]  
}
  • 번들링 실행

    • package.json 에 script 추가
      • "build" : "webpack",
        빌드시 디폴트로 'webpack.config.js를 찾음
      • "dev": "webpack serve",
        빌드는 번들링만 일어나므로 결과를 보려면 서버로 브라우저에 띄워서 봐야함
  • npm run dev 데브서버연결

  • localhost:9999 브라우저로 확인

  • npm run build

    • /dist 에 bundle.js, index.html 생성됨
  • 개발할 때 쓰는 파일들은 src 폴더에 옮겨놓기

    • js, css 등
    • webpack.conifig.js entry 패스 수정

create element

트리구조

// react.js
export function createElement(tag, props, ...children) {
  // ...children 가변인자로 받기 때문에
  return{
    tag, props, children // children이 자연스럽게 배열이 됨
  }
};

// app.js
const vdom = createElement('p', {}, 
  createElement('h1',{}, "react 만들기"),
  createElement('ul',{}, 
    createElement('li',{},"1"),
    createElement('li',{},"2"),
    createElement('li',{},"3"),
  ),
);
  • 코드량을 많이 줄임
  • 하지만 요소가 많아질 경우 관리가 어려움!

@jsx

프론트엔드 개발자들에게 가장 익숙한 포맷

  • 함수호출 (지금까지 한 방법)
  • mark up

소스코드안에 /* @jsx createElement */

react.js
export function createElement(tag, props, ...children ) {
  return { tag, props, children
}

// app.js
import { createDOM, createElement, render } from './react';
  
const vdom = <p>
  <ul>
    <li>첫번째</li>
  </ul>
</p>
  • createElement(tag, props, ...children )
    • props null로 받는 경우의 처리
      • default parameter ={}
        undefine으로 들어올 때만 작동 하기 때문에 null은 object로 취급해서 작동하지 않음

함수 컴포넌트

리액트가 함수 컴포넌트를 만드는 방법

  • 함수로 만들기
function Title(props) {
  return <h1>{ props.children }</h1>;
}

함수에서 작성할 수 있는 코드들을 삽입할 수 있다는 장점

클래스 컴포넌트

class Title extends Components {
  render() {
    return <h1 { this.props.children }</h1>;
  }
}
  
  • 소문자 / 대문자로 나누는 이유
    문자열로 처리할지 함수로 취급할지 확인할 방법이 없기 때문에
  • 대문자 태그는 문자열로 취급되지 않고 값(함수)로 취급됨.
  • 소문자로 시작하면 일반 태그로 취급됨.
  • 함수 컴포넌트는 모든 것이 초기상태 그대로이기 때문에 상태를 가질 수가 없다.
  • 하지만 Hook 이 제공되면서 instance를 만들어서 제어하고 있다

jsx

dom처리는 react가, jsx로 컴포넌트 베이스의 개발방식을 만들어 줄수 있음

virtual DOM

Hook : 원리와 제약

  • react는 hook 이 호출되는 순서에 의존한다
  • 함수가 상태를 유지하는 것 같은 효과를 낼 수 있는 react가 제공

사이드 이펙트

  • 변환기로 리액트를 web app 으로도 만들 수도 이쏘 native앱으로도 만들 수 있음
  • 다양한 플랫폼에 이식 할 수 있는 react native
  • 제공되는 component가 재활용성이 높지 않다
    • 스펙이 단순하고 다른 프로젝트에 적용하기 어렵다
  • 너무 제공되는 기능이 없다.
profile
코딩과 사별까지

0개의 댓글