반응형

유데미 사이트에 있는 리액트 유료강의를 보며 정리한 내용입니다. 정리라기엔 그냥 필기수준

보면서 정리하는대로 꾸준히 추가중입니다

https://www.udemy.com/course/best-react/

 

【한글자막】 React 완벽 가이드 with Redux, Next.js, TypeScript

Javascript부터 웹 어플리케이션 배포까지, React와 프론트엔드 최신 기술을 가장 쉽고 확실하게 배우는 법

www.udemy.com

할인기간에 싸게 구매하였는데 이 강의를 추천합니다.

쉽고 잘 이해가 가게 설명을 해주시고 자막을 보면서 해서 불편할 수 있지만

저는 오히려 자막을 보면서 음악을 듣거나 할수있어서 편했습니다,,

대신 자막에 의존하게 되서 오역이 있다면 안좋겠지만요.. 하지만 지금까지는 오역이 없었던 것 같고 이해하는데 어렵지도 않았습니다

 

저는 섹션1을 듣고 난 후 섹션 29 요약부분을 먼저 듣고 있습니다

요약 부분을 들은 후 일단 코드를 짜보고 싶어서요!

코드를 짜면서 막히거나 더 공부를 해야겠다는 생각이 들때 그때 나머지 부분을 들을 생각입니다

자바스크립트나 다른 프로그래밍 언어를 이용한 경험이 있다면 강의 내용이 크게 어렵지는 않을 거라고 생각합니다

하지만 경험이 없고 처음이라면 다소 어렵게 느껴질 것이라고 생각합니다.

최소한 JS는 안다는 전제하에 강의를 설명하시는 것 같아서 JS는 먼저 공부하고 보시길 추천합니다 


