WIL(3주차)

김성욱·2022년 10월 4일
0

1. 함수 선언식 / 표현식 코드

기본적인 방법으로 function 키워드를 사용해 함수를 정의한다.

  • Function keyword (함수 선언식)
function sayHello () {
return "hello world!"
}
  • Function keyword (함수 표현식)
const sayHellow = function(){
return "hello world"
}
- 작동방식의 차이는 없지만, 함수선언식은 호이스팅의 영향을 받는다.

2. 화살표함수

함수 표현식보다 단순하고 간결하게 함수를 만들 수 있는 방법이다 .

  • Arrow function 방식 1
const sayHello = () => {
  // < --- 함수 로직 --- >
	return "hello world!"
}
  • Arrow function 방식 2 - 암시적 반환(implicit return)
// return 을 생략하는 방식입니다.
const sayHello = () => "hello world!"
  • Arrow function 방식 3 - 익명 함수
// 예시 1 - 기본적인 함수 표현 방식
const sayHelloOnClick = () => {
	console.log('hello friend')
}
<App onClick={sayHelloOnClick} />

// ❗️ 예시 1 - 익명 함수
<App onClick={() => console.log('hello friend')} />

// 예시 2 - 기본적인 함수 표현 방식
const ReturnItem = (item) => {
	return <div>{item}</div>
}
{array.map(ReturnItem)}

// ❗️ 예시 2 - 익명 함수
{array.map((item)=> <div>{item}</div>))}

3. 객체와 배열 구조분해 할당

  • 객체 구조분해 할당 코드 비교
// 구조분해 할당을 하지않은 기본 모습
const user = {name: "손석구", age: 10};
console.log(user.name) // 손석구
console.log(user.age) // 10
}

// 구조분해 할당을 사용했을 때
const { name, age } = user
console.log(name) // 손석구
console.log(age) // 10 
  • 배열 구조분해 할당 코드 비교
// 구조분해 할당을 하지않은 기본 모습
const games = ['배틀그라운드', '리그오브레전드'];
console.log(game[0]) // 배틀그라운드
console.log(game[1]) // 리그오브레전드
}

// 구조분해 할당을 사용했을 때
const [battleGround, Lol] = games;
console.log(battleGround); // 배틀그라운드
console.log(Lol); // 리그오브레전드
  • 함수 내 구조분해 할당 코드 비교
// 구조분해 할당을 하지않은 기본 모습

// user가 객체일 때
const getUserName = (user) => {
	return user.name
}

// user가 배열일 때
const getUserName = (user) => {
	return user[0]
}



// 구조분해 할당을 사용했을 때
// 객체일 때
const getUserName = ({name, age}) => {
	return name;
};

// 배열일 때
const getUserName = ([name, age]) => {
	return name
};

4. spread operator

  • 전개구문 : 기존에 있는 객체나 배열의 내용을 그대로 가져오거나 새로운 값을 덧붙여서 새로운 객체나 배열 생성
const box = {size: "big", color: "red"};
const newBox = { ...box}; // {size: "big", color: "red"}
const newBlueBox = {...box, color: "blue" }; // {size: "big", color: "blue"}

5. rest operator

  • 객체나 배열에서 구조 분해 할당을 했을 때 나머지 정보를 묶어서 표현할 수 있습니다. rest 라는 키워드를 꼭 사용하지 않아도 되고, 개발자가 편한 키워드를 사용해도 됩니다.
const user ={id: 1, name: "둘리", age: 10, location: "seoul"};
// rest 사용
const {name, ...rest} = user;
// 콘솔에 rest를 찍어보면, 구조분해 할당을 한 정보를 제외한 나머지 값을 보여줍니다. 
console.log(rest) // {id: 1, age: 10, location: "seoul"}

