리액트 시작

npx create-react-app 폴더이름

타입 스크립트 시작

npx create-react-app 폴더이름 --template typescript

JSX & 문법

JSX [형태]

1. 최상위 element는 형제 요소가 없는 고유한 요소여야야한다

ex) 가능한 형태

function App() {
  return (
    <div>
      <h1>가능</h1>
    </div>
  );
}

export default App;

불가능한 형태

function App() {
  return (
    <div>
     
    </div>
    <h1>불가능</h1>
  );
}

export default App;

최상위에 아무것도 넣고 싶지 않다면 빈태그로 넣을 수 있다.

 

2. 종료 태그는 무조건 존재해야한다. ex) < h1></h1>이거나  <input />

 

 

JSX [변수 삽입]

위에서 선언한 변수를 html에 {}로 넣을 수 있다.

 

JSX [아이디와 클래스]

id 적용이 가능하며, class는 className으로 입력해야한다.

 

JSX [스타일 적용]

style 적용은 중괄호 안의 중괄호, 즉 객체 형태로 작성하여 스타일을 넣어준다.

ex) { {color : 'red', fontSize : '40px'} } // 카멜케이스로 넣어야한다.

 

문법 [map()과 filter()]

JSX에서 변수 삽입할 때 사용하는 중괄호 안에는 if문이나 for문은 들어가지 못한다. 그래서 배열을 편하게 html에 보여주고 싶을때, map 함수와 filter 함수를 이용해 편하게 보여줄 수 있다.

 

- map() 함수

예를 들어서 나열을 하고 싶은 arr이라는 배열이 있을때, for문을 사용하지 않고 map을 이용한다면 편하게 정렬할 수 있다.

let arr = [1, 2, 3, a, b, c];

arr.map((val, index, arr) => {

        retrun 보여주고 싶은 값

});

여기서 각 콜백 함수에 대해 설명하자면

val : 현재 돌고 있는 값

index : 돌고 있는 값의 인덱스값

arr : 현재 돌고 있는 배열 전체

 

그리고 보통 리액트에서 사용할때는 객체가 배열에 들어가 있는 경우가 많기 때문에 단일로 써서 각 객체에서 원하는 값을 불러낸다. 여기서 중요한 점은 key값을 설정해줘야 오류가 나지 않는다. 아래와 같은 객체를 가진 배열이 있다고 가정하자. 그럼 map은 다음과 같이 사용할 수 있다.

//리액트의 한 컴포넌트라고 가정하자

    export default function 컴포넌트이름 () {
        //여기에 옆에 있는 객체를 가진 배열도 존재한다고 가정한다.

        return (<>
            <ul>
                {list.map((listData) => {
                    return <li key={listData.id}> {listData.alpha} </li>
                })}
            </ul>
        </>)
    }

따로 아이디가 존재하지 않다면 인덱스를 가지고 오는 콜백함수를 키값으로 넣을 수 있다. 

 

- filter() 함수

 보통 map 함수를 이용해 배열을 나열하기 전에 어떠한 조건을 기준으로 배열을 가공할 때 사용한다. 즉, filter 함수로 리턴된 배열을 map함수로 나열한다고 생각하면 편하다. 왜냐하면 filter 함수는 map 함수처럼 html 자체를 반환해주지는 않고, 그냥 조건에 맞는 배열 자체를 보내주기 때문이다. 사용법은 다음과 같다.

//리턴한 배열을 받기 위해서 변수를 선언하어 사용하는 경우가 많다.
arr.filter((val, index, arr)=> {
	return 조건
})

 

 

문법 [sort함수]

sort 함수를 통해 문자나 숫자를 정렬할 수 있다.

 

문법 [구조분해할당] 

지금까지는 변수를 선언할때 보통

let num = [1, 2, 3, 4];

이런 식으로 했었는데, 선언을 할때부터 각 값을 지정할 수 있다.

let [a, b, c, d] = [1, 2, 3, 4];

이를 활용하여서 어떠한 객체 파일을 보냈을때 받은 데이터에서 원하는 값만 사용하고 싶은 경우 구조분해 할당을 통해 원하는 값만 뽑아서 사용할 수 있다.

ex)

보낸 객체

const data = {

    name: result.name,

    pw: result.pw,

}

 

받은 곳에서 이름만 사용하고 싶음

const data = req.body.data //(경우에 따라 params가 올 수도 있다.)

