๐ ๏ธ ์ฃผ์ ์์
- ์ปค์คํ ์๋ฌ๋ก ์๋ฒ ์๋ฌ ์ฒ๋ฆฌ โ 2024-09-10
- ๊ธฐ์กด ๋ชจ๋ธ, ๋ ํฌ์งํ ๋ฆฌ, ์ปจํธ๋กค๋ฌ ๊ธฐ๋ฅ์ ๋ค์๊ธ ํ์ธํ๊ณ ๊ธฐ๋ฅ์ ๋ง๊ฒ ๋ฆฌํฉํ ๋ง โ 2024-09-10
- ์ต์ ๋ฒ ํจํด์ผ๋ก ์ํ๊ด๋ฆฌ ์ค๊ณ
๐ ํ์ต ํค์๋
- ์ปค์คํ ์๋ฌ ์ ๋ฆฌโ 2024-09-10
- ์ํ๊ด๋ฆฌ โ 2024-09-10
- ์ต์ ๋ฒ ํจํด โ 2024-09-10
๊ณ ๋ฏผ ๋ฐ ํด๊ฒฐ๊ณผ์
Connection Pool๊ณผ ๋น๋๊ธฐ ๋ณ๋ ฌ ์ํ
Connection Pool์ ๊ฒฝ์ฐ db์ ์ฐ๊ฒฐ๋ Connection์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๊ณ Pool์ ๋ณด๊ดํ์๋ค๊ฐ ํ์ํ ๋ Connection์ ๊ฐ์ ธ๋ค๊ฐ ์ฌ์ฉํ ํ, ๋ค์ Pool์ ๋ฐํํ๋ ๊ธฐ๋ฒ์ด๋ค.
์ด๋ฌํ Connection Pool์ ์ฌ์ฉํ ๋ฐฉ์์์ ํ ์ปจํธ๋กค๋ฌ์์ ์ฌ๋ฌ๋ฒ db์ ์ ๊ทผํ ๋ Promise.all ์ ์ฌ์ฉํ์ง ์์ ์ด์ ์ ๋ํ ์ง๋ฌธ์ด ๋์๋ค.
async function getCard(username) {
try {
const cards = await cardModel.getCard(username);
const columns = await columnModel.getColumn(username);
const data = columns.map((col) => {
return {
classify: col.classify,
cards: cards.filter((card) => card.task_column === col.classify),
};
});
return data;
} catch (error) {
// ๋์ค์ ์ปค์คํ
์๋ฌ๋ก ๊ณ ์น๊ธฐ
throw new Error("์๋ฌ");
}
}์ด๋ฌํ Promise.all์ ๊ฒฝ์ฐ ๋ชจ๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ํด์ ์ํ์ ๋ณ๋ ฌ์ ์ผ๋ก ์คํํ๋ค๋ ์ฅ์ ์ด ์์๋ค. ํ์ง๋ง ์ด๋ฅผ mysql์์ ์ฌ์ฉํ ์ ์์๊น์ ๋ํ ์ ๋๋ก ๋ ์ง์์ด ๋ถ์กฑํ์ฌ ์ ๋๋ก ๋๋ตํ์ง ๋ชปํ๋ค.
์ฐพ์๋ณธ ๊ฒฐ๊ณผ Mysql์ ๋ฉํฐ์ค๋ ๋ ๊ธฐ๋ฐ์ด๊ธฐ ๋๋ฌธ์ connection ์์ฒด๊ฐ ์ด๋ฌํ ๋ณ๋ ฌ ์ฒ๋ฆฌ์ ๋ํด์ connection pool์ ๋๊ณ ๋ณ๋ ฌ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ํ๋ก๋ฏธ์ค ์ฌ๋ฌ๊ฐ์ผ ๊ฒฝ์ฐ๋ฅผ ๊ฐ๊ฐ์ connection์ด ๋๊ณ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด์๊ธฐ ๋๋ฌธ์ Promise.all์ ์ฌ์ฉํด๋ ์๊ด ์์ ๋ฟ๋๋ฌ ์ถ๊ฐ์ ์ผ๋ก ์ฑ๋ฅ ํฅ์๊น์ง ๊ฐ๋ฅํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๊ฟ์ฃผ์๋ค.
export default (function () {
async function getCard(username) {
try {
const [cards, columns] = await Promise.all([
cardModel.getCard(username),
columnModel.getColumn(username),
]);
const data = columns.map((col) => {
return {
classify: col.classify,
cards: cards.filter((card) => card.task_column === col.classify),
};
});
return data;
} catch (error) {
// ๋์ค์ ์ปค์คํ
์๋ฌ๋ก ๊ณ ์น๊ธฐ
throw new Error("์๋ฌ");
}
}์ปค์คํ ์๋ฌ ์ฌ์ฉ๊ณผ ์๋ฌ ๋์ง๊ณ ๋ฐ๋ ์์น ์ ์
์ด๋ฒ์ ์ปค์คํ ์๋ฌ๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ ์ด๋์์ ์๋ฌ๊ฐ ๋ฐ์์ํค๊ณ , ์ด๋์ ๋ฐ์์ผ ํ ์ง์ ๋ํ ๊ณ ๋ฏผ์ด ๋ง์๋ค.
๊ธฐ์กด์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ fetch ํด์ค๋ ๋ชจ๋ ๊ฒฝ์ฐ(db์ฐ๊ฒฐ, ๋ ํฌ์งํ ๋ฆฌ ๋ฑ)์ ๋ํด์ try-catch๋ฌธ์ ์ฌ์ฉํ์ง๋ง ์ ์ ์ด๋ฌํ catch๋ฌธ์์ console๋ก ์ถ๋ ฅ๋ง ์ํฌ ๋ฟ ์ด์ ๋ํ ์๋ฌ์ฒ๋ฆฌ๊ฐ ๋์ด์์ง ์์ ํด๋น ํ๋ก๋ฏธ์ค๋ฅผ ํธ์ถํ๋ ํจ์ ๋ด์์๋ ์ ๋๋ก catchํ์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋ฐ๋ผ์ ์ด๋ฌํ ์๋ฌ๋ฅผ ์ธ์ ๋์ง๊ณ ์ด๋์์ ์ด๋ฌํ ์๋ฌ๋ฅผ ๋ฐ๋์ง๋ฅผ ๋ค์๊ธ ์๊ฐํด์ผ ํ ํ์์ฑ์ด ๋๋๋์๋ค.
๊ณ ๋ฏผํ ๊ฒฐ๊ณผ db์ชฝ์์ ๋๋ถ๋ถ ์ค๋ฅ๊ฐ ๋๋ ํ ์ํฉ์ ๊ณ ๋ คํ์ฌ
- ์ฟผ๋ฆฌ ์คํ๋ฌธ
- connection ์คํ๋ฌธ ์ ๊ธฐ์ค์ผ๋ก ์ปค์คํ ์๋ฌ๋ฅผ ๋๋์ด ๋์ง๋๋ก ์ค๊ณํ๋ค.
const getConnection = async () => {
try {
const connection = await sql.getConnection();
return connection;
} catch (error) {
throw new DBConnectionError(error.message);
}
};
export const transaction = async (logic, query) => {
const connection = await getConnection();
try {
await connection.beginTransaction();
const result = await logic(connection, query);
await connection.commit();
return result;
} catch (error) {
console.log("error: rollback:", error.message);
await connection.rollback();
throw new QueryError(error.message);
} finally {
console.log("release connection");
connection.release();
}
};์ฟผ๋ฆฌ ์คํ๋ฌธ์ ๊ฒฝ์ฐ transaction์์ try-catch๋ฌธ์์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด QueryError๋ฅผ ๋์ง์ผ๋ก์จ query๋ฅผ ์คํํ๋ ๊ณผ์ ์์์ ์๋ฌ๋ฅผ ์ก์ ๋ฐ๋ก ๋์ ธ์ฃผ์๊ณ , try-catch๋ฌธ ๋ฐ์์ ํ์ฉํด์ผ ํ๋ connection์ ๊ฒฝ์ฐ
async function getCard(...args) {
try {
const data = await transaction(cardRepository.getCardByUsername, args);
return data;
} catch (error) {
handleDBError(error);
}
}๊ฐ ๋ชจ๋ธ์์ ํธ๋์ญ์ ์ ํ๋ ์ผ๋ จ์ ๊ณผ์ ์์ ์๊ธฐ๋ DBConnectionError์ QueryError๋ฅผ ๋ชจ๋ ์ฌ๊ธฐ์ ์บ์นํ์ฌ ํ๋์ ์๋ฌ๋ก ๊ฐ์ธ๋ ํํ๋ก ๊ณ ์ํ๋ค.
export class DBConnectionError extends Error {
constructor(message) {
super(message);
this.name = "DBConnectionError";
}
}
export class QueryError extends Error {
constructor(message) {
super(message);
this.name = "QueryError";
}
}
export class DBError extends Error {
constructor(message, cause) {
super(message);
this.status = 500;
this.cause = cause;
this.name = "DBError";
}
}
export function handleDBError(err) {
if (err instanceof QueryError) {
throw new DBError("Server Error", err);
} else if (err instanceof DBConnectionError) {
throw new DBError("Server Error", err);
} else {
throw new DBError("Server Error", err);
}
}
์ด๋ ๊ฒ ๊ฐ๊ฐ์ ๋ถ๋ถ์ ๋ํด ์ฐ๊ฒฐ์์ ์๊ธฐ๋ ์ค๋ฅ์ ์ฟผ๋ฆฌ๋ฌธ์ ์คํํ ๋ ์๊ธฐ๋ ์ปค์คํ ์๋ฌ๋ฅผ ๋ง๋ค๊ณ , ์ด ๋์ ๋ชจ๋ ๋ฌถ์ด ํ๋์ ์๋ฌ๋ก ๊ด๋ฆฌํ ์ ์๋ DBError๋ฅผ ๋ง๋ค์ด ์ด๋ฅผ ๋์ ธ Controller์์ ๋ฐ์ ์ ์๋๋ก ํ์๋ค.
async function getCard(username) {
try {
...
} catch (error) {
throw error;
}
}์ปจํธ๋กค๋ฌ์์ ๋ฐ์ ์๋ฌ์ ๊ฒฝ์ฐ ํ์ฌ๊น์ง๋ DBError๋ฐ์ ์์ง๋ง, ๋์ค์ ValidationError์ ๊ฐ์ ์์ธ๋ค์ ์ก์ ๋์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ปจํธ๋กค๋ฌ๋ ๊ฐ ์ข ๋ฅ์ ์๋ฌ๋ฅผ ๋ฐ์ ๋ค์๊ธ router์๊ฒ ๋์ง๋ค.
router.get("/", isLoggedIn, async (req, res, next) => {
try {
let todoData = await cardController.getCard(req.user.username);
res.status(200).json({ data: todoData, path: process.env.DIRNAME });
} catch (error) {
next(error);
}
});router์์ ๋ค์๊ธ ์ก์ ์๋ฌ๋ next๋ฅผ ํตํด ๋ค์ ๋ฏธ๋ค์จ์ด๋ก ๋์ด๊ฐ๊ฒ ํ๋๋ฐ, ๋ค์ ๋ฏธ๋ค์จ์ด๋ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฏธ๋ค์จ์ด์ด๋ค.
export default function errorMiddleWare(app) {
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status).json(err.message);
});
}์๋ฌ ๋ฏธ๋ค์จ์ด์ ๊ฒฝ์ฐ ์์ง๊น์ง๋ ๊ฐ๋จํ๊ฒ ๊ตฌํํ์๋ค. error์ property๋ก ์ค์ ํ status์ฝ๋์ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ฐํํ๋๋ก ํ์๋ค.
๋ฆฌ๋ทฐ ์์ฒญ
์๋ ํ์ธ์ ๋ฉํ ๋. ๊ณ ์ ๋ง์ผ์ญ๋๋ค. ์ด์ ์ค๋๋์ ์ ๋ MVC ํจํด์ ๋ํด ๋ค์๊ธ ํ์ตํ๊ณ ์ด๋ฅผ ์ง์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๋ ์๊ฐ์ ๊ฐ์ก์ต๋๋ค. ํ์ง๋ง ํด๋น ํจํด์ ๊ณต๋ถํ๋ฉด์๋ ์์ง๊น์ง๋ ์ด๋ฅผ ์ ๋๋ก ์ค๊ณํ๋์ง ํ์ ์ด ๋ค์ง ์์ ์ด๋ฌํ ๋ถ๋ถ์ ๋ฆฌ๋ทฐ ์์ฒญ ๋๋ฆฌ๋ ค ํฉ๋๋ค.
์ ๊ฐ ๋ฆฌ๋ทฐ ์์ฒญ๋๋ฆฌ๋ ๋ถ๋ถ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
MVC ํจํด์ ์ ์ฉ๊ณผ ์๋ฌ์ฒ๋ฆฌ
์ด๋ฒ์ ์๋ฒ์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๊พธ๋ฉด์ ๊ฐ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ๋ํ ์๋์ ๊ฐ์ด ๋ฐ๊พธ์์ต๋๋ค
- Controller
- ๋ผ์ฐํฐ ๋๋ ํ ๋ฆฌ
- ๊ฐ api ์์ฒญ์ ๋ํ ๋ผ์ฐํฐ๋ฅผ ๋๋ฉ์ธ๋ณ๋ก ์ค์
- ๋๋ฉ์ธ๋ณ controller ํ์ผ
- model์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ ์ดํ๋๋ก ์ ๋ฌ
- ๊ฐ ๋๋ฉ์ธ๋ณ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๊ณ , ์ฌ๋ฌ ๋๋ฉ์ธ๋ณ ๋น์ฆ๋์ค๊ฐ ๊ฒน์ณ์ ธ ์๋ ๊ฒฝ์ฐ ํด๋น ์ปจํธ๋กค๋ฌ์์ ์ ์ดํ์ฌ ์๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ฆฌํดํ๋๋ก ๋ณํ
- model์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ ์ดํ๋๋ก ์ ๋ฌ
- ๋ผ์ฐํฐ ๋๋ ํ ๋ฆฌ
- Model
- ๋๋ฉ์ธ๋ณ ๋ชจ๋ธํ์ผ์ ๋ด์
- ๋ ํฌ์งํ ๋ฆฌ์ ์์กดํ์ฌ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ๋จ์ผ ๋น์ฆ๋์ค ๋ก์ง์ ์ํ
- ๋ ํฌ์งํ ๋ฆฌ ๋๋ ํ ๋ฆฌ
- db์ ์ง์ ์ ์ผ๋ก ์ ๊ทผํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ์ดํจ
- View
- ๋ ์ด์์์ ์ ์ธํ๊ณ ํด๋ผ์ด์ธํธ ์ธก์์ ๋ชจ๋ ์ฒ๋ฆฌ
๋ฐ๋ผ์ ํ์ฌ ๊ตฌ์กฐ๋ api ์์ฒญ โ router โ controller(ํตํฉ ๋น์ฆ๋์ค ๋ก์ง) โ model(๊ฐ๋ณ ๋น์ฆ๋์ค ๋ก์ง) โ repository ์์ผ๋ก ์ ๊ทผํ์ฌ ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ณ ์ ์ดํ๋ ๊ณผ์ ์ ๊ฑฐ์น๊ฒ ๋ฉ๋๋ค. ๋ํ ์ด ๊ณผ์ ์์ db์ ์ ๊ทผํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ ์ค ์๊ธฐ๋ ์๋ฌ์ db์ connection์ ์์ฑํ๋ ๊ณผ์ ์ค์ ์๊ธด ์๋ฌ๋ฅผ ์ปค์คํ ์๋ฌ๋ก ์ฒ๋ฆฌํ์ฌ router์์ ๋ฐ์ ๋ค, ๋ฏธ๋ค์จ์ด๋ฅผ ํตํด ์๋ฌ๋ฅผ response๋ก ๋ณด๋ด๋๋ก ์ค๊ณํ์์ต๋๋ค. ์ค๊ณํ ๊ตฌ์กฐ์ ์๋ฌ์ฒ๋ฆฌ ๋ถ๋ถ์ ๋ํด์ ๋ณด์ํ ๋ถ๋ถ ํผ๋๋ฐฑ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค!