개발자 되는 중/개발 공부

스파르타 리액트 입문

SeonChoco 2022. 12. 14. 21:11
  • node 설치
  • yarn 다운로드
  • 확장팩 prettier 설치 
  • gitbash에 yarn create react-app week-1 입력 (우리는 아직 초보니까 세트로 깔아주었다)
  • vscode에서 터미널 켜고 yarn start 했더니 오류가 났다
yarn : File C:\Users\Seon\AppData\Roaming\npm\yarn.ps1 cannot be loaded because running scripts i
s disabled on this system. For more information, see about_Execution_Policies at https:/goripts i.micros                                                                                   .micros
oft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ yarn start
+ ~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

터미널 창을 gitbash로 켜고 다시 yarn start 했더니 react 창이 성공적으로 켰다. 

 

 

컴포넌트 만들기

기본 형태

const App=()=>{
	return
		<div>
		HTML을 사용하기위한 문법인 JSX를 쓸 수 있는 영역
		</div> 
}

 

컴포넌트 이름 첫글자는 대문자로

폴더이름은 CamelCase로

 

이벤트를 넣어줄 때 함수모양이 다르다. 

 <button onClick={handleclick}>클릭!</button>

 

컴포넌트끼리 연결하기

import React from "react";

function Child() {
  return <div>연결됐지롱</div>;
}

function Father() {
  return <Child />;
}

function GrandFather() {
  return <Father />;
}

function App() {
  return <GrandFather />;
}

export default App;

 

JSX란?

화면에서의 레이아웃 위치, 돔 구조를 나타낼 때 쓴다

Babel이라는 라이브러리가 javascript로 변환해준다.

 

JSX 문법

1. 태그를 안 닫아줄 때 오류

2. 하나 이상의 요소를 넣어줄 때 - <div> </div>로 감싸주지 않을 때 오류

3. javascript를 가져올 때 중괄호 써야한다

ex)  변수 가져올 때, 삼항연산자 써줄 때

function App() {
  const cat_name = "wandu";
  return (
    <div>
      <p>우리 고양이 이름은 {cat_name}입니다</p>
    </div>
  );
}
function App() {
  const number = 10;
  return (
    <div>
      <p>
        {number > 10
          ? number + "은 10보다 크다"
          : number + "은 10보다 작다"}
      </p>
    </div>
  );
}

 

4. class 대신에 className 이라는 것 사용

5.자스처럼 style=color:red, font-size:20px 이렇게 못쓰고 style = {color: 'red', font-size: '20px'}  JSON 형태로 중괄호와 따옴표가 필요함

객체 생김새로 묶어서 쓸 수도 

const styles = {

color: 'red',

font-size:'20px'

}

<p style={styles}> </p> 

 

 

Props, State

 

props:

부모 컴포넌트에서 받아온 모든 데이터

부모컴포넌트에서 자식 컴포넌트로 내려보내기 위해서 쓰는 것

그 데이터는 props 객체 안에 존재한다.

부모 컴포넌트 --> 자식 컴포넌트 단방향으로만 데이터 전달 가능 (자식 컴포넌트들끼리도 데이터 전달 불가능)

function GrandFather() {
  return <Mother />;
}

function Mother() {
  const name = "마덜";
  return <Child motherName={name} />;
}

function Child(props) {
  console.log(props);
  return <div>연결</div>;
}

객체 안에서 motherName에 접근하여 가져와서 이용하기

function Child(props) {
  console.log(props);
  return <div>울 엄마 이름은 {props.motherName}</div>;
}

 

객체분해가 헷갈려서 자바스크립트 기본 doc 에서 다시 보고 왔다

  • 2) 객체 구조분해 할당 코드 비교
  • 구조분해 할당을 사용하지 않을 때
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 {키값} = 객체명 이여야한다 키값 대신에 내 맘대로 넣어줬더니 객체구조분해 할당이 되지 않는다.

 

props 객체 안의 속성을 매개변수로 가져올 때 중괄호를 안해주면 오류가 난다.

function Mother(props) {
  return <Child grandFatherName={props.grandFatherName} />;
}

