-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// 필요한 모듈들 | ||
const functions = require('firebase-functions'); | ||
const { Pool, Query } = require('pg'); | ||
const dayjs = require('dayjs'); | ||
const dotenv = require('dotenv'); | ||
dotenv.config(); | ||
|
||
// DB Config (유저, 호스트, DB 이름, 패스워드)를 로딩해줍시다. | ||
const dbConfig = require('../config/dbConfig'); | ||
|
||
// NODE_ENV라는 글로벌 환경변수를 사용해서, 현재 환경이 어떤 '모드'인지 판별해줍시다. | ||
let devMode = process.env.NODE_ENV === 'development'; | ||
|
||
// SQL 쿼리문을 콘솔에 프린트할지 말지 결정해주는 변수를 선언합시다. | ||
const sqlDebug = true; | ||
|
||
// 기본 설정에서는 우리가 실행하게 되는 SQL 쿼리문이 콘솔에 찍히지 않기 때문에, | ||
// pg 라이브러리 내부의 함수를 살짝 손봐서 SQL 쿼리문이 콘솔에 찍히게 만들어 줍시다. | ||
const submit = Query.prototype.submit; | ||
Query.prototype.submit = function () { | ||
const text = this.text; | ||
const values = this.values || []; | ||
const query = text.replace(/\$([0-9]+)/g, (m, v) => JSON.stringify(values[parseInt(v) - 1])); | ||
// devMode === true 이면서 sqlDebug === true일 때 SQL 쿼리문을 콘솔에 찍겠다는 분기입니다. | ||
devMode && sqlDebug && console.log(`\n\n[👻 SQL STATEMENT]\n${query}\n_________\n`); | ||
submit.apply(this, arguments); | ||
}; | ||
|
||
// 서버가 실행되면 현재 환경이 개발 모드(로컬)인지 프로덕션 모드(배포)인지 콘솔에 찍어줍시다. | ||
console.log(`[🔥DB] ${process.env.NODE_ENV}`); | ||
|
||
// 커넥션 풀을 생성해줍니다. | ||
const pool = new Pool({ | ||
...dbConfig, | ||
connectionTimeoutMillis: 60 * 1000, | ||
idleTimeoutMillis: 60 * 1000, | ||
}); | ||
|
||
// 위에서 생성한 커넥션 풀에서 커넥션을 빌려오는 함수를 정의합니다. | ||
// 기본적으로 제공되는 pool.connect()와 pool.connect().release() 함수에 디버깅용 메시지를 추가하는 작업입니다. | ||
const connect = async (req) => { | ||
const now = dayjs(); | ||
const string = | ||
!!req && !!req.method | ||
? `[${req.method}] ${!!req.user ? `${req.user.id}` : ``} ${req.originalUrl}\n ${!!req.query && `query: ${JSON.stringify(req.query)}`} ${!!req.body && `body: ${JSON.stringify(req.body)}`} ${ | ||
!!req.params && `params ${JSON.stringify(req.params)}` | ||
}` | ||
: `request 없음`; | ||
const callStack = new Error().stack; | ||
const client = await pool.connect(); | ||
const query = client.query; | ||
const release = client.release; | ||
|
||
const releaseChecker = setTimeout(() => { | ||
devMode | ||
? console.error('[ERROR] client connection이 15초 동안 릴리즈되지 않았습니다.', { callStack }) | ||
: functions.logger.error('[ERROR] client connection이 15초 동안 릴리즈되지 않았습니다.', { callStack }); | ||
devMode ? console.error(`마지막으로 실행된 쿼리문입니다. ${client.lastQuery}`) : functions.logger.error(`마지막으로 실행된 쿼리문입니다. ${client.lastQuery}`); | ||
}, 15 * 1000); | ||
|
||
client.query = (...args) => { | ||
client.lastQuery = args; | ||
return query.apply(client, args); | ||
}; | ||
client.release = () => { | ||
clearTimeout(releaseChecker); | ||
const time = dayjs().diff(now, 'millisecond'); | ||
if (time > 4000) { | ||
const message = `[RELEASE] in ${time} | ${string}`; | ||
devMode && console.log(message); | ||
} | ||
client.query = query; | ||
client.release = release; | ||
return release.apply(client); | ||
}; | ||
return client; | ||
}; | ||
|
||
module.exports = { | ||
connect, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
userDB: require('./user'), | ||
postDB: require('./post'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
const _ = require('lodash'); | ||
const convertSnakeToCamel = require('../lib/convertSnakeToCamel'); | ||
|
||
const getAllPosts = async (client) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM post p | ||
WHERE is_deleted = FALSE | ||
`, | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows); | ||
}; | ||
|
||
const getPostById = async (client, postId) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM post p | ||
WHERE id = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[postId], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const addPost = async (client, userId, title, content) => { | ||
const { rows } = await client.query( | ||
` | ||
INSERT INTO post | ||
(user_id, title, content) | ||
VALUES | ||
($1, $2, $3) | ||
RETURNING * | ||
`, | ||
[userId, title, content], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const updatePost = async (client, title, content, postId) => { | ||
const { rows: existingRows } = await client.query( | ||
` | ||
SELECT * FROM post p | ||
WHERE id = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[postId], | ||
); | ||
|
||
if (existingRows.length === 0) return false; | ||
|
||
const data = _.merge({}, convertSnakeToCamel.keysToCamel(existingRows[0]), { title, content }); | ||
|
||
const { rows } = await client.query( | ||
` | ||
UPDATE post p | ||
SET title = $1, content = $2, updated_at = now() | ||
WHERE id = $3 | ||
RETURNING * | ||
`, | ||
[data.title, data.content, postId], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const deletePost = async (client, postId) => { | ||
const { rows } = await client.query( | ||
` | ||
UPDATE post p | ||
SET is_deleted = TRUE, updated_at = now() | ||
WHERE id = $1 | ||
RETURNING * | ||
`, | ||
[postId], | ||
); | ||
|
||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const getPostsByUserId = async (client, userId) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM post | ||
WHERE user_id = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[userId], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows); | ||
}; | ||
|
||
const getPostsByUserIds = async (client, userIds) => { | ||
if (userIds.length < 1) return []; | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM post | ||
WHERE user_id IN (${userIds.join()}) | ||
AND is_deleted = FALSE | ||
`, | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows); | ||
}; | ||
|
||
module.exports = { getAllPosts, getPostById, addPost, updatePost, deletePost, getPostsByUserId, getPostsByUserIds }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
const _ = require('lodash'); | ||
const convertSnakeToCamel = require('../lib/convertSnakeToCamel'); | ||
|
||
const getAllUsers = async (client) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM "user" u | ||
WHERE is_deleted = FALSE | ||
`, | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows); | ||
}; | ||
|
||
const getUserById = async (client, userId) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM "user" u | ||
WHERE id = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
// client.query()의 두 번째 파라미터에는, 쿼리문에 집어넣고 싶은 변수들의 배열을 적습니다. | ||
// $1에는 배열의 첫번째 변수가, $2에는 배열의 두 번째 변수... 이런 식으로 쿼리문에 변수가 들어가게 됩니다! | ||
[userId], | ||
); | ||
// 위의 getAllUsers와는 달리, 이번에는 유저 하나만 가져오고 싶기 때문에 rows[0]만 리턴해 줍니다. | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const getUserByIdFirebase = async (client, idFirebase) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM "user" u | ||
WHERE id_firebase = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[idFirebase], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const getUserByEmail = async (client, email) => { | ||
const { rows } = await client.query( | ||
` | ||
SELECT * FROM "user" u | ||
WHERE email = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[email], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const updateUser = async (client, username, phone, userId) => { | ||
const { rows: existingRows } = await client.query( | ||
` | ||
SELECT * FROM "user" | ||
WHERE id = $1 | ||
AND is_deleted = FALSE | ||
`, | ||
[userId], | ||
); | ||
|
||
if (existingRows.length === 0) return false; | ||
|
||
const data = _.merge({}, convertSnakeToCamel.keysToCamel(existingRows[0]), { username, phone }); | ||
|
||
const { rows } = await client.query( | ||
` | ||
UPDATE "user" u | ||
SET username = $1, phone = $2, updated_at = now() | ||
WHERE id = $3 | ||
RETURNING * | ||
`, | ||
[data.username, data.phone, userId], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const deleteUser = async (client, userId) => { | ||
const { rows } = await client.query( | ||
` | ||
UPDATE "user" u | ||
SET is_deleted = TRUE, updated_at = now() | ||
WHERE id = $1 | ||
RETURNING * | ||
`, | ||
[userId], | ||
); | ||
|
||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
const addUser = async (client, email, username, phone, idFirebase) => { | ||
const { rows } = await client.query( | ||
` | ||
INSERT INTO "user" u | ||
(email, username, phone, id_firebase) | ||
VALUES | ||
($1, $2, $3) | ||
RETURNING * | ||
`, | ||
|
||
[email, username, phone, idFirebase], | ||
); | ||
return convertSnakeToCamel.keysToCamel(rows[0]); | ||
}; | ||
|
||
module.exports = { getAllUsers, getUserById, getUserByIdFirebase, getUserByEmail, updateUser, deleteUser, addUser }; |