soju 스터디 5주차 - React 내용 정리

thousand_yj·2023년 1월 31일
0

soju 스터디

목록 보기
5/7

CRA (create-react-app)

리액트 프로젝트를 시작하는 가장 쉬운 방법은 create-react-app을 사용한 방법이리라 생각한다.
계속하여 최선 버전으로 업데이트되도록, npx 명령어를 사용하여 생성한다.

// npx create-react-app *myapp
npx create-react-app test

*myapp 자리에 원하는 프로젝트 명을 적고 실행하면, 입력한 프로젝트 명으로 폴더가 하나 만들어져 있다.
해당 폴더를 실행하면, localhost:3000 으로 가장 기본적인 react 페이지가 뜬다.

여기서 이제 src폴더 내에서 App.js index.js index.css 를 제외한 파일은 다 지우고 프로젝트를 시작했다. 당장은 필요없기도 하고, 많은 리액트 강의에서도 다 지우더라!

index.js 파일을 열어보면 다음과 같다. (버전별로 차이는 있지만 신경X)

import ReactDOM from 'react-dom';

import './index.css';
import App from './App';

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

리액트 자체는 js의 확장판이기 때문에, 기본적으로 js 문법을 따라간다. 다만 기본 js에서는 지원하지 않는 문법이 위 파일에서 2개 보인다.

import './index.css'

보통 js 파일에서는 위와 같이 import하지 않는다.

<App />

js 코드 내의 HTML 코드이다. (JSX)

위와 같이 React가 도입한 문법은, npm run 명령어를 통해 실행되어, 개발자가 원하는 방향대로 화면에 뿌려진다. 원래대로라면 작동하지 않을 문법이 react로 인해, 브라우저로 전달되기 전 transform 되어 원하는 모습을 보여주는 것이다.

그리고 또 하나 기억해야 할 것은, index.js 파일은 가장 먼저 실행되는 파일이라는 것이다.

index.js가 최종적으로 실행하는 것은 다음이다.

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

App이라는 것은 위에서 App.js를 import한 component이다.
(third-party library나 js파일을 import할 때는 뒤의 확장자를 빼고 import한다)

컴포넌트란?

쉽게 말하자면, UI 상에서 다시 사용할 수 있는 building block이다. 단순히 표시해줘야하는 데이터만 바뀌고 UI는 그대로라면, 굳이 여러번 동일한 코드를 반복해서 작성할 필요 없이, 하나의 block을 만들고, 그 안의 데이터만 넘겨주는 방식이다.
Component 자체는 쉽게 말하자면, Html+js (+ css) 덩어리다. (css를 괄호처리한 이유는 리액트 상에서는 크게 중요한 개념이 아니라서다.) 각각의 UI 상에서 모든 걸 쪼개서 component로 구성할 수도 있다. 따라서 개발자는 UI를 구성하는 모든 요소를 쪼개서 component로 만든 뒤, 최종적으로 어떻게 UI를 그릴지 React에게 구성도를 던져주기만 하면 된다. (재사용성에 너무 집착할 필요는 없다.)

js와 JSX 문법 차이

기본적인 js 문법과의 차이를 한번은 짚고 넘어가는 것이 좋을 것 같아 추가한다.

function App() {
	return(
		<div>
    		<h2>Hello world!</h2>
        	<p>my cat is rockstar</p>
    	</div>
    );
}

위의 App.js 파일에서 p 태그 하나를 추가하고 싶으면 기본 리액트 문법은 위와 같이 작성하면 된다. 하지만 기본 js 문법에 따르면 다음과 같이 작성해야한다. (imperative approach로, js에게 명료하게 하나하나 어떻게 해야할지 전달한다.)

function App() {
	const paragraph = document.createElement('p');
    paragraph.textContent = 'my cat is rockstar';
    document.getElementById('root').append(paragraph);
	return(
		<div>
    		<h2>Hello world!</h2>
    	</div>
    );
}

단순히 코드만 봐도, 리액트가 좀 더 짧고 간결하다.

JSX란?

JSX란 JavaScript에 XML을 추가한 것으로, 브라우저로 전달되기 전에 브라우저가 이해하기 쉽게 한번 변환되어 전달된다. 실제로 react 프로젝트를 개발자도구에서 살펴보면 작성한 적 없는 코드들을 볼 수 있다.

Event

일반적인 JS 방식에서 해당 element를 찾아 addEventListener를 만들어주거나 onXXX 속성을 사용하여 이벤트를 처리해줬다. 리액트 역시 비슷하게 JSX 내에서 특별한 prop을 사용하여 이벤트를 들을 수 있다. on으로 시작하는 속성은 모두 이벤트와 관련된 요소이다.

<button onClick={}>버튼</button>

위와 같이 onClick 속성에 넘겨줘야 하는 값은 함수의 이름이다.

