bcryptjs
비밀번호를 안전하게 암호화(해시화) 하기 위해 사용하는 JavaScript 라이브러리
- 비밀번호 해시 생성 (Hashing)
- 비밀번호 검증 (Compare) – 입력된 평문 비밀번호와 저장된 해시가 일치하는지 확인
- Salt 사용 – 동일한 비밀번호라도 매번 다른 해시 결과를 만듬 (보안 강화)
설치
npm i bcryptjs
회원가입 시, 비밀번호 암호화
import bcrypt from "bcryptjs";
const saltRounds = 10; // salt 길이
app.post("/register", async (req, res) => {
const { userName, password } = req.body;
// userModel 에서 이미 존재하는 사용자인지 확인
const existingUser = await userModel.findOne({ userName });
if (existingUser)
return res.status(409).json({ error: "이미 존재하는 사용자입니다." });
// 새 사용자 생성
const userDoc = new userModel({
userName,
password: bcrypt.hashSync(password, saltRounds), // 비밀번호를 암호화(해시)
});
// DB에 저장
const savedUser = await userDoc.save();
// 저장 성공 시, 응답 메시지 전송
res
.status(200)
.json({ user: { userName: savedUser.userName, _id: savedUser._id } });
});
로그인 시, 비밀번호 검증
app.post("/login", async (req, res) => {
const { userName, password } = req.body;
const userDoc = await userModel.findOne({ userName });
if (!userDoc) return res.status(401).json({ error: "없는 사용자입니다." });
// 비밀번호 확인(암호 해독)
const passOk = bcrypt.compareSync(password, userDoc.password);
if (!passOk) return res.status(401).json({ error: "비밀번호가 틀렸습니다." });
else {};
}
JWT 토큰
웹에서 사용자 인증과 정보 전달을 위해 사용하는 토큰 기반 인증 방식
- 로그인한 사용자임을 증명하거나, 서버 간 안전하게 데이터를 교환할 때 사용
설치
npm install jsonwebtoken
JWT 토큰 발급
import jwt from "jsonwebtoken";
// 환경 변수로 관리 필요
const secretKey = "test"; // JWT 서명에 사용할 비밀 키
const tokenLife = "1h"; // JWT의 만료 시간 설정
app.post("/login", async (req, res) => {
// ...중략
else {
// JWT 토큰 발급
const { _id, userName } = userDoc;
const payload = { id: _id, userName };
const token = jwt.sign(payload, secretKey, {
expiresIn: tokenLife,
});
// 쿠키에 저장
res
.cookie("token", token, {
// 보안 설정
httpOnly: true, // JavaScript에서 쿠키 접근 불가 (XSS 방지)
secure: process.env.NODE_ENV === "production", // 환경에 따른 쿠키 보안 설정, 배포 환경에서만 true 설정
sameSite: "Strict", // 동일 출처에서만 쿠키 전송 (CSRF 방지)
maxAge: 1000 * 60 * 60, // 쿠키 만료 시간(ms)
})
.json({ user: { id: userDoc._id, userName } });
}
}
헤더 로그인 상태 유지
1. 서버 - 로그인 성공 시 JWT 토큰을 HttpOnly 쿠키에 저장
res
.cookie("token", token, {
// 보안 설정
httpOnly: true, // JavaScript에서 쿠키 접근 불가 (XSS 방지)
secure: process.env.NODE_ENV === "production", // 환경에 따른 쿠키 보안 설정, 배포 환경에서만 true 설정
sameSite: "Strict", // 동일 출처에서만 쿠키 전송 (CSRF 방지)
maxAge: 1000 * 60 * 60, // 쿠키 만료 시간(ms)
})
.json({ user: { id: userDoc._id, userName } });
2. API 요청 시 브라우저가 자동으로 Authorization 헤더 없이 해당 쿠키를 요청에 포함
📍 CORS 설정 필요
- 서버에서 credentials: true, origin 설정
app.use(cors({ credentials: true, origin: "http://localhost:5173" })); // 쿠키에 포함한 요청을 허용
- 클라이언트에서 withCredentials: true를 설정
axios.defaults.withCredentials = true // 모든 요청에 대해 withCredentials 설정
3. 클라이언트 - 쿠키에 저장된 인증 정보를 기반으로 로그인된 사용자의 정보 요청
// userApi.js
export const getUserProfile = async () => {
const res = await axios.get(`${API_URL}/profile`)
return res.data
}
4. 서버 - 쿠키에서 JWT 토큰 추출
쿠키에 저장된 JWT 토큰을 cookie-parser 미들웨어를 사용하여 추출하고 인증 확인
- 설치
npm i cookie-parser
- 쿠키에서 JWT 토큰 추출
import cookieParser from "cookie-parser";
app.use(cookieParser()); // 쿠키 파싱을 위한 미들웨어 추가
// 회원 정보 조회
app.get("/profile", (req, res) => {
const token = req.cookies.token; // 쿠키에서 JWT 토큰 추출
if (!token) {
return res.status(401).json({ error: "로그인이 필요합니다." });
}
// 토큰이 변조되었거나 만료되었는지 검사
jwt.verify(token, secretKey, (err, info) => {
if (err) return res.json({ error: "로그인이 필요합니다." });
res.json(info); // 정상이라면 payload를 복호화해서 callback의 두 번째 인자로 넘겨줌
});
});
5. 클라이언트 - 로그인한 사용자 정보 전역 상태 관리
- Redux Toolkit을 사용해서 user 상태를 관리하는 slice 정의
// userSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const user = createSlice({
name: 'user',
initialState: {
user: '',
},
reducers: {
setUserInfo: (state, action) => {
state.user = action.payload
},
},
})
export const { setUserInfo } = user.actions
- 로그인한 사용자 정보를 가져와 Redux에 저장하고, UI에 반영
// Header.jsx
export const Header = () => {
const dispatch = useDispatch()
const user = useSelector(state => state.user.user)
const userName = user?.userName
useEffect(() => {
const getProfile = async () => {
try {
const userData = await getUserProfile()
dispatch(setUserInfo(userData))
} catch {
dispatch(setUserInfo(''))
}
}
getProfile()
}, [dispatch, userName])
return (
// ... 생략
)
}
로그아웃 처리
서버
app.post("/logout", (req, res) => {
res
.cookie("token", "", {
httpOnly: true,
expires: new Date(0), // 쿠키 만료 시간을 0으로 설정하여 삭제
})
.json({ message: "로그아웃 되었습니다." });
});
클라이언트
// userApi.js
export const logoutUser = async () => {
const response = await axios.post(`${API_URL}/logout`)
return response.data
}
// Header.jsx
const handleLogout = async () => {
try {
await logoutUser()
dispatch(setUserInfo(''))
} catch (err) {
console.log(err)
}
}
react-quill
React에서 WYSIWYG(What You See Is What You Get) 에디터를 구현할 때 사용하는 리치 텍스트 에디터 라이브러리. 즉, "글 작성기"를 만들어주는 도구
- 글씨 크기, 색상, 볼드/이탤릭, 리스트 등 서식 있는 텍스트 작성 가능
- 이미지, 링크, 코드블럭 등 포맷팅 요소 지원
- HTML 형태로 데이터 저장 가능 ➡️ DB에 저장하거나 미리보기 가능
- 커스터마이징 가능 (툴바, 테마 등)
- react-quill-new는 기존 react-quill의 개선된 버전으로, React 19와 함께 사용할 수 있도록 호환성 문제를 해결한 패키지
npm install react-quill-new
dotenv
Node.js 환경에서 환경 변수를 .env 파일에 정의하고, 이를 process.env를 통해 사용할 수 있도록 도와주는 환경 변수 로딩 라이브러리
npm install dotenv
// index.js
import dotenv from "dotenv";
dotenv.config();
const port = process.env.PORT || 4000; // 포트 번호
const saltRounds = Number(process.env.SALT_ROUND); // salt 길이
const secretKey = process.env.JWT_SCR_KEY; // JWT 서명에 사용할 비밀 키
const tokenLife = process.env.SALT_ROUND; // JWT의 만료 시간 설정
728x90
반응형
'LG 유플러스 유레카 SW > React' 카테고리의 다른 글
[#67] Multer 파일 업로드 + 게시글 작성/조회 (0) | 2025.05.02 |
---|---|
[#65] React + Express + MongoDB 연동하기 (feat. 회원가입) (2) | 2025.04.30 |
[#64] TanStack Query(React Query) 사용해보기 (0) | 2025.04.29 |
[#63] 날씨 오픈 API 연동 (1) | 2025.04.28 |
[#62] 용돈 기입장 프로젝트 (feat. React) (0) | 2025.04.28 |