1-0 컴포넌트 꾸미기(CSS in JS)
Styled components 란? 조금 더 간편한 방법으로 컴포넌트를 꾸밀 수 있는 CSS-in-Js 방식
styled-components 사용하기
기본적인 사용법
꾸미고자 하는 컴포넌트를 SC의 방식대로 먼저 만들고, 그 안에 스타일 코드를 작성하는 방식으로 진행합니다.
// src/App.js
import React from "react";
// styled-components에서 styled 라는 키워드를 import 합니다.
import styled from "styled-components";
// styled키워드를 사용해서 styled-components 방식대로 컴포넌트를 만듭니다.
// const StBox = styled.div`` 가 핵심이 되는 코드인데 따옴표가 아니라 백틱을 사용
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의 태그가 옵니다. 아래 처럼 내가 원하는 html 태그를 사용해서 styled-components를 만들 수 있습니다.
div ➡️ styled.div
span ➡️ styled.span
button ➡️ styled.button
조건부 스타일링
CSS-in-JS 방식의 강점이 바로 스타일 코드를 JS코드 작성하듯이 스타일 코드를 작성할 수 있다는 점 입니다.
구현 예시
// 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;
Swtich문과 map을 사용해서 리팩토링 해보기
// src/App.js
import React from "react";
import styled from "styled-components";
const StContainer = styled.div`
display: flex;
`;
const StBox = styled.div`
width: 100px;
height: 100px;
border: 1px solid ${(props) => props.borderColor};
margin: 20px;
`;
// 박스의 색을 배열에 담습니다.
const boxList = ["red", "green", "blue"];
// 색을 넣으면, 이름을 반환해주는 함수를 만듭니다.
const getBoxName = (color) => {
switch (color) {
case "red":
return "빨간 박스";
case "green":
return "초록 박스";
case "blue":
return "파란 박스";
default:
return "검정 박스";
}
};
const App = () => {
return (
<StContainer>
{/* map을 이용해서 StBox를 반복하여 화면에 그립니다. */}
{boxList.map((box) => (
<StBox borderColor={box}>{getBoxName(box)}</StBox>
))}
</StContainer>
);
};
export default App;
더 알아보면 좋은 키워드
- styled-components의 GlobalStyles 이란 무엇이고, 어떻게 사용해야 할까?
- 스타일링을 전역 상태로 관리할 수 있다. h2 , p 등의 전체적인 스타일링을 할 수 있다. 따라서 같은 스타일링의 반복을 줄일 수 있다.
- app.js 혹은 index.js 등 라우터를 설정해주는부분에서 불러와 적용을 시켜주면 된다.
- css nesting 이란 무엇일까?
- CSS 규칙을 다른 규칙안에 종속 시켜 그룹화하고 편리하게 관리할 수 있다.
- nesting 을 위해서는 "nesting selelctor" 문법을 사용해야 한다.
- 중첩이 과하면 좋지 않다. 3회정도까지 하는것이 좋다.
- CSS reset 이란, 무엇이고 왜 필요한가?
- 브라우저 마다 style이 default 값으로 적용되어 있기 때문에 본래 의도와 다르게 css가 적용될 수있다.
- 이 브라우저 마다 default 값으로 적용되어 있는 style을 초기화 해주는 것을 css reset이라 한다.
- 초기화 하는 방법은 구글링 해볼 것.
02. React Hooks (useState)
함수형 업데이트
기본식
// 기존에 우리가 사용하던 방식
setState(number + 1);
// 함수형 업데이트
setState(() => {});
일반 업데이트와 함수형 업데이트의 차이
일반 업데이트 방식은 버튼을 클릭했을 때 , 첫번째 줄 ~ 세번째 줄의 있는 setNumber가 각각 실행되는 것이 아니라, 배치(batch)로 처리합니다. 즉 우리가 onClick을 했을 때 setNumber 라는 명령을 세번 내리지만, 리액트는 그 명령을 하나로 모아 최종적으로 한번만 실행을 시킵니다. 그래서 setNumber을 3번 명령하던, 100번 명령하던 1번만 실행됩니다.
반면에 함수형 업데이트 방식은 3번을 동시에 명령을 내리면, 그 명령을 모아 순차적으로 각각 1번씩 실행시킵니다. 0에 1더하고, 그 다음 1에 1을 더하고, 2에 1을 더해서 3이라는 결과가 우리 눈에 보이는 것이죠.
더 알아보면 좋은 키워드
- 왜 리액트팀은 useState가 위 방식으로 동작하도록 만들었을까?
03. React Hooks (useEffect)
1. useEffect
(1) useEffect는 언제 사용할까?
- useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
- 어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면? 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면? 그럴때 useEffect를 사용합니다.
- useState와 마찬가지로 React에서 제공하는 훅 (기능) 이므로, import React, { useEffect } from "react"; 로 import 해서 사용합니다.
(2) 코드로 보는 useEffect 기초
기본예시
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
// 이 부분이 실행된다.
console.log("hello useEffect");
});
return <div>Home</div>;
}
export default App;
(3) useEffect와 리렌더링(re-rendering)
useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행됩니다.
input에 값을 새로 입력하는 등 state가 변하게 되면 리렌더링되면서 같은 동작이 반복되는데, 아래와 같은 순서로 일어납니다.
- input에 값을 입력한다.
- value, 즉 state가 변경된다.
- state가 변경되었기 때문에, App 컴포넌트가 리렌더링 된다.
- 리렌더링이 되었기 때문에 useEffect가 다시 실행된다.
- 1번 → 5번 과정이 계속 순환환다.
우리는 콘솔이 브라우저에 한번만 찍히길 원했지만, input을 입력할 때마다 계속 찍히고 있는 것 입니다. 하지만 이런 부분도 해결할 수 있습니다. 그것이 바로 의존성배열
2. 의존성 배열
(1) 의존성 배열(dependency array) 이란?
“이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게” 라는 뜻입니다.
기본 식
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열])
(2) 코드로 보는 의존성 배열 → 의존성 배열이 빈 배열인 경우
아무것도 넣지 않았으니 useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행이 되서는 안됩니다.
useEffect를 사용하는데, 어떤 함수를 컴포넌트가 렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [ ] 빈 상태로 넣으면 됩니다.
3. clean up
(1) 클린 업 이란?
"어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면” 이제 컴포넌트가 사라졌을 때 무언가를 어떻게 실행하는지 배워봅시다. 이 과정을 우리는 클린 업 (clean up) 이라고 표현합니다.
(2) 클린 업 하는 방법
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
}
}, [])
return <div>hello react!</div>
};
export default App;
4. 정리
- useEffect는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅이다.
- 의존성 배열을 통해 함수의 실행 조건을 제어할 수 있다.
- useEffect 에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둔다.
5. 더 알아보면 좋은 키워드
- Strict mode란 무엇일까?
- 리액트에서 제공하는 애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구. 아래와 같은 경우를 발견하는데 도움이 된다고 한다.
- React 라이프 사이클이란 무엇일까?
- React에서 말하는 mount와 unmount란 무엇일까?
04. Redux part 1
1. 리덕스가 필요한 이유
(1) useState의 불편함
- 컴포넌트에서 컴포넌트로 State를 보내기위해서는 반드시 부-모 관계가 되어야 합니다.
- 조부모 컴포넌트에서 손자 컴포넌트로 값을 보내고자 할때도 반드시 부모 컴포넌트를 거쳐야만 한다. 즉, 정작 부모컴포넌트에서는 그 값이 필요가 없어도 단순히 손자 컴포넌트에게 전달하기 위해 불필요하게 거쳐야만 하는 것을 의미합니다. (조부모 → 부모 → 손자)
- 자식 컴포넌트에서 부모 컴포넌트로 값을 보낼 수 없습니다.
리덕스를 사용하면State를 공유하고자 할때 부-모 관계가 아니여도 되고, 중간에 의미없이 컴포넌트를 거치지 않아도 됩니다. 그리고 자식 컴포넌트에서 만든 State를 부모 컴포넌트에서도 사용할 수 있게 됩니다.
(2) Global state와 Local state
- Local state (지역상태) 란?
- 컴포넌트에서 useState를 이용해서 생성한 state 입니다. 좁은 범위 안에서 생성된 State 라고 생각하시면 됩니다.
- Global state (전역상태)란?
- Global state는 컴포넌트에서 생성되지 않습니다. 중앙화 된 특별한 곳에서 State들이 생성됩니다. 좀 더 쉽게 얘기해서 “중앙 state 관리소” 라고 생각하면 됩니다.

