Group By 는 같은 값을 가진 Rows (Records) 에 대해서 그룹화 하는 키워드 이다.
주로 Aggregation Function 과 함께 쓰인다.
일반적으로 많이 쓰이는 Aggregation Function 은 MAX(), MIN(), SUM() 같은 것들인데
이 글에서는 JSON_ARRAYAGG() 라는 JSON 데이터를 배열로 Aggregation 하는 함수를 적용시켜 보겠다.
JSON_OBJECT() 함수는 key ,value 를 인자로 받아 JSON 형태로 반환해주는 함수이다.
사용전
사용후
posts 에 대해서 id, title, posts 인 컬럼들이 JSON_OBJECT 를 사용했을때 posts 라는 컬럼 하나로 합쳐졌다.
AGG 는 Aggregate 의 약자로 합계를 구한다는 의미를 내포하고 있는 것 같다.
그리고 JSON_ARRAYAGG() 함수는 다음과 같이 mysql 공식문서에 설명 되어 있다.
Aggregates a result set as a single JSON array whose elements consist of the rows.
하나의 JSON 배열을 구한다는 의미이고 배열에는 여러개의 rows 들이 있을 수 있다고 한다.
위에서 Aggreagation 함수는 Group By 와 함께 쓰인다고 설명하였으니
users.id 를 Group 하여 JSON_ARRAYAGG() 함수를 사용해보면
다음과 같이 users.id 로 그룹화 된 Rows 를 가져올 수 있고 posting 필드 값이 배열로 되어 있는 것을 확인 할 수 있다.
위에 SQL 문에서는 users.id 가 개별적으로 출력되었고 JSON_OBJECT 값들도 각각의 Row 로 출력이 되었지만
JSON_ARRAYAGG() 함수와 GROUP BY 를 이용해서 공통된 값을 가진 필드를 GROUP 하고 JSON 값을 배열에 집어넣어 하나의 Row 로 표현 할 수 있다.
그래서 이 SQL 문을 현재 작업중인 node.js instagram API 서버에 적용해보자.
유저 한명이 작성한 모든 게시물을 가져오는 api 이고 posting 을 json 형태로 구조화하여 postings 라는 배열에 넣는 작업을 한다.
// 과제 5 유저 한명이 작성한 모든 게시물 가져오기
app.get('/posts/:userId', async (req, res) => {
const rawQuery = `
SELECT
u.id AS userId,
u.profile_image AS userProfileImage,
p.id AS postingId,
p.image_url AS postingImageUrl,
p.content AS postingContent
FROM posts p
INNER JOIN users u
ON p.user_id = u.id WHERE u.id = ${req.params.userId};
`
try {
await database.query(rawQuery, (err, rows) => {
user = {}
postings = []
for (const row of rows) {
if (!user['userId']) {
user['userId'] = row['userId']
}
if (!user['userProfileImage']) {
user['userProfileImage'] = row['userProfileImage']
}
posting = {}
posting['postingId'] = row['postingId']
posting['postingImageUrl'] = row['postingImageUrl']
posting['postingContent'] = row['postingContent']
postings.push(posting)
}
user['postings'] = postings
const result = {}
result['data'] = user
res.status(200).json(result)
})
} catch (error) {
return res.status(400).json({ message: error.sqlMessage })
}
})
// 과제 5 유저 한명이 작성한 모든 게시물 가져오기
app.get('/posts/:userId', async (req, res) => {
const rawQuery = `
SELECT
u.id AS userId,
u.profile_image AS userProfileImage,
JSON_ARRAYAGG(JSON_OBJECT(
'postingId', p.id,
'postingImageUrl', p.image_url,
'postingContent', p.content)) AS posting
FROM posts AS p
INNER JOIN users AS u
ON p.user_id = u.id WHERE u.id = ${req.params.userId}
GROUP BY u.id;
`
try {
await database.query(rawQuery, (err, rows) => {
return res.status(200).json({ data: rows })
})
} catch (error) {
return res.status(400).json({ message: error.sqlMessage })
}
})
이런식으로 코드가 더 간결해 질 수 있는 방법을 항상 탐구하고
내가 작성한 코드에 항상 의문을 품어서 더 나은 코드를 작성할 수 있도록 고민을하고
다른 사람은 어떤식으로 작성했는지 다른 문제 해결방식 또한 참고해본다면
좋은 개발자가 될 수있지 않을까 하고 생각해봅니다.
const [rawQuery] = await conn.query()
구조분해 할당으로 해서 윗 코드처럼 짤수 있는 방법이 있습니다. 한번 고려해보세요:)
${req.params.userId} << 이부분도 구조분해 할당으로 const {userId} = req.params 식으로 하여 해주는게 가독성이 좋습니다.
더 나아가시면 MVC 패턴으로 분리해서 리팩토링 해보세요^^