1. prettier 단축키 : Shift+Alt+F 
2. visual studio code에서 Terminal-new Terminal해서 밑에 터미널 창을 열수있음
3. IDE의 터미널 창에서 npm start를 하면 프로젝트가 실행됨
4. npm install하면 폴더 안에 node_modules 폴더와 안에 파일이 생김
5. 자바스크립트 구문 안의 HTML 코드는 빌드 단계를 통해서만 사용 가능하다.
이런 코드를 jsx 코드라고 부른다
6. jsx 코드(=자바스크립트 내에 있는 HTML코드)를 리턴하는 (js 파일에서의)함수를 리액트 컴포넌트라고 한다. HTML element 처럼 사용할 수 있다.
7. 리액트 컴포넌트라고 부르는 사용자 지정 HTML element(jsx코드를 리턴하는 함수)는 결국은 함수이다
8. 리액트에서 컴포넌트로써 동작하고 컴포넌트로 이용되는 함수는 반드시 결과값을 리턴해야한다. 리턴값이 브라우저에 렌더링될 수 있도록!
리턴값은 텍스트일 수도 있고 다른 것일 수도 있다. 보통은 HTML 코드인 경우가 많다
9. 리액트 컴포넌트는 jsx를 리턴하는 함수.
10. 함수에서 리턴할때 jsx코드를 쓸때 class를 추가할때 태그 옆에 class를 쓰는 것이 아닌 className을 써야한다. 
HTML이 아닌 js이기 때문. 바닐라 js에서는 클래스 이름을 지정할때 className이라고 지정함
예시)<div className='card'></div>
11. export할때는 export default Todo; 이 구문을 사용한다. 여기서 Todo는 함수명임
12. 리액트 컴포넌트에서 함수명은 항상 대문자로 시작해야한다.
HTML코드의 컴포넌트로 사용할 때(즉, 사용자 정의 HTML element로 사용할 경우) 대문자로 시작해야만 내장 HTML element와 구분할 수 있기 때문!
리액트가 내부적으로 <div>, <h1>과 같은 내장 HTML element와 사용자 정의 element를 구분할 때 대소문자로 구별하기 때문
13. 다른 js 파일에서 작성한 리액트 컴포넌트를 js파일에서 return할때 HTML element처럼 사용할 수 있음
다른 파일에서 작성한 사용자 정의 element를 사용하려면 import를 해야한다.
예시)import Todo from './components/Todo';
((여기서 확장자가 .js일 경우 생략가능))
14. 매개변수를 받아 처리를 하려면 function Todo(props) { //... } 이런식으로 props를 적어준다.. 이름은 바꿀 수 있지만 보통은 props라는 이름을 사용한다. props의 뜻은 객체이다.
<Todo text="Learn React"/> 이런식으로 사용하면 props에 key-value 쌍으로 저장된다. 
위에서는 속성 이름인 text가 key가 되고 속성 값인 Learn React가 value가 된다.
그렇게 하면 Todo 컴포넌트 안의 props는 text 속성을 가진 객체가 된다.
text 속성마다 각기 다른 값을 가질 수 있고 속성 이름을 다르게 해도 괜찮다.
Todo 컴포넌트에 text속성을 사용하려면 props.text 이런식으로 사용하면된다.. 
매개변수명.속성명
jsx 코드에서 동적 표현식을 표현하려면 이건 단순 텍스트로 취급하면 안되고 자바스크립트로 취급하라고 지시하면 된다.  중괄호 한 쌍 { } 를 사용하면 됨.. 중괄호 안에 자바스크립트 코드를 입력하면 된다.
{props.text}
중괄호 안에서는 if문과 같은 구문은 쓰면 안된다.
15. 바닐라JS(리액트를 사용 안한 JS)에서는 버튼에 클릭 이벤트를 주려면 
document.querySelector('button').addEventListener('click')이런식으로 어떤 일이 일어냐아 하는지를 기술하는 방식인 명령형 접근을 사용하였다..
리액트에서는 목표 결과를 기술하는 방식인 선언형 접근을 사용한다.
여기서 중요한 것은 리액트 컴포넌트에서 return 에 사용하는 모든 HTML element들이 결국은 리액트 컴포넌트들이라는 것이다. 리액트에 내장되어 있는 컴포넌트들이다. 그렇기 때문에 거기에 속성을 추가할 수 있다. 그냥 HTML에서는 그렇게 할 수 없다.
onClick 속성을 추가할 수 있다.. onClick은 이름에서 봐서 알수 있듯이 클릭시 반응하는거임
<button onClick = //.... ></button>
여기서 onClick 속성에 할당하는 값은 텍스트나 문자열이 아닌 동적 표현식이다. 클릭이 일어났을 때 수행할 함수가 들어있는 동적 표현식이다

onClick={function () {}}
익명의 인라인 함수를 정의하거나
onClick = {() => {}}
화살표를 이용한 함수 구문을 사용해도 된다.
또는 다른 함수를 지정해도 된다.
새로운 함수를 지정할때는 Todo함수 안에 중첩된 함수로 만들어야한다.
deleteHandler라는 함수를 만들었으면
onClick={deleteHandler} 이런식으로 사용할 수 있다.
onClick={deleteHandler()} 이렇게 소괄호 ()를 붙이게 되면 이 줄에 있는 코드가 검증될때 함수가 실행되기 때문에,, ()는 빼줘야한다. 
코드 검증은 브라우저에 이 내용이 렌더링되기 전에 일어난다
()를 빼고 이름만 적어주게 되면 이것은 함수이고 이 element가 클릭되었을 때 실행해야 한다고 알려주는것이다.

16. 각각의 빌딩 블록은 각각의 파일에 즉, 컴포넌트들은 각자 파일 안에 넣고 너무 긴 JSX 블록은 피하고 되도록 개별 컴포넌트로 쪼갠다.
일반적으로 컴포넌트 함수는 작고 유지보수 쉽게 하는 것이 좋다
한 컴포넌트에 속해 있는 JSX 코드는 너무 복잡해서는 안된다.
17. 모든 컴포넌트 함수가 매개변수를 가진다. 
매개변수명은 마음대로 지정할 수 있지만 보통은 props를 사용한다
18. react를 임포트할때는 서드파티 라이브러리이기 때문에 ./를 쓰지 않고 아래와 같이 사용하면 된다.
밑의 import코드는 useState 함수를 import한다는 것이다..
import {useState} from 'react';
useState함수는 react 라이브러리에서 나왔고 컴포넌트 함수에서 사용할 수 있다.

 

19. useState(); 는 컴포넌트 함수 안에 바로 써야한다. useState는 소위 말하는 리액트 훅이다.
리액트 훅은 컴포넌트 함수나 커스텀 훅 안에서 바로 호출되어야 한다.
일단은 리액트 컴포넌트 함수에서 리액트 훅을 사용할 수 있다는 점만 알면 된다.
useState를 호출하면 state 리액트가 생성된다.
이 state에 초기값을 줄 수 있다. useState에 인자로 전달할 수 있다.
초기값으로 false를 넘겨주려면 밑에와 같이 작성하면 된다.
useState(false)

useState는 항상 배열을 리턴하는데 이 배열에는 두 가지 element가 있다.
그 리턴하는 배열을 상수에 저장하거나 배열을 destructuring 할 수 있다.
항상 두 개의 element가 있고 각각의 상수에 할당될 것이다.
const [modalIsOpen, setModalIsOpen] = useState(false);

function deleteHandler() {
    setModalIsOpen(true)
  }

리턴된 배열의 첫번째 element는 현재 state의 snapshot이다.
두번째 element는 state값을 변경할 수 있게 하는 함수이다.
새로운 값을 첫번째 element에 할당하는 식으로는 state를 바꾸지 못한다.
대신에, 두 번째 함수(=두번째 element)를 호출해서 새로운 값을 지정해야 한다.
state 변경 함수(=두번째 함수, 두번째 element)를 호출할 때마다 리액트는 이 state가 속해 있는 컴포넌트 함수를 재실행해서 코드를 재검증하기 때문에 중요하다.
아울러, 화면에 렌더링된 내용을 업데이트하고 이를 통해 최신 state 값을 가져올 수 있고 state에 따라 다른 결과물을 렌더링할 수 있다.

20. return 에서 동적 표현식 { } 과 삼항 연산자 ? :를 통해 코드를 작성할 수 있다.
{modalIsOpen ? <Modal /> : null}
중괄호 안에 쓴 동적 표현식 안에 다시 JSX를 사용할 수 있다.

21.{modalIsOpen && <Modal />}
JS에서 이런식으로 && 를 2개 쓰는 연산자를 사용하면 양쪽이 모두 True일 경우, 두번째 값이 반환된다.

22. 자바스크립트에서 함수는 일급 객체이다.
함수도 어디든 value로 전달할 수 있다.
23. 올바른 React Router 버전 사용하기..
라우팅을 추가하는 데 도움을 주는 추가 패키지를 설치할때 아래 명령을 실행해 올바른 버전을 설치해야 한다.
npm install --save react-router-dom@5
>>강의에서는 5버전을 사용하여 설명하고 있어서 이것을 다운받아서 해야함. 
최신 릴리즈 버전은 v6이다.. 이에 관한거는 298강에서 다루고 있음.. 
npm install react-router-dom 으로 터미널에 입력해서 추가하면 최신의 버전이 다운됨..
react-router-dom의 버전은 리액트 프로젝트의 package.json을 통해 확인할 수 있고, npm install하여 설치할때 명령어를 입력할때는 서버를 다운하고(ctrl+c) 해야한다.  
24. 라우팅을 추가한다는 것 : 라우팅 툴을 우리 코드에 추가해서 URL 변화를 감지하도록 한다
즉, 이 라우팅 툴이 어떤 컴포넌트를 어떤 경로, 어떤 URL에 로딩할지 인지하도록 해야한다.
src 폴더에 componenets폴더와 pages 폴더를 생성해서 컴포넌트 안에 임베드 될 컴포넌트들은 components 폴더에 저장하고 페이지로 로딩될 컴포넌트들은 pages 폴더에 저장하는 식으로 구분하여 하면 좋음~
25. index.js에서 렌더링되고 있는 App 컴포넌트를 다른 컴포넌트로 감싸주려면 'react-router-dom'라이브러리에 있는 BrowserRouter를 import 해줘야한다.
import { BrowserRouter } from 'react-router-dom';
>> BrowserRouter 자체도 컴포넌트이다. 그러므로 HTML element로 사용할 수 있다.
opening tag와 closing tag를 사용해서 App 컴포넌트를 감싸준다.
<BrowserRouter>
    <App />
</BrowserRouter>
>>이것은 예전에 본 적 없는 비표준 컴포넌트이다. 
비 HTML 컴포넌트에 의해 감싸져 있는 형태임 
26. 라우터 패키지 초기화 
: 라우터가 이 앱을 인식하고 URL을 감지하게 한다. 
다음 단계는 지원하고자 하는 URL을 정의하고 각 URL 별로 어떤 페이지를 로딩할건지를 결정해야함 -> 이것은 App 컴포넌트(저기(BrowserRouter태그)에 감싸져 있는 컴포넌트인 App 컴포넌트 !!)에서 할것임

27. 다른 컴포넌트를 사용하고 싶을때 (?) 
App.js에 import { Route } from 'react-router-dom'; 을 추가해줌 
BrowserRouter처럼 Route도 컴포넌트임
보통 컴포넌트처럼 사용할 것임, 그런데 이 컴포넌트가 URL 내의 각기 다른 경로를 정의하고 또, 각 경로별로 어떤 컴포넌트를 로딩할 건지 결정한다. 
그러므로 로딩하고 싶은 컴포넌트를 import 해준다.

컴포넌트 return(=JSX)에서 Route 컴포넌트를 사용한다.. path prop도 넣기
path는 문자열을 받는데 그 문자열이 URL에서 도메인 다음 나올 경로이다.
예를 들어 my-page.com 이 도메인이라면 
my-page.com/blabla  이 페이지에서 path에는 blabla가 해당하겠다
path는 경로에 따라 어떤 컴포넌트가 로딩될 건지 결정하는 것이다.
/ 는 도메인 다음에 아무것도 없는 것으로 디폴트 경로(=기본 경로)이다.
Route 태그 사이에 실제로 로딩될 컴포넌트를 집어넣는다.
<Route path='/'>
      <AllMeetupsPage />
</Route>

밑의 코드는 도메인주소/new-meetup 을 방문하면 Route 태그 사이에 렌더링되는 컴포넌트가 로딩될 것이다.
<Route path='/new-meetup'> 
//... 
</Route>

28. '/favorites' 경로는 '/favorites'와 '/' 둘 다 포함하고 있기 때문에 두 컴포넌트가 다 렌더링된다. 이런 화면을 원하지 않는다면 react-router-dom에서 제공하는 다른 컴포넌트인 Switch가 필요하다.
import { Route, Switch } from 'react-router-dom';

Switch 컴포넌트로 모든 Route 컴포넌트를 감싸주는 식으로 사용한다.
<Switch>
  <Route path = '/'>
    <AllMeetupsPage />
  </Route>
// ....
</Switch>

이렇게 하면 리액트 라우터에게 여러 Route 중에서 단 하나만 활성화하라고 지시할 수 있다.
즉, 최대 하나의 페이지만 렌더링되는 것이다.
Switch는 위에서부터 코드를 실행해서 매칭되는 경로를 찾으면 더 이상 다른 Route를 찾지 않고 거기서 멈춘다(=종료된다).
즉, '/favorites'는 '/'로 시작하니까 Switch 때문에 
'/'를 발견하면 더 이상 검색하지 않는다.
이것을 방지하려면  Route 에 exact prop을 추가해야한다. exact={true} 으로..
true일때는 prop 이름만 쓰면 true로 값을 설정한다.
<Route path='/' exact={true}>
<Route path='/' exact> //위와 같음

이렇게 써주면 exact prop때문에 리액트 라우터가 '/' 로 시작하는 경로를 매칭하지 않고 전체 경로로 검색하게 된다.
즉 '/favorites'는 '/'와 매칭되지 않는다.
그래서 계속 검색해서 결국 '/favorites'를 찾을 것이다.

*지금은 URL을 일일이 입력하며 화면을 확인하고 있는데 실제로는 이러지 않고, 현실에선 아마 화면 상단에 헤더나 내비게이션 메뉴가 있어서 여러 링크를 제공할 것이다.


29. 원래는 태그 안에 있는 속성들은 attribute라고 불렀었는데 리액트에서는 prop이라고 부르는 가 보다..

30. 네비게이션 메뉴에 관한 컴포넌트는 라우터와는 전혀 상관이 없다. 라우터를 통해 페이지로 로딩되지 않는다.
대신에 다른 코드들 사이에 삽입될 것이다.

31. 컴포넌트 함수를 정의하고 나서는 늘 그랬듯이 함수를 export해주고(예: export default MainjNavigation;) 그리고 JSX 코드를 반환(return)해야한다. 

32. 표준 HTML에서는 보통 링크를 <a> 태그를 이용해서 렌더링한다. JSX 코드에서도 가능하지만 이 방법을 이용하면 새로운 request가 서버로 전달된다는 단점이 있다.
-여기서 서버란, 리액트 앱을 호스팅하는 서버를 말한다.
서버가 응답을 보내면 라우터가 어떤 페이지를 로딩할 지 다시 판단해야한다.
링크가 동작하긴 하지만 서버로 요청을 보내게 되서 굳이 필요하지 않다.
이미 리액트 애플리케이션이 실행되고 있는데 다른 링크로 옮기려고 페이지에서 완전히 나갈 필요는 없다.
즉, 서버로 새로운 request를 보내는 건 불필요한 일이다. 
그렇기 때문에 <a> 태그 보다는 'react-router-dom' 패키지에서 제공하는 다른 컴포넌트인 Link 컴포넌트를 사용한다.
-Link 컴포넌트를 사용하기 위해서는 import 해야함
import { Link } from 'react-router-dom';

그렇게 import를 하고 나면 <a>태그 대신 <Link>로 텍스트를 감싸줄 수 있다.

<Link> 태그를 사용하여도 내부적으로는 <a>태그를 렌더링하겠지만, react-router-dom이 <a> 태그에 클릭 리스너를 추가한다. 클릭하면 브라우저가 서버로 request하는 것을 방지하고 옮겨가려는 경로의 URL을 해석해서 브라우저의 URL 창을 변경한다.
그러면 서버로 request를 보내지 않고도 리액트와 자바스크립트 만으로도 적절한 컴포넌트를 화면에 로드할 수 있다.
이렇게 하면, 추가 request를 보내지 않고도 이미 로딩된 페이지에 머물러 있을 수 있음
그래서 Link를 사용하는 것임

33. Link에 이동할 경로를 설정하려면 Link 태그 안에 to prop을 추가해야한다. to prop은 Link 컴포넌트를 사용할 때 자주 쓰는 prop 중 하나다.
예를 들어 <Link to='/'>blabla</Link> 라고 to에 /을 적어주면 App.js에서 설정한 <Route path='/'> 에 해당하는 컴포넌트인 AllMeetupsPage 의 경로를 뜻하는 것이다.

34. 프로젝트를 만들 때 사용한 툴이 스타일을 컴포넌트에 한정하는 기능을 내장한 프로젝트를 제공함 -> 이 기능을 CSS 모듈 이라고 함
내부적으로 코드 변환이 일어나면서 특정 컴포넌트에 css 파일을 매핑할 수 있는 기능.
이 기능을 사용하려면 우선 css 파일 이름을 특정 방식으로 지정해야 함.
반드시 이름이 '.module.css' 로 끝나야 함
예시 : MainNavigation.module.css
그렇게 파일을 만들고 나서 js에 css 파일을 import 해오면 됨 (표준 자바스크립트에서는 작동 안함..!)
이렇게 css 파일을 import 하면, 내부적으로 빌드 프로세스가 알아서 추가해 준다.

스타일 파일과 클래스들을 컴포넌트에 추가하려면 css 파일을 import 해오면 안되고 예를 들어 class 같은 무언가를 import 할 것이다.
예시 : import classes from './MainNavigation.module.css';

여기서 import 하는 것이 자바스크립트 객체인데 css 파일에서 정의한 모든 css 클래스들이, 이 객체의 속성이 되기 때문이다.

이제 이 객체를 JSX 코드에서 사용해서 클래스를 element에 추가하면 내부적으로 코드가 변환되면서 클래들의 이름이 각 컴포넌트에 대해 유일하도록 바뀐다.

예를 들어 JSX element인 <header>에 className prop을 추가하고 여기에 하드 코딩된 문자열이 아닌 중괄호를 이용해 동적인 값을 지정할 수 있습니다.
예시 : <header className={classes.header}>

이제 예시에서 사용한 classes.header를 사용할 수 있다. 
왜냐하면 classes.header를 통해 css 파일에 접근할 수 있기 때문이다.
이 안에(MainNavigation.module.css 파일 안에 .header) 지정하는 모든 스타일은 이 컴포넌트에만 적용될 것이다.
만약 다른 컴포넌트에 다른 header 클래스가 있다 해도 스타일이 적용되지 않을 것이다. 스타일의 범위가 한정되어 있기 떄문이다.

35. JSX element로 구성된 리스트를 어떻게 동적으로 렌더링하는지, 리스트에 있는 데이터를 어떻게 JSX element로 변환하는지를 알아야 한다. 
왜냐하면 JSX에서는 JSX 배열로 렌더링할 수 있다. 즉, 중괄호를 이용해서 동적 표현식을 쓰고 그 안에 배열을 추가하면 된다.
예시 : {[<li>Item1</li>, <li>Item2</li>]}
이렇게 하면 리액트가 자동으로 배열을 렌더링한다.
이게 중요한 이유는 객체 배열을 JSX element의 배열로 변환하면 밑에서 출력할 수 있기 때문이다.
자바스크립트에는 한 배열을 다른 데이터의 배열로 변환하는 방법이 있다. 
자바스크립트에 존재하는 함수로 .map()을 사용하면 된다.
예시 : {DUMMY_DATA.map()}
DUMMAY_DATA 라고 변수명을 정한 상수에 내장함수인 map 사용

{DUMMY_DATA.map(() => {})}
화살표 함수 사용.. (() => {})
배열 내의 모든 element를 말함
이 element를 입력 값으로 받는다.
여기에선 meetup이 자동으로 입력값이 된다.
이 함수는 자바스크립트가 자동으로 호출하는 함수이기 떄문이다. ((사실 이 말은 이해를 못했음.. 484강 데이터 목록 출력하기에서 3분쯤.. )) 

map을 호출한 결과값은 변환된 데이터가 들어있는 새로운 배열이다. 그리고 리스트 아이템으로 반환할 수 있다.

36. 리스트와 관련해서 한 가지 명심할 점이 있다. 크롬 개발자 도구를 열면, 경고 문구가 보인다. 각 리스트의 자식 노드들은 고유한 key prop을 가져야 한다는 것인데 이것은 리액트에서 리스트를 효과적으로 업데이터하고 렌더링하기 위해서 지켜야할 사항이다.
각 리스트(li 태그 안)에 key prop을 추가하고 거기에 고유한 값(예를 들면 id 같은 값) 을 지정해야 함
예시 :  <li key={meetup.id}>{meetup.title}</li>

이렇게 map을 이용하면 리스트를 간단히 렌더링할 수 있다.
일반적으로 리액트에서 이런 식으로 리스트를 렌더링한다. 하지만 나중에는 JSX 요소의 모든 배열을 어떤 배열이든 상관없이 렌더링 할 수 있다.

37. 컴포넌트와 애플리케이션은 가능한 작고 재사용 가능한 조각으로 분리하는 것이 좋다.

38. 함수를 만들면 함수를 export해줘야함~~

예시 : 
function MeetupItem() {}

export default MeetupItem;

39. 기본 HTML 태그 중에 <address> 도 있구나.. 신기하다

40. 모두 동적이고 하드코딩을 하지 않고, 외부 컴포넌트로부터 전달되어야 하는 것을 소위 부모 컴포넌트라고 한다. --> props을 받아서 사용함

41. 객체 배열을 JSX element의 배열로 매핑하는 방식
그러면 JSX element가 리스트 아이템이 될 것이다.
예시 : {props.meetups.map(meetup => <MeetupItem />)}

42. key prop은 리액트에 내장된 특수한 prop 중 하나이며 어떤 컴포넌트에도 사용할 수 있다. 
사용자가 만든 컴포넌트에서도 별도로 코드를 작성할 필요 없이 그냥 사용 가능하다. 어디서든 key prop을 사용할 수 있다.

43. prop을 넘겨줄때
<MeetupItem
          key={meetup.id}
          id={meetup.id}
          image={meetup.image}
          title={meetup.title}
          address={meetup.address}
          description={meetup.description}
/>
이런식으로 개별 prop을 하나하나 넘기는 식으로 작성할 수도 있지만
<MeetupItem
         meetup={meetup}
/>
이런식으로 통째로 넘기는 식으로 작성할 수도 있다.
2번째 방법인 meetup prop에 meetup을 통째로 넘기는 것은
 MeetupItem 컴포넌트 안에서 통으로 받은 meetup을 분해해야한다


44. 
function Card() {}

export default Card;
이것은 간단한 함수형 컴포넌트이고 export한것임

45. 컴포넌트를 만들어서 JSX 콘텐츠를 감싸는 방식은 많이 사용한다. JSX 콘텐츠를 이 컴포넌트 안에 삽입하는 것이다.

이런 경우에는 prop을 넘겨주는 방법이 있다.
(1) element에 속성을 추가하는 식으로 prop을 넘기는 방법
new (2) 오프닝과 클로징 태그 사이의 콘텐츠를 넘기는 식으로 할 수 있음.. 그 콘텐츠는 감싸고 있는 컴포넌트의 특수한 prop으로 노출된다.
그것은 컴포넌트에 props를 넣고..? {props.children} 을 사용하면 된다.

예시 : 
function Card(props) {
    return <div className={classes.card}>
        {props.children}
    </div>
}

children prop은 특수한 prop이다.
children prop은 모든 컴포넌트가 기본적으로 받아들이는 prop이다. 이 children은 항상 콘텐츠를 갖고 있는데 오프닝과 클로징 태그 사이의 컴포넌트 텍스트가 그 내용이다.
즉, 이 JSX 콘텐츠가 children prop에 저장되는 내용이다. 

--> 486강 props.children을 사용하여 어쩌고 3분 40초쯤


46.
label 에 for 속성을 htmlFor 이라고 적는다. 
이것은 className과 더불어, 일반 HTML 속성 이름과 다른 속성 이름을 사용하는 또 다른 예외이다. 사실상 className 외에 유일한 예외로 꼭 기억해야한다.

47.
리액트로 파일 업로드 구현하는방법:
https://academind.com/tutorials/reactjs-image-upload

48.
input태그와 textarea태그 안에 required 속성을 넣어주면 브라우저 내의 유효성 검사를 한다.

49.
폼이 submit 되었다는 것을 감지하려면 form태그에 onSubmit prop을 추가해야한다. 폼에 버튼이 있을 때 버튼을 클릭하면 디폴트로 submit 이벤트가 발생한다. (type="button" 인 경우 제외)
submit 이벤트를  onSubmit prop에서 catch할 수 있고 onSubmit prop을 이용해서 로직을 실행할 수 있다.

50. function안에 function이 있는 것이 중첩함수!! 

51. event 객체에 preventDefault 메소드가 있어서 브라우저의 기본 동작을 차단하기 위해 호출할 수 있다. 브라우저의 기본 동작을 차단하지 않으면 페이지가 리로딩될것임
이 event 객체와 메소드는 바닐라 자바스크립트(순수한 js) 이다.

52. event.preventDefault(); 를 통하여 브라우저의 기본 동작을 차단한 후에 폼의 submit은 자바스크립트와 리액트로 제어할 수 있다.


53. 리액트의 ref
ref는 reference의 준말임
리액트는 DOM element로 reference를 설정할 수 있게 해준다. 이를 통해 DOM element에 직접적으로 액세스가 가능하다.
그런 reference나 connection을 설정하려면 userRef 훅을 리액트로부터 import해야한다.
import { useRef } from 'react';

userState와 마찬가지로 리액트가 제공하는 특수한 기능 중 하나임
우리의 함수인 컴포넌트 안에서 실행할 수 있다
컴포넌트 안에서 실행하면 ref 객체, 즉 참조 객체가 생성됨

54. ref prop은 key prop과 같은 특수한 prop이자 리액트의 내장 기능이며 모든 element를 지원하는 prop 이다.
const titleInputRef = useRef();

<input type="text" required id="title" ref={titleInputRef}/>

이런식으로 ref로 연결이 되면 이 ref 객체를 통해 input element로 접근이 가능해진다

55. useRef()로 생성된 모든 ref 객체는 current 속성을 갖고 있고 이에 연결된 값이 있다.
따라서 이 .current가 input element 객체 input element의 자바스크립트 표현식을 가진다.

모든 <input> element들은 value  속성을 가진다. --> 자바스크립트의 작동 방식.

<input> element를 대리하는 자바스크립트 객체도 value 속성을 갖고 있다.
그리고 <input>에 입력된 current.value가 들어있다
const enteredTitle = titleInputRef.current.value;
이런 식으로 사용자가 입력한 값을 가져올 수 있다

화면에 출력되는 것을 바꾸고 싶다면 
titleInputRef.current.value = 'blabla';
이런식으로 바꾸는게 아니라 state를 사용해야한다.

56. <input>의 값을 읽어오는 용도라면 ref는 매우 유용한 수단이다.
여기에서 사용자가 입력한 값을 읽어온다.

57. 하나의 컴포넌트에 하나 이상의 ref를 가질 수 있다.

58. 리액트 혹은 다른 단일 페이지 애플리케이션(SPA)은 대부분 백엔드 API가 필요하다.
백엔드 API를 통해 Request를 보내게 된다.
그러면 백엔드는 응답으로 HTML대신에 특정한 포맷의 데이터를 보내는데 주로 JSON 포맷이다.
즉, JSON 포맷으로 회신을 한다.
Request를 보낼 수 있는 몇몇 URL을 간단히 노출한다.
어떤 URL로 request를 보내느냐에 따라 다른 동작이 일어날 것임
이게 리액트를 통해 연결하는 일종의 백엔드이다.

리액트나 앵귤러로 된 프론트엔드 애플리케이션은 데이터베이스에 직접 접속해서는 안된다.
리액트로 데이터베이스에 직접 접속하지 않는 이유는 보안 때문이다.

리액트 애플리케이션에 작성하는 모든 코드는 페이지를 방문하는 이들에게 노출된다. 리액트에 작성하게 되면 데이터베이스 정보도 보이기 때문에 그런 위험 때문에 백엔드 API(request 보낼 백엔드 서버)를 사용하는 것이다.

그리고 이 request 보낼 백엔드 서버가 서버에 있는 데이터베이스에 접속해서 데이터베이스에 데이터를 저장하는 것임

--> 이 강의에서는 백엔드 API를 직접 만들지 않고 대신에 Firebase를 더미 백엔드로 사용할 것임

59. Firebase는 Google에서 제공하는 서비스로 여러 가지 다른 서비스들이 모여있는데 무료로 사용이 가능하다.
https://firebase.google.com/
이 안에 데이터베이스와 API도 있어서 request를 보낼 수 있고 이를 통해 데이터베이스에 데이터를 저장할 수 있다.

60. 구글 로그인을 하고 나면 여기에 프로젝트를 생성할 수 있다. 
--> 이 강의에서 이 백엔드, 이 서비스를 사용하는 이유는 request 보내는 방법을 보기 위해서임

여기서 빌드-Realtime Database (=실시간 데이터베이스)를 사용할 것임
페이지를 이동하면 Create Database 버튼을 클릭하고 지역은 디폴트로 놔두고 꼭 
테스트 모드에서 시작(=Start in Test Mode)을 선택해야함. 안그러면 request를 보낼 수 없다

그렇게 하면 데이터베이스가 생성되고 request를 보낼 수 있는 API도 함께 설정된다.
화면에 나오는 URL을 이용해 request를 보낼 수 있고 Firebase 서버 내부에서 request들이 Parse된다.(JSON.parse()같은 parse를 말하는 것 같음 아닐수도)

어떤 종류의 request를 보내느냐에 따라 그 request에 딸린 데이터가 추출되어 데이터베이스에 자동으로 저장된다.
얼핏 보면 데이터베이스로 직접 request를 보내는 것처럼 보이지만 사실은 Firebase API로 대신 request를 보내고 이를 통해 내부적으로 데이터베이스에 저장되는 것임
즉, 이 URL이 폼을 제출할 때 request를 보낼 주소이다.
어디서 request를 보낼지는 우리가 결정하면 된다.

61. prop을 이용해서 포워딩할 수 있다

 

62. HTTP request를 보내려면 fetch 함수를 사용한다. 자바스크립트에서 기본적으로 제공하는 함수로 리액트와는 관련이 없다.
표준 자바스크립트 함수로 최신 브라우저에서 지원된다.
그리고 이 함수를 통해 HTTP request를 보낼 수 있다. 
Axios와 같은 서드파티 패키지를 사용할 수도 있다.

fetch함수에는 인자가 필요한데 첫 번째 인자는 문자열이다. request 보낼 URL을 써준다.
이 URL은 Firebase 실시간 데이터베이서 콘솔에서 가져올 수 있다.

그런데 Firebase 실시간 데이터베이스 콘솔에서 URL을 변경할 수도 있다. 도메인 뒤에 세그먼트를 추가할 수 있다. 그러면 그 세그먼트는 하위 폴더로 인식이 된다. 
도메인이 데이터베이스라면, 세그먼트는 테이블이 되는 것임.


63. Firebase API의 한 가지 특이한 점은 끝에 '.json'을 붙여야 한다는 점이다.
리액트의 특성은 아니고 Firebase의 요구 사항이다.

64. Firebase API에 데이터를 저장한다는 신호를 보내려면 반드시 POST request를 보내야 한다.
fetch는 디폴트로 GET request를 보낸다

어떤 API로 작업하느냐에 따라 다르긴 하지만 대부분의 API는 데이터를 저장할 때 POST request를 필요로 한다.

GET request대신에 POST를 보내려면 fetch함수에 두 번째 인자를 추가해야한다.
두 번째 인자는 객체로서 fetch 함수 호출과 HTTP request를 설정할 수 있게 한다. 

 

65. 이제 POST request를 보낼 떄 이 request에 실제로 저장할 데이터를 추가해야 한다. 
이를 위해 body를 이용한다. fetch의 두 번째 인자인 이 configuration 객체에 body를 쓴다. 이제 body에 데이터를 넣어야 하는데 이때 데이터는 주로 JSON 포맷이다. JSON 포맷은 가장 일반적인 포맷이며 HTTP request로 데이터를 전송할 때 가장 널리 사용되는 포맷이기도 한다.
자바스크립트에서 쉽게 JSON을 생성할 수 있다.
내장된 JSON 객체를 사용하고 stringify 메소드를 호출해서 생성할 수 있다. JSON.stringify(변수명)
stringify는 디폴트 자바스크립트 객체나 raise나 value를 일반적으로 전달할 수 있고, 이들은 JSON으로 변환된다. 
인자(=매개변수)로 받아온 값을 stringify 메소드에 다시 인자로 넘기는 것이다.

마지막으로 중요한 점은 fetch()에 원한다면 headers를 추가할 수도 있다. 
예를 들어, 'Content-Type'을 추가하고 'application/json'으로 지정하면 이 추가적인 메타데이터가 request에 추가되면 이 request가 json 데이터를 전달한다는 것을 명시할 수 있다. 
어떤 API는 이런 식의 header를 요구하기도 한다.
*메타데이터는 데이터에 대한 데이터이다. 어떤 목적을 가지고 만들어진 데이터, 다른 데이터를 설명해 주는 데이터이다.

fetch 로는 더 많은 것을 할 수 있다. 이 request가 성공인지 에러인지를 감지할 수도 있다.


fetch(
            'https://react-getting-started-11983-default-rtdb.firebaseio.com/meetups.json',
            {
                method: 'POST',
                body: JSON.stringify(meetupData),
                headers: {
                    'Content-Type': 'application/json'
                }
            }
        );
이런식으로 쓰면 POST request로 데이터를 Firebase로 전송하는 것이다. 


66. 여기서는 페이지에서 버튼을 눌렀을 때 POST request로 데이터를 Firebase로 전송하게끔 해서 데이터를 입력하고 버튼을 누르면 Firebase 에 빌드-Realtime Database에 meetups이라는 노드가 생성된 것을 볼 수 있고 펼쳐보면 입력하여 버튼을 누름으로써 제출한 데이터가 잘 들어온 것을 확인할 수 있다. Firebase 서버에 데이터가 잘 저장이 됩니다.


67. 어떤 작업을 완료하면 HTTP Request가 전송되도록 하는 것은 리액트 라우터를 이용하면 간단하게 구현할 수 있다. react-router-dom 에서 useHistory라는 훅을 임포트해오면 된다.
리액트에는 내장된 훅이 몇가지 있는데 다른 서드파티 패키지에도 여러가지 훅이 내장되어 있다. 여러분만의 새로운 훅을 만들 수도 있다.

68. NewMeetupPage라는 함수 내부에 컴포넌트 함수로 useHistory()를 호출하면 history라는 객체를 반환한다. 이 history 객체는 브라우저의 검색 기록을 조작하는데 필요한 특정 메소드를 보여주는 단순한 기능을 한다. 이렇게 다른 메뉴로 이동하는 메소드 같은 걸 보여준다. 
데이터가 제출되고 나면 다른 메뉴로 이동하는 기능을 구현하려면 이 문단이 실행되고 나면 Fetch로 가져오는 값을 Promise 객체로 반환하는 기능을 사용해야한다. 그러니까 'then' 구문을 사용해야한다. then 구문 안에서 history를 실행하고 여기에 다른 메뉴로 이동하는 메소드를 붙여야한다.
 push()메소드는 페이지 더미에 새로운 페이지를 삽입합니다. 
이렇게 하면 뒤로가기 버튼을 눌러 이전 페이지로 돌아갈 수 있습니다. 하지만 이미 페이지 양식이 제출된 후라 이전 페이지로 돌아가는게 아무런 의미가 없습니다.
그럴땐 replace() 메소드를 사용하면 됩니다.
replace() 메소드도 다른 메뉴로 이동하는 메소드지만 이전 페이지로 돌아가는 "뒤로가기" 버튼을 비활성화합니다. 
대신 여기에 시작 페이지로 되돌아갈 수 있도록 '/'을 입력해줍니다. 
).then(() => {
      history.replace('/');
\});

