React +5

LEE EUI JOO·2023년 2월 10일
0

Web Programming

목록 보기
14/17
post-custom-banner

props : 상위 컴포넌트에서 넘겨주는 속성

  • 클래스 컴포넌트에서는 this.props 로 사용 가능
  • 함수형 컴포넌트에서는 함수의 매개변수로 받아야 함

state : 컴포넌트 내에서 변경 가능한 데이터

  • 클래스형 컴포넌트에서는 state 라는 속성을 생성하면 사용 가능
  • 함수형 컴포넌트에서는 state라는 속성을 생성할 수 없음

children : 태그 와 태그 사이의 데이터

  • props 의 속성으로 처리 가능 - props.chidren
class T {
  
  	let t; //인스턴스가 생성될 때 인스턴스에 할당되는 변수 인스턴스가 유지되는 동안 계속 유지 
	// 속성(attribute, property, field)

function f(){
  	let t; //함수가 호출될 때 일시적으로 생성되고 함수의 수행이 종료되면 소멸

수명주기 : 클래스형 컴포넌트가 생성되서 소멸되기 까지의 상태 변화와 관련된 메서드

  • Component 클래스로부터 상속을 받아서 이 메서드를 내장하고 있다

  • 필요에 따라 오버라이딩 해서 사용

  • ❗️매우 중요 ❗️함수는 호출되면 메모리 할당을 받고 자신의 작업이 끝나면 바로 메모리 해제가 발생사기 때문에 상태 변화가 없음

  • 함수형 컴포넌트에 상태 유지를 위한 방법 과 수명 주기 관련된 기능을 제시하고 자 만들어진 것이 Hook

    • useState() : 상태 유지를 위한 함수

    • useEffect() : 수명 주기 관련된 기능을 사용하기 위한 함수

    • useRef() : 함수 내에서 사용할 변수나 컴포넌트의 참조를 저장하기 위한 데이터를 생성

    • useMemo, useCallback, React.memo : 컴포넌트 성능 최적화를 위한 Hook - 함수 호출이나 함수생성 및 리랜더링을 특정한 state의 변화가 생겼을 때 만 수행

    • useReducer : 상태 관리를 컴포넌트에서 분리시키기 위한 Hook


Custom Hook 사용자 정의 훅

  • 개요
    • 개발자가 직접 만든 훅
    • 훅 함수들을 이용해서 함수를 생성
    • 관례상 이름은 use로 시작
    • 3rd party 개발자들이 만든 Hook : 가장 많은 기능은 network
  • 사용자 정의 Hook 만들기 - 타이머
    • 타이머를 생성해주고 정리해주는 사용자 정의 Hook
    • CustomHook.js
import { useEffect } from "react";

//타이머가 수행할 함수와 시간을 받아서 타이머를 생성해주고 정리해주는 hook
//함수형 컴포넌트가 아니지만 useEffect를 사용
export const useInterval = (callback, duraion = 1000) =>{
    useEffect(()=>{
        //duraion 마다 callback 을 호출하는 타이머 작성
        const id = setInterval(callback, duraion);

        //컴포넌트가 화면에서 제거될 때 수행하는 코드
        return () =>{
            clearInterval(id);
        }
    })
}
  • 타이머를 이용해서 state를 수정하는 사용자 정의 Hook
  • UseClock.js
import { useState } from "react";
import { useInterval } from "./CustomHook";
//1초마다 현재 시간을 리턴하는 사용자 정의 훅
export const useClock = () => {
    //현재 시간 및 날짜를 기본값으로 갖는 state를 생성
    const [today, setToday] = useState(new Date());
    //1 초마다 today를 현재 시간으로 수정
    useInterval(()=> setToday(new Date()))

    return today;
}
  • 현재 시간과 날짜를 출력할 컴포넌트를 생성
  • 컴포넌트 디렉토리에 Clock.jsx
const Clock = ({today}) => {
    return(
        <div>
            <div>{today.toLocaleTimeString()}</div>
            <div>{today.toLocaleDateString()}</div>

        </div>

    )

}
export default Clock;
  • 화면에 출력하기 위해 App.js 파일 수정
import './App.css';
import Clock from "./components/Clock";
import { useClock } from "./hook/UseClock";

function App() {

  const today = useClock();
  return (
    <>
      <Clock today = {today}/>
    </>
  );
}
export default App;


불변성

불변성

  • 리액트에서 state의 조작
    • 리액트에서는 state를 직접 조작하는 것은 허용하지 않음
    • state 원본을 유지하면서 복사본을 만들어 복사본을 조작한 후 다시 대입받는 형태로 state를 업데이트 한다
const object = {a:1, b:2};
//object 의 b 속서ㅓㅇ의 값을 3으로 수정
object.b=3; //object 가 리액트의 state라면 허용되지 않음

//불변성을 유지하면서 수정
const nextObject = {
	...object,
  	b:3
};

object = nextObject  // database 에서의 commit
  • 복제해서 작업하는 가장 대표적인 프로그램이 Database
const todos = [
  {}.{} ];

//데이터 추가 - 복제본을 만들어서 객체를 추가하고 리턴
todos.concat(추가할 객체)
//데이터 삭제 - 조건에 맞는 데이터를 삭제
todos.filter(todo => 삭제되지 않을 조건)

//데이터 수정 - 조건에 맞는 데이터만 수정하고 나머지는 그대로
todos.map(todo => 수정할 조건? 수정할 내용 : todo )
  • ...연산자
    • 얕은 복제를 수행
    • 가장 마지막의 데이터를 복제
    • 배열안의 배열이나 객체를 복제할 때는 복제를 하지 않는다
[1,2,3]

{a:1, b:2}

의 경우는 ...을 이용하면 복제가 가능하다

[[],[],..., {}] 의 경우는 ...을 이용하면 복제가 불가하다

이런 경우는 직접 복제를 수행하거나 immer 같은 라이브러리를 이용하면 됨
  • immer를 사용하지 않고 state의 불변성을 유지
  • username 과 name 속성을 갖는 객체의 배열을 이용
    • 데이터를 삽입하고 삭제하는 부분에 대한 처리
    • App.js에서 작업
import './App.css';
import React, {useCallback, useState, useRef} from 'react';

function App() {

  const [form, setForm] = useState({username:'', name:''});
  const [data,setData] = useState({
    array:[],
    uselessValue: null
  })

  const onChange = useCallback((e) => {
    //form 은 2개의 속성을 갖는 일반 객체이므로 ...으로 복제가 가능
    const {name, value} = e.target;
    setForm({
      ...form,
      [name]:[value]
    })
  }, [form]);

  const nextId = useRef(1);
  //submit 은 form 에 추가하는 이벤트
  const onSubmit = useCallback(
    (e)=>{
      e.preventDefault()//기본 이벤트 처리 코드 무시
      const info ={
        id:nextId.current,
        name: form.name,
        username:form.username
      }
      //배열에 데이터 추가
      setData({
        ...data,
        array:data.array.concat(info)
      })
      //폼 초기화
      setForm({
        name:'',
        username:''
      })
      //id 깂 1 증가
      nextId.current = nextId.current + 1;
    }, [data, form.name, form.username]
  );
  
  const onRemove = useCallback((id)=>{
    setData({
      ...data,
      array: data.array.filter((info)=> info.id !== id)
    })
  }, [data])

  return (
    <>
      <form onSubmit={onSubmit}>
        <input name='username' value={form.username} onChange={onChange}
        placeholder='아이디 입력'/>
        <input name='name' value={form.name} onChange={onChange}
        placeholder='이름을 입력'/>
        <button type='submit'>등록</button>
        
      </form>
      <div>
        <ul>
          {data.array.map(info => (<li key={info.id} onClick={()=> onRemove(info.id)}>{info.username}이름은({info.name})</li>))}
        </ul>
      </div>
    </>
  );
}
export default App;

  • 해당 데이터를 클릭하면 삭제


immer

  • 불변성 관리를 위한 라이브러리
    • produce 라는 함수를 이용해서 불변성을 유지하며 데이터를 수정
    • 사용법
produce(상태이름, draft=>{
	  draft.속성이름 = 수정하고자 하는 값이 나 표현식 
});
  • 상태의 구조가 어떻게 되어 있던 깊은 복사를 수행해 수정해서 리턴한다
  • 라이브러리 설치
$ yarn add immer 
		OR
$ npm install immer
  • App.js
import './App.css';
import React, {useCallback, useState, useRef} from 'react';
import produce from 'immer';

function App() {

  const [form, setForm] = useState({username:'', name:''});
  const [data,setData] = useState({
    array:[],
    uselessValue: null
  })

  const onChange = useCallback((e) => {
    //form 은 2개의 속성을 갖는 일반 객체이므로 ...으로 복제가 가능
    const {name, value} = e.target;
    setForm(
      produce(draft => {draft[name]=value})
    )
  }, []);

  const nextId = useRef(1);
  //submit 은 form 에 추가하는 이벤트
  const onSubmit = useCallback(
    (e)=>{
      e.preventDefault()//기본 이벤트 처리 코드 무시
      const info ={
        id:nextId.current,
        name: form.name,
        username:form.username
      }
      //배열에 데이터 추가
      setData(
        produce(draft => {
          draft.array.push(info);
        })
        
      )
      //폼 초기화
      setForm({
        name:'',
        username:''
      })
      //id 깂 1 증가
      nextId.current = nextId.current + 1;
    }, [form.name, form.username]
  );
  
  const onRemove = useCallback((id)=>{
    setData(
      produce(draft => {draft.array.splice(draft.array.findIndex(info => info.id === id) ,1)}
    )
    )}
  , [])

  return (
    <>
      <form onSubmit={onSubmit}>
        <input name='username' value={form.username} onChange={onChange}
        placeholder='아이디 입력'/>
        <input name='name' value={form.name} onChange={onChange}
        placeholder='이름을 입력'/>
        <button type='submit'>등록</button>
        
      </form>
      <div>
        <ul>
          {data.array.map(info => (<li key={info.id} onClick={()=> onRemove(info.id)}>{info.username}이름은({info.name})</li>))}
        </ul>
      </div>
    </>
  );
}
export default App;


ContextAPI (데이터 공유를 위한 API)

리액트에서의 데이터 공유

  • 속성을 이용해서 데이터 전달이 가능한데 하위 컴포넌트의 깊이가 낮을 때는 상관없지만 깊어지면 번거로운 작업을 많이 수행해야 함
  • ContextAPI 를 사용하면 쉽게 공유 데이터를 만들어서 사용하는 것이 가능
  • 생성
const 이름 = createContext(데이터)
export default 이름;
  • 사용
<이름.Consumer>
  	value.데이터
</이름.Consumer>
  • 데이터 수정
<이름.Provider value={이름:}>
</이름.Provider>

Context 사용

  • 공유데이터를 저장할 contexts 디렉토리 생성
  • 디렉토리에 공유데이터를 생성할 파일을 생성 - Color.js
import { createContext } from "react";

const ColorContext = createContext({color:'white'});

export default ColorContext;
  • 공유데이터를 사용할 컴포넌트 생성 - ColorBox.jsx
import React from "react";
import ColorContext from "../contexts/Color";

const ColorBox = () => {
    return(
        <ColorContext.Consumer>
        {
            value => (
                <div style={{
                    width:'64px',
                    height:'64px',
                    backgroung:value.color
                }}/>
            )
        }
        </ColorContext.Consumer>
    )
}
export default ColorBox;
  • App.js 파일 수정
import './App.css';
import React, {useCallback, useState, useRef} from 'react';
import produce from 'immer';
import ColorBox from './components/ColorBox';

function App() {

  const [form, setForm] = useState({username:'', name:''});
  const [data,setData] = useState({
    array:[],
    uselessValue: null
  })

  const onChange = useCallback((e) => {
    //form 은 2개의 속성을 갖는 일반 객체이므로 ...으로 복제가 가능
    const {name, value} = e.target;
    setForm(
      produce(draft => {draft[name]=value})
    )
  }, []);

  const nextId = useRef(1);
  //submit 은 form 에 추가하는 이벤트
  const onSubmit = useCallback(
    (e)=>{
      e.preventDefault()//기본 이벤트 처리 코드 무시
      const info ={
        id:nextId.current,
        name: form.name,
        username:form.username
      }
      //배열에 데이터 추가
      setData(
        produce(draft => {
          draft.array.push(info);
        })
        
      )
      //폼 초기화
      setForm({
        name:'',
        username:''
      })
      //id 깂 1 증가
      nextId.current = nextId.current + 1;
    }, [form.name, form.username]
  );
  
  const onRemove = useCallback((id)=>{
    setData(
      produce(draft => {draft.array.splice(draft.array.findIndex(info => info.id === id) ,1)}
    )
    )}
  , [])

  return (
    <>
    <ColorBox/>
      <form onSubmit={onSubmit}>
        <input name='username' value={form.username} onChange={onChange}
        placeholder='아이디 입력'/>
        <input name='name' value={form.name} onChange={onChange}
        placeholder='이름을 입력'/>
        <button type='submit'>등록</button>
        
      </form>
      <div>
        <ul>
          {data.array.map(info => (<li key={info.id} onClick={()=> onRemove(info.id)}>{info.username}이름은({info.name})</li>))}
        </ul>
      </div>
    </>
  );
}
export default App;

  • Color.js
import { createContext, useState } from "react";

//const ColorContext = createContext({color:'red'});

const ColorContext = createContext({
    state: {color:'black', subcolor:'red'},
    actions:{
        setColor: () => {},
        setSubColor:()=> {}
    }
})

const ColorProvider = ({children}) => {
    const [color,setColor] = useState('black');
    const [subcolor,setSubColor] = useState('red');

    const value = {
        state:{color,subcolor},
        actions:{setColor,setSubColor}
    }

    return(
        <ColorContext.Provider value={value}>{children}</ColorContext.Provider>
    )
}
const {Consumer:ColorConsumer} = ColorContext;
export {ColorProvider, ColorConsumer}
export default ColorContext;
  • App.js 를 수정해서 값을 변경
import './App.css';
import React, {useCallback, useState, useRef} from 'react';
import produce from 'immer';
import ColorBox from './components/ColorBox';
import ColorContext from './contexts/Color';

function App() {
  return (
    <ColorContext.Provider value={{color:'red'}}>
      <ColorBox/>
    </ColorContext.Provider>
  );
}
export default App;

  • SelecColor.jsx
import React, {Component} from "react";
import ColorContext from "../contexts/Color";

const colors = ['red', 'orange', 'black','green', 'blue','indigo'];

class SelectColor extends Component{
    static contextType = ColorContext;

    handleSetColor = color => {
        this.context.actions.setColor(color);
    }

    handleSetSubColor = subcolor => {
        this.context.actions.setSubColor(subcolor);
    }

    render(){
        return(
            <div>
                <h2>색상 선택</h2>
                <div style={{display:'flex'}}>
                    {colors.map(color => (
                        <div key={color}
                        style={{
                            background:color,
                            width:'24px',
                            height:'24px',
                            cursor:'pointer'
                        }}
                        onClick={()=> this.handleSetColor(color)}
                        onContextMenu = {(e) =>{
                            e.preventDefault();
                            this.handleSetSubColor(color)
                        }}
                        />
                    ))}
                </div>
            </div>
        )
    }

}
export default SelectColor
  • App.js 수정
import './App.css';
import ColorBox from './components/ColorBox';
import SelectColor from './components/SelectColor';
import ColorContext, { ColorProvider } from './contexts/Color';

function App() {
  return (
    <ColorProvider>
      <SelectColor/>
      <ColorBox/>
    </ColorProvider>
  );
}
export default App;


React Router

SPA (Single Page Application)

  • SPA 가 아닌 전통적인 방식의 Web Application

    • 웹 브라우저에서 서버로 요청을 하면 서버는 요청에 따른 HTML 페이지를 만들어서 응답

    • HTML 을 만들 떄 정적인 HTML 을 이용하기도 하고 Template Engine 이 만들어 낸 HTML을 이용

    • 클라이언트 애플리케이션의 뷰가 어떻게 보여질 것인지 서버가 결정해서 전송하는데 이 때 클라이언트 웹 브라우저는 HTML 을 전송받아서 해석 후 랜더링을 수행

    • 서버에서 제공되는 정보가 많거나 주기적으로 데이터가 변경되는 경우 랜더링 속도에 문제가 발생하는데 캐싱과 압축을 이용해서 서비스를 제공 하면 문제를 어느정도 해소 되자 사용자 와 인터랙션이 많은 모던 웹 애플리케이션에서는 완전한 해결책이 아니다

    • 랜더링을 서버 측에서 담당하게 되면 랜더링을 위한 서버 자원이 소모되고 불필요한 트래픽도 발생

  • SPA - Single Page Application

    • SPA 는 하나의 화면을 출력하는 애플리케이션인데 클라이언트는 서버에게서 랜더링 하는 코드를 받는 것이 아니고 출력허고 사용할 데이터를 응답으로 받아서 출력하는 구조
    • 리액트와 같은 라이브러리나 프레임워클르 사용해서 뷰 랜더링을 브라우저가 담당하도록 하고 Application 을 부라우저에 로드 한 후 필요한 데이터만 전달받아 출력하는 방식
    • SPA 라고 한 종류의 화면만 있는 것은 아니며, 여러 화면을 가질 수 있고 각 화면에 따라 다른 URL 을 가질 수 있다
    • URL을 별도로 가질 수 있어야 북마크도 사용할 수 있고 검색 엔진을 통해서 유입될 수 도 있다
      • 리액트에는 이 기능이 없어서 다른 라이브러리를 이용해야 함 (react-router)를 이용해서 구현
  • SPA 의 단점

    • 애플리케이션의 규모가 커지면 자바스크립트 파일의 사이즈가 너무 커지게 되고 불필요한 랜더링 관련 스크립트 코드를 불러오게 된다
      • Code Splitting 을 이용해서 해결할 수 있음
    • 검색 엔진에서 검색 결과에 반영이 잘 안됨
    • 자바 스크립트가 실행될 때 까지 빈 화면을 출력
      • 서버 사이드 랜더링을 적절하게 이용해서 첫번째 화면이 만들어질 때 까지 별도의 화면을 출력

기본적인 라우팅 - URL 을 변경하면서 다른 컴포넌트를 출력

  • 라이브러리 설치
$ npm install react-router-dom
		OR
$ yarn add react-router-dom
  • 리액트 라우터는 라이브러리의 변화가 최근에 많이 일어나서 1~2년 전의 코드도 제대로 동작하지 않는 경우가 많다

  • 세 프로젝트 생성 - react-router

  • index.js 수정

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

reportWebVitals();
  • BrowserRouter 는 HTML5 의 History API 를 사용해서 전체 페이지를 새로 고침하지 않고고 URL 을 변경하고 props 로 조회가 가능하도록 해줌

  • 메인 화면으로 사용할 컴포넌트 생성 - Home.jsx

import React from "react";

const Home = () => {
    return(
        <div>
                <h1>메인 page</h1>
                <p>처음 페이지에 오신것을 환영합니다</p>
        </div>
    )
}
export default Home;
  • 소개 화면으로 사용할 컴포넌트 생성 - About.jsx
import React from "react";

const About = () =>{
    return(
        <div>
            <h1>소개 page</h1>
            <p>리액트 라우터에 대한 페이지 입니다! 환영합니다</p>
        </div>

    )
}
export default About;

라우팅 작성

<Routes>
	<Route path='url element = {컴포넌트} />
    ...
</Routes>
  • App.js 에 Routing 코드 작성
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />}/>
      <Route path="/about" element={<About />} />
    </Routes>
  );
}

