[ TWIL 9주차 ]

박재영·2020년 7월 10일
1

코드스테이츠 39,40일차 (07월 06일, 07월 07일)

    서버에 대한 감이 아직 안 왔는데 express로 리팩토링하니 뭐가 뭔지 몰라 해맸다. 그놈의 cors 문제도 해결이 안 되었고ㅜㅜㅜ 그나마 office hour에서 알게 된 내용과 추가로 공부한 결과를 요약정리하자면 아래와 같다.

  • router 란? client의 요청과 server의 요청 처리(handler)를 매핑시켜주는 것. url path와 method별로 분기점을 두어 처리하는 게 일반적이다.

  • cors : client에서 options를 보내지 않아도 브라우저에서 자체 options메소드를 실행시키는 로직이 있다. (물론 지원하지 않는 브라우저도 있지만 대체로 그러하다) 따라서 신경쓸 부분은 서버쪽이다. cors관련 헤더를 모든 요청에 응답하면 말짱 도루묵일 줄 알았다. client가 method에 get을 지정하더라도 server에서 get을 다루지 않으면 요청이 무시되듯 http packet 자체는 그저 '명시적인 설명서'에 불과하다. cors allow 에 대한 response를 보냈다해서 이상한 사이트에 허용해주는 일은 없다는 말씀? 그리고 options 메소드의 응답만 여러 cors allow를 보내고 그 이후부터는 Access-Control-Allow-Origin만 명시하면 된다.

  • content-type : 리소스의 media type을 알려주는 헤더. 브라우저에서 종종 MIME Sniffing(바이트로 확인하여 headers에 명시된 타입과 다르면 확인한 결과에 맞춰서 해석)이 일어나는데, 본래 요청한 타입과 다른 악성 파일을 읽게 할 수 있기 때문에 X-Content-Type-Options:nosniff 으로 보안 유지를 해야한다고 한다.

코드스테이츠 41,42,43일차 (07월 08일, 07월 09일, 07월 10일)

    드디어 리액트를 하게 되었다! 한 번 다뤄봤어서 개념 이해하는 데는 무리가 없었다. 처음 접했을 때 공식문서를 보고 끙끙 앓았던 기억이 떠올랐다. 특히 lifting state up이 예제를 이해하는 게 힘들었었다. office hour시간에서도 이 부분에 막힌 사람들이 있었다. (다들 나와 같구나 ㅋㅋㅋ)

    익숙하다고 대충 공부하기는 싫어서 공식문서를 최대한 꼼꼼하게 읽었다. 정규시간을 공식문서를 읽고 예제를 따라쳐보는데 다썼다. 아차 싶어서 저녁에 빠르게 실습을 했다. 오랜만에 쓰니 살짝 어색하긴 했는데, 이전에 배웠던 개념들이 향수처럼 떠올랐다. 다시 리액트를 능숙하게 다루도록 연습에 연습을 해야지!


What is React Library ?

1. react 라이브러리 등장 배경

    -동적 웹, 유저와의 상호작용 많아짐에 따라 상태관리의 필요성이 생겼다. 상태관리를 편하게 해주는 여러 라이브러리, 프레임워크 중 하나가 React이다.

    - React는 유저 인터페이스를 구축하는 데 유용한 자바스크립트 라이브러리

    - React가 라이브러리인가 프레임워크인가 논란이 많다. 라이브러리와 프레임워크의 개념이 확실히 잡히기 전까지는 React는 프레임워크라고 생각했다. 하지만 React 공식 홈페이지에서 나와있듯 라이브러리다. create-react-app 을 했다고 React 문법만 써야 한다는 생각을 할 수도 있지만(예전엔 그렇게 생각했다) 라이브러리의 특징처럼 쓰고 싶을 때 쓸 수 있다. 즉, html에 root 엘리먼트 div만 둘 필요는 없고 일반적으로 쓰듯 다른 html 엘리먼트를 같이 둬도 상관이 없다. 어차피 ReactDOM 엘리먼트인 JSX가 트렌스파일링되어 html 엘리먼트로 변환될 거기 때문이다.


JSX and Component

1. JSX

    - JSX자바스크립트 확장문법으로 ReactDOM의 element이다.

    - 자바스크립트 문법 내부에서 html 형식을 사용하기 위해 만들었다.

    - html처럼 생겼지만 자바스크립트 확장문법이란 말에도 알 수 있듯 일종의 객체이다.


