Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE][Feat] #159 : 회원가입 api 구현, [BE][Refactor] swagger 문서 tag 추가, jsdoc 추가 #160

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions backend/src/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { loginUser } from '../services/authService.js';
import { loginUser, registerUser } from '../services/authService.js';

export const login = async (req, res) => {
/**
* @description 로그인 컨트롤러
*/
export const loginController = async (req, res) => {
const { id, password } = req.body;

try {
Expand All @@ -18,3 +21,24 @@ export const login = async (req, res) => {
return res.status(500).json({ success: false, message: 'Server error occurred' });
}
};

/**
* @description 회원가입 컨트롤러
*/
export const registerUserController = async (req, res) => {
try {
const { id, name, password, email } = req.body;
const newUser = await registerUser(id, name, password, email);
return res.status(201).json({
success: true,
message: 'Login successfully',
data: newUser,
});
} catch (error) {
if (error.message === 'User ID already exists') {
return res.status(409).json({ error: 'User ID already exists' });
}
console.error('User registration error:', error);
res.status(500).json({ error: 'Server error' });
}
};
15 changes: 15 additions & 0 deletions backend/src/controllers/channelController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
getUserChannels,
} from '../services/channelService.js';

/**
* @description 채널 생성 컨트롤러
*/
export const createChannelController = async (req, res) => {
try {
const { name, host_id, guests } = req.body;
Expand All @@ -26,6 +29,9 @@ export const createChannelController = async (req, res) => {
}
};

/**
* @description 채널에 게스트 추가 컨트롤러
*/
export const addGuestController = async (req, res) => {
try {
const { channelId } = req.params;
Expand Down Expand Up @@ -53,6 +59,9 @@ export const addGuestController = async (req, res) => {
}
};

/**
* @description 채널 정보 조회 컨트롤러
*/
export const getChannelInfoController = async (req, res) => {
const { id } = req.params;

Expand All @@ -72,6 +81,9 @@ export const getChannelInfoController = async (req, res) => {
}
};

/**
* @description 채널에 특정 게스트 정보 조회 컨트롤러
*/
export const getChannelGuestInfoController = async (req, res) => {
const { channelId, guestId } = req.params;
try {
Expand All @@ -91,6 +103,9 @@ export const getChannelGuestInfoController = async (req, res) => {
}
};

/**
* @description 사용자의 채널 리스트 조회 컨트롤러
*/
export const getUserChannelsController = async (req, res) => {
const { userId } = req.params;

Expand Down
17 changes: 0 additions & 17 deletions backend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import express from 'express';
import swaggerUi from 'swagger-ui-express';
import http from 'http';
import { specs } from '../swaggerConfig.js';
import { pool } from './db/db.js';
import { PORT } from './constants/constants.js';
import { initializeWebSocketServer } from './websocketServer.js';
import { authRouter } from './routes/authRouter.js';
Expand All @@ -16,22 +15,6 @@ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
app.use('/api/auth', authRouter);
app.use('/api/channel', channelRouter);

// TODO: 데이터베이스에서 데이터 가져오기 예시
app.get('/guests', async (req, res) => {
try {
const result = await pool.query('SELECT * FROM guest');
res.json(result.rows);
} catch (err) {
console.error(err);
res.status(500).send('서버 오류');
}
});

// TODO: 예제 라우터 (추가 예정인 라우터의 주석을 Swagger 주석 형식으로 문서화)
app.get('/example', (req, res) => {
res.send('Hello World');
});

// HTTP 서버 생성
const server = http.createServer(app);

Expand Down
40 changes: 40 additions & 0 deletions backend/src/repositories/userRepository.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
import { pool } from '../db/db.js';

/**
* @description 데이터베이스에 새로운 사용자 생성
* @param {string} id - 사용자 id
* @returns {object} id로 찾은 사용자 정보
*/
export const findUserById = async id => {
const result = await pool.query('SELECT * FROM "main"."user" WHERE id = $1', [id]);
return result.rows[0];
};

/**
* @description 사용자 ID 중복 여부 확인
* @param {string} id - 사용자 ID
* @returns {boolean} 중복 여부
*/
export const isUserIdDuplicate = async id => {
const query = `
SELECT 1 FROM "main"."user"
WHERE id = $1;
`;
const result = await pool.query(query, [id]);

return result.rows.length > 0;
};

/**
* @description 데이터베이스에 새로운 사용자 생성
* @param {string} id - 사용자 ID
* @param {string} name - 사용자 이름
* @param {string} password - 사용자 비밀번호
* @param {string} email - 사용자 이메일
* @returns {object} 새로 생성된 사용자 정보
*/
export const createUserInDB = async (id, name, password, email) => {
const query = `
INSERT INTO "main"."user" (id, name, password, email)
VALUES ($1, $2, $3, $4)
RETURNING id, name, email;
`;
const values = [id, name, password, email];
const result = await pool.query(query, values);

return result.rows[0];
};
42 changes: 40 additions & 2 deletions backend/src/routes/authRouter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express';
import { body } from 'express-validator';
import { login } from '../controllers/authController.js';
import { loginController, registerUserController } from '../controllers/authController.js';
import { validationMiddleware } from '../middleware/validationMiddleware.js';

export const authRouter = express.Router();
Expand All @@ -11,6 +11,7 @@ export const authRouter = express.Router();
* post:
* summary: 사용자 로그인 API
* description: 사용자가 로그인할 수 있도록 ID와 비밀번호를 통해 인증 후 토큰을 반환합니다.
* tags: [Auth]
* requestBody:
* required: true
* description: 로그인을 위한 ID와 비밀번호를 포함한 요청 body
Expand Down Expand Up @@ -41,5 +42,42 @@ authRouter.post(
.withMessage('Password must be at least 6 characters long'),
],
validationMiddleware,
login,
loginController,
);

/**
* @swagger
* /user/register:
* post:
* summary: "회원가입 API"
* description: "사용자가 회원가입을 통해 계정을 생성합니다."
* tags: [Auth]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RegisterRequest'
* responses:
* "201":
* description: "회원가입 성공"
* "400":
* description: "유효성 검사 실패"
* "409":
* description: "중복된 사용자 ID"
* "500":
* description: "서버 오류"
*/
authRouter.post(
'/register',
[
body('id').isLength({ min: 4 }).withMessage('User ID must be at least 4 characters long'),
body('name').notEmpty().withMessage('Name is required'),
body('password')
.isLength({ min: 6 })
.withMessage('Password must be at least 6 characters long'),
body('email').isEmail().withMessage('Valid email is required'),
],
validationMiddleware,
registerUserController,
);
5 changes: 5 additions & 0 deletions backend/src/routes/channelRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const channelRouter = express.Router();
* post:
* summary: '새로운 채널 생성 API'
* description: '채널 이름, 주인, 게스트 정보를 포함하여 채널을 생성합니다.'
* tags: [Channel]
* requestBody:
* required: true
* content:
Expand Down Expand Up @@ -51,6 +52,7 @@ channelRouter.post(
* post:
* summary: '게스트 추가 API'
* description: '특정 채널에 게스트를 추가합니다.'
* tags: [Channel]
* parameters:
* - name: 'channelId'
* in: 'path'
Expand Down Expand Up @@ -87,6 +89,7 @@ channelRouter.post(
* get:
* summary: '채널 정보 조회 API'
* description: '특정 채널의 정보를 조회합니다.'
* tags: [Channel]
* parameters:
* - name: 'id'
* in: 'path'
Expand Down Expand Up @@ -117,6 +120,7 @@ channelRouter.get(
* get:
* summary: '게스트 정보 조회 API'
* description: '특정 채널 내의 게스트 정보를 조회합니다.'
* tags: [Channel]
* parameters:
* - name: 'channelId'
* in: 'path'
Expand Down Expand Up @@ -155,6 +159,7 @@ channelRouter.get(
* get:
* summary: '사용자가 host인 채널 목록 반환 API'
* description: 'userId를 기준으로 해당 사용자가 host인 채널 목록을 반환합니다.'
* tags: [Channel]
* parameters:
* - in: path
* name: userId
Expand Down
28 changes: 27 additions & 1 deletion backend/src/services/authService.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { findUserById } from '../repositories/userRepository.js';
import { createUserInDB, findUserById, isUserIdDuplicate } from '../repositories/userRepository.js';

/**
* @description 로그인 서비스
* @param {string} name - 사용자 이름
* @param {string} password - 사용자 비밀번호
* @returns {object} 로그인된 사용자의 토큰과 id
*/
export const loginUser = async (id, password) => {
const user = await findUserById(id);
if (!user) {
Expand All @@ -17,3 +23,23 @@ export const loginUser = async (id, password) => {
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
return { token, userId: user.id };
};

/**
* @description 회원가입 서비스
* @param {string} id - 사용자 ID
* @param {string} name - 사용자 이름
* @param {string} password - 사용자 비밀번호
* @param {string} email - 사용자 이메일
* @returns {object} 새로 생성된 사용자 정보
*/
export const registerUser = async (id, name, password, email) => {
const isDuplicate = await isUserIdDuplicate(id);
if (isDuplicate) {
throw new Error('User ID already exists');
}

const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await createUserInDB(id, name, hashedPassword, email);

return newUser;
};
32 changes: 32 additions & 0 deletions backend/src/services/channelService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import {
} from '../repositories/channelRepository.js';
import { addGuestToChannel } from '../repositories/guestRepository.js';

/**
* @description 새로운 채널을 생성하고 게스트를 추가
* @param {string} name - 채널 이름
* @param {string} host_id - 채널 호스트 ID
* @param {Array} guests - 추가할 게스트 목록
* @returns {object} 생성된 채널 정보
*/
export const createChannelService = async (name, host_id, guests) => {
const channel = await createChannelInDB(name, host_id);

Expand All @@ -28,6 +35,12 @@ export const createChannelService = async (name, host_id, guests) => {
return channel;
};

/**
* @description 특정 채널에 게스트 추가
* @param {string} channelId - 채널 ID
* @param {Array} guests - 추가할 게스트 목록
* @returns {object|null} 채널 정보 (채널이 없을 경우 null 반환)
*/
export const addGuestService = async (channelId, guests) => {
const channel = await getChannelInfoByIdInDB(channelId);
if (!channel) return null;
Expand All @@ -50,6 +63,12 @@ export const addGuestService = async (channelId, guests) => {
return channel;
};

/**
* @description 채널 ID로 채널과 게스트 정보를 조회
* @param {string} id - 채널 ID
* @returns {object} 채널과 게스트 정보
* @throws {Error} 채널 조회 실패 시 오류 발생
*/
export const getChannelByIdService = async id => {
try {
return await getChannelWithGuestsByIdFromDB(id);
Expand All @@ -59,6 +78,13 @@ export const getChannelByIdService = async id => {
}
};

/**
* @description 채널 ID와 게스트 ID로 특정 게스트 정보를 조회
* @param {string} channelId - 채널 ID
* @param {string} guestId - 게스트 ID
* @returns {object|null} 채널과 해당 게스트 정보 (채널 또는 게스트가 없을 경우 null 반환)
* @throws {Error} 조회 실패 시 오류 발생
*/
export const getChannelGuestInfoService = async (channelId, guestId) => {
try {
return await getGuestByChannelAndGuestIdFromDB(channelId, guestId);
Expand All @@ -68,6 +94,12 @@ export const getChannelGuestInfoService = async (channelId, guestId) => {
}
};

/**
* @description 사용자 ID로 해당 사용자가 호스트인 채널 목록을 조회
* @param {string} userId - 사용자 ID
* @returns {Array} 사용자가 호스트인 채널 목록
* @throws {Error} 채널 조회 실패 시 오류 발생
*/
export const getUserChannels = async userId => {
try {
return await getChannelsByUserIdFromDB(userId);
Expand Down
Loading
Loading