본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[95-97일차 복습] 리액트 (+NEXT) 웹 홈페이지 만들기 기초 (레이아웃 / 로그인 / 회원가입)

반응형

React 로 웹 홈페이지 만들기 기초 

 

1. Visual Studio 환경 셋팅 

1) 새 폴더 생성 - back, front 폴더 생성 > 터미널 front 접근 > 

npm init
npm i next react react-dom

2) package.json 수정

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev":"next dev -p 3001 -H 0.0.0.0",
    "build":"next build",
    "start":"next start",
    "lint":"next lint"
  },

3) pages 폴더 생성 >index.jsx 생성 및 코드 작성

const Index =()=>{
    return(
        <>
            hello Next.js
        </>
    )
}

export default Index

4) 실행 

npm run dev 

=> 위에서 설정한 port 3001로 들어가서 'hello Next.js' 가 잘 뜨는지 확인 

 

 

 

 

 

* 노트북과 핸드폰 wifi 공유할 때 꿀팁 

노트북 window key -> cmd -> ipconfig 입력 -> 핸드폰 브라우저에 ipv4 주소+3001 입력하면 노트북에서 실행시키는 브라우저 내용이 뜬다! 

 

 

 

 

 

 

2. 라우터 / 동적 라우터 / pages 파일 트리 생성 

1) pages > join.jsx, login.jsx, join.jsx + posts 폴더 > post.jsx 파일 생성 + 기본 코드 작성 + 각각 브라우저에 잘 나오는지 확인 

* React 는 라우터가 위 파일트리처럼 아주 간단하게 가능하다 ! 폴더를 쓰면 한 번 더 들어가야함. 

localhost:3001
localhost:3001/join
localhost:3001/login
localhost:3001/posts/post 

 

2) post.jsx를 -----> 동적 라우팅되도록 만들기

* 파일명을 배열 형식으로 만들어야 함.  

* 동적 라우

- post.jsx 파일명을 ---> [post].jsx로 변경 

- [post].jsx 의 내용을 수정하기

import {useRouter} from 'next/router'

const Post =()=>{
    
    const router = useRouter()
    const {post} = router.query

    return(
        <>
            hello Post 동적라우팅  {post}
        </>
    )
}

export default Post
localhost:3001/posts/ 블라블라블라ㅏㅏㅏㅏ <- 요기에 쓰는것이 나온다 

 

3. 레이아웃 구성하기 

Header & Footer Components를 고정시키고 안의 내용만 바뀌도록 만들 예쩡, 

요즘에는 로그인, 회원가입 or 다른 페이지 경우 화면자체를 아예 바꿔버리는 게 트렌디 하다고 함 ! 

* BlogLayout이라는 레이아웃으로 전체 index, login, join, post 감싸기

children 사용하기  

 

1) front > components 폴더 생성 > blogLayout.jsx 생성 및 코드 작성

const BlogLayout = ({children}) =>{
    return(
        <>
            Header html
            <div className="header">
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li>Home</li>
                    <li>글쓰기</li>
                    <li>로그인</li>
                    <li>회원가입</li>
                </ul>
            </div>
            <div className="container">
                {children}    //<--------------------요 안에 index, join, login, post 
            </div>
            Footer html
            <div className="footer">
                copyright &copy; all reserved 
            </div>
        </>
    )
}

export default BlogLayout

 

 

질문: pages 라는 라우터 폴더 명은 정해진건지 ? 웅 

 

 

 

2) index, join, login, post.jsx 파일에 blogLayout.jsx 가져오기 + 감싸기 (감싸는 건 chidren 덕분에 감싸기 가능) 

아래 코드처럼 모두 바꾸기 ! 경로는 post의 경우 ../../ 두번 나가야함 

import { useRouter } from 'next/router'
import BlogLayout from '../../components/blogLayout'


const Post = () => {

    const router = useRouter()
    const { post } = router.query

    return (
        <>
            <BlogLayout>
                hello Post 동적라우팅  {post}
            </BlogLayout>
        </>
    )
}

export default Post

header 와 footer가 그대로 있슴 ! 

 

 

 

3) 트렌드에 따라 login, join page에서 header 없애기 ! 

components > FormLayout.jsx 파일 생성 

const FormLayout = ({children})=>{
    return(
        <>
            {children}
            Footer html
            <div className="footer">
                copyright &copy; all reserved 
            </div>
        </>
    )
}

export default FormLayout

blogLayout.jsx - 기존 코드에서 FormLayout코드로 대체 

import FormLayout from "./FormLayout"

const BlogLayout = ({children}) =>{
    return(
        <>
            Header html
            <div className="header">
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li>Home</li>
                    <li>글쓰기</li>
                    <li>로그인</li>
                    <li>회원가입</li>
                </ul>
            </div>
            <div className="container">
                {children}
            </div>
            <FormLayout/>
        </>
    )
}

export default BlogLayout

login.jsx, join.jsx 수정  -> BlogLayout ---> FormLayout 

import FormLayout from '../components/FormLayout'

const Join = () => {
    return (
        <>
            <FormLayout>
                hello join.jsx
            </FormLayout>
        </>
    )
}

export default Join

 

 

 

 

 4. 링크의 이동 

React -> Single page application (SPA) -> url 변경이 안되어서 Next의 Link 를 활용하여 사용 

Next Link 는 React Link to 와 다르게 href를 사용 ! 그리고 a tag 값이 비어있으면 안된다. 

BlogLayout.jsx

import FormLayout from "./FormLayout"
import Link from 'next/link'

const BlogLayout = ({children}) =>{
    return(
        <>
            Header html
            <div className="header">
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>로그인</a></Link></li>
                    <li><Link href="/join"><a>회원가입</a></Link></li>
                </ul>
            </div>
            <div className="container">
                {children}
            </div>
            <FormLayout/>
        </>
    )
}

export default BlogLayout

=> 이제 home/글쓰기/login/join을 누르면 해당 페이지로 url 이동이 된다. 

 

 

 

 5. 뒤로가는 버튼 만들기 

next/router 를 사용

header & footer가 있는 home, posts/post는 페이지 이동 없기 때문에 뒤로가는 버튼 일단 안만들고