function Child({ grandFatherName }) {
  return <div>{grandFatherName}</div>;
}

자바스크립트는 동적타입 언어로 변수를 선언할 때 바로 타입을 지정해 줄 필요가 없고

사용하는 쪽에서 동적으로 결정이 된다. 

props를 넘길 때 우리는 아직 타입을 지정해주지 않았다.

해당 타입에 사용할 수 없는 메소드를 만나면 에러를 일으킨다.

prop drilling가 왜 문제가 되는지에 대하여

https://slog.website/post/13

propsType 에 대해 글을 읽어보라고 해서 읽었는데 무슨 말인지 모르겠다 .

 

props children을 이용한 데이터 전달

function App() {
  return <User>앱에서 유저로</User>;
}

function User(props) {
  return <div>{props.children}</div>;
}

내려줄 컴포넌트 이름을 태그로 이용한다.

props는 객체

props children은 객체 안의 속성

 

layout component를 줄 때 자주 사용 (같은 스타일을 주고 싶을 때 사용)

-어떤 식으로 사용하는지 아직 모르겠다.

 

객체분해할당을 이용해서 props.name 아니고 {name}이렇게 쓸 수있다.

아래의 코드가 객체분해 할당을 이용한건가? 

function App() {
  const name = "귀염둥이";
  return <GrandFather grandFatherName={name} />;
}

function GrandFather(props) {
  return <Mother grandFatherName={props.grandFatherName} />;
}

function Mother({ grandFatherName }) {
  return <Child grandFatherName={grandFatherName} />;
}

function Child({ grandFatherName }) {
  console.log(grandFatherName);
  return <div>{grandFatherName}</div>;
}

 

defaultProps 설정 방법 2가지

1.

function App({ name }) {
  return <div>내 이름은 {name} 입니다. </div>;
}

App.defaultProps = {
  name: "기본 이름",
};

 

2. 구조분해할당을 이용해서 객체에 직접 접근한 경우

function App({ name = "기본 이름" }) {
  return <div>내 이름은 {name} 입니다. </div>;
}

 

State: component 내부에서 바뀔 수 있는 값.

Q: 자바스크립트에서 쓰던 것 처럼 let 쓰면 안되나?

A :

값이 변했을 때 화면에 다시 리렌더링 시켜되주기 위한 조건이 있다 - 그 중 하나가 state 변경되었을 때라서

let 안 쓰고 state 쓴다.

 

State를 생성하기 위해 React에서만 존재하고 제공하는 기능이 있다. 이런 기능들을 Hook이라 부른다

const [value, setValue] =  useState(초기값)

 

 

props 와 state는 같다?
부모 컴포넌트가 보낼 때 state 

자식 컴포넌트가 받을 때는 props (properties 소유물)

둘이 가지고 있는 값이 같다.

 

state를 생성해보자

function App() {
  return <GrandFather />;
}

function GrandFather() {
  const [name, setName] = useState("용할아");
  return <Mother grandFatherName={name} setName={setName} />;
}

function Mother(props) {
  return (
    <Child grandFatherName={props.grandFatherName} setName={props.setName} />
  );
}

function Child(props) {
  console.log(props);
  return (
    <div>
      <button
        onClick={() => {
          props.setName("홍할아");
        }}
      >
        할아버지 이름 바꾸기
      </button>
      <div>내 이름은 {props.grandFatherName} 입니다. </div>
    </div>
  );
}

버튼 클릭하면, 용할아에서 홍할아라고 바뀌지만 새로고침하면 초기값인 용할아로 돌아온다.

Q: const name = "용할아"라고 해놓았는데  const 를 이용했는데 어떻게 값을 변경할 수 있었나?

A: 구글링해서 읽어보았지만 이해가 안된다. 

리액트 라이프 사이클이란?

리액트는 컴포넌트 기반의 View를 중심으로 한 라이브러리이다.

그러다보니 각각의 컴포넌트에는 라이프사이클 즉, 컴포넌트의 수명 주기 가 존재한다.

컴포넌트의 수명은 보통 페이지에서 렌더링되기 전인 준비 과정에서 시작하여 페이지에서 사라질 때 끝이난다 .

 