그러면 이렇게 replace 메소드에 지정한 대로 'to' 프롬프트로 연결됩니다. --> 여기서는 MainNavigation.js에서 Link에 to가 / 인것으로 연결됨.
이제 이 프롬프트를 도착점으로 지정합니다. 이 링크를 History에 지정한 replace() 메소드와 push() 메소드의 목적지로 설정하고 목적지의 경로도 이렇게 메소드의 인수로 지정하였습니다.

이 then 구문 대신에 async와 await 구문을 사용해도 된다.
이제 이 request가 제대로 작동하면 다른 메뉴로 이동할 수 있다. 
API의 백엔드에서 데이터를 로드하려면 HTTP Request를 데이터를 저장할때처럼 다시 전송해야 한다.
그래서 allmeetups.js 파일의 AllMeetupsPage()라는 컴포넌트에 요청을 전송해야한다.
그런데 문제는 과연 언제 전송해야 하느냐 라는 것이다. 정답은 이 컴포넌트가 렌더링될때마다 보내야한다. 해당 페이지를 열때마다 보내야한다는 말입니다. 그렇다면 우리가 가장 먼저 해야 하는 작업은 JSX 코드를 반환하기 전에 이 컴포넌트 함수에다 데이터를 직접 fetch 하는 것이다.
데이터를 저장할 때 사용한 URL 을 그대로 사용하면 된다. 같은 API와 노드에서 데이터를 fetch 하는것이기 떄문에 같은 URL 을 사용할 수 있다. fetch 에 두 번쨰 인자는 작성하지 않아도 된다. 기본값으로 두면 "get" 요청이 전송되는데 바로 이것이 우리에게 필요한(=데이터를 얻기 위해서 요청을 보내는 것 의) 요청이기 때문이다.
따라서 이렇게 fetch를 호출하면 데이터를 취득할 수 있다. 데이터를 보내기 위해 사용했던 "POST" 요청과는 달리 이번에는 데이터를 가져오는 작업이므로 요청에 대한 응답을 받는 것도 중요하다. "fetch"는 프로미스를 반환하기 때문에  then 메소드를 사용하면 된다. 그런 다음 이 response를 처리하는 방식으로 변경할 수 있다.
).then(response => {}); 이렇게 then에 익명함수를 전달하고 여기에서 response 의 객체를 자동으로 인자로 얻을 수 있다. 이것이 fetch 함수의 작동 방식이기 때문이다. 어느 한 시점에 실제 응답이 이뤄지도록 하는 프로미스를 반환하는 것이다. 
이제 우리는 이 응답에 포함된 내용 즉, 데이터를 읽어야한다. 그러려면 json 메소드를 사용하면 된다. 이 메소드는 이 response 객체 내부에 포함된 메소드인데 이 메소드를 이용하면 json 방식에서 일반 자바스크립트 객체로 데이터를 자동으로 변환할 수 있다. json 도 프로미스를 반환한다. 그래서 이 프로미스가 해결될 때까지 기다려야한다. 
return response.json()을 입력하고 then 블록을 하나 추가하면 실제 데이터를 얻을 수 있다.
이 두번쨰 then 블록에서 데이터를 얻는 작업을 할것입니다. 여기에 오류 핸들링 기능을 추가할 수도 있다.
지금은 데이터를 얻고 데이터를 사용하는 것에 집중하고자 한다.
 ).then(response => {
    return response.json();
  }).then(data => {
    
  });

 


