JS FULL STACK 11장 Todo 회원가입/로그인 API 추가하기 jsonwebtoken 라이브러리
왜 추가했지?
내가 만든 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의 도움을 받아 기능을 일면적으로 구현해보긴햇지만 완벽하다고할수없다.