login & join 만 ㄱㄱ ! -> Login & Join 을 감싸고 있는 FormLayout.jsx에만 적용해보기 

 

FormLayout.jsx 

import Router from 'next/router'

const FormLayout = ({children})=>{
    return(
        <>
            <button onClick={()=>Router.back()}>뒤로가기</button>
            {children}
            Footer html

 

 

 

 

 6. head 가 안보인다. ---> head 관련 수정 하기 

파일 구성에 index.jsx가 없다. -> 리액트가 다 관리해줌

head를 수정하고 싶으면 이걸 바꿀 수 있는 (리액트가 제공해주는) Component에서 가능 ! 

index.jsx 

import BlogLayout from '../components/blogLayout'
import Head from 'next/head'

const Index = () => {
    return (
        <>
        <Head>
            <title>My website</title>
        </Head>
            <BlogLayout>

My sebsite가 뜬다 

각각의 page 들도 import & 코드 작성 해보기 

 

 

 

 6. _App.jsx 생성 - url 공유 시  그림도 뜨도록  만들기 (html기능) 

head tag -> meta tag 작성

모든 page에 설정하기 귀찮으니 모든 compo가 실행 전 거쳐가는 곳 => app.js에 <head>를 생성하기 

 

pages > _app.jsx 생성 (정해져 있는 이름 by 리액트) 

 

1) pages>_app.jsx 생성 & 코드 작성

const App = ({Component})=>{
    return(
        <>
            hello?
            <Component/>
            Hello back to u 
        </>
    )
}

export default App

* _app.jsx에서 props로 가져온 Component 안에 모 - 든 pages내 Components 들이 들어가 있다. 

따로 import 해오지않고 _App.js파일만 적어도 가능 

_App.jsx 에 적은 hello 와 hello back to you의 위치를 보면 모든 Compo의 밖에 있다 ! 

 

 

 

2) 그림 뜨도록 하는 방법 

https://velog.io/@byeol4001/Meta-Tag-OG%EC%98%A4%ED%94%88%EA%B7%B8%EB%9E%98%ED%94%84-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

Meta Tag , OG(오픈그래프) 사용하기

오픈그래프는 어떤 HTML 문서의 메타정보를 쉽게 표시하기 위해서 메타정보에 해당하는 제목, 설명, 문서의 타입, 대표 URL 등 다양한 요소들에 대해서 사람들이 통일해서 쓸 수 있도록 정의해놓

velog.io

 

 

 

 

 7. CSS 만들기 -> _App.jsx에 넣기 

front > index.css 생성 & 코드 작성 

*{
    padding:0;
    margin:0;
}

ul,li{
    list-style:none;
}

a{
    color:darkcyan;
    text-decoration: none;
}

_app.jsx에 index.css 가져오기 

import '../index.css'

 

 

 

 

 

 8. 글자에 폰트 넣기 

1) google font에서 원하는 글자 + url 복사 

2) _app.jsx Head import

3) <Head>에 해당 url 붙여넣기  ->  url 닫기    />  요고 ! 

4) url 중, crossorigin -> crossorigin="true" 로 수정 

5) index.css google fonts 의 CSS rules to specify families 복사 -> index.css 에 추가 

body{
    font-family: 'Cute Font', cursive;
}

바뀐 폰트 

 

Google Fonts 

https://fonts.google.com/

 

Google Fonts

Making the web more beautiful, fast, and open through great typography

fonts.google.com

 

 

 

 

 

 

 9. 이미지 가져오기 

1) front > public 폴더 생성    (  요 파일명도 react에서 정한것) 

2) 아무 이미지 넣기 -> index.jsx에 사용해보기 

            <BlogLayout>
                hello Next.js
                <div>
                    <img className="test_img" src="/github_profile2.jpg"/>
                </div>

 

 

 

 

 

 

 

 10. 각각 Component에 사용할 CSS 따로 빼서 적용해보기 

위에서 만든 index.css 는 공용! 

 

1) 요걸 하기 전 components > FooterLayout.jsx생성 따로 compo 뺴자 

const FooterLayout = () => {
    return (
        <>
            Footer html
            <div className={form_style.footer}>
                copyright &copy; all reserved
            </div>
        </>
    )
}

export default FooterLayout

2) BlogLayout.jsx, FormLayout.jsx에 FooterLayout 가져오기 

 

 

3) FooterLayout에만 사용하고 싶은 css 만들어 보기 

components> footer_style.module.css 생성 

.footer{
    background-color: yellow;
    color:white;
}

FooterLayout.jsx

import footer_style from './footer_style.module.css'

const FooterLayout = () => {
    return (
        <>
            Footer html
            <div className={footer_style.footer}>
                copyright &copy; all reserved
            </div>
        </>
    )
}

export default FooterLayout

객체로 오는 footer_style---> . 쩜 찍고 안의 내용 사용 가능 

import footer_style from './footer_style.module.css'   -> 요 안의 모든 css를 객체로 반환 ! 

 

 

 

 

 

 

 11. styled-component 사용하여 버튼 만들기 

1) 라이브러리 설치 

npm i styled-components 

2) components > NavToggle.jsx 파일 생성 

const NavToggle=()=>{
    return(
        <div>
            <input type ="checkbox" id="nav-toggle" className="nav-toggle"/>
            <label htmlFor="nav-toggle">
                <span>1</span>
                <span>2</span>
                <span>3</span>
            </label>
        </div>
    )
}

export default NavToggle

3) BlogLayout.jsx 에 NavToggle 가져오기 

import FooterLayout from "./FooterLayout"
import Link from 'next/link'
import NavToggle from './NavToggle'

const BlogLayout = ({children}) =>{
    return(
        <>
            Header html
            <div className="header">
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>로그인</a></Link></li>
                    <li><Link href="/join"><a>회원가입</a></Link></li>
                </ul>
                <NavToggle/>

 

4) styled component 불러오기 & 사용

* 자동 완성 기능 extension 다운 ex) vscode-styled-component

import Styled from 'styled-components'

