코딩 애플 React Part 1

신승준·2022년 7월 2일
0

React를 사용하는 이유

  • Single Page Application을 만들 때 사용한다.

    • 하나의 페이지에서 모바일 앱처럼 동작하는 웹을 만들고 싶을 때, 즉 부드럽게 동작하는 웹을 만들고 싶을 때 React 라이브러리를 사용한다.
  • HTML 재사용이 편리한다.

    • HTML이 많은 큰 사이트에서 재사용이 편리해진다.
  • 비슷한 문법으로 앱개발 또한 가능하다.(React Native)

  • 사실 React는 HTML, CSS, JavaScript를 이용한 웹 개발을 편리하게 해주는 도구일 뿐이다. 따라서 HTML, CSS, JavaScript의 근간은 알고 있어야 한다.




리액트에서 레이아웃 만들 때 쓰는 JSX 문법 3개

JSX(JavaScript XML)

  • JavaScript에서 XML을 추가한, 확장된 문법이다.
  • JSX는 React로 프로젝트를 진행할 시 사용되므로 공식적인 JavaScript 문법은 아니다.
  • 브라우저에서 실행되기 전에, Babel로 인해 일반 JavaScript 형태의 코드로 해석된다.
  • 어쨋든, JSX로 하나의 파일에 JavaScript와 HTML을 동시에 사용할 수 있어 편리하다.

CSS

  • React에서 태그에 class를 부여하기 위해서는 class가 아니라 className이라고 적어줘야 한다.
  • HTML이 아니라 결국엔 JavaScript 문법이기 때문이다.
<div className="black-nav">
    <h4>I'm Blog</h4>
</div>
  • 태그 안에다가 CSS 작성 시, 다음과 같이 중괄호 {}안에, object를 다루듯이 또 {}가 필요하다.
  • 그리고 id, className 등 어디서든 중괄호 {}를 통해 변수를 대입할 수 있다.
<h4 id={post} style={{ color: "red" }, { fontSize: '16px' } }>
    I'm Blog
</h4>
  • 폰트 사이즈도 font-size가 아니라 fontSize처럼 카멜 케이스로 작성해줘야 한다. JavaScript 문법이기 때문이다. -로 해놓으면 진짜 마이너스 -로 인식한다.

오류

  • 코드를 잘못 짜면, 오타를 내는 등. React는 브라우저 혹은 터미널에다가 오류 메세지를 출력해준다.
    • 물론 브라우저의 console에도 오류 메세지가 출력된다.



중요한 데이터는 변수가 아닌 State에

  • 다음과 같이 병렬로 2개 이상의 태그를 사용할 경우, React에서는 에러를 낸다.
return (
	<div></div>
	<div></div>
);



  • 다음과 같이 작성하여 state에 원하는 데이터를 저장할 수 있다.
import { useState } from "react";

    let [title, setTitle] = useState('남자 코트 추천');
    // a는 state에 보관한 자료, b는 state 변경을 도와주는 함수이다.
  • a는 state에 보관한 자료, b는 state 변경을 도와주는 함수이다.



  • Destructuring(자바스크립트 문법)
// let [title, setTitle]과 같은 문법은 자바스크립트에서 Destructuring이라는 문법이다.
let [a, b] = [1, 2];



  • 위에서 useState('남자 코트 추천')도 결국엔 Array 형태로 남는다.
    • ['남자 코트 추천', 함수]
  • 변수가 따로 있는데, state라는 문법을 왜 사용할까?




let post = "강남 우동 맛집";
  • 만약 위에서 post를 "역삼 우동 맛집"으로 바꾸려고 하면, 중간에 post = "역삼 우동 맛집"을 하면 되긴 한다.
    • 그러나 HTML에 자동으로 변경되지 않는다. 즉 렌더링이 자동으로 일어나지 않는다.
    • 반면에 state를 사용하면 자동으로 HTML 전체를 자동으로 재렌더링해준다.
    • 즉 state의 변수만 변해도 자동으로 HTML에 반영이 된다.
    • 변동 시 자동으로 html에 반영되게 만들고 싶으면 state를 사용하면 된다.



