1. Header 모듈로 만들기 + ul, li styled component
components > layout 폴더 > Header.jsx 만들어서
BlogLayout 의 header부분 따로 빼기
import Link from 'next/link'
import NavToggle from '../NavToggle'
// import Styled from 'styled-components'
// const
const Header =()=>{
return(
<>
<div className="header">
{/* 로고 & 메뉴 */}
<h1>로고</h1>
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/posts/post">
<a>글쓰기</a>
</Link>
</li>
<li>
<Link href="/login">
<a>login</a>
</Link>
</li>
<li>
<Link href="/join">
<a>join</a>
</Link>
</li>
</ul>
<NavToggle/>
</div>
</>
)
}
export default Header
CSS : Styled 주기
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 Header =()=>{
return(
<>
<HeaderContainer>
{/* 로고 & 메뉴 */}
<h1>로고</h1>
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/posts/post">
<a>글쓰기</a>
</Link>
</li>
<li>
<Link href="/login">
<a>login</a>
</Link>
</li>
<li>
<Link href="/join">
<a>join</a>
</Link>
</li>
</ul>
<NavToggle/>
</HeaderContainer>
</>
)
}
export default Header
npm run dev --> localhost:3001
ul - sytled 만들어서 대체하기
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;
height:10vh;
`
const Gnb = Styled.ul`
// 모바일
display:flex;
flex-direction:row;
// pc 내용들
@media only screen and (max-width:768px){
display:none;
}
`
const Header =()=>{
return(
<>
<HeaderContainer>
{/* 로고 & 메뉴 */}
<h1>로고</h1>
<Gnb>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/posts/post">
<a>글쓰기</a>
</Link>
</li>
<li>
<Link href="/login">
<a>login</a>
</Link>
</li>
<li>
<Link href="/join">
<a>join</a>
</Link>
</li>
</Gnb>
<NavToggle/>
</HeaderContainer>
</>
)
}
export default Header
ul 이랑 li 는 셋트
const Gnb = Styled.ul`
// 모바일
display:flex;
flex-direction:row;
& > li{
margin-left:20px;
}
// pc 내용들
@media only screen and (max-width:768px){
display:none;
}
`
2. 새로고침할 떄마다 CSS 풀리는 현상
SSR - Styled Component가 안먹힘 (next에서 styled component plugin을 설정 안해놓음 )
styled-component는 react가 만든 것이 아닌 다른 사람이 든거라 -> 설정 해야 함
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();
}
}
}
front > .babelrc 파일 생성
{
"presets":["next/babel"],
"plugins":[
["styled-components", {"ssr":true}]
]
}
아래 package 다운
npm i -D babel-plugin-styled-components
3. NavToggle.jsx
<span></span>
<span></span>
</label>
<div>
asdf
</div>
</Toggle>
위치만 확인해보기
StyledComponent 만들어서 적용하기
const Accordion = Styled.div`
position:absolute;
width:100%;
left:0px;
top:10vh;
z-index:5;
background: #fff;
`
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>
)
}
위의 메뉴들 예쁘게 만들기
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;
}
`
Styeld component의 장점 :
props 값을 던질 수 있고 css에서 받을 수 있다. -> display none or block 할 지 정할 수 있다.
4.
NavToggle.jsx
useState 가져오기
import Styled from 'styled-components'
import {useState} from 'react'
Label에 onClick 하면 click 두번 발생됨
input에다가 onclick event 주기
const NavToggle = () => {
const [visible, setVisible] = useState(false)
const handleToggle = ()=>{
setVisible(!visible)
}
return (
<Toggle>
<input
type="checkbox"
id="nav-toggle"
className="nav-toggle"
onClick={handleToggle}
/>
<label htmlFor="nav-toggle">
<span></span>
<span></span>
<span></span>
Accordion css compo에게 props 주기
<Accordion flag={visible}>
<ul>
<li>대분류 메뉴1</li>
<li>대분류 메뉴2</li>
<li>대분류 메뉴3</li>
<li>대분류 메뉴4</li>
<li>대분류 메뉴5</li>
</ul>
</Accordion>
</Toggle>
이제 Accodion css compo에서 props 사용하기
${} - JS 영역이라고 알려주는 것
import Styled from 'styled-components'
import {useState} from 'react'
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:#000;
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);
}
`
const Accordion = Styled.div`
position:absolute;
width:100%;
left:0px;
top:10vh;
z-index:5;
background: #fff;
padding:7vh 0;
// ()=>{} 근데 {} 요 괄호를 생략해줌
// props 가 가르키는것 : Accodion 이 가진 모든props를 말함
display:${(props)=> (props.flag) ? 'block':'none'};
& > ul{
margin-top:5vh;
display:flex;
flex-direction:column;
}
&>ul>li{
margin-top:20px;
text-align:center;
}
`
const NavToggle = () => {
const [visible, setVisible] = useState(false)
const handleToggle = ()=>{
setVisible(!visible)
}
return (
<Toggle>
<input
type="checkbox"
id="nav-toggle"
className="nav-toggle"
onClick={handleToggle}
/>
<label htmlFor="nav-toggle">
<span></span>
<span></span>
<span></span>
</label>
<Accordion flag={visible}>
<ul>
<li>대분류 메뉴1</li>
<li>대분류 메뉴2</li>
<li>대분류 메뉴3</li>
<li>대분류 메뉴4</li>
<li>대분류 메뉴5</li>
</ul>
</Accordion>
</Toggle>
)
}
export default NavToggle
클릭하면 껏다가 켜짐
5.
formLayout.jsx
import Styled
import Router from 'next/router'
//import styled from './FormLayout.module.css' //요 안의 모든 css를 객체로 변환
import Styled from 'styled-components'
-
import Router from 'next/router'
//import styled from './FormLayout.module.css' //요 안의 모든 css를 객체로 변환
import Styled from 'styled-components'
const FormLayout = ({ children }) => {
return (
<>
<div>
<div>
<button onClick={() => Router.back()}>뒤로가기</button>
{children}
{/* Footer html // 나중에 footer부분 / header도 따로 compo로 나누기
<div className={styled.footer}>
copyright © all reserved
</div> */}
</div>
</div>
</>
)
}
export default FormLayout
-
import Router from 'next/router'
//import styled from './FormLayout.module.css' //요 안의 모든 css를 객체로 변환
import Styled from 'styled-components'
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;
}
`
const FormLayout = ({ children }) => {
return (
<>
<Background>
<div>
<p onClick={() => Router.back()}>뒤로가기</p>
{children}
{/* Footer html // 나중에 footer부분 / header도 따로 compo로 나누기
<div className={styled.footer}>
copyright © all reserved
</div> */}
</div>
</Background>
</>
)
}
export default FormLayout
6.
login.jsx 를 form으로 만들어주기
import FormLayout from "../components/FormLayout"
import Head from 'next/head'
const Login = () => {
return (
<>
<Head>
<title>Blog | 로그인</title>
</Head>
<FormLayout>
로그인 페이지입니다.
<h2>로그인</h2>
<form>
<input type="text" placeholder="아이디를 입력해주세욥" />
<input type="password" placeholder="패스워드를 입력해주세욮" />
<button type="submit">로그인</button>
</form>
</FormLayout>
</>
)
}
export default Login
우리가 많이 써봤던 방법 ↓↓↓
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.preventDefualt()
console.log(serid,userpw)
}
return (
<>
<Head>
<title>Blog | 로그인</title>
</Head>
<FormLayout>
로그인 페이지입니다.
<h2>로그인</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
위의 코드를 custom hook 으로 짧게 만들어보기
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{ //useInput에 들어가 있는 상태 value값 은 value에 onChange함수는 onChange함수에 넣기
value,onChange
}
}
const Login = () => {
const userid = userInput('')
const userpw = userInput('')
const handleSubmit=(e)=>{
e.preventDefault()
console.log(userid,userpw)
}
return (
<>
<Head>
<title>Blog | 로그인</title>
</Head>
<FormLayout>
로그인 페이지입니다.
<h2>로그인</h2>
<form onSubmit ={handleSubmit}>
<input type="text" {...userid} placeholder="아이디를 입력해주세욥" />
<input type="password" {...userpw} placeholder="패스워드를 입력해주세욮" />
<button type="submit">로그인</button>
</form>
</FormLayout>
</>
)
}
export default Login
* 아래 그림 잘 분석 !
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)
}
//useInput에 들어가 있는 상태 value값 은 value에 onChange함수는 onChange함수에 넣기
return{
value,onChange
}
}
const Login = () => {
const userid = userInput('') // object
const userpw = userInput('') // object
const handleSubmit=(e)=>{
e.preventDefault()
console.log(userid.value,userpw.value)
userid.value==="asdf" && userpw.value==="asdf" ? alert('뒤로가기 or main으로 가기') : alert('id 또는 pw가 다릅니다.')
}
return (
<>
<Head>
<title>Blog | 로그인</title>
</Head>
<FormLayout>
로그인 페이지입니다.
<h2>로그인</h2>
<form onSubmit ={handleSubmit}>
{/* html 형태로 보이게끔 한 JavaScript이다! (bable로 가능케함) */}
{/* babel은 type="text" 를 객체로 바꿔줌 type="text" => "type":"text" */}
{/* {} 대괄호를 쓰면 JS 구문을 쓸 수 있게 해주겠다. */}
<input type="text" {...userid} placeholder="아이디를 입력해주세욥" />
<input type="password" {...userpw} placeholder="패스워드를 입력해주세욮" />
<button type="submit">로그인</button>
</form>
</FormLayout>
</>
)
}
export default Login
뒤로 보내는 것보다 메인으로 보내주거나 or url 내가 설정해주는게 좋음
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 또는 pw가 다릅니다.')
}
7. 로그인 후 메인으로 보내졌을 때 메뉴 바꿔주기
Login되었다는 상태 저장할 공간 필요
context & useReducer 사용해보기
* Context 놓을 곳 찾기
App == _app.jsx
8. context 사용하기
blogLayout.jsx
다른 곳에서 Store context를 사용해야하는데 사용하기 편하기 위해 context를 따로 뻄
pages> store>context.jsx
import {createContext} from 'react'
export const initialState = {
IsLogin: false,
}
const Store = createContext(initialState)
export default Store
_app
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=Black+Han+Sans&display=swap" rel="stylesheet" />
</Head>
<Store.Provider value={initialState}>
<Component /> {/* 우리가 만든 파일(index,login..)들이 여기에 위치 */}
</Store.Provider>
</>
)
}
export default App
Header에서 사용하기
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
return (
<>
<HeaderContainer>
{/* 로고 & 메뉴 */}
<h1>로고</h1>
<Gnb>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/posts/post">
<a>글쓰기</a>
</Link>
</li>
{
IsLogin === false ?
<>
<li>
<Link href="/login">
<a>login</a>
</Link>
</li>
<li>
<Link href="/join">
<a>join</a>
</Link>
</li>
</>
:
<>
<li>
<Link href="/login">
<a>logout</a>
</Link>
</li>
<li>
<Link href="/join">
<a>info</a>
</Link>
</li>
</>
}
</Gnb>
<NavToggle />
</HeaderContainer>
</>
)
}
export default Header
로긴 여부에 따라 false, true 만들기
리액트는 상태 (state)가 바뀌어야 rerender를 함
useReducer와 useContext와 같이 쓰는 이유
Context로 변수 값이 바뀌어도 rerender가 이루어지지않음
9. useReducer
_app.jsx
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) // context에 있는 값을 그대로 빼와서 담음
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=Black+Han+Sans&display=swap" rel="stylesheet" />
</Head>
<Store.Provider value={{state,dispatch}}>
<Component /> {/* 우리가 만든 파일(index,login..)들이 여기에 위치 */}
</Store.Provider>
</>
)
}
export default App
*** value={{state,dispatch}} 넘겨주는거 첫 번째 {} 는 JS / 두 번째 {} 는 객체 벌써 요 부분 두 번 틀ㄹ렷다
reducer 함수 따로 빼서 만들기
Store> reducer.jsx
export const reducer =(state,action)=>{
switch(action.type){
case 'login':
return{
...state,
}
case 'logout':
return{
...state,
}
}
}
_app.jsx에서 reducer 가져오기
useContext 로 받는 값이 다름
요게 바뀌어서
<Store.Provider value={{state,dispatch}}>
-> Header.jsx 수정
const Header = () => {
const globalStore = useContext(Store)
const { IsLogin } = globalStore.state
return (
<>
<HeaderContainer>
{/* 로고 & 메뉴 */}
<h1>로고</h1>
context -> IsLogin true로 만들기 (로그아웃 쳌 하기 위해)
reducer.jsx
export const reducer =(state,action)=>{
switch(action.type){
case 'login':
return{
...state,
}
case 'logout':
return{
...state,
IsLogin:false,
}
}
}
로그아웃 만들기
pages> logout.jsx
import Store from './store/context'
import { useContext, useEffect } from 'react'
import Router from 'next/router'
const logout=()=>{
const {dispatch} = useContext(Store)
useEffect(()=>{
dispatch({type:'logout'})
setTimeout(()=>{
Router.back()
},1000)
},[]) // 빈배열일 경우render 완료 되었을 때 딱 한번 실행되는 코드가 됨
return(
<>
logout
</>
)
}
export default logout
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[95-97일차 복습] 리액트 (+NEXT) 웹 홈페이지 만들기 기초 (레이아웃 / 로그인 / 회원가입) (0) | 2021.07.29 |
---|---|
[97일차]20210728 React 리액트 회원가입 로그인 / 리덕스 설치 (0) | 2021.07.28 |
[95일차]20210726 react Link, head, title, css, 라우터, 폰트, 이미지 넣기, 핸드폰과 연동하기 (0) | 2021.07.26 |
[94일차]20210723 front server 연결 / Next 설치 (0) | 2021.07.23 |
[ 93일차 복습 ] 리액트 React CRUD (0) | 2021.07.22 |