let { name } = data;


Component

MVC 모델의 view를 독립적으로 구성해 재사용할 수 있고, 새로운 컴포넌트도 만들 수 있다. 간단히 말하자면 하나로 이어진 html 파일은 구분하기와 읽기가 어렵기 때문에 개별적으로 컴포넌트를 만들어준 후에 App.js에서 import해서 가져올 수 있다. 이 과정에서 props를 넘겨주면 컴포넌트끼리 데이터를 간편하게 주고 받을 수 있다.

 

컴포넌트는 원래 함수형 컴포넌트롸 클래스형 컴포넌트가 있다. 예전에는 state와 라이프 사이클을 쓸 수 있는 클래스형

컴포넌트를 많이 사용했지만 현재는 Hook으로 state와 라이프사이클 등 다양한 부분들을 함수형 컴포넌트에서도 사용할 수 있게 되면서 함수형 컴포넌트를 많이 사용하게 됐다.  각각의 컴포넌트 구조를 알아보자

 

- 클래스형 컴포넌트

클래스형 컴포넌트는 리액트에서 컴포넌트를 불러오고 상속을 받아서 사용을 한다. 그래서 랜더 안에 리턴을 넣어서 사용한다.

import {Component} from "react";

class 컴포넌트이름 extends Component {
	render() {
    	//삼항연산자 한 번 써보자
        const classes = 'dd';
        
        return (
        	<>
            	<div>{classes === 'dd' ? "dd씨 반가워요" : "넌 뭐야"}
                {/* 참이라면 앞의 것을 출력해주고 거짓이라면 뒤의 것을 출력해준다. */}
                <div> 반가워!! </div>
            </>
        )
    }
    
    export default 컴포넌트이름;

}

- 함수형 컴포넌트

함수형 컴포넌트는 위에서 따로 불러올 필요 없이 바로 함수로 만들어서 사용할 수 있다. 그리고 함수 특성을 이용해 작성 동시에 앞에 export default를 적어서 바로 export 해 줄 수 있다.

export default function 컴포넌트이름() {
	
    return (<>
    	<div>함수형 컴포넌트</div>
    </>)
}

 클래스형 컴포넌트와 비교하면 정말 간단한 것을 알 수 있다. 그리고 export할때 디폴트를 쓰는 것과 았르 때 import 하는 방법이 다른데 다음과 같다.

이것도 기억하면 좋을 것 같아서 가져왔다.


Props (+ JS와 Ts 사용법 차이)

부모 컴포넌트에서 자식 컴포넌트로 정보를 보낼 때 사용한다. 보통 상위 컴포넌트에서 다른 컴포넌트를 불러올 때 아래와 같이 동시에 쓴다.

- JS 사용법

import Child from './Child';
import {useState} from 'react';


export default Parents() {
	const[name, setName] = useState('내이름');
    
    return (<>
    	<Child 보내는이름=문자열 혹은 {위에서 정의한 변수나 state}></Child>
    </>)
}

위와같이 작성하면 자식 컴포넌트에서 props로 받아서 쓸 수 있다.

 
 export default Child(props) {
 	
    return (<>
    	<div>{props}</div>//배열일 경우 {props[원하는인덱스값]}
        <div>{props.children}</div>// 위에서 보낸 값 말고 <>이사이의 값</>을 가져온다.
    </>)
 }
 
 //구조분해할당 방식
 
  export default Child({name, children}) {
 	
    return (<>
    	<div>{name}</div>
        <div>{children}</div>
    </>)
 }

- Ts 사용법

근데 이를 TypeScript에서 쓰려면 부모 컴포넌트와 자식 컴포넌트에서 타입을 모두 지정해줘야 오류가 나지 않고 잘 전달된다.

import Child from './Child';
import {useState} from 'react';


export default Parents() {
	const[name, setName] = useState<string>('내이름');//타입 지정해줘야함
    conconst[nameArr, setArr] = useState<string[]>(['내이름', '너이름', '우리이름']);
    //배열일 경우 타입지정
    
    return (<>
    	<Child name={name} setName={setName} name={nameArr} setName={setArr} >넌 할 수 있어</Child>
        //자식 컴포넌트에서 어떤 타입으로 받아야할지 모르겠다면, 
        //여기서 해당 변수위에 마우스를 올리면 어떠 타입인지 알 수 있다. 
    </>)
}