let [title, setTitle1] = useState(['남자 코트 추천', '강남 우동 맛집', '파이썬 독학']);
  • 위와 같이 배열로 state를 저장할 수도 있다.



/* eslint-disable */
  • 위와 같은 주석을 쓰면 터미널의 warning 문구들을 무시할 수 있다.



버튼에 기능 개발 및 state 변경

  • 좋아요 버튼 및 갯수 UI 만들기
{/* HTML 요소를 '클릭'했을 때 자바스크립트 코드가 동작하도록 해주는 것이 onClick이다. */}
{/* 대신 onClick 안에는 항상 '함수 이름'을 넣어야 한다. */}
<h4>{title[0]} <span onClick={func}> 👍 </span> {good} </h4>
{/* 밑의 경우와 같이 함수를 바로 만들어 넣어도 상관 없다. arrow function */}
{/* <h4>{title[0]} <span onClick={() => {console.log(1)}}> 👍 </span> {good} </h4> */}
<p>{date}</p>
  • onClick 안에 함수를 넣어서, 해당 HTML 요소를 클릭했을 때 우리가 원하는 함수가 실행되도록 할 수 있다.
    • 그것이 만약 state 함수라면, state를 변경시키고 HTML을 재렌더링할 것이다.



<h4>{title[0]} <span onClick={() => {setGood(good + 1)}}> 👍 </span> {good} </h4>
  • 기존의 good에서 +1을 해나간다. 따라서 따봉을 누를 때마다 good은 1씩 증가하며, state 변경 함수를 사용하는 것이니 재렌더링된다.
  • state 변경 시에는 이렇게 항상 state 변경 함수를 사용해야 한다.



array, object, state 변경

<button
    onClick={() => {
        let copy = [...title];
        copy[0] = "여자 코트 추천";
        setTitle(copy);
    }}
>
    Title 수정
</button>
  • state 동작 방식

    • state 변경 함수가 실행되면, 기존 state랑 변경되는 state(state 변경 함수의 인자)를 비교한다.
    • 같으면 state를 변경하지 않는다.(overhead 절약?)
    • 다르면 state를 변경한다.
  • 만약 위의 코드를 다음과 같이 썼다면,

    • copy가 바뀌면 title도 바뀐다. 따라서 state 변경 함수가 실행되어도 둘 다 같으니 재렌더링 되지 않는다.
    • 밑과 같이 작성 시 copy, 즉 포인터의 값은 title과 같아진다. 즉 둘다 같은 메모리 공간을 가르키는 포인터이다.
      • 그 말은 copy와 title 둘 다 저장하고 있는 주소값이 같다는 것이다.
      • 그러면 setTitle에서 기존 포인터(주소값)와 변경되는 포인터(주소값)이 같으므로, state 변경은 일어나지 않는다.
      • 따라서 주소값이 달라질 수 있도록, 다른 메모리 공간을 가르킬 수 있도록 deepcopy를 해줘야 한다.
<button
    onClick={() => {
        let copy = title;
        copy[0] = "여자 코트 추천";
        setTitle(copy);
    }}
>
    Title 수정
</button>

  • copy1은 주소값이 복사되어서 true가 나온다.
  • copy2는 arr의 대괄호를 벗겨서 1, 2, 3을 만들고, 이를 다시 대괄호를 씌우는 것이니, 엄밀히 말하면 arr을 그대로 복사하는 것이 아니다. arr의 원소값만 가지고 와서 다시 대괄호를 씌우는 것이니 새로운 메모리 공간을 필요로 한다.
    • 따라서 copy2는 새로운 메모리 공간에 생긴 [1, 2, 3]을 가르키는 주소값을 가지게 된다. 즉 포인터가 된다.
