일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 환경설정
- 다형성
- 대덕인재개발원
- 생성자오버로드
- 컬렉션 타입
- 사용자예외클래스생성
- exception
- GRANT VIEW
- 추상메서드
- EnhancedFor
- 자동차수리시스템
- abstract
- 어윈 사용법
- 정수형타입
- Java
- 오라클
- oracle
- 제네릭
- 인터페이스
- 예외처리
- 집합_SET
- cursor문
- 객체 비교
- 한국건설관리시스템
- NestedFor
- 참조형변수
- 예외미루기
- 메소드오버로딩
- 자바
- 컬렉션프레임워크
- Today
- Total
거니의 velog
(17) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 4 본문
(3) HTML 필터링하기
* sanitize-html 이라는 라이브러리를 사용하여 HTML을 필터링해 보겠다. 이 라이브러리는 HTML을 작성하고 보여 주어야 하는 서비스에서 매우 유용하다. 단순히 HTML을 제거하는 기능뿐만 아니라 특정 HTML만 허용하는 기능도 있기 때문에 글쓰기 API에서 사용하면 손쉽게 악성 스크립트 삽입을 막을 수 있다.
* 백엔드 프로젝트 디렉터리에서 yarn을 사용하여 sanitize-html을 설치하자.
$ yarn add sanitize-html
* 이어서 백엔드 프로젝트의 post.ctrl.js를 수정한다. 먼저 맨 위에 sanitize-html을 불러 오자.
[src/api/posts/posts.ctrl.js]
import Post from '../../models/post';
import mongoose from 'mongoose';
import Joi from 'joi';
import sanitizeHtml from 'sanitize-html';
(...)
* 앞으로 총 세 개의 함수를 수정할 것이다. 우선 포스트 목록을 조회하는 list 함수를 다음과 같이 수정해 보자.
[src/api/posts/posts.ctrl.js]
// html을 없애고 내용이 너무 길면 200자로 제한하는 함수
const removeHtmlAndShorten = (body) => {
const filtered = sanitizeHtml(body, {
allowedTags: [],
});
return filtered.length < 200 ? filtered : `${filtered.slice(0, 200)}...`;
};
/*
GET /api/posts?username=&tag=&page=
*/
export const list = async (ctx) => {
(...)
try {
const posts = await Post.find(query)
.sort({ _id: -1 })
.limit(10)
.skip((page - 1) * 10)
.lean()
.exec();
const postCount = await Post.countDocuments(query).exec();
ctx.set('Last-Page', Math.ceil(postCount / 10));
ctx.body = posts.map((post) => ({
...post,
body: removeHtmlAndShorten(post.body),
}));
} catch (e) {
ctx.throw(500, e);
}
};
* 기존에는 문자열 길이만 제한했는데, 이번에는 HTML을 제거하고 문자열 길이를 200자로 제한했다. 이 작업을 위해 removeHtmlAndShorten 이라는 함수도 새로 만들었다.
* 그 다음에 수정해야 할 API는 포스트의 작성 및 수정에 관한 것이다. 포스트를 작성하고 수정할 때는 모든 HTML을 제거하는 것이 아니라, 악성 스크립트가 주입되는 것을 방지하기 위해 특정 태그들만 허용해 준다.
* sanitize-html은 HTML의 특정 태그와 특정 속성만 허용할 수 있다. 코드의 상단에 sanitizeOptions 라는 객체를 선언하자.
[src/api/posts/posts.ctrl.js]
import Post from '../../models/post';
import mongoose from 'mongoose';
import Joi from 'joi';
import sanitizeHtml from 'sanitize-html';
const { ObjectId } = mongoose.Types;
const sanitizeOption = {
allowedTags: [
'h1',
'h2',
'b',
'i',
'u',
's',
'p',
'ul',
'ol',
'li',
'blockquote',
'a',
'img',
],
allowedAttributes: {
a: ['href', 'name', 'target'],
img: ['src'],
li: ['class'],
},
allowedSchemes: ['data', 'http'],
};
(...)
* sanitizeOptions 객체는 HTML을 필터링할 때 허용할 것을 설정해 준다. 더 자세한 설정은 다음의 공식 메뉴얼을 참고하자.
https://www.npmjs.com/package/sanitize-html
* 이제 write 함수와 update 함수를 업데이트해 보자.
[src/api/posts/posts.ctrl.js]
/*
POST /api/posts
{
title: '제목',
body: '내용',
tags: ['태그1', '태그2']
}
*/
export const write = async (ctx) => {
(...)
const post = new Post({
title,
body: sanitizeHtml(body, sanitizeOption),
tags,
user: ctx.state.user,
});
try {
await post.save();
ctx.body = post;
} catch (e) {
ctx.throw(500, e);
}
};
* 이어서 update 함수도 수정해 보자.
[src/api/posts/posts.ctrl.js]
/*
PATCH /api/posts/:id
{
title: '수정',
body: '수정 내용',
tags: ['수정', '태그']
}
*/
export const update = async (ctx) => {
(...)
const nextData = { ...ctx.request.body }; // 객체를 복사하고
// body 값이 주어졌으면 HTML 필터링
if (nextData.body) {
nextData.body = sanitizeHtml(nextData.body);
}
try {
const post = await Post.findByIdAndUpdate(id, nextData, {
new: true, // 이 값을 설정하면 업데이트된 데이터를 반환합니다.
// false 일 때에는 업데이트 되기 전의 데이터를 반환합니다.
}).exec();
if (!post) {
ctx.status = 404;
return;
}
ctx.body = post;
} catch (e) {
ctx.throw(500, e);
}
};
* 코드를 모두 수정했으면, http://localhost:3000/ 에 들어가서 HTML 태그가 제거된 상태로 포스트 목록이 나타나는지 확인해 보자.
* 이전에는 p 태그와 b 태그가 있었는데, 더 이상 보이지 않는다.
* 그리고 다음과 같이 Postman으로 script 태그를 넣어서 포스트 작성 API를 요청해 보자.
POST http://localhost:4000/api/posts
{
"title" : "스크립트 넣어 보기",
"body" : "<p>안녕하세요 <b>리액트</b> <script>alert('hello world!')</script>",
"tags" : ["스크립트"]
}
* body 안에 p 태그, b 태그, script 태그를 사용했다. 이렇게 요청했을 때 script 태그는 제외되고 나머지 태그만 남아 있다면 HTML 필터링은 성공이다. 다음 JSON은 예시 응답이다.
{
"tags": [
"스크립트"
],
"_id": "65d879aa5363631c74a7ad3e",
"title": "스크립트 넣어 보기",
"body": "<p>안녕하세요 <b>리액트</b> </p>",
"user": {
"_id": "65d5b4de491f6907e4e96f6e",
"username": "bbbb1234"
},
"publishedDate": "2024-02-23T10:55:38.190Z",
"__v": 0
}
'React_프론트엔드 프로젝트' 카테고리의 다른 글
(19) 프론트엔드 프로젝트 : 수정/삭제 기능 구현 및 마무리 1 (0) | 2024.02.23 |
---|---|
(18) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 5 (0) | 2024.02.23 |
(16) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 3 (0) | 2024.02.23 |
(15) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 2 (0) | 2024.02.23 |
(14) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 1 (0) | 2024.02.23 |