완성링크
저처럼 이름을 잘 못 짓는 사람들에게 있어서 닉네임 정하는 건 정말 힘든 일입니다. 남들이 보고 재밌어할 닉네임을 정하고 싶은데 딱히 떠오르는 아이디어도 없었고요. 필요하면 만드는 게 공돌이 마인드인 만큼 이번 주말의 프로젝트로는 닉네임 생성기를 만들어보겠습니다.
0. 있는 거 살펴보기
이미 좋은 닉네임 생성기가 있다면 만들 필요가 없으니 먼저 닉네임 생성기들이 어떤 상태인지 살펴봤습니다.
웹툴이라는 사이트의 닉네임 생성기는 외국어만 되는 것 같습니다. 저는 한글 닉네임만 만들 생각이니까 겹치지 않네요. 두 글자 닉네임 생성기 사이트도 있습니다. 몇 번 눌러보니 등록된 글자 중 랜덤으로 2개를 합쳐서 만들어주는 것 같습니다. 독특한 이름도 있지만 어색하거나 어감이 이상한 것도 꽤 나오는 것 같네요. 랜덤 단어 생성 사이트도 검색에 잡혔습니다. 작동 방법이 조금 어려웠는데 제가 원하는 느낌은 아니었습니다.
제가 좋아하는 닉네임은 색다른 두 개의 단어가 합쳐져 독특한 느낌을 주는 걸 좋아합니다. 전설의 공익요원, 할머니와 곽철용 같이 서로 어울리지 않는 단어가 뭉쳐진 게 재밌더라고요. 그럼 이걸 어떻게 만들어볼지 설계를 해봅시다.
1. 단어 뭉치 가져오기
먼저 닉네임에 들어갈 단어 뭉치가 필요합니다. 위키 낱말 사전에는 26,714개가 검색됩니다. 명사만 했는데도 상당하네요.
단어가 어마어마하게 많으니 직접 하나씩 옮기다간 하루가 다 지나갈 것입니다. 시간을 아끼기 위해 간단한 웹 크롤러를 만들어봅시다.
1.1 웹 크롤러 만들기
웹 크롤러는 웹에 존재하는 데이터를 갈퀴로 긁어오듯 가져오는 도구를 뜻합니다. 구글, 네이버, 다음 같은 검색 사이트는 크롤러를 돌려 사이트 인덱싱을 하는데 쓰이지만 저희는 간단하게 타깃 사이트의 원하는 데이터만 가져오는 형태로 만들어봅시다.
필요한 건 axios와 cheerio 모듈입니다. axios는 HTTP 메서드를 쏴주는데 편리한 도구이고, cheerio는 DOM 셀렉팅을 하는 데 사용할 겁니다. DOM 셀렉팅은 jQuery 사용자 분들은 $('.target-class') 형태로 사용해보셨을 겁니다.
위의 링크를 참고해(카피해) 빠르게 만들어봅시다. 타깃 클래스와 타깃 URL 부분만 수정하면 됩니다.
이렇게 간단하게 단어만 빼낼 수 있습니다. 아래 콘솔 창에 보시면 단어만 획득한 것을 보실 수 있습니다. 그럼 이제 여러 페이지에 대해서 크롤링을 진행해봅시다. 한 페이지 데이터를 획득한 후에 다음 페이지에 저장된 URL을 획득해 다시 단어를 획득합니다. 이를 반복하면 27,000여 개의 단어를 다 리스트업 할 수 있을 겁니다.
$('#mw-pages > a:last-child').attr('href')
위의 선택자를 통해서 다음 페이지 버튼에 저장된 href값을 획득합니다. /w/index.php?title=.... 형태로 되어있는데 origin(https://ko.wiktionary.org)이 빠져있으므로 origin값은 추가로 입력해주도록 합니다.
적당히 루프를 만들어 돌려보니 잘 작동합니다. 한 페이지 로드할 때마다 시간이 꽤 걸리긴 하지만 큰 문제는 없는 것 같습니다.
1.2 100퍼센트 초과?
한 페이지에 200개씩 26,714개의 단어만 가져오면 끝(135개 내외의 페이지)인 줄 알았는데 이상하게 100%를 초과해도 계속 진행됐습니다.
??? 이럴 줄 알았으면 url 체크라도 해둘걸 싶습니다. 아무래도 페이지 진행 중 문제가 생긴 걸로 보이는데, 마지막 단어의 마지막 페이지에서 정상적으로 루프가 멈추는지 테스트해봤습니다. 문제는 last-child 선택자였습니다.
마지막 페이지에서 :last-child <a>는 이전 페이지였고, 이전 페이지로 들어가고, 마지막 페이지로 가는 걸 반복하고 있었던 겁니다. 다음 페이지 버튼만 선택될 수 있도록 text 검사를 추가로 하면 될 것 같습니다.
마지막 <a>의 텍스트가 "이전 페이지"이면 루프 종료 이제 다시 돌려봅시다.
단어 개수는 맞는데 제가 퍼센트 비율을 잘못 계산했나 보네요. 50%에서 완료되는 걸 보니 2를 곱했어야 했네요. 이렇게 수학을 못하는데도 코딩을 하네요;;
2. 단어 합쳐주기
어쨌든 위키 낱말 사전을 통해서 26,714개의 단어를 가져왔습니다. 데이터는 가져왔지만 슬프게도 적절하지 못한 데이터도 섞여있습니다.
ㄲ, ㄳ도 명사라니.. 2.1 초성 지워주기
ㄲ, ㄳ, ㄵ 같은 초성만 있는 것도 명사라니 한글의 세계는 정말 깊었습니다. 그렇다고 초성만 있는걸 닉네임에 넣을 순 없으니 이걸 빼보겠습니다. 뺀 방법은 26,714개의 단어 중 한 글자 단어만 필터링한 후, 초성만 지우는 코드를 짤 수도 있겠으나 가, 나, 다, 라...로 시작하는 단어를 찾아 그 앞에 있는 초성만 지워주면 더 빨리 할 수 있었습니다. 한 2분 만에 초성은 다 지울 수 있었습니다.
2.2 단어 합치기
이제 단어를 합쳐봅시다. "A의 B", "A와 B", "A에 간 B"으로 테스트해봅시다.
저세상 닉네임 만들기 성공! 세상에나... 정말 생각지도 못한 닉네임들이 나타나고 있네요. 긴장도, 묘적, 마전, 과랭, 몰선묘법 모두 처음 들어보는 단어입니다. 어쨌든 제가 좋아하는 스타일의 근본 없는 닉네임이 잘 나오고 있습니다.
그럼 이제 웹사이트로 만들어봅시다.
3. 사이트 만들기
이 사이트는 클릭하면 랜덤 닉네임만 나오면 됩니다. 제 예전 프로젝트인 바삭한 햇빛과 구조가 똑같네요!
코드를 그대로 가져와서 안의 텍스트만 바꿔줍시다.
3.1 텍스트 가져오기 문제
예상은 했지만 fs 모듈은 node 서버에서만 작동됩니다. 클라이언트 사이드에서 호출은 할 수 없었습니다. txt 파일을 가져오기 위해선 raw loader가 필요합니다.
로더를 설치해준 후 nuxt.config.js에 코드를 추가해줍니다.
이렇게 하면 txt 파일을 import ~ from 형태로 가져올 수 있습니다.
3.2 "~와", "~과" 문제
뭔가 이상하다 "~의"는 앞의 단어가 어떻던 간 문제가 없었지만, "~와"는 어울리지 않는 경우가 있었습니다. 찾아보니 "~와"는 앞의 형태소가 받침(종성)이 존재해야 한다고 하는군요. 그럼 받침 체크 메서드를 만들어봅시다.
종성 분류에 대한 좋은 글을 구글링 해보니 바로 찾을 수 있네요. 해당 글을 참고해서 JS용 코드를 작성해봅시다.
위의 링크를 참고해서 코드를 작성해보니 한글의 유니코드 변환 시 문제가 발생하네요. JAVA에서의 한글 변환과 Javascript의 변환 방식이 다른 것 같습니다. Javascript용 변환 코드를 검색해봅시다.
너무 좋은 코드가 있네요. JAVA 링크와 거의 같은 형태지만 Javascript에 맞게 값이 변환되어있어서 문제없이 작동됩니다.
4. 일단 완성
한 1시간 반 정도 글을 쓰면서 작업하니 완성했네요. 이제 배포해봅니다. 배포는 언제나처럼 netlify를 통해서 간단하게 배포할 수 있습니다. github 레포지토리를 업데이트한 후 netlify에서 정적 페이지를 가져옵니다.
배포는 원하는 레포지토리를 선택하고, Build command, Publish directory만 설정해주면 됩니다. 빌드만 기다려주면 이제 완성입니다.
제 첫 번째 공식 닉네임은 "혁명정부의 예금주"입니다. 마음에 드는 사이트가 완성됐네요. 귀여운 햇빛 버튼을 누르면 닉네임은 계속 바뀌게 됩니다. 코드는 아래의 레포지토리를 참조해주세요. 감사합니다.
어제 생활코딩에 이 글을 공유하고나서 몇가지 피드백을 받아서 적용해봤습니다.
1. 두 글자 닉네임
두 글자 닉네임을 만들고 싶으시다는 의견과 히스토리를 보고 싶다는 의견이 있으셔서 추가해봤습니다.
lodash랑 fs 모듈을 사용해서 두 글자 단어만 필터링 했습니다. 결과를 살펴봅시다.
뭔가 무섭다.. 두 글자 단어는 총 10,558개로 준비된 단어 뭉치 중 40%나 차지했습니다. 어감이 이상한 단어도 많았지만 신기한 단어도 많이 보이는걸로 보아 잘만 돌리면 괜찮아 보입니다.
2. 기존 단어 리스트와 분리하기
전 "~의", "~와" 형태로 닉네임을 만드는 비율은 40%, 두 글자 닉네임은 30%, 세글자 닉네임은 20%로 나왔으면 좋겠습니다. 이를 위해 각각의 단어 뭉치(두 글자, 세 글자, 그 외)를 필터를 통해 분리해서 만들어주었습니다.
3. 히스토리 보여주기
여러번 누르다보면 순식간에 지나가서 히스토리를 보여달라는 요청이 있었습니다.
정말 밑도 끝도 없는 닉네임이 생성됩니다.. 히스토리를 보여주는건 크게 어렵지 않았지만, 문제는 인터렉션이었습니다. 텍스트를 단순히 넣고, 빼면 딱딱 끊겨서 맛이 안납니다. 그래서 트렌젝션 효과를 넣어봤습니다.
4. 얼마나 사용하는걸까
마지막으로 사용하시는 분들이 얼마나 사용하시는지 궁금했습니다. 구글 애널리틱스 연결을 해서 트래킹을 하고 있는데, 따로 이벤트 설정은 안했습니다.
그 밖에 추가하고 싶은 아이디어가 있으시면 댓글로 알려주세요. 감사합니다.