다음은 책 리액트를 다루는 기술을 읽고 공부한 내용을 바탕으로 작성된 글입니다.
이제 마지막 회원 인증 관련 API인 로그아웃 구현이다.
이는 매우 간단한 작업으로, 쿠키를 지워주기만 하면 되었다.
1. 로그아웃 구현
- src/api/auth/auth.ctrl.js
/*
POST /api/auth/logout
*/
export const logout = async (ctx) => {
// 로그아웃
ctx.cookies.set('access_token');
ctx.status = 204; // No content
};
- Postman
테스팅 결과 Set-Cookie의 access_token이 비워졌음을 확인하였다.
이렇게 회원 인증 시스템 작성은 끝이 났다.
이제 기존에 구현했던 posts API에 회원 인증 시스템을 도입해보자 !
사용자가 로그인을 해야만 포스트를 작성할 수 있고, 삭제와 수정은 오직 작성자만 할 수 있도록 구현한다.
1. Post 스키마 수정
일단 각 포스트를 어떤 사용자가 작성했는지 알 수 있도록 기존 Post 스키마를 수정한다.
- src/models/post.js
import mongoose from 'mongoose';
const { Schema } = mongoose;
const PostSchema = new Schema({
title: String,
body: String,
tags: [String], // 문자열로 이루어진 배열
publishedDate: {
type: Date,
default: Date.now, // 현재 날짜를 기본값으로 설정
},
user: {
_id: mongoose.Types.ObjectId,
username: String,
},
});
const Post = mongoose.model('post', PostSchema);
export default Post;
작성자 정보를 담을 user 필드를 추가했다.
이전에 생성한 포스트 데이터 (#1~#40) 들은 더이상 유효하지 않으므로 전부 삭제 처리하였다.
2. 로그인시에만 API 사용
checkLoggedIn 이라는 미들웨어를 만들어, 로그인시에만 글쓰기, 수정, 삭제가 가능하도록 구현한다.
- src/lib/checkLoggedIn.js
const checkLoggedIn = (ctx, next) => {
if (!ctx.state.user) {
ctx.status = 401; //Unauthorized
return;
}
return next();
};
export default checkLoggedIn;
로그인 상태를 확인하고, 로그인된 경우에만 다음 미들웨어를 실행한다.
이를 posts 라우터에 적용한다.
- src/api/posts/index.js
import Router from 'koa-router';
import * as postsCtrl from './posts.ctrl';
import checkLoggedIn from '../../lib/checkLoggedIn';
const posts = new Router();
posts.get('/', postsCtrl.list);
posts.post('/', checkLoggedIn, postsCtrl.write);
const post = new Router(); // /api/posts/:id
post.get('/', postsCtrl.read);
post.delete('/:id', checkLoggedIn, postsCtrl.remove);
post.patch('/:id', checkLoggedIn, postsCtrl.update);
//module.exports = posts;
posts.use('/:id', postsCtrl.checkObjectId, post.routes());
export default posts;
3. 포스트 작성시 사용자 정보 넣기
이제 포스트를 작성할 때 사용자 정보를 넣어 데이터베이스에 저장하도록 구현해야한다.
- src/api/posts/posts.ctrl.js
/*
POST /api/posts
{
title: "제목",
body: "내용",
tags: ['태그1','태그2']
}
*/
export const write = async (ctx) => {
const schema = Joi.object().keys({
// 객체가 다음 필드를 가지고 있음을 검증
title: Joi.string().required(), // required가 있으면 필수항목을 의미
body: Joi.string().required(),
tags: Joi.array().items(Joi.string()).required(), // 문자열로 이루어진 배열임을 뜻함
});
// 검증하고 나서 검증 실패인 경우 에러 처리한다.
const result = schema.validate(ctx.request.body);
if (result.error) {
ctx.status = 400; //Bad Request
ctx.body = result.error;
return;
}
const { title, body, tags } = ctx.request.body;
const post = new Post({
title,
body,
tags,
/* 추가한 부분 */
user: ctx.state.user,
});
try {
await post.save();
ctx.body = post;
} catch (e) {
ctx.throw(500, e);
}
};
- Postman
테스팅 결과, 성공적으로 포스트에 user의 정보가 담겨 저장되는 것을 확인하였다.
4. 포스트 수정 및 삭제 시 권한 확인하기
이제 마지막으로 포스트 작성자에 한해서만 포스트를 수정/삭제가 가능하도록 구현한다.
이 작업을 미들웨어로 처리하고 싶다면 id로 포스트를 조회하는 작업 또한 미들웨어로 해주어야 한다.
따라서 기존의 checkObjectId(Id 유효성 검사 미들웨어)를 getPostById로 바꾸고, 해당 미들웨어에서 id로 포스트를 찾은 후 ctx.state에 담는다.
- src/api/posts/posts.ctrl.js
export const getPostById = async (ctx, next) => {
const { id } = ctx.params;
if (!ObjectId.isValid(id)) {
// id의 유효성 검사
ctx.status = 400; // Bad Request
return;
}
try {
const post = await Post.findById(id);
// 포스트가 존재하지 않을 때
if (!post) {
ctx.status = 404; // Not Found
return;
}
ctx.state.post = post;
return next();
} catch (e) {
ctx.throw(500, e);
}
};
이후 posts 라우터에도 이를 반영한다.
- src/api/posts/index.js
import Router from 'koa-router';
import * as postsCtrl from './posts.ctrl';
import checkLoggedIn from '../../lib/checkLoggedIn';
const posts = new Router();
posts.get('/', postsCtrl.list);
posts.post('/', checkLoggedIn, postsCtrl.write);
const post = new Router(); // /api/posts/:id
post.get('/', postsCtrl.read);
post.delete('/:id', checkLoggedIn, postsCtrl.remove);
post.patch('/:id', checkLoggedIn, postsCtrl.update);
//module.exports = posts;
posts.use('/:id', postsCtrl.getPostById, post.routes());
/* 반영된 부분 */
export default posts;
그 다음, read 함수 내에서 id로 포스트를 찾는 코드를 간소화해준다.
- src/api/posts/posts.ctrl.js
/*
GET /api/posts/:id
*/
export const read = async (ctx) => {
ctx.body = ctx.state.post;
};
이제 checkOwnPost 라는 미들웨어를 만든다. 이 미들웨어가 현재 수정/삭제 하려는 포스트가 사용자 본인의 포스트인지 확인하는 역할을 할 것이다.
- src/api/posts/posts.ctrl.js
export const checkOwnPost = (ctx, next) => {
const {user, post} = ctx.state;
if( user._id !== post.user._id.toString()) {
ctx.status = 403;
return;
}
return next();
}
이제 이 미들웨어를 수정 및 삭제 API에 적용해주면 된다.
- src/api/posts/index.js
post.delete('/:id', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.remove);
post.patch('/:id', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.update);
- Postman
테스팅을 위해 username:user1 으로 회원가입 후, 포스트를 하나 생성했다.
이후 로그아웃하고, username:subbni로 로그인 한 다음 위의 포스트를 삭제하는 요청을 보내보았다.
근데 띠용 .. Method Not Allowed가 떴다.
뭐가 잘못됐나 .. 하고 코드를 확인해보던 중,
- src/api/posts/index.js 에서
post.get과 post.delete의 경로를 './:id'에서 '/'로 바꾸는 것을 빼먹은 것을 확인했다.
- src/api/posts/index.js
import Router from 'koa-router';
import * as postsCtrl from './posts.ctrl';
import checkLoggedIn from '../../lib/checkLoggedIn';
const posts = new Router();
posts.get('/', postsCtrl.list);
posts.post('/', checkLoggedIn, postsCtrl.write);
const post = new Router(); // /api/posts/:id
post.get('/', postsCtrl.read);
post.delete('/', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.remove);
post.patch('/', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.update);
//module.exports = posts;
posts.use('/:id', postsCtrl.getPostById, post.routes());
export default posts;
수정 후 다시 요청을 보내자 성공적으로 Forbidden이 뜨는 것을 확인하였다.
'Front-End > React' 카테고리의 다른 글
[React] 블로그 만들기 8 - 토큰 발급 및 검증 (0) | 2022.03.15 |
---|---|
[React] 블로그 만들기 7 - 로그인 구현 (0) | 2022.03.15 |
[React] 블로그 만들기 6 - 회원가입 구현 (0) | 2022.03.15 |
[React] 블로그 만들기 5 - 페이지네이션 구현 (0) | 2022.03.14 |
[React] 블로그 만들기 4 - Request Body 검증 (0) | 2022.03.14 |