지난 한 주간 2024 Winter - Spurt Project 를 진행하면서 작성한 게시글입니다.
프로젝트의 개요는 이전 게시글을 참고 부탁드립니다.
프리즈마 작성
위의 E-R-D 토대로 prisma schema를 작성하였다.
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Student{
student_id Int @id @default(autoincrement())
student_name String
student_email String
student_age String
student_aspiration String
enrollments Enrollment[]
}
model Instructor{
instructor_id Int @id @default(autoincrement())
instructor_name String
instructor_email String
instructor_introduction String
enrollments Enrollment[]
}
model Enrollment{
student_id Int
instructor_id Int
course_id Int
student Student @relation(fields: [student_id], references: [student_id])
instructor Instructor @relation(fields: [instructor_id], references: [instructor_id])
course Course @relation(fields: [course_id], references: [course_id])
@@id([student_id, instructor_id, course_id])
}
model Course{
course_id Int @id @default(autoincrement())
course_name String
course_description String
course_duration String
course_price Int
enrollments Enrollment[]
}
- 일대다 (One-to-Many):
- Student과 Instructor 모델은 각각 Enrollment 모델과 1:N 관계를 가지고 있다. 즉, 하나의 학생이나 강사는 여러 수강 등록 정보를 가질 수 있지만, 수강 등록 정보는 하나의 학생이나 강사에게 속한다.
- Course 모델도 1:N 관계를 가지며, 하나의 강의는 여러 수강 등록 정보를 가질 수 있지만, 수강 등록 정보는 하나의 강의에 속한다.
- 다대다 (Many-to-Many):
- Enrollment 모델은 학생(Student), 강사(Instructor), 강의(Course) 모델과 M:N 관계를 가진다. 이는 하나의 수강 등록 정보가 여러 학생과 여러 강사, 여러 강의와 연결되고 M:N의 중간 테이블을 위해 작성되었다.
학습을 위해 1차로 간단히 구현한 계층 구조는 다음과 같다.
app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const indexRouter = require('./routes/index');
const studentsRouter = require('./routes/students');
const instructorsRouter = require('./routes/instructors');
const coursesRouter = require('./routes/courses');
const enrollmentsRouter = require('./routes/enrollments');
const { Prisma, PrismaClient } = require('@prisma/client');
const app = express();
const prisma = new PrismaClient();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/students', studentsRouter);
app.use('/instructors', instructorsRouter);
app.use('/courses', coursesRouter);
app.use('/enrollments', enrollmentsRouter);
// main page 구현
app.get('/', (req,res) => {
res.render('index',{message: 'Welcom to Boflearn Main Page!'});
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
/routes/studentRoutes
const express = require('express');
const router = express.Router();
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
router.get('/', async (req, res, next) => {
try {
const students = await prisma.student.findMany();
res.render('students', { students });
} catch (error) {
next(error);
}
});
router.post('/', async (req, res, next) => {
try {
const { name, email, age, aspiration } = req.body;
const newStudent = await prisma.student.create({
data: {
student_name: name,
student_email: email,
student_age: age,
student_aspiration: aspiration,
},
});
res.redirect('/students');
} catch (error) {
next(error);
}
});
module.exports = router;
위의 구조는 app.js에서 routes로 핸들링 하는 구조이다. 하지만 부족한점은 다음과 같다.
1. 모듈화 부족
모듈화 부분이 부족하다.
모든 라우트 처리 로직이 students.js 파일에 포함되어 있어 나중에 애플리케이션을 유지보수하고 확장하는 데 어려움이 있을 것이다.
코드를 라우트, 컨트롤러 및 모델과 같은 서로 다른 파일로 분리하여 모듈화를 진행해야 한다.
2. Prisma Client 반복 초기화 ( 튜터 피드백 )
app.js 및 students.js에서 Prisma Client의 새 인스턴스가 생성된다.( 중복 호출된다 )
이를 최적화하려면 별도의 파일에서 단일 인스턴스를 만들고 필요한 곳에서 가져오도록 개선해야한다.
그러면 리소스 소비와 관련된 문제를 방지하고 데이터베이스 연결의 일관성을 보장할 수 있다.
이후 개선 사항 (MVC 패턴 리팩토링 & CRUD 기능 구현)
구현 이전에 MVC 패턴에 대해 완벽한 이해가 필요했다.
아래는 학습에 참고한 자료를 링크 첨부하였다.
https://developer.mozilla.org/ko/docs/Glossary/MVC
app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const { Prisma, PrismaClient } = require('@prisma/client');
const indexRouter = require('./routes/indexRoutes');
const studentsRouter = require('./routes/studentRoutes');
const instructorsRouter = require('./routes/instructorRoutes');
const coursesRouter = require('./routes/courseRoutes');
const enrollmentsRouter = require('./routes/enrollmentRoutes');
const app = express();
const prisma = new PrismaClient();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/students', studentsRouter);
app.use('/instructors', instructorsRouter);
app.use('/courses', coursesRouter);
app.use('/enrollments', enrollmentsRouter);
// main page 구현
app.get('/', (req, res) => {
res.render('index', { message: 'Welcome to Boflearn Main Page!' });
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
routes/studuentRoutes.js
const express = require('express');
const router = express.Router();
const studentController = require('../controllers/studentController');
router.get('/', studentController.getAllStudents);
router.post('/', studentController.createStudent);
router.delete('/:id',studentController.deleteStudent);
router.put('/:id',studentController.updateStudent);
module.exports = router;
controllers/studentController.js
const StudentModel = require('../models/studentModel');
const studentModel = new StudentModel();
exports.getAllStudents = async (req, res, next) => {
try {
const students = await studentModel.getAllStudents();
res.render('students', { students });
} catch (error) {
next(error);
}
};
exports.createStudent = async (req, res, next) => {
try {
const { name, email, age, aspiration } = req.body;
await studentModel.createStudent(name, email, age, aspiration);
res.redirect('/students');
} catch (error) {
next(error);
}
};
exports.deleteStudent = async (req, res, next) => {
try{
const studentId = parseInt(req.params.id,10);
await studentModel.deleteStudent(studentId);
res.status(204).send();
}catch(error){
next(error);
}
};
exports.updateStudent = async (req, res, next) => {
try {
const studentId = parseInt(req.params.id, 10);
const { name, email, age, aspiration } = req.body;
await StudentModel.updateStudent(studentId, {name, email, age, aspiration});
res.redirect('/students');
} catch (error) {
next(error);
}
};
models/studentModel.js
const { PrismaClient } = require('@prisma/client');
const { deleteStudent } = require('../controllers/studentController');
const prisma = new PrismaClient();
class StudentModel {
async getAllStudents() {
return prisma.student.findMany();
}
async createStudent(name, email, age, aspiration) {
return prisma.student.create({
data: {
student_name: name,
student_email: email,
student_age: age,
student_aspiration: aspiration,
},
});
}
async updateStudent(studentId, data) {
try {
const existingStudent = await prisma.student.findUnique({
where: {
student_id: studentId,
},
});
if (!existingStudent) {
throw new Error('Student not found');
}
return prisma.student.update({
where: {
student_id: studentId,
},
data:{
student_name: data.name,
student_email: data.email,
student_age: data.age,
student_aspiration: data.aspiration,
},
});
} catch (error) {
throw new Error(`Error updating student: ${error.message}`);
}
}
async deleteStudent(studentId){
return prisma.student.delete({
where:{
student_id: studentId,
},
});
}
}
module.exports = StudentModel;
Student의 이외의 다른 것은 Github을 참조하자
되돌아보기
기존의 코드를 리팩토링하면서 MVC 패턴을 직접 찾아보고,
이전에 작성한 코드가 초심자적인 부분이 많다는 것을 깨달았다.
특히, 직접 Model, Routes, Controller를 분리하고 테스트를 진행하면서 Express의 동작 흐름을 더 깊게 이해할 수 있었다.
이러한 리팩토링을 통해 코드의 가독성을 높이고 유지보수성을 향상시키는 것이 목표이다.
또한, 앞으로는 발생하는 오류에 대한 구문을 더 추가하여 어느 부분에서 문제가 발생했는지 명확하게 파악하고 수정할 계획이다.
다음은 JWT 인증을 관련하여 학습할 예정이다.
'Develop & Review' 카테고리의 다른 글
2024 Winter - Spurt Project - 1 [스터디 개인 프로젝트] (0) | 2024.01.16 |
---|