export default App;

  • a 태그 이용
  • a 태그는 기본적으로 페이지 전체를 새로고침하기 때문에 리액트에서는 사용하지 않음
  • 페이지를 전환하면 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 채 HTML5의 History API를 이용해서 URL만 변경

  • link 도 내부적으로 a 태그를 사용하지만 페이지 전환을 방지하는 기능을 내장

  • 사용

<Link to ='url'>내용</Link>
  • Home.jsx 수정 하여 About을 호출하는 Link를 추가
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
    return(
        <div>
                <h1>메인 page</h1>
                <p>처음 페이지에 오신것을 환영합니다</p>
                <Link to ='/about'>소개</Link>
        </div>
    )
}
export default Home;

  • 링크(소개)를 클릭하게 되면

  • about 페이지로 넘어가게 됨

하나의 컴포넌트에 여러개의 URL 설정 가능

  • App.js
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />}/>
      <Route path="/about" element={<About />} />
      <Route path="/info" element={<About />} />
    </Routes>
  );
}

export default App;


URL Parameter & Query String

import { useParams } from "react-router-dom";

const data ={
    lee:{
        name: 'LEE',
        description:'나는 LEE 사람입니다'
    },
    eui:{
        name: 'EUI',
        description:'나는 EUI 사람입니다'
    },
    joo:{
        name: 'JOO',
        description:'나는 JOO 사람입니다'
    }
}

