본문 바로가기

Next

next13 회원가입기능 만들어서 사용하는법

회원가입 폴더와 페이지를 하나 만든다.

예시.

const page = () => {
    return (
    <div>
        <form method="POST" action="/api/auth/signup">
          <input name="name" type="text" placeholder="이름" /> 
          <input name="email" type="text" placeholder="이메일" />
          <input name="password" type="password" placeholder="비번" />
          <button type="submit">id/pw 가입요청</button>
        </form>
    </div>
    );
};

export default page;

이제 서버로 보내야하니

api 폴더 어쓰(auth) 폴더안에 회원가입 기능서버 파일을 하나만든다.

 

이제 데이터를 보내야하는데 비밀번호를 암호화해서 해야한다.

암호화를 도와줄 라이브러리를 설치해야한다.

터미널에서.

npm install bcrypt

입력후 설치. (타입스크립트 사용한다면 type bcrypt 도 설치해야함 검색 참고. )

 

위에 하기전이나 그후에

api 넥스트 어쓰 파일안에

import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from 'bcrypt';

임포트 코드추가후

이런식으로 코드추가

export const authOptions = {
    providers: [
        GithubProvider({
            clientId: '909daa5bb09113cc30ac',
            clientSecret: '662e3829bf651da1863f6ef8410f85e54ab04e74',
        }),

        CredentialsProvider({
            //1. 로그인페이지 폼 자동생성해주는 코드 
            name: "credentials",
            credentials: {
                email: { label: "email", type: "text" },
                password: { label: "password", type: "password" },
                //폼을 하나더 만들고싶다면 위코드형식에 맞춰서 더만들면된다.
            },

            //2. 로그인요청시 실행되는코드
            //직접 DB에서 아이디,비번 비교하고 
            //아이디,비번 맞으면 return 결과, 틀리면 return null 해야함
            async authorize(credentials) {
                let db = (await connectDB).db('forum');
                let user = await db.collection('user_cred').findOne({ email: credentials.email })
                if (!user) {
                    console.log('해당 이메일은 없음');
                    return null
                }
                const pwcheck = await bcrypt.compare(credentials.password, user.password);
                if (!pwcheck) {
                    console.log('비번틀림');
                    return null
                }
                return user
            }
        })
    ],
    //3. jwt 써놔야 잘됩니다 + jwt 만료일설정
    session: {
        strategy: 'jwt',
        maxAge: 30 * 24 * 60 * 60 //30일
    },


    callbacks: {
        //4. jwt 만들 때 실행되는 코드 
        //user변수는 DB의 유저정보담겨있고 token.user에 뭐 저장하면 jwt에 들어갑니다.
        jwt: async ({ token, user }) => {
            if (user) {
                token.user = {};
                token.user.name = user.name
                token.user.email = user.email
            }
            return token;
        },
        //5. 유저 세션이 조회될 때 마다 실행되는 코드
        session: async ({ session, token }) => {
            session.user = token.user;
            return session;
        },
    },

    secret: 'zing105harim',
    adapter: MongoDBAdapter(connectDB)
};

Q. 관리자는 모든 글을 수정/삭제 가능하게 만드는법?

데이터베이스 가입시킬때 등급을 하나 추가한다 예시로 role 항목에 normal(일반유저) admin(관리자) 가입

유저정보에 role: 'admin' 으로 되어있으면 항상 모든 글을 수정/삭제 할수있게 하기. 

 

더보기

아이디/비번 방식으로 로그인하고 싶다면 

Next-auth 라이브러리 설정에서 Credentials provider를 선택하면 됩니다. 

다만 이 경우 session 방식 말고 강제로 JWT 방식만 사용하도록 셋팅되어있습니다.

라이브러리 만든 사람 말로는 "개발자가 직접 아이디/비번 관리시 위험하니 사용못하게 막아놨다"는데 이상한 논리군요.

근데 찾아보면 next-auth 아이디/비번 + session 방식 쓰는 방법도 검색해보면 몰래 나오긴 하는데 해보진 않았으니 

일단 아이디/비번 방식 회원기능 + JWT를 만들어봅시다. 

 

우선 회원가입을 시켜야 로그인도 할 것 아닙니까. 회원가입 페이지와 기능을 만들어봅시다.

1. 회원가입 페이지에서 유저가 아이디/비번을 서버로 제출하면

2. 서버는 그걸 DB에 저장

이게 회원가입 기능 끝 아닙니까

그대로 구현해봅시다. 

 

 

 

 

1. 회원가입페이지에서 유저가 아이디/비번 제출하면 

 

 

(app/register/page.js)

export default function Register() {
  return (
    <div>
        <form method="POST" action="/api/auth/signup">
          <input name="name" type="text" placeholder="이름" /> 
          <input name="email" type="text" placeholder="이메일" />
          <input name="password" type="password" placeholder="비번" />
          <button type="submit">id/pw 가입요청</button>
        </form>
    </div>
  )
}

전 회원가입페이지를 /register 경로로 만들어봤습니다.

그럼 이제 누가 /register 접속시 폼이 보이겠군요. 

 

요즘은 아이디 말고 이메일 쓰는게 유행입니다.

OAuth로 로그인시에도 유저 아이디는 없고 이메일만 나와서 

이메일 쓰는건 일종의 일관성 유지 용도도 있습니다. 

 

 

 

 

 