위와 같이 보내주었다면 개별로 타입을 지정해주는 것보다 interface로 만들어 지정해주는 것이 편하다.

import { Dispatch, SetStateAction } from 'react'

interface PROPS {
	name : string,
    nameArr : string[],
    setName : Dispatch<SetStateAction<string>>,  
    setArr : Dispatch<SetStateAction<string[]>>,
    //원래 ()=>{} 이 방식으로 보내도 됐었는데 계속 오류가 나서 찾아보니까 저렇게 하는거였다.
    children : React.ReactNode,
}

export default Child({name, nameArr, setName, setArr, chidren}: PROPS){
	
    const change = () => {
    	setName('너이름');
    }
    
	return (<>
    	<p>{name}</p> //이름
        <p>{nameArr.map((val. valindex) => {
        	return <p key={valindex}> {val} </p>
        })}</p> // 배열 출력
        <p> {children} </p>
        <button onClick={change}>이름변경</button>
    </>)
}

이렇게 사용할 수 있다.


 

 

 

 


props [객체를 가진 배열에 id를 추가한 후 id에 맞는 값을 가져와 모달창에 출력하는 방법](+Ts)

처음에는 객체를 가지지 않고 그냥 문자열을 가진 배열에서 인덱스를 뽑아서 보낼려고 했는데, 이유는 모르겠지만 계속 되지 않아서 객체를 가진 배열로 만들어 id와 내용을 넣어서 제목에 onClick 이벤트를 넣고 클릭을 했을때, 해당 id를 받아와서 자식 컴포넌트로 보내주는 방식을 사용했다. 이 과정에서 확실히 알게 된것은 타입 지정을 미리 해줘야한다는 것이다. 

 

배열 안에 있는 객체임으로 객체에 들어가는 변수들에 대한 타입 지정을 interface로 해준 뒤 그 인터페이스이름[] 이렇게 타입을 넣으면 된다. 실제 코드를 쳐보도록 하겠다.

 

App.js

//실제 내가 실습 중인 코드는 다른 내용들이 다 포함되어있어서
//위에서 말한 기능만 보여주는 코드를 짜보도록 하겠다.

import { useState } from 'react';
import Modal from './Modal';

//2. 만든 객체를 보고 interface를 만들어주자
interface Title {
	id: number;
    title: string;
    text: string;
}

export default Parents() {
	//1. 객체를 먼저 만들어주고 그에 맞는 interface를 만들어주자
    //3. 만든 interface를 useState에 적용해준다. Title[]
    const [title, setTitle] = useState<Title[]>([
        {
            id: 1,
            title: '제목1'
            text: '내용1'
        },
        {
            id: 2,
            title: '제목2'
            text: '내용2'
        },
        {
            id: 3,
            title: '제목3'
            text: '내용3'
        },
    ]);
    
    //4. 모달 창은 삼항연산자나 단축평가를 이용해 앞의 값이 true라면 창이 열리도록 할것이다.
    // 그리고 버튼을 눌렀을때 id 값도 같이 들어와야한다.
    // 모달을 열고 닫을 boolean을 담은 useState와 클릭이벤트가 일어났을때 값을 변경할 함수를 만들자
    // 또한 id의 값을 가져갈 useState도 만들어주자
    
    const[modal, setModal] = useState<boolean>(false);//처음엔 닫혀있음으로 false값이다.
    const[id, setId] = useState<number>(0);//값을 안써주면 오류가 난다. 0을 기본값으로 쓰자
    
    //5. 버튼을 눌렀을때 id값을 가져오는 부분을 만들고 id를 파라미터로 받아서 사용해주자
    const modalBtn = (id) => {
    	if(modal === false){
        	setModal(true);
            setId(id);
        } else {
        	setModal(false);
        }
    };
    
    return (<>
    	{/* 5. 화살표함수 방식으로 바꿔서 val.id를 하면 id를 받을 수 있다.*/}
        {title.map((val)=>{
        	return <p key={val.id} onClick={() => modalBtn(val.id)}> {val} </p>
        })}
        
        {/* 6. 가져온 모달창에 props를 보내주는 것과 
        modal state에 맞춰 열고 닫히는 코드를 짜보자 */}
    	
        //삼항연산자
        {modal ? <Modal titleObj={title} id={id}></Modal> : null}
        //단축평가
        {modal && <Modal titleObj={title} id={id}></Modal>}
        
    </>)	
    
}

