-
[React] 개발자 지망생 스터디 - 26일차스터디/KAKAOCLOUDSCHOOL 2022. 12. 7. 17:50
10. useCallback
1) 개요
> 특정 함수를 새로 만들지 않고 재사용하고자 할 때 사용
> 컴포넌트에 구현한 함수들은 컴포넌트가 랜더링될 때 마다 다시 생성
> 컴포넌트가 많아지고 랜더링이 자주 발생하면 함수들을 다시 만드는 것은 비효율적이 될 수 있음
> useCallback을 이용하면 데이터가 변경된 경우에만 함수를 다시 만들도록 할 수 있음
> 첫번째 매개변수는 함수이고 두번째 매개변수는 데이터의 배열임2) Average.jsx 파일을 수정
🗂 react_hooks → 📁 src → 📄 Average.jsx
import React, {useState, useMemo, useCallback} from 'react'; //배열의 평균을 구해서 리턴해주는 함수 const getAverage = (numbers) => { console.log("평균 계산"); if(numbers.length === 0) return 0; //reduce는 배열을 순회하면서 연산을 수행한 후 하나의 값을 리턴 //매개변수는 2개인데 첫번째 매개변수는 수행할 함수이고 //두번째 매개변수는 연산을 시작할 때의 초기값입니다. //두번째 매개변수를 생략하면 배열의 첫번째 요소로 설정 //첫번째 매개변수인 함수는 매개변수를 4개까지 갖는데 //첫번째는 누적값이고 두번째는 배열의 요소 //세번째는 배열의 인덱스이고 네번째는 배열 //[10, 20, 130, 240] //10+20=30, 30+130=160, 160+240=400 400리턴 const sum = numbers.reduce((a, b) => a + b); return sum / numbers.length; } const Average = () => { //숫자 배열 const [list, setList] = useState([]); //useMemo를 이용해서 getAverage를 호출 //list에 변화가 생긴 경우만 메서드를 호출하고 //그 이외의 경우는 결과를 재사용합니다. const avg = useMemo(() => getAverage(list), [list]) //입력받은 내용 const [number, setNumber] = useState(''); //input에 내용을 수정할 때 호출될 메서드 const onChange = useCallback(e => {setNumber(e.target.value)}, []); //추가를 눌렀을 때 호출될 메서드 //이 함수는 number 와 list가 변경될 때 만 다시 생성 const onInsert = useCallback(e => { const nextList = list.concat(parseInt(number)); setList(nextList); setNumber(''); }, [number, list]); return( <div> <input value={number} onChange={onChange}/> <button onClick={onInsert}>추가</button> <ul> {list.map((value, index) => ( <li key={index}>{value}</li> ))} </ul> <div> <b>평균:</b>{avg} </div> </div> ) } export default Average;
11. React.memo
1) 개요
> 컴포넌트의 props 가 변경되지 않았다면, 리랜더링을 방지해서 리랜더링의 성능 최적화를 해줄 수 있는 함수
> 이 함수를 이용해서 컴포넌트를 감싸주기만 하면 리랜더링이 필요한 상황에서만 리랜더링을 함2) Average.jsx 수정
🗂 react_hooks → 📁 src → 📄 Average.jsx
import React, {useState, useMemo, useCallback} from 'react'; //배열의 평균을 구해서 리턴해주는 함수 const getAverage = (numbers) => { console.log("평균 계산"); if(numbers.length === 0) return 0; //reduce는 배열을 순회하면서 연산을 수행한 후 하나의 값을 리턴 //매개변수는 2개인데 첫번째 매개변수는 수행할 함수이고 //두번째 매개변수는 연산을 시작할 때의 초기값입니다. //두번째 매개변수를 생략하면 배열의 첫번째 요소로 설정 //첫번째 매개변수인 함수는 매개변수를 4개까지 갖는데 //첫번째는 누적값이고 두번째는 배열의 요소 //세번째는 배열의 인덱스이고 네번째는 배열 //[10, 20, 130, 240] //10+20=30, 30+130=160, 160+240=400 400리턴 const sum = numbers.reduce((a, b) => a + b); return sum / numbers.length; } const Average = () => { //숫자 배열 const [list, setList] = useState([]); //useMemo를 이용해서 getAverage를 호출 //list에 변화가 생긴 경우만 메서드를 호출하고 //그 이외의 경우는 결과를 재사용합니다. const avg = useMemo(() => getAverage(list), [list]) //입력받은 내용 const [number, setNumber] = useState(''); //input에 내용을 수정할 때 호출될 메서드 const onChange = useCallback(e => {setNumber(e.target.value)}, []); //추가를 눌렀을 때 호출될 메서드 //이 함수는 number 와 list가 변경될 때 만 다시 생성 const onInsert = useCallback(e => { const nextList = list.concat(parseInt(number)); setList(nextList); setNumber(''); }, [number, list]); return( <div> <input value={number} onChange={onChange}/> <button onClick={onInsert}>추가</button> <ul> {list.map((value, index) => ( <li key={index}>{value}</li> ))} </ul> <div> <b>평균:</b>{avg} </div> </div> ) } export default React.memo(Average);
react styling
- 스타일 적용
1. styling 방식
- 일반 CSS 사용
- Sass : CSS 전처리기(pre-processor) 를 이용하는 방식으로 확장된 CSS 문법 사용
- CSS Module : 스타일을 작성할 때 CSS 클래스 이름이 다른 CSS 클래스 이름과 충돌하지 않도록 파일마다 고유한 이름을 자동으로 생성해주는 옵션
- styled-components : 컴포넌트 안에 스타일을 내장시키는 방식으로 동일한 스타일이 적용된 컴포넌트를 사용하는 방식
- 💡 실제 애플리케이션 제작 작업을 할 때 이런 방식으로 만들어진 컴포넌트들을 많이 이용
2. 일반 CSS 적용
1) 개요
> webpack 의 css-loader 를 이용해서 일반 CSS 를 불러오는 방식
> react 프로젝트를 생성하면 App.js 가 App.css를 불러와서 적용2) Naming
> react 프로젝트에서는 컴포넌트-클래스 이름의 형태로 네이밍
App-header : App 컴포넌트 안에 header라는 클래스를 의미함
> BEM(Block Element Modifier) 방법 - CSS 에서는 가장 많이 사용
각각의 요소는 - 나 _ 로 구분함
블럭요소이름__요소이름__수정자이름 의 형태로 네이밍
> SMACSS(Scalable and Modular Architecture for CSS) - 확장형 모듈식 구조
Base에는 아무런 접두어도 붙이지 않고 Layout 관련된 경우는 I - 이름을 정하는 방식
State의 경우는 is- 또는 s- 등을 추가
용도를 파악하기 편리
> 이외에도 OOCSS 등이 있음3) App.css를 수정해서 적용
🗂react_styling → 📁 src → 📄 App.js
> App.css 파일에 추가 (App 클래스 안에 a 태그)
.App a{ color: #333333; }
3. CSS Module 사용
1) 개요
> CSS를 불러와서 사용할 때 클래스 이름을 고유한값으로 만들어서 적용
[파일이름]_[클래스이름]_[해시값] 을 추가해서 클래스 이름을 부여
> 사용하는 방법은 CSS 파일의 확장자를 .module.css 로 만들면 됨
> 이 기능을 사용할 때는 CSS 파일의 클래스 이름을 일반적인 이름으로 사용하면 됨
> 별도의 클래스 이름을 부여하지 못하도록 하고자 하는 경우에는 클래스 이름 앞에 : global을 추가하면 됨2) 적용
# CSS 파일을 생성 - 이름.module.css
🗂react_styling → 📁 src → (CREATE) 📄 CSSModule.module.css
/* 파일 안에서만 사용하는 클래스 */ .wrapper{ background: black; padding: 1em; color: white; font-size: 2rem; } /* 모든 곳에서 사용할 수 있는 클래스 - 이름을 수정하지 않음 */ :global .something{ font-weight: 800; color: aqua; }
# css 파일의 디자인을 적용할 컴포넌트 생성
🗂react_styling → 📁 src → (CREATE) 📄 CSSModule.jsx
import React from "react"; import styles from "./CSSModule.module.css"; const CSSModule = () => { return( <div className = {styles.wrapper}> 처음 사용해보는 <span className="something">CSS Module</span> </div> ) } export default CSSModule;
3) 여러 개의 클래스 동시 적용
# CSSModule.module.css 파일에 클래스 추가
📁 src → 📄 CSSModule.module.css
.inverted{ color:black; background: white; border: 1px solid black; }
# CSSModule.jsx 파일 수정
📁 src → 📄 CSSModule.jsximport React from "react"; import styles from "./CSSModule.module.css"; const CSSModule = () => { return( <div className = {`${styles.wrapper} ${styles.inverted}`}> 처음 사용해보는 <span className="something">CSS Module</span> </div> ) } export default CSSModule;
4. classnames 라이브러리
1) 개요
> CSS 클래스를 조건부로 설정할 때 유용한 라이브러리로 여러 클래스를 설정할 때 편리
# 설치
☑︎ yarn : yarn add classnames
☑︎ npm : npm install classnames
# 설정 예시
☑︎ 2개 설정 : classNames('one', 'two')
☑︎ 3개 설정 : classNames('one', ['two', 'three'])
☑︎ 조건부 설정
> two 적용 : classNames('one', {'two':ture})
> two 적용 불가 : classNames('one', {'two':false})
:: true 나 false 위치를 변수로 설정하면 조건부 설정을 쉽게 구현하는 것이 가능함2) CSSModule.jsx 에 적용
# 설치
$yarn add classnames
#CSSModule.jsx 수정
📁 src → 📄 CSSModule.jsx
import React from "react"; import styles from "./CSSModule.module.css"; import classNames from 'classnames/bind'; // cx 안에서는 styles 생략하는 것이 가능 const cx = classNames.bind(styles); const CSSModule = () => { return( <div className = {cx('wrapper', 'inverted')}> 처음 사용해보는 <span className="something">CSS Module</span> </div> ) } export default CSSModule;
5. SASS
1) CSS Preprocessor(전처리기)
> CSS 가 동작하기 전에 사용하는 기능
> CSS의 불편함을 해결하기 위한 확장 기능
> 문법 자체는 CSS와 유사하지만 선택자의 중첩이나 조건문, 반복문, 다양한 단위의 연산 등이 가능함
> Sass, Less, Stylus 등이 있음2) SASS
> Syntactically Awesome Style Sheets
> 중복되는 코드를 줄여 가독성 좋게 작성이 가능함
> 가이드 참고 : https://sass-guidelin.es/ko/
# SCSS
> CSS 구문과 호환되도록 새로운 구문을 도입해 만든 SASS의 기능을 지원하는 CSS 의 super set
> 사용하기 위해서는 패키지 설치 필요
$yarn add node-scss scss-loader sass
> 확장자는 scss를 사용Sass Guidelines — Korean translation
분별 있고, 유지∙확장 가능한 Sass 작성을 위한 주관적인 스타일가이드.
sass-guidelin.es
3) CSSModule.module.css 파일을 수정해서 적용
📁 src → 📄 CSSModule.module.css
/* 파일 안에서만 사용하는 클래스 */ .wrapper{ background: black; padding: 1em; color: white; font-size: 2rem; /* wrapper 와 inverted가 모두 있는 경우에만 적용 */ & .inverted{ color:black; background: white; border: 1px solid black; } } /* 모든 곳에서 사용할 수 있는 클래스 - 이름을 수정하지 않음 */ :global { .something { font-weight: 800; color: goldenrod; } }
# CSSModule.jsx 파일 수정
📁 src → 📄 CSSModule.jsx
import styles from "./CSSModule.module.scss";
4) scss 파일을 생성하고 적용
# App.scss 파일을 생성하고 작성
📁 src → (CREATE) 📄 App.scss
import CSSModule from './CSSModule'; import styles from './App.scss'; import classNames from 'classnames/bind'; const cx = classNames.bind(styles); function App() { const isBlue = false; return( <div> <div className={cx('box',{blue:isBlue})}> <div className={cx('box-inside')}/> </div> <CSSModule/> </div> ) } export default App;
5) 변수와 믹스 인 사용
> $변수명:값; 의 형태로 변수를 만들어서 다른 곳에서 @import를 이용해서 사용이 가능
> 믹스 인은 여러 속성을 모아놓은 것
> 변수와 믹스 인은 자주 사용하는 속성이 있을 때 유용함@mixin 이름(){ 속성:값 ... } @incluse 이름();
# src 디렉토리에 styles 디렉토리 생성
📁 src → (CREATE) 📁 stytles
# 공통된 스타일 속성을 정의한 util.scss 파일 생성
📁 src → 📁 styles → (CREATE) 📄 util.scss
$size:100px; @mixin place-at-center(){ top:50%; left:50%; transform:translate(-50%, -50%); }
# App.scss 파일 상단에 코드를 추가하고 설정을 수정
📁 src → 📄 App.scss
:: @import './styles/util.scss';
:: width : $size
:: height : $size
:: @include place-at-center();
@import './styles/util.scss'; .box{ display: inline-block; width: $size; height: $size; border: 1px solid black; position: fixed; @include place-at-center(); &.blue{ background: blue; } &:hover{ background: yellow; } &:active{ background: red; } .box-inside{ background: black; width: 50px; height: 50px; } }
> 애플리케이션을 개발하다 보면 컴포넌트 1개와 scss 파일 1개가 쌍으로 만들어지는 경우가 많음
> 여러 컴포넌트를 만들다 보면 공통적으로 사용하는 scss 라이브러리들이 있는데 이러한 라이브러리들을 util.scss 에서 import하면 다른 곳에서도 별도의 import 없이 사용하는 것이 가능함6) SCSS 라이브러리
> SCSS가 이미 적용된 라이브러리
> 반응형 웹 디자인(디바이스의 크기에 상관없이 동일한 콘텐츠를 사용할 수 있도록 하는 것) : include- media
> 색상 : open-color(색상을 자주 사용하는 색상 이름과 숫자를 이용해서 강도를 설정하는 형태)
> icon : react-icons
※ 참고 자료
include-media : https://www.npmjs.com/package/include-media
open-color : https://yeun.github.io/open-color/
react-icon : https://react-icons.github.io/react-icons/include-media
Simple, elegant and maintainable media queries in Sass. Latest version: 1.4.10, last published: 2 years ago. Start using include-media in your project by running `npm i include-media`. There are 120 other projects in the npm registry using include-media.
www.npmjs.com
Open Color
Color scheme for UI design
yeun.github.io
React Icons
React Icons Include popular icons in your React projects easily with react-icons, which utilizes ES6 imports that allows you to include only the icons that your project is using. Installation (for standard modern project) npm install react-icons --save Usa
react-icons.github.io
# 라이브러리 설치
$yarn add include-media open-color react-icons
#util.scss 파일 수정
📁 src → 📁 styles → 📄 util.scss
@import '~open-color/open-color'; @import '~include-media/dist/include-media'; $breakpoints:( small:376px, medium:768px, large:1024px, huge:1200px ); $size:100px; @mixin place-at-center(){ top:50%; left:50%; transform:translate(-50%, -50%); };
# 컴포넌트를 만들 때 디렉토리 구조
> src 디렉토리 안에 components 라는 디렉토리를 생성
components 디렉토리 안에서 컴포넌트 파일과 디자인 파일을 생성
디렉토리 안에 컴포넌트 이름으로 디렉토리를 생성하고 컴포넌트 파일과 디자인 파일 그리고 index.js(컴포넌트를 내보내는 역할
만 수행) 파일을 생성
# 컴포넌트를 저장할 components 디렉토리 생성
📁 src → (CREATE) 📁 components
# components 디렉토리에 Button.jsx 파일 생성
📁 src → 📁 components → (CREATE) 📄 Button.jsx
import React from "react"; import styles from './Button.scss'; import classNames from "classnames/bind"; const cx = classNames.bind(styles); // props를 받아오는데 children으로 넘겨준 것은 children으로 받고 나머지는 rest로 받기 const Button = ({children, ...rest}) => { return( <div className={cx('button')}{...rest}> {children} </div> ) } export default Button;
# components 디렉토리에 Button.scss 파일을 생성하고 작성
📁 src → 📁 components → 📄 Button.scss
@import '../styles/util.scss'; .button{ background: $oc-green-7; transition: all .2s ease-in; display: inline-block; padding-top: 2rem; padding-bottom : 2rem; text-align: center; color: white; position: fixed; font-size: 2rem; border-radius: 4px; cursor:pointer; @include place-at-center(); width: 1200px; // 반응형 웹디자인 @include media("<huge"){ width:1024px; } @include media("<large"){ width:768px; } @include media("<medium"){ width:90%; } &:hover{ background: $oc-green-7; } &:active{ margin-top: 3px; background: $oc-green-8; } }
7) Material Design
#개요
> 웹 과 앱을 통틀어 모든 개발 플랫폼에서 사용자 경험을 하나로 묶기 위해서 (Progressive Web Design) 구글이 제시한 디자인 방식
#사용방법
> CDN 방식 - 스타일시트의 외부 링크 이용하는 것으로 네트워크를 사용할 수 없는 상태가 되면 적용되지 않음
💡 CDN(Content Delivery-Distrubution Network)
서버와 사용자 사이의 물리적인 거리를 줄여 콘텐츠 로딩에 소요되는 시간을 최소화하기 위해서 동일한 콘텐츠를 여러 네트워크에 분산 저장해서 요청을 하면 가장 가까운 Network에서 다운로드 하도록 해주는 서비스
> scss 파일이나 css 파일을 다운로드 받아서 적용 - 네트워크 사용 여부와 상관없이 사용할 수 있지만 앱의 크기가 커짐
# public 디렉토리 index.html 파일에 내용 수정
📁 public → 📄 index.html
:: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"/>
... <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"/> <title>React App</title> ...
# App.js 파일 내용 작성
📁 src → 📄 App.js
> 실행하고 위의 div와 아래의 div를 확인import './App.css'; function App() { return( <div> <nav> <div className="nav-wrapper"> <div>REACT</div> </div> </nav> <div>머터리얼 디자인</div> </div> ) } export default App;
# 다운로드 받아서 직접넣고 사용
> https://materializecss.com 에서 css나 scss를 다운로드
> 압축파일을 해제하고 프로젝트에 복사 (sass 폴더를 src 하위로 옮김)
🗂Project → 📁 src → (COPY) 📁 sass
> sass 디렉토리의 materialize.scss를 import 해서 사용Documentation - Materialize
materializecss.com
6. styled-components
1) 개요
> 자바스크립트 파일 안에 스타일을 선언하는 방식 - CSS in JS
> 컴포넌트와 디자인을 분리하지 않고 컴포넌트 와 디자인을 하나의 파일로 만들어서 사용하는 것으로 컴포넌트를 배치하면
자동으로 디자인이 적용
react-icons 의 각 icon을 컴포넌트를 삽입하는 형태로 추가하면 이미 설정된 style이 적용되서 출력이 됨
> 이러한 라이브러리의 종류는 https://github.com/MicheleBertoli/css-in-js 에서 확인 가능GitHub - MicheleBertoli/css-in-js: React: CSS in JS techniques comparison
React: CSS in JS techniques comparison. Contribute to MicheleBertoli/css-in-js development by creating an account on GitHub.
github.com
2) styled-component 를 이용한 버튼 만들기
# 패키지 설치
$yarn add styled-components
# components 디렉토리에 StyledComponent.jsx 파일로 만들고 작성
🗂react_styling → 📁 components → (CREATE) 📁 StyledComponent.jsx
import React from "react"; import styled, {css} from 'styled-components'; const Box = styled.div` backgroud:${props => props.collor || 'blue'}; padding:1rem; display:flex; `; const Button = styled.button` background:white; color:black; border-radius:4px; padding:0.5rem; display:flex; align-items:center; justify-content:center; box-sizing:border-box; font-size:1rem; font-weight:600; &:hover { background: rgba(255, 255, 255, 0.9); } & + button { margin-left: 1rem; } `; const styledComponent = () => { <Box color='black'> <Button>안녕하세요</Button> <Button>반갑습니다</Button> </Box> }; export default styledComponent;
#App.js에서 파일 출력
import StyledComponent from './components/StyledComponent'; import './App.css'; function App() { return( <div> <StyledComponent/> </div> ) } export default App;
7. 서버에서 데이터 받아오기
https://jsonplaceholder.typicode.com/users
1) 서버에서 데이터 받아오는 방법
#1 : ajax 이용 - load 이벤트 와 error 이벤트를 처리
import React from "react"; function App() { return( <div> <button onClick={(e)=>{ let request = new XMLHttpRequest(); // GET 방식으로 요청 request.open('GET', 'https://jsonplaceholder.typicode.com/users'); // POST 방식일 때는 send에 파라미터를 대입 request.send(''); request.addEventListener('load', ()=>{ let data= JSON.parse(request.responseText); // 데이터를 가져오는데 성공했을 때 처리 console.log(data); }); request.addEventListener('error', (error)=>{ // 데이터를 가져오는데 실패했을 때 처리 console.log(error); }); }}> 다운로드 </button> </div> ) } export default App;
#2 : Fetch API : Promise를 이용하는 APIimport React from "react"; function App() { return( <div> <button onClick={(e)=>{ fetch('https://jsonplaceholder.typicode.com/users') .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.log(error.message)) }}> 다운로드 </button> </div> ) } export default App;
#3 : axios 라이브러리를 사용
> $yarn add axiosimport React from "react"; import axios from 'axios'; function App() { return( <div> <button onClick={(e)=>{ axios.get('https://jsonplaceholder.typicode.com/users') .then(response => console.log(response.data)) .catch(error => console.log(error)) }}> 다운로드 </button> </div> ) } export default App;
> #3의 경우 코드가 상당히 간단해져서 사용하기 편리하나 라이브러리로서 변경될 경우에 대비해야한다.
따라서, 라이브러리를 사용하지 않는 #2의 Fetch API로 사용하는 것을 권장함2) Node 서버에서의 CORS 설정
# cors 라이브러리 설치
$yarn add cors
$npm add cors
# 서버 실행 파일에 추가 (Node에서만 사용 가능)
const cors = require('cors');
app.use(cors());3) 서버를 수정할 수 없을 때 proxy 설정
# package.json 파일에 설정을 추가
> "proxy" : "서버의 도메인"
> 요청을 할 때 /api/도메인 뒤의 url 로 요청하면 됨
# 라이브러리를 이용
> 라이브러리 설치
$yarn add http-proxy-middleware
> src 디렉토리의 setupProxy.js 파일을 만들고 작성
const {createProxyMiddleware} = require('http-proxy-middleware'); module.exports = (app) => { app.use(createProxyMiddleware('/클라이언트공통URL',{ target:'서버의URL', changeOrigin:true }) ); }
> http://localhost:9000/item/itemid에 요청
const {createProxyMiddleware} = require('http-proxy-middleware'); module.exports = (app) => { app.use(createProxyMiddleware('/api',{ target:'http://localhost:9000', changeOrigin:true }) ); }
'스터디 > KAKAOCLOUDSCHOOL' 카테고리의 다른 글
[React] 개발자 지망생 스터디 -28일차 (0) 2022.12.11 [React] 개발자 지망생 스터디 - 27일차 (0) 2022.12.08 [React] 개발자 지망생 스터디 - 25일차 (0) 2022.12.07 [React] 개발자 지망생 스터디 - 24일차 (0) 2022.12.06 [Node, React] 개발자 지망생 스터디 - 23일차 (0) 2022.12.05