잘하는 다른 사람의 코드를 보고 배우기
mysql2를 설치해야 JSON_arrayagg, JSON_OBJECT 구문을 쓸 수 있다!
npm install mysql2
app.get("/ping", (req,res) => {
res.json({ message : "pong" });
})
app.post("/users/signup", async(req, res, next) => {
const { name, email, profileImage, password } = req.body
await appDataSource.query(
`INSERT INTO users(
name,
email,
profile_image,
password
) VALUES (?, ?, ?, ?);
`, [name, email, profileImage, password]
);
res.status(201).json({ message : "userCreated"});
})
나
app.post("/posts/signup", async(req, res, next) => {
const { title, content, imageUrl, userId } = req.body;
await appDataSource.query(
`INSERT INTO posts(
title,
content,
image_url,
user_id
) VALUES ( ?, ?, ?, ? );
`, [title, content, imageUrl, userId]
);
res.status(201).json({ message : "postCreated"});
})
오현상
app.post("/post", async (req, res, next) => {
const { title, content, image_url, user_id } = req.body;
await appDataSource.query(
`
INSERT INTO posts(
title,
content,
image_url,
user_id
) VALUES (?, ?, ?, ?);
`,
[title, content, image_url, user_id]
);
res.status(201).json({ message: "postCreated" });
});
한상엽
app.post('/posts', async (req, res) => {
const { title, content, contentImage, userId } = req.body;
try {
await database.query(
`INSERT INTO posts(
title,
content,
content_image,
user_id
) VALUES (?,?,?,?);
`,
[title, content, contentImage, userId]
);
return res.status(201).json({ message: 'post successfully created' });
} catch (err) {
if (err.sqlMessage.includes('foreign key constraint fails')) {
return res.status(409).json({ error: 'no such user' });
} else {
return res.status(520).json({ error: 'unknown error' });
}
}
});
나
app.get("/posts/lookup", async(req, res, next) => {
await appDataSource.manager.query(
`SELECT
users.id as userId,
users.profile_image as userProfileImage,
posts.id as postingId,
posts.image_url as postingImageUrl,
posts.content as postingContent
FROM users
INNER JOIN posts
ON users.id = posts.user_id
`, (err, rows) => {
res.status(200).json({ "data" : rows });
});
})
오현상
app.get("/posts", async (req, res) => {
const posts = await appDataSource.query(
`
SELECT
u.id as userId,
u.profile_image as userProfileImage,
p.user_id as postingId,
p.image_url as postingImageUrl,
p.content as postingContent
FROM users u
JOIN posts p ON u.id = p.user_id;
`
);
res.status(200).json({ data: posts });
});
한상엽
app.get('/posts', async (req, res) => {
await database.query(
`SELECT
users.id as userId,
users.profile_image as userProfileImage,
posts.id as postingId,
posts.content_image as postingImageUrl,
posts.content as postingContent
FROM users
INNER JOIN posts ON posts.user_id = users.id
`,
(err, posts) => {
return res.status(200).json(posts);
}
);
});
나
const postArr = rows => {
for(let i=0; i<rows.length; i++){
delete rows[i].userId;
delete rows[i].userProfileImage;
}
return rows;
}
app.get("/users/posts/lookup/:id", async(req, res, next) => {
const { id } = req.params;
await appDataSource.manager.query(
`SELECT
users.id as userId,
users.profile_image as userProfileImage,
posts.id as postingId,
posts.image_url as postingImageUrl,
posts.content as postingContent
FROM users
INNER JOIN posts
ON users.id = posts.user_id
WHERE users.id = ${id};
`,
(err, rows) => {
res.status(200).json(
{ "data" : {
"userId" : rows[0].userId,
"userProfileImage" : rows[0].userProfileImage,
"postings" : postArr(rows)
}
});
});
})
오현상
app.get("/userPost/:userId", async (req, res) => {
const { userId } = req.params;
const userPost = await appDataSource.query(
`
SELECT
users.id,
users.profile_image,
JSON_ARRAYAGG(
JSON_OBJECT(
"postingId",posts.id,
"postingImageUrl", posts.image_url,
"postingContent",posts.content
)
) as postings
FROM users
LEFT JOIN posts ON users.id = posts.user_id
WHERE users.id = ?
GROUP BY users.id;
`,
[userId]
);
const result = userPost.map((userPost) => ({
...userPost,
postings: JSON.parse(userPost.postings),
}));
res.status(200).json({ data: result });
});
2023.01.17 - map과 spread연산자와 JSON.parse로 구현한 이유는 mysql 버전 때문에 파싱이 안돼서 그랬다고 한다. 해당 이슈는 npm i -g mysql2
로 해결했다고~
한상엽
app.get('/users/:userId/posts', async (req, res) => {
const { userId } = req.params;
try {
const rows = await database.query(
`SELECT
users.id as userId,
users.profile_image as userProfileImage,
JSON_ARRAYAGG(
JSON_OBJECT(
"postingId", posts.id,
"postingImageUrl", posts.content_image,
"postingContent", posts.content
)
) as postings
FROM
posts
JOIN users ON posts.user_id = users.id
WHERE user_id = ?
GROUP BY posts.user_id;
`,
[userId]
);
return res.status(200).json({ data: rows });
} catch (err) {
return res.status(409).json({ error: 'invalid input' });
}
});
나
app.patch("/posts/update/:userId/:postId", async(req, res, next) => {
const { userId, postId } = req.params;
const { content } = req.body;
await appDataSource.manager.query(
` UPDATE
posts
SET content=?
WHERE user_id=${userId} and id=${postId};
`, [content]
);
await appDataSource.manager.query(
`SELECT
users.id as userId,
users.name as userName,
posts.id as postingId,
posts.title as postingTitle,
posts.content as postingContent
FROM users
INNER JOIN posts
ON users.id = posts.user_id
WHERE users.id=${userId} and posts.id=${postId};
`, (err, rows) => {
res.status(200).json({"data" : rows});
});
})
오현상
app.patch("/post/:postId", async (req, res, next) => {
const { postId } = req.params;
const { title, content, image_url } = req.body;
const checkExisted = await appDataSource.query(
`
SELECT EXISTS
(SELECT * FROM posts
WHERE id = ?)
AS postExist
`,
[postId]
);
if (checkExisted[0].postExist == 1) {
await appDataSource.query(
`
UPDATE posts SET
title = ?,
content = ?,
image_url = ?
WHERE posts.id = ?
`,
[title, content, image_url, postId]
);
} else {
res.status(404).json({ message: "Non Existing Post" });
}
const editedPost = await appDataSource.query(
`
SELECT
users.id as userId,
users.user_name as userName,
posts.id as postingId,
posts.title as postingTitle,
posts.content as postingContent
FROM users
JOIN posts ON users.id = posts.user_id
WHERE posts.id = ?
`,
[postId]
);
res.status(201).json({ data: editedPost });
});
한상엽
app.put('/posts', async (req, res) => {
const { postingId, postingTitle, postingContent, postingImage } = req.body;
try {
await database.query(
`UPDATE posts
SET
title = ?,
content = ?,
content_image = ?
WHERE id = ?;
`,
[postingTitle, postingContent, postingImage, postingId]
);
const rows = await database.query(
`SELECT
users.id as userId,
users.name as userName,
posts.id as postingId,
posts.title as postingTitle,
posts.content as postingContent
FROM users
INNER JOIN posts
WHERE posts.id = ? AND posts.user_id=users.id;
`,
[postingId]
);
return res.status(200).json({ data: rows });
} catch (err) {
return res.status(409).json({ error: 'invalid input' });
}
});
나
app.delete("/posts/delete/:id", async(req, res, next) => {
const { id } = req.params;
await appDataSource.query(
`DELETE
FROM posts
WHERE id=${id};
`, (err, rows) => {
res.status(204).end();
}
);
})
오현상
app.delete("/post/:userId", async (req, res, next) => {
const userId = req.params.userId;
const { postId } = req.body;
const checkExisted = await appDataSource.query(
`
SELECT EXISTS
(SELECT * FROM posts
WHERE id = ?)
AS postExist
`,
[postId]
);
if (checkExisted[0].postExist == 1) {
await appDataSource.query(
`
DELETE
FROM posts
WHERE posts.user_id = ? and posts.id = ?
`,
[userId, postId]
);
res.status(200).json({ message: "postingDeleted" });
} else {
res.status(404).json({ message: "Non Existing Post" });
}
});
한상엽
app.delete('/posts/:postId', async (req, res) => {
const { postId } = req.params;
await database.query(
`DELETE FROM likes
WHERE post_id = ?
`,
[postId]
);
await database.query(
`DELETE FROM posts
WHERE id = ?
`,
[postId]
);
return res.status(200).json({ message: 'successfully deleted' });
});
그땐 단순히 게시글만 삭제하면 그만인 API과제라고 생각했는데
게시글이 삭제됐으니 그 게시글에 좋아요를 누른 정보도 사라져야 마땅하다.
불필요한 정보가 메모리를 차지하는 꼴이다
전 직장에서 NMS 소프트웨어를 시험 테스트할 때 유사한 경험이 있다
장비 목록이 있는 device table과 장비와 장비를 연결하는 link table이 있다.
장비A
와 장비B
를 링크1
로 연결한다 장비B
를 삭제한다 장비B
를 생성한다.무슨 일이 생기는가? 이 때 장비의 IP주소가 pk다. 새로 생성한 장비임에도 불구하고 장비A
의 IP주소와 장비B
의 IP주소가 연결되어 있다는 링크1
정보가 link table에 남아있어서 링크1
이 표출되었다. 이 후 개발자에게 요청하여 해당 이슈는 개선되었다
글자 그대로 API만 구현하면 완성~하고 끝나는 개발자가 아니라
제품이 어떻게 굴러가는가에 대해 그 다음을 생각하는 개발자가 되자.
나
app.post("/likes/:userId/:postId", async(req, res, next)=>{
const { userId, postId } = req.params;
await appDataSource.query(
`INSERT INTO likes(
user_id,
post_id
) VALUES (${userId}, ${postId});
` );
res.status(200).json({ message : "likeCreated" });
})
app.delete("/likeDelete/:userId/:postId", async(req, res, next)=>{
const { userId, postId } = req.params;
await appDataSource.query(
`DELETE
FROM likes
WHERE user_id=${userId} and post_id=${postId};`
, (err, rows) => {
res.status(200).json({ message : "likesDeleted" });
}
)
})
오현상
app.post("/likes", async (req, res, next) => {
const { userId, postId } = req.body;
const likes = await appDataSource.query(
`
SELECT EXISTS
(SELECT * FROM likes
WHERE user_id = ? and post_id = ?)
AS 'LIKED';
`,
[userId, postId]
);
if (likes[0].LIKED == 0) {
await appDataSource.query(
`
INSERT INTO likes(
user_id,
post_id
) VALUES (?,?);
`,
[userId, postId]
);
res.status(201).json({ message: "likeCreated" });
} else {
await appDataSource.query(
`
DELETE
FROM likes
WHERE user_id = ? and post_id = ?
`,
[userId, postId]
);
res.status(201).json({ message: "likesDeleted" });
}
});
한상엽
app.post('/likes', async (req, res) => {
const { userId, postId } = req.body;
const rows = await database.query(
`SELECT id,user_id,post_id
FROM likes
WHERE user_id = ? AND post_id = ?;
`,
[userId, postId]
);
try {
if (rows.length === 0) {
await database.query(
`INSERT INTO likes(
user_id,
post_id
) VALUES (?,?);
`,
[userId, postId]
);
return res.status(201).json({ message: 'like Created' });
} else {
throw 'already liked';
}
} catch (err) {
if (err === 'already liked') {
return res.status(409).json({ error: err });
} else {
return res.status(520).json({ error: 'Invalid input' });
}
}
});