관리 메뉴

CASSIE'S BLOG

[리액트 복습] 강의 별로 주요 포인트 정리한 것 본문

PROGRAMMING/React

[리액트 복습] 강의 별로 주요 포인트 정리한 것

ITSCASSIE1107 2023. 9. 3. 11:11

17강

 

 

creact-react-app은 명령어다.
여기서 my-app은 여러분들의 "어플리케이션" 이름이라고함.
-로 연결한걸 케밥케이스라고함.
소문자 + 하이픈 + 소문자 이런 식으로 이름 지어야된다고함.
이걸 그냥 터미널에서 하면 된다. 

이것도 모르고 있었네;;

 

 

그 다음에 프로젝트를 만들었기 때문에 그 경로로 
cd 해서 프로젝트 경로로 먼저 들어가야한다!! 

 

 

 

19-1강

 

 

 

 className 반드시 카멜케이스여야한다고 설명해주네. 

 

 

 

꺽쇠문자열은 반드시 중괄호로 감싸야한다.

 

{`tost toast-${props.title}`} 

 

 

 

20강

css파일은 js파일과 같은 위치에 둔다. 

 

 

 

Attribute를 만든다. 

 

 

 

 

 

 

이게 방법1

 

인자로 index도 가져올 수 있단다. 

 

message 가 왜 첫번쨰 인자인지 이해를 못해서 다른 블로그에서 찾아옴

 

https://we1cometomeanings.tistory.com/377

 

[React] to-do list를 만들어보자!

기본 setting 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function App(){ const[toDo,setToDo]=useState(""); const onChange=(event)=>setToDo(event.target.value); const onSubmit=(event)=>{ event.preventDefault() if(toDo===""){ return; }el

we1cometomeanings.tistory.com

 

 

map을 사용해서 배열의 원소들을 불러오자!

 

이제 이 배열에 담긴 원소들에 해당하는 component를 만들어볼 것이다!

 

map은 자바스크립트 함수인데 array각각의 element들을 바꾸고 싶을 때가 있을거다.

예를 들어

['fun','cool''good','pretty']라는 배열이 있을 때

['fun','cool''good','pretty']].map()을 해주면 ()<--요 괄호 안에 함수를 넣을 수 있게 해주는데

이 함수는 array의 모든 item에 대해 실행된다.

그리고 return한 값이 새로운 array에 들어간다.

배열의 원소의 개수인 4 만큼 map함수가 실행이 되고 "^^"가 4개인 새로운 배열이 만들어진다.

그런데 이러면  기존의 아이템들 fun,cool,good,pretty에는 접근할 수 없게 되었다!

map은 함수의 첫번째 인자로 현재의 item을 가져올 수 있다!

 

즉, map함수의 첫번째 인자는 진행되고 있는 순서에 맞는 item이라는 것이다. 

예를 들어 첫번째 순서의 첫번째 인자는 fun이 될 것이다.

요렇게 첫번째 인자로 현재 순서의 item을 받아오고 그 item을 대문자로 리턴해볼 수도 있겠다.

$와 백틱을 통해서 귀여운 배열을 새로 생성해볼 수도 있다!

 

 map은 예전 array를 가져와서 변형하는 역할을 한다! 

 

 

<hr> 태그를 통해 수평선을 그어주고 map함수를 사용해서 item을 li태그에 담아주면 

 

짜잔!

 

근데 살짝 문제가 생겼다.

같은 컴포넌트의 list를 랜더할때 key라는 prop을 넣어줘야 한다는 에러가 떴다.

 

https://velog.io/@green9930/React-%EB%B0%B0%EC%97%B4%EC%97%90%EC%84%9C-key-%EA%B0%92%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0

 

React 배열에서 key 값이 필요한 이유

key 엘리먼트 리스트를 만들 때 포함해야 하는 특수한 문자열 어트리뷰트 리액트가 어떤 항목을 변경/추가/삭제할지 식별하는 것을 돕는다. key값 지정하기 key값은 고유하게 식별 가능한 문자열

velog.io

 

map함수에 대한 문서를 읽어보면 첫번째 인자는 value여야 한다.(toDo)

두번째 인자는 index이다. (0,1,2,3,4같은 배열의 인덱스를 의미한다. 

li 앨리먼트에 속성으로 key값을 넣어줘서 고유한 인덱스를 갖도록 하면 에러가 해결된다. 

 

최종코드

 

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
29
30
31
32
33
function App(){
 
  const[toDo,setToDo]=useState("");
  const[toDos,setToDos]=useState([]);
  const onChange=(event)=>setToDo(event.target.value);
  const onSubmit=(event)=>{
    event.preventDefault()
    if(toDo===""){
      return;
    }
    setToDos((currentArray)=>[toDo,...currentArray]);
    setToDo("");
  };
  console.log(toDos);
  return <div>
    <h1>My To Dos ({toDos.length})</h1>
    <form onSubmit={onSubmit}>
      <input onChange={onChange}
      value={toDo} 
      type="text"
      placeholder="Write your to do ..."
      />
      <button> Add To Do</button>
    </form>
    <hr/>
    <ul>
      {toDos.map((item,index)=>
      <li key={index}>{item}</li>)}
    </ul>
    
    
  </div>
}
cs

 

 


in a list should have a unique "key" prop

[에러 원인]
react에서 map() 메서드를 사용하기위해서는 배열의 각 item마다 독립적인 key
값을 설정해야합니다. 
하지만 위의 코드를 보니 key prop가 관련된 세팅이 전혀 없습니다.

예시 상황 출처
https://itprogramming119.tistory.com/entry/React-Warning-Each-child-in-a-list-should-have-a-unique-key-prop-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95

 

Each child in a list should have a unique "key" prop. 해결 방법

이번 글에서는 Warning: Each child in a list should have a unique "key" prop. 에러 해결 방법에 대하여 알아보겠습니다. Warning: Each child in a list should have a unique "key" prop. [에러가 발생한 코드] class TestComponent exte

itprogramming119.tistory.com

 


 

 

 

 

 

 

body담아 보내는 방법 중에 여러가지 방법 있는데 많이 쓰는 방법 2가지만 준다고함.

 

 

 

 

 

 

 

 

30강

branch 3_6

 

먼저 초기값을 설정한 뒤에 업데이트 하는거다.

이 코드 

 

import React, { useState } from "react";

import "./PaymentForm.css";

const PaymentForm = () => {
    const [objectState, setObjectState] = useState({
        name: "",
        price: 0,
        today: new Date(),
    });

    const inputTextHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            name: event.target.value,
        }));
    };

    const inputPriceHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            price: event.target.value,
        }));
    };

    const inputTodayHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            today: event.target.value,
        }));
    };

    const buttonSubmitHander = (event) => {
        event.preventDefault();

        console.log(objectState);

        setObjectState({
            name: "",
            price: 0,
            today: new Date(),
        });
    };

    return (
        <div className="new-payment">
            <form onSubmit={buttonSubmitHander}>
                <div className="new-payment__controls">
                    <div className="new-payment__control">
                        <label>이름</label>
                        <input
                            type="text"
                            onChange={inputTextHandler}
                            value={objectState.name}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>금액</label>
                        <input
                            type="number"
                            min="0.01"
                            step="0.01"
                            onChange={inputPriceHandler}
                            value={objectState.price}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>날짜</label>
                        <input
                            type="date"
                            min="2019-01-01"
                            max="2023-12-31"
                            onChange={inputTodayHandler}
                            value={objectState.today}
                        />
                    </div>
                </div>
                <div className="new-payment__actions">
                    <button type="submit">결제 추가</button>
                </div>
            </form>
        </div>
    );
};

export default PaymentForm;

 

objectState는 React 함수 컴포넌트 내에서 사용되는 상태(state)를 관리하기 위한 변수입니다. 이 코드에서 useState를 사용하여 초기 상태를 설정하고, objectState 변수를 사용하여 컴포넌트의 상태를 변경하고 그 값을 렌더링합니다.

여기에서 useState를 호출할 때 초기 상태로 다음과 같은 객체를 설정합니다:

 

const [objectState, setObjectState] = useState({
  name: "",
  price: 0,
  today: new Date(),
});

 

 

이 객체는 세 가지 속성을 가지고 있으며, 초기 값으로 빈 문자열(""), 숫자 0, 그리고 현재 날짜를 가지고 있습니다. 이 세 가지 속성은 사용자의 입력을 받아서 변경될 것입니다.

각각의 입력 필드 (이름, 금액, 날짜) 에 대한 변경 핸들러 함수 (inputTextHandler, inputPriceHandler, inputTodayHandler)는 해당 입력 필드의 값을 변경할 때마다 setObjectState를 호출하여 objectState 객체의 해당 속성을 업데이트합니다. 예를 들어 inputTextHandler 함수는 이름 입력 필드의 값을 변경하고 objectState의 name 속성을 업데이트합니다.

마지막으로, "결제 추가" 버튼을 클릭하면 buttonSubmitHander 함수가 호출되고, 이 함수는 console.log(objectState)를 사용하여 현재 objectState 객체의 값을 로깅하고, 다시 초기 상태로 objectState를 설정합니다. 이렇게 하면 사용자가 입력한 값들이 초기화되고 다시 입력할 수 있게 됩니다.

즉, objectState는 이 컴포넌트에서 사용자의 입력 상태를 추적하고 관리하기 위한 상태 객체입니다.

 

 

 

 