위에서 업급했듯 저렇게 props를 받기 위해선 자식 컴포넌트에서 type을 다 설정해줘야한다.

 

Modal.js

//1. 부모에서 사용한 interface와 props에 적용할 interface를 만들어주자
interface Title {
	id: number;
    title: string;
    text: string;
}

interface PROPS {
	title: Title[];
    id: number; 
}

export default Modal({title, id}: PROPS){
	
    //2. 값을 받아와서 타입 설정을 해준 뒤에 인덱스 값이므로 id-1로 불러와서 넣어준다. 
    retrun (<>
    	<h4>제목 : {title[id-1].title}</h4>
        <p>날짜 : 10월 16일 발행(까먹고 추가 못함)</p>
        <p>내용 : {title[id-1].text} </p>
    </>)
}

이렇게 만들어주면 각 제목을 누를때만다 제목에 대한 내용이 나오는 모달창을 구현할 수 있다.


 

 

 


Hook

훅은 클래스형 컴포넌트에서만 가능했던 상태관리(state)와 생애주기(라이프 사이클)을 함수형 컴포넌트에서도 할 수 있도록 하는 기능이다.

훅의 중요한점은 컴포넌트 안에서만 호출이 가능하다는 것이다. 대표적인 것들을 설명하자면

- useState() : 상태관리를 해주는 가장 기본적인 훅이다.

- useEffect() : 클래스형에서 사용하는 라이프 사이클을 사용할 수 있도록 해준다.

- useContext() : 리액트에서 사용하고자 하는 데이터를 전역적으로 처리할 수 있다. 컴포넌트가 많은 큰 프로젝트에서 props를 사용하면 매우 번거로운데, 이에 대한 해결책 중 하나이다. 하지만 useContext보다는 리덕스를 더 많이 사용하는 추세이다.

- useReducer() : 복잡한 컴포넌트의 상태로직을 리듀서 함수로 관리해준다.

- useMemo() : 메모이제이션을 통해 함수의 리턴 값을 재사용할 수 있게 해주는 훅

- useCallback() : 함수를 메모이제이션하여 불필요한 렌더링을 줄이게 해주는 훅

- useRef() : 참조(reference)를 생성하고 관리할 수 있는 훅 (DOM 접근, 변수 보존 등)

 

 

Hook [useState]

- 기본

위에서 import로 선언을 해주고,

let/const [실제 값, 값변경함수] = useState(실제값 초기화);

위와 같은 방식으로 사용한다. 그리고 타입 스크립트로 지정해줄려면 

let/const [실제 값, 값변경함수] = useState<string, number 등등>(실제값 초기화);

let/const [실제 값, 값변경함수] = useState<string, number[]/ string, number{}>(실제값 초기화 >> 배열이나 객체);</string,>

 

 

이 state를 사용할 경우 state가 갱신되거나 변경될때마다 다시 랜더링 해주기 때문에 유용하게 사용할 수 있다.

즉 계속해서 변화하거나 추가되는 것에 state를 사용하면 된다.

원래는 클래스형 컴포넌트에서만 사용할 수 있었는데 useState를 통해 함수형 컴포넌트에서도 사용할 수 있게 됐다.

 

주의할 점은 무조건 값을 변경할 때는 실제 값인 state를 건들면 안되며, setState를 통해 변경해야한다.

 

- 배열을 변경할 때,

배열을 변경할 때는 직접 배열이 있는 state 가져올 수 없기 때문에. spread 연산자(...)를 사용한다.

만약에 state가 배열로 설정되어 있고, 해당 state에 새로운 값을 추가하고 싶다면 다음과 같이 작업하면 된다.

 

ex) 

- 배열 추가

const [arr, setArr] = useState<string[]>(['바보', '강아지', '야옹']);

추가

setArr([...arr], '추가할 값'); 

 

- 배열 삭제