const NavToggle = () => {
    return (
        <Toggle>
            <input type="checkbox" id="nav-toggle" className="nav-toggle" />
            <label htmlFor="nav-toggle">
                <span></span>
                <span></span>
                <span></span>
            </label>
        </Toggle>
    )
}

export default NavToggle


const Toggle = Styled.div`
    background:transparent;
    border-color:transparent;

    & > .nav-toggle{
        display:none;
    }

    & > .nav-toggle + label{
        display:block;
        width:2.5rem;
        height:2rem;
        position:relative;
        cursor:pointer;
    }

    & > .nav-toggle + label > span{
        display:block;
        position:absolute;
        width:100%;
        height:5px;
        border-radius:30px;
        background:black;
        transition:all .35s ;
    }

    & > .nav-toggle + label > span:nth-child(1){top:0}
    & > .nav-toggle + label > span:nth-child(2){top:50%; transform:translateY(-50%)}
    & > .nav-toggle + label > span:nth-child(3){bottom:0}
    
    & > .nav-toggle:checked + label > span:nth-child(1){
        top:50%;
        transform:translateY(-50%) rotate(45deg);
    }
    & > .nav-toggle:checked + label > span:nth-child(2){
        opacity:0;
    }

    & > .nav-toggle:checked + label > span:nth-child(3){
        bottom:50%;
        transform:translateY(50%) rotate(-45deg);
    }

햄버거 누르면 X 가 된다 

 

여기까지 내용 : https://blckchainetc.tistory.com/290

 

[95일차]20210726 react Link, head, title, css, 라우터, 폰트, 이미지 넣기, 핸드폰과 연동하기

프론트에 next 를 설치하기 1. blog > back, front 폴더 생성 2. 터미널 front까지 접근 3. npm init / 설치 npm init npm install next react react-dom 3. package.json 수정 { "name": "front", "version": "1..

blckchainetc.tistory.com

 

 

 

 12. Header 따로 빼서 CSS  (from Bloglayout) 

1) components > layout 폴더 생성 > Header.jsx 파일 생성 + 코드(from blogLayout) 작성

Header.jsx

import Link from 'next/link'
import NavToggle from '../NavToggle'

const Header = () => {
    return (
        <>
            <div className="header">
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>로그인</a></Link></li>
                    <li><Link href="/join"><a>회원가입</a></Link></li>
                </ul>
                <NavToggle />
            </div>
        </>
    )
}

export default Header

BlogLayout.jsx

import FooterLayout from "./FooterLayout"
// import Link from 'next/link'
// import NavToggle from './NavToggle'
import Header from './layout/Header'


const BlogLayout = ({children}) =>{
    return(
        <>
            <Header/>
            <div className="container">
                {children}
            </div>
            <FooterLayout/>
        </>
    )
}

export default BlogLayout

Header.jsx

Styeld CSS 추가 (div 대체 ) 

import Styled from 'styled-components' //추가 

const HeaderContainer = Styled.div`
    display:flex;
    flex-direction:row;
    align-items:center;
    justify-content:space-between;
    padding:0.5vw;
    box-sizing:border-box;
    border-bottom:1px solid #ddd;
    width:100vw;
`

const Header = () => {
    return (
        <>
            <HeaderContainer>
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <ul>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>로그인</a></Link></li>
                    <li><Link href="/join"><a>회원가입</a></Link></li>
                </ul>
                <NavToggle />
            </HeaderContainer>
        </>
    )
}

ul 대체 CSS 추가 

import Link from 'next/link'
import NavToggle from '../NavToggle'
import Styled from 'styled-components'

const HeaderContainer = Styled.div`
    display:flex;
    flex-direction:row;
    align-items:center;
    justify-content:space-between;
    padding:0.5vw;
    box-sizing:border-box;
    border-bottom:1px solid #ddd;
    width:100vw;
`

const Gnb =Styled.ul`
    // 모바일 
    display:flex;
    flex-direction:row;

    // pc
    @media only screen and (max-width:768px){
        display:none;
    }

    & > li{
        margin-left:20px;
        font-size:20px;
    }
`

const Header = () => {
    return (
        <>
            <HeaderContainer>
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <Gnb>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>로그인</a></Link></li>
                    <li><Link href="/join"><a>회원가입</a></Link></li>
                </Gnb>
                <NavToggle />
            </HeaderContainer>
        </>
    )
}

export default Header

 

 

 

 13. 새로고침 시 CSS 풀리지 않게 만들기  

NEXT 에서 styled component plugin설정을 안해놓음 

styled component는 react가 아닌 다른 사람이 만든거라 -> 우리가 직접 설정해야함 

 

1) pages > _document.jsx 생성 및 아래 코드 작성 

import Document from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      // sheet을 사용해 정의된 모든 스타일을 수집
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        });

      // Documents의 initial props
      const initialProps = await Document.getInitialProps(ctx);

      // props와 styles를 반환
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}

2) front > .babelrc 파일 생성 및 코드 작성

{
    "presets":["next/babel"],
    "plugins":[
        ["styled-components", {"ssr":true}]
    ]
}

3) package down

npm i -D babel-plugin-styled-components

=> 새로고침해도 풀리지 않음 

 

 

 14. 대분류 메뉴 꾸미기 css  

1) 메뉴 만들기

import Styled from 'styled-components'

const NavToggle = () => {
    return (
        <Toggle>
            <input type="checkbox" id="nav-toggle" className="nav-toggle" />
            <label htmlFor="nav-toggle">
                <span></span>
                <span></span>
                <span></span>
            </label>
            <Accordion>
                <ul>
                    <li>대분류 메뉴1</li>
                    <li>대분류 메뉴2</li>
                    <li>대분류 메뉴3</li>
                    <li>대분류 메뉴4</li>
                    <li>대분류 메뉴5</li>
                </ul>
            </Accordion>
        </Toggle>
    )
}

export default NavToggle

const Accordion = Styled.div`
    position:absolute;
    width:100%;
    left:0px;
    top:10vh;
    z-index:5;
    background:#fff;
    padding:7vh 0;

    & > ul{
        margin-top:5vh;
        display:flex;
        flex-direction:column;
    }

    & > ul > li{
        margin-top:20px;
        text-align:center;
    }
