Next.js로 블로그를 만들어보고 싶어서 개발을 시작했다. 인프라 구성을 먼저 하고(1편에서 이어서), 인프라 위에 올라갈 블로그 어플리케이션을 개발하기 시작했다.
Next.js 정적 사이트를 만들자
CLI를 통해 next export를 하게 되면, Next.js를 통해 정적사이트가 출력(export)된다. package.json에 next build와 next export를 추가해서 정적 파일이 생성되도록 했다.
{
"name": "blog-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build && next export"
}
}
Gitlab CI/CD에서 빌드하고 배포하기
Next.js를 통해 정적사이트를 빌드하고 출력하는 방법을 알았으니, Gitlab CI/CD를 통해 빌드하고 배포하는 스크립트를 작성하기 시작했다. Gitlab CI/CD는 그룹별로 월 400분의 무료 CI/CD 시간을 부여하기 때문에, 블로그 정도의 CI/CD는 무료로 사용할 수 있다. 깃랩에서 단계(Stage)를 빌드(build)와 배포(deploy)로 나눴고, 빌드하고 나서 artifact에 있는 정적 파일들을 Amazon S3에 배포하도록 간단하게 구성했다.
# /.gitlab-ci.yml
stages:
- build
- deploy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .next/cache/
build:
stage: build
only:
- main
image: node:16-alpine
script:
- yarn
- yarn build
artifacts:
expire_in: 6 hrs
paths:
- ./out
deploy:
stage: deploy
only:
- main
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
dependencies:
- build
script:
- aws s3 rm s3://S3주소/ --recursive
- aws s3 cp ./out s3://S3주소/ --recursive
- aws cloudfront create-invalidation --distribution-id CloudFront배포아이디 --paths "/*"
빌드할때는 가벼운 alpine 리눅스가 포함 된 Node.js 이미지만 있으면 되고 배포할 때는 AWS CLI가 포함 된 이미지가 필요해서 단계별로 도커 이미지를 구분했다. 처음에는 AWS CLI가 설치되어있는 이미지에 Node.js를 설치하다보니 빌드/배포가 3분 조금 넘게 걸렸는데, 아래와 같이 최적화 해서 빌드 및 배포에 약 1분 정도의 시간이 줄게 되었다.
개인 블로그에서 포스팅하는 빌드시간으로 과금이 될 일은 없지만(그룹별로 매월 CI/CD 400분), 깃랩같은 SaaS 서비스를 사용하면 빌드시간을 포함해서 모든 것이 비용이다보니 최적화 하는 습관을 항상 들여야 한다고 생각된다.😂
Next.js에서 마크다운 파일을 어떻게 읽을까?
이전에 구성했던 인프라와 Gitlab CI를 통해 소스를 커밋하고 푸시하면 배포되는 CI/CD 까지는 구성이 됐다. 이제 마크다운 파일 기반의 블로그를 만들어야 하는데, Next.js에서 만들어 본적은 없고, 어떻게 만드는지 찾아야 했다.
블로그를 어떻게 만드는지 궁금해서 nextjs markdown blog 라고 구글에 검색했고, 다음과 같은 next.js 공식 블로그의 포스팅을 찾게 되었다. 글을 쭉 읽어보니, remark-html을 통해서 마크다운을 HTML로 변환하고, Tailwind Typography를 통해서 HTML을 스타일링 하는 내용의 포스팅이었다. 해당 포스트와 포스팅에서 예시로 든 blog-starter을 참고로 블로그에서 마크다운 파일을 읽어가기 시작했다.
remark-html
remark-html은 마크다운으로 된 문서를 HTML로 변환해준다. remark의 플러그인으로, remark-html은 remark와 같이 설치해주면 된다.
블로그 포스팅을 위해 Remark를 다음과 같이 사용할 수 있다. (Markdown to HTML)
const result = await remark()
.use(html, { sanitize: false }) // html : remark-html
.process(post.content); // content : 블로그 Markdown 문서
const content = result.toString();
@tailwind/typography
tailwindcss-typography는 Tailwind CSS를 사용하고 있을 때, prose CSS 클래스 하나만으로 HTML 태그들을 그럴싸한 문서로 보이게 해주는 스타일링 플러그인이다.
마크다운을 HTML로 변환했다고 해서, 스타일링까지 내가 원하는 모습으로 적용되진 않는다. 지금 보이는 블로그처럼, 마크다운에 맞는 스타일링으로 보이려면 블로그에 어떠한 스타일링이라도 적용해야 한다.
아래와 같이 className에 prose와 다크모드에서의 prose-invert 두 개만 추가해주면 tailwind에서 정의한 스타일링이 적용된다. 물론 커스텀도 된다.
<article className="prose dark:prose-invert">{content}</article>
highlight.js
highlight.js는 코드구문을 에디터처럼 구문강조 해주는 툴이다.
마크다운 문서를 HTML로 변환하고 스타일링까지 했는데, 개발자 블로그를 위해서는 코드 구문의 강조가 필요했다. 코드구문 하이라이트를 위한 도구로는 highlight.js와 prismJS가 많이 보였다. 아무 이유 없이, highlight.js로 내가 원하는 스타일링이 되고 많이 쓰고 유명해서 선택했다.
적용할 때는, React의 useEffect를 통해 DOM을 조작해서 포스팅 페이지 중에 pre와 code 태그가 있는 모든 구문에 먹히도록 했다.
useEffect(() => {
document.querySelectorAll("pre code").forEach((element: any) => {
hljs.highlightElement(element);
});
}, []);
gray-matter
gray-matter은 마크다운 문서에서 일정한 포멧팅으로 제목과 요약, 날짜 등을 구문분석 할 때 도움이 되는 툴이다.
---
title: "Amazon API Gateway 프라이빗 통합(Amazon VPC 리소스 연결)"
excerpt: "VPC에서만 API Gateway를 통신하고, API Gateway에서 VPC를 통신하는 방법."
coverImage: "/assets/blog/api-gateway-private-integration/architecture.webp"
date: "2022-08-08T21:40:00+09:00"
ogImage:
url: "/assets/blog/api-gateway-private-integration/architecture.webp"
---
어떠한 블로그 예제든 마크다운을 쓰고 있다면 gray-matter을 통해서 파싱을 하고 있었고, 그만큼 마크다운 기반의 사이트를 운영 할 때 편하다보니 다들 쓰고 있었다. 나 또한 일정한 형식의 마크다운 양식이 필요했고 사용하게 되었다. 원하는 포멧이 있다면 가감해서 사용하면 된다.
next-themes
next-themes를 사용하면 Next.js 사이트의 다크모드를 useTheme라는 React Hook으로 손쉽게 구현할 수 있다. Tailwind CSS를 사용하더라도, 함께 적용하는 방법을 친절하게 알려준다. useTheme를 통해서 set함수를 이용하니 버튼을 통한 다크모드를 너무 손쉽게 구현했다.
import { FC, useEffect, useState } from "react";
import { useTheme } from "next-themes";
import { Moon, Sun } from "react-feather";
const ThemeButton: FC = () => {
const { theme, systemTheme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
const currentTheme = theme === "system" ? systemTheme : theme;
useEffect(() => {
setMounted(true);
}, []);
if (!mounted)
return <div className="ml-1 w-6 h-6 mr-1 sm:ml-4 cursor-pointer" />;
return (
<div
className="ml-1 mr-1 sm:ml-4 cursor-pointer"
onClick={(e) => {
setTheme(currentTheme === "dark" ? "light" : "dark");
e.preventDefault();
}}
>
{currentTheme === "dark" ? <Moon /> : <Sun />}
</div>
);
};
이슈가 있었는데, 카카오톡에 링크를 올렸을 때 카카오톡 전용 브라우저에서 다크모드가 적용되지 않는 버그가 있었다. 카카오톡 브라우저를 제외한 어디에서도 발생하지 않아서 고칠까 하다가, globals.css에서 body에 bg-black을 먹여버리는 것으로 해결하고 치웠다.
body {
@apply dark:bg-black;
}
SEO (Meta Tag)
검색엔진에서 블로그가 잘 노출되기 위해서 메타태그를 적용하기 시작했다. 또한 페이스북이나 트위터같은 SNS를 통해 블로그 링크를 보냈을 때, Open Graph를 통해서 웹페이지의 미리보기가 될 수 있도록 구성했다.
SEO같은 경우, Next.js 블로그 예제들을 참고해서 SEO 컴포넌트를 따로 만들었다. 페이지에서 적용 될 SEO와 포스팅에서 적용될 SEO 컴포넌트를 분리해서, 각각 맞춤으로 적용되도록 했다.
export const BlogSEO: FC<BlogSEOProps> = ({
title,
description,
ogImage,
publishedAt,
modifiedAt,
}) => {
return (
<SEO
title={title}
description={description}
ogType="article"
ogImage={ogImage}
twImage={ogImage}
publishedAt={publishedAt}
modifiedAt={modifiedAt}
/>
);
};
블로그 포스팅은 이미 구현된 Dynamic Routing 페이지에서 MD파일만 읽으니 더 이상 추가할 필요가 없고, 만약 새로운 라우팅이 추가되면, 추가되는 페이지마다 필요한 SEO를 Head 태그 안에 추가하면 된다.
next-sitemap
누가 언제 무슨 키워드로 블로그에 들어왔는지 확인하기 위해 Google Search Console을 등록했다.
사이트맵을 등록해야 구글이 열심히 읽어갈텐데, 블로그 사이트에는 아직 사이트맵이 없었고, Next.js를 위한 사이트맵 생성기가 있나 봤더니 next-sitemap이라는게 바로 검색됐다. 블로그를 만들면서 놀랐던건, 세상 많은 개발자들이 블로그를 포함한 마크다운 사이트를 만들고자 노렸했고, 이러한 블로그 사이트 하나 만들기에는 관련된 기술이 없는게 없었다(그만큼 넘쳐났다). 사이트맵과 로봇 또한 만들기 쉬워서, package.json에 postbuild 하나만 추가하면 바로 사이트맵과 robots.txt 파일이 출력되었다. 깃랩에서는 더 설정할 것도 없이 바로 적용되었고, 이렇게 블로그 어플리케이션이 완성되었다.
정리
블로그를 만들면서 마크다운 기반의 Next.js 개발에 대한 경험치가 생겼다. 또한 즐겨쓰던 CDK에서 Static Website 인프라 개발도 경험하게 되었다. 앞으로 어떤 기능이 필요할진 모르지만, 더 개선해 나가야겠고 개선점을 작성해보면서 기를 모아야겠다. (유튜브 영상도 찍어서 올리고 싶은게 많은데..😭)
블로그 개선 사항
- 내 블로그의 검색엔진 최적화는 아직 부족하다. 구글 검색했을 때, 프리티(연관 글, 글 요약/미리보기 등)하게 나오지 않는 문제가 있는 것 같다.
- SNS에 퍼갈 수 있는 링크가 필요하다. 페이스북, 트위터 등. 언젠가 나의 개발을 좋아해주고, 글을 잘 쓰게 된다면 내 글을 좋아해주고 퍼갈 수도 있지 않을까?
- RSS를 아직도 쓰나? RSS를 많이 사용해서 RSS를 통해서 글을 구독하나? 구현을 해야하는건가? 잘 모르겠다. 이에 대한 통계를 검색해봐야겠다.
- 글에 대한 커뮤니케이션(댓글 등) 기능이 필요하다. 방명록이라도. 누군가는 블로그에 와서 욕 또는 광고를 하고 싶어할 수도 있고. 소통할 길이 없어 답답할 수도 있으니까.