prevState는 React 컴포넌트에서 상태를 업데이트할 때 사용하는 함수의 인자로 전달되는 변수입니다. 이전 상태의 값을 나타냅니다.

여기에서 inputTextHandler 함수에서 사용되는 코드를 자세히 살펴보겠습니다:

 

const inputTextHandler = (event) => {
  setObjectState((prevState) => ({
    ...prevState,
    name: event.target.value,
  }));
};

 

  1. setObjectState 함수는 이전 상태를 업데이트하는 함수입니다. 이전 상태를 업데이트할 때 일반적으로 현재 상태를 변경하지 않고 이전 상태를 복사한 후 필요한 부분만 업데이트합니다.
  2. {...prevState}는 스프레드 연산자를 사용하여 이전 상태(prevState)를 복사합니다. 이렇게 하면 이전 상태의 모든 속성과 값을 포함한 새로운 객체가 생성됩니다.
  3. 그 다음, 새로운 객체의 name 속성을 현재 이벤트(event)의 타겟(event.target)에서 가져온 값으로 업데이트합니다. event.target.value는 사용자가 입력한 텍스트 값을 나타냅니다.

결과적으로, inputTextHandler 함수는 이전 상태를 복사하고 name 속성만 업데이트한 새로운 상태 객체를 setObjectState를 통해 설정합니다. 이렇게 하면 이전 상태의 다른 속성은 그대로 유지되고 name 속성만 변경됩니다. 이것이 React에서 상태를 업데이트하는 일반적인 방식 중 하나입니다.

 

 

{...prevState}는 스프레드 연산자를 사용하여 이전 상태(prevState)를 복사합니다. 여기서 스프레드 연산자가 ... 이거니?

 

네, 정확히 맞습니다. {...prevState}에서 사용된 ... 부분은 스프레드 연산자(Spread Operator)입니다. 이 연산자는 객체나 배열을 복사하거나 확장하는 데 사용됩니다.

{...prevState}는 이전 상태(prevState)를 복사하여 새로운 객체를 생성합니다. 이 새로운 객체는 이전 상태의 모든 속성과 값을 그대로 가지고 있지만 완전히 새로운 객체로 분리되어 있습니다. 이후에 해당 객체의 일부 속성을 업데이트하거나 변경할 수 있습니다.

스프레드 연산자는 객체뿐만 아니라 배열에도 사용할 수 있으며, 배열의 요소를 복사하거나 새로운 요소를 추가하는 데에도 유용합니다. React 컴포넌트에서 상태나 props를 업데이트할 때, 이전 상태나 이전 props를 변경하지 않고 복사하여 새로운 상태나 props를 생성하는 데 스프레드 연산자가 자주 사용됩니다.

 

 

...prevState를 사용하지 않으면?
 이전 상태의 다른 속성은 유지되지 않고 name 속성 (바꾸려고하는)만 변경됩니다.

 

예를 들어, 다음과 같은 객체 상태가 있다고 가정해 봅시다:

 

const prevState = {
  name: "이전 이름",
  age: 30,
};

 

그리고 이전 상태에서 name 속성만 업데이트하려고 한다고 가정해봅시다. ...prevState를 사용하여 name 속성만 변경하면 다음과 같이 됩니다:

 

const updatedState = {
  ...prevState, // 이전 상태를 복사
  name: "새로운 이름", // name 속성 변경
};

 

이제 updatedState 객체는 다음과 같이 됩니다:

 

{
  name: "새로운 이름", // 변경된 name 속성
  age: 30, // 다른 속성은 그대로 유지
}

 

여기서 "다른 속성은 그대로 유지"라는 것은 age 속성은 변경되지 않고 이전 상태와 동일한 값을 유지한다는 것을 의미합니다. 즉, ...prevState를 사용하면 이전 상태의 다른 속성은 변경되지 않고 그대로 유지됩니다.

반면에 {}를 사용하여 새로운 객체를 만들면 다음과 같이 됩니다:

 

const updatedState = {
  name: "새로운 이름", // name 속성 변경
};

 

이 경우에는 새로운 객체를 만들 때 다른 속성을 고려하지 않고 name 속성만 변경합니다. 따라서 이전 상태의 다른 속성은 무시되고 삭제됩니다.

 

쌤:

PaymentForm의 부모컴포넌트 App.js에서 콘솔을 찍었을 때
지식컴포넌트의 state값이 찍히도록 해보도록 하겠습니다.

 

 

state를 그래서 부모로 올리는걸 지금 하는거임!! 

 

그러면 또 부모에서 상태값을 만들어줘야함

 

console.log 찍는 함수 만들기

const getPaymentFormDate = (data) => {
console.log(data);
};

"인자로 받은 data를" 그대로 콘솔로 찍어주는 함수를 만들었는데
여기까지 코드

 

 

 

지금 App.js에서 PaymentForm을 (자식)을 다루고 있잖아

 

인자인 data에 자식컴포넌트에 작성한 form state값을 넣어주면 될 것 같다는데?

 

부모

import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

자식

import React, { useState } from "react";

import "./PaymentForm.css";

const PaymentForm = () => {
    const [objectState, setObjectState] = useState({
        name: "",
        price: 0,
        today: new Date(),
    });

    const inputTextHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            name: event.target.value,
        }));
    };

    const inputPriceHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            price: event.target.value,
        }));
    };

    const inputTodayHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            today: event.target.value,
        }));
    };

    const buttonSubmitHander = (event) => {
        event.preventDefault();

        console.log(objectState);

        setObjectState({
            name: "",
            price: 0,
            today: new Date(),
        });
    };

    return (
        <div className="new-payment">
            <form onSubmit={buttonSubmitHander}>
                <div className="new-payment__controls">
                    <div className="new-payment__control">
                        <label>이름</label>
                        <input
                            type="text"
                            onChange={inputTextHandler}
                            value={objectState.name}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>금액</label>
                        <input
                            type="number"
                            min="0.01"
                            step="0.01"
                            onChange={inputPriceHandler}
                            value={objectState.price}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>날짜</label>
                        <input
                            type="date"
                            min="2019-01-01"
                            max="2023-12-31"
                            onChange={inputTodayHandler}
                            value={objectState.today}
                        />
                    </div>
                </div>
                <div className="new-payment__actions">
                    <button type="submit">결제 추가</button>
                </div>
            </form>
        </div>
    );
};

export default PaymentForm;

 

지금 자식에 const PaymentForm 안에 있는 
const [objectState, setObjectState] = ~
여기 밑에
name: "", 
price: 0, 
todayL new Date(), 

이것들을 부모로 

form에 있는 상태를 자식 컴포넌트에서 다루고 있었는데
이 값을 "위치만" 부모로 옮겨서 처리를 하는 식으로 로직을 바꿔볼거라고함.

 

 

form은 자식컴포넌트에 있으니까 부모에 있는 
const getPaymentFormData를 자식컴포넌트에 "내려주어서"
한번 실행을 시켜본다고함.

 

props에서는 값뿐만 아니라 함수도 내려줄 수 있다고함.

 

이 부분 

 

 

 

<PaymentForm getPaymentFormData={getPaymentFormData} />

<PaymentForm getPaymentFormData={getPaymentFormData}

여기에서 
getPaymentFormData 처음에 이 함수를 
<PaymentForm /> 태그를 통해서 props로 받을 수 있게 해준다고함
코드가 이거임

 

<PaymentForm getPaymentFormData={getPaymentFormData}

여기에서 
getPaymentFormData 처음에 이 함수를 
<PaymentForm /> 태그를 통해서 props로 받을 수 있게 해준다고함. 
코드가 이거임

그러면 자식컴포넌트에

const PaymentForm = ({get[aymentFormData}  ) 에서 중괄호 만들어서 가능 

buttonSubmutHandler 안에 뭘 넣어야지 동작할 수 있게 한다는데

 

그러면 getPaymentFormData() 이거를 넣어주면 된다.

 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

 

[실제코드]

 

부모컴포넌트에서 함수 getPaymentFormData 만들고 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

 PaymentForm 컴포넌트를 렌더링 아 이게 자식 컴포넌트구나?

ChatGPT
네, 정확히 맞습니다. PaymentForm 컴포넌트는 부모 컴포넌트인 App 컴포넌트에서 렌더링되는 자식 컴포넌트입니다.

이렇게 기초적인 것을..
부모 컴포넌트인 App 컴포넌트에서 <PaymentForm />라고 작성함으로써 PaymentForm 컴포넌트가 App 컴포넌트의 내부에서 렌더링되고 표시됩니다. 이것이 React에서 컴포넌트 간에 계층 구조를 형성하고 부모 컴포넌트가 자식 컴포넌트를 렌더링하게 하는 일반적인 방식입니다.

따라서 PaymentForm 컴포넌트는 App 컴포넌트의 자식 컴포넌트로 동작하며, 부모 컴포넌트인 App에서 정의한 상태와 함수에 접근하여 상호작용하고 데이터를 전달합니다.

 

부모컴포넌트 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

자식 컴포넌트

 

import React, { useState } from "react";

import "./PaymentForm.css";

const PaymentForm = ({getPaymentFormdata}) => {
    const [objectState, setObjectState] = useState({
        name: "",
        price: 0,
        today: new Date(),
    });

    const inputTextHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            name: event.target.value,
        }));
    };

    const inputPriceHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            price: event.target.value,
        }));
    };

    const inputTodayHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            today: event.target.value,
        }));
    };

    const buttonSubmitHander = (event) => {
        event.preventDefault();

       
        getPaymentFormdata();
        console.log(objectState);

        setObjectState({
            name: "",
            price: 0,
            today: new Date(),
        });
    };

    return (
        <div className="new-payment">
            <form onSubmit={buttonSubmitHander}>
                <div className="new-payment__controls">
                    <div className="new-payment__control">
                        <label>이름</label>
                        <input
                            type="text"
                            onChange={inputTextHandler}
                            value={objectState.name}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>금액</label>
                        <input
                            type="number"
                            min="0.01"
                            step="0.01"
                            onChange={inputPriceHandler}
                            value={objectState.price}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>날짜</label>
                        <input
                            type="date"
                            min="2019-01-01"
                            max="2023-12-31"
                            onChange={inputTodayHandler}
                            value={objectState.today}
                        />
                    </div>
                </div>
                <div className="new-payment__actions">
                    <button type="submit">결제 추가</button>
                </div>
            </form>
        </div>
    );
};

