React, Mongoose(MongoDB), Node js 게시판 검색 구현

2021. 3. 13. 16:44·DEV/Node.js

졸업 작품으로 운동 자세 분석 웹을 개발 중이다.

거의 막바지라 슬슬 시간이 생겼으니 방금 게시판 검색을 구현해서 한번 적어보려 한다.

요근래 계속 프론트만 만지다가 서버랑 DB 만지니 어려웠다.

 

아 그리고 저는 정석으로 Node 랑 서버를 공부 하지 않아서 '쟤 왜 저렇게 힘들게 구현하지 ?' 라고 생각하실 수도 있으니 제 방식이 별로라면 다른 블로그에서 봐주세요

아직 갈길이 먼 개발자 꿈나무 입니다..

 

게시판 포스팅이나 여타 다른 것들은 다 만들어 놨었고, 시간이 남아 내가 검색 기능을 구현한 것이다.

 

전체적으로 백엔드 부분은 

ip99202.github.io/posts/nodejs,-mongodb-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5/

 

nodejs, mongodb 게시판 검색 기능

목표 게시판 내의 모든 게시물들에 대해 제목, 내용, 제목 + 내용으로 검색할 수 있게 한다. 예를 들어 제목이 ‘helloworld’라는 게시물이 있으면 ‘wor’을 검색해도 나올 수 있게 하는 것이 목표

ip99202.github.io

이 분의 블로그를 많이 참조하였다.

 

이런 식으로 게시판 하단 pagination 위에 검색 form 을 추가해주었다.

 

<form method="get" onSubmit={onSubmit}>
   <select value={options} onChange={selectHandle}>
   	<option value="title">제목</option>
  	<option value="body">내용</option>
   	<option value="title_body">제목+내용</option>
   </select>
 	<input 
       type="text" name="searchText"
	   onChange={ handleChange }
	   value={value}
	   placeholder="검색어를 입력하세요"/>
	<div>
    	<Cbutton type = "submit" onClick={onSubmit}>검색</Cbutton>
    </div>
</form>
 const onSubmit = (e) => {
        e.preventDefault();
        const { page } = qs.parse(location.search, {
            ignoreQueryPrefix: true,
        });
        
        dispatch(searchPosts({ page: page, option: options, content: value }));
        setValue('');
};

대충 프론트는 이런 식으로 짰다. 

중요한 건 나는 querystring 을 통해 uri 에서 api/posts/search?option=title&content=아무거나검색할말

 

이런식으로 들어가게 할 거기 때문에 form 태그에서 method="get"을 지정해줘야한다. 

그러면 자동으로 querystring을 쓰게끔 uri에 들어간다.

onSubmit에서 page 는 현재 주소에서 파싱해오고 dispatch 를 통해 searchPosts 함수에 page, option, content 를 보낸다.

 

export const searchPosts = ({ page, option, content }) => {
  const queryString = qs.stringify({
    page,
    option,
    content
  });
  return client.get(`/api/posts/search?${queryString}`);
}

frontend 에서 searchPosts 함수를 선언해준다. 

get method로 주소는 아까 정한 것 처럼 /api/posts/search?${queryString} 으로 지정한다.

또한 qs.stringify를 써서 쿼리스트링 변환을 해준다.

 

이제 백엔드를 보자.

 

먼저 검색 기능을 쓰기 위해서는 인덱스가 만들어져야한다. 

내 Post 스키마에서 title, body 를 검색하고 싶다면

PostSchema.index({ title: 'text', body: 'text' });

이런 식으로 스키마를 만드는 코드 맨 마지막에 이렇게 title과 body에 index를 만들어줘야 한다. (mongoose기준이다)

mongoDB에서는 createIndex 를 쓴다고 한다.

 

const posts = new Router();

... 중략

posts.get('/search', postsCtrl.searchPosts);
//GET /api/posts/search
export const searchPosts = async ctx => {
  console.log(ctx.query);
  const page = parseInt(ctx.query.page || '1', 10);
  let options = [];
  if(page < 1){
    ctx.status = 400;
    return;      
  }
  try{
    if(ctx.query.option == 'title'){
      options = [{ title: new RegExp(ctx.query.content) }];
    } else if(ctx.query.option == 'body'){
      options = [{ body: new RegExp(ctx.query.content) }];
    } else if(ctx.query.option == 'title_body'){
      options = [{ title: new RegExp(ctx.query.content) }, { body: new RegExp(ctx.query.content) }];
    } else {
      const err = new Error('검색 옵션이 없습니다.');
      err.status = 400;
      throw err;
    }
   
    const posts = await Post.find({ $or: options })
    .sort({ _id: -1 })
    .limit(10)
    .skip((page - 1) * 10)
    .lean()
    .exec();
    const postCount = await Post.countDocuments(posts).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);
  }
};

postCount 는 pagination을 위한 코드고, 검색 기능에 중요한 코드는 ctx.query.option을 비교하는 부분이다.

아까 form 태그 안에서 select-option 태그가 가지고 있는 각 value 값과 검증을 한다. 

사용자가 제목을 선택했다면 ctx.query.option => title이 들어가 있을 것이다.

