3. Current상수테 저장된 값을 결과를 표시할 요소의 값에 plus라면 +1 해서 넣고, minus라면 -1해서 넣는다.
1. Plus를 누르면 result값에 +1 한다. Minus를 눌렀다면 반대로 한다.
명령형 프로그래밍 방식에는 jQuery가 있고, 선언형 프로그래밍 방식에는 React.js가 있다.
🍅세 번째 이유 - Virtual DOM
DOM(document object model): 문서 객체 모델
HTML을 트리 형태로 변환시켜 놓은 객체
변환된 DOM은 이런 흐름에 따라 눈에 보이게 된다.
이런식으로 브라우저가 많은 일을 수행하기 때문에 노드의 수가 많아질수록, 변경이 잦아질수록 성능 저하로 이어질 수 있다.
Virtual DOM이란 실제 DOM에서 처리하는 방식이 아닌 Virtual DOM과 메모리에서 미리 처리하고 저장한 후, 실제 DOM과 동기화하는 프로그래밍 개념이다.
5번 업데이트 할 것을 한번만 업데이트 한다 정도의 개념.
Create React App
🍅프로젝트 생성
npx create-react-app [프로젝트 이름] 명령어를 이용해서 리액트 프로젝트를 생성한다.
프로젝트 구조는 다음과 같다.
🍅프로젝트 실행
pakage.json을 확인해보면 긴 문자열 명령을 별명을 붙여서 부를 수 있는 기능인 scripts가 있다.
npm start 명령어를 입력하면 프로젝트가 실행된다.
프로그램을 종료하고 싶다면 터미널에 ctrl(^)+c를 입력한다.
🍅src > App.js
//App.js
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
App.js의 초기 상태.
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h2>Hello World</h2>
</header>
</div>
);
}
export default App;
App.js의 header안의 내용을 지우고 h2태그를 추가했더니 화면이 다음과 같이 바꼈다.
개발자 도구로 확인본 결과는 다음과 같다.
결론적으로 App.js 파일에 있는 App()이라는 함수가 리턴하는 html들이 id가 root인 div 아래로 자식 요소로 들어갔다고 유추가 가능하다.
🍅src > index.js
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
index.js의 초기 상태이다.
확인해 볼 수 있는 것
root.render이라고 해서 root를 화면에 그리고 있다.
React.StricMode태그 안에 <App />이라고 되어 있는데 상단의 import로 App을 불러와서 사용하고 있는 것이다.
** 강의에선 React.render으로 되어있지만 강의 수강 기준 현재는 createRoot를 이용하는 방법을 사용한다.
즉 아까 봤던 App.js에 있던 App()함수가 return하는 값이 id가 root인 div아래로 들어가게 된다.
🍅public > index.html
<!-- index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
그렇다면 id가 root인 div는 어디에 위치할까?
바로 public안에 있는 index.html안에 있다.
💡정리:
리액트 앱이 실행이 되면 src밑에 index.js가 실행이 되면서 index.html안에 있는 id가 root인 div아래로 app.js 안에 있는 App()함수의 리턴값이 들어가게 되는 것.
📁node_modules
일부 모듈만 캡쳐.
src, public 외의 또 다른 패키지.
node.js 패키지의 구성 요소 중 하나로 외부 모듈을 저장하고 있는 폴더.
상당히 많은 모듈들을 가지고 있기 때문에 하나하나 설치할 수 없기에 create-react-app을 이용하여 한번에 설치를 했다.
사이즈가 상당히 크기 때문에 배포나 전송시 많은 시간이 소요될 수 있다.
node-modules는 삭제해도 문제가 없다. 왜냐하면 pakage.json에 필요한 모듈이 명시가 되어있으며, npm i (npm install)를 이용하여 설치할 수 있기 때문이다.
📁public
favicon.ico : 웹사이트의 아이콘
index.html : 위에서 확인
logo192.png, logo512.png, manifest.jspn : 해당 강의에서 사용하지 않을 파일.
robots.txt : 구글이나 네이버가 웹사이트를 수집할 때, 수집이 가능/불가능한 경로를 명시하는 파일.
📁src
App.css : 스타일파일
App.js : 위에서 확인
App.test.js : 테스트를 위한 파일. 해당 강의에선 사용x
index.css : 스타일 파일
index.js : 위에서 확인
logo.svg : 해당 강의에선 사용x
reportWebVitals.js : 해당 강의에선 사용x
setupTests.js : 해당 강의에선 사용x
🍅JSX
//App.js
import './App.css';
function App() {
let name = "cloudmato";
return (
<div className="App">
<header className="App-header">
<h2>Hello {name} World</h2>
</header>
</div>
);
}
export default App;
app.js파일로 돌아가서 App함수 밑에 지역변수인 name을 선언해주고 중괄호{ }를 사용해서 <h2>안에 넣어주면 화면에 반영이 된다.
JSX : javascript형식과 html형식을 합쳐서 사용할 수 있는 Javascript의 확장 문법
React는 이렇게 App()이라는 함수를 만들고 return으로 JSX 문법의 html을 리턴해주면서 컴포넌트를 만든다.
🍅 export default App;
common.js 모듈 시스템에서는 module.exports를 이용해서 모듈을 내보냈었다.
ES 모듈 시스템에서는 export default 를 이용해서 모듈을 내보내게 된다.
내보낸 모듈은 다른 파일에서 import [이름] from [경로] 이런 식으로 사용하게 된다.
Myfooter.js 파일을 생성한 후 export default를 이용해 내보내 주었다.
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import MyFooter from './MyFooter';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<MyFooter />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
index.js 에 <MyFooter />를 넣어주면 MyFooter가 렌더링 된다.
즉, index.js에서는 최상위 컴포넌트를 정의해줄 수 있다.
🍅JSX 문법 - 1. 닫힘규칙
예를 들어 <div></div> 등과 같이 닫힘 태그를 꼭 써주어야 한다.
<br>, <hr> 같은 경우 <br /><hr /> 등과 같이 self-closing해주어야 한다.
🍅JSX 문법 - 2. 최상위 태그 규칙
JSX로 컴포넌트를 만들어서 리턴하려면 반드시 하나의 최상위 태그로 다른 모든 태그들을 감싸주어야 한다.
최상위 태그: 가장 바깥에 있는 태그
//App.js
import './App.css';
import React from 'react';
import MyHeader from './MyHeader';
function App() {
let name = "cloudmato";
return (
<React.Fragment>
<MyHeader />
<header className="App-header">
<h2>Hello {name} World</h2>
</header>
</React.Fragment>
);
}
export default App;
만약 최상위 태그로 묶고 싶지 않다면 react.fragment라는 기능을 사용해주면 된다.
//App.js
import './App.css';
import MyHeader from './MyHeader';
function App() {
let name = "cloudmato";
return (
<>
<MyHeader />
<header className="App-header">
<h2>Hello {name} World</h2>
</header>
</>
);
}
export default App;
이런 식으로 빈 태그를 만들어 줘도 된다.
🍅JSX문법과 CSS 결합하기 - 1. css파일 이용하기
//App.js
import './App.css';
import MyHeader from './MyHeader';
function App() {
let name = "cloudmato";
return (
<div className="App">
<MyHeader />
<h2>Hello {name} World</h2>
<b id='boldText'>react.js</b>
</div>
);
}
export default App;
import를 이용하여 css파일을 불러와 사용할 수 있다.
jsx문법에서는 <div className="App">처럼 className 속성을 사용해주어야 한다.
let a = [];
const result = a ? true : false;
console.log(result); //true
🍅삼항 연산자의 중첩
//TODO : 학점 계산 프로그램
//90점 이상 A+
//50점 이상 B+
//둘 다 아니면 F
let score = 40;
score >= 90
? console.log("A+")
:score >= 50
?console.log("B+")
:console.log("F");
//F
let a = 10;
let b = 20;
let tmp = 0;
tmp = a;
a = b;
b = tmp;
console.log(a, b); //20,10
해당 코드를 줄여보자.
let a = 10;
let b = 20;
[a,b] = [b,a];
console.log(a, b); //20, 10
비구조화 할당을 이용하여 swap이 이루어졌다.
🍅객체의 비구조화 할당
let object = {one: "one", two: "two", three: "three"};
let one = object.one;
let two = object.two;
let three = object.three;
console.log(one, two, three); //one two three
해당 코드를 줄여보자.
let object = {one: "one", two: "two", three: "three", name: "cloudmato"};
let {name, one, two, three} = object
console.log(one, two, three, name); //one two three cloudmato
객체에서도 배열의 비구조화 할당을 사용할 수 있다.
이때 순서는 상관 없다. 왜냐하면 순서가 아닌 키값을 기준으로 할당이 이루어지기 때문.
키값을 이용하여 비구조화 할당을 해야한다는 변수 이름에 대한 한계가 있다. 하지만 극복할 수 있는 방법이 있다.
let object = {one: "one", two: "two", three: "three", name: "cloudmato"};
let {name: myName, one, two, three} = object
console.log(one, two, three, myName); //one two three cloudmato
다음과 같이 원래 키값: 내가 사용하고 싶은 변수이름 이런 식으로 명시해주면 다른 변수명으로 할당 가능하다.
let object = {one: "one", two: "two", three: "three", name: "cloudmato"};
let {name: myName, one, two, three, abc="four"} = object
console.log(one, two, three, myName, abc); //one two three cloudmato fore
배열의 내장함수인 concat을 사용할 수도 있지만 스프레드 함수를 사용하면 중간에 '함정쿠키'와 같이 유연하게 활용을 할 수 있다.
동기 & 비동기
:순서대로 실행하는 것과 그렇지 않은 것들
🍅동기 방식의 처리 - 블로킹 방식
자바스크립트는 싱글 스레드 언어이다.
자바 스크립트는 코드가 작성된 순서대로 작업을 처리한다.
이전 작업이 진행 중일 때는 다음 작업을 수행하지 않고 기다린다.
먼저 작성된 코드가 다 실행이 된 이후에 뒤에 작성된 코드를 실행한다.
Tread - taskA----- taskB--------------- tastC--|
🍅동기처리 방식의 문제점
하나의 작업이 너무 오래 걸리게 될 시,
-> 모든 작업이 오래 걸리는 하나의 작업이 종료되기 전까지 대기해야 한다.
-> 전반적인 흐름이 느려진다.
🍅멀티 쓰레드
코드를 실행하는 일꾼 Thread를 여러 개 사용하는 방식인 멀티 쓰레드 방식으로 작동시키면 분할 작업이 가능하다.
오래걸리는 일이 있어도 다른 Tread에게 지시하는 방식.
Tread taskA----|
TreadA taskB----------|
TreadC taskC--|
그러나 자바스크립트는 싱글쓰레드 사용.
🍅비동기 작업 - 논블로킹 방식
싱글 쓰레드 방식을 이용하면서, 동기적 작업의 단점 극복을 위해 여러 개의 작업 동시에 실행시킴
즉, 먼저 작성된 코드의 결과를 기다리지 않고 다음 코드를 바로 실행한다.
Tread taskA----|
taskB-------------|
taskC--|
비동기 처리를 할 때는 우리가 자바스크립트에서 함수를 호출할 때 콜백 함수를 붙여서 그 비동기 처리의 결과값이나 끝났는지의 여부를 확인하는 역할을 하게 된다.
🍅코드로 확인하기
function taskA() {
console.log("A작업 끝");
};
taskA();
console.log("코드 끝");
/*결과
A작업 끝
코드 끝
*/
우리가 알게 모르게 사용해왔던 동기적 방식.
taskA가 끝난 후에야 다음 코드가 실행된다.
function taskA() {
setTimeout(() => {
console.log("A TASK END");
}, 2000)
};
taskA();
console.log("코드 끝");
/* 결과
코드 끝
A TASK END
*/
먼저 지시된 작업을 끝나기까지 기다리지 않고 그냥 다음 작업업을 바로 실행하는 비동기 방식.
setTimeout함수는 두 개의 파라미터를 받는다. (콜백함수, 시간(ms단위))
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000)
};
taskA(3,4,(res)=>{
console.log("A TASK RESULT : ", res)});
console.log("코드 끝");
/**
* 코드 끝
* A TASK RESULT : 7
*/
🍅자바스크립트엔진의 비동기 처리
작성한 글 날라감 이슈로 그림만..
Promise - 콜백 지옥에서 탈출하기
자바스크립트의 비동기를 돕는 객체
비동기 처리의 결과값을 핸들링하는 코드를 비동기 함수로부터 분리할 수 있다.
🍅비동기 작업이 가질 수 있는 3가지 상태
🍅콜백 함수를 이용한 비동기 처리
//2초 후에 이 수가 양수인지, 음수인지 판단하는 함수.
function isPositive(number, resolve, reject) {
setTimeout(() => {
if(typeof number === "number") {
//성공 -> resolve
resolve(number >= 0? "양수":"음수")
} else {
//실패 -> reject
reject("주어진 값이 숫자형 값이 아닙니다")
}
},2000)
};
isPositive([], (res)=> {
console.log("성공적으로 수행됨 : ", res);
}, (err)=> {
console.log("실패 하였음 : ", err);
});
🍅Promise 사용
어떤 함수가 promise를 반환한다는 것은 이 함수는 비동기 작업을 하고 작업의 결과를 promise 객체로 반환 받아서 사용할 수 있는 함수라는 것이다.
function isPositiveP(number) {
const executor = (resolve, reject) => {
setTimeout(()=>{
if(typeof number === "number") {
//성공 -> resolve
console.log(number);
resolve(number >= 0? "양수":"음수")
} else {
//실패 -> reject
reject("주어진 값이 숫자형 값이 아닙니다")
}
},2000);
};
const asyncTask = new Promise(executor);
return asyncTask;
}
const res = isPositiveP([]);
res.then((res)=>{console.log("작업 성공 : ", res)}).catch((err)=>console.log("작업 실패 :", err));
🍅Promise로 콜백지옥 탈출하기
function taskA(a,b,cb) {
setTimeout(()=>{
const res = a+b;
cb(res);
}, 3000);
}
function taskB(a,cb) {
setTimeout(()=>{
const res = a * 2;
cb(res);
}, 1000);
}
function taskC(a,cb) {
setTimeout(()=>{
const res = a * -1;
cb(res);
}, 2000);
}
taskA(3,2,(a_res)=>{
console.log("taskA : ", a_res);
taskB(a_res,(b_res)=>{
console.log("taskB : ", b_res);
taskC(b_res,(c_res)=>{
console.log("taskC : ", c_res);
});
});
});
이전코드이다.
콜백이 계속 안으로 들어가며 콜백헬, 콜백 지옥이 생긴 모습이다.
Promise로 해결해보자.
function taskA(a,b) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a+b;
resolve(res);
}, 3000);
});
}
function taskB(a) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a * 2;
resolve(res);
}, 1000);
});
};
function taskC(a) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a * -1;
resolve(res);
}, 2000);
});
}
taskA(5,1).then((a_res)=>{
console.log("A RESULT : ", a_res);
taskB(a_res).then((b_res)=>{
console.log("B RESULT : ", b_res);
taskC(b_res).then((c_res)=>{
console.log("C RESULT : ", c_res);
});
});
});
기대한 것과는 다르다.
그 이유는 then을 콜백 함수를 쓰는 식으로 사용했기 때문.
바꿔서 다시 해보자.
function taskA(a,b) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a+b;
resolve(res);
}, 3000);
});
}
function taskB(a) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a * 2;
resolve(res);
}, 1000);
});
};
function taskC(a) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const res = a * -1;
resolve(res);
}, 2000);
});
}
taskA(5,1).then((a_res)=>{
console.log("A RESULT : ", a_res);
return taskB(a_res);
})
.then((b_res)=> {
console.log("B RESULT : ", b_res);
return taskC(b_res);
})
.then((c_res)=>{
console.log("C RESULT : ",c_res);
});
이렇게 then으로 계속해서 이어나가는 방식을 then chaining방식이라고 한다.
이런 방법이 가능한 이유는 return으로 promise를 반환해준 것이나 마찬가지기 때문에 then을 사용할 수 있는 것이다.
콜백을 계속 이용했다면 > 이런 모양으로 들어가게 되는데 promise를 사용함으로써 해결이 되었다.
const person = {
name: "cloudmato", //member
age: 24, //member
say: function () {
console.log("hello");
} //method -> 방법
};
person.say();
person["say"]();
객체의 프로퍼티 중 함수를 메서드, 함수가 아닌 것을 멤버라고 한다.
const person = {
name: "cloudmato",
age: 24,
say: function () {
console.log(`안녕 나는 ${this.name}`);
console.log(`안녕 내 나이는 ${this["age"]}`);
}
};
person.say();
// 안녕 나는 cloudmato
// 안녕 내 나이는 24
this를 사용하여 객체의 메서드에서 멤버를 접근할 수 있다.
🍅 존재하지 않는 프로퍼티에 접근한 경우
const person = {
name: "cloudmato",
age: 24,
say: function () {
console.log(`안녕 나는 ${this.name}`);
console.log(`안녕 내 나이는 ${this["age"]}`);
}
};
console.log(person.gender); //undefined
에러를 일으키지 않고 undefined를 띄운다.
이러한 방식은 유연한 프로그래밍을 가능하게 하지만 잘못된 연산을 하게 할 수도 있다.
const person = {
name: "cloudmato",
age: 24,
say: function () {
console.log(`안녕 나는 ${this.name}`);
console.log(`안녕 내 나이는 ${this["age"]}`);
}
};
console.log(`name : ${"name" in person}`); //true
console.log(`name : ${"hobby" in person}`); //false
in 연산자를 객체와 함께 활용하면 프로퍼티의 존재 여부를 boolean형태로 알 수 있다.
배열
🍅배열
배열은 비원지 자료형에 해당하며, 순서 있는 요소들의 집합, 즉 여러개의 항목이 들어있는 리스트이다.
여러가지 자료형이 들어갈 수 있는 특징이 있다.
let arr = new Array(); //생성자를 이용한 생성
let arr1 = []; //배열 리터럴
console.log(arr); //[]