const Profile = () =>{
    //URL 파라미터 읽기
    const params = useParams();
    // username 이라는 URL 파라미터를 이용해서 data 선택
    const profile = data[params.username];

    return(
        <div>
            <h1>사용자의 프로필</h1>
            {profile ? (
                <div>
                    <h2>{profile.name}</h2>
                    <p>{profile.description}</p>
                </div>
            ) : (<p>존재하지 않는 프로필</p>)}
        </div>
    )
}
export default Profile;
  • App.js
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Profile from './components/About';


function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />}/>
      <Route path="/about" element={<About />} />
      <Route path="/info" element={<About />} />
      <Route path="/profiles/:username" element={<Profile/>} />

    </Routes>
  );
}

export default App;
  • Home.jsx 파일에 profiles 링크를 추가
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
    return(
        <div>
                <h1>메인 page</h1>
                <p>처음 페이지에 오신것을 환영합니다</p>
                <Link to ='/about'>소개</Link>
                <ul>
                    <li><Link to ='/profiles/lee'>lee</Link></li>
                    <li><Link to ='/profiles/eui'>eui</Link></li>
                    <li><Link to ='/profiles/joo'>joo</Link></li>
                </ul>
        </div>
    )
}
export default Home;


Query String

  • 읽는 방법
    • useLocation 이라는 Hook 을 이용해서 location 객체를 리턴받아서 읽기
      • pathname : query string 을 제외한 url
      • search : ?를 포함한 query string
      • hash : #문자열 뒤의 값 - 세로로 긴 페이지에서 특정한 곳으로 이동하기 위해서 사용
      • state : 페이지로 이동할 때 임의로 설정할 수 있는 값
  • react-router-dom 6 에서부터는 useSearchParams 라는 Hook을 제공
  • qs 라는 라이브러리 이용
    • query string 객체로 변환할 수 있는 라이브러리
  • About.jsx 파일 수정