69. 이 응답 데이터에서 meetups의 배열을 추출하고 이것들을 MeetupList 에 있는 Meetups prop에 값으로 전달해야한다. 그런데 문제가 하나 있다.
fetch가 프로미스를 반환하기 때문에 지금은 이 프로미스 체인 내부에 있는 것이다.
그런데 자바스크립트는 프로미스가 완료될 떄까지 기다리지 않는다. "async"와 "await"을 사용하여 완료될 때까지 기다리게 하면 되지 않느냐고 하겠지만 그 생각은 좋은 방법이 아니다. 그렇게 하면 전체 컴포넌트 함수가 프로미스를 반환하게 된다. async의 기능이 그런 것이기 때문이다. 그러면 이 함수가 프로미스를 반환하는 함수로 변환되어 버린다. 그렇게 되면 이것은 더이상 유효한 리액트 컴포넌트가 아닌 것이다. 리액트 컴포넌트 함수들은 반드시 동기식이어야 하며 프로미스를 반환하는 게 아니라 JSX를 바로 반환해야하기 떄문이다. 그렇기 때문에 async와 await은 여기에 사용할 수 없다. 응답을 받을때까지 값을 반환하는 것을 연기할 수 없다는 것이다. 
로딩 스피너와 같은 임시 JSX 코드를 반환하는 것이 지금 하려고 하는 것이다.
그런 다음 응답을 받으면 반환된 JSX 코드를 업데이트 하는 것이다. 이럴때는 이전에도 배웠던 state를 사용하면 된다. state를 사용하여 특정 조건이 바뀔 떄 화면에 표시되는 것들을 바꾸게 할 수 있다. 그전에는 사용자가 버튼을 클릭할 때 오버레이가 표시되게 되어 있었다. 이제는 응답 데이터를 확보하면 state 를 바꿀 것이다. 그러기 위해서 이 AllMeetups.js 파일에서 리액트의 useState 훅을 임포트할 것이다.