export default PaymentForm;

 

 

*오타 고침 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

*오타 고침

import React, { useState } from "react";

import "./PaymentForm.css";

const PaymentForm = ({getPaymentFormData}) => {
    const [objectState, setObjectState] = useState({
        name: "",
        price: 0,
        today: new Date(),
    });

    const inputTextHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            name: event.target.value,
        }));
    };

    const inputPriceHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            price: event.target.value,
        }));
    };

    const inputTodayHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            today: event.target.value,
        }));
    };

    const buttonSubmitHander = (event) => {
        event.preventDefault();

       
        getPaymentFormData(objectState);
        //console.log(objectState);

        setObjectState({
            name: "",
            price: 0,
            today: new Date(),
        });
    };

    return (
        <div className="new-payment">
            <form onSubmit={buttonSubmitHander}>
                <div className="new-payment__controls">
                    <div className="new-payment__control">
                        <label>이름</label>
                        <input
                            type="text"
                            onChange={inputTextHandler}
                            value={objectState.name}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>금액</label>
                        <input
                            type="number"
                            min="0.01"
                            step="0.01"
                            onChange={inputPriceHandler}
                            value={objectState.price}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>날짜</label>
                        <input
                            type="date"
                            min="2019-01-01"
                            max="2023-12-31"
                            onChange={inputTodayHandler}
                            value={objectState.today}
                        />
                    </div>
                </div>
                <div className="new-payment__actions">
                    <button type="submit">결제 추가</button>
                </div>
            </form>
        </div>
    );
};

export default PaymentForm;

 

 

 

child component에 있는 state
objectState라고 불리는 
name, price, today 
이름, 가격, 날짜
에 대한 state 정보를 parent로 보내려는 작업을 
처음에 요구사항을 받았습니다.

 

그래서 자식컴포넌트에서 부모컴포넌트로 state를 바로 보낼 수 있는 방법이 없기 때문에
부모컴포넌트에서 자식컴포넌트의 state를 받을 수 있는 props로 펑션을 보내주고
그러면 그 함수가 실행이 되면서 state를 부모 컴포넌트로 올려주는 식으로 처리를 해야한다는데? 

 


기본적으로 props를 내려주는 건 단방향
부모 -> 자식 
양방향 바인딩이 지원안된다.

 

부모에에 자식으로 props를 내려주는건 가능하지만

 

   <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />

여기서 props는 getPaymentFormData & items

 

자식컴포넌트에 있는 data또는 함수를 props같은 형태로 부모컴포넌트로 보내주는 건 불가능하다.

 

 

그래서 자식컴포넌트의 state를 받아볼 수 있는 함수를 부모에서 만들어서
제일 쉬운 함수가 console.log잖아 (가장 쉬운 방법)
콘솔에 찍는다는게 우리가 값을 받았다는 말이잖아
그래서 props로 내려주고
그러면 자식 컴포넌트에서 부모에서 받은 이 펑션을 실행해서
이 state를 부모로 올려주는 거를 
아주 간단한 로직으로 해본거다.

 

 

이 부분이 어려운거라고 11분 20초부터 시작 

부모 컴포넌트의 상태를 자식 컴포넌트에 전달 
자식 컴포넌트에서 부모 컴포넌트로 올려진 state를 
또 다른 자식 컴포넌트로 전달해보기

 

 

부모컴포넌트의 상태를 자식 컴포넌트의 상태는 props로 쉽게 내릴 수 있다. 

 

 

App.js에서 expenses라는 코드가
값이 하드코딩되어있는데
부모컴포넌트에서 상태로 한번 바꿔보겠다고함. 

아래가 원본 코드임 (App.js)

 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })

    const expenses = [
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ];

    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

이렇게 바꾼다는 거임.

 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })
   
    const [expenses, setExpenses] = useState([
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ]);


    const getPaymentFormData = (data) => {
        console.log(data);
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

값을 변수가아니라 useState로 바꿔주는 거임.

 

부모

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {

    const [parentObjectState, setParentObjectState] = useState({

    })
   
    const [expenses, setExpenses] = useState([
        {
            id: "e1",
            title: "수건",
            amount: 12.33,
            date: new Date(2025, 8, 14),
        },
    ]);


    const getPaymentFormData = (data) => {

        setExpenses([
            {
                id: Math.random().toString(),
                title: data.name,
                amount: data.price,
                date: data.today,
            },
        ])
    }

    return (
        <>
            <PaymentForm getPaymentFormData={getPaymentFormData} />
            <Expenses items={expenses} />
        </>
    );
}

export default App;

 

 

import React, { useState } from "react";

import "./PaymentForm.css";

const PaymentForm = ({getPaymentFormData}) => {
    const [objectState, setObjectState] = useState({
        name: "",
        price: 0,
        today: new Date(),
    });

    const inputTextHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            name: event.target.value,
        }));
    };

    const inputPriceHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            price: event.target.value,
        }));
    };

    const inputTodayHandler = (event) => {
        setObjectState((prevState) => ({
            ...prevState,
            today: event.target.value,
        }));
    };

    const buttonSubmitHander = (event) => {
        event.preventDefault();

       
        getPaymentFormData(objectState);
        //console.log(objectState);

        setObjectState({
            name: "",
            price: 0,
            today: new Date(),
        });
    };

    return (
        <div className="new-payment">
            <form onSubmit={buttonSubmitHander}>
                <div className="new-payment__controls">
                    <div className="new-payment__control">
                        <label>이름</label>
                        <input
                            type="text"
                            onChange={inputTextHandler}
                            value={objectState.name}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>금액</label>
                        <input
                            type="number"
                            min="0.01"
                            step="0.01"
                            onChange={inputPriceHandler}
                            value={objectState.price}
                        />
                    </div>
                    <div className="new-payment__control">
                        <label>날짜</label>
                        <input
                            type="date"
                            min="2019-01-01"
                            max="2023-12-31"
                            onChange={inputTodayHandler}
                            value={objectState.today}
                        />
                    </div>
                </div>
                <div className="new-payment__actions">
                    <button type="submit">결제 추가</button>
                </div>
            </form>
        </div>
    );
};

export default PaymentForm;

 

 

 

결제추가버튼 누르면 빨간색 값이 부모컴포넌트로 올라가고
그값이 부모컴포넌트의 상태를 업데이트 해주고
그 업데이트된 상태를 또다른 자식컴포넌트 즉 Expenses라는 자식컴포넌트로
내려줄 수 있게되는거임
부모의 상태가 자식 상태으로 들어가기때문에
아래에 바뀐 것들이 반영됨 

 

 

유레카 감잡음 ㅋㅋㅋ 12일로

콘솔찍혀도..등록은 25일로 되잖아 현재날짜 콘솔찍혀도..

 

 

31강 일반 html에서 이미지 불러오는거랑 리액트에서 이미지 불러오는거랑 좀 
다르다고 함 브랜치 3_7

 

 

 

<img src="path..." alt="대체문자열" />
이거 말고 리액트스러운 새로운 이미지 부르는 방법이 있다고함.

 

 

변수나 함수명은 소문자로 시작하는데
컴포넌트명이나 이미지파일은 대문자로 시작하는 경우가 많다.
이거는 그냥 컨벤션이다.

이미지파일을 소문자로 시작한다고 틀린건 아닌데 import 할 때 거의 다 대문자로 시작해서 import함.

 

 

./ 하면 지금 App.js에서 불러오고 있잖아 App.js에서 같은 위치의 디렉토리에서
파일을 참조할 수 있다.
./ 게만 하면 images 디렉토리가 불러와지잖아 같은 위치니까
그 다음 그 하위에 있는 /alexa.png

 

컴포넌트 import할 때랑 차이점이 있는데 그 점은 바로 뒤에 확장자가 붙는다는 점
import AlexaImage from './images/alexa.png' 
뒤에 png가 붙잖아.

 

 

쌍따옴표를 쓰면 이미지경로를 불러온게 아니라
AlexaImage라는 String값을 불러온거기 때문에 완전 다른 개념이 된다.
그래서 중괄호를 써줘야한다.

 

 

 

 

32강

체크아웃 practice/3-8

 

 

결제추가라는 PaymentForm.js에 있었고 이걸 누르면
App.js에서 getPaymentFormData(objectState);가 실행이 됨
그리고 setExpenses를 업데이트 해줬고 (state가 반영이 됨) 
그리고 나서
<Expenses items={expenses}/>에 반영이 되고
itmes라는 props에다가 expenses라는 값을 넣은거임
Expenses와 Expenseitem에 들어가게 된다.

setExpenses를 조금 바꿔보면 추가나 삭제를 할 수 있다. 