setArr(arr.filter(e => e.id !== 삭제하고자 선택한 값의 아이디);

배열의 객체 속에 아이디가 있다고 가정한 후 위와 같이 코드를 적으면 선택된 아이디와 다른 아이디만 반환하기 때문에 

선택된 값이 삭제된 배열을 얻을 수 있다. 

 

- 배열 교체

Array.prototype.push.apply(기존 배열, 새로운 배열);

 

 

Hook [useEffect]

라이프 사이클을 리액트에서 사용할수 있도록 해주는 훅이다. 라이프 사이클은 총 3가지로 나뉘는데 

1. Mount : 처음 생성될때 === 컴포넌트를 처음 불러올때 

2. Update : state나 props가 변경되어 재랜더링 될때

3. Unmount : 컴포넌트가 사라질때

 

이렇게 3가지의 경우를 useEffect를 사용해 각 경우에 어떤 작업을 실행할 지 정해줄 수 있다. 

클래스형에서는 •  componentDidMount • componentDidUpdate • componentWillUnmount

위의 세 가지 메서드를 자주 사용하고 공식 문서에도 권장하고 있다.

 

클래스형에서 사용하는 예시를 간단히 보여주고 useEffect의 사용법을 배워보자

state에 있는 숫자를 더해주고 빼주는 기능을 하는 코드이다. 클래스형에서 state와 함수를 사용하기 위해선 this를 써야하고, props와 state는 생성자에 넣어줘야한다. 그리고 위에서 사용하는 메소드들은 그냥 적어서 바로 사용할 수 있다.

 

Hook [useEffect()]

- useEffect( () => {} ); //컴포넌트가 랜더링 될때마다 실행된다.

- useEffect( () => {}, [] ); // mount가 될때만 즉 최초로 컴포넌트를 불러올때만 실행한다.  

- useEffect( () => {}, [특정값] ); // 특정값이 변경될때만 실행한다.

 

만약에 컴포넌트가 사라지거나 특정값이 사라질때 어떤 작업을 수행하고 싶다면, 중괄호 안에 return () => {}; 를 이용해 해당 함수 안에 넣으면 수행하게 할 수 있다.

 

간단히 요약하자면 useEffect(랜더링 이후 수행할 함수, 어떤 상황에 할 것인지 나타냄(  or [] or [특정값]) )

 

 

Hook [useRef()]

훅 useRef는 html의 요소를 가지고 오기 위해 사용한다. 근데 이를 ts에서 사용하기가 진짜 빡셌다. 특정 요소에서 원하는 값을 가지고 올려고 할때 형식이 맞지 않아서 가지고 올 수 없기 때문에 요소에 적는 ref에도 형식 지정을 해줘야한다..ㅠㅠ

 

기본적인 사용법은 useRef로 myref하나 만들어주고 이를 원하는 요소에 ref={}로 넣어준뒤 myref.current로 가져올 수 있는 다. ts에서도 똑같이 해주면 되는데 형식 지정을 해줘야한다. 

 

프로젝트를 하면서 겪은 것을 올리도록 하겠다.

에러 

'MutableRefObject<HTMLTextAreaElement | undefined>' 형식은 'LegacyRef<HTMLTextAreaElement> | undefined' 형식에 할당할 수 없습니다. 'MutableRefObject<HTMLTextAreaElement | undefined>' 형식은 'RefObject<HTMLTextAreaElement>' 형식에 할당할 수 없습니다. 'current' 속성의 형식이 호환되지 않습니다. 'HTMLTextAreaElement | undefined' 형식은 'HTMLTextAreaElement | null' 형식에 할당할 수 없습니다. 'undefined' 형식은 'HTMLTextAreaElement | null' 형식에 할당할 수 없습니다.ts(2322) index.d.ts(159, 9): 필요한 형식은 여기에서 'DetailedHTMLProps<TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>' 형식에 선언된 'ref' 속성에서 가져옵니다.

 

해결법

이 에러는 TypeScript에서 useRef의 반환 값과 textarea 요소의 ref 속성의 타입이 일치하지 않아 발생합니다. TypeScript에서는 기본적으로 useRef를 사용하면 current 속성이 HTMLTextAreaElement | null 형식을 가집니다. 반면 textarea 요소의 ref 속성은 HTMLTextAreaElement | undefined일 수 있습니다.

이 문제를 해결하려면 useRef와 textarea 요소의 타입이 일치하도록 타입을 지정해야 합니다. HTMLTextAreaElement | null 형식을 HTMLTextAreaElement | undefined 형식으로 변경하거나 그 반대로 변경할 수 있습니다. 아래는 두 가지 방법 중 하나를 선택하여 문제를 해결하는 방법입니다:

1. useRef를 HTMLTextAreaElement | undefined 형식으로 변경:

const storyTextAreaRef = useRef<HTMLTextAreaElement | undefined>(undefined);

2. textarea 요소의 ref를 HTMLTextAreaElement | null로 변경:

<textarea ref={storyTextAreaRef as React.LegacyRef<HTMLTextAreaElement> | undefined} id='story'>
  {title[id-1].story}
</textarea>

 

이렇게 하면 textarea 요소의 ref 속성과 useRef의 반환 값의 타입이 일치하게 됩니다.

어느 방법을 선택하느냐는 개발 환경 및 팀의 코딩 스타일에 따라 다를 수 있습니다. 각 방법은 문제를 해결하는 데 사용될 수 있습니다.

 

위와같이 하면 가져올 수 있다. 근데 정말 저렇게 하는 것이 맍는지 의문이들긴한다.

 


 

 

 

 

 


리액트 라우터 (React Router)

사용자가 요청한 주소에 따라 원하는 페이지를 보여주기 위해 사용한다.

npm i react-router-dom@6

버전 6으로 지정해서 설치해준다. 

 

 BrowserRouter와 createBrowserRouter를 import 해서 사용할 수 있고, 최근에 사용하는 것은 유지보수하기 수월한  createBrowserRouter라고 한다. 

 

그러므로 BrowserRouter는 간단히 사용법만 알아보고 현재 진행중인 프로젝트는 createBrowserRouter를 이용할 것이다.

 

두 작업 모두 먼저 해야하는 것은 Router파일을 만들어줘야한다.

 

- BrowserRouter

먼저 위에서 import를 해준 뒤에 라우터 컴포넌트를 만든 후 그 안에 < BrowserRouter > </ BrowserRouter >를 넣어주고, Routes와 Route를 이용해 각 컴포넌트의 경로를 지정해주면된다.

import { BrowserRouter, Routes, Route } from 'react-router-dom';

import Header from './12Router/Header';
import Home from './12Router/Home';
import About from './12Router/About';
import NotFound from './12Router/404';
import User from './12Router/User';
import Redirect from './12Router/Redirect';
import UserDetail from './12Router/UserDetail';
import Error from './12Router/Error';
import Comment from './12Router/Comment';

export default function Router() {
    return (
        <>
            <BrowserRouter>
                <Header />
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/about" element={<About />} />
                    <Route path="/user" element={<User />} />
                    <Route path="/user/:id" element={<UserDetail />} />
                    <Route path="/redirect" element={<Redirect />} />
                    <Route path="*" element={<NotFound />} />
                </Routes>
            </BrowserRouter>
        </>
    );
}

 path는 지정할 경로이며 elment는 보여줄 컴포넌트를 지정하는 것이다.

 

- createBrowserRouter

 크리에이트브라우저라우터는 이거 자체만 import해서 가져오면 사용가능하다.

import { createBrowserRouter } from 'react-router-dom';

import App from './App';
import NotFound from './12Router/404';
import Header from './12Router/Header';
import Student from './12Router/Student';
import Codingon from './12Router/Condingon';
import Search from './12Router/Sech';

const Router = createBrowserRouter([
	{
    	path: '/',
        element: <Header />,
        // 여기서 children : [{},{}...]를 이용해서 api/children에 지정된 경로
        // 이렇게 사용할 수 있다.
    },
    {
    	path: '/student',
        element: <App />,
        children: [
        	{
                path: 'kdt',
                element: <Student />,
            },
            {
                path: 'codingon',
                element: < Codingon/>,
            },
            {
                path: 'new',
                element: <Search />,
            },
        ],
    },
]);

export default Router

이렇게 지정한 뒤에 api에 따라 자식 컴포넌트로 원하는 컴포넌트를 불러올 수 있는 Outlet을 App.js에 적용하고, 인덱스에는 다음과 같이 적용하면된다.

App.js

import { Outlet } from 'react-router-dom';
import Header from './12Router/Header';
import Router from './Router';

function App() {
    return (
        <>
            {/* 최근 버전 */}
            <Header />{/* 계속 페이지에 고정하고 싶은 컴포넌트가 있다면 헤더처럼 여기서 적어주면된다. */}
            <Outlet />
            
            {/* 옛 버전은 라우터를 불러왔다 */}
            {/* <Router /> */}
            
        </>
    );
}

export default App;

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import Router from './Router';

import App from './App';
import { RouterProvider } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    {/* 옛 버전 */}
    {/* <App />  */}
    
    {/* 최근버전 */}
    <RouterProvider router={Router} />
  </React.StrictMode>
);

