From ed4dd9dfd1be095fbf3d23c393f417094b5f2723 Mon Sep 17 00:00:00 2001 From: yjhss <20211420@sungshin.ac.kr> Date: Fri, 14 Feb 2025 13:05:16 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=20=EA=B2=80=EC=A6=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/auth/signup/signupController.js | 7 +- src/domains/auth/signup/signupValidation.js | 83 +++++++++++++-------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/src/domains/auth/signup/signupController.js b/src/domains/auth/signup/signupController.js index b8dd0bb..d231c88 100644 --- a/src/domains/auth/signup/signupController.js +++ b/src/domains/auth/signup/signupController.js @@ -14,10 +14,13 @@ const signupController = [ const errors = validationResult(req); if (!errors.isEmpty()) { const statusCode = 400; - logger.warn(`(${statusCode}) Validation failed: ${errors.array().map(err => err.msg).join(', ')}`); // 검증 실패 로깅 - return res.status(400).json({ message: errors.array().map(err => err.msg).join(', ') }); + logger.warn(`(${statusCode}) Signup validation failed: ${errors.array().map(err => err.msg).join(', ')}`); // 검증 실패 로깅 + return res.status(400).json({ + "Invalid values": errors.array().map(err => ({ message: err.msg })) + }); } + // gender 값 변환 let gender = req.body.gender; if (gender === '남성') gender = 'm'; diff --git a/src/domains/auth/signup/signupValidation.js b/src/domains/auth/signup/signupValidation.js index 5f91841..5d1234a 100644 --- a/src/domains/auth/signup/signupValidation.js +++ b/src/domains/auth/signup/signupValidation.js @@ -3,43 +3,60 @@ const { prisma } = require('../../../config/db'); const signupValidation = [ body('username') - .isString() - .notEmpty() - .withMessage('Username is required.') - .isLength({ min: 5, max: 20 }) - .withMessage('Username must be between 5 and 20 characters.') - .custom(async (value) => { - // Prisma를 사용해 username 중복 확인 - const existingUser = await prisma.user.findUnique({ - where: { username: value }, - }); - - if (existingUser) { - throw new Error('Username already exists. Please choose another.'); - } - }), + .notEmpty().withMessage('Username is required.') + .isString().withMessage('Username must be a string.') + .isLength({ min: 5, max: 20 }).withMessage('Username must be between 5 and 20 characters.') + .custom(async (value) => { + const existingUser = await prisma.user.findUnique({ + where: { username: value }, + }); + if (existingUser) { + throw new Error('Username already exists. Please choose another.'); + } + }), body('password') - .isString() - .notEmpty() - .withMessage('Password is required.') - .isLength({ min: 8 }) - .withMessage('Password must be at least 8 characters long.'), - - body('gender') - .isIn(['남성', '여성', '비공개']) - .withMessage('Gender must be either 남성, 여성, or 비공개.'), - - body('birthDate') - .matches(/^\d{4}-\d{2}-\d{2}$/) // YYYY-MM-DD 형식을 검증하는 정규 표현식 - .withMessage('Birthdate must be a valid date in YYYY-MM-DD format.'), + .notEmpty().withMessage('Password is required.') + .custom((value) => { + if (value && value.length < 8) { + throw new Error('Password must be at least 8 characters long.'); + } + return true; + }), body('name') - .isString() - .notEmpty() - .withMessage('Name is required.') - .isLength({ max: 50 }) - .withMessage('Name must not exceed 50 characters.') + .custom((value) => { + if (!value) { + throw new Error('Name is required.'); + } + if (typeof value !== 'string') { + throw new Error('Name must be a string.'); + } + if (value.length > 50) { + throw new Error('Name must not exceed 50 characters.'); + } + return true; + }), + + body('gender') + .notEmpty().withMessage('Gender is required.') + .custom((value) => { + if (value && !['남성', '여성', '비공개'].includes(value)) { + throw new Error('Gender must be either 남성, 여성, or 비공개.'); + } + return true; + }), + + body('birthDate') + .custom((value) => { + if (!value) { + throw new Error('Birthdate is required.'); + } + if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { + throw new Error('Birthdate must be a valid date in YYYY-MM-DD format.'); + } + return true; + }), ]; module.exports = { signupValidation }; From 467f198455cb41552d1ce2b9fddeeddc23e2b5d1 Mon Sep 17 00:00:00 2001 From: yjhss <20211420@sungshin.ac.kr> Date: Fri, 14 Feb 2025 18:55:07 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20gender=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/auth/signup/signupController.js | 28 +++++++++----------- src/domains/auth/signup/signupDto.js | 20 ++++++++++++-- src/domains/auth/signup/signupRepository.js | 17 ++++++------ src/domains/auth/signup/signupService.js | 29 +++++++++++---------- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/domains/auth/signup/signupController.js b/src/domains/auth/signup/signupController.js index d231c88..70ab9ae 100644 --- a/src/domains/auth/signup/signupController.js +++ b/src/domains/auth/signup/signupController.js @@ -2,6 +2,7 @@ const signupService = require('./signupService'); const { signupValidation } = require('./signupValidation'); const logger = require('../../../config/logger'); const { validationResult } = require('express-validator'); +const UserDto = require('./signupDto'); const signupController = [ ...signupValidation, // 유효성 검사 미들웨어 추가 @@ -14,33 +15,28 @@ const signupController = [ const errors = validationResult(req); if (!errors.isEmpty()) { const statusCode = 400; - logger.warn(`(${statusCode}) Signup validation failed: ${errors.array().map(err => err.msg).join(', ')}`); // 검증 실패 로깅 + logger.warn(`(${statusCode}) user validation failed: ${errors.array().map(err => err.msg).join(', ')}`); // 검증 실패 로깅 return res.status(400).json({ "Invalid values": errors.array().map(err => ({ message: err.msg })) }); } - - // gender 값 변환 - let gender = req.body.gender; - if (gender === '남성') gender = 'm'; - if (gender === '여성') gender = 'f'; - if (gender === '비공개') gender = 'n'; - - const signupData = { - ...req.body, - gender, - }; + const userData = new UserDto(req.body.username, req.body.password, req.body.name, req.body.gender, req.body.birthDate); // 회원가입 처리 - const user = await signupService.signupUser(signupData); + const user = await signupService.createUser(userData); logger.info(`User created successfully: ID=${user.id}, username=${user.username}`); // 성공 로깅 - const { password, ...userWithoutPassword } = user; - return res.status(201).json(userWithoutPassword); + const responseUser = { + ...user, + gender: UserDto.convertGenderToKorean(user.gender), + password: undefined + }; + + return res.status(201).json(responseUser); } catch (error) { const statusCode = 500; - logger.error(`(${statusCode}) Signup failed: ${error.message}`); // 오류 로깅 + logger.error(`(${statusCode}) Signup failed: ${error.message}\nStack trace: ${error.stack}`); return res.status(500).json({ message: '서버 오류', error: error.message }); } } diff --git a/src/domains/auth/signup/signupDto.js b/src/domains/auth/signup/signupDto.js index b9bd992..5bceb1e 100644 --- a/src/domains/auth/signup/signupDto.js +++ b/src/domains/auth/signup/signupDto.js @@ -1,4 +1,4 @@ -class SignupDto { +class signupDto { constructor(username, password, name, gender, birthDate) { this.username = username; this.password = password; @@ -6,6 +6,22 @@ class SignupDto { this.gender = gender; this.birthDate = birthDate; } + + // DB에 저장할 때 + static convertGenderToEnglish(gender) { + if (gender === '남성') return 'm'; + if (gender === '여성') return 'f'; + if (gender === '비공개') return 'n'; + return gender; // 유효하지 않은 값은 그대로 반환 + } + + // DB에서 값을 가져올 때 + static convertGenderToKorean(gender) { + if (gender === 'm') return '남성'; + if (gender === 'f') return '여성'; + if (gender === 'n') return '비공개'; + return gender; // 유효하지 않은 값은 그대로 반환 + } } -module.exports = SignupDto; \ No newline at end of file +module.exports = signupDto; \ No newline at end of file diff --git a/src/domains/auth/signup/signupRepository.js b/src/domains/auth/signup/signupRepository.js index fb8e291..3f0b431 100644 --- a/src/domains/auth/signup/signupRepository.js +++ b/src/domains/auth/signup/signupRepository.js @@ -1,26 +1,25 @@ const { prisma } = require('../../../config/db'); -const SignupDto = require('./signupDto'); +const signupDto = require('./signupDto'); const logger = require('../../../config/logger'); -const createUser = async (SignupDto) => { +const createUser = async (signupDto) => { try { - const birthDateObject = new Date(SignupDto.birthDate); + const birthDateObject = new Date(signupDto.birthDate); const user = await prisma.user.create({ data: { - username: SignupDto.username, - password: SignupDto.password, - name: SignupDto.name, - gender: SignupDto.gender, - //birthDate: SignupDto.birthDate, + username: signupDto.username, + password: signupDto.password, + name: signupDto.name, + gender: signupDto.gender, + //birthDate: signupDto.birthDate, //birthDate: birthDateObject, birthDate: birthDateObject.toISOString().split('T')[0], }, }); return user; } catch (error) { - console.error('Error stack trace:', error.stack); // 에러 스택을 콘솔에 출력 logger.error(`createUser failed: ${error.message}`); throw new Error('createUser 실패'); } diff --git a/src/domains/auth/signup/signupService.js b/src/domains/auth/signup/signupService.js index 3833c1f..2b8cbf3 100644 --- a/src/domains/auth/signup/signupService.js +++ b/src/domains/auth/signup/signupService.js @@ -1,23 +1,24 @@ const bcrypt = require('bcryptjs'); const signupRepository = require('./signupRepository'); -const SignupDto = require('./signupDto'); +const signupDto = require('./signupDto'); -const signupUser = async (signupData) => { +const createUser = async (userData) => { // 비밀번호 해싱 - const hashedPassword = await bcrypt.hash(signupData.password, 10); + const hashedPassword = await bcrypt.hash(userData.password, 10); - const signupDto = new SignupDto( - signupData.username, - hashedPassword, - signupData.name, - signupData.gender, - signupData.birthDate - ); + const gender = signupDto.convertGenderToEnglish(userData.gender); - // 유저 생성 - const newUser = await signupRepository.createUser(signupDto); + // DB에 저장할 객체 생성 + const newUser = { + username: userData.username, + password: hashedPassword, + name: userData.name, + gender: gender, + birthDate: userData.birthDate, + }; - return newUser; + // 유저 생성 + return await signupRepository.createUser(newUser); }; -module.exports = { signupUser }; \ No newline at end of file +module.exports = { createUser }; \ No newline at end of file From 376cc3a30c8fceacc8e3e2dd5d8da79ecaf0764d Mon Sep 17 00:00:00 2001 From: yjhss <20211420@sungshin.ac.kr> Date: Fri, 14 Feb 2025 19:49:55 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=84=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=20=EB=A1=9C=EA=B9=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../timeline/controller/timelineController.js | 12 +++++++----- src/domains/timeline/service/timelineService.js | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/domains/timeline/controller/timelineController.js b/src/domains/timeline/controller/timelineController.js index 587e9f8..394945e 100644 --- a/src/domains/timeline/controller/timelineController.js +++ b/src/domains/timeline/controller/timelineController.js @@ -5,7 +5,7 @@ const timelineService = require('../service/timelineService'); const createTimeline = async (req, res) => { const userId = req.user.id; // JWT 토큰에서 사용자 정보 가져오기 const { name, items } = req.body; // 요청 본문에서 타임라인 이름과 아이템들 받기 - logger.info(`Received request to create timeline: userId = ${userId}, name = ${name}`); + logger.info(`Received request to create timeline: ${req.method} ${req.originalUrl}, userId=${userId}, name=${name}`); if (!name) { return res.status(400).json({ success: false, message: 'Timeline name is required' }); @@ -26,7 +26,7 @@ const createTimeline = async (req, res) => { const deleteTimeline = async (req, res) => { const userId = req.user.id; const { timelineId } = req.params; - logger.info(`Received request to delete timeline: userId = ${userId}, timelineId = ${timelineId}`); + logger.info(`Received request to delete timeline: ${req.method} ${req.originalUrl}, userId=${userId}, timelineId=${timelineId}`); try { const isDeleted = await timelineService.deleteTimeline(userId, timelineId); @@ -47,7 +47,7 @@ const deleteTimeline = async (req, res) => { // 타임라인 목록 조회 const getTimelines = async (req, res) => { const userId = req.user.id; - logger.info(`Received request to fetch timelines for user: ${userId}`); + logger.info(`Received request to fetch timelines for user: ${req.method} ${req.originalUrl}, userId=${userId}`); try { const timelines = await timelineService.getTimelines(userId); @@ -63,7 +63,7 @@ const getTimelines = async (req, res) => { const getTimelineById = async (req, res) => { const { timelineId } = req.params; const userId = req.user.id; - logger.info(`Received request to fetch a timeline: userId = ${userId}, timelineId = ${timelineId}`); + logger.info(`Received request to fetch a timeline: ${req.method} ${req.originalUrl}, userId=${userId}, timelineId=${timelineId}`); try { const timeline = await timelineService.getTimelineById(timelineId, userId); @@ -86,7 +86,7 @@ const updateTimeline = async (req, res) => { const { timelineId } = req.params; const userId = req.user.id; const { name, items } = req.body; - logger.info(`Received request to update a timeline: userId = ${userId}, timelineId = ${timelineId}`); + logger.info(`Received request to update a timeline: ${req.method} ${req.originalUrl}, userId=${userId}, timelineId=${timelineId}`); try { const updatedTimeline = await timelineService.updateTimeline(userId, timelineId, name, items); @@ -101,6 +101,7 @@ const updateTimeline = async (req, res) => { } catch (error) { // 순서 이상 오류 처리 if (error.code === 'INVALID_POSITIONS') { + logger.warn(`Invalid positions error: ${error.message}`); return res.status(400).json({ success: false, message: 'Positions must be consecutive starting from 1 and cannot have duplicates.', @@ -109,6 +110,7 @@ const updateTimeline = async (req, res) => { // 중복된 scrapId 오류 처리 if (error.code === 'DUPLICATE_SCRAPS') { + logger.warn(`Duplicate scrapId error: ${error.message}`); return res.status(400).json({ success: false, message: 'Duplicate scrapId found within the same timeline.', diff --git a/src/domains/timeline/service/timelineService.js b/src/domains/timeline/service/timelineService.js index e79fae7..18fe0c8 100644 --- a/src/domains/timeline/service/timelineService.js +++ b/src/domains/timeline/service/timelineService.js @@ -117,7 +117,7 @@ const deleteTimeline = async (userId, timelineId) => { logger.info(`Successfully deleted timeline ${timelineId} for user ${userId}`); return true; } catch (error) { - logger.error(`Error deleting timeline ${timelineId}: ${error.message}`); + logger.error(`Error deleting timeline ${timelineId}: ${error.message}\nStack trace: ${error.stack}`); throw error; } }; @@ -137,7 +137,7 @@ const getTimelines = async (userId) => { return timelines; } catch (error) { - logger.error(`Error retrieving timelines for user: ${userId}, Error: ${error.message}`); + logger.error(`Error retrieving timelines for user: ${userId}, Error: ${error.message}\nStack trace: ${error.stack}`); throw error; } }; @@ -163,7 +163,7 @@ const getTimelineById = async (timelineId, userId) => { logger.info(`Successfully retrieved timeline with ID: ${timelineId} for user: ${userId}`); return { ...timeline, items: itemsWithArticles }; } catch (error) { - logger.error(`Error retrieving timeline with ID: ${timelineId} for user: ${userId}, Error: ${error.message}`); + logger.error(`Error retrieving timeline with ID: ${timelineId} for user: ${userId}, Error: ${error.message}\nStack trace: ${error.stack}`); throw error; } }; @@ -233,7 +233,7 @@ const updateTimeline = async (userId, timelineId, name, items) => { return updatedTimeline; }); } catch (error) { - logger.error(`Error updating timeline: ${error.message}, userId = ${userId}, timelineId = ${timelineId}`); + logger.error(`Error updating timeline: ${error.message}, userId = ${userId}, timelineId = ${timelineId}\nStack trace: ${error.stack}`); throw error; } }; From 9972dcb9bad8546b593f1f5fbaa5c50f4a8a2e3a Mon Sep 17 00:00:00 2001 From: yjhss <20211420@sungshin.ac.kr> Date: Fri, 14 Feb 2025 20:24:21 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EB=B3=80=EC=88=98=EB=AA=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/auth/signup/signupController.js | 6 +++--- src/domains/auth/signup/signupRepository.js | 16 ++++++++-------- src/domains/auth/signup/signupService.js | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/domains/auth/signup/signupController.js b/src/domains/auth/signup/signupController.js index 70ab9ae..1028033 100644 --- a/src/domains/auth/signup/signupController.js +++ b/src/domains/auth/signup/signupController.js @@ -2,7 +2,7 @@ const signupService = require('./signupService'); const { signupValidation } = require('./signupValidation'); const logger = require('../../../config/logger'); const { validationResult } = require('express-validator'); -const UserDto = require('./signupDto'); +const SignupDto = require('./signupDto'); const signupController = [ ...signupValidation, // 유효성 검사 미들웨어 추가 @@ -21,7 +21,7 @@ const signupController = [ }); } - const userData = new UserDto(req.body.username, req.body.password, req.body.name, req.body.gender, req.body.birthDate); + const userData = new SignupDto(req.body.username, req.body.password, req.body.name, req.body.gender, req.body.birthDate); // 회원가입 처리 const user = await signupService.createUser(userData); @@ -29,7 +29,7 @@ const signupController = [ const responseUser = { ...user, - gender: UserDto.convertGenderToKorean(user.gender), + gender: SignupDto.convertGenderToKorean(user.gender), password: undefined }; diff --git a/src/domains/auth/signup/signupRepository.js b/src/domains/auth/signup/signupRepository.js index 3f0b431..0d81c92 100644 --- a/src/domains/auth/signup/signupRepository.js +++ b/src/domains/auth/signup/signupRepository.js @@ -1,19 +1,19 @@ const { prisma } = require('../../../config/db'); -const signupDto = require('./signupDto'); +const SignupDto = require('./signupDto'); const logger = require('../../../config/logger'); -const createUser = async (signupDto) => { +const createUser = async (SignupDto) => { try { - const birthDateObject = new Date(signupDto.birthDate); + const birthDateObject = new Date(SignupDto.birthDate); const user = await prisma.user.create({ data: { - username: signupDto.username, - password: signupDto.password, - name: signupDto.name, - gender: signupDto.gender, - //birthDate: signupDto.birthDate, + username: SignupDto.username, + password: SignupDto.password, + name: SignupDto.name, + gender: SignupDto.gender, + //birthDate: SignupDto.birthDate, //birthDate: birthDateObject, birthDate: birthDateObject.toISOString().split('T')[0], }, diff --git a/src/domains/auth/signup/signupService.js b/src/domains/auth/signup/signupService.js index 2b8cbf3..50fb536 100644 --- a/src/domains/auth/signup/signupService.js +++ b/src/domains/auth/signup/signupService.js @@ -1,12 +1,12 @@ const bcrypt = require('bcryptjs'); const signupRepository = require('./signupRepository'); -const signupDto = require('./signupDto'); +const SignupDto = require('./signupDto'); const createUser = async (userData) => { // 비밀번호 해싱 const hashedPassword = await bcrypt.hash(userData.password, 10); - const gender = signupDto.convertGenderToEnglish(userData.gender); + const gender = SignupDto.convertGenderToEnglish(userData.gender); // DB에 저장할 객체 생성 const newUser = { From 10924e5f19946d8c84bb9d62700b4b964233e11a Mon Sep 17 00:00:00 2001 From: yjhss <20211420@sungshin.ac.kr> Date: Fri, 14 Feb 2025 20:31:57 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20ci=20=EC=8B=A4=ED=96=89=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=97=90=20pr=20synchronize=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5dc33b2..a2cc254 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ on: types: - opened - reopened + - synchronize paths-ignore: - 'README.md'