NEXT.js> styled-components

목표
화면 그리기

styled-components

SSR으로 인한 문제 발생 해결 코드

React에 styled-CSS를 적용했는데 새로고침과 함께 풀릴때가 많다. 서버 사이드 렌더링으로 인해 스타일 컴포넌트가 적용되지 않는 경우이다. 그런 경우는 다음과 같은 코드를 작성해주면 된다.
pages>_document.jsx

[_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();
    }
  }
}

.babelrc

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

그리고 다음과 같이 npm 패키지를 다운받는다.
$npm i -D babel-plugin-styled-components

그리고 실행시킨다면 잘 실행이 될 것이다.

Custom-Hook

로그인

로그인을 하기 위해서는 아이디와 비밀번호의 입력을 받아야 한다. 여러개의 인풋을 제어하기 위한 방법으로는 여러가지가 있다.

여러개의 인풋을 제어하기위해서 제일 쉬운 방법은

const[input,setInput1]=React.useState('')
const[input2,setInput2]=React.useState('')

다음과 같이 각 변수를 선언해서 할 수 있다. 두번째로는 커스텀훅을 이용하면 같은 것을 한 함수에서 처리할 수 있다.

[login.jsx]
import FormLayout from '../../components/FormLayout'
import {useState} from 'react'


//여러개의 인풋 처리하기 위해서 사용

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

    return{value,onChange}
}


const Login =()=>{
    const userid = useInput('');
    const userpw = useInput('');



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


    return (
        <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

여기서 첫번째의

const[input,setInput1]=React.useState('')
const[input2,setInput2]=React.useState('')

const ChangeId=(e)=>{
	const {value}={...e.target}
	setInput1(value)
    }

const ChangePw =(e)=>{
	const {value} = {...e.target}
    setInput2(value)
    }

이처럼 반복되는 코드를 커스텀 훅을 이용하여

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

    return{value,onChange}
}

이렇게 줄인 것을 알 수 있다.

이를 이해하려면 먼저 babel을 이해해야 한다.

우리가 React에서 return 안에 쓴 내용들은 html 내용이지만 실제로는 javascript이다. 바벨이 javascript내용을 html으로 변환해주는 것임. babel은 {}안에 쓰면 자바스크립트 영역으로 인정해준다. 그리고 babel은 우리가 return 영역에 쓴 html(?)들을 다음과 같이 객체로 해석을 한다.


type="text" ------> "type":"text"


<input type = "text" value="ok"/>

<input type = "text" {...{"value":"ok"}} />


{value:"asdfasdf", onChange:()=>{alert(1)}

value="adsfadsf" onChange={()=>alert(1)} 

여기서
<input type ="text" {...userid} placeholder = "아이디를 입력해주세요"/>
를 babel은 다음과 같이 해석한다.
(userid는 value값과 onChange로 이루어져 있다. )

userid={value:'',onChange:f}이기에
{...userid}value="" onChange=f가 되는 것을 알 수 있다.

<input type = "text" value={value} onChange={onChange} placeholder = "아이디를 입력해주세요"/>
와 같이 해석함 것을 알 수 있다.

변수값에 따른 렌더링 가능(단일 컴포넌트)

styled-component의 가장 큰 장점은 굳이 값을 주고 받는 과정이 없이도 변수를 통제해서 css를 조건부 렌더링 할 수 있다는 점이다.

예를들어 메뉴부분을 석삼(三)자로 만드는 메뉴같은 경우 , 클릭함에 따라 컴포넌트의 상태값을 변경하면서 그에 따라 메뉴를 나타내고 나타내지 않을 수 있는 것이다.

const Accordion = Styled.div`
...
display:${props=>(props.flag ? 'block' : 'none')};
...
`

위와 같이 Accordion에 styled-component가 적용된 코드가 다음과 같은 코드에 쓰인다면

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>
            {/* {메뉴생성} */}
            <div>
                <Accordion flag ={visible}>
                    <ul>
                    <li>대분류메뉴1</li>
                    <li>대분류메뉴2</li>
                    <li>대분류메뉴3</li>
                    <li>대분류메뉴4</li>
                    <li>대분류메뉴5</li>
                    </ul>
                </Accordion>
            </div>
        </Toggle>
    )
}
export default NavToggle

visible이라는 변수값에 따라 값을 올리고 내리고 하지 않아도 컴포넌트 내에서 충분히 css 변경이 가능하다.

styled-components에서는 ${}으로 Javascript 문법을 사용할 수 있다.

변수값에 따른 렌더링 가능(여러 컴포넌트)

로그인 로그아웃 기능

로그인과 로그아웃을 하려면 javascript에서는 session이나 쿠키값을 이용했었다. react에서는 context와 reducer, 이 둘을 이용해서 할 수 있다. context와 reducer가 선언되어야 하는 부분은 최상위 컴포넌트이다.
Next.js에서는 여러 상위 컴포넌트가 많지만 우리가 사용할 값을 넣어야 하는 곳은 App컴포넌트이다. 우리가 저번에 만든 _app.jsx파일의 부분에 우리가 만든 모든 컴포넌트가 담긴다.

pages>_app.jsx

[_app.jsx]
 import'../index.css'
import Head from 'next/head'
import Store from './store/context'
import {useReducer,useContext} from 'react'
import reducer from '../pages/store/reducer'
const App=({Component})=>{
   const globalContext=useContext(Store)
   const [state,dispatch] = useReducer(reducer,globalContext)
   return (
       <>
       <Head>
           <link/>

           <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=Do+Hyeon&display=swap" rel="stylesheet"/>
       </Head>
       <Store.Provider value ={state,dispatch}/>
           <Component/>/{/*우리가 만든 모든 것이 components에 위치한다.  */}
       <Store.Provider/>
       </>
   )
}
export default App
 

_app.jsx파일의 부분에 우리가 만든 모든 컴포넌트가 담긴다.

<Store.Provider value ={state,dispatch}/>와 같이 state와 dispatch를 모든 컴포넌트에 넘긴다. 그러면 각 컴포넌트의 dispatch를 통해 _app.jsx에 있는 reducer가 실행된다.

pages>store>context.jsx

[context.jsx]
import {createContext} from 'react'

export const initialState ={
    IsLogin:false,
}

const Store = createContext(initialState)

export default Store

pages>store>reducer.jsx

export const reducer =(state,action)=>{
    switch(action.type){
        case "LoGIN":
            return state;
        case "LOGOUT":
            return {
                        ...state,
                        IsLogin:false
                    }
        case "":
            return state
        default:
            return state;
    }
}


이렇게 설정하고 login화면에서 값을 넘겨준다.

 userid.value==="nara7875" && userpw.value ==="1234"
    ? Router.push('/')
    :alert('아이디와 패스워드가 다릅니다')
}

c.f
Router.push('/'): 내가 원하는 경로로 이동

좋은 웹페이지 즐겨찾기