즉 /api/posts/search?option=title  이렇게 queryString이 존재하는 것이다.

실제로 ctx.query 를 찍어보면 

 

[0] [Object: null prototype] { option: 'title', content: 'kid' }

 

이런식으로 json 형태로 찍힌다.

page는 1페이지일 때 queryString에 없어서 만약 page가 따로 주어지지 않는다면 1을 default로 준다.

 

이제 options 배열에 우리가 검색할 조건들을 넣어줄 것이다.

 if(ctx.query.option == 'title'){
      options = [{ title: new RegExp(ctx.query.content) }];
    } else if(ctx.query.option == 'body'){
      options = [{ body: new RegExp(ctx.query.content) }];
    } else if(ctx.query.option == 'title_body'){
      options = [{ title: new RegExp(ctx.query.content) }, { body: new RegExp(ctx.query.content) }];
    } else {
      const err = new Error('검색 옵션이 없습니다.');
      err.status = 400;
      throw err;
    }

RegExp 란게 보이는데 정규표현식 객체를 만드는 것이라고 한다.

'hi' 란 검색어를 찾고 싶다면 먼저 'hi'를 정규표현식 객체로 만들어야한다.

이제 여기까지 하면 options 배열을 가지고 find()만 해주면 된다.

const posts = await Post.find({ $or: options })
    .sort({ _id: -1 })
    .limit(10)
    .skip((page - 1) * 10)
    .lean()
    .exec();
    const postCount = await Post.countDocuments(posts).exec();
    ctx.set('Last-Page', Math.ceil(postCount / 10));
    ctx.body = posts.map((post) => ({
      ...post,
      body: removeHtmlAndShorten(post.body),
    }));

 뭐가 거창해보이는데 중요한건 그냥 Post.find({ $or : options }) 요 부분이다.

or 옵션을 사용해 find 시 정말 말 그대로 options중에 하나만 만족해도 다 찾아준다.

어차피 제목, 내용, 제목+내용 이니까 or만 사용해주면 된다.

 

백엔드 코드도 다 짰으니 프론트에서 해보기 전에 Postman에서 한번 테스트 해보자

 

자 이렇게 Postman에서 Params의 Key Value - option, content 를 지정해주자

실제로는 form 태그에 의해 자동으로 된다.

 

결과를 보면 성공적으로 Post 스키마에서 title 안에 'kid'가 들어가는 데이터를 보여준다.

 

터미널에도 잘 찍힌다.

이제 프론트를 마저 잘 구현해서 실제로 검색이 되는 지 보면 된다.

제목으로 수고를 검색했다.

잘 된다.

title_body 옵션으로도 해보자

 

운동을 검색해봤다.

잘된다.

 

그럼 게시판 만드는 여러분 모두 힘내세요

 

저작자표시 비영리 변경금지 (새창열림)

'DEV > Node.js' 카테고리의 다른 글

[CI/CD] AWS CodeDeploy, CodePipeline 으로 node.js, ec2, git 배포 자동화하기  (4) 2021.12.16
[Node.js] Express, TypeScript, MongoDB 회원가입 (1)  (3) 2021.06.18
Node.js , express, mongoDB, typescript 초기 설정  (0) 2021.05.26
Node.js + Koa + Typescript 로 슬랙 봇 개발해보기 (1)  (0) 2021.04.19
Node js + Express + Socket.io 로 1대 1 채팅 구현하기 (1)  (4) 2021.03.14
'DEV/Node.js' 카테고리의 다른 글
  • [Node.js] Express, TypeScript, MongoDB 회원가입 (1)
  • Node.js , express, mongoDB, typescript 초기 설정
  • Node.js + Koa + Typescript 로 슬랙 봇 개발해보기 (1)
  • Node js + Express + Socket.io 로 1대 1 채팅 구현하기 (1)
jobchae
jobchae
말하는 감자지만, 코드를 끄적이는 Node.js 백엔드 개발자입니다.
  • jobchae
    JOBCHAE
    jobchae
  • 전체
    오늘
    어제
    • 🚀 JOBCHAE (177)
      • DEV (146)
        • PS (108)
        • Node.js (12)
        • React (3)
        • docker (1)
        • 잡다한 개발 일지 (20)
        • injection (1)
        • CI CD (0)
        • JS, TS (1)
      • 축구 (0)
      • 일상 (19)
      • 영화 (3)
      • 음악 (8)
  • 블로그 메뉴

    • 💻 Github
    • 🙋🏻 Linkedin
    • 📖 방명록
  • 링크

    • PS Github
  • 공지사항

  • 인기 글

  • 태그

    Nest
    mongoDB
    앱잼
    알고리즘
    SOPT
    PS
    백준
    nodejs
    DFS
    aws
    솝트
    일상
    슬랙
    우선순위큐
    DP
    위상정렬
    리액트
    렛츠락페스티벌
    node.js
    회고
    GitHub
    슬랙봇
    slack
    Express
    typescript
    이분탐색
    Nest.js
    boj
    react
    BFS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
jobchae
React, Mongoose(MongoDB), Node js 게시판 검색 구현
상단으로

티스토리툴바