let arr = [1, 2, 3];
let copy1 = arr;
let copy2 = [...arr];

console.log(arr === copy1);     // true
console.log(arr === copy2);     // false

  • 가나다순 정렬 버튼 구현
<button onClick={() => {
    let copy = [...title];
    copy = copy.sort();
    setTitle(copy);
}}>가나다순 정렬</button>



Component

  • 모달창 뜨게 만들어보기
  • HTML을 막 쓰는 것은 더러워 보이게 만든다.
  • 이를 축약할 수 있는게 component이다.
{/* Modal */}
<div className="modal">
    <h4>제목</h4>
    <p>날짜</p>
    <p>상세 내용</p>
</div>

위의 코드를

<Modal></Modal>

이렇게 간단하게 만들어주는 것이 component이다.

Component를 만드는 3 스텝

  1. function 만들기(다른 곳에, 즉 <함수명></함수명>이 속해 있는 function의 밖에다가 선언)
  2. return 문의 ()안에 HTML 담기
  3. component를 쓸 곳에 <함수명></함수명>으로 쓰기(앞 문자는 대문자로)
  • 주의할 점으로는, 다음 코드와 같이 병렬적으로 태그를 짜면 안된다. 하나의 컴포넌트안에는 하나의 큰 태그만 써줘야 한다.
function Modal() {
    return (
        <div className="modal">
            <h4>제목</h4>
            <p>날짜</p>
            <p>상세 내용</p>
        </div>
        <div>
            <h4>이렇게 병렬적으로 하면 안됨!</h4>
        </div>
    );
}
  • fragment 태그를 사용하면 가능해진다.
function Modal() {
    return (
        <>
            <div className="modal">
                <h4>제목</h4>
                <p>날짜</p>
                <p>상세 내용</p>
            </div>
            <div>
                <h4>이렇게 fragment를 써서 병렬적으로 짤 수도 있다.</h4>
            </div>
        </>
    );
}

언제, 어떤 것을 컴포넌트로 만들면 좋은가

  • 반복적으로 HTML이 나올 때, 이를 축약해주면 효율적이다.
  • 큰 페이지들을 컴포넌트 하나로 만들면 효율적이다.
  • 자주 변경될 것 같은 것들(state가 자주 들어가는 것들)

  • 이렇게 작성해도 컴포넌트를 만들 수 있다.
// const로 만들면
// Modal = 123; 등 재할당하려고 하면 에러를 출력해준다.
// 에러를 잡기 쉬워서 const로 자주 사용하곤 한다.
// 밑에 처럼해도 똑같이 컴포넌트를 만들 수 있다.
const Modal = () => {
    return (
        <>
            <div className="modal">
                <h4>제목</h4>
                <p>날짜</p>
                <p>상세 내용</p>
            </div>
            <div>
                <h4>이렇게 fragment를 써서 병렬적으로 짤 수도 있다.</h4>
            </div>
        </>
    );
};



리액트 환경에서 동적인 UI 만들기(모달창)

  • 숨겼다가 제목을 누르면 모달창이 나오게 만들기

동적인 UI를 만드는 3 Step

  1. HTML, CSS로 미리 디자인을 완성한다.
  2. UI의 현재 상태를 state로 저장한다.
  3. state에 따라 UI가 어떻게 보일지 작성한다.(조건문 등을 사용해서)
  • HTML 중간에 조건문을 작성하려면 삼항 연산자 ?를 사용한다.
  • 비어있는 HTML 내용으로는 null을 자주 사용한다.
  • state를 변경하고 싶으면 무조건 state 변경 함수를 사용한다.

<div className="list" onClick={() => {
    if (modal === false) {
        setModal(true);
    } else {
        setModal(false);
    }
}}>
    <h4>{title[2]}</h4>
    <p>{date}</p>
</div>