2. 서버는 그걸 DB에 저장 

 

npm install bcrypt

비번은 DB에 암호화해서 저장하는게 좋기 때문에 암호화 라이브러리 하나만 설치합시다. 

 

 

 

(pages/api/auth/signup.js)

import { connectDB } from "@/util/database";
import bcrypt from "bcrypt";

export default async function handler(요청, 응답) {
  if (요청.method === "POST") {
      const hash = await bcrypt.hash(요청.body.password, 10);
      요청.body.password = hash;

      let db = (await connectDB).db('forum');
      await db.collection('user_cred').insertOne(요청.body);
      응답.status(200).json('성공');
  }
}; 

폼으로 전송된 유저정보를 DB에 저장하는 서버기능을 만들어놨습니다. 

(1) bcrypt.hash 안에 패스워드 넣으면 암호화해줍니다. 10은 암호화정도인데 맘대로 수정가능

(2) 그리고 유저정보를 object자료로 만들어서 user_cred 컬렉션에 넣으라고 했습니다. 

 

진짜 되는지 가입해보고 DB에 저장 잘 되나 확인합시다. 

 

 

 

 

 

Credentials provider 설정하기

 

Next-auth 설정에 Credentials provider도 설정해놓으면 아이디/비번 로그인이 가능합니다. 

그대로 붙여넣기합시다.  

 

import { connectDB } from "@/util/database";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from 'bcrypt';

export const authOptions = {
  providers: [
    GithubProvider({
      clientId: 'Github에서 발급받은 ID',
      clientSecret: 'Github에서 발급받은 Secret',
    }),

    CredentialsProvider({
      //1. 로그인페이지 폼 자동생성해주는 코드 
      name: "credentials",
        credentials: {
          email: { label: "email", type: "text" },
          password: { label: "password", type: "password" },
      },

      //2. 로그인요청시 실행되는코드
      //직접 DB에서 아이디,비번 비교하고 
      //아이디,비번 맞으면 return 결과, 틀리면 return null 해야함
      async authorize(credentials) {
        let db = (await connectDB).db('forum');
        let user = await db.collection('user_cred').findOne({email : credentials.email})
        if (!user) {
          console.log('해당 이메일은 없음');
          return null
        }
        const pwcheck = await bcrypt.compare(credentials.password, user.password);
        if (!pwcheck) {
          console.log('비번틀림');
          return null
        }
        return user
      }
    })
  ],

  //3. jwt 써놔야 잘됩니다 + jwt 만료일설정
  session: {
    strategy: 'jwt',
    maxAge: 30 * 24 * 60 * 60 //30일
  },


  callbacks: {
    //4. jwt 만들 때 실행되는 코드 
    //user변수는 DB의 유저정보담겨있고 token.user에 뭐 저장하면 jwt에 들어갑니다.
    jwt: async ({ token, user }) => {
      if (user) {
        token.user = {};
        token.user.name = user.name
        token.user.email = user.email
      }
      return token;
    },
    //5. 유저 세션이 조회될 때 마다 실행되는 코드
    session: async ({ session, token }) => {
      session.user = token.user;  
      return session;
    },
  },

  adapter: MongoDBAdapter(connectDB),
  secret: 'qwer1234'  
};
export default NextAuth(authOptions); 

라이브러리 사용법이기 때문에 굳이 외우고 공부할 필요없이 그대로 복붙해서 사용하면 됩니다.

그냥 어떤 용도의 코드인지 잘 살펴보면 되겠습니다.

가장 중요한건 2번인데

로그인시 아이디/비번을 DB에 저장된 내용과 비교하는건 여러분이 직접 해야하니까 

여러분 DB 상황에 맞게 수정해서 쓰도록 합시다. 

 

 

 

 

 

 

.env 파일 사용하려면

 

.js파일을 공유하거나 어디 올리는 일이 잦을텐데

.js파일들이 털리면 여러분들 코딩인생도 끝이 나기 때문에 

소스코드 곳곳에 있는 암호나 중요한 문자열들은 다른 파일로 빼놓는 것도 좋습니다. 

 

 

NEXTAUTH_SECRET='secretkey1234' 

.env파일을 프로젝트폴더에 하나 생성해서

작명1=보관할중요한문자1

작명2=보관할중요한문자2

...

이렇게 적어놓습니다. 

 

([...nextauth].js 파일 일부)

adapter: MongoDBAdapter(connectDB),
secret: process.env.NEXTAUTH_SECRET 

그리고 필요한 js 파일 가서 process.env.작명1 이라고 사용하면

.env 파일에 있던 작명1에 담아놨던 데이터가 그 자리에 남습니다.

 

 

 

 

 

오늘의 응용사항 :

Q1. 회원가입 시켜주기 전에 이름, 이메일, 비번란에 빈칸을 보내는 경우 가입을 거절해봅시다.

 

Q2. 회원가입 시켜주기 전에 같은 이메일이 이미 DB에 있는지 조회부터 해보고 

같은 이메일이 이미 DB에 있으면 가입을 거절해봅시다.

 

Q3. /write 페이지는 로그인한 사람만 보여주려면? 

 

Q4. 관리자 권한을 가진 유저는 모든 글을 삭제가능하게 서버기능을 업그레이드해봅시다.