Figma 프레임/가이드/그리드

프레임(디자인 작업 틀)

#모양을 클릭하면, 프레임으로 접근 가능

 

프레임 종류

 

가이드 (눈금자에서 드래그해서 꺼내서 사용, 안내선)

 

shift + R을 통해 상단, 왼쪽에 눈금자 표시

눈금자를 가죠오는 중간에 길이 비교를 하고 싶으면, Alt 이

 

그리드 (격자선)

margin = 프레임과 전체 컬럼간의 여백

gutter = 컬럼과 컬럼간의 여백

 

해당 프레임 클릭 -> Layout grid

 

 

 

해당 프레임 클릭 -> Layout grid - > grid를 columns로 변경

margin, gutter, count 수정 가능

 

 

 

해당 프레임 클릭 -> Layout grid - > grid를 rows로 변경

margin, gutter, count 수정 가능

 

http://gridcalculator.dk/

margin, gutter, count를 지정하여 넓이 계산을 쉽게 해주는 사이

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figma 사각형/원형

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React 카카오톡 로그인 (api)

카카오 개발자 센터 - 애플리케이션 추가 

1. 앱키를 통해 JavaScript키 확인

2. 플랫폼 - Web - Web플랫폼 등록 (Web 활성화)

3. 앱 권한 신청 - 비즈 앱 전환 - 개인 개발자 비즈 앱 전환 (biz 활성화)

%카카오 로그인 - 활성화 = 각종 개인정보를 얻을 수 있다%

 

발급받은 JavaScript키 입력

    // 카카오톡 로그인
    useEffect(() => {
        if (!window.Kakao.isInitialized()) {
            window.Kakao.init(''); // 발급받은 JavaScript 키
        }
    }, []);
    const handleKakaoLogin = () => {
        window.Kakao.Auth.login({
            success: function (authObj) {
                console.log('카카오 로그인 성공', authObj);
                navigate('/kakao/callback');
            },
            fail: function (err) {
                console.log('카카오 로그인 실패', err);
            },
        });
    };

 

/kakao/callback 함수

	const [isLoading, setIsLoading] = useState(true); // 로딩 상태 추적
    const [error, setError] = useState(null); // 에러 상태 추적

    useEffect(() => {
        // 로그인 후 /kakao/callback에서 인증 정보를 처리합니다
        if (window.Kakao.Auth.getAccessToken()) {
            // 로그인 성공 시 액세스 토큰을 가져옵니다
            const accessToken = window.Kakao.Auth.getAccessToken();
            console.log('Access Token:', accessToken);

            // 사용자 정보를 요청합니다
            window.Kakao.API.request({
                url: '/v2/user/me',
                success: function (res) {
                    console.log('사용자 정보:', res);
                    const kakaoAccount = res.kakao_account;

                    if (kakaoAccount.has_email) {
                        console.log('사용자 이메일:', kakaoAccount.email);
                        console.log('사용자 이름:', kakaoAccount.profile.nickname);
                        setIsLoading(false); // 로딩완료
                        // 로그인 성공 후 처리할 작업 추가 (예: 상태 저장, 리다이렉트 등)
                        navigate('/');
                    } else {
                        console.log('이메일 제공에 동의하지 않음');
                        setIsLoading(false); // 로딩 완료
                    }
                },
                fail: function (error) {
                    console.error('사용자 정보 요청 실패:', error);
                    setError('사용자 정보 요청에 실패했습니다.');
                    setIsLoading(false); // 로딩 완료
                },
            });
        } else {
            console.error('액세스 토큰이 없습니다.');
            setError('액세스 토큰이 없습니다.');
            setIsLoading(false); // 로딩 완료
        }
    }, [navigate]);

 

%만약 두번 실행 된다면, index.js - React.StrictMode삭제%

%React.StrictMode = 개발 모드에서 코드 품질을 향상시키고, 잠재적인 문제를 찾을 수 있도록 도와주는 도구%

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React 구글 로그인 (api)

vscode - npm install @react-oauth/google

https://console.cloud.google.com/ - 검색(oauth 동의 화면) - 프로젝트 생성 - User Type 외부로 설정 

- 범위 추가 또는 삭제 상단 3가지(이메일, 개인정보, 연결) 선택 

 