useState를 활용하는 법

1. button

function App() {
  const [name, setName] = useState("강아지");
  const onClickHandler = () => {
    setName("고양이");
    console.log("button 활용한 useState");
  };
  return (
    <div>
      {name}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

2. input

function App() {
  const [value, setValue] = useState("");
  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
    console.log(value);
  };
  return (
    <div>
      <input onChange={onChangeHandler} value={value} type="text"></input>
    </div>
  );
}

 

불변성 (자바스크립트 심화강의에서 배웠던 것)

원시데이터 상수, 변수는 불변성이 있다

참조데이터 객체, 배열, 함수는 불변성이 없다

 

let firstnum = 1

let secondnum = 1

if (firstnum === secondnum) (값을 같은 데이터 공간에서 가져온다)

true

firstnum = 2 (새로운 메모리 저장공간에 2라는 새로운 값을 저장하고 가져온다)

 

let obj_1 = {name : "kim"}

let obj_2 = {name : "kim"}

if (obj_1 === obj_2) (값을 다른 데이터 공간에서 가져온다) 

false

 

obj_1 = {name: "park"} (원래 가지고 있던 데이터 공간에서 값을 바꿔준다 "kim" 에서 "park"으로)

 

React에서 불변성이 중요한 이유

리렌더링 할때 State 변화 유무를 확인 하고 State의 변화를 확인하는 방법이 State 변화 전 후의 메모리 주소를 비교하기 때문이다.

원시데이터는 값을 바꿔주면 메모리 주소도 변하기 때문에 리렌더링에 문제 없다.

참조데이터는 값을 바꿔줘도 메모리 주소가 그대로라서 리렌더링이 안된다. 

 

불변성이 없어서 리렌더링이 안되는 문제해결 방법

전개 구문을 이용해서 값을 복사하고 그 이후에 값을 추가하거나 복사한다.

 

Component 

  • 리액트 핵심 빌딩 블록 중 하나
  • UI요소를 표현하는 최소한의 단위
  • 화면의 특정부분이 어떻게 생길지 정하는 선언체 (JSX로 선언했다)
  • 리액트에서 개발할 모든 어플리케이션은 컴포넌트라는 조각으로 구성된다.
  • 컴포넌트는 UI 구축 작업을 훨씬 쉰게 만들어준다 

 

React 이전 vs React 이후

 

React 이전 

직접 DOM을 조작하는 명령형 프로그래밍

명령형: How (어떻게) 중요

 

React 이후

UI를 선언하고 render 함수를 호출하는 선언형 프로그래밍

선언형 : What (무엇) 중요

우리가 기존에 어떻게 조작할 건지에 써주었던것이  React 내부에 숨겨져 추상화 되어있어 우리눈에는 

무엇을 조작할 것인지만 보인다.

 

렌더링이란?

컴포넌트가 현대 props와 states의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미한다

 

렌더링의 과정을 쪼개고 비유를 들어 설명해보자

component는 주방장

react는 웨이터

1. 렌더링 일으킨다  triggering - 리액트가 UI 주문하고 주방으로 전달 (React app 실행되고 처음에는 자동으로 렌더링 일어남)

2. 렌더링 한다  rendering = 주방에서 컴포넌트가 UI 만들고 준비

3. 렌더링 결과를 실제 DOM에 커밋한다 commit  - 리액트가 준비된 UI를 손님 테이블에 올린다

 

리렌더링이란?

컴포넌트 상태에 변화가 생기면 리렌더링이 발생합니다.

이때 여러 상태가 변경됐다면 리액트는 이를 큐 자료구조에 넣어 순서를 관리합니다. (Queue First In First Out)

[react] 리렌더링이 되는 조건들 살펴보기
  • state 변경이 있을 때 ...
  • 새로운 props이 들어올 때 ...
  • 부모 컴포넌트가 렌더링 될 때 ...
  • shouldComponentUpdate에서 true가 반환될 때 ...
  • forceUpdate가 실행될 때

1. 주방 예시를 다시 들어보면 리렌더링은 음식점 손님이 첫 주문 이후에 갈증이 생겨 추가로 음료를 주문하거나 처음 받은 음식이 마음에 들지 않아 새로운 메뉴를 주문하는 것과 같습니다. triggering

2. 새로운 UI주문(리렌더링)이 일어나면 리액트가 변경된 내용을 주방에 있는 요리사인 컴포넌트에 전달하고 컴포넌트는 새로운 변경된 주문을 토대로 새로운 요리(UI)를 만듭니다. rendering

3. 새롭게 만들어진 요리(렌러딩 결과)는 리액트에 의해 다시 손님 테이블에 올려집니다(DOM에 반영 - commit phase).

브라우저 렌더링

브라우저의 렌더링과 리액트의 렌더링은 엄연히 다른 독립적인 프로세스입니다.

렌더링이 완료되고 React가 DOM을 업데이트한 후 브라우저는 화면을 그립니다.

이 프로세스를 "브라우저 렌더링"이라고 하지만 혼동을 피하기 위해 "페인팅"이라고도 합니다.

 

버튼 누를 때마다 count +1, -1 하게 만들자. 

function App() {
  const [count, setCount] = useState(0);
  const plusCount = () => {
    setCount(count + 1);
  };
  const minusCount = () => {
    setCount(count - 1);
  };
  return (
    <div>
      <button onClick={plusCount}> +1 </button>
      <button onClick={minusCount}> -1 </button>
      <div>{count}</div>
    </div>
  );
}

setCount(++count) 라고 넣었더니 오류가 났다.

Assignment to constant variable. 검색해보니 이 오류는 상수값을 바꾸려고 할 때 나는 오류다.

setCout(count +1)이라고 해주니 오류가 해결되었다.

++count 는 count = count +1 인데 왜 안되는지 모르겠다.

 

익명함수로 써준경우

function App() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          setCount(count - 1);
        }}
      >
        -1
      </button>
      <div>{count}</div>
    </div>
  );
}

