티스토리 뷰
보투게더 팀 블로그에서 작성한 글을 가져왔습니다.
문제점
- 게시글, 댓글의 경우에 링크 넣기 버튼을 누른 후 [[ ]] 사이에 사용자가 직접 괄호 사이에 링크를 넣고 저장 버튼을 눌러야 했습니다. 그러나 게시글이나 댓글 기능을 사용하면서 링크 첨부를 하는 방식이 불편하다고 느꼈습니다.
해결 방법
- 본문 링크 삽입 시 [[ ]] 을 붙여주고 있는데, 이걸 작성자에게 붙이도록 하지 않고 렌더링 시 http | https | www로 시작하는 문자를 정규표현식을 이용하여 [[ 주소 ]]를 붙여주도록 함
- 이때 기존의 사용되던 [[ 링크 ]] 텍스트가 있다면 변환되지 않도록 하였음
구현 방법
- 정규식을 이용하여 https로 시작하는 문자가 있는지 확인하여 변환합니다.
- https가 변환된 문자 중 www로 시작하는 문자가 있는지 확인하여 변환합니다.
- 이때 [[ ]]로 감싸져 있는 문자는 변환하지 않습니다.
convertTextToUrl.ts
/**
* https://abc.co.kr/@abc/4
* https://votogether.com/
* http://localhost:3000/posts/100035
* http://votogether.com/
* (?
const httpsOrHttpRegex = /(?<!\[\[)(https?:\/\/[^\s]+)(?!\]\])/g;
/**
* www.naver.com
* www.tistory.com
* (?<!\[\[) 는 앞에 [[로 시작하는 지 여부를 확인한다
* (?<!\/)는 앞에 /로 시작하는 지 여부를 확인한다. https://www 에서 www 앞에 /가 있기에 중복되어 확인하는 것을 방지하기 위함
* \b(w{3})\b 는 www로 시작하는 지 여부를 정확히 확인한다. w가 4개인 경우 판별하지 않음
* [^\s] 는 공백이 아닌 문자인지 여부를 확인한다.
* (?!\]\]) 는 뒤에 ]]로 끝나는 지 여부를 확인한다.
*/
const wwwRegex = /(?<!\[\[)(?<!\/)\b(w{3})\b[^\s]+(?!\]\])/g;
export const convertTextToUrl = (text: string) => {
const httpOrHttpsConvertedText = text.replace(httpsOrHttpRegex, url => `[[${url}]]`);
const wwwConvertedText = httpOrHttpsConvertedText.replace(wwwRegex, url => `[[${url}]]`);
return wwwConvertedText;
};
convertTextToUrl.test.ts
import { convertTextToUrl } from '@utils/post/convertTextToUrl';
test.each([
['www.naver.com 이걸 어째', '[[www.naver.com]] 이걸 어째'],
[
'반갑다 https://github.com/woowacourse-teams/2023-votogether/issues/703 임',
'반갑다 [[https://github.com/woowacourse-teams/2023-votogether/issues/703]] 임',
],
['안녕 wwwww.naver.com', '안녕 wwwww.naver.com'],
['http://localhost:3000/ 피카츄', '[[http://localhost:3000/]] 피카츄'],
[
'http://localhost:3000/http://localhost:3000/ 피카츄',
'[[http://localhost:3000/http://localhost:3000/]] 피카츄',
],
['www.naver.com', '[[www.naver.com]]'],
['[[www.naver.com]] www.naver.com', '[[www.naver.com]] [[www.naver.com]]'],
[
'[[http://localhost:3000/]] http://localhost:3000/',
'[[http://localhost:3000/]] [[http://localhost:3000/]]',
],
[
'[[https://votogether.com/ranking]] https://www.naver.com/',
'[[https://votogether.com/ranking]] [[https://www.naver.com/]]',
],
[
'www.naver.com www.naver.com www.naver.com https://www.npmjs.com/package/dotenv-webpack',
'[[www.naver.com]] [[www.naver.com]] [[www.naver.com]] [[https://www.npmjs.com/package/dotenv-webpack]]',
],
])(
'convertTextToUrl 함수에서 링크가 포함된 문자를 입력했을 때 문자에서 링크는 [[]]로 감싸서 반환한다.',
(word, expectedWord) => {
const result = convertTextToUrl(word);
expect(result).toBe(expectedWord);
}
);
사용 예시
- [[ ]] 로 감싼 문자가 있는지 정규식으로 확인하여 split 해줍니다.
- [[ ]] 를 기준으로 split 하게 되면 괄호는 사라지고 링크 내용만 남게 되고, index % 2 === 1이 링크 내용이 됩니다.
- 링크 내용이라면 a 태그로 반환하고, 아니라면 span으로 반환합니다.
convertTextToElement.tsx
import { MouseEvent } from 'react';
import { convertTextToUrl } from './convertTextToUrl';
export const convertTextToElement = (text: string) => {
const convertedUrlText = convertTextToUrl(text);
const linkPattern = /\[\[([^[\]]+)\]\]/g;
const parts = convertedUrlText.split(linkPattern);
const elementList = parts.map((part, index) => {
if (index % 2 === 1) {
// 링크
const linkText = part;
const linkUrl = linkText.startsWith('http' || 'https') ? linkText : `https://${linkText}`;
return (
<a
onClick={(event: MouseEvent<HTMLAnchorElement>) => {
event.stopPropagation();
}}
key={index}
href={linkUrl}
target="_blank"
style={{ textDecoration: 'underline', color: '#004EC5' }}
rel="noreferrer noopener"
>
{linkText}
</a>
);
}
// 링크가 아닌 문자열
return <span key={index}>{part}</span>;
});
return elementList;
};
본문을 변환해 줘서 사용
<S.Content>
{convertTextToElement(content)}
</S.Content>
링크 첨부하는 영상
댓글 링크 첨부
🚨 safari 에러 발생 🚨
? <와 같은 문법을 safari에서는 최근에서야 지원을 하기 시작해서 업데이트를 하지 않은 safari에서는 사이트가 보이지 않게 됩니다.
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- error
- TopLayer
- 위코드
- electron
- nextjs
- 프론트앤드
- 아차산
- 초보
- CLASS
- 노개북
- env
- import/order
- javascript
- 윤성우 열혈C프로그래밍
- C언어
- nodejs
- 원티드
- NextRequest
- WSL2
- jest
- 북클럽
- 노마드코더
- Storybook
- React
- 우아한테크코스
- 프리온보딩
- 스토리 북
- NextApiRequest
- createPortal
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함