중앙 State관리소에서 State를 생성하고, 만약 어떤 컴포넌트에서 State가 필요하다면 컴포넌트가 어디에 위치하고 있든 상관없이 State를 불러와서 사용 할 수 있게 됩니다.
이러한 값들을 관리하는 것을 전역 상태 관리 라고 합니다.
2. 리덕스란 무엇인가?
(1) 리덕스 정의
리덕스란, 우리가 위에서 말한 “중앙 state 관리소”를 사용할 수 있게 도와주는 패키지(라이브러리) 입니다.
프론트엔드 개발자들은 “리덕스”를 전역 상태관리 라이브러리 라고 많이 표현합니다. 전역 상태, 즉 Global State를 의미하고 그것을 관리하게 도와주는 라이브러리 (패키지) 이기 때문입니다.
3. 정리
- 리덕스는 전역상태 관리 라이브러리이다.
- 리덕스는 useState를 통해 상태를 관리했을 때 발생하는 불편함을 일부 해소시켜준다.
- 리덕스는 중앙 State 관리소를 가지고 있으며, 모든 State는 이곳에서 생성된다.
- useState로 생성한 State는 Local State이고, 리덕스에서 생성한 State는 Global State이다.
4. 더 알아보면 좋은 키워드
- Props Drilling 란 무엇일까?
05. Redux part 2
1. 리덕스 설정
(1) 리덕스 설치
vscode 터미널에서 아래 명령어를 입력해서 2개의 패키지를 설치합니다.
yarn add redux react-redux
아래와 같은 의미
yarn add redux
yarn add react-redux
(2) 폴더 구조 생성하기

2. 설정 코드 작성
(1) 설정 코드 작성 시, 주의사항
- 우리가 작성하는 설정코드는 이해할 필요가 없는 코드들 입니다, 코드 분석 금지 ❌
- 리덕스 사용 “방법”을 중점으로 공부해야 합니다.지금 우리가 배우는 리덕스도 마찬가지 입니다. 리덕스를 어떻게 만들었는지, 왜 이렇게 설정하는지는 중요하지 않습니다. 우리가 해야할 것은 단지 리덕스를 쓰는 방법만 익히면 됩니다.
3. 정리
- 리액트에서 리덕스를 사용하려면, redux, react-redux 가 필요하다.
- 설정코드는 지금 당장 이해 할 필요가 없다.
- “중앙 State 관리소"를 Store (스토어)라고 부른다.
- 모듈이란, State들이 그룹이다.
06. Redux part 3
1. 모듈 만들기
모듈이란, State의 그룹입니다.
기본 코드
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
2. 모듈의 구성요소 살펴보기
(1) initialState === 초기 상태값
기본 예시
// 초기 상태값
const initialState = {
number: 0,
};
위 코드에서 만든 State의 초기값은 { } (객체) 이고, 그 안에 number 라는 변수에 초기값 0을 할당해준 것입니다. 초기값은 꼭 객체가 아니어도 됩니다. 배열이 되어도 되고, 그냥 원시데이터가 돼도 됩니다. 그리고 객체에도 여러개의 변수를 넣어줄 수 있습니다.
예시
// 초기값이 0
const initialState = 0;
// 초기값이 0이 있는 배열
const initialState = [0];
// 초기값이 number = 0, name = '석구'인 객체
const initialState = {
number: 0,
name: '석구'
};
(2) Reducer === 변화를 일으키는 함수
리듀서란, 변화를 일으키는 함수 를 말합니다.
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
리듀서의 인자에 보면 (state = intialState, action) 이라고 되어 있습니다.
우리는 리듀서 인자 첫번째 자리에서는 state를, 두번째 자리에서는 action 이라는 것을 꺼내서 사용할 수 있습니다. 이것이 무엇인지는 지금 당장 몰라도 됩니다. 다만, state = intialState 처럼 state에 initialState를 할당해줘야 하는 것만 기억해주세요.
(3) 카운터 모듈을 스토어에 연결하기
// src/redux/modules/config/configStore.js
// 원래 있던 코드
import { createStore } from "redux";
import { combineReducers } from "redux";
// 새롭게 추가한 부분
import counter from "../modules/counter";
const rootReducer = combineReducers({
counter: counter, // <-- 새롭게 추가한 부분
});
const store = createStore(rootReducer);
export default store;
3. 스토어와 모듈 연결 확인하기
(1) useSelector = 스토어 조회 = state를 가져다 쓸때 사용하는 것
우리가 생성한 모듈을 스토어에 잘 연결했는지 확인하는 방법은 컴포넌트에서 스토어를 직접 조회하면 됩니다. 컴포넌트에서 리덕스 스토어를 조회하고자 할때는 useSelector라는 ‘react-redux’의 훅을 사용해야 합니다.
// 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
});
4. 정리
- 보통 모듈은 기능의 이름을 따서 파일을 생성한다.
- 모듈의 구성요소로는 initialState, Reducer가 있다.
- 모듈을 만들면, 스토어에 연결을 해야만 한다. 그리고 연결은 configStore.js에서 한다.
- 컴포넌트에서 Store를 조회할 때는 useSelector를 사용해야 한다.
- useSelector((state)⇒state) 에서 state는 모든 모듈의 state 를 조회할 수 있는 값이다.
07. Redux part 4
0. *리덕스의 흐름 도식화*