다른 곳에서 이렇게 설명함

출처: https://velog.io/@jhs000123/%EB%A6%AC%EC%95%A1%ED%8A%B8-props%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EA%B2%8C-%EA%B0%92-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0
리액트
실습 props 전달1
전에 작성한 코드에서 Hello 컴퍼넌트에게 name이라는 값을 전달해보자

function App() {
  
  return (
    <Hello name="react"/>  
  );
}
Hello태그 옆에 name="react"라고 작성을 한다. 이뜻은 name이라는 prop에다가 react라는 값을 집어 넣었다라는 뜻이다.

1000바이트 = 9.7kb

 

32강 - 리스트렌더링 시작

32강에서는 

리스트 렌더링 Part 1. 
-리스트를 state 값과 연동하기
 * 추가
 * 삭제

리스트도 application에서 정말 많이 보이는데요
당근마켓같은 거는 내리면 내릴 수록 물품들이 계속 생기죠
원하는 조건으로 필터를 걸 수도 있고
리스트 그려주는 부분이라 엄청 중요하다.

30강 강의까지는
1. form에 값 입력 후 submit
2. 그 submit데이터가 부모컴포넌트로 "state"가 올라감
3. 그 state를 아래에 있는 자식컴포넌트에 다시 보내서 값을 업데이트하는것 까지 살펴봄 

 

이제 업데이트가 아닌 추가를 해주는거임.
(내가 하고싶었던 것)

 

1. git commit -m "finished"

2. git push

Your branch is ahead of 'upstream/practice/3-7' by 1 commit.
  (use "git push" to publish your local commits)

 

git push는 현재 로컬 저장소의 코드를 온라인 저장소로 올리는 작업을 한다. master branch로 이 저장소를 올리겠다는 의미로 보면 되겠다.

 

nothing to commit, working tree clean
PS D:\personal\supercoding-workspace-react-2\react-branch-4_18\react-fundamental> git push
remote: Permission to dev-owen/react-fundamental.git denied to MangwonCassie.

 

오늘은 프로젝트 협업 시에 유용한 명령어인 fork를 나혼자 정리하고, clone이랑 비교해서 한번 정리해보려고 함.

 

먼저 fork와 clone의 차이를 크게 적게씀.

 

clone 

원본 레포지토리의 내용을 내 로컬 레포지토리로 완전히 복사한다. 연결된 레포지토리는 로컬에서 remote로 연결된 origin 이다. 

clone 한 프로젝트는 원본 레포지토리의 로그를 볼 수 없다. 권한이 없는 경우, 내 로컬의 변경 사항을 원본 레포지토리로 push 또한 할 수 없다. -- 소규모 팀작업에서는 활용하기에 좋을 수 있음.

만약 협업 중 각각 클론한 프로젝트에서 변경 사항을 적용하고 싶을 때, 상대방이 먼저 push ( 권한이 있으면 가능하다. ) 를 하게 되면, push 를 할 때 마다 fetch 를 수행한 후 push 를 해야한다는 번거로움이 있다. 

 

fork 

다른 사람의 레포지토리에서 어떤 부분을 수정하거나, 추가 기능을 넣고 싶을 때 해당 레포지토리를 내 레포지토리로 그대로 복제.

하지만 fork 한 저장소는 원본 레포지토리 ( 내가 연결한 레포지토리 ) 와 연결되어 있다. 

내 로컬 저장소와 연결 되어 있지 않음. 

만약 원본 레포지토리에 새로운 변경 사항인 commit 이 생기면, 포크된 레포지토리 ( 내가 지금 현재 사용 중인 로컬에 있는 포크된 레포지토리 ) 로 반영할 수 있는데, 이 때에는 fetch, rebase 등의 과정이 필요하다.

또한, 내 로컬 저장소에 있는 변경 사항을 원본 레포지토리에 적용도 가능하다. 

pull request 를 하면 되는데, 이 때 해당 원본 관리자로부터의 승인이 있어야 하고, 관리자가 승인하게 되면, 내가 만든 코드가 commit, merge 되어 원본 레포지토리에 반영 되게 된다. 풀리퀘를 하기 전에는 내가 포크한 레포지토리에만 변경 사항이 적용 된다. 

 

이 부분:

 

https://korinkorin.tistory.com/42

 

2021.01.21 git fork vs clone

2021.01.21_ git fork vs clone _정리노트 ㅎㅇㅎㅇ 오늘은 프로젝트 협업 시에 유용한 명령어인 fork를 나혼자 정리하고, clone이랑 비교해서 한번 정리해보려고 함. 먼저 fork와 clone의 차이를 크게 적게씀.

korinkorin.tistory.com

 

32강 조건 시작

 

[추천]
1. 배열을 가지고 있어야하고
2. 배열 상태값에 다가 제일 앞에 추가를 해줘야한다. 

 

 

 

자바스크립트 기본 문법 배울 때
... 이용해서 배열과 배열을 붙일 수가 있고
객체와  객체를 합칠 수 있다.
이런 문법이 자바스크립트 es6에 나왔다.
작업에서 적용해볼거임.

 

 

이게 될까요?이러는데
여기서 ,expenses는 이전 expenses다. 

 

 

 

이 방법 틀렸데

expenses는 배열인가요? 객체인가요?

객체아님..?

썜: 물론 배열도 객체이긴 한데

썜: {} 이 형태로 되어있기 때문에 배열이란다.

{
id: "e1",
title: "수건",
amount: 12.33,
date: new Date(2025, 8, 14),
},



[궁금했던 것]

useState([])에서 대괄호 []는 React의 useState 훅의 초기 상태(initial state)를 의미합니다.

useState 훅은 React 함수 컴포넌트에서 상태를 관리할 때 사용되며, 배열을 반환합니다. 배열의 첫 번째 요소는 현재 상태 값을 나타내며, 두 번째 요소는 상태를 업데이트하는 함수입니다.

여기서 useState([])는 초기 상태로 빈 배열([])을 설정한 것을 의미합니다. 즉, expenses라는 상태 변수가 초기에 빈 배열로 설정되었습니다. 이렇게 초기 상태를 설정하면 컴포넌트가 처음 렌더링될 때 expenses 상태는 빈 배열로 시작하게 됩니다.

이후 getPaymentFormData 함수를 통해 expenses 상태를 업데이트하여 지출 항목을 추가하거나 변경할 수 있습니다.

 

 

[ ] 안에 {} 배열 여러개 ,로 연결해서 올 수 있다고함.

 

리스트 추가하는 코드 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {
  const [expenses, setExpenses] = useState([
    {
      id: "e1",
      title: "수건",
      amount: 12.33,
      date: new Date(2025, 8, 14),
    },
  ]);

  const getPaymentFormData = (data) => {
    console.log(data);
    setExpenses([
      {
        id: Math.random().toString(),
        title: data.name,
        amount: data.price,
        date: new Date(data.today),
      },
      ...expenses
    ]);
  };

  return (
    <>
      <PaymentForm getPaymentFormData={getPaymentFormData} />
      <Expenses items={expenses} />
    </>
  );
}

export default App;

expenses에 배열의 값이 몇개가있을지모름
어쩃든 배열이니까........
배열안에 배열을 넣어줘야하는데
그냥 넣어주면
원소가 2개인 배열이 되는데
그러면 ... 으로 스프레드 연산자로
여러개가 있는 경우에 하나하나 다 처리를 할 수 있게 바꿔줘야한단다.

 

 

리스트 삭제하는 코드 

 

1,2,3,4 있으면 3번 삭제하면 1,2,4번만 남게해본다고함.

 

못생기기긴 했지만 ExpenseItem.js에 가서 button 태그 만들어준다. 

 

삭제로직을 넣을 수 있는 공간이 한 3군데 후보가 있음

App의 자식 Expenses, Expenses에서 ExpenseItem을 배열로 받아서 map를 통해서 여러개를 보여주고 있다.

 

✔🤷‍♂️ 

여기서 [expenses, setExpenses]가 배열이야? 이랬는데 

 

  const [expenses, setExpenses] = useState([
    {
      id: "e1",
      title: "수건",
      amount: 12.33,
      date: new Date(2025, 8, 14),
    },
  ]);

 


네, 맞습니다. const [expenses, setExpenses]는 배열 구조 분해 (array destructuring)를 사용하여 두 개의 변수를 선언하는 구문입니다. 이렇게 선언된 두 변수는 배열과 배열의 첫 번째 및 두 번째 요소에 대응됩니다.

expenses: 이 변수는 배열의 첫 번째 요소에 대응하며, 현재 상태 값을 나타냅니다. useState 훅을 통해 반환된 배열에서 첫 번째 요소는 현재 상태 값을 가지고 있습니다. 이 예제에서 expenses는 지출 항목들의 배열을 나타냅니다.

setExpenses: 이 변수는 배열의 두 번째 요소에 대응하며, 상태를 업데이트하는 함수를 나타냅니다. useState 훅을 통해 반환된 배열에서 두 번째 요소는 상태를 업데이트하는 함수입니다. setExpenses 함수를 호출하면 상태를 변경할 수 있습니다.

따라서 const [expenses, setExpenses]는 expenses 변수를 선언하고, 이 변수는 현재 지출 항목 상태를 나타내며, setExpenses 변수는 지출 항목 상태를 업데이트하기 위한 함수를 나타냅니다. 이런 식으로 배열 구조 분해를 사용하면 React 컴포넌트에서 상태를 관리하는 데 도움이 됩니다.





