[Spring]마크다운 기능과 검색시스템

Beanzinu·2022년 5월 13일
0

스프링부트

목록 보기
2/7

서론

보통 개발자들은 글을 작성할 때 다양한 편의기능을 통해 일목요연하게 정리한다.
예를 들어, 코드와 같은 경우에는 Hello World 등으로 구분하여 표현하거나 사용한 기술스택들을 태그하여 작성하기도 한다.
나는 이번 Team Project에서 커뮤니티를 구축하면서 게시물들을 작성할 때 어떻게 편의기능들을 만들었고 이러한 게시물들을 검색하는 시스템을 구축했는 지 정리해보려고 한다.

게시물 작성 시 편의기능 추가

언어: JavaScript
라이브러리: React

  1. 작성한 게시물에 코드스페이스를 의미하는 "```"의 존재 여부를 확인
  • 모든 본문을 "```" 기준으로 String의 내장함수 split을 통해 배열로 분할
  • 이를 기준으로 일반 텍스트와 코드스페이스 안의 텍스트를 구분하여 렌더링
let tmp = content.split("```");
        return(
                tmp.map( (text,index) => {
                    // 본문
                    if( index % 2 == 0){
                        return(
                            <pre style={{ fontFamily: "inherit" ,verticalAlign: 'middle' }}>
                                <Grid container spacing={2}>
                                {renderLine(text)}
                                </Grid>
                            </pre>
                        );
                    }
                    // 코드
                    else {
                        text = text.replace('\n','');
                        return(
                            <Typography key={index} sx={{ p: 2 ,backgroundColor: 'rgb(240,240,240)' }}>
                                <pre style={{ fontFamily: 'inherit' }}>{renderLine(text)}</pre>
                            </Typography>
                        );
                    }
                  });
        		);
  1. 1번에서 나누어진 텍스트들을 한 줄 단위로 다시 나누어 렌더링
  • 한 줄에 대하여 사용자들이 다른 마크다운 코드를 삽입하였을때 확인하기 위함
const renderLine = (text) => {
        return(
            text.split("\n").map( line => {
                return(
                    <Grid item xs={12} sx={{ flexDirection: 'row' ,display: 'flex' ,justfiyContent: 'center' ,alignItems: 'center' }}>
                        {renderStrongText(line)}
                    </Grid>
                );
            })
        );
}
  1. 각 줄에 대하여 굵은 글씨를 표현하는 마크다운 코드가 있는 지 확인
  • 정규식을 통해 **text**의 형식을 확인하여 일반 텍스트와 굵은 글씨의 텍스트를 구분하여 직접 배열에 넣어줬다.
const renderStrongText = (value) => {
        let text = value;
        const regExp = /\*\*.{0,}\*\*/m;
        let s = [];
        
        // **text** 모두 찾을때까지
        while( text && text.length > 0 ){
            if( regExp.test(text) ){
                const startIndex = text.search(regExp);
                // normal text
                s.push({ "type" : "normal" , "text" : text.substring(0,startIndex) });
                let restText = text.substring(startIndex+2,text.length);
                const finIndex = restText.search(/\*\*/); 
                // // strong text
                s.push({ "type" : "strong", "text" : restText.substring(0,finIndex) })
                text = restText.substring(finIndex+2,restText.length);
            }
            else {
                s.push({ "type": "normal", "text" : text });
                text = "";
            }
        }
        return(
                s.map( item => {
                    if( item.type === "normal") 
                        return <pre style={{ fontFamily: 'inherit' }}>{item.text}</pre>
                    else if( item.type === "strong")
                        return <b style={{ fontFamily: 'inherit' ,fontWeight: 'bold'}}>{item.text}</b>    
            })
        )

    }

게시물 검색 시스템

게시물 검색은 크게 3가지로 나누었다.

  • 일반 검색
  • 정확한 문구로 검색
  • 태그로 검색
  1. 일반 검색
  • 일반 검색으로 검색한 경우 공백 기준으로 글자를 구분하여 "일반","검색"으로 나누어 모두 검색하도록 하였다.
  • JPARepository에서 기본적으로 제공하는 findAllBy~ContainingAnd~Containing 인터페이스를 통해 제목과 내용에 대해 이중 검색이 가능하다.
  1. 정확한 문구로 검색
  • 나는 정확한 문구의 검색인 경우 문구를 ""로 감싸서 넣도록 구현했다.
    ( 일반 검색 -> "일반 검색" )
  • 정확한 문구의 검색인 경우 공백 기준으로 단어를 나누어 검색하면 안되므로 '"'가 있는 경우 flag를 설정하여 다음 '"'가 나올 때 까지를 하나의 단어로 묶어 검색하도록 하였다.
  1. 태그로 검색
  • 태그 검색은 [react]로 검색한 경우 정확한 문구로 검색과 같이 [] 안에 있는 단어를 태그로 가지는 게시물들을 검색하였다.
  • api 호출 시 params로 검색 문구들을 넘기는 데 이때 []와 같은 경우에는 특정 키에 대하여 multiple values를 넘기는 경우 사용되기 때문에 [react] -> %react% 로 변환하여 호출하고 그에 따라 서버에서 처리하였다.

작성코드

		String[] s = sentence.split(" ");
        List<Post> findPosts = new ArrayList<Post>();

        boolean exact_phrase_flag = false;
        String exact_phrase_concat = "";
        // 검색된 문장을 공백 기준으로 모두 검색하여 결과에 추가한다.
        for ( String keyword : s ) {

            // 1. [태그] 검색일 경우 -> %태그% 로 변환하여 요청받음.
            if( keyword.startsWith("%") && keyword.endsWith("%") ) {
                // [tag] -> tag
                String tag = keyword.substring(1,keyword.length()-1);
                Optional<List<Post>> result = postRepository.findAllByTagsContaining(tag);
                addSearchResults(findPosts,result);
            }

            // 2. "정확한 문구" 검색일 경우
            // ex) "react"
            else if( keyword.startsWith("\"") && keyword.endsWith("\"")){
                // "react" -> react
                String exact_phrase = keyword.substring(1,keyword.length()-1);
                Optional<List<Post>> result = postRepository.findAllByTitleContainingOrContentContaining(exact_phrase,exact_phrase);
                addSearchResults(findPosts,result);
            }
            // ex) "react and native" -> "react
            else if( keyword.startsWith("\"")){
                exact_phrase_flag = true;
                exact_phrase_concat += keyword.substring(1);
            }
            // native"
            else if( keyword.endsWith("\"")){
                exact_phrase_concat += " " + keyword.substring(0,keyword.length()-1);
                Optional<List<Post>> result = postRepository.findAllByTitleContainingOrContentContaining(exact_phrase_concat,exact_phrase_concat);
                addSearchResults(findPosts,result);
                // 초기화
                exact_phrase_flag = false;
                exact_phrase_concat = "";
            }
            // and
            else if( exact_phrase_flag ){
                exact_phrase_concat += " " + keyword;
            }

            // n. 일반적인 검색
            else {
                Optional<List<Post>> result = postRepository.findAllByTitleContainingOrContentContaining(keyword, keyword);
                addSearchResults(findPosts,result);
            }
        }
        return findPosts;
profile
당신을 한 줄로 소개해보세요.

0개의 댓글