{/* Modal */}
{/* 여기는 HTML 작성 공간이라서 JavaScript의 if문 등을 쓸 수 없다. 근데 삼항 연산자는 가능하다. */}
{
    modal === true ? <Modal></Modal> : null
}
  • modal state가 true이면 모달창이 보이게 한다.
  • false이면 true로 보이게 만들고, true이면 false가 되게 하여 토글 방식 구현.
<div
    className="list"
    onClick={() => {
        setModal(!modal);
    }}
>



map을 이용해 반복 줄이기

  • map 등의 반복 문법을 통해 HTML을 작성할 경우 아래와 같은 경고 문구가 뜬다.
  • 이는 반복으로 생긴 각 HTML을 식별할 수 있는 key라는 속성이 필요하다는 뜻이다. 다음과 같이 HTML마다 key값을 다르게 주면 된다.
{title.map(function (element, index) {
    return (
        <div className="list" key={index}>
            <h4>{title[index]}</h4>
            <p>{date}</p>
        </div>
    );
})}

  • for문으로 HTML 반복하기
    • 먼저 function으로 반복되는 HTML을 array 자료구조에 담는다.
    • 해당 HTML을 쓸 곳에 function 태그를 적는다.
function For() {
    let arr = Array();
    
    for (let i = 0; i < 3; i++) {
        arr.push(<div>for문으로 HTML 반복</div>);
    }
    
    return (
        <div>
            {arr}
        </div>
    )
}

...

{/* <For></For> */}

  • 각 title마다 좋아요가 다르게 올라가도록 하기
let [good, setGood] = useState([0, 0, 0]);

...

{title.map(function (element, index) {
    return (
        <div className="list" key={index}>
            <h4 onClick={() => setModal(!modal)}>{title[index]}</h4>
            <span onClick={() => {
                let newGood = [...good];
                newGood[index] += 1;
                setGood(newGood);
            }}> 👍 </span> {good[index]} 
            <p>{date}</p>
        </div>
    );
})}



자식이 부모의 state를 쓰고 싶을 때는 props

  • function 안에서의 변수는 다른 곳에서 사용할 수 없다.
  • 부모의 state를 자식이 쓰려면 부모의 state를 전송하면 된다.
<Modal title={title}></Modal>

...

function Modal(props) {
    return (
        <div className="modal">
            <h4>{props.title}</h4>
            <p>날짜</p>
            <p>상세 내용</p>
        </div>
    );
}
  • props 전송은 부모에서 자식으로만 가능하다.
    • 자식의 state를 부모로, 자식에서 자식으로 보내는 것은 불가능하다.
    • 자식에서 자식



props를 응용한 상세페이지 만들기

let [date, setDate] = useState("2월 17일 발행");

...

{title.map(function (element, index) {
    return (
        <div className="list" key={index}>
            <h4 onClick={() => {
                setModal(true);
                setTitleIndex(index);
            }}>{title[index]}</h4>
            <span onClick={() => {
                let newGood = [...good];
                newGood[index] += 1;
                setGood(newGood);
            }}> 👍 </span> {good[index]} 
            <p>{date}</p>
        </div>
    );
})}

***

input 1: 사용자가 입력한 글 다루기

  • onChange, onInput
    • 두 속성 모두, input 요소에 사용자가 입력을 할 때마다 해당하는 코드를 실행해준다.
  • onMouseOver
    • 이 태그에 마우스를 갖다댔을 때마다 코드를 실행해준다.
  • onScroll
    • 이 태그에 스크롤이 있고, 스크롤을 움직일 때마다 코드를 실행해준다.
  • 이벤트 발생 시 버블링 막기
    • propagation : 번식
event.stopPropagation();

  • 비동기처리
    • 밑의 코드를 적어두고 input란에 값을 적으면, 맨 처음 글자는 빈 칸으로 출력되는 것을 볼 수 있다.
    • 2번째부터는 처음 글자만 찍히는 것을 볼 수 있다.
    • React에서 state를 변경하는 것은 작업 시간이 오래 걸린다.
    • 따라서 그 작업이 실행되는 동안에, 다음 작업도 실행되게 하여 효율적으로 시간을 줄이려고 한다.
    • 즉, 다음 줄인 console.log가 실행된다.
    • 결론적으로는 state의 변경이 완료되기 전에 console.log가 먼저 찍히게 된다.