3군데다가 하는데
쌤이라면 App에 한데

지금 state를 바꿔주는건데
state를 바꿔주는 로직은 state가 있는 곳에서 하면 제일 좋을 것 같다고함. 

 

id값을 가지고 삭제로직을 생각을 했는데 왜냐 그값이 primary key 
유니크한 값이기 때문에

 


로직을 쓰기 전
App의 deleteExpenseItem 이거를 
ExpenseItem.js 에서 받고 돌아갈 수 있게 만들면 좋을 것 같다고함.

 

그러면 부모 -> 자식 보내줘야하니까
props를 통해서 보내주면 좋을 것 같습니다.


[전]
return (
<>
<PaymentForm getPaymentFormData={getPaymentFormData} />
<Expenses items={expenses} />
</>
);

[후]
return (
<>
<PaymentForm getPaymentFormData={getPaymentFormData} />
<Expenses items={expenses} deleteExpenseItem={deleteExpenseItem}/>
</>
);

 

 

 

Math라는 인터페이스가 있다.
0보다 크거나 같고 1보다 작은 랜덤의 수 0은 들어간다.
근데 이건 리턴값이 "Number"예요
아이디값은 String 값을 써야해서 toString()

 

 

const deleteExpenseItem = (id) => {
//1. filter
//2. slice
삭제를 위해서 쓸 수 있는 메소드가 2개나 있단다. 
그냥외우면 좋을 것 같다. 
}

 

 

진짜 많이 쓰이는 메소드란다. 

 

 

 

 

deleteExpenseItem 함수를 만들었잖아
이 매개변수  id값을 넣는 함수로 짜줬기때문에
그래서 id 값을 이런 식으로 넣어야함
새롭게 만들어야하는 리스트에는 id값이 없어야한다.
id값은 들어가면 안된다.


Expenses.js
-----------
<ExpenseItem>
id={item.id}
-----------


-----------
ExpenseItem.js에서 
() => 이렇게 만들어야하고 
-----------


 const newFilteredArray = expenses.filter(item => item.id !== id); 아 이거는 화살표함수가 매개변수가 1개라서 (item) => {item.id!==id} 이렇게 안한거네?


맞습니다, item 하나만 매개변수로 받는 경우에는 괄호를 생략하고 (item) => {item.id !== id} 대신에 item => item.id !== id와 같이 작성할 수 있습니다. 화살표 함수에서 매개변수가 하나인 경우에는 괄호를 생략할 수 있습니다.

따라서 const newFilteredArray = expenses.filter(item => item.id !== id);와 같이 작성한 코드는 item 하나를 매개변수로 받고 item.id !== id 조건을 검사하여 필터링하는 화살표 함수입니다. 이 코드는 expenses 배열에서 id가 id 매개변수와 다른 경우만을 선택하여 새로운 배열인 newFilteredArray에 저장하는 역할을 합니다.



filter(): 배열에서 쓰라고 만들어진 메소드다.
true인 경우만 필터를 해서
검색하면 다 나온다.

화살표 함수는 JavaScript에서 함수를 간결하게 정의할 수 있는 방법입니다. 화살표 함수는 (매개변수) => { 함수 본문 } 형식으로 작성됩니다.

 

 

setExpenses(newFilteredArray); 

이거를 해줘야지

업데이트된 Array가 상태로 들어가고
상태가 자식 컴포넌트에 전달이되서
바뀔때 마다 업데이트가 됨

 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {
  const [expenses, setExpenses] = useState([
    {
      id: "e1",
      title: "수건",
      amount: 12.33,
      date: new Date(2025, 8, 14),
    },
  ]);

  const getPaymentFormData = (data) => {
    console.log(data);
    setExpenses([
      {
        id: Math.random().toString(),
        title: data.name,
        amount: data.price,
        date: new Date(data.today),
      },
      ...expenses
    ]);
  };

  const deleteExpenseItem = (id) => {
    //확인용
    console.log(id);
    //1. filter
    const newFilteredArray = expenses.filter(item => item.id !== id);
    setExpenses(newFilteredArray);
  }

  return (
    <>
      <PaymentForm getPaymentFormData={getPaymentFormData} />
      <Expenses items={expenses} deleteExpenseItem={deleteExpenseItem}/>
    </>
  );
}

export default App;

 

삭제로직 완성

여기서는 App.js

 

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {
  const [expenses, setExpenses] = useState([
    {
      id: "e1",
      title: "수건",
      amount: 12.33,
      date: new Date(2025, 8, 14),
    },
  ]);

  const getPaymentFormData = (data) => {
    console.log(data);
    setExpenses([
      {
        id: Math.random().toString(),
        title: data.name,
        amount: data.price,
        date: new Date(data.today),
      },
      ...expenses
    ]);
  };

  const deleteExpenseItem = (id) => {
    //확인용
    console.log(id);
    //1. filter
    const newFilteredArray = expenses.filter((item) => item.id !== id);
    setExpenses(newFilteredArray);
  }

  return (
    <>
      <PaymentForm getPaymentFormData={getPaymentFormData} />
      <Expenses items={expenses} deleteExpenseItem={deleteExpenseItem}/>
    </>
  );
}

export default App;
import React from "react";

import ExpenseItem from "./ExpenseItem";
import Card from "../UI/Card";
import "./Expenses.css";

const Expenses = (props) => {
    return (
        <Card className="expenses">
            {props.items.map((item) => (
                <ExpenseItem
                    id={item.id}
                    //id값 추가함
                    title={item.title}
                    amount={item.amount}
                    date={item.date}
                    deleteExpenseItem={props.deleteExpenseItem}
                />
            ))}
        </Card>
    );
};

export default Expenses;
import React from "react";

import ExpenseDate from "./ExpenseDate";
import Card from "../UI/Card";
import "./ExpenseItem.css";

const ExpenseItem = (props) => {
    //props.id 를 가져왔잖아
    return (
        <Card className="expense-item">
            <ExpenseDate date={props.date} />
            <div className="expense-item__description">
                <h2>{props.title}</h2>
                <div className="expense-item__price">${props.amount}</div>
                <button onClick={() => props.deleteExpenseItem(props.id)}>삭제하기</button>
            </div>
        </Card>
    );
};

export default ExpenseItem;

2. slice함수

 

slice도 array에 내장되어있는함수인데
처음부터 그냥 끝까지 자르는 함수다.
slice는 id값이 아닌 index값이 필요하다.
지금 index를 가지고오지않고있어서
그래서 deleteExpenseItem 함수를 조금 다르게 만들어줘야한다고함.

 

 

두개로 나눠서 볼거래
만약에 index를 삭제한다고 치면
[0,1,2,3..., index-1] [index+1, ..., n-1]

 

beforeArray는 [0, 1, 2, ...,index-1] 를 의미하고
afterArray는 [index+1, ..., n-1]를 의미한다.

 

 

slice할 때
(0, index) 하잖아
이 때 index는 포함을 안한다..!!

slice로 코드 구현한 것

App.js

import { useState } from "react";
import "./App.css";
import PaymentForm from "./components/PaymentForm/PaymentForm";
import Expenses from "./components/Payments/Expenses";

function App() {
  const [expenses, setExpenses] = useState([
    {
      id: "e1",
      title: "수건",
      amount: 12.33,
      date: new Date(2025, 8, 14),
    },
  ]);

  const getPaymentFormData = (data) => {
    console.log(data);
    setExpenses([
      {
        id: Math.random().toString(),
        title: data.name,
        amount: data.price,
        date: new Date(data.today),
      },
      ...expenses
    ]);
  };

  const deleteExpenseItem = (index) => {
    //확인용
    //console.log(id);
    //1. filter
    // const newFilteredArray = expenses.filter((item) => item.id !== id);
    // setExpenses(newFilteredArray);

    //2.slice
    const beforeArray = expenses.slice(0, index);
    const afterArray = expenses.slice(index+1); //끝까지는 콤마해서 또 적는 거 뒤에 생략해도된다함.
    setExpenses([...beforeArray, ...afterArray]);
  }

  return (
    <>
      <PaymentForm getPaymentFormData={getPaymentFormData} />
      <Expenses items={expenses} deleteExpenseItem={deleteExpenseItem}/>
    </>
  );
}

export default App;

 

Expenses.js

import React from "react";

import ExpenseItem from "./ExpenseItem";
import Card from "../UI/Card";
import "./Expenses.css";

const Expenses = (props) => {
    return (
        <Card className="expenses">
            {props.items.map((item, index) => (
                <ExpenseItem
                    id={item.id}
                    //id값 추가함
                    index={index}
                    title={item.title}
                    amount={item.amount}
                    date={item.date}
                    deleteExpenseItem={props.deleteExpenseItem}
                />
            ))}
        </Card>
    );
};

export default Expenses;

ExpenseItem.js

import React from "react";

import ExpenseDate from "./ExpenseDate";
import Card from "../UI/Card";
import "./ExpenseItem.css";

const ExpenseItem = (props) => {
    //props.id 를 가져왔잖아
    return (
        <Card className="expense-item">
            <ExpenseDate date={props.date} />
            <div className="expense-item__description">
                <h2>{props.title}</h2>
                <div className="expense-item__price">${props.amount}</div>
                <button onClick={() => props.deleteExpenseItem(props.index)}>삭제하기</button>
            </div>
        </Card>
    );
};

export default ExpenseItem;

 

 

33강 key 복습하기 

 

리스트의 key 새로운 개념

이 오류 계속 넘겼잖아