`
. 
.
.

Styled Component의 장점 : props 값을 던질 수 있고 css 에서 받을 수 있다. -> display none or block 할 지 정할 수 있

 

 

 

 

 15. NavToggle 햄버거에 onClick event 

1) NavToggle.jsx 에 useState를 사용해서 visible = false 상태값 만들기 

2) 해당 상태값을 Accordion css compo에게 props로 전달하기 

import Styled from 'styled-components'
import {useState} from 'react'

const NavToggle = () => {

    const [visible,setVisible] = useState(false)
    const handleToggle=()=>{
        setVisible(!visible)
    }

    return (
        <Toggle>
            <input 
                type="checkbox" 
                id="nav-toggle" 
                onClick={handleToggle} 
                className="nav-toggle" 
            />
            <label htmlFor="nav-toggle">
                <span></span>
                <span></span>
                <span></span>
            </label>
            <AccordionMenu flag={visible}>
                <ul>

3) 전달 받은 flag로 css 'block' or 'none' 결정하기

${} -> js 영역 

const AccordionMenu = Styled.div`
    position:absolute;
    width:100%;
    left:0px;
    top:10vh;
    z-index:5;
    background:#fff;
    padding:7vh 0;

    // props 가 가리키는 것 : Accordion이 가진 모든 props를 말함 
    display:${(props)=>(props.flag) ? 'block':'none'};

 

 

 

 

 16. Header없는 formLayout.jsx CSS 

import Router from 'next/router'
import FooterLayout from './FooterLayout'
import Styled from 'styled-components'

const FormLayout = ({ children }) => {
    return (
        <>
            <Background>
                <div>
                    <button onClick={() => Router.back()}>뒤로가기</button>
                    {children}
                    <FooterLayout />
                </div>
            </Background>
        </>
    )
}

export default FormLayout

const Background = Styled.div`
    width:100vw;
    height:100vh;
    background:#eee;
    display:flex;
    align-items:center;
    justify-content:center;

    &>div{
        width:300px;
        height:400px;
        background:#fff;
        padding:20px;
    }
`

 

 

 

 17. login.jsx 로그인 형식 + 기능 추가 ( form, input, button ) 

1) 형식 만들기 

            <FormLayout>
                <h2>로그인 page</h2>
                <form>
                    <input type="text" placeholder="아이디를 입력해주세요"/>
                    <input type="password" placeholder="패스워드를 입력해주세요"/>
                    <button type="submit">로그인 </button>
                </form>
            </FormLayout>

2) 기존의 방법으로 로그인 기능 추가해보기 

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import {useState} from 'react'

const Login = () => {

    const [userid, setUserid] = useState('')
    const [userpw, setUserpw] = useState('')

    const ChangeUserid = e=>{
        setUserid(e.target.value)
    }    

    const ChangeUserpw=e=>{
        setUserpw(e.target.value)
    }

    const handleSubmit=e=>{
        e.preventDefault()
        console.log(userid, userpw)
    }

    return (
        <>
            <Head>
                <title>login</title>
            </Head>
            <FormLayout>
                <h2>로그인 page</h2>
                <form onSubmit={handleSubmit}>
                    <input type="text" onChange={ChangeUserid} placeholder="아이디를 입력해주세요"/>
                    <input type="password" onChange={ChangeUserpw} placeholder="패스워드를 입력해주세요"/>
                    <button type="submit">로그인 </button>
                </form>
            </FormLayout>
        </>
    )
}

export default Login

=> 버튼 눌렀을 때 console.log 잘 찍히는지 쳌

 

3) 위의 기존 방식을 조금 더 편리(?)하게 바꿔보기 by custom hook 

userInput 이라는 함수를 사용해서 userid, userpw input 값의 변경 시 value와 onChange 함수를 return 해줌 

하나의 함수로 여러개의 기능을 관리할 수 있다 ! 

근데 input 쪽에 쓴 {...userid} 요 부분은 아무리봐도 낯설다,,

{...{"value":"ok"}} == value="ok"
import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import {useState} from 'react'

const userInput = (defaultValue)=>{
    const [value,setValue] =useState(defaultValue)
    const onChange =e=>{
        setValue(e.target.value)
    }
    return{
        value,onChange
    }
}


const Login = () => {

    const userid = userInput('')
    const userpw = userInput('') 

    const handleSubmit=e=>{
        e.preventDefault()
        console.log(userid, userpw)
        console.log(userid.value,userpw.value)
    }

    return (
        <>
            <Head>
                <title>login</title>
            </Head>
            <FormLayout>
                <h2>로그인 page</h2>
                <form onSubmit={handleSubmit}>
                    <input type="text" {...userid} placeholder="아이디를 입력해주세요"/>
                    <input type="password" {...userpw} placeholder="패스워드를 입력해주세요"/>
                    <button type="submit">로그인 </button>
                </form>
            </FormLayout>
        </>
    )
}

export default Login

4) id & pw 가  === asdf 일 때 메인으로 다시 redirect 되도록 만들기 

Router 가져오기 

어디론가 보내는건 Router.push 이군... 

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import {useState} from 'react'
import Router from 'next/router'

.
.
.

    const handleSubmit=e=>{
        e.preventDefault()
        console.log(userid.value,userpw.value)

        userid.value==='asdf' && userpw.value==='asdf' ? Router.push('/') : alert('id or pw가 다릅니다.');
    }

 

 

 

 

 18. 로그인 시, main menu 바꿔주기 

context & useReducer  사용해서 Login 되었다는 상태 저장할 공간 만들기 

 

1) pages > store > context.jsx

import {createContext} from 'react'

export const initialState ={
    IsLogin:false,
}
const Store = createContext(initialState)

export default Store

2) 만든 context를 최상위 Compo _app.jsx 에 가져오기 + _app.js 이하 모든 컴포에서 사용할 수 있도록 provider 로 감싸기

import '../index.css'
import Head from 'next/head'
import Store, { initialState } from './store/context'

