full stack mysql, node js express,react

JS FULL STACK 11장 Todo 회원가입/로그인 API 추가하기 jsonwebtoken 라이브러리

우주전사버즈 2024. 2. 6. 14:47

왜 추가했지?

내가 만든 Todo app은 회원가입을하고 로그인을해서 각 회원마다 어울리는 Todo화면을 보여준다는 컨셉인데

이전에 작성한 API로는 회원가입 / 로그인 요청이 올바르게 이루어지지 않고있었다.

 

회원가입(./register) 로그인(./login)과 같이 직접적인 post요청을 통해 처리하는 API가 작성이 되있지않아서 추가해줬다.

그리고 이 과정에서 jwt 토큰에 대해서 알게되었다. 



백엔드 코드( express todo crud API)

 

GitHub - MkBaek0229/express_Todo_2024-01

Contribute to MkBaek0229/express_Todo_2024-01 development by creating an account on GitHub.

github.com

 

 

회원가입 API

// 회원가입 API
app.post("/signup", async (req, res) => {
  const { username, password, callnum } = req.body;

  // 필수 정보 검증
  if (!username || !password || !callnum) {
    res.status(400).json({
      resultCode: "F-1",
      msg: "사용자 이름, 비밀번호, 전화번호를 모두 입력해주세요",
    });
    return;
  }

  try {
    // 사용자 등록
    const { rows } = await pool.query(
      `
      INSERT INTO member (name, password, callnum) VALUES ($1, $2, $3)
      RETURNING id;
      `,
      [username, password, callnum]
    );

    res.json({
      resultCode: "S-1",
      msg: "회원가입이 완료되었습니다",
      data: {
        userId: rows[0].id,
        username,
      },
    });
  } catch (error) {
    console.error("에러 발생:", error);
    // 중복된 사용자 이름 또는 다른 오류에 대한 처리
    if (error.code === 'ER_DUP_ENTRY') {
      res.status(409).json({
        resultCode: "F-1",
        msg: "이미 존재하는 사용자 이름입니다",
      });
    } else {
      res.status(500).json({
        resultCode: "F-1",
        msg: "서버 에러",
      });
    }
  }
});

로그인 API

// 로그인 API
app.post("/login", async (req, res) => {
  const { username, password } = req.body;

  // 필수 정보 검증
  if (!username || !password) {
    res.status(400).json({
      resultCode: "F-1",
      msg: "사용자 이름과 비밀번호를 모두 입력해주세요",
    });
    return;
  }

  try {
    // 사용자 인증
    const { rows } = await pool.query(
      `
      SELECT id, name
      FROM member
      WHERE name = $1 AND password = $2;
      `,
      [username, password]
    );

    // 로그인 실패 시
    if (rows.length === 0) {
      res.status(401).json({
        resultCode: "F-1",
        msg: "사용자 이름 또는 비밀번호가 올바르지 않습니다",
      });
      return;
    }

    // 로그인 성공 시
    const userId = rows[0].id;
    const token = generateToken(userId); // 토큰 생성 함수 호출

    res.json({
      resultCode: "S-1",
      msg: "로그인 성공",
      data: {
        userId,
        username,
        token,
      },
    });
  } catch (error) {
    console.error("에러 발생:", error);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 에러",
    });
  }
});

 

 

Token? 

서버 :"내가 직접 싸인해둔 이 입장권 티켓을 너에게 줄테니 너의 계정으로 로그인을하고 서비스를 이용 하고싶다면 이 입장권을 나에게 건내주도록해"

대충 입장권 티켓같은것이라고 할 수 있다. 이러한 token을 명확하게 알기위해서는 쿠키,세션과 같은 지식 또한 필요한데 이에 대한 게시글을 따로 작성해봐야할 것 같다.

// 로그인 성공 시
    const userId = rows[0].id;
    const token = generateToken(userId); // 토큰 생성 함수 호출

로그인 생성시 토큰 생성 함수를 호출한다고 되어있다.

 

generateToken 함수

import jwt from 'jsonwebtoken';

const generateToken = (userId) => {
  // JWT 생성
  const token = jwt.sign({ userId }, 'your-secret-key', { expiresIn: '1h' });
  return token;
};

이를 사용하기 위해 jsonwebtoken이라는 함수를 설치해주어야했다. 

 

그리고 토큰을 발급받기 위해 sign()함수를 사용하여 발급받는데

1번째인자인 { userId }, 의 경우 토큰에 담을 json 데이터 

2번째인자인  'your-secret-key' 의 경우 key로 토큰을 검증할시에 필요로하고 있다.

3번째인자인  { expiresIn: '1h' }인 경우 토큰이 유효한 시간이 1시간이라는 뜻이다. 

// 로그인 성공 시
    const userId = rows[0].id;
    const token = generateToken(userId); // 토큰 생성 함수 호출

    res.json({
      resultCode: "S-1",
      msg: "로그인 성공",
      data: {
        userId,
        username,
        token,
      },
    });

 

로그인에 성공하였을시 발급된 토큰을 함께 반환하면 

클라이언트 측은 로그인이 되있고 토큰이 존재할경우에 해당 계정에맞는 todo를 보여준다

// TodoPage.js

import React, { useState, useEffect } from "react";
import TodoWrite from "../TodoWrite";
import TodoList from "../TodoList";
import TodoTemplate from "../TodoTemplate";
import axios from "axios";

// TodoPage 컴포넌트 정의
function TodoPage({ username }) {
  // 할 일 목록을 관리하는 상태 변수
  const [todos, setTodos] = useState([]);

  // 컴포넌트가 처음 마운트될 때 실행되는 useEffect 훅
  useEffect(() => {
     // 로그인 상태 확인 및 토큰 가져오기
     const token = localStorage.getItem('token');
     
      if (token) {
      // 토큰이 존재하면 서버에 요청
      getTodos(token);
    } else {
      console.log("사용자 인증 실패: 토큰이 없습니다.");
    }
  }, []);

  // 서버에서 할 일 목록을 가져오는 비동기 함수
  const getTodos = async (token) => {
    try {
      // 서버에서 할 일 목록을 가져온 후 상태 업데이트
      const response = await axios.get(`https://todoapp-spring-brook-5982-little-grass-565-silent-shape-3149.fly.dev/${username}/todos`,
      { headers: { Authorization: `Bearer ${token}` }}
      );
      setTodos(response.data.data);
    } catch (error) {
      // 에러가 발생한 경우 콘솔에 에러 메시지 출력
      console.error("할 일 목록을 불러오는 중 에러 발생:", error);
    }
  };

  // TodoPage 컴포넌트의 렌더링 결과
  return (
    <div>
      {/* TodoTemplate을 이용하여 레이아웃 구성 */}
      <TodoTemplate username={username}>
        {/* 할 일을 작성하는 컴포넌트 */}
        <TodoWrite username={username} setTodos={setTodos} />
        {/* 할 일 목록을 표시하는 컴포넌트 */}
        <TodoList username={username} todos={todos} setTodos={setTodos} />
      </TodoTemplate>
    </div>
  );
}

export default TodoPage;

 

 

아직은 알듯말듯..

토큰을 발급받고 클라이언트측에서 토큰 인증 처리를 위해 어떻게 처리해야할지 아직 잘 모르겟다..

gpt의 도움을 받아 기능을 일면적으로 구현해보긴햇지만 완벽하다고할수없다.