list should have a unique "key" prop.

친절하게 다 설명해주잖아

Check the render method of `Expenses`. See https://reactjs.org/link/warning-keys for more information.

 

근데 render함수가 없음...

 

import React from "react";

import ExpenseItem from "./ExpenseItem";
import Card from "../UI/Card";
import "./Expenses.css";

const Expenses = (props) => {
    return (
        <Card className="expenses">
            {props.items.map((item, index) => (
                <ExpenseItem
                    id={item.id}
                    //id값 추가함
                    index={index}
                    title={item.title}
                    amount={item.amount}
                    date={item.date}
                    deleteExpenseItem={props.deleteExpenseItem}
                />
            ))}
        </Card>
    );
};

export default Expenses;

 

 

맵핑을 통해서 만들어지는 각각의 div요소들이 있는데
그 요소들이 서로 구분이 되어지지않기 때문에 생기는 오류다.

검사도구를 보면
펼쳐보면 다르긴한데
브라우저 즉 리액트의 관점에서 보면

<div class="expense-item__description"><h2>수건</h2><div class="expense-item__price">$12.33</div><button>삭제하기</button></div>

이 부분이 차이가 없는거야.
"<div class="expense-item__description">" 

그리고 지금 불필요한 렌더링이 일어남.

값을 추가하면

그 전에 있던 값이 업데이트 되면서 렌더링 되고
각각 요소 전부다 그렇게 됨.
리소스가 낭비되고 있다.

 

 

리스트의 key

리액트에서 리스트 형태로 만드는 모든 컴포넌트 (HTML tag, 커스텀 컨포넌트 등)
에 대해서 key값을 고유하게 부여해주어야 에러가 발생하지 않는다.

 

 

 

보통 map일때나 똑같은 div 같은 것들 계속해서 만들어줘야할 때 사용

map: 배열로 받은 props 데이터를 map으로 돌려주고 있고
그 맵에서 각각 ExpenseItem을 지금 "컴포넌트로" 돌려주고 있습니다.

 

 

ExpenseItem이 여러개 만들어지는데 그게 구별이 안되서 에러 생기는거다.
그리고 불필요한 렌더링도 일어나는거다.

key값으로 어떤걸 추가? id값을 넣어보겠다고함.
key = item.id 하면 된다.

import 했잖아 그러면 import 한 부분이 제일 막내

 

import React from "react";

import ExpenseItem from "./ExpenseItem";
import Card from "../UI/Card";
import "./Expenses.css";

const Expenses = (props) => {
    return (
        <Card className="expenses">
            {props.items.map((item, index) => (
                <ExpenseItem
                    key={item.id}
                    id={item.id}
                    //id값 추가함
                    index={index}
                    title={item.title}
                    amount={item.amount}
                    date={item.date}
                    deleteExpenseItem={props.deleteExpenseItem}
                />
            ))}
        </Card>
    );
};

export default Expenses;

말그대로 실시간데이터베이스 뜻한다. 

 

 

실제코드에서 이 데이터베이스에 데이터를 post요청을 통해서 넣는거까지 한번해보면 된다함.

 

지금 62강 하고 있음. 

 

 

 

# package.json 생성

- 수많은 패키지를 다운받다보면 관리가 어려워진다.

- 그래서 프로젝트마다 package.json 파일을 만들어서 패키지 목록 및 버전을 관리한다.

* 프로젝트를 생성하자마자 package.json을 만들어주고 시작하길 권장한다.

 

- 명령어:  npm init 

 

62강

async/awiat는 현업에서 진짜 많이 쓰임

비동기적인 코드를 마치 동기적인 코드처럼 해준단다. 

 

fetch는 기본적으로 비동기처리이다. 

 

이게 뭔지를 몰랐는데, "데이터베이스"의  url이다. 

데이터베이스도 url이 있다. 

 

원래는 여기까지인데

우리가 만드는 데이터의 이름을 만들어줘야해서

뒤에 슬래쉬랑 test.json 내가 임의로 붙인거임...! 

json도 그냥 json형태로 보낼거라서 저렇게적은 것 같은데 다른 이름

test.yeooul도 되는지 한번 확인해봐. 

 

import React from 'react';


function App(){
   
    const postRequestHandler = async () => {
        method: 'POST',
        body: JSON.stringify({
        "key1": "value1",
        "key2": "value2"
        }),
        headers: {
            'Content-Type':'application/json'
        }
        })
    }

    return (
        <button onClick={postRequestHandler}>Post</button>
    )


}

export default App;

 

아 뒤에 json점을 꼭 적어줘야한다.
이 말인 즉슨
test라는 이름의 json 형식으로 보낸다. 라는 뜻이 된다고함.

 

get방식으로 쓸 떄는 첫번 째 url만 썼는데
post 방식이면 body도 넣어서 보내야한다. 그래서 두번째에 body값을 넣어줘야한다고함.

 

JSON.stringfy라는 "문법"을 통해서 
"JSON data"를 "string"으로 변환시켜서 보내야한단다.

 

그냥 이거는 post 요청의 문법이라서 외워야하는거다.

 

"key1": "value1",
"key2": "value2"

이 형태로 내가 json를 보내는거다.

 

body를 먼저 적고
그 다음 headers를 적어야하는데
headers에는 'application/json' 타입을 넣어주면
내가 해야할 일은 다 끝난거다.

 

데이터를 요청에 "같이" 담아서 보내는 거다. 그게 get요청과 유사하지만 다른점임.

 

 

데이터를 보내는 부분을 body라고하는데
데이터를 어떻게 보내줄지는 항상 똑같이 보내는게아니라
데이터를 서버에 보내는 요청을 할 수 있는 방식이 여러가지다.

 

헤더에 들어가는 값들은 요청을 서버가 처리하기 전에 알아야할 사항들을 주로 저장하는 편이다.

 

이 콘텐츠 타입으로 보낼거야..서버에 알려주는 역할이다.
header에 컨텐츠 타입을 넣으면 

 

이름은 어려운데 손쉽게 쓸 수 있는 컨텐츠타입이라고함.
컨텐츠타입 자체가 application/x-www-form-urlencoded.
유알앨인코디드

 

해석을 하면

POST 
HTTP요청이 POST 방식이라는 걸 의미

/echo/post/form 
요청을 보내는 path을 의미함
요청을 어디에서 처리해야하는지
post라면 사용자가 sns에 게시글을 "작성할 떄" post에 많이 씀
echo는 여기서 형식적인거고

HTTP/1.1 
HTTP프로토콜을 사용을 하고
버전은 1.1이라는 뜻임.

 

아 firebase에 그 값이 이렇게 들어가는구나..사이트에서도 확인을 할 수 있다.

 

 

꿀팁인데
데이터 구조를 설계하는 회의에 초대를 해달라고하거나
회의가 끝나고 "구현 하기 전" 먼저 결과를 공유해달라고 부탁하기


62강 복습중

 

postman은 전세계에서 제일 많이 쓸 건데 api명세를 편리하게 볼 수 있고
테스트요청까지 잘 보낼 수 있는 좋은 도구다.

 

그래프ql방식으로 통신하면 옆에꺼 써도된다고함.

 

BEM은 전체적으로 이렇게 이루어집니다.

  • 항상 영어 소문자만을 사용합니다. 카멜 케이스 등은 사용하지 않습니다.
  • 일반적으로 한 요소는 하이픈으로 연결합니다. (예를 들면 input-text, button-submit, modal-alert 등등.. )
  • 네이밍의 조합은 형태-의미-순서-상태 순으로 사용합니다. (예시 button-submit-03-disable)
  • 언더스코어는 파일, 폴더, 이미지 등에만 사용합니다(image_elysia_asset_01.png)
  • 숫자를 사용할 때는 확장성을 고려해 1, 2 이런 식으로 표현하지 않고 01, 02, 03… 혹은 001, 002, 003처럼 사용합니다. 앞에 0을 붙이지 않으면, 이미지 정렬 시 1 다음에 2가 오지 않고 10이 오는 등, 정렬 순서가 엉망이 될 수 있기 때문입니다.

리액트가 생기고나서 달라진점이
라이프사이클을 "통해서" 컴포넌트를 관리할 수 있게된거라고함. 

 

원래는 클래스 기반의 컴포넌트로 컴포넌트가 관리가 되어있었는데
버전이 올라가면서 클래스 기반보다 함수형태의 컴포넌트가 리액트가 권장하게되면서
16.8버전의 리액트 부터 사용가능. 리액트 훅을 정말 많이 사용하게되었다고함.

 

 


전부다 빌트인 훅이다.
1. useState() 는 컴포넌트 안에서 상태관리하게해주는 "빌트인 훅" 
2. useEffect() 는 컴포넌트가 렌더링이 될 때마다 새롭게 매번 실행시켜줘야하는 로직을
다룰 수 있는 공간이다. 우리가 의존성을 줄 수 있어서 우리가 원하는 값들 몇가지를 설정을 해놓으면
값들이 바뀔 떄마다 "그 로직을 실행시켜줄 수 있게된다"
3. useRef() 는 가상dom형태로 리액트가 로직들을 관리를 하는데 실제로 dom에 접근을 해서
뭔가값을 수정하거나 행동을 하는 그러할 때 사용한다
4. 메모이제이션에 역활하는 useMemo와 useCallback 불필요한 값이나 함수가 매번 생성되는 것을 줄여줄 수 있다.

 

 

 