const App = ({ Component }) => {
    return (
        <>
            <Head>
                <link rel="preconnect" href="https://fonts.googleapis.com" />
                <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="true" />
                <link href="https://fonts.googleapis.com/css2?family=Cute+Font&display=swap" rel="stylesheet" />
            </Head>
            <Store.Provider value={initialState}>
                <Component />
            </Store.Provider>
        </>
    )
}

export default App

3) Header에서 사용하기 

- useContext , Store 가져오기 

import Link from 'next/link'
import NavToggle from '../NavToggle'
import Styled from 'styled-components'
import {useContext} from 'react'
import Store from '../../pages/store/context'

IsLogin == true or false 값에 따라 변수값 다르게 설정 

import Link from 'next/link'
import NavToggle from '../NavToggle'
import Styled from 'styled-components'
import {useContext} from 'react'
import Store from '../../pages/store/context'

const Header = () => {

    const globalStore = useContext(Store)
    const {IsLogin} = globalStore
    let LoginOrLogout = (IsLogin) ? '로그아웃' : '로그인'
    let JoinOrInfo = (IsLogin) ? '회원정보' : '회원가입'

    return (
        <>
            <HeaderContainer>
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <Gnb>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>{LoginOrLogout}</a></Link></li>
                    <li><Link href="/join"><a>{JoinOrInfo}</a></Link></li>
                </Gnb>
                <NavToggle />
            </HeaderContainer>
        </>
    )
}

export default Header

css 생략

4) IsLogin 상태 바꿔주기 만들기 with Reducer 

* react는 상태 state 가 바뀌어야 rerender를 한다 -> 그래서 Context만 쓰지않고 useReducer와 함께 쓴다. 

context으로 변수 값이 바뀌어도 상태값이 바뀌지 않는다.

Context = 공용 저장소 
Reducer = 상태값 저장소 + 변경소 (+ rendering) 

_app.jsx

value={{state, dispatch}} 넘겨줄 때  객체 *** {{}} 두 번 쓰기 

import '../index.css'
import Head from 'next/head'
import Store, { initialState } from './store/context'
import { useReducer, useContext } from 'react'

const App = ({ Component }) => {

    const globalContext = useContext(Store)
    const [state,dispatch] = useReducer(reducer,globalContext)

    return (
        <>
            <Head>
                <link rel="preconnect" href="https://fonts.googleapis.com" />
                <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="true" />
                <link href="https://fonts.googleapis.com/css2?family=Cute+Font&display=swap" rel="stylesheet" />
            </Head>
            <Store.Provider value={{state, dispatch}}>
                <Component />
            </Store.Provider>
        </>
    )
}

export default App

reducer 따로 빼서 만들어 주기 

pages  > store > reducer.jsx

export const reducer =(state,action)=>{
    switch(action.type){
        case 'login':
            return{
                ...state,
            }
        case 'logout':
            return{
                ...state,
            }
    }
}

_app.jsx 에 reducer 가져오기

import {reducer} from './store/reducer'

Header.jsx 수정

받는 value 가 객체가 되었고 그 중 state 안에 IsLogin 값을 담기 

import Link from 'next/link'
import NavToggle from '../NavToggle'
import Styled from 'styled-components'
import {useContext} from 'react'
import Store from '../../pages/store/context'

const Header = () => {
    const globalStore = useContext(Store)
    const {IsLogin} = globalStore.state
    console.log(IsLogin)
    let LoginOrLogout = (IsLogin) ? '로그아웃' : '로그인'
    let JoinOrInfo = (IsLogin) ? '회원정보' : '회원가입'

    return (
        <>
            <HeaderContainer>
                {/* 로고 & 메뉴 */}
                <h1>Logo</h1>
                <Gnb>
                    <li><Link href="/"><a>Home</a></Link></li>
                    <li><Link href="/posts/post"><a>글쓰기</a></Link></li>
                    <li><Link href="/login"><a>{LoginOrLogout}</a></Link></li>
                    <li><Link href="/join"><a>{JoinOrInfo}</a></Link></li>
                </Gnb>
                <NavToggle />
            </HeaderContainer>
        </>
    )
}

export default Header

css 생략

 

login.jsx

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import {useState, useReducer, useContext} from 'react'
import Router from 'next/router'
import {reducer} from '../store/reducer'
import Store from '../store/context'
import userInput from '../hooks/userInput'


const Login = () => {
    const userid = userInput('')
    const userpw = userInput('') 

    const {dispatch} = useContext(Store)

    const handleSubmit=e=>{
        e.preventDefault()
        dispatch({type:'login'})
        userid.value==='asdf' && userpw.value==='asdf' ? Router.push('/') : alert('id or pw가 다릅니다.');
    }

    return (
        <>
            <Head>
                <title>login</title>
            </Head>
            <FormLayout>
                <h2>로그인 page</h2>
                <form onSubmit={handleSubmit}>
                    <input type="text" {...userid} placeholder="아이디를 입력해주세요"/>
                    <input type="password" {...userpw} placeholder="패스워드를 입력해주세요"/>
                    <button type="submit">로그인 </button>
                </form>
            </FormLayout>
        </>
    )
}

export default Login

reducer.jsx 수정



export const reducer = (state, action) => {
    switch (action.type) {
        case 'login':
            // state.state.IsLogin=true;
            return {
                ...state,
                IsLogin: true,
            }
        case 'logout':
            console.log(state)
            return {
                ...state,
                IsLogin: false,
            }
        default:
            return state
    }
}

질문1 : return{ IsLogin:true, ...any} 요렇게 하면 false -> true 로 바뀌지 않음 (근데 logout은 바뀜) 

-> Login.jsx에서 useContext를 사용하고 또 state에 객체를 넣었음 ! 

요 부분 위 코드 수정함 

로그인 되어 있으면 '로그아웃', '회원정보' 로 뜬다

 

 

 

 

 19. 로그 아웃 

1) pages > logout.jsx 생성 & 코드 작성

import { useContext, useEffect } from "react"
import Store from './store/context'
import Router from 'next/router'

const Logout =()=>{

    const {dispatch} = useContext(Store)
    useEffect(()=>{
        dispatch({type:'logout'})
        setTimeout(()=>{
            Router.back()
        },1000)    
    },[])
    

    return(
        <>
            로그아웃 되셨습니다
        </>
    )
}