import React from "react";
//uselocation 이용
import { useLocation } from "react-router-dom";

const About = () =>{
    const location = useLocation();
    return(
        <div>
            <h1>소개 page</h1>
            <p>리액트 라우터에 대한 페이지 입니다! 환영합니다</p>
            <p>query string : {location.search}</p>
        </div>

    )
}
export default About;
$ npm install qs
		OR
$ yarn add qs        
  • About.jsx를 수정
import React from "react";
//uselocation 이용 //useSearchParams 임포트
import { useLocation, useSearchParams } from "react-router-dom";
import qs from 'qs';


const About = () =>{
    //파라미터를 하나씩 읽기
    const location = useLocation();
    const [searchParams, setSearchParams] = useSearchParams();
    const name = searchParams.get('name');
    const job = searchParams.get('job');
    //전체파라미터를 하나의 객체로 변환
    const queryString = qs.parse
    (location.search, {ignoreQueryPrefix:true});
    console.log(queryString);
    
    return(
        <div>
            <h1>소개 page</h1>
            <p>리액트 라우터에 대한 페이지 입니다! 환영합니다</p>
            <p>query string : {location.search}</p>
            <p>{name}:{job}</p>
        </div>

    )
}
export default About;
  • 이전과 동일하게 브라우저에 입력 - 쪼개짐


