티스토리 뷰

Redux

pub-sub model(구독패턴)을 구현한 라이브러리이다.

(어떤 객체가 하나 있을 때 이 객체가 변경되면 구독한 함수를 호출해서 변경될 때마다 알려줌)

 

 

웹앱

어떠한 데이터를 어떠한 모양으로 어디에 배치하는지가 중요하다, 그 이후는 HTML과 CSS가 다 처리한다.

데이터는 API호출을 통해 비동기적으로 가져온다. 비동기로 가져오다보면 예외상황이 발생할 수 있으므로 각 경우에 따라 대응되는 코드들이 추가된다.

 

웹앱 개발시 코드를 빠르게 찾고 고칠 수 있는 구조를 연구하여 나온 것 중에 하나가 리액트.

 

"어떤것이 좋은 아키텍쳐인가?"

→ 결국 같은 것끼리 묶고, 다른 것들은 분리하는 것.(지식의 수준/역량에 따라서 차이가 발생할 수 있음. 데이터의 복잡도에 따라도 달라질 수 있음)

→ 이름만 잘 지어도 70%는 먹고들어간다. 구조가 읽기 쉬워지니까.

 

 


React

리액트의 원리

리액트의 원리를 이해하기 위해 간단하게 만들어BOJA

//index.js

const list = [
  { title: "React에 대해 알아봅시다." },
  { title: "Recux에 대해 알아봅시다." },
  { title: "TS에 대해 알아봅시다." }
];

const rootElement = document.getElementById("root");

function app() {
  rootElement.innerHTML = `
    <ul>
      ${list.map((item) => `<li>${item.title}</li>`).join("")}
    </ul>
  `;
}

app();

 

위의 코드는 바깥의 영향을 받을 수 밖에 없으므로 아래와 같이 수정한다.

*바깥에 영향을 받지 않는 함수를 순수함수라고 한다.

const list = [
  { title: "React에 대해 알아봅시다." },
  { title: "Recux에 대해 알아봅시다." },
  { title: "TS에 대해 알아봅시다." }
];

const rootElement = document.getElementById("root");

function app(items) {
  rootElement.innerHTML = `
    <ul>
      ${items.map((item) => `<li>${item.title}</li>`).join("")}
    </ul>
  `;
}

app(list);

위 코드에서는 rootElement에 접근해서, innerHTML로 직접 DOM에 코드를 주입한다. 

실제 리액트에서는 Virtual DOM으로 좀 더 간단하게 처리할 수 있도록 해준다.

 

 

Virtual DOM


브라우저는 HTML 코드 문자열을  DOM tree로 만들어 다루기 쉽게 만든다.
하지만 앱의 규모가 커지면서 이 DOM tree를 다루는 것이 더 복잡해짐에따라 real DOM을 다루는건 조금 구시대적이 되었다.

그래서 같은 컨셉으로, 리액트는 (DOM에 직접 접근하면 복잡하니) Virtual DOM(=VDOM)을 만들어서 좀 더 쉬운 구조로 만들도록 했다.
VDOM과 JS 사이를 쉽게 구조화(?) 하기위해 나온것이 바로 JSX

virtual DOM은 아마도 이런 구조로 생겼을 것이다. 제일 상위에 최상위 컴포넌트가 있을것이고, 그래서 VDOM은 한개가 존재할 수 있는 것이다.

 

{  
    type: "ul",  
    props: {}, 
    children: [    
    	{      
        	type: "li",     
            props: { className: "item" },      
            children: "React"    
        },    
        {      
        	type: "li",      
            props: { className: "item" },      
            children: "Redux"    
        },    
        {      
        	type: "li",      
            props: { className: "item" },      
            children: "TypeScript"    
        }  
    ]
 };

 

리액트의 구조

React와 React DOM을 import하고 컴포넌트를 render함수로 실행해준다.

import React from "react";
import ReactDOM from "react-dom";

function App() {
  return <h1>1</h1>;
}

ReactDOM.render(<App />, document.getElementById("root"));

 

참고) 아래와 같이 리턴해 주는 부분이 JSX문법이다. 바벨에서 확인해보면 Virtual DOM을 만드는 함수 호출되는걸 알 수 있다.

function App() {
  return (
    <div>
      <h1>Hello?</h1>
      <ul>
        <li>React</li>
        <li>Redux</li>
        <li>TypeScript</li>
      </ul>
    </div>
  );
}

 

이렇게 컴포넌트를 더 만들 수 있다. StudyList라는 이름으로 컴포넌트를 생성함으로써 좀 더 이해하기 쉽고, 관리가 수월해지게 된다.

function StudyList() {
  return (
    <ul>
      <li className="item">React</li>
      <li className="item">Redux</li>
      <li className="item">TypeScript</li>
    </ul>
  );
}

function App() {
  return (
    <div>
      <StudyList />
    </div>
  );
}

*HTML과 달리 class가 아니라 className이라고 쓰는 이유는 class가 예약어이기 때문이다.

 

function StudyList(props) {
  return (
    <ul>
      <li className="item">React</li>
      <li className="item">Redux</li>
      <li className="item">TypeScript</li>
    </ul>
  );
}

function App() {
  return (
    <div>
      <StudyList item="abc"/>
    </div>
  );
}

컴포넌트에 속성을 전달할 때는 위와 같이 props를 이용하는데, 컴포넌트는 함수이기 때문에 props 외에 다른 방법으로 데이터를 전달 할 수는 없다. 

 

 