export default Logout

1초 동안 '로그아웃 되셨습니다' 가 나오고 Router.back()으로 자동으로 url 넘어감 

 

 

여기까지의 내용

https://blckchainetc.tistory.com/291

 

[96일차] 20210727 React 리액트, 레이아웃 CSS, 새로고침할 때 CSS 풀리는 현상,

1. Header 모듈로 만들기 + ul, li styled component components > layout 폴더 > Header.jsx 만들어서 BlogLayout 의 header부분 따로 빼기 import Link from 'next/link' import NavToggle from '../NavToggle'..

blckchainetc.tistory.com

 

 


 

 

 20. 파일 중간 정리 

1) store 폴더를 front>하위 폴더로 이동 

-> store 폴더 안 context, reducer 를 사용한 _app.js, header, login, logout, etc.....jsx 경로 바꾸기 

 

2) hooks 따로 빼기  (login.jsx 안에 있던 userInput 함수) 

front > hooks 폴더 생성 > userInput.jsx 파일 생성 

import {useState} from 'react'

const userInput = (defaultValue)=>{
    const [value,setValue] =useState(defaultValue)
    const onChange =e=>{
        setValue(e.target.value)
    }
    return{
        value,onChange
    }
}

export default userInput

login.jsx

뺴낸 userInput 가져오기 

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import {useState, useReducer, useContext} from 'react'
import Router from 'next/router'
import {reducer} from '../store/reducer'
import Store from '../store/context'
import userInput from '../hooks/userInput'


const Login = () => {
    const userid = userInput('')
    const userpw = userInput('')

 

 

 

 

 21. 회원가입 형식 만들기 with hook userInput  

join.jsx 

join도 login처럼 input box에 내용을 넣고 해당 내용을 출력받기 떄문에 위의 userInput hook을 사용 가능 

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import userInput from '../hooks/userInput'

const Join = () => {

    const username=userInput('')
    const userid=userInput('')
    const password=userInput('')
    const passwordCheck=userInput('')
    const userphone=userInput('')

    const handleSubmit =(e)=>{
        e.preventDefault()
        console.log(username,userid,password,passwordCheck, userphone)
    }

    return (
        <>
            <Head>
                <title>join</title>
            </Head>
            <FormLayout>
                <h2>회원가입</h2>
                <form onSubmit ={handleSubmit}>
                    <input type="text" {...username} placeholder="이름을 입력해주세요"/> <br/>
                    <input type="text" {...userid} placeholder="아이디를 입력해주세요"/> <br/>
                    <input type="text" {...password} placeholder="비밀번호를 입력해주세요"/> <br/>
                    <input type="text" {...passwordCheck} placeholder="비밀번호 확인"/> <br/>
                    <input type="text" {...userphone} placeholder="핸드폰 번호를 입력해주세요"/> <br/>
                    <button type="submit">회원가입하기</button>
                </form>
            </FormLayout>
        </>
    )
}

export default Join

console.log가 잘 나오는지 확인 -> .value 객체로 사용해야함을 알 수 있다 

=> passwordCheck은 요 페이지에서 한 번밖에 안쓸거라 hooks 를 굳이 쓰지 않아도 됨  =>

const passwordCheck =userInput('') 제거! 

 

 

 

 

 

 22. 회원가입 시 password 실시간 체크 

1) passwordCheck과 passwordError 라는 상태값을 만들기 (useState 사용하므로 useState도 가져오기 from 'react') 

2) passwordCheck input의  value =passwordCheck을 주고 onChange로 passwordCheck 값 넣기  + passwordError true or false 정해서 set 하기 

import {useState} from 'react'

const Join = () => {

    const [passwordCheck, setPasswordCheck] = useState('')
    const [passwordError, setPasswordError] = useState(false)

    const handlePassword =e=>{
        const {value} = {...e.target}
        setPasswordError(password.value!==value) // 같지 않으면 true 다르면 false
        setPasswordCheck(value)
    }


    return (
                    <input type="text" {...password} placeholder="비밀번호를 입력해주세요"/> <br/>
                    <input type="text" value={passwordCheck} onChange={handlePassword} placeholder="비밀번호 확인"/> <br/>
                    {passwordError && <div style={{color:'red'}}> 비밀번호가 일치하지 않습니다. </div>}
                    <input type="text" {...userphone} placeholder="핸드폰 번호를 입력해주세요"/> <br/>
                    <button type="submit">회원가입하기</button>

새로 배운 구문 {passwordError && <div></div>}  passwordError 가 true 면 && 뒤의 명령 실행 

 

 

 

 

 

 

 23. 약관 동의 checkbox 

join.jsx

1) checkbox 추가 -> checked = 는 {term}으로 설정 

2) 상태값 term, termError 추가 

3) label 추가해서 '약관 동의해주십쇼' 글씨를 클릭해서도 체크박스 체크되도록 

-> 요 부분은 

<input type="checkbox" id ="term" checked={term} onChange={handleTerm} />                       
    <label htmlFor="term"> 약관에 동의해 주십시오</label> <br/>  

input 에 id 를 넣고 
lable은 htmlFor = 향하고 싶은 id 명 쓰기 
import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import userInput from '../hooks/userInput'
import {useState} from 'react'