아웃소싱: 우리가 만든 로직들을 컴포넌트안에 두는 것이 아니라
그 복잡한 로직들을 자주 쓰이고 복잡하게 쓰는 것들을 "컴포넌트 밖으로" 빼서
재사용할 수 있게 만든다. 커스텀훅의 기본적인 원리이다.

 

지금 63강 하는 중임. 

4-18 브랜치를 따서 오라고함.

 

특정 브랜치만 clone 하는 방법 빈 폴더에 그냥 clone 하고 그 폴더 들어가면 된다.

 

PS D:\personal\supercoding-workspace-react-2\react-branch-4_18> git clone -b practice/4_18 https://github.com/dev-owen/react-fundamental.git
Cloning into 'react-fundamental'...
remote: Enumerating objects: 425, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (17/17), done.
Receiving objects:  97% (413/425)sed 15 (delta 15), pack-reused 393
Receiving objects: 100% (425/425), 455.02 KiB | 4.38 MiB/s, done.
Resolving deltas: 100% (166/166), done.
PS D:\personal\supercoding-workspace-react-2\react-branch-4_18> 

 

2개의 컴포넌트가 있는데 ForwardCounter랑 BackwardCounter 

 

-

checkout 원리를 잘 모르는 것 같네

 

GitHub Actions에서 가장 많이 사용되는 액션(Action)은 무엇일까요? 바로 저장소로 부터 코드를 내려받기 위한 체크아웃(Checkout) 액션인데요. 

 

그래서... 빈 폴더에 해야하는거아님? 

 

git branch를 하기 전에
git init를 해야하는건가?

 

git init은 나 이제 깃허브랑 연동할거야

라는 뜻이래

이제 깃허브에 올리기도 하고

뭐 그럴거야 

 

https://daco2020.tistory.com/73

 

git 이것만 알면 돼!

git 기본 명령어 정리 1. 원하는 디렉토리로 들어가 git 초기화하기(깃 연동 준비) git을 사용하기 위해서는 우선 원하는 디렉토리(폴더)로 들어가 git을 초기화 해주어야 합니다. 초기화라고 해서

daco2020.tistory.com

 

활용을 이런 식으로 할 수 있다. 

 

최근에 리액트 프로젝트를 진행하면서 setInterval을 사용할 일이 많아졌다.

채팅방 목록을 새로고침할 때 setInterval을 사용해서 주기적으로 HTTP 요청을 보낸다던가(polling),

채팅이 도착했을 때 setInterval을 사용해서 브라우저 탭 제목을 바꾼다던가 하는 식이다.

 

 

일단 state값을 가져야한다.

 

counter라는 state를 가지고 있고
카운터가 useEffect setInterval 즉 1초마다 함수가 실행되는데 

 

 

 

무슨 말인지 잘 모르겠어 쉽게 설명해줘..useEffect 함수가 새롭게 매번 실행해주잖아... setInterval함수를 그러면 새롭게 실행한 다음에 clearInterval를 매번 해줘야하는거야?

ChatGPT
네, 정확히 맞습니다! useEffect 함수는 컴포넌트의 상태나 프로퍼티가 변경될 때마다 실행됩니다. 따라서 setInterval 함수를 사용하여 타이머를 시작하면 타이머가 새로 시작되고 매번 useEffect가 실행될 때마다 새로운 타이머가 생성됩니다.

이것은 메모리 누수와 예상치 못한 동작을 초래할 수 있습니다. 그래서 우리는 타이머를 정리해야 합니다. clearInterval(interval)을 호출하여 이전에 생성된 타이머를 정리하고, 그렇게 하면 메모리 누수를 방지하고 정확한 동작을 보장할 수 있습니다.

return () => clearInterval(interval); 이 코드는 컴포넌트가 언마운트(unmount)될 때 실행되거나 의존성 배열에 있는 값이 변경될 때 실행되는 함수를 정의하고, 이 함수 내에서 clearInterval(interval)을 호출하여 타이머를 정리합니다. 이것은 컴포넌트가 사라지거나 다른 의존성이 변경될 때마다 이전 타이머를 정리하고 새로운 타이머를 시작하지 않도록 하는 역할을 합니다.

 

이게 맞다.

그러면 실행할 때마다 매번 타이머를 없애줘야한다는거지? clearInterval함수를 이용해서?

ChatGPT
네, 맞습니다. useEffect 내에서 타이머를 시작하는 경우, 매번 useEffect가 실행될 때마다 이전 타이머를 정리(clearInterval)하고 새로운 타이머를 시작하는 것이 좋습니다. 이렇게 하면 메모리 누수를 방지하고 정확한 동작을 보장할 수 있습니다.

다음과 같은 순서로 작동합니다:

컴포넌트가 처음으로 렌더링될 때 useEffect가 실행되고, 타이머가 시작됩니다.

만약 컴포넌트가 다시 렌더링되면(상태나 프로퍼티가 변경될 때), 이전 타이머를 정리하고(기존 clearInterval 호출) 새로운 타이머가 시작됩니다.

컴포넌트가 언마운트(unmount)되면 useEffect 내에서 정의한 정리 함수(return () => clearInterval(interval);)가 실행되고, 타이머가 완전히 정리됩니다.

이렇게 하면 매번 타이머를 적절하게 관리하여 예기치 않은 동작이나 메모리 누수를 방지할 수 있습니다.

 

 

 

clearInterval은 빌트인 함수 같은데 맞지?
ChatGPT
네, 맞습니다. clearInterval은 JavaScript의 내장 함수 중 하나입니다. 이 함수는 타이머를 제거하고 해당 타이머의 실행을 중지하는 데 사용됩니다. setInterval 함수와 함께 사용되어 일정 시간 간격으로 실행되는 반복 작업을 중지할 때 주로 활용됩니다.

clearInterval은 웹 브라우저 환경에서 제공되며, 타이머 관련 작업을 수행할 때 유용하게 사용됩니다. React나 다른 프레임워크에서도 clearInterval을 활용하여 타이머를 정리하고 메모리 누수를 방지하는데 사용됩니다.

 

디렉토리

src 폴더안에 hooks 이런 "디렉토리"를 만들어주게됩니다.

 

커스텀훅을 만들 때 네이밍 규칙은
use*** 이렇게 만들어야한다.

 

UI디렉토리에 있는데 이제 useCounter 라는 커스텀훅을 hooks 디레곹리에 만들어서 하나봐 

이거는 UI 디렉토리에 있는 ForwardCounter.js임. 

 

import { useState, useEffect } from 'react';
import Card from './UI/Card/Card';


const ForwardCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <Card>{counter}</Card>;
};

export default ForwardCounter;

훑어보던 중

store 디렉토리에 auth-context 

이 웹사이트가 auth-code 맨날 쓰는 거 귀찮지않냐 context를 써라 이렇게 적어놨는데...?

 

https://dayvster.com/blog/use-context-for-auth

 

import React, { useState, useEffect } from 'react';

const AuthContext = React.createContext({
  isLoggedIn: false,
  onLogout: () => {},
  onLogin: (email, password) => {}
});