컴포넌트 꾸미기

css에 약한데 배워보자.

display: "flex", // 이거 해줘야 요소들을 움직일 수 있나보다
alignItems: "center", // 위 아래 중간에 넣어준다
justifyContent: "center", //좌 우 중간에 넣어준다

display 기본 속성이 block인데, 우리가 flex를 넣어주어서 삭제된 모습이다.  콘솔창 스타일에서 확인할 수 있다.

 

css 파일 분리

js 파일에 있을 때 style 모습

App.css로 분리했을 때 방식은 원래 자바스크립트에서 쓴 것과 같다.

쌍따옴표 없고, 쉽표 대신에 세미콜론

긴 이름들은 -(대시)로 연결

 

className을 넣어서 연결해준다.

 

App.js 를 App.jsx로 바꿔서 사용해도 무방

 

배열 메소드 map을 이용해서 반복되는 부분 없애기

 

기존 코드

function App() {
  return (
    <div className="app-style">
      <div className="square-style">감자</div>
      <div className="square-style">고구마</div>
      <div className="square-style">오이</div>
      <div className="square-style">가지</div>
      <div className="square-style">옥수수</div>
    </div>
  );
}

 

map 이용한 코드

function App() {
  const vegetables = ["감자", "고구마", "오이", "가지", "옥수수", "토마토"];

  return (
    <div className="app-style">
      {vegetables.map((vegetable) => {
        return (
          <div className="square-style" key={vegetable}>
            {vegetable}
          </div>
        );
      })}
    </div>
  );
}

어떤 key를 받아오는지 써주는 방식은 자바스크립트에서는 못 봤다. 

 

객체가 들어있는 배열을  map을 이용해서 꺼내주고, props로 넘겨주는 코드

const User = (props) => {
  return (
    <div className="square-style">
      {props.user.age}살-{props.user.name}
    </div>
  );
};

const App = () => {
  const users = [
    { id: 1, age: 30, name: "송중기" },
    { id: 2, age: 24, name: "송강" },
    { id: 3, age: 21, name: "김유정" },
    { id: 4, age: 29, name: "구교환" },
  ];
  return (
    <div className="app-style">
      {users.map((user) => {
        return <User user={user} key={user.id} />;
      })}
    </div>
  );
};

 