70. 그런 다음 useState를 사용해 상태들을 설정합니다.
예를 들어, 로딩 상태를 설정해보면 상수명을 isLoading으로 지정하고 setIsLoading 을 입력해 상태 업데이트 기능을 추가한다. 
배웠듯이 useState는 항상 두 개의 요소를 포함하는 배열을 반환하기 때문이다. 그 중 첫 번째 요소는 현재 상태를 나타낸다. 두 번째 요소는 상태를 업데이트하는 함수이다. 데이터를 취하면 isLoading을 다시 false로 설정한다. 더 이상 로드하지 않기 떄문이다. 하지만 초기 상태는 반드시 false가 아니라 true 여야한다. true여야 로딩 상태에서 시작하기 때문이다. 데이터를 얻으면 false 로 바꾸면 된다. 
그런 다음 if를 사용해 return 문이 작동하기 전에 컴포넌트 함수가 로딩 중인지 확인한다. 로딩중이라면 JSX 코드를 하나 더 사용해서 반환한다. 
예를 들어 section을 만들어 Loading... 이라는 문구를 집어넣을 수 있다. 이 문구는 로딩하는 동안 이 컴포넌트 대신 실행하게 할 대체용 문구이다. 실제 MeetupList는 로딩이 완료되어 데이터를 얻고나서야 반환할 수 있습니다. 
지금은 로딩 상태만 관리하고 있고 가져온 실제 데이터로는 아무런 작업도 하지 않고 있다. 따라서 이 컴포넌트에 다른 state를 추가해야 할 것 같다. 지금까지 하지 않았던 작업이긴 하지만 하나의 컴포넌트에 여러 개의 state를 지정할 수도 있고 제한이 없다. 