export const AuthContextProvider = (props) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const storedUserLoggedInInformation = localStorage.getItem('isLoggedIn');

    if (storedUserLoggedInInformation === '1') {
      setIsLoggedIn(true);
    }
  }, []);

  const logoutHandler = () => {
    localStorage.removeItem('isLoggedIn');
    setIsLoggedIn(false);
  };

  const loginHandler = () => {
    localStorage.setItem('isLoggedIn', '1');
    setIsLoggedIn(true);
  };

  return (
    <AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn,
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

 

이 코드는 React 애플리케이션에서 사용자 인증 관련 정보를 관리하는 컨텍스트(Context)를 설정하는데 사용됩니다. 여기에서 AuthContext라는 컨텍스트를 생성하고 AuthContextProvider라는 컨텍스트 제공자(provider) 컴포넌트를 생성합니다.

여기에 대한 간단한 설명은 다음과 같습니다:

1.         AuthContext는 React의 createContext 함수를 사용하여 생성한 컨텍스트 객체입니다. 이 컨텍스트 객체는 사용자 인증과 관련된 정보를 제공하고, 이 정보를 필요한 컴포넌트에 제공할 때 사용됩니다.

2.         AuthContextProvider는 사용자 인증 정보를 관리하는 컨텍스트를 제공하기 위한 컴포넌트입니다. 이 컴포넌트는 사용자가 로그인하고 로그아웃할 때 상태를 관리하고, 이 정보를 하위 컴포넌트에 전달합니다.

•          isLoggedIn: useState 훅을 사용하여 사용자의 로그인 상태를 관리하는 상태 변수입니다. 초기값은 false로 설정되어 있습니다.

•          useEffect: 컴포넌트가 마운트될 때 localStorage에서 사용자 로그인 정보를 확인하고 isLoggedIn 상태를 설정하는 부분입니다. 이렇게 함으로써 페이지를 새로고침해도 사용자 로그인 상태가 유지됩니다.

•          logoutHandler 함수: 사용자 로그아웃을 처리하며, localStorage에서 로그인 정보를 제거하고 isLoggedIn 상태를 false로 설정합니다.

•          loginHandler 함수: 사용자 로그인을 처리하며, localStorage에 로그인 정보를 저장하고 isLoggedIn 상태를 true로 설정합니다.

3.         <AuthContext.Provider>는 컨텍스트의 값을 하위 컴포넌트에 제공하는 역할을 합니다. value 속성을 사용하여 컨텍스트의 값으로 isLoggedIn, logoutHandler, loginHandler 함수를 제공합니다. 이렇게 하면 하위 컴포넌트에서 이 컨텍스트에 접근하여 사용자 인증 관련 작업을 수행할 수 있습니다.

•          <AuthContext.Provider> 내부에 포함된 {props.children}는 이 컴포넌트의 자식 요소들을 포함하는 부분으로, AuthContextProvider를 사용하는 곳에서 정의한 하위 컴포넌트들을 렌더링합니다.

AuthContext.Provider는 React 컨텍스트의 일부입니다. AuthContext는 이 컨텍스트를 생성한 객체이며, AuthContextProvider는 이 컨텍스트를 활용하여 사용자 인증 관련 정보를 애플리케이션 전체에서 공유하고 관리할 수 있도록 도와주는 컴포넌트입니다. AuthContext.Provider에 정의한 값을 이용하여 로그인, 로그아웃 상태를 애플리케이션의 여러 컴포넌트에서 사용하고 관리할 수 있습니다.

 

 

 

 

컴포넌트가 마운트 된다는 말이 무슨 말이야?

 

ChatGPT

"컴포넌트가 마운트 된다"는 용어는 React 컴포넌트 라이프사이클에서 사용되는 개념 중 하나입니다. 이 용어는 컴포넌트가 화면에 나타나고 렌더링되어 사용자에게 표시되는 시점을 나타냅니다.

 

React 컴포넌트는 세 가지 주요 단계로 나눌 수 있습니다:

 

마운트(Mounting): 컴포넌트가 생성되고 화면에 나타나는 과정입니다. 이 단계에는 constructor 메서드(클래스형 컴포넌트) 또는 함수형 컴포넌트의 함수 부분이 호출되고, 컴포넌트가 화면에 나타납니다. componentDidMount 또는 useEffect 훅을 사용하여 이 단계에서 초기화 작업을 수행할 수 있습니다.

 

업데이트(Updating): 컴포넌트가 업데이트되는 과정입니다. 업데이트는 컴포넌트의 상태나 프로퍼티가 변경될 때 발생하며, render 메서드(클래스형 컴포넌트) 또는 함수형 컴포넌트의 함수 부분이 다시 호출됩니다. componentDidUpdate (클래스형 컴포넌트) 또는 useEffect 훅을 사용하여 업데이트 시 추가 작업을 수행할 수 있습니다.

 

언마운트(Unmounting): 컴포넌트가 화면에서 사라지고 제거되는 과정입니다. 이 단계에는 componentWillUnmount (클래스형 컴포넌트) 또는 useEffect 훅에서 반환한 정리(clean-up) 함수가 호출되며, 컴포넌트는 화면에서 제거됩니다.

 

"컴포넌트가 마운트 된다"는 것은 주로 컴포넌트가 처음으로 화면에 나타날 때의 상태를 의미합니다. 이 시점에서 초기화 작업을 수행하거나 데이터를 불러와 화면에 표시하는 등의 작업을 수행할 수 있습니다.

 

 

초기화 작업이라는게 뭐야?

ChatGPT

"초기화 작업"은 컴포넌트가 화면에 나타나기 전에 수행해야 하는 작업을 의미합니다. 이러한 작업은 주로 컴포넌트가 처음 마운트될 때 수행되며, 컴포넌트의 상태를 설정하거나 외부 데이터를 가져와서 컴포넌트에 적용하는 등의 작업을 포함할 수 있습니다. 초기화 작업은 컴포넌트의 초기 상태와 동작을 설정하는 데 사용됩니다.

 

React에서 초기화 작업은 주로 다음과 같은 작업을 포함할 수 있습니다:

 

상태(State) 초기화: 컴포넌트의 상태 변수를 초기 값으로 설정합니다. 이렇게 하면 컴포넌트가 처음으로 렌더링될 때 원하는 초기 상태를 가질 수 있습니다.

 

외부 데이터 불러오기: 외부 API에서 데이터를 가져오거나 로컬 스토리지에서 데이터를 불러와 컴포넌트에 적용합니다. 예를 들어, 사용자 프로필 정보를 가져와 화면에 표시할 수 있습니다.

 

이벤트 리스너 등록: 사용자 상호 작용을 처리하기 위해 이벤트 리스너를 등록합니다. 예를 들어, 버튼 클릭 이벤트를 처리하는 핸들러를 등록할 수 있습니다.

 

외부 라이브러리 초기화: 컴포넌트에서 사용하는 외부 라이브러리를 초기화하고 설정합니다. 예를 들어, 차트 라이브러리나 지도 라이브러리를 사용하는 경우 초기화 작업이 필요할 수 있습니다.

 

기타 설정 및 준비 작업: 컴포넌트의 동작을 위해 필요한 기타 설정 및 준비 작업을 수행합니다.

 

초기화 작업은 주로 컴포넌트의 생명주기 메서드 (클래스형 컴포넌트) 또는 useEffect 훅 (함수형 컴포넌트) 내에서 수행됩니다. 이를 통해 컴포넌트가 화면에 나타나기 전에 필요한 모든 설정과 작업을 수행할 수 있습니다.

 

<62강 다시 돌아감 강의 내용>

 

hooks에 useCounter.js파일만들고
UI에 폴더에 있던 ForwardCounter 파일에서

엄청 중요한 말이야
ForwardCounter에서 "재사용하고자"하는 로직들 일단 그대로 가져오면 됩니다.

 

<띵언>

forwardCounter와 backwardCounter에서 중복되는 부분을 
hook으로 만들면 좋을 것 같다고 말함.

 

 

<띵언>

사소한 차이는 있지만 이 부분이 비슷해서 통째로 hook으로 뺴내볼려고한다고함.

 

내부에 일부부분

 

  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

 

그러면 useCounter에 useState도 있고 useEffect도 있다는 걸 볼 수 있습니다.

왼쪽에 받아야할게 counter라고 하고 드래그를 하는데?

 

 

counter는 넘버타입의 값인데
0부터 시작해서 1씩 증가하거나 감소하는 것

 

그 값을 우리가 받아줘야하기때문에
오른쪽에서 return counter;라고해서 
useCounter에서 관리하고 있는 state 
우리가 counter를 이 useCounter 커스텀훅에서 가지고와서 사용을 해야하기 때문에
그래서 오른쪽에 return counter; 해야한다.

 

이렇게 아래와 같이

 

import React from 'react';

const useCounter = () => {
   
    const [counter, setCounter] = useState(0);

    useEffect(() => {
      const interval = setInterval(() => {
        setCounter((prevCounter) => prevCounter + 1);
      }, 1000);
 
      return () => clearInterval(interval);
    }, []);

    return counter;

};

export default useCounter;

 

useCounter(); 라고 쓰기만 하면
우리가 만든 커스텀 훅 useCounter를 import할 수 있다고함

 

이거를 

 

import잘 안되면 한 글자 지우고 다시 불러오게끔 하기.

import React, { useEffect, useState } from 'react';

const useCounter = () => {
   
    const [counter, setCounter] = useState(0);

    useEffect(() => {
      const interval = setInterval(() => {
        setCounter((prevCounter) => prevCounter + 1);
      }, 1000);
 
      return () => clearInterval(interval);
    }, []);

    return counter;

};

export default useCounter;

 

어떤 플래그값을 props로 줄 수 있다는데
하나는 +1 씩 증가하는거고
하나씩 -1씩 감소하는거잖아

그러면 useCounter에서 Counter = () => {} 이 부분에서
()이 부분에
isForward라는 boolean타입의 값을 하나 줌으로써
여기에서 만약에
그러면 isForward가 true 면 1 false면 -1 
1이면 1초마다 1 더해주고, -1면 1초마다 - 해주고 
"이렇게 로직을 구별할 수 있다.
setCounter((prevCounter) -> prevCounter + 1 * (isForward ? 1 : -1 ));

 

실제 코드

 

import React, { useEffect, useState } from 'react';

const useCounter = (isForward) => {
   
    const [counter, setCounter] = useState(0);

    useEffect(() => {
      const interval = setInterval(() => {
        setCounter((prevCounter) => prevCounter + 1(isForward ? 1 : -1));
      }, 1000);
 
      return () => clearInterval(interval);
    }, []);

    return counter;

};

export default useCounter;

 

그리고 forwardCounter.js에 가서 useCounter(true)이렇게 넣어줘야함
backwardCounter.js가서는 false 넣어주고

그냥 컴포넌트안에 함수에서 매개변수 만드는거는 아무데나 할 수 있는 것 같은데? 

 

true/false가 props로 받아지게되고 그 값을 
(isForward ? 1 : -1) 에서 1를 더해줄지 -1를 더해줄지 결정하게 된다.

 

 

 

 

 

 

 

it can be caused because of your github account in VS-Code or because of your role in the project.In my case, I loged out in vs code and loged in again by right account.

 

 

이게 내가 원하는게 아닌데...?

내 리포지토리에 push를 하고싶은데 그런 개념이 아닌가?

 

pull request 잘 정리해놓음. 

 

https://blog.naver.com/hunii123/222438830784

 

[깃허브로 협업하기] 4. 협업에서 브랜치 사용하기 : 2_풀 리퀘스트(pull request)로 푸시한 브랜치 병

#git #github #깃허브 #pullrequest ※ "Do it! 지옥에서 온 문서 관리자 깃&깃허브 입문" ...

blog.naver.com

 

https://medium.com/axisj/github-fork-%EC%97%90%EC%84%9C-pull-request-%EA%B9%8C%EC%A7%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-merge-a22bdd097283

반응형