OAuth 동의 화면 - 앱 게시

 

사용자 인증 정보 - 사용자 인증 정보 만들기 - OAuth 클라이언트 ID 

- 승인된 JavaScript 원본 ( 구글 로그인을 사용할 홈페이지 주소 입력 )

 

- 승인된 리디렉션 URI ( 구글 로그인 후 Redirect할 주소 입력 )

 

사용자 인증 정보에서 클라이언트 ID클라이언트 보안 비밀번호를 확인 할 수 있다.

    // 구글 로그인
    const clientId = ''; // 클라이언트 id 기입
    const redirectUri = 'http://localhost:3000/google/callback';
    
    const handleGoogleLogin = () => {
        const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=token&scope=email%20profile&include_granted_scopes=true&state=state_parameter_passthrough_value`;
        window.location.href = googleAuthUrl; // Google 로그인 페이지로 리다이렉트
    };

 

/google/callback 함수

    const [isLoading, setIsLoading] = useState(true); // 로딩 상태 추적
    const [error, setError] = useState(null); // 에러 상태 추적

    useEffect(() => {
        const hash = window.location.hash;
        const params = new URLSearchParams(hash.substring(1)); // # 뒤의 값을 추출
        const accessToken = params.get('access_token'); // 토큰을 추출

        if (accessToken) {
            console.log('Access Token:', accessToken);

            // 사용자 정보를 요청하는 API 호출
            fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
                method: 'GET',
                headers: {
                    Authorization: `Bearer ${accessToken}`,
                },
            })
            .then((response) => {
                if (!response.ok) {
                    throw new Error('네트워크 응답이 좋지 않습니다.');
                }
                return response.json(); // JSON 형태로 응답 변환
            })
            .then((userInfo) => {
                console.log('사용자 정보: ', userInfo);
                console.log('사용자 이메일: ', userInfo.email);
                console.log('사용자 이름: ', userInfo.name)
                setIsLoading(false); // 데이터 로딩 완료 시 상태 변경
            })
            .catch((error) => {
                console.error('사용자 정보 요청 실패:', error);
                setError('사용자 정보 요청에 실패했습니다.'); // 에러 상태 처리
                setIsLoading(false); // 로딩 완료 상태
            });

            navigate('/'); // 토큰 처리 후 원하는 페이지로 이동
        } else {
            setIsLoading(false); // 액세스 토큰이 없으면 로딩 완료 처리
            setError('액세스 토큰이 없습니다.'); // 에러 처리
        }
    }, [navigate]);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React 네이버 로그인 (api)

https://developers.naver.com/ - Application - 애플리케이션 등록 - API 연동 정보 확인 

- Client ID, Client Secret 확인 - 네이버 로그인 검수 요청

 

 

 

 

 

 

 

 

 

 

 

 

React 미디어쿼리 (@media)

@media screen and (max-width:500px) {
    .asdf{
        background-color: red;
    }
}

화면의 너비가 500px이하가 되었을 때, asdf클래스 네임을 가진 부분의 배경색상 변화 (빨강색)

 

@media screen and (min-width:500px) {
    .asdf{
        background-color: blue;
    }
}

화면의 너비가 500px이상이 되었을 떄, asdf클래스 네임을 가진 부분의 배격생상 변화 (파랑색)

 

 

 

문제1) ~500px : red, 501 ~ 600px : green, 601px ~ : blue

@media screen and (max-width:600px) {
    .asdf{
        background-color: green;
    }
}
@media screen and (max-width:500px) {
    .asdf{
        background-color: red;
    }
}
@media screen and (min-width:600px) {
    .asdf{
        background-color: blue;
    }
}

max-width를 두가지 함으로써, 500px이하의 배경색상이 green 과 red가 겹친다.

우선순위의 문제를 해결하기 위해, 500px이하의 배경색을 설정하고 싶은 부분을 후순위로 배치한다.

 

 

문제 2) 화면 크기에 따라, 배치 순서 변경

@media(max-width:500px){
    main{
        order:0;
    }
    nav{
        order:1;
    }
    aside{
        order:2;
        display: none;
    }
}

main, nav, aside가 display: flex의 적용을 받고 있는 상태에서, order를 통해 배치 순서 설정 가능

display: none으로 아예 안보이게도 가능

 

 

https://www.w3schools.com/cssref/css3_pr_mediaquery.php : 더 많은 정보

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript Literal types

Literal types : 해당 변수에 지정한 값의 타입만 들어오게(더 엄격) 한다

방법 : :'kim' = kim만 들어오게 한다 ( |(union type)를 사용하여 설정 가능 )

* 함수에도 사용 가능

 

literal types의 문제점 : 

해당 값을 가져오는게 아니라, 해당 값의 타입을 가져온다

해결방법 : 

가져오는 값 뒤에 as const를 붙여준다 = literal type 지정을 알아서 해준다

(속성들에 모두 readonly 적용)

var 자료 = {
    name : "kim"
} as const


function 내함수(a :'kim'){
}

내함수(자료.name);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript 함수, methods에 타입 지정

생성 방법 : type 함수타입 = (a: string) => number;

적용 방법 : let 함수 :함수타입 = function(a){ return 1; }

type 함수타입 = (a :string) => number;

let 함수 :함수타입 = function(a){
    return 1;
}

함수("123");

 

* object 안에 함수 만들수 있다

* 함수안에 함수를 넣으려면, 받은 함수를 ()를 붙여서 사용

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript typescript로 HTML 변경 및 조작

tsconfig.json파일 - "strictNullChecks": true추가

* document.querySelector()를 쓰면, null이 아닌지 narrowing 해줘야한다

narrowing 방법 5가지

1. if (제목 != null) : 무난

2. if (제목 instanceof Element) : 자주 쓴다

3. document.querySelector() as Element : 웬만하면 쓰지 말것

4. if (제목?.innerHTML) : 제목이 있으면 출력, 없으면 undefined (if아니여도 인정해준다)

5. tsconfig.json파일 - "strictNullChecks": true 지우기 : 무식한 방법

 

* href를 바꿀 때, narrowing을 하려면 instanceof HTMLAnchorElement 사용

그 외 HTMLHeadingElement, HTMLButtonElement 등이 있다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript class 키워드(JS 문법)

<script>

    class Hero{
        constructor(구멍){
            this.q = 구멍;
            this.w = 'snowball';
        }
    }
    
    var nunu = new Hero("consume")
    var garen = new Hero("strike")

</script>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript prototype 문법(JS 문법)

prototype : 유전자

해당 클래스를 상속받은 객체들은 해당 클래스의 prototype을 설정해주면 모두 그 값을 받아올 수 있다

<script>

    class 기계{
        constructor(){
            this.q = "strike";
            this.w = 'snowball';
        }
    }
    
    기계.prototype.name = 'kim';

    var nunu = new 기계();
    var garen = new 기계();

    var 어레이 = [4, 2, 1];
    var 어레이 = new Array(4, 2, 1);

    Array.prototype.함수 = function(){}

</script>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript class 타입지정

constructor()는 필드값에 변수가 미리 있어야 this.변수명 이 가능하다

class Person{
    name :string;
    constructor(a :string){
        this.name = a;
    }
    함수(a :string){
        console.log('안녕' + a);
    }
}

let 사람1 = new Person("kim");
let 사람2 = new Person("park");

사람1.함수("안녕");

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript interface

object 타입지정시 interface 사용가능

interface의 장점 : extends로 복사가능

interface Student {
    name : string
}
interface Teacher extends Student{
    age : number
}

let 학생 :Student = {name : 'kim'}
let 선생 :Teacher = {name : 'kim', age : 20}

 

type alias도 &로 extend기능 사용 가능

type Animal = {
    name : string
}
type Cat  = {
    age : number
} & Animal

let 고양이 :Cat = {name : 'kim', age : 20}

 

type vs inteface

interface는 중복선언 가능

type은 중복선언 불가능

interface Student {
    name : string
}
interface Student {
    score : number
}
interface Teacher extends Student{
    age : number
}

let 학생 :Student = {name : 'kim', score : 23}
let 선생 :Teacher = {name : 'kim', age : 20, score : 100}

 

 

 

 

 

TypeScript 설치 셋팅

이미 있는 React프로젝트에 설치 방법 : 

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

 

React 프로젝트를 새로 만들어 설치 방법 :

npx create-react-app my-app --template typescript

 

그냥 vscode에서 typescript 실행 방법 : 

npm install -g typescript

tsconfig.json파일(ts -> js컴파일시 옵션 설정 가능) 생성 후

{

"compilerOptions":{

"target": "es5",

"module": "commonjs",

}

}

 

그 후 ts파일을 js로 인식하게 하려면 terminal창에 tsc -w 입력

그러면 코드를 입력하면 js파일이 생성된다

 

이제 .js 파일을 .ts 파일로 바꿔서 이용가능

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript 타입 지정 

javascript는 Dynamic Typing이 가능하다

typescript는 타입을 엄격히 검사해준다

 

변수를 만들 때 타입을 지정하는 방법 : let 변수명 :string = 'kim'

해당하는 변수에는 string 타입만 들어올 수 있게 설정

 

array 타입 지정 방법 : let 변수명 :string[] = ['kim', 'park']

 

object 타입 지정 방법 : let 변수명 :{name : string} = {name : 'kim'}

* let 변수명 :{name? : string} = {name : 'kim'} = ?를 통해 name이 없어도 에러 발생안하게 설정

object에 타입지정해야할 속성이 너무 많으면

* let 변수명: {[key :string] : string} = {name : 'kim', age : '123'} = 글자로 된 모든 object속성의 타입은 string

 

여러가지 타입의 데이터를 들어올 수 있게 하는 방법 : let 변수명 :string | number = 123;

 

* 타입을 따로 변수로 사용하면 만들수 있다

type 타입 = string | number;

let 변수명: 타입 = 123;

 

함수에 타입지정 하는 방법 : 

function 함수(x :number) :number{

return x * 2;

}

해당 함수는 파라미터로 number, return값으로 number

 

* class는 미리 타입을 지정해야한다

 

Tip : 타입지정은 원래 자동으로 된다. 타입지정 문법 생략 가능

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript 타입 지정 (union, any, unknown)

Union type(|)을 통해 두개 이상 타입 지정이 가능하다

array 타입 : let 회원들 :(number | string)[] = [1,'2',3];

object 타입 : let 오브젝트 :{a :(string | number)} = {a : 123};

* union타입은 두가지 타입을 합쳐서 새로운 타입을 만드는 것이다. 

 

any타입(타입 실드 해제 문법)을 통해 모든 자료형을 허용해줄 수 있다

let 이름 :any;
이름 = 123;
이름 = [];

 

unknown타입 : 기능은 any와 같으나, 조금 더 안전하다

이유 : any는 다른 곳에서 불러와도 타입실드를 해제한다 (오염)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript 타입 지정 (함수, void)

타입 지정하는 함수 만드는 방법 :

function 함수(x :number) :number{

return x * 2;

}

함수(6);

 

함수에서 void타입 활용 방법 : 

function 함수(x :number) :void{

x + 1;

}

함수(2);

:void = return을 했을 경우 error나게 설정

 

* 함수에서 파라미터값을 설정하면 typescript에서는 무조건 보내줘야한다

이것을 막기 위해서는 파라미터변수 뒤에 ?를 붙이면 된다

 

* 변수? :number는 변수 :number | undefined와 같다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript Narrowing & Assertion

type이 아직 하나로 확정되지 않았을 경우(string | number) Type Narrowing을 써야한다

대표적인 Narrowing 방법 : typeof

if문 등으로 Narrowing 해줘야 조작가능

 

assertion 문법 : 타입 덮어쓰기(확정되지 않은 타입을 변경, 확정된 타입은 변경 불가)

아직 확정되지 않은 변수 뒤에 as type명 = 해당 변수가 지정한 type으로 덮어 써진다

 

* assertion 문법의 용도 : 

1. Narrowing할 때 쓴다

2. 무슨 타입이 들어올지 100% 확실할 때 쓴다

 

웬만하면 Narrowing 쓰삼

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript type alias & readonly

type alias : 타입 변수

만드는 방법 : type 변수명(첫번째 대문자) = 내용;

 

* const 변수명 = 값 : 해당 변수의 값을 바꿀수 없다(const이기 때문에)

하지만 object는 바꿀 수 있다.

 

따라서, object여도 바꿀수 없게 하는 방법 : 

type을 지정해줄 때, 해당 obejct의 키값 앞에 readonly를 붙여준다 

type girlfriend = {
    readonly name : string
}

 

* 타입스크립트 에러는 에디터 & 터미널에서만 존재

실제 변환된 js파일은 에러 없음

 

type변수도 union type으로 extend(합치기) 가능

type Name = string;
type Age = number;
type Person = Name | Age;

 

 

object type 변수도 &로 extend(합치기) 가능

type PositionX = {x : number};
type PositionY = {y : number};
type NewType = PositionX & PositionY;

 

* type변수는 재정의 불가능하다

 

 

 

 

 

 

 

React 개발자 도구 & lazy import 

개발자 도구

https://chromewebstore.google.com/ - react delveloper tools 검색 - 다운로드 

* react로 만들어진 페이지만 확인 가능

F12 - Components = Element보다 좀더 편하게 확인 가능

 

F12 - Profiler = 성능저하되는 컴포넌트 범인 찾기

 

lazy import

Single Page Application 특징 : js파일 하나에 모든 코드를 다 쑤셔 넣는다. (사이즈가 크다)

따라서, 해당 페이지를 로딩할 때 필요하지 않는 import부분이 있으면, 

그 부분은 lazy import()로 묶어준다

 

로딩 중일 때, 흰색 화면이 뜨기 때문에 그것을 방지하기 위해 

Suspense를 import해주고, lazy import한 컴포넌트 부분을 <Suspense>로 감싸준다

import { Container, Nav, Navbar } from 'react-bootstrap';
import './App.css';
import bg from './img/bg2.jpg';
import { createContext, lazy, Suspense, useEffect, useState } from 'react';
import data from './data.js';
import { Routes, Route, Link, useNavigate, Outlet} from 'react-router-dom'
import axios from 'axios'
import { useQuery } from 'react-query';

//import Detail from './routes/Detail.js'
// import Cart from './routes/Cart.js'

const Detail = lazy(() => import('./routes/Detail.js'));
const Cart = lazy(() => import('./routes/Cart.js'));


export let Context1 = createContext()

function App() {

  let obj = {name : 'kim'}

  let [shoes, setShoes] = useState(data)
  let [재고] = useState([10, 11, 12])
  
  let navigate = useNavigate();

  useEffect(() => {
    localStorage.setItem('watched', JSON.stringify([]))

  }, [])

  let result = useQuery('작명', () => {
    return axios.get('https://codingapple1.github.io/userdata.json').then((a) => {
      console.log('요청됨')
      return a.data;
    })
  })

  return (
    <div className="App">
      
      <Navbar bg="light" data-bs-theme="light">
        <Container>
          <Navbar.Brand href="#home">ShopShop</Navbar.Brand>
          <Nav className="me-auto">
            <Nav.Link onClick={() => { navigate('/')}}>Home</Nav.Link>
            <Nav.Link onClick={() => { navigate('/cart') }}>cart</Nav.Link>
          </Nav>
          <Nav className='ms-auto'>
            {result.isLoading && '로딩중'}
            {result.error &&'에러남'}
            {result.data && result.data.name}
          </Nav>

        </Container>
      </Navbar>
      <Suspense fallback={<div>로딩중임</div>}>
      <Routes>
        <Route path='/' element={
          <>
          <div className='main-bg' style={{backgroundImage:'url(' + bg + ')' }}></div>
            <div className='container'>
              <div className='row'>
                {
                  shoes.map(function(a, i){
                    return(
                      <Card shoes = {shoes[i]} i = {i}></Card>
                    )
                  })
                }
              </div>
            </div>
            <button onClick={() => {
              axios.get('https://codingapple1.github.io/shop/data2.json')
              .then((data) => {
                console.log(data.data);
                let copy = [...shoes, ...data.data];
                setShoes(copy)
              })
              .catch(() => {
                console.log('실패함')
              })

            }}>더보기</button>
          </>
        } />
        <Route path='/detail/:id' element={
          <Context1.Provider value={{재고}}>
            
              <Detail shoes={shoes}/>
            
          </Context1.Provider >
        } />
        <Route path="/cart" element={<Cart></Cart>}></Route>
      </Routes>
      </Suspense>
      
    </div>
  );
}

function Card(props){
  let navigate = useNavigate();
  return(
    <div className='col-md-4'>
      <img onClick={() => {navigate('detail/' + (props.i))}} src={"https://codingapple1.github.io/shop/shoes" + (props.i+1) + ".jpg"} width="80%"></img>
      <h5>{props.shoes.title}</h5>
      <p>{props.shoes.price}</p>
    </div>
  )
}

export default App;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React memo & useMemo

자식 컴포넌트 재렌더링 막기

자식 컴포넌트 무거울 경우 계속 재렌더링 할 경우 속도가 저하될 수 있으므로, 

재 렌더링 필요없는 경우면, memo(import 해줘야함)로 해당 컴포넌트를 감싸준다

 

* memo 원리 : memo로 된 함수의 props가 변할 때에는 재렌더링 된다

 

useMemo(함수) : 해당하는 페이지가 처음 로드 될때만 실행하고, 나머지는 실행하지 않는다

하지만 useEffect처럼 , [state] 를 통해 state가 변경될때도 실행 가능

import { Table } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import { increase, changeName} from "./../store/userSlice";
import { addCount } from "./../store.js";
import { memo, useMemo, useState } from "react";

function 함수(){
    return 반복문 10억번;
}

let Child = memo( function(){
    console.log("재렌더링됨")
    return <div>자식임</div>
})

function Cart(){

    let result = useMemo(() => {return 함수()}, [state])

    let state = useSelector((state) => { return state })
    let dispatch = useDispatch()
    let [count, setCount] = useState(0)

    return(
        <div>
            <Child count={count}></Child>
            <button onClick={() => {setCount(count+1)}}>+</button>
            <h6>{state.user.name} {state.user.age}의 장바구니</h6>
            <button onClick={() => {dispatch(increase(10))}}>버튼</button>
            <Table>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        state.cart.map(function(a, i){
                            return(
                                <tr key={i}>
                                    <td>{a.id}</td>
                                    <td>{a.name}</td>
                                    <td>{a.count}</td>
                                    <td>
                                        <button onClick={() => {
                                            dispatch(addCount(a.id))
                                        }}>+</button>
                                    </td>
                                </tr>
                            )
                        })
                    }
                </tbody>
            </Table> 
        </div>
    )
}

export default Cart;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React useTransition & useDeferredValue

useTransition

계산이 많아서 성능이 저하될 때, 해당하는 함수에 useTransition을 사용

동작 원리 : 코드 시작을 뒤로 늦춰서, 한꺼번에 코드를 처리할 때의 성능저하를 막아줌

사용 방법 : useTransition을 import 한다

 

계산이 복잡한 부분에 startTransition을 감싸준다

 

계산중(로딩중)일 때 true를 반환한다

 

useDeferredValue

useTransition과 마찬가지로 느린 컴포넌트 성능을 향상시켜줌

useDeferredValue(state명) : 복잡한 state가 변할때 성능 향상

 

 

 

 

 

 

React if문 작성 패턴

1. 컴포넌트 안에서 쓰는 if/else

2. JSX안에서 쓰는 삼항연산자 

3. && 연산자로 if 역할 대신하기

4. switch / case 조건문

5. object/array 자료형 응용

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React localStorage

LocalStorage : 브라우저에서 제공하는 데이터 저장소 (키, 밸류 형식)

* 브라우저를 청소하면 삭제됨

 

* 새로고침하면 state가 초기값으로 돌아간다

따라서, localStorage에 저장한다(반영구적 저장가능)

 

localStorage 확인법 : F12 - application - Local Storage

localStorage 데이터 저장법 : localStorage.setItem('이름', '값')

localStorage 데이터 출력법 : localStorage.getItem('이름')

localStorage 데이터 삭제법 : localStorage.removeItem('이름')

* 수정은 따로 명령어가 없음

* sessionStorage로도 사용 가능

 

* localStorage에는 array/object로 저장하려면 JSON형식으로 변환 후 저장

JSON형식 변환 방법 : JSON.stringify(변수명)

JSON에서 array/object형식 변환 방법 : JSON.parse(변수명)

 

* array에서 중복제거를 쉽게하려면 Set자료형써도 된다

array -> Set(중복 없앤 array) -> array

Set으로 바꾸는 방법 = new Set(array명)

array로 바꾸는 방법 = Array.from(array명)

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React react-query (실시간 데이터)

react-query 설치 : npm install react-query

세팅 방법 : index.js - 

 

* QueryClient, QueryClienProvider import 하기

 

사용 방법 : useQuery('작명', () =< {return axios.get('url').then((data) => {retrun data})}

위에서 받아온 result.data = 데이터 

위에서 받아온 result.isLoding= 로딩 중일 때 true

위에서 받아온 result.error = 에러가 났을 때 true

 

* 틈만나면 자동으로 refetch 해줌

따라서 staleTime으로 refetch 간격 조절 가능

 

* 요청 실패시 알아서 retry 해준다

* ajax-query가 같은 요청을 다른 컴포넌트에서 해도 한번에 잘 보내주기 때문에 굳이 props 사용안해도 됨

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React PWA셋팅해서 앱으로 발행

PWA 장점 

1. 설치 마케팅 비용이 적음

2. 아날로그 유저들 배려

3. html css js 만으로 앱까지

4. 푸시 알림, 센서 등

 

* 기존 프로젝트를 PWA로 만드려면, 새 프로젝트를 만들고 기존 프로젝트 복붙

 

세팅 방법 : 

작업 폴더 열기 - terminal창 - npx create-react-app 프로젝트명 --template cra-template-pwa

- 해당 프로젝트 열기 - index.js - register로 변경

 

terminal창 - npm run build - build폴더가 생긴다(PWA에 필요한 것들이 생성)

 

* service-worker.js = 오프라인에서도 사이트 열 수 있게 도와줌

 

실행 방법 : 

vscode로 build 폴더 열기 - view - Extensions - Live Server 설치 

- index.html 우클릭 - Open with Live Server

우측 상단에 앱으로 연결하는게 새로 생김

 

해당 버튼을 누르면 위와 같이 뜬다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React async

async함수는 오래걸리면 제껴두고 다음 줄 코드부터 실행한다.

따라서 순차적으로(sync 스럽게) 순차적으로 실행하고 싶으면, useEffect를 사용한다

 

 

 

 

 

 

 

React Context API

context = state 보관함

context 가져오는 법 : let 변수명 = createContext() (createContext는 import해줘야 한다)

state공유를 원하는 것을 감싸준다 = <변수명.Provider>

value{} 안에 보내고 싶은 state명을 쓴다

 

보내고 싶은 state를 export해준다 

 

받고싶은 파일에서 import 해준다

 

해당 파일에서 보관함을 해체(state들이 남는다)

 

* 위와 같이 받아오면 useContext(변수명)을 통해 해당 파일의 어느 위치에서든 사용 가능

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React Redux (Redux toolkit 설치)

Redux = 보관함

Redux 설치 : npm install @reduxjs/toolkit react-redux

 

따라서 파일(store.js)을 생성

해당 파일에

import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
  reducer: { }

})

입력

 

index.js 파일에서 <Provider>태그 묶고, store에 해당 store.js 넣기

* Provider과 store 모두 import

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React Redux 보관 및 사용

Redux 쓰는 이유 : 컴포넌트간 state공유가 편해짐

store에 state 보관(생성)하는 방법 : 

 

store에 state 등록하는 방법 : 

 

store에 있는 state 가져오는 방법 : 

* return 값을 통해 원하는 state만 가져올 수 있다

 

import { configureStore, createSlice } from '@reduxjs/toolkit'

let user = createSlice({
    name : 'user',
    initialState : 'kim'
})

let stock = createSlice({
    name : 'stock',
    initialState : [
    {id : 0,
    name : 'White and Black',
    count : 2}, 
    {id : 2,
    name : 'Grey Yordan',
    count : 1}
    ]
})

export default configureStore({
  reducer: { 
    user : user.reducer,
    stock : stock.reducer
  }
})
import { Table } from "react-bootstrap";
import { useSelector } from "react-redux";

function Cart(){

    let a = useSelector((state) => { return state })
    console.log(a.stock)

    return(
        <div>
            <Table>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        a.stock.map(function(a, i){
                            return(
                                <tr>
                                    <td>{a.id}</td>
                                    <td>{a.name}</td>
                                    <td>{a.count}</td>
                                    <td>asdf</td>
                                </tr>
                            )
                        })
                    }
                </tbody>
            </Table> 
        </div>
    )
}

export default Cart;

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React Redux state 변경

state 변경하는 방법

state 변경 함수 만들기 : 

1. reducer : {} 을 통해 함수를 만든다. (여러함수 생성 가능)

 

2. 만든 함수를 export 한다

해당 state명.actions를 하고, 안에 있는 변경 함수들을 작성해 export 한다

 

3. 만든 함수 import해서 사용

* useDispatch() = store.js로 요청보내주는 함수다.

import { configureStore, createSlice } from '@reduxjs/toolkit'

let user = createSlice({
    name : 'user',
    initialState : 'kim',
    reducers : {
        changeName(state){
            return 'john ' + state;
        }
    }
})

export let {changeName} = user.actions

let cart = createSlice({
    name : 'cart',
    initialState : [
    {id : 0,
    name : 'White and Black',
    count : 2}, 
    {id : 2,
    name : 'Grey Yordan',
    count : 1}
    ]
})

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart : cart.reducer
  }
})
import { Table } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import { changeName } from "./../store.js";

function Cart(){

    let state = useSelector((state) => { return state })
    let dispatch = useDispatch()

    return(
        <div>
            {state.user}의 장바구니
            <Table>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        state.cart.map(function(a, i){
                            return(
                                <tr key={i}>
                                    <td>{a.id}</td>
                                    <td>{a.name}</td>
                                    <td>{a.count}</td>
                                    <td>
                                        <button onClick={() => {
                                            dispatch(changeName())
                                        }}>+</button>
                                    </td>
                                </tr>
                            )
                        })
                    }
                </tbody>
            </Table> 
        </div>
    )
}

export default Cart;

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React Redux state가 object/array일 경우 변경하는 방법

object/array일 경우 아래와 같이 변경 가능

 

* 변경함수에서 파라미터값을 받아올 수도 있다. (대신 .payload 사용)

 

store에 있던 파일이 길어질 경우 분할관리하는 방법

위와 같이 새로운 파일에 해당 처럼 작성해주고, 

store.js에서 import로 불러와준다

 

findIndex 란?

* 해당 조건식을 통해, 알아서 지정한 배열에서 반복문을 돌려 맞는 인덱스 번호를 가져온다.

 

import { configureStore, createSlice } from '@reduxjs/toolkit'

import user from './store/userSlice.js'

let cart = createSlice({
    name : 'cart',
    initialState : [
    {id : 0, name : 'White and Black', count : 2}, 
    {id : 2, name : 'Grey Yordan', count : 1}
    ], 
    reducers : {
        addCount(state, action){
            let num = state.findIndex((a) => {return a.id == action.payload});
            state[num].count++;
        },
        addItem(state, action){
            state.push(action.payload)
        }
    }
})

export let {addCount, addItem} = cart.actions

export default configureStore({
  reducer: { 
    user : user.reducer,
    cart : cart.reducer
  }
})
import { Table } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import { increase, changeName} from "./../store/userSlice";
import { addCount } from "./../store.js";

function Cart(){

    let state = useSelector((state) => { return state })
    let dispatch = useDispatch()

    return(
        <div>
            <h6>{state.user.name} {state.user.age}의 장바구니</h6>
            <button onClick={() => {dispatch(increase(10))}}>버튼</button>
            <Table>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        state.cart.map(function(a, i){
                            return(
                                <tr key={i}>
                                    <td>{a.id}</td>
                                    <td>{a.name}</td>
                                    <td>{a.count}</td>
                                    <td>
                                        <button onClick={() => {
                                            dispatch(addCount(a.id))
                                        }}>+</button>
                                    </td>
                                </tr>
                            )
                        })
                    }
                </tbody>
            </Table> 
        </div>
    )
}

export default Cart;

 

실행 결과

+ Recent posts