
1) nodebird-api
//models/domain.js
class Domain extends Sequelize.Model {
static initiate(sequelize) {
Domain.init({
host: {
type: Sequelize.STRING(80),
allowNull: false,
},
type: {
type: Sequelize.ENUM('free', 'premium'),
allowNull: false,
},
// clientSecret은 UUID 타입으로 설정
clientSecret: {
type: Sequelize.UUID,
allowNull: false,
},
}, {
sequelize,
timestamps: true,
paranoid: true,
modelName: 'Domain',
tableName: 'domains',
});
}
static associate(db) {
db.Domain.belongsTo(db.User);
}
};
module.exports = Domain;
//middlewares/index.js
const jwt = require('jsonwebtoken');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
// 1. JWT 검증
exports.verifyToken = (req, res, next) => {
try {
// 요청 헤더의 토큰 검증
res.locals.decoded = jwt.verify(req.headers.authorization, process.env.JWT_SECRET);
return next();
} catch (error) {
if (error.name === 'TokenExpiredError') { //유효 기간 초과
// 유효 기간 만료 시 419 코드 반환
return res.status(419).json({ code: 419, message: '토큰이 만료되었습니다' });
}
return res.status(401).json({ code: 401, message: '유효하지 않은 토큰입니다' });
}
};
// 2. 사용량 제한
exports.apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1분
max: 10, // 허용 횟수
handler(req, res) {
// 제한 초과 시 429 코드 반환
res.status(this.statusCode).json({
code: this.statusCode,
message: '1분에 열 번만 요청할 수 있습니다.',
});
},
});
// 3. CORS
exports.corsWhenDomainMatches = async (req, res, next) => {
const domain = await Domain.findOne({
where: { host: new URL(req.get('origin')).host },
});
if (domain) {
cors({
origin: req.get('origin'),
credentials: true,
})(req, res, next);
} else {
next();
}
};//nodebird-api/routes/v1.js
const express = require('express');
const { renderLogin, createDomain } = require('../controllers');
const { isLoggedIn } = require('../middlewares');
const router = express.Router();
// POST /v1/token
router.post('/token', createToken);
// POST /v1/test
router.get('/test', verifyToken, tokenTest);
module.exports = router;// //nodebird-api/routes/v2.js
const express = require('express');
const { verifyToken, apiLimiter } = require('../middlewares');
const { createToken, tokenTest, getMyPosts, getPostsByHashtag } = require('../controllers/v2');
const router = express.Router();
router.use(corsWhenDomainMatches); // CORS
// POST /v2/token
router.post('/token', apiLimiter, createToken);
// POST /v2/test
router.get('/test', apiLimiter, verifyToken, tokenTest);
// GET /v2/posts/my
router.get('/posts/my', apiLimiter, verifyToken, getMyPosts);
// GET /v2/posts/hashtag/:title
router.get('/posts/hashtag/:title', apiLimiter, verifyToken, getPostsByHashtag);
module.exports = router;
2) nodecat
// controllers/index.js
const axios = require('axios');
const URL = process.env.API_URL;
axios.defaults.headers.origin = process.env.ORIGIN; // origin 헤더 추가
const request = async (req, api) => {
try {
if (!req.session.jwt) { // 세션에 토큰이 없으면
const tokenResult = await axios.post(`${URL}/token`, {
clientSecret: process.env.CLIENT_SECRET,
});
req.session.jwt = tokenResult.data.token; // 세션에 토큰 저장
}
return await axios.get(`${URL}${api}`, {
headers: { authorization: req.session.jwt },
}); // API 요청
} catch (error) {
if (error.response?.status === 419) { // 토큰 만료 시 토큰 재발급받기
delete req.session.jwt;
return request(req, api);
} // 419 외의 다른 에러이면
throw error;
}
};
exports.getMyPosts = async (req, res, next) => {
try {
const result = await request(req, '/posts/my');
res.json(result.data);
} catch (error) {
console.error(error);
next(error);
}
};
exports.searchByHashtag = async (req, res, next) => {
try {
const result = await request(
req, `/posts/hashtag/${encodeURIComponent(req.params.hashtag)}`,
);
res.json(result.data);
} catch (error) {
if (error.code) {
console.error(error);
next(error);
}
}
};
exports.renderMain = (req, res) => {
res.render('main', { key: process.env.CLIENT_SECRET });
};
jwt.sign({ id: 'nodebird' }, 'jwt-secret', { expiresIn: '1m' });
2.
cors({ origin: 'http://localhost:4000', credentials: true });
출처 : 조현영, 『 Node.js 교과서 개정 3판』, 길벗(2022)
Corner Node.js 2
Editor Arom
| [Node.js 2팀] 9장 익스프레스로 SNS 서비스 만들기 (0) | 2025.11.28 |
|---|---|
| [Node.js 2팀] 8장. 몽고디비 (0) | 2025.11.21 |
| [Node.js 2팀] 7장. MySQL (1) | 2025.11.14 |
| [Node.js 2팀] 5장. 패키지 매니저~ 6장. 익스프레스 웹 서버 만들기 (0) | 2025.11.07 |
| [Node.js 2팀] 3장. 노드 기능 알아보기 ~ 4장. http 모듈로 서버 만들기 (0) | 2025.10.31 |