React 내부에 존재하는 createElement 함수를 살펴보자.

React.createElement(type, props = {}, ...children) {
	return { type, props, children }
}

//실제 코드 실행시 아래와 같이 실행이 되는 것
React.createElement('ul', {}, createElement('li', {}, 'React')); 

요렇게 Render()가 객체를 쫒아가면서 만들어진 VDOM을 realDOM으로 converting 할 것이다.

 

 

바벨 기능 중에 아래와 같은 방식으로 작성을 하면, 컴파일 타임에 작성한 문법을 찾아서 변환해주는 것이 있다.

(요즘은 브라우저가 직접 실행할 수 없으므로 컴파일 타임이 생김. 컴파일타임인지 런타임인지 알고 있어야한다.)

/* @jsx createElement */

function App() {
...
}

위와 같이 작성하면 컴파일시 이렇게 된다.

function createElement( App(), {}, createElement(...)}

 

이 때 중요한 점!

바벨은 대문자 소문자 처리방식이 다르다.

소문자 예) <ul> → 문자열로 처리

대문자 예) <App /> → 함수로 처리

그래서 사용자 컴포넌트는 반드시 대문자로 시작해야하는 것.

 

 

다음으로 render element를 구현한다. 역할은 virtual DOM 객체를 리턴하는 것이다.

function renderElement(node) {
  if (typeof node === "string") {
    return document.createTextNode(node);
  }
  const el = document.createElement(node.type);

  //자식들 렌더링
  node.children.map(renderElement).forEach((element) => {
    el.appendChild(element);
  }); //재귀호출함
  return el;
}

이제 화면에 그려줄 render 함수를 만들어보자.

function render(vdom, container) {
  container.appendChild(renderElement(vdom));
}

 

 

*완성된 코드*

/* @jsx createElement */

function renderElement(node) {
  if (typeof node === "string") {
    return document.createTextNode(node);
  }
  const el = document.createElement(node.type);

  //자식들 렌더링
  node.children.map(renderElement).forEach((element) => {
    el.appendChild(element);
  }); //재귀호출함
  return el;
}

function render(vdom, container) {
  container.appendChild(renderElement(vdom));
}

function createElement(type, props = {}, ...children) {
  //자식은 여러개 올 수 있으니까
  if (typeof type === "function") {
    return type.apply(null, [props, ...children]);
  }
  return { type, props, children };
}

function Row(props) {
  return <li>{props.label}</li>;
}

function StudyList(props) {
  return (
    <ul>
      <Row label="음메" />
      <li className="item">React</li>
      <li>Redux</li>
      <li>TypeScript</li>
    </ul>
  );
}

function App() {
  return (
    <div>
      <StudyList item="abc" />
    </div>
  );
}

render(<App />, document.getElementById("root"));

이것이 바로 리액트가 돌아가는 방식이다.

실제 리액트에서는 render안에서 실제 DOM과 비교해서 바뀐부분만 반영된다.

 

 

컴포넌트

클래스 컴포넌트

 

클래스 형태의 컴포넌트는 아래와 같다.

 

class  Hello extends React.Component{
  constructor(props) {
    super(props) //받은 props를 react에 전달
  }
  render() {
    //반드시 렌더함수를 구현해야함.
    return (
      <p>Hi</p>
    )
  }
}

//리액트 내부적으로 아래를 실행해줌
//const hello = new Hello();
//hello.render();

function App() {
  return (
    <div>
      <h1>상태</h1>;
      <Hello />
    </div>
  )
}

 

클래스형 컴포넌트는 객체 안에서 값을 유지하고 있을 수 있었기 때문에 자체적으로 상태를 자연스럽게 가질 수 있다.

또한 함수형 컴포넌트는 라이프사이클이 없는 반면, 클래스 컴포넌트는 필요한 부분만 호출을 하여 사용하기 때문에 lifecycle methods가 존재한다.

(예: componentDidMount 등)

 

 

함수형 컴포넌트

 

함수형은 지금까지 위에서 써왔던 형태의 컴포넌트이고, 클래스 컴포넌트보다 간단하고 선언적이다.

초창기 리액트에서 함수형 컴포넌트는 호출시마다 초기화되어 상태를 가질 수 없었다.

그러나 Hooks가 생기면서 함수형 컴포넌트에서도 상태를 가질 수 있게 되었다.

 

Hooks

함수형 컴포넌트에서 상태를 가지게 할 수 있는 스펙이다.

 

useState()

리액트가 제공하는 함수로 기본적으로 배열을 리턴한다.

배열의 첫번째 값은 상태, 두번째 값은 dispatcher 함수가 된다.

function App() {
    const [ counter, setCounter] = useState(1); 
	
    return (
    	<div>
        	<h1 onClick={() => setCounter(coutner + 1 )}>상태 { counter }</h1>
        </div>
    );
}

함수를 호출해도 counter가 리셋되지 않고 값을 유지할 수 있는 이유:

(함수는 컴포넌트를 리턴한다는 것을 리액트가 알고 있으니) hook 계열의 함수를 호출하면 연관된 Hook 전역 배열에 초기값과 값의 index를 담는다. 이후 변경이 발생하면 저장되어있는 값을 가지고 변경 값을 리턴한다.

 

훅 사용시 중요한 것!

*반복문, 조건문, 중첩된 함수에서 hook을 사용하면 안된다.*

 

 

 

*나중에 볼 것

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함