Day4 - 회원가입&로그인

RINM·2022년 12월 30일

NextJS - Reddit Clone

목록 보기
4/9
post-thumbnail

CSS Framework - Tailwind CSS

npm i -D postcss-preset-env tailwindcs
npx tailwind init

tailwind.config.js

module.exports = {
  content: ['./src/**/*.tsx'],
  theme: {
    extend: {},
  },
  plugins: [],
}

PostCSS

Javascript 플러그인을 사용하여 CSS를 변환
postcss.config.js

module.exports={
    plugins: ['tailwindcss','postcss-preset-env']
}

global.css에 적용

@tailwind base;
@tailwind components;
@tailwind utilities;

classnames

npm install classnames --save

변수값에 따라 대상의 css를 컨트롤

classNames({'prop':true}) // 'prop'
classNames({'prop':false}) // ''

classNames(`border-gray-400`,{"border-red-500":error}) //error가 있을 때 빨간색으로

Sign up

Frontend

Axios

서버로부터 데이터를 가져오고 보낼때 사용하는 모듈
post

const res = await axios.post('/auth/register',{email,password, username,})

프론트의 entry point에서 Axio로 통신할 서버의 url을 지정(환경변수 이용)

Axios.defaults.baseURL=process.env.NEXT_PUBLIC_SERVER_BASE_URL+"/api";

Backend

express의 Router를 사용하여 해당 요청 받기

import { Request, Response, Router } from "express";

const register = async (req: Request,res:Response)=>{
    const {props} = req.body;  
}

const router=Router()
router.post('/register',register)

server.ts에서 해당 라우터를 받아 app에 연결

import authRoutes from "./routes/auth"
app.use("/api/auth",authRoutes)

CORS

통신하려는 데이터의 origin이 다를 때 허용하지 않는다. cors 모듈을 사용하여 클라이언트에서 외부의 요청을 받아오게 허용할 수 있다.

import cors from 'cors'

const origin = "http://localhost:3000"
app.use(cors({
    origin
}))

Login

Frontend

withCredentials: true

로그인시 쿠키로 인증 토큰을 발행하기 위한 설정
도메인 주소가 다르면 별도 설정 없이는 쿠키가 전송되지 않는다.
Backend에서는 cors 부분에서 credentials: true

const handleSubmit = async (e:FormEvent) =>{
        e.preventDefault();
        try {
            const res= await axios.post('/auth/login',{
                password,
                username,
            },{
                withCredentials: true,
            })
            console.log('res',res);
            router.push("/")
        } catch (error: any) {
            console.error(error);
            seterrors(error?.response.data||{});
        }
    }

Context

유저 정보는 Context에 담아서 이용한다. 여러개의 컴포넌트에서 동시에 필요한 경우가 많기 때문이다.

import { createContext } from "react";
import { User } from "../types";

interface State{
    authenticated: boolean;
    user: User | undefined;
    loading: boolean;
}

const StateContext = createContext<State>({
    authenticated: false,
    user: undefined,
    loading: true,
})

const DispatchContext = createContext<any>(null);

Context를 사용하기 위해서는 컴포넌트를 Context Provider로 감싸야한다.

export const AuthProvider = ({children}:{children: React.ReactNode}) =>{
    return(
        <DispatchContext value={dispatch}>
            <StateContext.Provider value={state}>{children}</StateContext.Provider>
        </DispatchContext>
    )
}
export const useAuthState = () => useContext(StateContext);
export const useAuthDispatch = () => useContext(DispatchContext);

//in _app.tsx
import { AuthProvider } from '../context/auth';

export default function App({ Component, pageProps }: AppProps) {
  Axios.defaults.baseURL=process.env.NEXT_PUBLIC_SERVER_BASE_URL+"/api";
  return <AuthProvider>
    <Component {...pageProps} />
  </AuthProvider>
}

UseReducer

useState 대신 사용하는 react 내장 함수
dispatch와 짝을 이루어 각 상황에 맞게 변수 값을 설정한다.

export const AuthProvider = ({children}:{children: React.ReactNode}) =>{
    const [state, defaultDispatch] = useReducer(reducer, {
        user: null,
        authenticated: false,
        loading: true
    })

    const dispatch = (type:string,payload?: any) =>{
        defaultDispatch({type,payload})
    }

    return(
        <DispatchContext.Provider value={dispatch}>
            <StateContext.Provider value={state}>{children}</StateContext.Provider>
        </DispatchContext.Provider>
    )
}

Backend

npm i jsonwebtoken dotenv cookie --save
npm i @types/jsonwebtoken @types/cookie --save-dev

Jsonwebtoken(JWT)

dotenv

백엔드에서 환경변수(.env)를 사용하기 위한 모듈

//compare password
const passwordMatches = await bcrypt.compare(password,user.password);
    
//wrong password
if(!passwordMatches) return res.status(401).json({password: "Wrong password"})
        
//issue token
const token = jwt.sign({username},process.env.JWT_SECRET)
    

전달 받은 쿠키를 사용자 브라우저에 저장

//save cookie
res.set("Set-Cookie",cookie.serialize("token",token,{
	httpOnly: true,
	secure: process.env.NODE_ENV === "development",
	sameSite:"strict",
	maxAge: 60 * 60 * 24 * 7, // 1 week
	path: "/"
}))
  • httpOnly: 클라이언트의 스크립트가 쿠키를 사용 못하게 설정 (document.cookie로 접근하는 것)
  • secure: HTTPS 연결 시에만 쿠키 사용 허용
  • sameSite: 요청이 외부 사이트에서 오는 경우 쿠키를 보내지 못하게 차단
  • expires/maxAge: 쿠키 유효시간(초)

0개의 댓글