SubRouting

  • Routing 안에서 다시 Routing
    • 메인 URL
    • 메인 URL/서브URL
  • 상세보기 형태를 구현할 때 많이 사용
  • URL Parameter 를 사용하는 것과 유사
<Route path='/articles' element={<Articles />}/>
<Route path='/articles/:id' element={<Articles />}/>
//메인으로 들어가야함
  • 상위 라우터에서 보여질 컴포넌트를 생성 - Articles.jsx
import React from 'react';
import { Link } from 'react-router-dom';

const Articles =() =>{
    return(
        <ul>
            <li>
                <Link to ='/articles/1'>
                손으로 코딩하고 뇌로 컴파일 하고 눈으로 디버깅
                코딩을 하기전에 생각을 먼저하는 습관을 가지자.
                </Link>
            </li>
            <li>
                <Link to ='/articles/2'>
                생각후에는 행동으로 실행하자
                </Link>
            </li>
            <li>
                <Link to='/articles/3'>
                        행동으로 실행 하며 정리하자
                </Link>
            </li>
        </ul>

    )
}
export default Articles;
  • 하위 라우터에서 보여질 컴포넌트 생성 - Article.jsx
import { useParams } from "react-router-dom";

const Article = () => {
    const {id} = useParams();

    return(
        <div>
            <h2>게시글 {id}</h2>
        </div>
    )
}
export default Article;
  • Home.jsx 파일 수정
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
    return(
        <div>
                <h1>메인 page</h1>
                <p>처음 페이지에 오신것을 환영합니다</p>
                <Link to ='/about'>소개</Link>
                <ul>
                    <li><Link to ='/profiles/lee'>lee</Link></li>
                    <li><Link to ='/profiles/eui'>eui</Link></li>
                    <li><Link to ='/profiles/joo'>joo</Link></li>
                </ul>
                <Link to ='/articles'>게시물 전체</Link>
        </div>
    )
}
export default Home;
  • App.js 파일 수정
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Profile from './components/Profile';
import Articles from './components/Articles';
import Article from './components/Article';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />}/>
      <Route path="/about" element={<About />} />
      <Route path="/info" element={<About />} />
      <Route path="/profiles/:username" element={<Profile/>} />
      <Route path="/articles" element={<Articles/>} />
      <Route path="/articles/:id" element={<Article/>} />

    </Routes>
  );
}