8

 {users.map((user) => {
          return (
            <User handleDelete={deleteUserHandler} user={user} key={user.id} />
          );
        })}

Map을 이용할 때 key값을 넘겨주는 이유

안 넣으면 에러가 난다.

 Each Child in a list should have a unique "key" prop.

React는 map을 사용해서 component를 반복렌더링 할 때 반드시 component의 key를 넣어주어야 한다

map이 가진 index를 key로 넣는 것을 좋지 않다.

Q: 왜 key를 넘겨줘야해?

A: React에서 Component 배열을 렌더링할 때 각각의 원소에서 변동이 있는지 알아내려고 사용하기 때문이다.

- key 값이 있으면 변화가 일어난 곳을  순차적으로 찾지 않고 바로 찾을 수 있어서 React 성능이 최적화 된다.

 

추가, 삭제기능 구현하기

button 컴포넌트 따로 만들어주기

조건에 맞는 것만  불러오기

props로  함수를 넘겨줄 수 있다.

function CustomButton(props) {
  const { color, onClick, children } = props;
  if (color) {
    return (
      <button
        style={{ backgroundColor: color, color: "white", borderRadius: "10px" }}
        onClick={onClick}
      >
        {children}
      </button>
    );
  }
  return <button onClick={onClick}>{children}</button>;
}

const User = (props) => {
  return (
    <div>
      <div className="square-style">
        {props.user.age}살-{props.user.name}
      </div>
      <CustomButton
        color="red"
        onClick={() => {
          props.handleDelete(props.user.id);
        }}
      >
        삭제하기
      </CustomButton>
    </div>
  );
};

const App = () => {
  const [users, setUsers] = useState([
    { id: 1, age: 30, name: "송중기" },
    { id: 2, age: 24, name: "송강" },
    { id: 3, age: 21, name: "김유정" },
    { id: 4, age: 29, name: "구교환" },
  ]);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  const addUserHandler = () => {
    const newUser = {
      id: users.length + 1,
      age: age,
      name: name,
    };
    setUsers([...users, newUser]);
  };

  const deleteUserHandler = (id) => {
    const newUserList = users.filter((user) => user.id !== id);
    setUsers(newUserList);
  };

  return (
    <div>
      <div className="app-style">
        {users.map((user) => {
          if (user.age > 22) {
            return (
              <User
                handleDelete={deleteUserHandler}
                user={user}
                key={user.id}
              />
            );
          } else {
            return null;
          }
        })}
      </div>
      <input
        value={name}
        placeholder="이름을 입력해주세요"
        // 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
        onChange={(e) => setName(e.target.value)}
      />
      <input
        value={age}
        placeholder="나이를 입력해주세요"
        // 인풋 이벤트로 들어온 입력 값을 age의 값으로 업데이트
        onChange={(e) => setAge(e.target.value)}
      />
      <CustomButton color="green" onClick={addUserHandler}>
        추가하기
      </CustomButton>
    </div>
  );
};

 

컴포넌트 분리하기

-여러번 렌더링하여 기능을 재사용하는 Component들은 분리해서 사용

 

분리하지 않으면 생기는 문제

1. App.js 파일 역할이 명확하지 않다

2. component 분리로 가독성을 높혔으나, component가 커지거나 새 컴포넌트가 들어오면 가독성이 떨어진다

3. 어디에 어떤 component가 있는지 찾기 힘들다

 

분리한 파일에 component가 하나만 있을 경우에

export default 컴포넌트 명 해주면 된다.

export default User;

 

분리한 파일에도 사용하는 컴포넌트는 import  해준다. 

User 컴포넌트에는 CustomButton 컴포넌트도 사용하므로 import 해주었다.

 

배포하기

Vercel를 이용해서 배포해보자

'개발자 되는 중 > 개발 공부' 카테고리의 다른 글

CS 특강 - CPU  (0) 2022.12.15
스파르타 알고리즘 - Python 1주차  (0) 2022.12.14
스파르타 React 숙련  (0) 2022.12.14
스파르타 Python 기초  (0) 2022.12.14
스파르타 Javascript 기초  (0) 2022.12.14