const element = (
  <h1 className='greeting'>
  	Hello World!
  </h1>
);

const element = {
  type:'h1',
  props:{
    className:'greeting',
    children:'Hello World!
  }
};

    - 명령형이 아닌 선언형으로 작성할 수 있게 하여 좀 더 인간의 사고에 가깝게 프로그래밍할 수 있도록 돕는다.


// 명령형으로 작성한 코드. 한눈에 알아보기 어렵다. 
React.createElement('header',{ className : 'App-container' } ... );

// 선언형으로 작성한 코드. 직관적으로 바로 이해할 수 있다. 
<header className='App-container'></header>

    - JSX는 vanilla javascript에서 허용되지 않은 문법이기 때문에 Babel이라는 트렌스파일러를 사용하여 html로 변환시켜야 한다.

    - JSX를 html DOM에 표시하기 위해서는 ReactDOM을 사용한다.


const JSX = <div> Hello World! </div> // JSX는 객체이기 때문에 변수에 값할당, 인자, 반환값으로 사용가능
ReactDOM.render(JSX, document.querySelector('#root');

    - HTML과 JSX 차이 : JSX는 HTML의 속성과 이벤트 명칭을 camel형식으로 사용한다.

naming conventionHTMLJSX
attributeclassclassName
eventonclickonClick

2. Component

    - html과 js를 분리하여 작업할 때 매번 왔다갔다 확인해야 하는 불편함이 있었다.

    - 또한 html에서 서로 다른 페이지에 같은 ui를 매번 반복해서 작성해야했다.

    - React는 공식문서에서도 나와있듯 ui 랜더링 로직은 html과 javascript에 밀접한 연관성이 있음을 인정했다.

    - React의 component는 엘리먼트들을 잘게 조각내어 필요할 때 조합해서 사용하자는 생각과 js, html을 한 곳에서 작업할 때의 편의성에서 탄생한 결과물이다.

    - 추가적으로 styled component를 사용한다면 html + css + js를 한 곳에 다룰 수 있다.

    - component는 독립된 하나의 기능을 가진 모듈 단위이자 재사용 가능한 엘리먼트(JSX)들의 조합이다.

3. 컴포넌트 생성방법?

    - state란 component의 상태랜더링에 영향을 끼치는 가변적인 값이다.

  • state가 될 수 있는 조건
    1. 변하는 값
    2. 상위 컴포넌트로 부터 전달된 props가 아닌 값
    3. 기존 state값으로 도출할 수 없는 값

    - 컴포넌트는 각자 한 개의 state를 가질 수 있고 state가 없는 경우도 있다.

stateless component : state가 없는 컴포넌트
stateful component: state가 있는 컴포넌트

    - component를 만드는 방법은 2가지가 존재한다. 하나는 stateless한 functional component, 다른 하나는 stateful한 classical component다.

    - functional component는 JSX를 반환하면 되고 classical component는 render메소드를 생성 후 render 메소드의 반환값으로 JSX를 사용한다.


// functional component
function Component(){
  return ( <h1> Hello World! </h1> );
}

// classical component
class Component extends React.Component {
  
  render(){
     return ( <h1> Hello World! </h1> );
  }
  
}

functional component도 React Hook 중 하나인 useState를 사용하여 state를 사용할 수 있다. 다만 자체 내장된 state가 없다는 점에서 stateless이다.

4. Component 와 state 사용하기

    - 가령, dark mode on/off 기능 구현할 때 상태를 저장할 필요가 있다.

    - 상태 관리는 state 객체를 사용한다.

    - class의 경우, constructor 내에서 state 초기화 혹은 class 내부 상단에 state 초기화한다.


import React from 'react';

class WinColorMode extends React.Component{
  // class 내부 상단에서 state 초기화 
  state = {
    mode : "white"
  }

  handleToggle = (e) => {
    this.setState((prevState) => ( { mode : prevState === "dark" ? "white" : "dark" } );
  }

  render(){
    const { mode } = this.state;
    const style = { 
      backgroundColor: mode === "dark" ? "black" : "white",
      color: mode === "dark" ? "black" : "white",
      fontSize: "100px",
      display:"flex",
      justifyContent:"center",
      alignItems:"center"
    };
    return (
      	<>
           <button onClick={this.handleToggle}> TOGGLE MODE </button>
      	   <div style={style}>
      		{ this.state.mode === "dark" ? "DARK MODE" : "WHITE MODE" } 
	   </div>
	</>
    );
}

class WinColorMode extends React.Component {
  // constructor 안에서 state 초기화 
  constructor(props){
    super(props);
    this.state = {
      	mode : "white"
    };
  }
  
  		:
  
}

    - function의 경우, useState를 사용한다.


import React, { useState } from 'react';

function WinColorMode(props){
  // useState 인자 : 초기값
  // useSate 반환값 : [ state변수, state 변수 갱신 함수 ]
  const [ mode, setMode ] = useState("white");
  
  const handleToggle = (e) => {
    	setMode((prevState)=> (prevState.mode === "dark" ? "white" : "dark"));
  };
  
  const style = { 
      backgroundColor: mode === "dark" ? "black" : "white",
      color: mode === "dark" ? "black" : "white",
      fontSize: "100px",
      display:"flex",
      justifyContent:"center",
      alignItems:"center"
  };
  return (
      <>
        <button onClick={handleToggle}> TOGGLE MODE </button>
        <div style={style}>
          { mode === "dark" ? "DARK MODE" : "WHITE MODE" } 
        </div>
      </>
  );
}

    - 이후 state 변경 시 class의 경우, this.state로 직접 변경하는 대신 setState를 사용해야 한다.

    - state변경에 따라 render 메소드 재호출하여 state를 반영한 화면이 출력하기 때문이다.

    - function의 경우 useState의 반환값 2번째 변수인 함수를 사용한다.


setState({ mode : "dark"}); // class

setMode("dark"); // function

    - setState가 비동기적으로 작동할 수 있다. 이 말은 setState 호출 후 바로 다음 줄에서 변경된 state를 얻을 수 있다는 보장할 수 없다는 뜻이다.

    - 이전 state를 바탕으로 변경사항을 적용하고 싶다면 setState의 인자로 callback함수를 넘긴다.


setState((previousState,props)=> ({ mode: previousState.mode === "dark" ? "white" : "dark" }));

    - setState vs useState : setState는 각 state 끼리 영향을 주지 않고 개별적으로 변경사항을 적용할 수 있지만 useState는 이전 state 값을 덮어쓴다. 만약 특정 state만 변경하고 나머지는 그대로 두고 싶다면 spread연산자를 활용한다.


// user = { id: 10001, username:'jaan', avatar:'url'} 

setUser((prevUser) => {
  return { ...prevUser, username:'jane' };
}
setStateuseState
updatemergereplace

독립적인 컴포넌트들이 어떻게 상호작용을 할 수 있을까? props!

    - state는 SSOT(Single Source Of Truth)에 기반 : 데이터를 한 곳에 모아서 관리하고 외부의 접근,연결은 참조로만 이루어진다.

    - 컴포넌트 내 state는 자신 외 컴포넌트에서 수정을 가해선 X

    - state 변경사항은 state를 갖고 있는 컴포넌트 안에서만 이뤄져야 한다.

    - 외부 컴포넌트가 state에 접근은 top-down 방식의 data flow를 따라 props를 통해 가능하다.

props란 ?
- 상위 컴포넌트에서 하위 컴포넌트로 전달한 데이터
- read only 읽기 전용 (수정X)


function UserInfo(props){ // 인자로 props를 받아서 사용한다.
  const { username, avatar } = props;
  
  return(
     <div className="userInfo-header">
       <img className="userInfo-avatar" src={avatar} alt="" />
       <span className="userInfo-username">{ username } </span>
     </div>
  );
}

function Post({post}){ //  ES6 문법인 destructuring을 해서 props를 바로 가져와도 된다.
  const {
    id, 
    user,
    content,
    date,
    tag
  } = post;
  
  return (
    <>
      <UserInfo { ...user } />  // spread 연산자로 props를 넘겨줄 수도 있다.
      <div className="post-content">
        {content}
      </div>
      <div className="post-footer">
        <span className="post-date">{ date }</span>
        <span className="post-tag">{ tag } </span>
      </div>
    </>
  );
}
  
const post = {
  id : 10001,
  user: {
    id: 20001,
    username:'mina',
    avatar: 'http://ec2-00-000-000-000.ap-northeast-2.compute.amazonaws.com:8080/user/20001'
  },
  content: 'I love seoul!',
  date:'2020.02.10',
  tag:'seoul'
};

ReactDOM.render(<Post post={post} />, document.querySelector('#root'));

    - 상위 컴포넌트의 state를 변경해야 한다면 상위컴포넌트가 제공하는 메서드(내부에서 setState를 호출함)를 props로 받아서 간접적으로 영향을 준다. (lifting state up)


function QuantityButton(props){ // props로 className, onClick 함수를 전달받는다.
  // 상위컴포넌트에서 지정한 함수를 사용하여 간접적으로 value를 변경한다.
  return(
    <button {...props}> { props.children } </button>
  );
}

function ProductInCart({ name, price, description }){
  const [value,setValue] = useState(0);
  
  const add = () => { 
    setValue((preValue) => (preValue + 1)); 
  };
  
  const subtract = () => {
    setValue((preValue) => (preValue - 1));
  };
  
  return (
    <li className="product">
      <div className="product-name">{ name }</div>
      <div className="product-price">{ price }</div>
      <p className="product-description">{ description }</p>
      <div className="product__quantity-buttons">
        <QuantityButton className="btn-add" onClick={ add }> + </QuantityButton>
        <span className="product-quantity">{ value }</span>
        <QuantityButton className="btn-subtract" onClick={ subtract }> - </QuantityButton>
      </div>
    </li>
  );
}

const product = {
  name: "funcky jacket",
  price:"$30.00",
  description:"It's very good jacket!"
}

ReactDOM.render(<ProductInCart {...product}/>, document.querySelector("#root"));

생애주기(Life cycle)

    componentDidUpdate에서 검색어가 변경(state)될 때마다 youtube api를 호출하는 식으로 코드를 짰다가 블락을 먹었다;; 다시 새로운 key를 받아서 해결했다. 휴.. 꼭!꼭! 조건문에 감싸서 setState 사용하기!!

    constructor

    - state를 초기화하거나 event handler 역할을 하는 메소드를 bind할 때 사용

    - super 생성자를 호출하여 props를 넘겨줘야 한다.

    - 특별히 초기화 과정이 필요없다면 생략해도 자동으로 constructor 내부 처리를 해준다.

    componentDidMount

    - component가 처음 render 한 후 단 한 번 호출 된다.

    - 비동기 처리 (setTimeout, API)를 할 때 사용된다.

    render

    - this.props 와 this.state 값에 따라 렌더링을 결정한다.

    - JSX, Array/fragments, string/numbers, Boolean/null 타입 중 하나를 반환한다.

    - shouldComponentUpdate가 false를 반환하면 호출되지 않는다.

    componentDidUpdate

    - render가 두 번째 호출 될 때부터 매번 render 후에 호출된다.

    - 인자로 이전 props, 이전 state를 받을 수 있다.

    - 이전 props와 현재 props를 비교하여 request 요청을 할 지 판단할 때 사용한다.

    - 조건문으로 setState를 감싸지 않으면 무한 루프에 빠질 수 있으니 주의한다.

    - getSnapshotBeforeUpdate(드물게 사용)에서 value를 반환하면 componentDidUpdate의 세 번째 parameter로 전달된다.

    - render와 마찬가지로 shouldComponentUpdate가 false를 반환하면 호출되지 않는다.

    componentWillUnmount

    - component가 제거될 때 호출된다.

    - 이벤트 리스너 해제, setTimeout과 같은 타이머 함수 해제, request 취소 등이 이뤄진다.

    getSnapshotBeforeUpdate

    - scroll positon 값을 조정할 때 사용

function의 경우
- useState : state 초기화 및 state 값을 읽는 역할, 두번째 반환값으로 setState를 대체
- useEffect : componentDidMount, componentDidUpdate, componentWillUnMount

useEffect(()=>{
  // componentDidMount 처리할 사항 (단 한번 호출)
},[]); // 빈배열을 두 번째로 넘겨준다. 

useEffect(()=>{
  // componentDidUpdate 처리할 사항
},[some value]); // 두 번째 인자에 들어간 value의 값이 변할 때마다 호출 

useEffect(()=>{
  return () => {
    // componentWillUnMount 처리할 사항
  }
});

// 종합 
 const [time, setTime] = useState(new Date().toLocaleString());

  useEffect(() => {
    const timerId = setInterval(() => {
      setTime(new Date().toLocaleString());
    }, 1000);
    return () => {
      clearInterval(timerId);
    };
  }, []);

0개의 댓글