1.nodejs
npx create-react-app projectname
You are running `create-react-app` 4.0.3, which is behind the latest release (5.0.1).
We no longer support global installation of Create React App.
Please remove any global installs with one of the following commands:
- npm uninstall -g create-react-app
- yarn global remove create-react-app
The latest instructions for creating a new app can be found here:
https://create-react-app.dev/docs/getting-started/
이러한 경고가 나오는 경우.
검색해보니 5.0.0 버그때문에 4.0.3 을 사용하려 하는사람이 있는듯하나
딱히 방법이 없어보이므로 그냥 @latest
를 붙여서 레이터스트 버전으로 설치한다.
현시점 최신버전은 5.0.1 이다.
npx create-react-app@latest projectname
App.js 를 편집하면된다.
이안에 쓰여진 html은 JSX 라고 하는 html대용품
// <초기상태의 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;
html 과 다른점 1
class
는 className
이라 쓴다 ( class 는 문법상 사용하기 때문에 )
import './App.css';
위와 같이 css 파일을 적용해준다.
function App(){
let post = '강남 우동 맛집';
return (
<div>{ post }</div>
)
}
중괄호 { } 를 이용해 html 에 꽂아 넣을수 있다 ( 데이터 바인딩 )
js 처럼 document.queryselector("h4").innerhtml
같은거 쓸필요없다.
모범답안
<div style={ {color : 'blue', fontSize : '30px'} }> 글씨 </div>
중괄호를 2중으로 사용 ( 오브젝트형 으로 기술)
html 에서 사용하던 font-size
같은경우 -
를 뺄셈으로 인식하므로
카멜 케이스를 사용하여 fontSize
와같이 기술해야한다
먼저 임포트해주고
import { useState } from 'react';
렛 으로 정의해준다 그리고 첫번째 어레이에 작명해주고
let [작명,b] = useState('남자 코트 추천');
작명한걸 써주면 끝
<div>{작명}</div>
useState 를 사용하면 html 이 재랜더링이 된다
변수를 만들어놓고 사용안하거나 하면 워닝 메세지가 뜬다
귀찮으면
/*eslint-disable*/
맨위에다가 이거 추가하면 됨
클릭시 동작은
함수를 먼저 만들고
function i (){ d = d + 1 }
그 함수를 적어줘야한다
<span onClick={ i }> 👍 </span>
아래와 같은 직접 코딩은 안됨 🔴
<span onClick={ console.log(i) }> 👍 </span>
하지만 아래와 같이 function 을 직접 적어주는건 됨 🟢
<span onClick={ function i (){ d = d + 1 } }> 👍 </span>
<span onClick={ () => { d = d + 1 } }> 👍 </span>
그러나
위와 같이 d = d + 1 하면 실시간 적용이 안된다
그럼 어쩌라고?
useState 의 두번째 어레이를 사용하면됨
let [a, b] = useState(1);
↑ 두번째 어레이는 어따 쓰는지 궁금했는데 여기서 쓰임.
function i (){ b ( d + 1 ) }
이렇게 해주면 d+1 의 결과는 b 로 전달되고 a 로 다시 전달되는 효과를 가지며
html 의 정보가 갱신된다
array 의 동작방식 ( reference data type )
어레이는 이렇게 사용하지만
var arr = [1,2,3]
1,2,3 을 arr 에 담는게아니라
1,2,3 에 대한 링크를 arr 에 담는것. ( 레퍼런스 데이타 타입 )
실제로 1,2,3 은 램의 미지의 공간에 저장되어 있으며
바탕화면 바로가기 처럼 링크화되어 array 에 담겨진다고 한다
기존 스테이트와 신규 스테이트를 비교해서
바뀐게 없으면 동작하지않음 (절전모드)
그래서?
function App(){
let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] );
▲ 기존 state
return (
<button onClick={ ()=>{
let copy = 글제목;
copy[0] = '여자코트 추천';
▲ 신규 state
글제목변경(copy)
} }> 수정버튼 </button>
)
}
위를 보면 기존 state / 신규 state 가 있다.
어레이의 내용은 바꼇지만 링크는 "그대로" 이기때문에
"바뀐게 없으면 동작하지 않는" 사양이므로
위 의 경우, 버튼을 눌러도 글제목은 변경되지않는다.
방법은 있다
let copy = [...글제목];
[...] 을 사용해주면, "새로운" 어레이를 만들어 준다
더이상 "그대로" 가아니게되므로, 위 버튼은 동작하게 된다.
기존 스테이트를 변경하고싶으면, 복사본을 떠서 사용하라
function App(){
let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] );
▲ 기존 state
return (
<button onClick={ ()=>{
let copy = [...글제목]; ◀︎ 글제목의 복사본을 뜬다 (... 을 붙여야 한다)
copy[0] = '여자코트 추천'; ◀︎ 복사본을 바꿔준다
▲ 신규 state
글제목변경(copy) ◀︎ 바꿔준 복사본을 사용해준다
} }> 수정버튼 </button>
)
}
우선 Modal 이라는 이름의 펑션을 만들어주고
function Modal () {
return (
<div className="modal">
<h4>제목</h4>
<p>날짜</p>
<p>상세내용</p>
</div>
)
}
html 에다가
<Modal></Modal>
이렇게 꽂아 주면 된다.
arrow function 사용도 가능
// 에로우 펑션
let Modal = () => {
return ( <div></div>)
}
"ㄱ" 펑션에 있는 함수를 "ㄴ" 펑션에서 사용하려면
props 를 사용해서 전달해줘야한다
<>
</>
<>
<div className="modal">
<h4>제목</h4>
<p>날짜</p>
<p>상세내용</p>
</div>
</>
jsx 안에서 if 사용이 안되기 때문에
modal == true ? <Modal /> : null
이런걸 쓴다
모달이 츄루 라면 ? 모달창 보이기 : 아니라면 안보이기
그럼 버튼을 하나 만들어서
<button onClick={() => { setmodal(true) }}>open modal</button>
누르면 state 를 바꿔주게하고
스테이트도 만들어준다
let [modal, setmodal] = useState(false);
즉 버튼을 누르면 false 엿던 스테이트가 true 가 되고
삼항연산자에서 true 일때 모달창 보이라고 해놧으니
트리거가 되어 모달창이 보이겟지?
true 로 하라햇으니 false 로는 안해주니 꺼질일은없다.
토글 하고싶다?
setModal(!modal)
이렇게 해주면
let [modal, setmodal] = useState(false);
여기 있는 modal
이의 반대로 바꿔준다
글샘플 복붙 하려면 코드길어지고 귀찮다
var 어레이 = [2,3,4];
어레이.map(function(){
console.log(1)
});
요래하면 2,3,4
총 3 개 니까
콘솔 로그가 3번실행된다
<div>
{
[1,2,3].map(function(){
return ( <div>안녕</div> )
})
}
</div>
긍께 이래하면
3번 반복해줌
return
을 써줘야 한다.
props 는 자식한테만 정보를 줄수있다고 한다
근데 같은 계층에 있는 펑션인데 뭐가 자식이냐?
아래를 보자
function App() {
return (
<div className="App"></div>
<Modal/> ◀◀️◀◀️◀◀️◀◀️ 품고있음
);
}
function Modal(props) {
return (
<div>
</div>
)
}
<Modal/>
을 품고있으니 자식인거다...
내가 품고있는 애새퀴 한테 첩보를 보내고싶다 면?
function App() {
let [title, settitle] = useState([title]) <<<< 펑션 App 안에 속해있기때문에, 프롭스로 보내줘야함.
return (
<div className="App"></div>
<Modal a={title} b={settitle}/> <<<<<< 프롭스로 보낼것들을 써준다
);
}
function Modal(props) {
return (
<div>{props.a} <<<< 위에서 보낸 title 이 들어있다
{props.b} <<<< 위에서 보낸 settitle 이들어있다.
</div>
)
}
function Modal({a,b}) {
return (
<div>{a} <<<< 위에서 보낸 title 이 들어있다
{b} <<<< 위에서 보낸 settitle 이들어있다.
</div>
)
}
{} 안에 보낸 요소롤 직접 써주면 props.a 같이 props. 를 안붙여도 사용가능.
<input type="text"/>
<input type="range"/>
<input type="date"/>
<input type="number"/>
<textarea></textarea>
<select></select>
<input onChange={()=>{ 실행할코드 }}/>
<input onInput={()=>{ 실행할코드 }}/>
// 마우스 올리면
<input onMouseOver={()=>{ 실행할코드 }}/>
// 스크롤하면
<input onScroll={()=>{ 실행할코드 }}/>
<input onChange={(e)=>{ console.log(e.target.value) }}/>
e.target 현재 이벤트가 발생한 곳을 알려주고
e.preventDefault() 이벤트 기본 동작을 막아주고
e.stopPropagation() 이벤트 버블링 막아줌
function App (){
let [입력값, 입력값변경] = useState('');
return (
<input onChange={(e)=>{
입력값변경(e.target.value)
console.log(입력값)
}} />
)
}
<input onClick={(e)=>{ e.stopPropagation() }}/>
onKeyDown={e => e.key === "Enter" && settitle([...title,e.target.value], setgood([...good,0]), e.target.value="")}
이렇게 하면 아래쪽에 생성됨.
위쪽에 생성시키려면 아래 ⤵️
onClick={()=>{
let copy = [...글제목];
copy.unshift(입력값);
글제목변경(copy)
}}
처럼 unshift 를 사용해주면 된다
// 수정
<button className="button is-success mx-2"
onClick={() => {
let copy = [...props.title]; // 어레이 복사본을 뜬다
copy.splice(props.index, 1, "sujung wanryo"); // 뜬 복사본을 수정한다
props.settitle(copy); // 복사본을 원본에 씌워버린다
}}>글수정</button>
// 삭제
<button className="button is-danger"
onClick={() => {
let copy = [...props.title]; // 어레이 복사본을 뜬다
copy.splice(props.index, 1); // 복사본에서 해당 부분을 뜯어낸다
props.settitle(copy); // 복사본을 원본에 씌워버린다
}}>글삭제</button>
splice 메소드에서 괄호안에 (1, 1) 또는 (index, 1) 같은게 들어가는데
이는 (몇번째, 몇개) 를 지우거나 삭제할건지를 기입하는곳으로
(1,2) 를 입력하면 어레이 첫번째 부터 2개를 삭제하라 라는뜻이된다
["어레이1","어레이2"]
의경우 둘다 삭제된다
class Modal2 extends React.Component {
constructor(){
super()
}
render(){
return (
<div>안녕</div>
)
}
}
constructor, super, render
함수 3개 넣어주면 기본 템플릿.
컴포넌트는 길고 복잡한 html
축약할 때 쓰는것.
return
안에 축약할 html
적으면됨
class
는 변수, 함수 보관하는 통.
extends
는 기존 class
안에 있던 변수, 함수 복사해주는 문법.
React.Component
라는 class
안에 있던 변수와 함수들을 복사해야 컴포넌트라고 인정해주기 때문에 class 어쩌구 extends React.Component
라고 쓰는 것
리액트만든 사람이 그렇게 정한 것일 뿐.
class Modal2 extends React.Component {
constructor(){
super();
this.state = {
name : 'kim',
age : 20
}
}
render(){
return (
<div>안녕 { this.state.name }</div>
)
}
}
class Modal2 extends React.Component {
constructor(props){
super(props);
this.state = {
name : 'kim',
age : 20
}
}
render(){
return (
<div>안녕 { this<.props.프롭스이름 }</div>
)
}
}
홈피: https://react-bootstrap.github.io/getting-started/introduction
npm install react-bootstrap bootstrap
app.js 에 추가
import Button from 'react-bootstrap/Button';
혹은 index.html 에 추가
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
crossorigin="anonymous"
/>
https://react-bootstrap.netlify.app/components/buttons/#rb-docs-content
에서 검색하면됨.
버튼을 박고싶으면
<Button variant="primary">Primary</Button>
이렇게 박아주면 되는데, 일반적인 부트스트랩 사용법과 다른점은,
버튼까지 일일이 임포트해줘야함
import Button from 'react-bootstrap/Button';
아까 임포트 햇던것과 요소 하나하나 임포트해줘야 하는 번거로움;
많아지면 줄이 길어지니까
import {Button,Navbar,Nav,Container} from 'react-bootstrap';
이런식으로 써주는게 좋을듯.
빌드하면 src
폴더 안에 있는것들은 모두 압축된다.
public
폴더 안에 있는건 그대로.
public/img.jpg
를 사용하고싶다면
<img src="/img.jpg"/>
src/img.jpg
를 사용하고 싶다면
<img src="./img.jpg"/>
hayden.com
에 발행한다고 치면
<img src="/img.jpg"/>
이렇게 써도 무방.
그러나
hayden.com/shop
에 발행할 경우
<img src="/shop/img.jpg"/>
이렇게 해줘야함.
일일이 바꿀순 없으니
create react app 공식 사이트에 나와있는대로
<img src={process.env.PUBLIC_URL + '/logo192.png'} />
이렇게 해주면 됨. (권장사항)
(example.js)
let a = 10
export default a // 위의 a 를 익스포트 하겟다
그리고 사용하려면 임포트해준다
(app.js)
import 이그젬플 from './example.js'
import 이그젬플 from './example' // js 없어도 되긴함.
사용시에는 { } 중괄호
(app.js)
{ 이그젬플 }
(example2.js)
let a = 10
let b = 100
export { a,b }
(app.js)
import { a,b } from './example.js'
(app.js)
{ a }
{ b }
{data.map(item => (
<img key={item.id} src={require(`./src/img/${item.imgFile}`)} />
))}
require 을 붙여주면된다.
const 오브젝트어레이 = [
{ id: 1, src: 'image1.jpg' },
{ id: 2, src: 'image2.jpg' },
{ id: 3, src: 'image3.jpg' }
];
function App() {
return (
<div>
{오브젝트어레이.map(obj => ( // 각 오브젝트가 obj에 담긴다
<img key={obj.id} src={obj.src} /> // 담긴 obj 를 사용한다
))}
</div>
);
}
npm install react-router-dom
index.js
import { BrowserRouter } from "react-router-dom"; // 이부분 추가
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter> // 여기서
<App />
</BrowserRouter> // 여기까지
</React.StrictMode>
);
App.js
import { Routes, Route, Link } from "react-router-dom"
<Routes>
<Route path="/items" element={
<div>
<p> jsx 내용 </p>
</div>
} />
</Routes>
Link 는 말그대로 링크 버튼 만들때사용
<Link to="/">홈</Link>
<Link to="/detail">상세페이지</Link>
페이지 이동기능을 만들고 싶으면 useNavigate()
import { useNavigate } from 'react-router-dom'
<button onClick={()=>{ navigate('/detail') }}>디테일</button>
<button onClick={()=>{ navigate(1) }}>앞으로가기</button>
<button onClick={()=>{ navigate(-1) }}>뒤로가기</button>
<button onClick={()=>{ navigate(-2) }}>뒤로두번가기</button>
<Route path="*" element={ <div>없는페이지임</div> } />
<Route path="*">
맨 밑애 만들어야함.
맨위에 만들면 라우팅 해논거 다무시당하고 없는페이지됨
about 안에다가 member 이라는 하위 라우팅을 하려면?
<Route path="/about/member" element={ <div>멤버들</div> } />
<Route path="/about/location" element={ <div>회사위치</div> } />
⬆️이렇게 만들어되되고
⬇️이렇게 만들어도 똑같음
<Route path="/about" element={ <About/> } >
<Route path="member" element={ <div>멤버들</div> } />
<Route path="location" element={ <div>회사위치</div> } />
</Route>
근데 ⬆️이렇게 만든걸 네스티드 라우트라고함.
그리고 about 안에다 네스티드 라우트를 어디다 표시할지
<Outlet>
으로 표시해줘야함. 아니면 아무것도 표시안됨
아래 예문 참조
function About(){
return (
<div>
<h4>about페이지임</h4>
<Outlet></Outlet>
</div>
)
}
설치
npm install styled-components
임포트
import styled from 'styled-components'
기본적인 사용법
import styled from 'styled-components';
let Box = styled.div`
padding : 20px;
color : grey
`;
let YellowBtn = styled.button`
background : yellow;
color : black;
padding : 10px;
`;
function Detail(){
return (
<div>
<Box>
<YellowBtn>버튼임</YellowBtn>
</Box>
</div>
)
}
장점1. CSS 파일 오픈할 필요없이 JS 파일에서 바로 스타일넣을 수 있습니다.
장점2. 여기 적은 스타일이 다른 JS 파일로 오염되지 않습니다. 원래 그냥 CSS파일은 오염됩니다.
장점3. 페이지 로딩시간 단축됩니다.
왜냐면 저기 적은 스타일은 html 페이지의 <style>
태그에 넣어줘서 그렇습니다.
단점1. JS 파일이 매우 복잡해집니다.
그리고 이 컴포넌트가 styled 인지 아니면 일반 컴포넌트인지 구분도 어렵습니다.
단점2. JS 파일 간 중복 디자인이 많이 필요하면 어쩌죠?
다른 파일에서 스타일 넣은 것들 import 해와서 쓰면 됩니다.
근데 그럼 CSS파일 쓰는거랑 차이가 없을 수도요
단점3. CSS 담당하는 디자이너가 있다면 협업시 불편할텐데
그 사람이 styled-components 문법을 모른다면
그 사람이 CSS로 짠걸 styled-components 문법으로 다시 바꾸거나 그런 작업이 필요하겠군요.
그래서 신기술같은거 도입시엔 언제나 미래를 생각해보아야합니다.
css 파일명을 수정
파일명.css
⬇️
파일명.module.css
App.module.css
App.js
⬇️
App.js에만 적용
Lifecycle
컴포넌트는
인생에 간섭하는 방법
"Detail 컴포넌트 등장 전에 이것좀 해줘"
"Detail 컴포넌트 사라지기 전에 이것좀 해줘"
"Detail 컴포넌트 업데이트 되고나서 이것좀 해줘"
옛날 React에서 Lifecycle hook 쓰는 법
class Detail2 extends React.Component {
componentDidMount(){
//Detail2 컴포넌트가 로드되고나서 실행할 코드
}
componentDidUpdate(){
//Detail2 컴포넌트가 업데이트 되고나서 실행할 코드
}
componentWillUnmount(){
//Detail2 컴포넌트가 삭제되기전에 실행할 코드
}
}
요즘 React에서 Lifecycle hook 쓰는 법
import {useState, useEffect} from 'react';
function Detail(){
useEffect(()=>{
//여기적은 코드는 컴포넌트 로드 & 업데이트 마다 실행됨
console.log('안녕')
});
return (생략)
}
Q. 왜 저는 '안녕' 2번 출력됨?
index.js에 <React.StrictMode>라는 태그가 있으면 2번 출력해줍니다.
디버깅용으로 편하라고 2번 출력해주는데 싫으면 저 태그를 제거하거나 그럽시다.
근데 useEffect 밖에 적어도 똑같은데요
실은 useEffect 바깥에 적어도 똑같이 컴포넌트 mount & update시 실행됩니다.
컴포넌트가 mount & update시 function 안에 있는 코드도 다시 읽고 지나가서 그렇습니다.
useEffect 안에 적은 코드는 html 렌더링 이후에 동작합니다.
sideEffect = 부작용
여기서 따온거
컴포넌트의 핵심 기능은 html 렌더링이라
그거 외의 쓸데없는 기능들은 useEffect 안에 적으라는 소리입니다.
오래걸리는 반복연산, 서버에서 데이터가져오는 작업, 타이머다는거
이런건 useEffect 안에 많이 적습니다.
useEffect(()=>{ 실행할코드 }, [count])
useEffect()의 둘째 파라미터로 [ ] 를 넣을 수 있는데
거기에 변수나 state같은 것들을 넣을 수 있습니다.
그렇게 하면 [ ]에 있는 변수나 state 가 변할 때만 useEffect 안의 코드를 실행해줍니다.
그래서 위의 코드는 count라는 변수가 변할 때만 useEffect 안의 코드가 실행되겠군요.
(참고) [ ] 안에 state 여러개 넣을 수 있음
useEffect(()=>{ 실행할코드 }, [])
아무것도 안넣으면 컴포넌트 mount시 (로드시) 1회 실행하고 영영 실행해주지 않습니다.
useEffect(()=>{
그 다음 실행됨
return ()=>{
여기있는게 먼저실행됨
}
}, [count])
어따쓰는데?
setTimeout() 쓸 때마다 브라우저 안에 타이머가 하나 생깁니다.
그럼 잘못 코드를 짜면 타이머가 100개 1000개 생길 수도 있겠군요.
나중에 그런 버그를 방지하고 싶으면useEffect에서 타이머 만들기 전에 기존 타이머를 싹 제거하라고 코드를 짜면 되는데
그런거 짤 때 return ()=>{} 안에 짜면 됩니다.
setTimeout( ()=>{ 1초 후 실행할 코드 }, 1000);
// shorthand syntax
param => expression
// full syntax
(param) => {function body}
서버에 GET, POST 요청을 할 때 새로고침 없이 데이터를 주고받을 수 있게 도와주는 간단한 브라우저 기능을 AJAX라고 합니다.
AJAX로 GET/POST요청하려면 방법 3개 중 택1 하면 됩니다.
3번이 가장 편하니 3번을 써봅시다.
npm install axios
import axios from 'axios'
function App(){
return (
<button onClick={()=>{
axios.get('https://codingapple1.github.io/shop/data2.json').then((결과)=>{
console.log(결과.data)
})
.catch(()=>{
console.log('실패함')
})
}}>버튼</button>
)
}
원래 서버와는 문자만 주고받을수 있음.
axios 라이브러리는 JSON -> object/array 변환작업을 자동으로 해줘서
출력해보면 object/array가 나옴
fetch('URL').then(결과 => 결과.json()).then((결과) => { console.log(결과) } )
쌩자바스크립트 문법인 fetch() 를 이용해도 GET/POST 요청이 가능한데
그건 JSON -> object/array 이렇게 자동으로 안바꿔줘서 직접 바꾸는 작업이 필요합니다.
마음에 들면 쓰도록 합시다.