Just Do IT!
[React] 스파르타코딩 내일배움캠프 React 숙련 내용 정리 본문
728x90
반응형
컴포넌트 꾸미기 (CSS-in-JS)
CSS-in-JS
- 자바스크립트 코드로 CSS 코드를 작성하여 컴포넌트를 꾸미는 방식
- styled-components : 리액트에서 CSS-in-JS 방식으로 컴포넌트를 꾸밀 수 있게 도와주는 패키지
styled-components 준비하기
- VSCode 플러그인 : vscode-styled-components 설치
- styled-components 설치하기
# npm을 사용하는 경우
npm install --save styled-componetns
# yarn을 사용하는 경우
yarn add styled-components
styled-components 사용하기
예제
// src/App.js
import React from "react";
// styled-components에서 styled 라는 키워드를 import 합니다.
import styled from "styled-components";
// styled키워드를 사용해서 styled-components 방식대로 컴포넌트를 만듭니다.
const StBox = styled.div`
// 그리고 이 안에 스타일 코드를 작성합니다. 스타일 코드는 우리가 알고 있는 css와 동일합니다.
width: 100px;
height: 100px;
border: 1px solid red;
margin: 20px;
`;
const App = () => {
// 그리고 우리가 만든 styled-components를 JSX에서 html 태그를 사용하듯이 사용합니다.
return <StBox>박스</StBox>;
};
export default App;
- styled. 뒤에 html 태그를 사용해서 만들 수 있다.
- (백틱) 안에 원하는 스타일 코드를 작성하면 된다.
- 조건부 스타일링 : 스타일 코드를 JS 코드 작성하듯이 스타일 코드를 작성할 수 있다.
- 스타일 코드를 작성할 때 if문을 사용할 수 있다면? , switch문을 사용할 수 있다면? , 삼항연산자를 사용할 수 있다면? 같은 일부만 스타일 코드 작성하고 싶을 때
// src/App.js import React from "react"; import styled from "styled-components"; // 1. styled-components를 만들었습니다. const StBox = styled.div` width: 100px; height: 100px; border: 1px solid ${(props) => props.borderColor}; // 4.부모 컴포넌트에서 보낸 props를 받아 사용합니다. margin: 20px; `; const App = () => { return ( <div> {/* 2. 그리고 위에서 만든 styled-components를 사용했습니다. */} {/* 3. 그리고 props를 통해 borderColor라는 값을 전달했습니다. */} <StBox borderColor="red">빨간 박스</StBox> <StBox borderColor="green">초록 박스</StBox> <StBox borderColor="blue">파랑 박스</StBox> </div> ); }; export default App;
- props를 통해서 부모 컴포넌트로부터 값을 전달받고, 조건문을 이용해서 조건부 스타일링을 할 수 있다.
React Hooks (useState)
useState 복습
- 가장 기본적인 hook이며, 함수 컴포넌트에서 가변적인 상태를 가지게 해준다.
- state를 변수로 사용했고, setState를 이용해서 state 값을 수정할 수 있다
- 만약 state가 원시 데이터 타입이 아닌 객체 데이터 타입인 경우 불변성을 유지해줘야 한다.
함수형 업데이트
- setState의 () 안에 수정할 값이 아니라 함수를 넣을 수 잇다.
- 그 함수의 인자에서는 현재의 state를 가지고 올 수 있고 {} 안에서 이 값을 변경하는 코드를 작성할 수 있다.
// 기존에 우리가 사용하던 방식
setState(number + 1);
// 함수형 업데이트
setState(() => {});
// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환한 것 입니다.
setState((currentNumber)=>{ return currentNumber + 1 });
- 일반 업데이트 방식과 함수형 업데이트 방식의 차이점
- 일반 업데이트 방식: 명령을 여러번 내려도 리액트는 그 명령을 하나로 모아 최종적으로 한 번만 실행시킨다
- 함수형 업데이트 : 동시에 명령을 내리면 그 명령을 모아 순차적으로 1번씩 실행시킨다
Reach Hooks (useEffect)
useEffect
- 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
- useEffect가 속한 컴포넌트가 화면에 렌더링될 때 실행된다
- import React, { useEffect } from ‘react’;로 import 해서 사용한다
- 어떤 컴포넌트가 화면에 보여졌을 때 무언가를 실행하고 싶거나 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶을 때 사용
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
// 이 부분이 실행된다.
console.log("hello useEffect");
});
return <div>Home</div>;
}
export default App;
의존성 배열(dependency array)
- 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행하는 것
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열])
- useEffect를 사용하는데, 어떤 함수를 컴포넌트가 렌더링될 때 단 한번만 실행하고 싶으면 의존성 배열을 [ ] 빈 상태로 넣으면 된다.
- 의존성 배열에 값이 있는 경우에는 그 값이 변할 때마다 useEffect가 실행횐다.
clean up
- 컴포넌트가 사라졌을 때 무언가를 실행하는 과정
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
}
}, [])
return <div>hello react!</div>
};
export default App;
Redux
Redux가 필요한 이유
- useState의 불편함
- 컴포넌트에서 컴포넌트로 state를 보내기 위해서는 반드시 부-모 관계가 되어야 한다
- 어떤 컴포넌트에서 그 값이 필요 없어도 단순히 전달하기 위해 불필요하게 거쳐가야 한다.
- 자식 컴포넌트에서 부모 컴포넌트로 값을 보낼 수 없다.
- Redux를 써야 하는 이유
- state를 공유하고자 할 때 부-모 관계가 아니어도 된다
- 중간에 의미없이 컴포넌트를 거치지 않아도 된다
- 자식 컴포넌트에서 만든 sate를 부모 컴포넌트에서도 사용할 수 있다
- Local State (지역 상태)
- 컴포넌트에서 useState를 이용해서 생성한 staet
- 좁은 범위 안에서 생성된 State
- Global State (전역 상태)
- 컴포넌트에서 생성되지 않는다
- 중앙 state 관리소와 같은 의미
- 중앙 state 관리소에서 state를 생성하고, 만약 어떤 컴포넌트에서 state가 필요하다면 컴포넌트가 어디에 위치하고 있든 상관 없이 state를 불러와서 사용할 수 있게 된다.
- 특정 컴포넌트에 종속되어 있는 것이 아니라 ‘중앙 state 관리소’에서 생성된 state를 global state라고 한다.
- 전역 상태 관리 : 이러한 값들을 관리하는 것
Redux
- 중앙 state 관리소를 사용할 수 있게 도와주는 패키지(라이브러리)
- 전역 상태 관리 라이브러리
- 리덕스 설치
- react-redux : 리덕스를 리액트에서 사용할 수 있도록 서로 연결시켜주는 패키지
yarn add redux react-redux 아래와 같은 의미 yarn add redux yarn add react-redux
- 폴더 구조 생성하기
- src 폴더 안에 redux 폴더를 생성
- redux 폴더 안에 config, modules 폴더를 생성
- config 폴더 안에 configStore.js파일을 생성합니다.
각각의 폴더와 파일은 역할이 있습니다.
- redux : 리덕스와 관련된 코드를 모두 모아 놓을 폴더 입니다.
- config : 리덕스 설정과 관련된 파일들을 놓을 폴더 입니다.
- configStore : “중앙 state 관리소" 인 Store를 만드는 설정 코드들이 있는 파일 입니다.
- modules : 우리가 만들 State들의 그룹이라고 생각하면 됩니다. 예를 들어 투두리스트를 만든다고 한다면, 투두리스트에 필요한 state들이 모두 모여있을 todos.js를 생성하게 되텐데요, 이 todos.js 파일이 곧 하나의 모듈이 됩니다.
- 설정 코드 작성
src/configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";
/*
1. createStore()
리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수) 입니다.
리덕스는 단일 스토어로 모든 상태 트리를 관리한다고 설명해 드렸죠?
리덕스를 사용할 시 creatorStore를 호출할 일은 한 번밖에 없을 거예요.
*/
/*
2. combineReducers()
리덕스는 action —> dispatch —> reducer 순으로 동작한다고 말씀드렸죠?
이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생합니다.
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/
const rootReducer = combineReducers({});
const store = createStore(rootReducer);
export default store;
index.js
// 원래부터 있던 코드
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
// 우리가 추가할 코드
import store from "./redux/config/configStore";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
//App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣어줍니다.
);
// 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();
모듈
- 모듈 : state의 그룹
- initialState === 초기 상태값
- 어떤 state의 초기값을 정해주는 것
- 초기값은 꼭 객체가 아니어도 된다. (배열, 원시데이터 가능, 여러 개의 변수 넣어줄 수 있따)
- Reducer === 변화를 일으키는 함수
- reducer는 함수다
- redux에서는 값을 바꾸고 싶으면 reducer가 이 역할을 한다
- (state = initialState, saction)
- state에 initialState를 할당해줘야 한다.
- useSelector = 스토어 조회
- 조회할 때는 useSelector라는 ‘react-redux’의 훅을 사용해야 한다.
- 화살표 함수에서 꺼낸 state라는 인자는 현재 프로젝트에 존재하는 모든 리덕스 모듈의 state이다.
// 1. store에서 꺼낸 값을 할당 할 변수를 선언합니다. const number = // 2. useSelector()를 변수에 할당해줍니다. const number = useSelector() // 3. useSelector의 인자에 화살표 함수를 넣어줍니다. const number = useSelector( ()=>{} ) // 4. 화살표 함수의 인자에서 값을 꺼내 return 합니다. // 우리가 useSelector를 처음 사용해보는 것이니, state가 어떤 것인지 콘솔로 확인해볼까요? const number = useSelector((state) => { console.log(state) return state });
리덕스의 흐름
- View 에서 액션이 일어난다.
- dispatch 에서 action이 일어나게 된다.
- action에 의한 reducer 함수가 실행되기 전에 middleware가 작동한다.
- middleware 에서 명령내린 일을 수행하고 난뒤, reducer 함수를 실행한다. (3, 4번은 아직 몰라도 됩니다!)
- reducer 의 실행결과 store에 새로운 값을 저장한다.
- store의 state에 subscribe 하고 있던 UI에 변경된 값을 준다.
- 리듀서란, 디스패치를 통해 전달받은 액션객체를 검사하고, 조건이 일치했을 때 새로운 상태값을 만들어내는 “변화를 만들어내는" 함수이다.
- 리듀서에게 보낼 명령 만들기
- 액션 객체 : 리듀서에게 어떤 action을 하기를 원한다고 표현하는 걸 코드로 나타내면 객체로 만든다
- 반드시 type이라는 key를 가져야 한다. (우리가 이 액션 객체를 리듀서에게 보냈을 때 리듀서는 객체 안에서 type이라는 key를 보기 때문)
- 액션객체 type의 value는 대문자로 작성한다. (JS에서 상수는 대문자로 작성하는 룰이 있음)
- 리덕스 모듈에 있는 state를 변경하기 위해서는 그에 해당하는 액션 객체를 모두 만들어줘야 한다.
- 명령(액션 객체) 보내기
- 액션 객체를 리듀서로 보내기 위해서는 새로운 훅을 사용해야 한다. ⇒ useDispatch
- react-redux에서 import해서 사용할 수 있으며, 우리가 만든 액션 객체를 리듀서로 보내주는 역할을 한다.
- useDispatch라는 훅을 사용하기 위해서는 컴포넌트 안에 dispatch라는 변수를 생성해줘야 한다. ⇒ dispatch는 함수다
- dispatch () 안에 액션 객체를 넣어주면 된다.
- dispatch 라는 함수에는 액션을 파라미터로 전달한다. (. dispatch(action) 이런식으로)
- 액션 객체 명령대로 리듀서가 state 값을 변경하는 코드 구현하기
- 컴포넌트로부터 dispatch를 통해 액션객체를 전달 받는다.
- action 안에 있는 type을 스위치문을 통해 하나씩 검사해서, 일치하는 case를 찾는다.
- type과 case가 일치하는 경우에, 해당 코드가 실행되고 새로운 state를 반환(return) 한다.
- 리듀서가 새로운 state를 반환하면, 그게 새로운 모듈의 state가 된다.
Action Creator
- 액션 객체의 value를 변경해아 할 일이 생길 때 ⇒ 액션 객체를 한 곳에서 관리할 수 있도록 “함수”와 액션 value를 상수로 만든다
- Action Creator : 액션을 만드는 생성자
- 액션의 value는 상수로 따로 만들어주고 그것을 이용해서 액션 객체를 반환하는 함수를 작성한다
// src/redux/modules/counter.js
const PLUS_ONE = "PLUS_ONE"; // value는 상수로 생성
// 액션객체를 반환하는 함수 생성
// export 가 붙는 이유는 plusOne()는 밖으로 나가서 사용될 예정이기 때문입니다.
export const plusOne = () => {
return {
type: PLUS_ONE, // type에는 위에서 만든 상수로 사용 (vscode에서 자동완성 지원)
};
};
- 사용하는 순서
- export 된 Action Creator imoprt 하기
- dispatch()에 있던 액션 객체를 지우고 Action creator 넣기
// src/App.js import React from "react"; import { useDispatch, useSelector } from "react-redux"; // 사용할 Action creator를 import 합니다. import { minusOne, plusOne } from "./redux/modules/counter"; const App = () => { const dispatch = useDispatch(); const number = useSelector((state) => state.counter.number); return ( <div> {number} <button onClick={() => { dispatch(plusOne()); // 액션객체를 Action creator로 변경합니다. }} > + 1 </button> {/* 빼기 버튼 추가 */} <button onClick={() => { dispatch(minusOne()); // 액션객체를 Action creator로 변경합니다. }} > - 1 </button> </div> ); }; export default App;
- Action Creator를 사용하는 이유
- 휴먼에러(오타) 방지
- 유지보수의 효율성 증가
- 코드 가독성
- 리덕스 공식문서에서 소개되고 있는 방법
Payload
- 목적어도 액션 객체에 담아서 같이 보내줄 때 사용
- state를 변경하는 데 있어 만약 리듀서에게 어떤 값을 같이 보내줘야 한다면 payload를 액션객체에 담아 보낸다
- payload는 Action Creator를 생성할 때 매개변수에 자리에서 받을 준비를 하고, 반환하는 액션객체에 payload라는 key와 받은 매개변수를 value로 하여 구현한다.
- 리듀서에서 payload를 사용하고자 할 때는 action.payload로 사용할 수 있다.
- ES6에서 객체를 생성할 때 key와 value가 같으면 축약해서 작성할 수 있다.
⭐️ 꼭 payload라는 이름을 통해서 보내야하나요?
리덕스는 굉장히 유연한 라이브러리이기 때문에 많은 것들이 “표준화”되어 있지 않습니다. 때문에 자신만의 방식으로 프로그래밍 할 수 있는 유연성을 제공하죠.
리덕스 공식 문서를 확인해보면 액션은 객체이며 해당 액션이 어떤 기능을 수행해야 하는지 명시하는 type이라는 프로퍼티를 반드시 가져야 한다고 나와 있습니다. 그래서 지금까지는 아래와 같이 액션 객체에 type 프로퍼티를 추가해 어떤 기능을 수행해야 할지 명시해줬습니다.
{type: "ADD_NUMBER"}
하지만 그 외에 데이터들을 어떤 프로퍼티에 값으로 넣어줘야 하는지는 개발자 마음이라는거죠.
- payload를 이용해서 기능 구현 작업하기
- 사용자가 입력한 값을 받을 input 구현하기
- Action Creator 작성하기
- 리듀서 작성하기
- 구현된 기능 테스트 하기
Ducks 패턴
- 리덕스를 사용하기 위해서는 우리가 리덕스의 구성요소를 모두 만들어야만 사용 가능하다
- 이를 패턴화하여 작성한 것
- Reducer 함수를 export default 한다.
- Action creator 함수들을 export 한다.
- Action type은 app/reducer/ACTION_TYPE 형태로 작성한다.
(외부 라이브러리로서 사용될 경우 또는 외부 라이브러리가 필요로 할 경우에는 UPPER_SNAKE_CASE 로만 작성해도 괜찮다.)
⇒ 모듈 파일 1개에 Action Type, Action Creator, Reducer 가 모두 존재하는 작성방식
React-router-dom
react-router-dom
- 패키지 설치하기 : yarn add react-router-dom
- 사용방법
- 페이지 컴포넌트 생성
- Router.js 생성 및 router 설정 코드 작성
- 브라우저에 우리가 URL을 입력하고 이동했을 때 우리가 원하는 페이지 컴포넌트로 이동하게끔 만드는 부분
- URL 1개당 페이지 컴포넌트를 매칭해주는 것 ⇒ 이 한개의 URL = Route
import React from "react"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import Home from "../pages/Home"; import About from "../pages/About"; import Contact from "../pages/Contact"; import Works from "../pages/Works"; const Router = () => { return ( <BrowserRouter> <Routes> {/* Routes안에 이렇게 작성합니다. Route에는 react-router-dom에서 지원하는 props들이 있습니다. path는 우리가 흔히 말하는 사용하고싶은 "주소"를 넣어주면 됩니다. element는 해당 주소로 이동했을 때 보여주고자 하는 컴포넌트를 넣어줍니다. */} <Route path="/" element={<Home />} /> <Route path="about" element={<About />} /> <Route path="contact" element={<Contact />} /> <Route path="works" element={<Works />} /> </Routes> </BrowserRouter> ); }; export default Router;
- App.js에 import 및 적용
- App 컴포넌트에 넣어주는 이유 : 우리가 만든 프로젝트에서 가장 최사우이에 존재하는 컴포넌트가 App.js 이기 때문
- 페이지 이동 테스트
react-router-dom Hooks
- react-router-dom에서도 우리가 유용하게 사용할 수 있는 hook을 제공
- React-router-dom 공식문서 바로가기
- useNavigate
- navigate 를 생성하고, navigate(’보내고자 하는 url’) 을 통해 페이지를 이동 시킬 수 있다
- 어떤 버튼을 누르면 페이지로 이동하거나 어떤 컴포넌트를 눌렀을 때 페이지를 이동하게 만들 때 사용하는 훅
// src/pages/home.js import { useNavigate } from "react-router-dom"; const Home = () => { const navigate = useNavigate(); return ( <button onClick={() => { navigate("/works"); }} > works로 이동 </button> ); }; export default Home;
- useLocation
- react-router-dom을 사용하면, 우리는 우리가 현재 위치하고 있는 페이지의 여러가지 정보를 추가적으로 얻을 수 있다
- 이 정보들을 이용해서 페이지 안에서 조건부 렌더링을 사용하는 등, 여러 용도로 활용할 수 있다
// src/pages/works.js import { useLocation } from "react-router-dom"; const Works = () => { const location = useLocation(); console.log("location :>> ", location); return ( <div> <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div> </div> ); }; export default Works;
- Link
- 훅은 아니지만 꼭 알아야할 API
- html 태그 중에서 a 태그의 기능을 대체하는 API
- JSX에서 a 태그를 사용해야 할 때 반드시 Link를 사용해서 구현해야 한다
- a 태그를 사용하면 페이지를 이동하면서 브랑루저가 새로고침(refresh)되기 때문
- 브러우저가 새로고침 되면 모든 컴포넌트가 다시 렌더링되야 하고, 리덕스나 useState를 통해 메모리상에 구축해놓은 모든 상태갓이 초기화 된다.
import { Link, useLocation } from 'react-router-dom'; const Works = () => { const location = useLocation(); console.log('location :>> ', location); return ( <div> <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div> <Link to="/contact">contact 페이지로 이동하기</Link> </div> ); }; export default Works;
children 용도
- 어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올지 미리 예상할 수 없는 경우가 있습니다. 범용적인 ‘박스’ 역할을 하는 Sidebar 혹은 Dialog와 같은 컴포넌트에서 특히 자주 볼 수 있다
- 범용적인 “박스” 역할을 하는 컴포넌트란 크게 봤을 때 Layout 역할을 하는 컴포넌트라고 생각하면 된다
Dynamic Route (동적 라우팅)
- path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법을 말한다
- react-router-dom에서 지원하는 Dynamic Routes 기능을 이용해서 간결하게 동적으로 변하는 페이지를 처리할 수 있다
- Dynamic route 설정하기 예시
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
<Route path="works" element={<Works />} />
{/* 아래 코드를 추가해주세요. 👇 */}
<Route path="works/:id" element={<Works />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
- ⇒ path에 works/:id 라고 path가 들어갑니다.:id 는 useParams 훅에서 조회할 수 있는 값
- :id 라는 것이 바로 동적인 값을 받겠다라는 의미
Dynamic Routes와 useParam
- query parameter 조회하기
- <Route path="works/:id" element={<Work />} />
- useParam을 이용하면 같은 컴포넌트를 렌더링하더라도 각각의 고유한 id 값을 조회할 수 있다
- useParams : path의 있는 id 값을 조회할 수 있게 해주는 훅
숙련 강의를 듣고 노션에 정리해둔 것을 옮긴 것이다.
블로그에 글을 쓰다보면 겹치는 부분들이 많지만...그냥 반복 학습이라고 생각하고 있다...ㅎ
728x90
'개발 공부 > React' 카테고리의 다른 글
[React] react-toastify 사용하기 (0) | 2022.12.19 |
---|---|
[React] React의 생명주기 (LifeCycle) 이해하기 (0) | 2022.12.15 |
[React] Redux(리덕스) 이해하기 (1) | 2022.12.13 |
[React] 처음 만난 리액트 강의 내용 정리 (1) | 2022.12.09 |
[React] 스파르타코딩 내일배움캠프 React 입문 내용 정리 (0) | 2022.12.06 |