<input
    onChange={(event) => {
        // console.log(event.target);
        setInputValue(event.target.value);
        console.log(inputValue);
    }}
></input>



input 2: 블로그 글 등록 및 글 삭제

  • new Date()를 활용해 글을 등록한 시간도 추가



최종 코드

  • App.js
/* eslint-disable */

import logo from "./logo.svg";
import "./App.css"; // src 폴더에서 App.css 파일을 사용하겠다.
import { useState } from "react";

function App() {
    let post = "강남 우동 맛집";
    // 바닐라 자바스크립트를 썼을 때의 방식
    // document.querySelector('#temp').innerHTML = post;

    let [title, setTitle] = useState([
        "남자 코트 추천",
        "강남 우동 맛집",
        "파이썬 독학",
    ]);
    // a는 state에 보관한 자료, b는 state 변경을 도와주는 함수이다.

    let [good, setGood] = useState([0, 0, 0]);
    let [modal, setModal] = useState(false);
    let [titleIndex, setTitleIndex] = useState(0);
    let [inputValue, setInputValue] = useState('');

    let [logo, setLogo] = useState("React Blog");
    
    let temp = new Date();
    // temp = temp.getFullYear() + '년 ' + (temp.getMonth() + 1) + '월 ' + temp.getDate() + '일';
    temp = temp.toLocaleString();
    let [date, setDate] = useState([temp, temp, temp]);

    // let [title, setTitle]과 같은 문법은 자바스크립트에서 Destructuring이라는 문법이다.
    // let [a, b] = [1, 2];

    return (
        <div className="App">
            <div className="black-nav">
                {/* 사실 사이트 제목과 같은 것들은 state로 저장할 필요는 딱히 없다. 따라서 {logo}로 하기 보다는 그냥 React Blog라고 해주는 것이 오히려 좋을 수 있다. */}
                <h4 style={{ color: "red", fontSize: "16px" }}>{logo}</h4>
            </div>
            <div className="nav-button">
                <button
                    className="button"
                    onClick={() => {
                        let copy = [...title];
                        copy = copy.sort();
                        setTitle(copy);
                    }}
                >
                    가나다순 정렬
                </button>
                <button
                    className="button"
                    onClick={() => {
                        let copy = [...title];
                        copy[0] = "여자 코트 추천";
                        setTitle(copy);
                    }}
                >
                    Title 수정
                </button>
            </div>
            {/* 단순히 중괄호 {} 안에 변수를 작성하면서, 바닐라 자바스크립트만 사용했을 때보다 더 편리하게 작성이 가능하다. */}
            <h4 id="temp">{post}</h4>

            {/* <div className="list"> */}
            {/* 좋아요 갯수도 자주 바뀌니 state로 바꿔보자. */}
            {/* HTML 요소를 '클릭'했을 때 자바스크립트 코드가 동작하도록 해주는 것이 onClick이다. */}
            {/* 대신 onClick 안에는 항상 '함수 이름'을 넣어야 한다. */}
            {/* <h4>
                    {title[0]}{" "}
                    <span
                        onClick={() => {
                            setGood(good + 1);
                        }}
                    >
                        {" "}
                        👍{" "}
                    </span>{" "}
                    {good}{" "}
                </h4> */}
            {/* 밑의 경우와 같이 함수를 바로 만들어 넣어도 상관 없다. arrow function */}
            {/* <h4>{title[0]} <span onClick={() => {console.log(1)}}> 👍 </span> {good} </h4> */}
            {/* <p>{date}</p> */}
            {/* </div> */}
            {/* <div className="list">
                <h4>{title[1]}</h4>
                <p>{date}</p>
            </div> */}
            {/* <div
                className="list"
                onClick={() => {
                    setModal(!modal);
                }}
            >
                <h4>{title[2]}</h4>
                <p>{date}</p>
            </div> */}

            {/* map을 활용해 HTML 반복 */}
            {/* 중괄호 안에서는 for문 사용이 불가하여 map을 사용한다. */}
            {title.map(function (element, index) {
                return (
                    <div className="list" key={index}>
                        <h4
                            onClick={() => {
                                setModal(true);
                                setTitleIndex(index);
                            }}
                        >
                            {title[index]}
                            <span
                                onClick={(event) => {
                                    event.stopPropagation();
                                    let newGood = [...good];
                                    newGood[index] += 1;
                                    setGood(newGood);
                                }}
                            >
                                {" "}
                                👍{" "}
                            </span>{" "}
                            <span onClick={(event) => {
                                event.stopPropagation();
                            }}>{good[index]}{" "}</span>
                        </h4>
                        <p>{date[index]}</p>
                        <button onClick={() => {
                            let newTitle = [...title];
                            let newGood = [...good];
                            let newDate = [...date];
                            newTitle.splice(index, 1);
                            newGood.splice(index, 1);
                            newDate.splice(index, 1);
                            setTitle(newTitle);
                            setGood(newGood);
                            setDate(newDate);
                        }}>삭제</button>
                    </div>
                );
            })}

            <input
                onChange={(event) => {
                    setInputValue(event.target.value);
                }}
            ></input>
            <button onClick={() => {
                if (inputValue === '') {
                    alert('제목을 입력해주세요.');
                    
                    return;
                }
                
                let temp = new Date();
                temp = temp.toLocaleString();
                
                let newTitle = [...title];
                let newGood = [...good];
                let newDate = [...date];
                newTitle.unshift(inputValue);
                newGood.unshift(0);
                newDate.unshift(temp);
                setTitle(newTitle);
                setGood(newGood);
                setDate(newDate);
            }}>글 등록</button>

            {/* for문을 활용해 HTML 반복 */}
            {/* <For></For> */}

            {/* Modal */}
            {/* 여기는 HTML 작성 공간이라서 JavaScript의 if문 등을 쓸 수 없다. 근데 삼항 연산자는 가능하다. */}
            {modal === true ? (
                <Modal
                    index={titleIndex}
                    setTitle={setTitle}
                    color="skyblue"
                    title={title}
                ></Modal>
            ) : null}
        </div>
    );
}