6. 컴포넌트

  • 화면을 구성하는 하나의 단위
  • html을 리턴 하는 함수
  • 컴포넌트 밖에서는 내가 필요한 파일을 import하거나, export default를 통해 컴포넌트를 밖으로 내보냄
  • 컴포넌트를 만들때 첫글자는 대문자
  • 폴더는 소문자로 시작하는 카멜케이스로 작성, 파일은 대문자로 시작하는 카멜케이스로 작성
  • jsx 규칙
    - 태그는 꼭 닫기
    - 무조건 1개의 엘리먼트를 반환하기
    - JSX에서 javascript를 가져올때는 중괄호 사용
    - 값 뿐만아니라, map, 삼항연산자 등 자바스크립트 문법을 JSX안에 쓸때도 중괄호 사용
    - 인라인으로 style 주기
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  const number = 1;

  return (
    <div className="App">
      <p>안녕하세요! 리액트 반입니다 :)</p>
      {/* JSX 내에서 코드 주석은 이렇게 씁니다 :) */}
      {/* 삼항 연산자를 사용했어요 */}
      <p>{number > 10 ? number+'은 10보다 크다': number+'은 10보다 작다'}</p>
    </div>
  );
}

export default App;

---------------------------------------------------------

// 중괄호를 두 번 쓰는 이유? 딕셔너리도 자바스크립트니까요!
// 이렇게 쓰거나,
<p style={{color: 'orange', fontSize: '20px'}}>orange</p>

//혹은 스타일 딕셔너리를 변수로 만들고 쓸 수 있어요!
function App() {
  const styles = {
    color: 'orange',
    fontSize: '20px'
  };

  return (
    <div className="App">
      <p style={styles}>orange</p>
    </div>
  );
}

7. 배열과 유사배열 차이

var array = [1, 2, 3];
array; // [1, 2, 3]
var nodes = document.querySelectorAll('div'); // NodeList [div, div, ...]
var els = document.body.children; // HTMLCollection [noscript,div,...]
  • 배열
    - 연관된 데이터를 하나의 변수에 그룹핑해서 관리하기 위한 방법이다.
    - 명시적 타입이 없기 때문에 하나의 배열에 여러 자료형을 가질수 있다.

  • 유사배열
    - 위의 "nodes"와 "els"처럼 데이터들이 대괄호 안에 묶여서 마치 배열같이 생겼지만 Array.isArray() 또는 array instance of 로 확인했을때 false가 나오는 객체다.
    - 유사배열의 경우 배열의 메서드를 쓸 수 없다.

  • "숫자 형태의 indexing이 가능할것", "length 속성을 포함할것" 2가지 조건이 성립하면 유사배열을 만들수 있다.
    - 배열도 객체라는 성질을 이용한 트릭, yoosa[0], yoosa[1], yoosa.length과 같은 것을 활용할 수 있다.
var yoosa = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};
  • 최상단의 예시에서 "els"에 배열의 메소드인 foreach나 map 등의 함수를 사용하면 유사배열이기 때문에 에러가 발생한다.
    배열에 내포되어있는 기능들을 유사배열에 사용하기 위해 "call", "apply", "bind" 등을 사용한다.
    "call"과 "apply"는 함수를 호출하는 방법 중 하나로, 다른 객체에 내포되어있는 함수를 내것처럼 사용할수 있게 해준다.
    두 함수를 이용하여 배열에 내포되어 있는 기능을 유사배열에 사용할 수 있다.
    - "call"의 첫번째 인자는 this를 대채할 대상과 파라미터를 리스트형태로 전달한다.
    - "apply"는 첫번째 인자로 this를 대채할 대상과 파라미터를 배열로 묶어 전달한다.
let arrayLikeObject = {
	0 : '1 Row',
	1 : '2 Row',
	2 : '3 Row',
	length : 3,
	splice : function() {}
}

Array.prototype.forEach.call( arrayLikeObject, row => {
	console.log( 'row', row )
} )
// row 1 Row
// row 2 Row
// row 3 Row

arrayLikeObject.forEach( row => {
	console.log( 'row', row )
} )
// Error
//  - arrayLikeObject.forEach is not a function
  • Array.from() 메서드를 활용하여 유사배열 객체나 반복가능한 객체를 얕게 복사해 새로운 객체를 만든 후 수정이 가능하다.
    - Array.form(arrayLike, mapFn, thisArg)
    - arrayLike : 배열로 변환하고자 하는유사 배열 객체나 반복 가능한 객체.
    - mapFn : 배열의 모든 요소에 대해 호출할 맵핑 함수.
    - thisArg : mapFn 실행 시에 this로 사용할 값.