export default App;

  • 게시물 전체 클릭

  • 게시물 클릭


공통된 레이아웃

  • 공통 레이아웃 컴포넌트
    • 여러 곳에서 동일한 컴포넌트를 출력해야 하는 경우에 Sub Routing 과 Outlet을 이용해서 구현이 가능
    • 레이아웃으로 사용할 컴포넌트를 만들고 각자 다른 모양으로 보여질 부분을 Outlet 으로 설정
    • 공통된 레이아웃을 사용할 곳의 라우팅을 수정
<Route element = {<공통 레이아웃 컴포넌트 />}>
  <Route path='url' element={<컴포넌트/>} />
</Route>
  • url 에 해당하는 컴포넌트를 출력할 때 는 공통 레이아웃 컴포넌트를 출력하고 그 안의 Outlet 부분에 컴포넌트가 출력된다.

  • 공통 레이아웃 적용

    • 공통된 레이아웃으로 사용할 컴포넌트를 추가 - Layout.jsx
import { Outlet } from "react-router-dom";

const Layout = () => {
    return(
        <div>
            <header style={{background:'lightgray', padding:16, fontSize:24}}>
                머리말
            </header>
            <main>
                <Outlet />
            </main>
        </div>

    );
}
export default Layout;
  • App.js 수정
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Profile from './components/Profile';
import Articles from './components/Articles';
import Article from './components/Article';
import Layout from './components/Layout';