function Modal(props) {
    return (
        <div className="modal" style={{ background: props.color }}>
            <h4>{props.title[props.index]}</h4>
            <p>날짜</p>
            <p>상세 내용</p>
            <button
                onClick={() => {
                    let newTitle = [...props.title];
                    newTitle[0] = "여자 코트 추천";
                    props.setTitle(newTitle);
                }}
            >
                Title 수정
            </button>
        </div>
    );
}

// function For() {
//     let arr = Array();

//     for (let i = 0; i < 3; i++) {
//         arr.push(<div>for문으로 HTML 반복</div>);
//     }

//     return (
//         <div>
//             {arr}
//         </div>
//     )
// }

export default App;

class를 이용한 예전 React 방식

  • 예전 react에서는 component 1개를 만들기 위해 다음과 같이 사용해야 했다.
    • 최근에는 function을 쓰게 되면서 extends, constructor, super, render 등의 문법을 쓰지 않아도 되기 때문에 코드가 간단해졌다.
function App(){
  return (
    <div>
      HTML 잔뜩있는 곳
      <Profile />
    </div>
  )
}


class Profile extends React.Component {
  constructor(){
    super();
  }

  render(){
    return (
      <div>프로필영역입니다</div>
    )
  }
}
profile
메타몽 닮음 :) email: alohajune22@gmail.com

0개의 댓글