8. React state와 LifeCycle

  • 컴포넌트 사이클 : 생성(Mount) -> 업데이트(Update) -> 제거(Unmount)
    - 위 그림은 update와 unmount를 통합한 그림이다.
    - 생성 : 컴포넌트가 페이지에 나타날 때
    - 업데이트 : 컴포넌트 내부의 값이 업데이트 될 때
    - 제거 : 컴포넌트가 페이지에서 사라질 때
  • Render 단계
    - component, props, state 등을 로드
    - rendering
  • Commit
    - 기존의 값들을 갖는 가상 돔과 업데이트된 값들을 갖는 가상돔 비교
    - 기존 돔과의 차이가 감지되면 바뀐 부분만 실제 DOM에 다시 그림
    - useEffect 실행
import React, {useState, useEffect} from 'react';

function App(){
    const [isLoading, setIsLoading] = useState(true);
   
    useEffect(() => {
        if(!isLoading){
            console.log('I am ready!');
        }else{
            console.log('componentDidMount');
            return;
        }
        console.log('componentDidUpdate');
      
        return () => {
            console.log('componentWillUnmount');
        }
    },[isLoading])
 
    const onClick = () => {
        setIsLoading(false);
    }

    return (
        <div>
            <button onClick={onClick}>
                {isLoading ? "true" : "false"}
            </button>
        </div>
    )
}
  • 위의 코드는 다음의 순서를 따릅니다.
    1. componentDidMount 출력
    2. 버튼 클릭
    3. I am ready 출력
    4. componentDidUpdate 출력

  • 위 컴포넌트의 사이클
    -- 생성 (Mounting) --
    1. Render 단계: 함수형 컴포넌트 App을 호출
    2. Render 단계: 위에서부터 아래로 함수를 읽음
    3. Render 단계: isLoading을 true로 초기화
    4. Render 단계: onClick 함수를 생성
    5. Render 단계: return 값을 렌더링
    6. Commit 단계: Virtual DOM을 비교
    7. Commit 단계: DOM에 컴포넌트 전체를 그림 (기존에 Virtual DOM 값이 없기 때문에)
    8. Commit 단계: useEffect를 실행 (ComponentDidMount 출력)
    -- 유저 인터랙션 --
    9. 사용자가 버튼을 클릭
    -- 업데이트 (Updating) --
    10. button에 등록된 onClick 함수가 실행
    11. setIsLoading 함수를 통해 isLoading = false로 변경
    12. Render 단계: return 값을 렌더링
    13. Commit 단계: Virtual DOM을 비교
    14. Commit 단계: DOM에 button 엘리먼트 부분만 다시 그림
    15. Commit 단계: useEffect를 실행 (I am ready, componentDidUpdate 출력)
    -- 제거 (Unmount) --
    16. 컴포넌트가 DOM에서 사라짐을 감지 (위의 코드는 DOM에서 컴포넌트를 제거하지 않음.)
    17. useEffect의 return 부분을 실행한다.

9. 렌더링

  • 리액트의 기본적인 동작은 부모 컴포넌트가 렌더링되면, 리액트는 모든 자식 컴포넌트를 순차적으로 리렌더링 한다.
    예를 들어, A > B > C > D 순서의 컴포넌트 트리가 있다고 가정해보자. B에 카운터를 올리는 버튼이 있고, 이를 클릭했다고 가정해보자.
1. B의 setState()가 호출되어, B의 리렌더링이 렌더링 큐로 들어간다.
2. 리액트는 트리 최상단에서 부터 렌더링 패스를 시작한다.
3. A는 업데이트가 필요하다고 체크 되어 있지 않을 것이므로, 지나간다.
4. B는 업데이트가 필요한 컴포넌트로 체크되어 있으므로, B를 리렌더링 한다. B는 C를 리턴한다.
5. C는 원래 업데이트가 필요 한것으로 간주되어 있지 않았다. 그러나, 부모인 B가 렌더링 되었으므로, 리액트는 그 하위 컴포넌트인 C를 렌더링 한다. C는 D를 리턴한다.
6. D도 마찬가지로 렌더링이 필요하다고 체크되어 있지 않았지만, C가 렌더링된 관계로, 그 자식인 D도 렌더링 한다.