const clickHandler = () => console.log("clicked!);

흔히 할 수 있는 실수는 다음과 같다. 이 코드는 파싱되는 즉시 함수 실행이 되어버린다. 즉 JSX 코드가 읽히는 순간에 실행될 것이다.

<button onClick={clickHandler()}>버튼</button>

버튼을 클릭할 때에만 해당 함수를 실행하고 싶기 때문에 함수를 실행( () 붙이기) 하는 것이 아니라, 함수의 이름만 넘겨줘야 한다.

<button onClick={clickHandler}>버튼</button>

state

Component 함수는 어떻게 실행되는가?

이벤트에서 화면에 띄우는 변수를 바꾸더라도 화면은 바뀌지 않는다. 왜 바뀌지 않을까?

Component 자체가 하나의 함수임을 기억한다면 화면이 바뀌지 않는 것은 당연하다. JSX 코드를 리액트가 읽는 순간 그 안에 있는 컴포넌트 함수를 호출한다. 더이상 호출해야 하는 함수가 없을 때까지 리액트는 JSX 코드를 읽어가며 함수를 모두 호출할 것이다. 함수가 모두 호출된 후에는 해당 코드를 브라우저의 DOM instruction으로 번역하여 브라우저로 넘겨준다.

리액트는 위와 같이 작동하며, 여기에서 유의할 점은 JSX 코드를 읽는 작업을 리액트가 반복하지 않는다는 것이다. 그렇다면 화면을 업데이트하고 싶을 때는 어떻게 해야할까?

여기에서 state라는 리액트에서 사용하는 개념이 등장한다.
특정 데이터가 바뀔 때 리액트에게 해당 부분을 다시 읽어오도록 알려줄 수 있는 속성이 바로 state이다. 이를 사용하려면 react 라이브러리에서 useState라는 요소를 import해야 한다.

use로 시작하는 요소들은 React Hook이다. react hook은 반드시 리액트 컴포넌트 함수 내에서만 사용이 되어야 한다. 컴포넌트 함수 내에 다른 함수로 중첩되어서는 안되며, 반드시 컴포넌트 함수 바로 안에서만 위치해야 한다. (예외사항이 있기는 하나 그 부분은 추후에 다루겠다)

useState함수는 초기값을 인자로 넘겨줄 수 있으며, state 변수와 그 변수를 수정할 수 있는 함수 2개를 배열로 리턴한다. 따라서 useState의 기본적인 사용 방식은 다음과 같다.

const [value, setValue] = useState(data);

state에 값을 할당할 때는 '=' 연산자를 이용하는 것이 아니라, setState 함수를 사용하여 값을 할당한 뒤 리액트에게 해당 값이 수정되었으니 해당 컴포넌트를 재실행하라고 알려준다. 즉, 업데이트가 가능하다는 것이다!

useState 뜯어보기

state가 컴포넌트 내에 정의되어 있고 해당 컴포넌트가 여러 번 호출된다면, 각각의 state는 component별로 고유하다. 어떤 컴포넌트의 state 값을 바꾼다고 다른 요소가 영향을 받지 않는다는 것이다.

자 그러면 왜 계속하여 바뀌는 값인 state를 const형식으로 할당하였을까? state를 업데이트할 때 '=' 연산자를 사용하지 않고 set___ 함수를 사용하여 값을 업데이트하기 때문이다. useState로 전달된 초기값은 말 그대로 가장 처음 state를 초기화할 때만 사용되며 그 이후는 계속하여 업데이트된 최신 값만 가져온다.

state와 이벤트를 함께 잘 사용한다면 얼마든지 UI를 업데이트할 수 있을 것이다.

props

component 간에 데이터를 주고 받기 위해 사용한다. 컴포넌트 함수에게 매개변수 형식으로 넘겨준다. props로 넘겨줄 수 있는 데이터는 다양한다. state를 props로 넘겨줄 수도 있다!

Routing

react-router-dom 라이브러리를 사용하여 url에 따라 다른 페이지를 보여줄 수 있다. 보통 가장 Root단에 있는 App 컴포넌트에서 라우팅을 처리해주는데, 가장 바깥을 BrowserRouter로 감싸준 뒤 라우팅할 경우에 따라 Switch를 사용하여 정확히 어떤 경로로 데이터가 들어왔을 때 어떤 컴포넌트를 보여줄지 결정한다. 경로를 넘겨줄 때는 Route를 사용한다.

나는 보통 App.js에서 라우팅 관련 부분을 보여주고 index.js에서 BrowserRouter로 다음과 같이 처리한다.

function App() {
  return (
    <div>
      <Header />
      <Route path="/home">
        <Home/>
      </Route>
      <Route path="/login">
        <Login/>
      </Route>
    </div>
  )
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

useEffect

보다 잘 이해하기 위해 그림으로 정리해보았다.

profile
함께 일하고 싶은 개발자가 되기 위해 노력합니다. 코딩테스트 관련 공부 및 이야기는 티스토리에도 업로드되어 있습니다.

0개의 댓글