처음에는 비어 있는 상태의 배열을 지정해 두고 API의 백엔드에서 데이터를 가져오고 나면 이 비어 있는 배열을 다시 정의할 수도 있다. 그럼 이제 이 배열 loadedMeetups와 setLoadedMeetups 라고 정의할것이다. 그런다음 fetch().then().then() 안에 setLoadedMeetups를 data로 설정한다.
setLoadedMeetups(data);

이게 마지막 단계는 아니지만 현재로서 이렇게만 설정해두도록 한다.. 

71. 이제 더미 데이터 대신 loadedMeetups를 아래의 meetups prop에 전달한다.
이제 더미 데이터를 없애도 된다는 뜻이다 :) 

상태 업데이트 함수를 호출하면 리액트가 이 컴포넌트 함수를 재실행하고 재평가한 다음 업데이트된 JSX 코드를 반환한다. <- 이것이 바로 state의 개념이다. 
state를 사용하면 컴포넌트를 재평가할 수 있으며 상태가 변경될 때마다 화면에 다른 내용을 렌더링할 수 있는 것이다.

우리가 isLoading과 LoadedMeetups의 상태를 바꾸면 이 컴포넌트 함수가 다시 실행된다. fetch 함수가 다시 실행된다는 뜻이다. 그래서 다시 요청을 보내게 된다.
그러고 나면 또 상태를 업데이트 할 것이다. 그러면 컴포넌트 함수가 또 실행되고 fetch가 또 호출되는 과정이 반복된다. 이렇게 무한 루프가 생성되면 애플리케이션이 중단될 것이다.

API에 보내지는 요청이 스팸처럼 밀려들 것이다. 
이것을 막는 방법은 493강에서 다루고 있음..

493강부터 봐야함!! 







반응형
복사했습니다!