즉, 컴포넌트를 렌더링 하는 작업은, 기본적으로, 하위에 있는 모든 컴포넌트 또한 렌더링 하게 된다.

  • 일반적인 렌더링의 경우, 리액트는 props가 변경되어 있는지 신경쓰지 않는다. 부모 컴포넌트가 렌더링 되어 있기 때문에, 자식 컴포넌트도 무조건 리렌더링 된다.
    즉, 루트에서 setState()를 호출한다는 것은, 기본적으로, 컴포넌트 트리에 있는 모든 컴포넌트를 렌더링 한다는 것을 의미한다. 이제 트리의 대부분의 컴포넌트가 동일한 렌더링 결과물을 반환할 가능성이 높기 때문에, 리액트는 DOM을 변경할 필요가 없다. 그러나 리액트는 여전히 컴포넌트에게 렌더링을 요청하고, 이 렌더링 결과물을 비교하는 작업을 요구한다. 두가지 모두 시간과 노력이 필요하다.
    한가지 기억해둬야 할 것은, 렌더링이 꼭 나쁜 것만은 아니라는 것이다. 단지 리액트가 실제로 DOM을 변경해야 하는지 여부를 확인하는 것일 뿐이다.

10. 데이터 바인딩

  • 데이터 바인딩 : 두 데이터 혹은 정보의 소스를 모두 일치시키는 기법이다. 화면에 보이는 데이터와 브라우저 메모리에 있는 데이터를 일치시키는 기법이다. 데이터 바인딩에는 양방향과 단방향이 있다.

  • 단방향 데이터 바인딩
    - JavaScript(Model)에서 HTML(View)로 한방향으로만 데이터를 동기화 하는것을 의미한다.
    - 역으로 직접적인 데이터 갱신은 불가능하다. 이벤트함수를 주고 호출한뒤 JavaScript에서 HTML로 데이터를 변경해야한다.
    - React에서 사용
    - 장점 : 데이터의 변화에 따른 성능 저하 없이 DOM객체 갱신이 가능하다. 데이터 흐름이 단방향이어서 데이터 추적과 디버깅이 쉽다.
    - 단점 : 변화를 감지하고 화면을 업데이트하는 코드를 매번 작성해야한다.

  • 양뱡향 데이터 바인딩
    - JavaScript(Model)과 HTML(View)사이에 ViewModel이 존재하여 하나로 묶여서 바인딩 되어 둘중 하나만 변경되어도 함께 변경된다.
    - 컴포넌트 간에서는 부모컴포넌트에서 자식 컴포넌트로는 props를 통해 데이터를 전달하고, 자식 컴포넌트에서 부모 컴포넌트로는 Emit Event를 통해서 데이터를 전달한다.
    - Vue.js, Angular에서 사용
    - 장점 : 코드의 사용면에서 코드량을 크게 줄여준다.
    - 단점 : 변화에 따라 DOM객체 전체를 랜더링 해주거나 데이터를 바꿔주므로 성능이 저하되는 경우가 있다.

11. EventListner

  • 이벤트
    - 돔에서 특정 이벤트가 발생되면 자바스크립트 이벤트 객체에서 이를 확인할 수 있다.
    - 포커스 이벤트(focus, blur)
    - 폼 이벤트(reset, submit)
    - 뷰 이벤트(scroll,resize)
    - 키보드 이벤트(keydown, keyup)
    - 마우스 이벤트(mouseenter, mouseover, click, dbclick, mouseleave)
    - 드래그 앤 드롭 이벤트 (dragstart, drag, dragleave, drop)
  • 이벤트리스너
    - 돔 객체에서 이벤트가 발생할 경우 해당 이벤트 처리 핸들러를 추가할 수 있는 오브젝트이다.
    - 특정 돔에 위의 이벤트가 발생할 경우 특정 함수를 호출 한다.
    - 소문자 대신 camelCase를 사용 한다.
    - JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.
    - false를 반환해도 기본동작을 방지할 수 없으므로, "preventDefault"를 명시적으로 호출
    - 돔 앨리먼트가 생성된 후 리스너를 추가하기 위해 addEventListener를 호출 할 필요가 없다. 대신, 엘리먼트가 처음 렌더링 될때 리스너를 제공한다.
  • 리무브 이벤트 리스너
    - 이벤트 리스너는 메모리 누수의 원인이 되므로, 더 이상 필요가 없다고하면 반드시 삭제해주어야 한다.
    - DOM객체. removeEventListener(이벤트명, 실행했던 함수명);
