React를 사용하기 위해서
react, react-dom 라이브러리를 import 해주어야 함.
react: 엔진과 비슷함. interactive한 UI를 만들 수 있게 해줌. element 생성, eventListener 추가.
react-dom: 모든 React element들을 HTML <body></body> 태그 부분에 둘 수 있도록 해줌. (JavaScript를 HTML element로 바꾸는 과정)
=> render() : React element들을 HTML로 만들어 배치한다. 사용자에게 보여준다.
React 규칙
1. HTML 코드를 직접 작성하지 않는다.
JavaScript로부터 시작해서 HTML이 생성됨. => JavaScript를 활용하여 element를 업데이트 할 수 있다.
유저에게 보여질 내용을 컨트롤 할 수 있다.
2. useState의 state를 변경하는 함수를 통해 state가 변경되면 모든 컴포넌트가 다시 렌더링되며, 필요한 부분들만 React가 리렌더링한다.
=> props가 변경되지 않으면 Memo를 활용하여, state가 변경될 때 모두 렌더링 되지 않고 필요하지 않은 부분은 리렌더링하지 않도록 막을 수 있다.
✅ React 코드
<script>
const root = document.getElementById("root");
const h3 = React.createElement(
"h3",
{
id: "title",
onMouseEnter: () => {
console.log("mouse enter");
},
},
"Hello, I'm a span"
);
const btn = React.createElement(
"button",
{
style: { backgroundColor: "tomato" },
onClick: () => {
console.log("im clicked");
},
},
"Click me"
);
const container = React.createElement("div", null, [h3, btn]);
ReactDOM.render(container, root);
</script>
✅ JSX 코드
<script type="text/babel">
const root = document.getElementById("root");
const Title = () => (
<h3
id="title"
onMouseEnter={() => {
console.log("mouse enter");
}}
>
Hello, I'm a span
</h3>
);
const Button = () => (
<button
style={{ backgroundColor: "tomato" }}
onClick={() => {
console.log("im clicked");
}}
>
Click me
</button>
);
const Container = () => (
<div>
<Title />
<Button />
</div>
);
ReactDOM.render(<Container />, root);
</script>
※ JSX 컴포넌트 구현
function Title() {
return (
<h3
id="title"
onMouseEnter={() => {
console.log("mouse enter");
}}
>
Hello, I'm a span
</h3>
);
}
// 두 함수는 동일함
// 함수로 변환하면서 return() 안에 태그를 포함시킨 것임.
const Title = () => (
<h3
id="title"
onMouseEnter={() => {
console.log("mouse enter");
}}
>
Hello, I'm a span
</h3>
);
✅ React의 useState()를 사용하면 컴포넌트가 재생성된다. 그리고 바뀐 데이터만 리렌더링된다.
<script type="text/babel">
const root = document.getElementById("root");
const App = () => {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter + 1);
};
console.log("rendered");
console.log(counter);
return (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={onClick}>Click me</button>
</div>
);
};
ReactDOM.render(<App />, root);
</script>
=> 버튼이 클릭될 때마다 rendered\n{counter}가 console에 보여진다. 리렌더링되는 변경된 데이터: counter, 컴포넌트가 재생성되며 실행되는 rendered.
✅ useState의 set함수
// 정상
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter + 1);
};
// 오류 발생. const는 재할당이 불가능하다. counter++과 동일함.
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter = counter + 1);
};
// counter의 복사가 여러번 발생하면서 반응속도가 느림.
let [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter++);
};
// 정상
let [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(++counter);
};
✅ useState의 올바른 set함수 사용 방법. 현재 state로 새로운 state를 계산하는 법.
// 가능은 하지만 추천하지 않음.
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(5);
};
// 올바른 방법. 현재 state를 매개변수로 받아 바뀐 상태를 반환한다.
// ※ return 키워드가 생략된 모습 ※
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter((current) => current + 1);
};
// 오류 발생. 단순히 `current + 1;`이라는 코드만 존재하고,
// ※ 값을 반환하지 않고 있다. ※
// setCounter 내부에서는 상태를 반환해야 한다!!!
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter((current) => {
current + 1;
});
};
=> setCounter()는 2가지 방법으로 사용할 수 있다.
1. 값을 직접 보내는 것. setCounter("");
2. 함수를 보내는 것. 이 때, 함수의 첫 번째 매개변수에는 기존의 state값이 들어있다.
💯💯💯 화살표 함수 return 키워드 생략
const add = (a, b) => {
return a + b;
};
// 동일한 함수임.
const add = (a, b) => a + b;
💯💯💯
const [flipped, setFlipped] = React.useState(false);
const onFlip = () => setFlipped((current) => !current);
// 동일한 함수임.
const [flipped, setFlipped] = React.useState(false);
const onFlip = () => {
setFlipped((current) => {
return !current;
});
};
✅ HTML <label/> 태그, <input/> 태그
<label/> 태그를 클릭하면 <input/>이 선택됨.(label htmlFor="", input id="")
=> JSX에서의 property와 HTML에서의 property는 다르다.(JSX: htmlFor, className / HTML: for, class)
✅ 인라인 형태
// 1. 인라인 함수
const add = (a, b) => a + b;
// 2. 인라인 스타일(React)
const style = { color: 'blue', fontSize: '16px' };
// 3. 인라인 조건문
const message = isLogged ? 'Welcome back!' : 'Please log in.';
// 4. 인라인 JSX(React), 익명 함수 사용.
<button onClick={() => console.log('Button clicked!')}>Click me</button>
=> 간결하고 직관적임. 가독성 향상. 익명 함수
✅ 익명 함수.
function(){
}
() => {
}
const [toDo, setToDo] = useState("");
const [toDos, setToDos] = useState([]);
const onSubmit = (event) => {
event.preventDefault();
setToDos((currentArray) => [toDo, ...currentArray]);
};
// 동일한 표현임.
const onSubmit = (event) => {
event.preventDefault();
setToDos(function (currentArray) {
return [toDo, ...currentArray];
});
};
✅ props
// <Btn /> 컴포넌트에 대한 모든 정보를 매개변수로 받음.
const Btn = (props) => {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: props.big ? 18 : 16,
}}
>
{props.text}
</button>
);
};
const App = () => {
return (
<div>
<Btn text="Save Changes" big={true} />
<Btn text="Continue" big={false} />
</div>
);
};
// props shortcut. Btn 컴포넌트의 매개변수로 Object를 받음.
const Btn = ({ text, big }) => {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: big ? 18 : 16,
}}
>
{text}
</button>
);
};
const App = () => {
return (
<div>
<Btn text="Save Changes" big={true} />
<Btn text="Continue" big={false} />
</div>
);
};
=> const Btn = ({text, big = false}) 과 같이 props의 기본값을 지정할 수 있다. 자바스크립트 문법.
✅ props로 넘기는 함수(onClick)
const Btn = ({ text, onClick }) => {
return (
<button
onClick={onClick}
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
}}
>
{text}
</button>
);
};
const App = () => {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => {
setValue("Revert Changes");
};
return (
<div>
<Btn text={value} onClick={changeValue} />
<Btn text="Continue" />
</div>
);
};
=> onClick은 사용자가 정의내린 이름이다. 이벤트 리스너가 아님. 직접 props를 HTML 태그에 넣어줘야 한다.
✅ 부모 컴포넌트의 state가 바뀌면 모든 자식 컴포넌트가 리렌더링된다. 하지만, Memo를 사용하면 props가 변경되지 않은 컴포넌트는 리렌더링하지 않는다. 최적화!!!
const Btn = ({ text, changeValue }) => {
console.log(text, "was rendered");
return (
<button
onClick={changeValue}
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
}}
>
{text}
</button>
);
};
const MemorizedBtn = React.memo(Btn);
const App = () => {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => {
setValue("Revert Changes");
};
return (
<div>
<MemorizedBtn text={value} changeValue={changeValue} />
<MemorizedBtn text="Continue" />
</div>
);
};
✅ props로 숫자 넘길 때 중괄호 사용. 자바스크립트 문법.
const Btn = ({ text, fontSize }) => {
console.log(text, "was rendered");
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize, // fontSize: fontSize 동일함. shortcut.
}}
>
{text}
</button>
);
};
const App = () => {
return (
<div>
<Btn text="Save Changes" fontSize={18} />
</div>
);
};
=> HTML 태그의 속성값과 props가 동일할 때, shortcut 사용 가능.
✅ PropTypes. TypeScript 느낌? props의 속성을 지정해준다.
Btn.propTypes = {
text: PropTypes.string.isRequired,
fontSize: PropTypes.number,
};
const App = () => {
return (
<div>
<Btn text="Save Changes" fontSize={18} />
<Btn text={"Continue"} />
</div>
);
};
=> 여러 props를 전달받을 때, 한눈에 어떤 props를 가지고 있는지 확인할 수 있다.
✅ create-react-app
React 애플리케이션을 만들기 쉬워진다.
=> 많은 <script></script>들과 사전 설정을 준비해준다. 이를 통해,
1. 개발 서버에 접근하고
2. 자동으로 새로고침을 시켜주고
3. 즉각적으로 애플리케이션에 CSS를 적용시킨다.
package.json에서 <script></script>를 확인할 수 있다.
하나의 컴포넌트로 하나의 파일이 구성된다.
css를 module.css로 분리하여 js파일에 import 시킨다. 이때, css가 javascript 오브젝트로 변환된다. className={임포트명.btn}. HTML에서는 무작위 class 이름을 가진다.
🤔🤔🤔 css의 오브젝트 형태란?
JSX의 style={}에 오브젝트 형태로 css를 직접 입력하는 방식. 매우 힘듦.
하지만, module.css를 사용하면 오브젝트 형태가 아닌 css 문법을 사용하여 편하다.
🤔🤔🤔 HTML에서 무작위의 class 이름을 가지는 장점?
일일이 class명을 기억하지 않아도 된다. 랜덤으로 각각 다르게 작성해주기 때문이다.
module.css에서 css 문법을 사용해 작성하고, 필요한 스타일만 className={임포트명.btn} 형태로 사용할 수 있다.
💯 💯 💯
module.css 간에는 서로 독립적이므로 동일한 class명을 사용해도 된다. 다른 module.css라면 HTML에서 다르게 class명이 설정된다.
Ex) App.module.css에서의 .title과 Button.module.css에서의 .title은 서로 다른 class로 HTML에 나타난다.
✅ useEffect. Ex) 가장 처음 API를 불러온 후, state가 변경되더라도 또 다시 여러번 API를 호출할 필요 없음.
1. useEffect(함수, 빈 배열), dependencies가 빈 배열이기 때문에 React가 아무것도 지켜보지 않는다.
맨 처음 컴포넌트가 렌더링되는 순간 딱 한 번 코드를 실행하고, 다른 state변화에는 실행하지 않는다.
2. useEffect(함수, 코드를 실행하기 위해 변경되는 부분)
특정한 부분이 변경되었을 때만(update되는 경우), 코드를 실행시키고 싶은 경우. React가 dependencies를 살펴보다가, 변경되면 코드를 실행시킨다.
3. clean-up 함수. 컴포넌트가 destroy될 때, 코드를 실행시키고 싶은 경우.
🤔🤔🤔 가장 처음 컴포넌트가 렌더링될 때 2번씩 호출되는 이유?? 이 컴포넌트로 감싸지면서??? 추가 학습 필요
const [counter, setValue] = useState(0);
const [keyword, setKeyword] = useState("");
const onClick = () => setValue((prev) => prev + 1);
const onChange = (event) => setKeyword(event.target.value);
console.log("i run all the time");
useEffect(() => {
console.log("I run only once.");
}, []);
useEffect(() => {
console.log("I run when 'keyword' changes.");
}, [keyword]);
useEffect(() => {
console.log("I run when 'counter' changes.");
}, [counter]);
useEffect(() => {
console.log("I run when 'keyword || counter' changes.");
}, [keyword, counter]);
// 콘솔 출력 결과
i run all the time
i run all the time
I run only once.
I run when 'keyword' changes.
I run when 'counter' changes.
I run when 'keyword || counter' changes.
I run only once.
I run when 'keyword' changes.
I run when 'counter' changes.
I run when 'keyword || counter' changes.
🤔🤔🤔 useEffect는 화면이 다 그려지고 난 뒤에 실행된다!!!
=> return문이 다 실행될 때까지 기다린다. 콜백함수, 비동기 함수. 추가 학습 필요
🤔🤔🤔 clean-up 함수. 컴포넌트가 destroy 되는 때, 실행되는 함수. 특별한 경우에만 사용됨.
=> 컴포넌트가 종료될 때, 원하는 코드를 실행할 수 있다. 함수를 리턴한다.
function Hello() {
function byFn() {
console.log("destroyed");
}
function hiFn() {
console.log("created");
return byFn;
}
useEffect(hiFn, []);
return <h1>Hello</h1>;
}
// 두 형태 다 가능함.
// 더 자주 쓰임. () => {}, 실행을 기다리는 중.
// foo();에서 ()가 빠진 형태 foo;
function Hello() {
useEffect(() => {
console.log("created");
return () => console.log("destroyed");
}, []);
return <h1>Hello</h1>;
}
✅ map()란? 매개변수로 입력받은 함수를, 배열의 각 item에 대해서 적용시켜 새로운 배열을 return한다.
⁕ 매개변수로 입력받은 함수의, 첫번째 매개변수는 배열의 각 item을 나타낸다. 두번째 매개변수는 각 index를 나타낸다.
<ul>
{toDos.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
// 두 개는 동일한 표현이다. return문을 생략하느냐, 않느냐의 차이
// ※ map()함수는 배열을 return 한다. ※
// ※ 중괄호를 사용해 element를 나타낼 때는 return 키워드를 사용해야 한다. ※
<ul>
{toDos.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
💯💯💯 배열의 속성이 모든 item에 존재하지 않는 경우를 대비하자! 3가지 방법 모두 가능함.
{movies.map((movie) =>
movie.hasOwnProperty("genres") ? (
<ul>
{movie.genres.map((g) => (
<li key={g}>{g}</li>
))}
</ul>
) : null
)}
{movies.map((movie) => {
movie.genres && movie.genres.map((g) => g);
})}
// Optional chaining
{movies.map((movie) => (
<ul>
{movie.genres?.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
))}
✅ async-await 문법
// 세 함수 동일한 기능임.
const getMovies = async () => {
const json = await (
await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year"
)
).json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovies();
}, []);
const getMovies = async () => {
const response = await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year"
);
const json = await response.json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovies();
}, []);
useEffect(() => {
fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year"
)
.then((response) => response.json())
.then((json) => {
setMovies(json.data.movies);
setLoading(false);
});
}, []);
✅ React Router Dom
: App.js 는 URL을 바라보고 그에 따른 component를 보여준다.
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
return (
<>
<Router>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/movie" element={<Detail />}></Route>
</Routes>
</Router>
</>
);
}
=> Router의 종류에는 BrowserRouter, HashRouter가 존재하지만 주로 BrowserRouter 사용함. (일반적인 URL)
Routes는 한 번에 하나의 component만 렌더링 하도록 조절한다.
Link는 a태그와 달리 새로고침 없이 다른 페이지로 이동할 수 있는 컴포넌트이다.
✅ react-router 동적 URL
useParams: url의 변수값을 반환하는 함수(/movie/:id)
import { useParams } from "react-router-dom";
const Detail = () => {
const x = useParams();
console.log(x); // {id: '58831'}
const { id } = useParams();
console.log(id); // 58831
return <h1>Detail</h1>;
};
function App() {
return (
<>
<Router>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/movie/:id" element={<Detail />}></Route>
</Routes>
</Router>
</>
);
}
=> url에 선언한 변수명대로 값을 반환함.
✅ React Published
: 만든 웹사이트의 production ready(코드 압축 및 최적화) code를 생성함.
{
"name": "movie-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"gh-pages": "^6.1.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "gh-pages -d build", //추가
"predeploy": "npm run build" //추가
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"homepage": "https://dekoms.github.io/movie-app" // 추가
}
1. npm run deploy를 입력하면 gh-pages -d build를 실행하며 배포 시작.
2. 배포 전에 predeploy 확인하여 자동으로 npm run build 실행하여 build 폴더 생성
3. homepage에 build 폴더 업로드.
✅ Breaking Change
: 툴 버전 업그레이드로 인한 코드 수정
=> React.js는 Breaking Change가 발생하지 않는다. 이전 기능에서 새로운 기능을 업데이트하고 추가만 했기 때문.
'인강 > 노마드코더' 카테고리의 다른 글
[노마드코더] React Hooks (0) | 2024.02.15 |
---|---|
[노마드코더] React Custom Hooks (0) | 2024.02.06 |
[노마드코더] HTML, CSS, JavaScript 클론코딩 chrome-app (0) | 2023.12.19 |