얼마나 빨리 똑같이 만들수 있었을까
카페에서 글을 읽고 있었습니다. 네이버 D2에 올라온 javascript 동향이라는 글입니다. 이 글을 이미 여러번 읽었고, 리마인드겸 읽어보는거라 무척이나 딴생각이 많이 났습니다.(대충 읽었다는 뜻입니다) 딴생각은 꼬리의 꼬리를 물어 이 사이트를 통째로 하루만에 카피하면 어떨까에 이르렀고, 클론 코딩을 시작해보게 됐습니다.
* 모든 코드는 깃허브 링크에 공개되어있습니다. 혹시라도 이미지가 안 보인다면 네이버측에서 막아둔 것이니 이미지가 안나온다면 CORS 에러가 나타났는지 확인해주세요. 재미로 만들어본 것이지만 혹시라도 안의 컨텐츠의 저작권이 문제라면 모든 이미지 및 텍스트를 수정하도록 하겠습니다. *
페이지 구성을 살펴봤습니다. 전반적인 구성은 홈, 개별 게시글, 검색 결과, 태그 검색 결과, 카테고리별 게시판 등이었습니다. 검색 결과와 태그 검색 결과는 모양만 다를 뿐 구성은 동일하니 4개의 뷰 레이아웃만 있으면 된다는 사실을 알 수 있었습니다.
눈에 띄는 것을 몇 개 추려봤습니다. 게시글(카드), 페이지네이션, 네비게이션 바, 푸터, 히어로 배너, 랭킹 박스, 키워드 박스 등이었습니다. 각 게시글 안을 살펴보면 제목, 메타 데이터, 글쓴이, 댓글 박스 등 몇 가지 구성요소가 보였습니다.
최근에 저는 create-nuxt-app을 활용해서 서비스를 만드는걸 좋아합니다. 그래서 vue, nuxt를 사용했고, 서버는 따로 만들지 않았습니다.(어차피 서비스할 것도 아니니까) 라이브러리는 bulma의 vue 컴포넌트 라이브러리인 buefy를 사용했습니다.
히어로 배너는 직접 만들기 귀찮으니 모듈을 사용해서 빠르게 처리했습니다. vue-flickity는 최근에 자주 사용하는 히어로 배너인데 적당히 스타일만 수정하면 드래그 효과 등을 구현할 수 있어서 편리합니다.
기본적인 코드는 위의 절차를 통해서 만들고, 뷰 레이아웃 파일을 만들었습니다. 이후 가장 큰 컴포넌트 순서로 하나하나 만들어봤습니다.
처음 마주한 문제는 제가 자주 사용하던 vue-flickity는 nuxt 위에서 제대로 작동되지 않았습니다. nuxt에서 가져올 때 모듈을 인식하지 못하는 경우가 있는데 ssr 설정을 따로 해주고 플러그인 파일을 따로 만들어야만 작동이 이뤄졌습니다. vue-flickity의 문제는 이뿐만이 아니었는데 이미지를 제대로 출력하지 못했습니다. 첫번째 이미지만 출력된 후 2번부터 이미지를 제대로 출력하지 못했습니다. 결국 다른 carousel을 사용해봤습니다.
다른 carousel 모듈은 hooper였습니다. 이 모듈은 간단하지만 이상하게 모든 슬라이드가 1번에 몰려나오는 문제가 나타났습니다. 두 개의 모듈이 문제가 발생했고, 해결책이 딱히 보이지 않아서 vue-carousel을 사용해봤습니다. 가장 많이 쓰이는 모듈이지만 이것 역시 제대로 작동하지 않았습니다. 대부분 nuxt의 ssr과 관련된 문제로 보였는데 ssr를 꺼주고, 모듈을 개별로 호출하거나, 모듈을 등록해서 쓰는 방법 등이 적용됐지만 대부분 제대로 작동하지 않았습니다.
한시간 정도 삽질을 한 후 vue-owl-carousel 모듈만큼은 제대로 작동되는걸 확인했습니다.
히어로 배너로 인한 1시간이 공중분해됐지만 1시간 20분 정도 진행됐을 때의 모습입니다.
새로운 문제가 나타났습니다. buefy의 기본 스타일링 중 수정해야할 것을 전역 css로 설정해야 했습니다. 하지만 nuxt.config.js에 module.css에 파일을 넣었을 때 제대로 작동하지 않았습니다. 해당 문제는 검색해보니 sass-resources-loader 를 설치해주는 방법으로 해결할 수 있었습니다.
이후 컬러가 들어간 상단 배너를 만들고, 나눔 바른 고딕을 입혀줬습니다.
이제 각각의 게시글을 만들어봅시다. 데이터베이스를 따로 연결하지 않을 것이므로 json 파일을 통해 데이터를 다루겠습니다.
sample 디렉토리에 article.json 파일은 메인 화면과 Hello world 카테고리에 들어갔을 때 나타날 데이터입니다. news.json 데이터는 뉴스, 마지막으로 program.json은 프로그램을 의미합니다.
작업 2시간째 모습입니다. 폰트 스타일이나 크기 등의 CSS는 복사로 가져오고, 아이콘이나 레이아웃은 제가 직접 스타일은 비슷하게 맞춰봤습니다.
여기서 새로운 문제가 발생했습니다. 본문 안에는 링크, 이미지 등을 포함한 데이터를 넣어주고, 각각의 구성요소마다 스타일이 적용되어야 하는데 모든 게시글의 데이터를 따오는 건 하고 싶지 않았습니다. 그래서 혹시라도 크롤링으로 가져올 수 있을지 체크해봤습니다.
개발자 도구 네트워크 탭에서 데이터 가져오는 부분을 찾아봅니다. GET 요청이 보이네요. 제가 원하는 요청입니다. 이걸 저도 요청할 수 있을지 Postman을 이용해 확인해봅니다.
CORS를 딱히 막아두지 않아주신 덕분에 GET요청으로 얻어올 수 있었습니다.
이제 제가 할 수 있는 방법은 2가지입니다.
첫번째 방법은 게시글 ID값만 따서 axios를 활용해 GET요청 루프를 돌리면 D2의 모든 게시글을 가져올 수 있습니다. 무척 간단하지만 왠지 크롤링을 하면 안될것 같은 두려움이 생깁니다. 크롤링을 허용하는지 안 허용하는지는 robot.txt를 통해서 확인해볼 수 있지만 D2 관리하시는 개발자 분들에게 혹시라도 불편함을 드리지 않고 싶습니다.
두번째 방법인 GET 요청을 하지 않고 몇 개만 제가 긁어서 샘플로 만들어 게시판을 구현해보겠습니다. 귀찮으니 각 게시판별로 2개의 글의 서두 및 제목, 메타데이터, 썸네일 정도만 가져왔습니다. 그리고 2개의 글을 위해 페이지네이션을 구현했습니다. 각 페이지에는 1개의 게시글이 나타납니다.
페이지네이션의 스타일은 buefy의 기본 모양이 나쁘지 않아 그냥 내버려뒀습니다.(D2랑 똑같이 스타일하는게 번거롭기도 하구요;;)
이제 푸터를 만들어보겠습니다.
위의 이미지는 실제 푸터의 이미지입니다. 보시면 좌측 상단엔 이미지로 된 링크가 보이고, 우측엔 소셜 링크, 하단엔 카피라이트 문구가 보입니다.
푸터 이미지를 다운받아보려고 하니 이미지 스프라이트를 활용하는걸 볼 수 있었습니다.
이미지 스프라이트는 위와 같이 이미지 여러개를 묶어서 한장을 만들어 놓고, 이미지를 가져올 때는 CSS의 background-position 값을 조정해 가져옵니다. 이렇게 하나로 묶게되면 하나의 압축된 파일만 가져오다보니 용량과 속도, 요청 횟수 면에서 도움이 됩니다. 저는 이미지 스프라이트를 처음 본게 인스타그램을 뜯어보다가 발견했는데, 오늘 인스타그램에 다시 들어가보니 스프라이트도 아깝다는건지 svg와 path를 활용하고 있네요.
어쨌던 스프라이트를 사용하려면 각각의 포지션값이 필요하므로 이 경우엔 어쩔수 없이 포지션값을 복사했습니다.
푸터를 완성하면 이런 느낌이 됩니다.
이제 뉴스, 프로그램에 각각 2개의 포스트를 올려보겠습니다.
프로그램을 넣어보니 문제가 발생했습니다. 제목 부분이 첫번째꺼는 일반 이미지인데 두번째꺼부턴 스프라이트 이미지를 사용중이네요. 과감하게 1개만 넣고 넘어갔습니다.
이제 남은건 어바웃 페이지, 메인 페이지의 내부 화면, 그리고 검색 결과 화면입니다. 현재까지 약 4시간 정도 소요됐습니다. 오늘은 벌써 밤이 됐네요. 남은 작업은 내일 하면서 작성하도록 하겠습니다.
오늘은 메인 페이지 부터 작업을 시작해보겠습니다. 메인페이지는 모든 컨텐츠를 모아서 보여줄 뿐 아니라 랭킹, 태그 박스 등이 있습니다.
제가 언듯보니 랭킹은 조회수를 기반으로 나타나는 것으로 보입니다. 모든 컨텐츠 데이터를 하나로 묶은 다음 조회수로 정렬하고 5개만 남기면 TOP 5 기능을 만들 수 있습니다. 빠르게 작업하기 위해 배열을 여러개를 합치고 정렬하는데 좋은 라이브러리인 lodash를 사용했습니다.
코드상으론 5개만 필터링 하지만 모든 컨텐츠가 4개 밖에 없어서 4개만 출력됩니다. 태그 박스를 만들어보겠습니다. 원칙적으론 각각의 게시글에 태그 데이터가 존재하고, 그 태그를 바탕으로 태그 박스에 나타나는게 맞지만 저는 게시글 전체 데이터를 가져오지 않을 것이므로 디자인만 구현하도록 하겠습니다.
이 따로 제가 손볼 것도 없기도해서 CSS를 가져왔습니다.
홈의 마지막 구성요소로 키워드 태그 아래에 보이는 이메일 박스를 만들어봅시다.
이메일 박스는 생각보다 오래걸렸습니다. input의 포커스 상태시 outline 하이라이팅 되는걸 맞춰주려고 buefy의 기본 input을 사용하지 않았습니다. 이메일 아이콘도 살짝 다른데 네이버에서 사용하는 아이콘 세트가 아닌 구글 매터리얼 디자인에 사용되는 이메일 아이콘입니다.
이제 거의 마지막을 향해서 가고 있습니다. 검색 결과와 게시글의 내용은 따로 만들지 않을 것이므로, 어바웃 페이지만 마무리하면 끝입니다.
어바웃 페이지의 이벤트 목록이 보이는데 데이터 세트가 독특해보입니다. 어떤건 날짜가 두 줄이기도 하고, 시간이 지났다고 해서 종료됨 표시가 붙은게 아닌 것도 있습니다. 결국 종료됨은 관리자가 직접 설정해주는 것으로 보이니 하나의 파라메터로 지정해야합니다.
schedule.json 파일을 만들어서 데이터를 만들어봤습니다. 날짜부분은 두 줄을 표시하는게 귀찮으므로 v-html을 통해서 가져오도록 합니다.(다들 아시겠지만 v-html은 XSS 공격 위험 때문에 사용을 자제하거나 sanitize해서 사용해야 합니다.)
작업 1시간 째입니다. 만들고 나니 실제 사이트에선 바로가기, 사이트 바로가기로 2가지가 있네요;; 왜 굳이 두가지로 나누신건진 의문이지만 저는 바로가기로 통일했습니다.
이제 마지막 컴포넌트네요. 타임라인입니다.
살펴보니 각각의 히스토리 데이터는 링크, 날짜, 설명으로 구성되어있는걸 알 수 있습니다. 보통의 경우 타임라인도 라이브러리를 통해 빠르게 구현할 수 있지만 오늘은 그냥 만들어봅시다. 타임라인에는 나눔스퀘어 폰트가 사용되네요. 가져옵니다.
데이터 세트를 적당히 만들어서 작업을 해봅니다.
완벽하진 않지만 거의 비슷하게 만든것 같습니다.
이제 스크롤 이동만 구현해줍시다. 스크롤 이동은 직접 구현하는 것보단 모듈이 빠릅니다. vue-scrollto를 사용해줍니다. 그럼 D2 히스토리 바로가기 버튼을 눌렀을 때 히스토리로 바로 이동합니다.
마지막으로 배포해봅시다. netlify를 통해서 배포하기 위해서 nuxt.config.js의 generate을 추가해줍니다.
최종 완성본은 링크에서 확인해주세요.
총 5~6시간 정도 걸려서 네이버 D2를 만들어봤습니다. 빠진 부분도 많이 있는데 모바일 최적화나 검색 결과창 등은 나중에 생각나면 해보겠습니다.