//함수 호출 예시
<button onClick={activateLasers}>
  Activate Lasers
</button>


//preventDefault 예시
function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}  
  • 함수형 컴포넌트의 이벤트리스너
    - componentDidMount() 역할을 하는 useEffect() 훅을 사용하여 이벤트 리스너를 위치 시킨다.
    - 라이프 사이클 함수중 componentDidMount, componentDidUpdate, componentWillUnmount를 합쳐서 useEffect 사용
    - 컴포넌트가 사라질때 이벤트를 제거해준다. useEffect의 clean up은 return 구문에서 실행된다.
 React.useEffect(() => {
   // rendering 시 실행 구문이 들어가는 부분.
   // componentDidMount, componentDidUpdate일 때 동작하는 부분
   // do something...
   retrun () => {
     // clean up 부분
     // componentWillUnmount 때 동작하는 부분
     // do something ...
   };
 }, []);


//이벤트 함수 만들기
 const hoverEvent = (e) => {
   // 이벤트가 누구에게서 일어나는지 확인
   console.log(e.target);
   // ref와 같은 요소에서 발생했는지 확인
   console.log(text.current);

   text.current.style.background = 'yellow';
 }  


//이벤트 등록
 React.useEffect(()=> {
   // rendering 때 실행는 부분
   // componentDidMount, componentDidUpdate일 때 동작 부분
   text.current.addEventLisener('mouseover', hoverEvent);
  
   return () => {
     // clean up부분
     // componentWillUnmount 동작부분
     // do something
   };
 },[text]); // 두번째인자 []! 디펜던시 array는 여기 넣어준 값이 변하면 
//첫번째 인자인 콜백함수를 실행합니다.