const Join = () => {

    const username=userInput('')
    const userid=userInput('')
    const password=userInput('')
    const userphone=userInput('')

    const [passwordCheck, setPasswordCheck] = useState('')
    const [passwordError, setPasswordError] = useState(false)

    const handlePassword =e=>{
        const {value} = {...e.target}
        setPasswordError(password.value!==value) // 같지 않으면 true 다르면 false
        setPasswordCheck(value)
    }

    const [term,setTerm] = useState(false)
    const [termError, setTermError] = useState(false)

    const handleTerm=e=>{
        // setTerm(!term) 또는 
        setTerm(e.target.checked)
    }

    const handleSubmit =(e)=>{
        e.preventDefault()
        console.log(username,userid,password, userphone)
    }

    return (
        <>
            <Head>
                <title>join</title>
            </Head>
            <FormLayout>
                <h2>회원가입</h2>
                <form onSubmit ={handleSubmit}>
                    <input type="text" {...username} placeholder="이름을 입력해주세요"/> <br/>
                    <input type="text" {...userid} placeholder="아이디를 입력해주세요"/> <br/>
                    <input type="text" {...password} placeholder="비밀번호를 입력해주세요"/> <br/>
                    <input type="text" value={passwordCheck} onChange={handlePassword} placeholder="비밀번호 확인"/> <br/>
                    {passwordError && <div style={{color:'red'}}> 비밀번호가 일치하지 않습니다. </div>}
                    <input type="text" {...userphone} placeholder="핸드폰 번호를 입력해주세요"/> <br/>
                    <input type="checkbox" id ="term" checked={term} onChange={handleTerm} />
                        <label htmlFor="term"> 약관에 동의해 주십시오</label> <br/>
                    <button type="submit">회원가입하기</button>
                </form>
            </FormLayout>
        </>
    )
}

export default Join

4) termError 로직 추가 

    const [term,setTerm] = useState(false)
    const [termError, setTermError] = useState(false)

    const handleTerm=e=>{
        // setTerm(!term) 또는 
        setTerm(e.target.checked)
        setTermError(e.target.checked ===false)
    }

    return (
        <>
                    <input type="checkbox" id ="term" checked={term} onChange={handleTerm} />
                        <label htmlFor="term"> 약관에 동의해 주십시오</label> <br/>
                    {termError && <div style={{color:'red'}}>약관 동의는 필수입니다.</div>}
                    <button type="submit">회원가입하기</button>

 

 

 

 

 24. submit 했을 때  마지막으로 한번 더 체크해주기 

    const handleSubmit =(e)=>{
        e.preventDefault()
        if (password.value!==passwordCheck){
            setPasswordError(true)
        }else{
            setPasswordError(false)
        }

        if(!term){
            setTermError(true)
            return;
        }
    }

 

join.jsx 전체 코드 

import FormLayout from '../components/FormLayout'
import Head from 'next/head'
import userInput from '../hooks/userInput'
import {useState} from 'react'

const Join = () => {

    const username=userInput('')
    const userid=userInput('')
    const password=userInput('')
    const userphone=userInput('')

    const [passwordCheck, setPasswordCheck] = useState('')
    const [passwordError, setPasswordError] = useState(false)

    const handlePassword =e=>{
        const {value} = {...e.target}
        setPasswordError(password.value!==value) // 같지 않으면 true 다르면 false
        setPasswordCheck(value)
    }

    const [term,setTerm] = useState(false)
    const [termError, setTermError] = useState(false)

    const handleTerm=e=>{
        // setTerm(!term) 또는 
        setTerm(e.target.checked)
        setTermError(e.target.checked ===false)
    }

    const handleSubmit =(e)=>{
        e.preventDefault()
        if (password.value!==passwordCheck){
            setPasswordError(true)
        }else{
            setPasswordError(false)
        }

        if(!term){
            setTermError(true)
            return;
        }
    }

    return (
        <>
            <Head>
                <title>join</title>
            </Head>
            <FormLayout>
                <h2>회원가입</h2>
                <form onSubmit ={handleSubmit}>
                    <input type="text" {...username} placeholder="이름을 입력해주세요"/> <br/>
                    <input type="text" {...userid} placeholder="아이디를 입력해주세요"/> <br/>
                    <input type="text" {...password} placeholder="비밀번호를 입력해주세요"/> <br/>
                    <input type="text" value={passwordCheck} onChange={handlePassword} placeholder="비밀번호 확인"/> <br/>
                    {passwordError && <div style={{color:'red'}}> 비밀번호가 일치하지 않습니다. </div>}
                    <input type="text" {...userphone} placeholder="핸드폰 번호를 입력해주세요"/> <br/>
                    <input type="checkbox" id ="term" checked={term} onChange={handleTerm} />
                        <label htmlFor="term"> 약관에 동의해 주십시오</label> <br/>
                    {termError && <div style={{color:'red'}}>약관 동의는 필수입니다.</div>}
                    <button type="submit">회원가입하기</button>
                </form>
            </FormLayout>
        </>
    )
}

export default Join

 

 

 

 

 

 25. 대분류 메뉴 뿌리기 

카테고리 만들어 뿌리기 From DB => 요걸 json으로 받을 것 

1) NavToggle 파일 정리 

components > Accordion.jsx 생성 + 코드 옮기기 

import Styled from 'styled-components'

const Accordion = ({visible}) => {        // props 받기 
    return ( 
        <>
            <AccordionMenu flag-{visible}>
                <ul>
                    <li>대분류 메뉴1</li>
                    <li>대분류 메뉴2</li>
                    <li>대분류 메뉴3</li>
                    <li>대분류 메뉴4</li>
                    <li>대분류 메뉴5</li>
                </ul>
            </AccordionMenu>
        </>
    )
}


const AccordionMenu = Styled.div`
    position:absolute;
    width:100%;
    left:0px;
    top:10vh;
    z-index:5;
    background:#fff;
    padding:7vh 0;

    // props 가 가리키는 것 : Accordion이 가진 모든 props를 말함 
    display:${(props)=>(props.flag) ? 'block':'none'};
    


    & > ul{
        margin-top:5vh;
        display:flex;
        flex-direction:column;
    }

    & > ul > li{
        margin-top:20px;
        text-align:center;
    }
`


export default Accordion

NavToggle.jsx

import Styled from 'styled-components'
import {useState} from 'react'
import Accordion from './Accordion'

const NavToggle = () => {

    const [visible,setVisible] = useState(false)
    const handleToggle=()=>{
        setVisible(!visible)
    }

    return (
        <Toggle>
            <input 
                type="checkbox" 
                id="nav-toggle" 
                onClick={handleToggle} 
                className="nav-toggle" 
            />
            <label htmlFor="nav-toggle">
                <span></span>
                <span></span>
                <span></span>
            </label>
            <Accordion visible={visible}/>
        </Toggle>
    )
}

export default NavToggle