function App() {
  return (
    <Routes>
      <Route element={<Layout/>}>
      <Route path="/" element={<Home />}/>
      <Route path="/about" element={<About />} />
      <Route path="/info" element={<About />} />
      <Route path="/profiles/:username" element={<Profile/>} />
      </Route>

      <Route path="/articles" element={<Articles/>} />
      <Route path="/articles/:id" element={<Article/>} />
      </Routes>
    
  );
}

export default App;


부가 기능

  • index props
    • Route 컴포넌트에는 index 라는 프로퍼티가 존재하는데 이 프로퍼티는 path='/' 와 동일한 효과
      • 메인 페이지로 라우팅 설정할 때는 path='/' 대신에 index를 설정해도 된다
  • App.js
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Profile from './components/Profile';
import Articles from './components/Articles';
import Article from './components/Article';
import Layout from './components/Layout';

function App() {
  return (
    <Routes>
      <Route element={<Layout/>}>
      <Route index element={<Home />}/>
      <Route path="/about" element={<About />} />
      <Route path="/info" element={<About />} />
      <Route path="/profiles/:username" element={<Profile/>} />
      </Route>

      <Route path="/articles" element={<Articles/>} />
      <Route path="/articles/:id" element={<Article/>} />
      </Routes>
    
  );
}