// 이벤트 제거
React.useEffect(()=>{
    text.current.addEventListener('mouseover', hoverEvnet);
   
    return () => {
      text.current.removeEventListener('mouseover', hoverEvent);
    }
  }  

12. React ref

  • DOM 요소에 이름을 달아 직접 접근할 때 사용
  • ref는 일반객체로 콘솔로그를 찍어보면 current 프로퍼티 하나를 가진 객체가 나온다.
  • state로 해결할 수 없고, DOM을 반드시 직접 건드려야 할 때 사용하게 된다.
    - 특정 input에 focus 주기, 스크롤 박스 조작, cansvas 요소에 그림그리기 등
  • 아래예시는 useRef라는 훅을 이용하여 ref를 활용한 것이다. dom-to-image(특정 DOM요소를 png로 바꿔 저장시켜줌) 라이브러리를 사용하기 위해, 원하는 요소에 ref를 부여했다.
    - 아래 용도 외에도 useRef는 값이 변해도 리렌더링하지 않도록 하는 변수를 관리하는 용도로 사용 가능하다.
import { useRef } from 'react';
import domtoimage from 'dom-to-image'

...
const TestComponent = (props) => {

    const {id, nickName} = props;
    const personInfo = useRef(); // personInfo에 ref 객체를 반환

    // 영역을 png로 저장하는 함수
    const onClick = function () {
      domtoimage.toPng(personInfo.current) // ref를 부여한 요소에 .current로 접근, personInfo.current는 이 div 요소를 가리킴
        .then(function (blob) {
          window.saveAs(blob, 'user-card.png');
        })
    };

    return (
        <>
            <div className="box" ref={personInfo}> {/*div.box 요소에 ref 부여함*/}
                <p> {id} - {nickName} 테스트 영역 </p>
            </div>
            <button onClick={onClick}>저장</button>
        </>
    );
}
  • 예측하지 못한 상황으로 DOM요소를 가져오지 못한다면 DOM API 사용은 큰 결함이 될수 있다.
  • 컴포넌트가 여러개가 생성되는 경우 id나 class로 특정 DOM요소에 접근하는 것은 쉽지않다. DOM요소를 특정할 수 있도록 관심영역을 특정 컴포넌트에 제한하는 역할도 ref가 할 수 있다.

13. SPA와 MPA

  • SPA
    • Single Page Application
    • CSR(Client Side Rendering)
    • 단한번만 리소스(HTML, CSS, JavaScript)를 로딩한다.(이후에는 데이터를 받아올때만 서버와 통신)
    • 필요한 부분만 갱신하기 때문에 네이티브 앱에 가까운 페이지 이동과 UX 제공이 가능하다.
    • 검색엔진최적화(SEO)가 어렵다.



  • MPA
    • Multiple Page Application
    • SSR(Sercer Side Rendering)
    • 새로운 페이지를 요청할 때마다(페이지 이동, 새로고침) 서버에서 렌더링된 정적 리소스(HTML, CSS, JavaScript)가 다운로드 된다.
    • SEO 관점에서 유리하다.(검색엔진 크롤링 적합)
    • 첫 로딩이 매우 짧다.(그러나 클라이언트가 JS파일을 모두 다운로드하고 적용하기전까지 각각의 기능은 작동하지 않는다.)
    • 새로운 페이지 로딩, 페이지 이동, 새로고침 시 깜빡임이 발생한다.
    • 불필요한 템플릿도 중복해서 로딩하므로 성능 저하 이슈가 발생한다.

14. setState의 비동기 동작

import React, {useState} from 'react';

const Counter2 = () => {
    const [count, setCount] = useState(0);

    const onClick = () => {
        setCount(count+1);
        console.log('click');

        setCount(count+1);
        console.log('click');
    }

    return (
        <div>
            <h1>{count}</h1>
            <button onClick={onClick}>+</button>
        </div>
    );
}

export default Counter2;
  • Counter2의 컴퍼넌트에 onClick 함수를 실행하면 count가 "2"로 렌더링 될것 같지만, "1"이 렌더링 된다.
    매번 랜더링 될 시 비효율 적이기 때문에 setState는 모든 주문을 받은 후에 가장 최신의 결과를 렌더링 하게 된다.
    즉, setState 동작때문에 useState 훅은 비동기적으로 동작한다고 볼수 있다.
  • 이를 해결하기 위해 아래와 같이 함수형으로 업데이트를 해주기 되면 문제를 해결할 수 있다.
//이전 코드
    const onClick = () => {
        setCount(count+1);
        console.log('click');

        setCount(count+1);
        console.log('click');
    }


//함수형 업데이트 코드
    const onClick = () => {
        setCount(count => count+1);
        console.log('click');

        setCount(count => count+1);
        console.log('click');
    }

14. Automatic Batching

  • Batching
    • React가 더 나은 성능을 위해 여러개의 state 업데이트를 한번의 리렌더링으로 묶어서 진행하는 것을 의미한다.
    • React 18에서는 React 패키지 내부의 index.js 파일에 아래와 같이 "ReactDOMClient"와 "createRoot"메서드가 정의되어있어서 batching을 자동으로 실행해준다.
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

15. React에서의 key의 역할

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>
  • 예를 들어 자식요소의 끝에 엘리먼트를 추가하면 두 트리 사이의 변경된 사항은 React가 잘 찾아 낼수 있고, 마지막 요소인 <li>third</li>만 트리에 추가 할것이다.
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>
  • 하지만 위와 같이 리스트의 맨 앞에 엘리먼트를 추가하면 Duke와 Villanova를 유지하고 Connecticut를 추가하는 것이 아니라, 모든 자식 요소들을 변경 한다.
  • 이러한 문제를 해결하기 위해 key속성이 필요하다. 아래와 같이 자식 요소들이 key를 가지고 있으면 key를 통해 기존 트리와 이후 트리의 요소들이 일치하는지를 확인한다.
<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>
  • 해당 key는 형제 요소 사이에서만 유일하면 되고, 전역에서 유일할 필요는 없다.
  • 최후의 수단으로 배열의 인덱스 값을 key로 사용할 수 있지만, 항목들이 재배열 되지 않을 때에만 가능하다. 재배열이 되는경우 오류가 발생한다.

16. DOM과 VirtualDOM

먼저 DOM이란 HTML에 들어간 Element들을 tree형태로 표현하는 것이다. 단순하게 생각하면 페이지 하나는 Document이고 각각의 요소들(페이지에 존재하는 모든 구성들)은 Element 이다. 우리가 화면에 있는 요소를 바꾸기 위해서는 DOM을 조작해야 한다. 우리는 DOM이 제공하는 API를 통해서 DOM구조에 접근하고 원하는 요소를 변경할 수 있다. 이 행위가 돔 조작이다.

가상DOM은 실제 DOM과 같은 내용을 가진 자바스크립트 객체 형태로 메모리 안에 존재한다. 이 가상DOM은 일반 DOM과 다르게 브라우저의 문서에 직접 접근할 수 없고 따라서 화면에 보여지는 내용을 직접 수정할 수 없다.

그렇다면 DOM은 어떻게 화면을 요소를 바꿔줄까?

먼저 DOM구조에 접근하여 원하는 요소의 내용이나 스타일을 변경한 뒤 HTML에서 해당요소를 찾고, 해당 요소와 자녀 요소들을 DOM에서 제거하고 새롭게 수정된 요소들로 교체한다. 그 다음 CSS들을 다시 계산하고 레이아웃 정보를 알맞게 수정한 뒤 새롭게 계산된 내용에 따라서 브라우저에 다시 그려준다. 사실 DOM이 트리구조이고 이를 조작 하는건 그렇게 어려운 작업이 아니지만 DOM을 조작할 때 마다 브라우저 화면의 UI를 새롭게 그려주는 작업이 복잡하고 시간이 많이 걸린다. 그런데 만약 변경할 요소가 열 개 라면, 이 과정을 무려 열번이나 거쳐야 한다.

가상 DOM은 어떻게 화면 요소를 바꿔줄까?

가상 DOM은 두개의 가상DOM을 가진다. 하나는 렌더링 이전 화면 구조, 다른 하나는 렌더링 후에 보이게 될 구조 이다. state가 변경되면 변경된 가상DOM이 생성되고, 이전의 가상 DOM과 비교하여 어디가 바뀌었는지 찾아낸다. (이를 Diffing 이라고 함) 그 다음 바뀐 부분의 요소들만 브라우저 화면에 적용 시켜 주는데 이를 Reconciliation(재조정) 이라고 부른다. 리액트가 일반 DOM형태보다 효율적인 이유는 재조정 해주는 방식이 Batch update 방식이기 때문이다. Batch Update란 바뀐 요소들을 한번에 그려주는 방식이다.

DOM조작에 있어서 가장 시간이 많이 드는 작업은 화면을 그려주는 작업인데 가상DOM은 바꿔야 할 요소를 한번에 모두 그려주기 때문에 매우 효율적인 것이다.

17. 삼항 연산자와 논리연산자 사용법(&&, ||)

  • React에서 삼항 연산자를 써야하는 이유
    JSX에서는 if문을 쓸 수 없는 제약이 있기 때문에 삼항 연산자를 사용하여야 한다.
    ( 다른 방법으로는 JavaScript에서 if문을 작성해서 값을 넘겨줄 수 있다. )

  • 삼항연산자 사용법
    { name === “리액트” ? true : false }
    해석 : name string의 값이 “리액트” 일 경우 true를 반환하고, “리액트”가 아닐 경우 false를 반환한다.

  • && 연산자 사용법
    { name === “리액트” && “리액트가 맞습니다.” }
    해석 : name string의 값이 “리액트” 일 경우 “리액트가 맞습니다.” 를 반환, “리액트” 가 아닐 경우 false(0) 을 반환

  • || 연산자 사용법
    { name === “리액트” || “리액트가 아닙니다.” }
    해석 : name string의 값이 “리액트” 가 아닐 경우 “리액트가 아닙니다.” 를 반환, “리액트” 일 경우 true(1)을 반환

18. 서버리스

  • 서버리스는 클라우드 컴퓨팅의 모델 중 하나로 사용자가 서버를 직접 관리할 필요가 없는 모델을 의미한다.
    여기서 서버를 직접 관리할 필요가 없다는 의미는 IaaS와 같은 모델처럼 트래픽에 따라 사용자가 직접 서버의 가용량을 증/감 시킬 필요가 없다는 뜻 이다.
    서버(Server) + 리스(Less)의 합성어라서 간혹 '서버가 없다'라고 문자 그대로 이해하실 수도 있는데 서버가 없을수는 없다. 어딘가에 저장할 공간이 있긴 해야 한다.
    클라우드 컴퓨팅이 등장하면서 우리는 더 이상 서버를 직접 설치하고 관리 할 필요가 없어졌다.
    집에다 직접 서버를 사서 설치하고 관리하고 정전이라도 나면 눈물을 흘리고 할 필요 없이 그저 MS, 아마존, 구글에게 돈만 주면 그들이 제공하는 짱짱한 서버를 이용 할 수 있게 된 것이다.
    하지만 서버의 물리적인(하드웨어) 부분은 클라우드 서비스를 제공하는 기업이 직접 관리해주지만 여전히 서버의 소프트웨어적인 부분은 사용자가 직접 관리를 해야 한다. 서버에 깔린 운영체제 등을 업데이트하고, 데이터를 백업하고, 보안에도 신경 써야 하는 등 생각보다 귀찮은 일이 많다.
    그리고 IaaS모델이나 PaaS모델은 실제 사용자에 관계없이 미리 결제한 용량에 따라 요금을 내야 한다.
    사용자가 1000명이 될 걸 예상하고 그에 맞는 용량의 서비스를 구입했다면 실제 사용자가 1000명이든 0명이든 같은 금액을 내야 하는 것이다.
    하지만 서버리스는 이와 좀 다르다. 서버리스는 동적으로 서버의 자원을 할당한다. 즉, 사용자가 없다면 자원을 할당하지 않고 대기하다가 요청이 들어오면 그 때 자원을 할당해서 요청을 처리하고 다시 대기 상태로 들어가게 된다. 자원을 효율적으로 사용할 수 있는 것이다. 비용 또한 대기상태를 제외한 실제 사용 자원에 대해서만 청구가 되기 때문에 굉장히 경제적이다.
    게다가 이 서버는 클라우드 제공 기업에서 전적으로 관리하기 때문에 사용자는 스케일링, 업데이트, 보안 등 서버에 대해 일절 관리하거나 신경 쓸 필요가 없어진다. 서버를 고려하지 않고 서비스와 애플리케이션에 집중할 수 있게 되는 것이다.
    요약하자면 '기존 클라우드 컴퓨팅 모델에 비해 경제적이고 가용성이 좋은 모델이 서버리스다' 라고 생각하시면 될 것이다.
  • FaaS(Function as a Service)와 Baas(Backend as a Service)
    - FaaS는 Function, 즉 함수를 서비스로 제공한다.
    사용자가 작성한 코드(백엔드)를 서버리스 제공자의 서버에 업로드하게 되면 해당 서버는 업로드한 코드를 함수 단위로 쪼개어 대기상태로 두게 된다. 그러다 요청이 들어오면 서버가 대기상태에 두었던 함수를 실행시켜 처리한 다음 작업이 끝나면 다시 대기상태로 만드는 구조다. 비용은 함수 호출 횟수에 따라 청구된다.
    쉽게 말하자면 업로드한 코드가 평상시에는 쿨쿨 자고있고, 요청이 들어오면 서버가 코드를 깨워 일을 시킨 후 다시 재운다..정도로 이해하면 될 것이다. 함수 호출 후 일정 시간이 경과되어도 다시 수면상태로 들어간다고 한다.(AWS Lambda의 경우는 5분)
    대표 서비스 : AWS Lambda, MS Azure Function
    - BaaS(Backend as a Service)
    BaaS는 백엔드 개발에 필요한 여러 기능을 API로 제공하는 서비스이다. 쉽게 말하자면 SNS연동이나 DB와 같이 백엔드에 필요한 기능들을 사용자가 직접 구현 할 필요 없이 제공하는 API로 해당 기능을 구현할 수 있게 해 주는 것이다. 클라우드 공급자가 백엔드 개발 환경까지 제공해 준다고 보시면 될 것이다.
    대표 서비스 : Firebase
    - 즉, FaaS와 BaaS의 차이는 직접 백엔드를 구현했느냐 아니냐의 차이 정도로 보면 된다.
profile
성욱

0개의 댓글