const Toggle = Styled.div`
    background:transparent;
    border-color:transparent;

    & > .nav-toggle{
        display:none;
    }

    & > .nav-toggle + label{
        display:block;
        width:2.5rem;
        height:2rem;
        position:relative;
        cursor:pointer;
    }

    & > .nav-toggle + label > span{
        display:block;
        position:absolute;
        width:100%;
        height:5px;
        border-radius:30px;
        background:black;
        transition:all .35s ;
    }

    & > .nav-toggle + label > span:nth-child(1){top:0}
    & > .nav-toggle + label > span:nth-child(2){top:50%; transform:translateY(-50%)}
    & > .nav-toggle + label > span:nth-child(3){bottom:0}
    
    & > .nav-toggle:checked + label > span:nth-child(1){
        top:50%;
        transform:translateY(-50%) rotate(45deg);
    }
    & > .nav-toggle:checked + label > span:nth-child(2){
        opacity:0;
    }

    & > .nav-toggle:checked + label > span:nth-child(3){
        bottom:50%;
        transform:translateY(50%) rotate(-45deg);
    }
`

 

 

 

2) DB에서 받았다고 가정하고 json 형태 임시로 만들어 놓기 -> map으로 뿌리기 

Accordion.jsx

import Styled from 'styled-components'
import Link from 'next/link'

const menu = [
    {
        id:'1',
        category:'대분류 메뉴 1',
        url: '/posts/1'
    },
    {
        id:'2',
        category:'대분류 메뉴 2',
        url: '/posts/2'
    },
    {
        id:'3',
        category:'대분류 메뉴 3',
        url: '/posts/3'
    },
    {
        id:'4',
        category:'대분류 메뉴 4',
        url: '/posts/4'
    },
    {
        id:'5',
        category:'대분류 메뉴 5',
        url: '/posts/5'
    }
]

const Accordion = ({visible}) => {
    const category = menu.map((v)=>{
        return <li key={v.id}><Link href={v.url}><a>{v.category}</a></Link></li>
    })
    return (
        <>
            <AccordionMenu flag={visible}>
                <ul>
                    {category}
                </ul>
            </AccordionMenu>
        </>
    )
}


const AccordionMenu = Styled.div`
    position:absolute;
    width:100%;
    left:0px;
    top:10vh;
    z-index:5;
    background:#fff;
    padding:7vh 0;

    // props 가 가리키는 것 : Accordion이 가진 모든 props를 말함 
    display:${(props)=>(props.flag) ? 'block':'none'};
    


    & > ul{
        margin-top:5vh;
        display:flex;
        flex-direction:column;
    }

    & > ul > li{
        margin-top:20px;
        text-align:center;
    }
`


export default Accordion

 

 

 

3) 페이지 이동 되도록 만들기 

NavToggle.jsx 에서 Accordion에 handleToggle보내기

const NavToggle = () => {

    const [visible,setVisible] = useState(false)
    const handleToggle=()=>{
        setVisible(!visible)
    }

    return (
        <Toggle>
            <input 
                type="checkbox" 
                id="nav-toggle" 
                onClick={handleToggle} 
                className="nav-toggle" 
            />
            <label htmlFor="nav-toggle">
                <span></span>
                <span></span>
                <span></span>
            </label>
            <Accordion visible={visible} handleToggle = {handleToggle}/>
        </Toggle>
    )

Accordion.jsx 받아서 사용하기

const Accordion = ({visible, handleToggle}) => {
    const category = menu.map((v)=>{
        return <li key={v.id} onClick={handleToggle}><Link href={v.url}><a>{v.category}</a></Link></li>
    })
    return (
        <>
            <AccordionMenu flag={visible}>
                <ul>
                    {category}
                </ul>
            </AccordionMenu>
        </>
    )
}

 

 

 

 

4) 글쓰기 post page 꾸미기

posts> [post].jsx

import { useRouter } from 'next/router'
import BlogLayout from '../../components/blogLayout'
import Head from 'next/head'

const data =[
    {
        id:'1',
        subject:'my website',
        content:'html 왼쪽 위에서부터 내려옵니다.',
        date:'2021-07-29',
        hit:'10',
    },
    {
        id:'2',
        subject:'my website',
        content:'html- block과 inline 스타일로 나눠집니다. ',
        date:'2021-07-30',
        hit:'1',
    }
]

const Post = () => {

    const router = useRouter()
    const { post } = router.query

    const list = data.map(v=>{
        return(
            <div key={v.id}>
                <ul>
                    <li>{v.subject}</li>
                    <li>{v.content}</li>
                    <li>{v.date}</li>
                    <li>{v.hit}</li>
                </ul>
            </div>
        )
    })

    return (
        <>
            <Head>
                <title>posting </title>
            </Head>
            <BlogLayout>
                hello Post 동적라우팅  {post}
                <div>
                    {list}
                </div>
            </BlogLayout>
        </>
    )
}

export default Post

d

 

 


 

 

질문 2

최상위 Compo에서 context 사용할 때 꼭 Provider 에 변수에 state, dispatch 등을 담아서 보내야 하는지 ? -> useConetxt(Store) 할 때 나오는 값들이 props에 담은 값 , reducer에서 사용할 때 state는 상태만 있음 dispatch는 없다. 

 

받는 Logout Compo에서 useContext(Store) => 하면 나오는 값은 value 안의 값들인지 -> _app.js에서 value 라는 변수로 보낸 값들이 담겨있음 거기서 dispatch만 빼서 사용 한 것 ! 

 

 

질문 3

아래 는 왜 안되는지 ???? useEffect를 지움 

-> 무한 랜더가 된다. -> dispatch 하면 render가 되고 또 Logout 이 실행 ..반복 

useEffect는, 두 번째 인자값 []을 주면 == componentdidmount와 같은 생명주기가 되어 딱 한 번 render 된다. 

import { useContext, useEffect } from "react"
import Store from './store/context'
import Router from 'next/router'

const Logout =()=>{

    const {dispatch} = useContext(Store)
    // useEffect(()=>{
        dispatch({type:'logout'})
    //     setTimeout(()=>{
    //         Router.back()
    //     },1000)    
    // },[])
    

    return(
        <>
            로그아웃 되셨습니다
        </>
    )
}

export default Logout

 

반응형