export default App;
  • useNavigate Hook
    • 자바스크립트의 navigator 와 유사한 역할을 수행할 수 있는 navigate 함수를 리턴하는 훅
    • 리턴되는 함수를 이용하면 인덱스 나 url 을 이용해서 이동이 가능하다
      • 인덱스는 정수를 설정하는데 음수를 설정하면 뒤로의 기능을 하고 양수를 설정하면 앞으로 기능을 수행
    • url 이 변경된 경우만 사용
    • Layout.jsx 수정
import { useNavigate } from "react-router-dom";
import { Outlet } from "react-router-dom";

const Layout = () => {
    //Link 컴포넌트를 이용하지 않고 이동이 가능한 훅
    const navigate = useNavigate();

    const goArticles = () => {
        navigate('/articles');
    }

    const goBack = () => {
        navigate(-1);
    }

    return(
        <div>
            <header style={{background:'lightgray', padding:16, fontSize:24}}>
                머리말
                <button onClick={goBack}>뒤로</button>
                <button onClick={goArticles}>게시글 전체</button>
            </header>
            <main>
                <Outlet />
            </main>
        </div>

    );
}
export default Layout;

  • NavLink
    • Link 와 유사한데 isActive 라는 속성을 가지고 있어서 현재 경로와 Link의 경로가 일치하면 다른 스타일을 적용할 수 있는 컴포넌트
    • 목록 태그 와 상세보기를 하나의 컴포넌트로 구현해서 출력할 때 현재 출력되고 있는 상세보기가 어떤 목록임을 알려주기 위해서 사용
    • Articles.jsx 를 수정
import React from 'react';
import { NavLink, Outlet } from 'react-router-dom';

const Articles =() =>{
    //현재 출력 중인 데이터와 동일한 링크에 적용할 스타일
    const activeStyle = {
        color:'green',
        fontSize:24
    }

    return(
        <div>
            <Outlet/>
        <ul>
            <li>
                <NavLink to ='/articles/1'
                style={({isActive}) => (isActive ? activeStyle : undefined)}>
                손으로 코딩하고 뇌로 컴파일 하고 눈으로 디버깅
                코딩을 하기전에 생각을 먼저하는 습관을 가지자.
                </NavLink>
            </li>
            <li>
                <NavLink to ='/articles/2'
                style={({isActive}) => (isActive ? activeStyle : undefined)}>
                생각후에는 행동으로 실행하자
                </NavLink>
            </li>
            <li>
                <NavLink to='/articles/3'
                style={({isActive}) => (isActive ? activeStyle : undefined)}>
                        행동으로 실행 하며 정리하자
                </NavLink>
            </li>
        </ul>
        </div>

    )
}
export default Articles;
  • Navigate
    • 리다이렉트 할 때 이용
      • 보여지자 마자 다른 곳으로 이동
      • 특정 페이지를 출력해야 하는데 조건에 밎는 경우만 출력해야 할 때 이용
      • Authentication(인증, 로그인이 되어 있는 경우) 과 Authorization(인가, 권한의 문제)에서 이용
      • 최근에는 로컬 스토리지 와 토큰을 많이 이용한다
    • 로그인 안에 로그인 컴포넌트를 생성 - Login.jsx
  • App.jsx 수정
profile
무럭무럭 자라볼까
post-custom-banner

0개의 댓글