// 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

위와 같이 라우터프로바이더를 불러오고 거기에서 라우터를 props로 보내주면된다.

 

Ts에서도 똑같이 해주면된다. 그리고 리액트 라우터를 하면서 유용하게 사용할 수 있는 것들을 설명하며 라우터 부분을 마치도록 하겠다.

이는 주소로 아이디를 보내서 아이디에 맞는 페이지를 보여주고 싶을 때 사용할 수 있다. api/:id로 해서 그 id값을 가지고 올때 useParams로 받아서 가져올 수 있다.

 

주소에 있는 ? 뒤의 값을 바꿀 수 있다.

 

 

import { useNavigate } from 'react-router-dom';

import 로 가져와서 사용할 수 있다. 이후에 위처럼 navigate('이동할 주소'); 이 방식으로 원하는 페이지로 이동할 수 있다.

 

useNavigate()와 useLocation()

네비게이트로 파일을 보내주면 uselocation으로 받아서 쓸 수 있다.

먼저 useNavigate로 원하는 데이터를 보내준다.

const navigate = useNavigate();

const gotoMain = () => {
  navigate("/main", {
    state: {
      userId: user.uid
    }
  });
};
// 두 번째 인자에 데이터를 써서 보내준다

 

그 다음 useLocation을 import 해준다.