- UI 에서 event가 일어난다.
- dispatch 에서 action이 일어나게 된다.
- action에 의한 reducer 함수가 실행되기 전에 middleware가 작동한다.
- middleware 에서 명령내린 일을 수행하고 난뒤, reducer 함수를 실행한다. (3, 4번은 아직 몰라도 됩니다!)
- reducer 의 실행결과 store에 새로운 값을 저장한다.
- store의 state에 subscribe 하고 있던 UI에 변경된 값을 준다.
- 출처 : https://ivorycode.tistory.com/entry/Redux%EC%9D%98-%ED%9D%90%EB%A6%84%EA%B3%BC-%EC%98%88%EC%A0%9C
1. counter.js 모듈의 state 수정 기능 만들기
(1) 리듀서에게 보낼 “명령” 만들기
명령을 보내기 전에 ‘명령'을 만들어야 합니다. 리덕스에서는 그 명령을 Action 이라고 합니다. 즉, 리듀서에게 내가 어떤 Action을 하길 원한다. 라고 표현하는 것이죠. 행동을 코드로 나타내면 객체 로 만듭니다. 그래서 이것을 액션 객체 라고 합니다.
액션 객체는 반드시 type이라는 key를 가져야 합니다. 왜냐하면 우리가 이 액션 객체를 리듀서에게 보냈을 때 리듀서는 객체 안에서 type이라는 key를 보기 때문입니다
// 예시 코드
//number에 +1 을 하는 액션 객체
{ type : "PLUS_ONE" };
(2) “명령”(액션 객체) 보내기
액션객체를 리듀서로 보내기 위해서는 새로운 훅을 사용해야 합니다. 그 훅은 useDispatch라는 훅 입니다. react-redux에서 import 해서 사용할 수 있으며, 우리가 만든 액션 객체를 리듀서로 보내주는 역할을 하는 훅입니다.
사용예시
// src/App.js
import React from "react";
import { useDispatch } from "react-redux"; // import 해주세요.
const App = () => {
const dispatch = useDispatch(); // dispatch 생성
return (
<div>
<button
// 이벤트 핸들러 추가
onClick={() => {
// 마우스를 클릭했을 때 dispatch가 실행되고, ()안에 있는 액션객체가 리듀서로 전달된다.
dispatch({ type: "PLUS_ONE" });
}}
>
+ 1
</button>
</div>
);
};
export default App;
디스패치를 이용해서 액션객체를 리듀서로 보낼 수 있습니다
(3) 액션 객체 받기
(4) 액션객체 명령대로 리듀서가 state값을 변경하는 코드 구현하기
리듀서가 액션객체를 받아 상태를 바꾸는 원리는 아래와 같습니다.
- 컴포넌트로부터 dispatch를 통해 액션객체를 전달 받는다.
- action 안에 있는 type을 스위치문을 통해 하나씩 검사해서, 일치하는 case를 찾는다.
- type과 case가 일치하는 경우에, 해당 코드가 실행되고 새로운 state를 반환(return) 한다.
- 리듀서가 새로운 state를 반환하면, 그게 새로운 모듈의 state가 된다.
사용예시
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
console.log(action);
switch (action.type) {
// PLUS_ONE이라는 case를 추가한다.
// 여기서 말하는 case란, action.type을 의미한다.
// dispatch로부터 전달받은 action의 type이 "PLUS_ONE" 일 때
// 아래 return 절이 실행된다.
case "PLUS_ONE":
return {
// 기존 state에 있던 number에 +1을 더한다.
number: state.number + 1,
};
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
(5) useSelector로 변경된 state값 확인하기
// src/App.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
const App = () => {
const dispatch = useDispatch();
// 👇 코드 추가
const number = useSelector((state) => state.counter.number);
console.log(number); // 콘솔 추가
return (
<div>
{/* 👇 코드 추가 */}
{number}
<button
onClick={() => {
dispatch({ type: "PLUS_ONE" });
}}
>
+ 1
</button>
</div>
);
};
export default App;
3. 정리
- 액션객체란, 반드시 type이란 key를 가져야 하는 객체이다. 또한 리듀서로 보낼 “명령"이다.
- 디스패치란, 액션객체를 리듀서로 보내는 “전달자” 함수이다.
- 리듀서란, 디스패치를 통해 전달받은 액션객체를 검사하고, 조건이 일치했을 때 새로운 상태값을 만들어내는 “변화를 만들어내는 함수"이다.
- 디스패치(dispatch)를 사용하기위해서는 useDispatch() 라는 훅을 이용해야 한다.
- 디스패치는 스토어의 내장함수 중 하나입니다.
- 우선, 디스패치는 액션을 발생 시키는 것 정도로 이해하시면 됩니다.
- dispatch 라는 함수에는 액션을 파라미터로 전달합니다.. dispatch(action) 이런식으로 말이죠.
- 액션객체 type의 value는 대문자로 작성한다. (JS에서 상수는 대문자로 작성하는 룰이 있음
08. Redux part 5
1. Action Creator
(1) Action Creator 란?
액션객체를 한곳에서 관리하기 좋도록 모아두는 것.
액션객체를 한 곳에서 관리할 수 있도록 “함수"와 액션 value를 상수로 만들어보겠습니다.
만약 PLUS_ONE 이라는 액션 객체를 만드는 함수를 만든다면 아래와 같이 만들 수 있습니다. 그리고 우리는 이것을 Action Creator라고 부릅니다. 해석 그대로 액션을 만드는 생성자 인 것이죠.
사용예시
// src/modules/counter.js
// 추가된 코드 👇 - 액션 value를 상수들로 만들어 줍니다. 보통 이렇게 한곳에 모여있습니다.
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";
// 추가된 코드 👇 - Action Creator를 만들어 줍니다.
export const plusOne = () => {
return {
type: PLUS_ONE,
};
};
export const minusOne = () => {
return {
type: MINUS_ONE,
};
};
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case PLUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다.
return {
number: state.number + 1,
};
case MINUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다.
return {
number: state.number - 1,
};
default:
return state;
}
};
export default counter;
(2) Action Creator 사용하기
사용하는 방법은 아래순서로 진행합니다.
- export된 Action Creator import 하기
- 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;
2. 왜 Action creator를 사용해야 하나?
(1) 휴먼에러 (오타) 방지
액션객체의 type value를 상수로 만들어놓았기 때문에, 개발툴에서 자동완성등의 보조 기능을 지원받을 수 있습니다. 그래서 의도치 않은 휴먼에러(오타)를 없앨 수 있어요
(2) 유지보수의 효율성 증가
우리가 만든 Action Creator가 만약 100군데에서 쓰이고 있는 상태에서 혹여나 그것을 바꾸어야 하는 상황이 오더라도 단 한번의 수정으로 100군데에 모든 수정사항을 반영할 수 있습니다.
(3) 코드 가독성
모듈 파일에서 Action Creator가 일목요연하게 정리가 되어있으면, 내가 아닌 다른 개발자가 보았을 때 해당 모듈이 가지고 있는 모든 Action들을 한눈에 알 수 있게 됩니다. 즉 그 자체가 Action 들의 리스트업을 해주는 역할을 갖게 되는 것 입니다.
bindActionCreators | Redux
API > bindActionCreators: wrapping action creators for dispatching
redux.js.org
3. 정리
- 액션객체를 만드는 함수를 Action Creator(액션 크리에이터)라고 한다.
- Action Creator는 모듈 파일안에서 생성된다.
- 액션객체의 type value로 상수로 생성해서 관리한다.
- Action Creator를 사용하면, 여러가지 문제점을 해소할 수 있다.
09. Redux part 6
1. Payload란?
지금까지는 ~을 이라는 목적어가 없었다면, 이제는 그 목적어가 생긴것이고 목적어도 액션객체에 담아 같이 보내줘야 할 것 입니다. 이렇게 액션객체에 같이 담아 보내주는 그 것을 우리는 payload라고 합니다. 만약 10을 더해 라는 것을 리듀서에게 보내고 싶으면 액션객체에 payload를 같이 담아주는 것 입니다.
???? 꼭 payload라는 이름을 통해서 보내야하나요?
리덕스 공식 문서를 확인해보면 액션은 객체이며 해당 액션이 어떤 기능을 수행해야 하는지 명시하는 type이라는 프로퍼티를 반드시 가져야 한다고 나와 있습니다. 그래서 지금까지는 아래와 같이 액션 객체에 type 프로퍼티를 추가해 어떤 기능을 수행해야 할지 명시해줬습니다.
하지만 그 외에 데이터들을 어떤 프로퍼티에 값으로 넣어줘야 하는지는 개발자 마음이라는거죠.
2. payload를 이용해서 기능 구현하기
(1) 사용자가 입력한 값을 받을 input 구현하기
input의 onChange 핸들러를 input안에서 인라인으로 작성하지 않고, 위쪽에서 함수를 별도로 분리하여 작성하고, 그것을 넣어주는 방식
// src/App.js
import React from "react";
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
const onChangeHandler = (event) => {
const { value } = event.target;
// event.target.value는 문자열 입니다.
// 이것을 숫자형으로 형변환해주기 위해서 +를 붙여 주었습니다.
setNumber(+value);
};
// 콘솔로 onChangeHandler가 잘 연결되었는지 확인해봅니다.
// input에 값을 넣을 때마다 콘솔에 그 값이 찍히면 연결 성공!
console.log(number);
return (
<div>
<input type="number" onChange={onChangeHandler} />
<button>더하기</button>
<button>빼기</button>
</div>
);
};
export default App;
(2) counter.js 모듈 작성: Action Creator
지금까지 작성한 Action Creator와 조금 차이가 있습니다. payload가 필요한 Action Creator에서는 함수를 선언할 때 매개변수 자리에 paylaod를 넣어줘야 합니다.
// src/redux/modules/counter.js
// Action Value
const ADD_NUMBER = "ADD_NUMBER";
// Action Creator
export const addNumber = (payload) => {
return {
type: ADD_NUMBER,
payload: payload,
};
};
// Initial State
// Reducer
// export default reducer
(3) counter.js 모듈 작성: Initial State, Reducer, 내보내기(export default)
우선 Initial State와 리듀서의 기본 형태를 만들어줍니다. 그리고 파일의 마지막 부분에 export default 를 통해서 생성한 리듀서를 내보내줄게요.
// src/redux/modules/counter.js
// .. 중략
// Initial State
const initialState = {
number: 0,
};
// Reducer
const counter = (state = initialState, action) => {
switch (action.type) {
case ADD_NUMBER:
return {
// state.number (기존의 nubmer)에 action.paylaod(유저가 더하길 원하는 값)을 더한다.
number: state.number + action.payload,
};
default:
return state;
// export default reducer
export default counter;
(4) 기능 작동 테스트
우선, 첫째로 App.js에서 useSelector를 이용해서 Store의 값을 조회하고 그것을 화면상에 렌더링하는 기능을 추가하겠습니다.
두번째로, Action Creator를 import 하고, payload를 담아 dispatch 해보겠습니다. 아래와 같이 코드를 추가 합니다.
import React from "react";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
// 4. Action Creator를 import 합니다.
import { addNumber } from "./redux/modules/counter";
const App = () => {
// 1. dispatch를 사용하기 위해 선언해줍니다.
const dispatch = useDispatch();
const [number, setNumber] = useState(0);
const globalNumber = useSelector((state) => state.counter.number);
const onChangeHandler = (event) => {
const { value } = event.target;
setNumber(+value);
};
// 2. 더하기 버튼을 눌렀을 때 실행할 이벤트핸들러를 만들어줍니다.
const onClickAddNumberHandler = () => {
// 5. Action creator를 dispatch 해주고, 그때 Action creator의 인자에 number를 넣어줍니다.
dispatch(addNumber(number));
};
return (
<div>
<div>{globalNumber}</div>
<input type="number" onChange={onChangeHandler} />
{/* 3. 더하기 버튼 이벤트핸들러를 연결해줍니다. */}
<button onClick={onClickAddNumberHandler}>더하기</button>
<button>빼기</button>
</div>
);
};
export default App;
3. Ducks 패턴
(1) Ducks 패턴이란?
Erik Rasmussn 라는 개발자가 이것을 패턴화하여 작성하는 것을 제안했는데, 그것이 바로 Ducks패턴입니다. Ducks 라는 이름은 리덕스의 덕스 라고 하네요. 심볼은 오리 입니다. 🐤
(2) Duck 패턴으로 작성하기
**Erik Rasmussen 이 제안한 Ducks 패턴은 아래의 내용을 지켜 모듈을 작성하는 것 입니다.**
- Reducer 함수를 export default 한다.
- Action creator 함수들을 export 한다.
- Action type은 app/reducer/ACTION_TYPE 형태로 작성한다.
(외부 라이브러리로서 사용될 경우 또는 외부 라이브러리가 필요로 할 경우에는 UPPER_SNAKE_CASE 로만 작성해도 괜찮다.)
4. 정리
- 리듀서로 보내는 액션객체에 어떤 정보를 같이 담아보내고자 한다면 payload를 이용한다.
- payload는 Action Creator를 생성할 때 매개변수에 자리에서 받을 준비를 하고, 반환하는 액션객체에 payload라는 key와 받은 매개변수를 value로 하여 구현한다.
- 리듀서에서 payload를 사용하고자 할 때는 action.payload로 사용할 수 있다.
- ES6에서 객체를 생성할 때 key와 value가 같으면 축약해서 작성할 수 있다.
- Ducks 패턴은 Erik Rasmussen 이 제안했고, 현재 리덕스 모듈 작성방법의 정석으로 여겨지고 있다.
10. react-router-dom part 1
1. react-router-dom이란?
react-router-dom을 배우면 여러 페이지를 가진 웹을 만들 수 있게 됩니다!
2 react-router-dom 설치하기
vscode 터미널에서 아래 코드를 입력
yarn add react-router-dom
3. react-router-dom 사용하기
(1) 페이지 컴포넌트 생성
src 폴더에 pages 라는 폴더를 만들고 그 안에 여러 페이지 파일 생서
(2) [중요] Router.js 생성 및 route 설정 코드 작성
브라우저에 우리가 URL을 입력하고 이동했을 때 우리가 원하는 페이지 컴포넌트로 이동하게끔 만드는 부분
src안에 shared 라는 폴더를 생성해주고, 그 안에 Router.js 파일을 생성
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;
(3) App.js에 Router.js import 해주기
App.js 코드
import React from "react";
import Router from "./shared/Router";
function App() {
return <Router />;
}
export default App;
우리가 어떤 컴포넌트를 화면에 띄우던, 항상 App.js를 거쳐야만 합니다. 그래서 path 별로 분기가 되는 Router.js 를 App.js에 위치시키고 우리의 서비스를 이용하는 모든 사용자가 항상 App.js → Router.js 거치도록 코드를 구현해주는 것입니다.
3. react-router-dom Hooks
(1) react-router-dom Hooks이란?
react-router-dom에서도 우리가 유용하게 사용할 수 있는 hook을 제공
(2) useNavigate
버튼의 클릭 이벤트 핸들러에 아래와 같이 코드를 작성하면 버튼을 클릭했을 때 우리가 보내고자 하는 path로 페이지를 이동시킬 수 있습니다.
사용 예시
// 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;
(3) 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;
(4) Link
Link 는 html 태그중에 a 태그의 기능을 대체하는 API 입니다. 만약 JSX에서 a 태그를 사용해야 한다면, 반드시 Link 를 사용해서 구현해야 합니다. 왜냐하면 a 태그를 사용하면 페이지를 이동하면서 브라우저가 새로고침(refresh)되기 때문입니다. 브라우저가 새로고침 되면 모든 컴포넌트가 다시 렌더링되야 하고, 또한 우리가 리덕스나 useState를 통해 메모리상에 구축해놓은 모든 상태값이 초기화 됩니다. 이것은 곧 성능에 악역향을 줄 수 있고, 불필요한 움직임입니다.
4. children 용도
children props를 가지고 페이지 레이아웃을 만들며 개별적으로 존재하는 Header, Footer, Page를 합성하여 개발자가 의도하는 UI를 만들어주는 Layout 컴포넌트를 만들 수 있다.
5. 정리
- react-router-dom을 이용하면, SPA 기반 인 리액트 프로젝트 안에서 여러개의 페이지를 구현할 수 있다.
- Router.js에 Route 설정에 관련된 코드를 작성하고, 최상위 컴포넌트인 App.js에서 import 해서 사용한다.
- react-router-dom는 여러가지 훅을 제공한다
6. 더 알아보면 좋은 키워드
- HTTP란 무엇일까?
- URI (URL, URN)
- SPA (Sigle Page Application)
11. react-router-dom part 2
1. 상세 페이지 구현하기
(1) Dynamic Route란?
Dynamic Route란, 동적 라우팅이라고도 말하는데 path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법을 말합니다.
(2) 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 라는 것이 바로 동적인 값을 받겠다라는 의미입니다. 그래서 works/1 로 이동해도 <Work /> 로 이동하고, works/2, works/3 …. works/100 모두 <Work /> 로 이동하게 해줍니다.
2. Dynamic Routes와 useParam
(1) query parameter 조회하기
useParam을 이용하면 같은 컴포넌트를 렌더링하더라도 각각의 고유한 id 값을 조회할 수 있습니다.
useParams이라는 훅을 사용해봅시다. useParams 은 path의 있는 id 값을 조회할 수 있게 해주는 훅 입니다.
Router.js
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';
import Layout from './Layout';
import Work from '../pages/Work';
const Router = () => {
return (
<BrowserRouter>
<Layout>
<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 />} />
<Route path="works/:id" element={<Work />} />
</Routes>
</Layout>
</BrowserRouter>
);
};
export default Router;
Works.js
// src/pages/Works.js
import React from 'react';
import { Link, useParams } from 'react-router-dom';
const data = [
{ id: 1, todo: '리액트 배우기' },
{ id: 2, todo: '노드 배우기' },
{ id: 3, todo: '자바스크립트 배우기' },
{ id: 4, todo: '파이어 베이스 배우기' },
{ id: 5, todo: '넥스트 배우기' },
{ id: 6, todo: 'HTTP 프로토콜 배우기' },
];
function Works() {
return (
<div>
{data.map((work) => {
return (
<div key={work.id}>
<div>할일: {work.id}</div>
<Link to={`/works/${work.id}`}>
<span style={{ cursor: 'pointer' }}>➡️ Go to: {work.todo}</span>
</Link>
</div>
);
})}
</div>
);
}
export default Works;
Work.js
// src/pages/Work.js
import React from 'react';
import { useParams } from 'react-router-dom';
const data = [
{ id: 1, todo: '리액트 배우기' },
{ id: 2, todo: '노드 배우기' },
{ id: 3, todo: '자바스크립트 배우기' },
{ id: 4, todo: '파이어 베이스 배우기' },
{ id: 5, todo: '넥스트 배우기' },
{ id: 6, todo: 'HTTP 프로토콜 배우기' },
];
function Work() {
const param = useParams();
const work = data.find((work) => work.id === parseInt(param.id));
return <div>{work.todo}</div>;
}
export default Work;
3. 정리
- react-router-dom을 통해 Dynamic Route를 설정할 수 있다.
- Dynamic Route를 설정할때는 :id 로 설정하고, id 값은 useParams을 이용해서 각 컴포넌트에서 조회할 수 있다.
4. 더 찾아보면 좋을 키워드
- query parameter, query string, path variable 이란 각각 무엇인가?
'Hanghae99' 카테고리의 다른 글
| 221208 TIL 언어스터디 4회차 (0) | 2022.12.09 |
|---|---|
| 221207 TIL 팀과제 1 (0) | 2022.12.07 |
| 221203 WIL 항해99 3주차 (0) | 2022.12.02 |
| 221201 TIL 주특기 언어 스터디 3회차 (0) | 2022.12.02 |
| 221201 TIL Visual Studio Code에서 github 연결하기 (0) | 2022.12.02 |