import { useLocation } from 'react-router-dom';

그다음에 uselocation을 불러오면서 navigate로 넘어온 데이터를 받을 수 있다.

const location = useLocation();


const [userId, setUserId] = useState(
  location.state?.userId
);

위와 같이 location에 state의 정보를 담을 수 있다.


 

 

 

 

 


이벤트 리스너

 

onClick = {} : 중괄호 안에 함수를 적으면 클릭할 때, 실행된다.

원래는 함수를 적고 바인드를 해줘야하지만 화살표 함수를 쓰면 바로 사용할 수 있다 그래서 중괄호 안에 들어가는 함수는 아래와 같이 작성해주면 된다.

const 함수이름 = () => {
	원하는 기능
}

 

onChange = {}: onChange가 있는 요소의 값이 변할때 작동한다. 보통 값이 변할 때마다 그 값을 보내주는 방식으로 사용을 많이하는데 다음과 같이 사용하면된다.

onChange = {(e) => { e.target.value }
//변한 값을 받을 수 있다.

이를 함수에서 받고 싶다면 다음과 같이 사용하면 된다.

export default function Component () {
	
    const 함수이름 = (e) => {
    	consle.log('변한 값', e.target.value)
    }
    
    return (<>
    	<input onChange={함수이름}> </input>
    </>)
}

이렇게 사용하면 변한 값을 함수에서 받아서 사용할 수 있다.


 

 

 

 


부가기능

에디터를 만들어주는 Quill 사용해보기

먼저 npm으로 깔아준다. 

npm i react-quill
 

React-Quill 사용하기(1)

ReactQuill 사용하기

velog.io

 

생각해보니까 벨로그처럼 만들어야해서 퀼말고 토스트 ui를 가져다 쓰기로 했다.

 

[React] Toast UI Editor 적용기

Next.js 프로젝트에 Toast UI Editor를 적용해보자

velog.io

잠들어 버려서 프로젝트를 완성할 시간이 부족했다..ㅠ 빠르게 할려고 참고한 블로그를 올린다.

 

근데 치명적인 단점이 토스트 ui가 리액트 18버전에서는 적용이 안된다는 것이었다. 그래서 마크다운은 포기하고 Quill을 다시 이용하기로 했다.

 

지금  Ts를 같이 사용중이기 때문에 npm으로 설치할때 아래와 같이 설치해줘야한다

npm install @types/react-quill

 

 


에러관리

리액트 정리를 하면서 블로그 프로젝트를 만들고 있다. 이 과정에서 생기는 에러들을 정리하는 곳이다.

 

 

에러 1.

'App'은(는) 값을 참조하지만, 여기서는 형식으로 사용되고 있습니다. 'typeof App'을(를) 사용하시겠습니까?

이는 ts 오류인데 코드를 적고 있는 파일의 형식을 tsx로 바꾸어주면 해결된다.

 

에러 2.

Error: input is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.

input을 p태그 안에 넣어서 생긴오류

 

+ Recent posts