댓글 기능 추가 (1)
Comment_providers
Jekyll은 정적 사이트를 만드는 도구다. 반면 댓글 기능은 동적인 기능이니, Jekyll에서 구현하는 것은 원칙적으로 힘든 일이다.

이를 해결하기 위해서는 크게 두 가지 방법이 있는데, 하나는 외부 사이트를 이용해서, 댓글은 외부 사이트에서 전적으로 관리하고 이를 블로그에 미러링하는 방식이고 다른 하나는 댓글이 달리면 해당 댓글을 내 사이트 소스에 추가해서 보여주는 것이다.
_config.yml 파일을 열어보면, comments 항목의 provider에 다양한 댓글 제공 방식이 있는데, 이 중 disqus, facebook 등이 전자의 방식이고, staticman, utterances 등이 후자의 방식이다.
나는 둘째 방식이 여러가지 측면에서 합리적이라 생각하여 두 번째 방법을 사용했다. 내 블로그가 개발자 블로그였다면 망설이지 않고 utterance를 택했을 것이나, 댓글을 달기 위해 github 계정이 꼭 필요하다는 점, 그리고 내 블로그에 들르는 분들 중 많은 분들이 개발자가 아닐 것이라는 점(따라서 github 계정이 없을 것이라는 점)에서 나는 staticman을 이용하기로 했다.
2022년 11월부로 Heroku에서 제공하던 무료계정이 없어졌다. 변경 후엔 한 달에 5달러 정도에 가장 저렴한 플랜을 이용할 수 있는데, disqus의 plus는 한 달에 11달러라 그냥 disqus로 갈아타기로 했다.
Staticman의 작동원리
우선 staticman을 이용하여 댓글 기능을 추가하려면, 이 기능이 어떻게 돌아가는지를 아는 것이 도움이 된다.
- 방문자가 페이지 하단의 댓글란을 입력한 후 전송버튼을 누르면 staticman에 댓글 내용이 전송된다.
- Staticman이 이 댓글의 내용을
.yml형식으로 저장하여, 사이트 저장소에 pull request를 보낸다. - PR을 받으면
.yml파일이 내 사이트 저장소에 속하는 파일 중 하나가 된다. - 이렇게 모여있는
.yml파일들 가운데에서, 지금 읽고 있는 포스트에 대한 댓글만 골라내어 페이지 하단에서 보여주면 된다.
나는 댓글에 답글을 달 수 있도록 하고 싶어서 이 또한 해결해야 했다. 또, 댓글 기능은 로컬에서는 보이지 않기 때문에 커밋을 하지 않고는 잘 작동하는지 그렇지 아닌지를 판단하기가 힘들다. 그래서 이 부분이 커밋 히스토리를 가장 난잡하게 만든 주범이었다.
봇 계정 만들기
우선 위의 작동원리에서 2번 과정을 보자. 내 블로그 저장소에 pull request를 보낼 수 있는 사람은 나 뿐이므로, 거꾸로 말하면 이를 위해서는 staticman에 내 github 계정 정보를 넘겨주어야 한다는 말과 같다.
이는 권장할만한 일이 아니므로 우리는 github에 봇 계정을 만든다. 구글 계정을 만들 때는 본인인증이 필요하지 않으므로, 이를 통해 새로 github에 가입한다. 나는 이 계정을 math-jh-bot으로 이름붙였다.
이제 봇 계정이 사이트 저장소에 pull request를 보낼 수 있도록 이를 collaborator에 추가해준다.
각종 암호키 설정
Staticman을 굴리기 위해서는 두 가지의 키, personal access token과 RSA 암호키가 각각 필요하다.
- 아무리 봇 계정이라 하더라도 staticman에 이 계정의 접속 정보를 알려줄 수는 없다. 이럴 때를 위한 기능이 Github의 personal access token이다. Personal access token은 계정의 비밀번호와 거의 비슷하지만, 처음 토큰이 생성될 때 사용을 허가한 기능만 사용할 수 있다.
- Staticman을 통해 주고받는 정보에는 민감한 정보가 포함될 수 있다. 이를 암호화하기 위해 RSA 암호키가 필요하다.
이제 이 두 키를 각각 만들자. 우선 봇 계정으로 접속하여, Settings / Developer settings / Personal access tokens으로 접속한다.

이후 오른쪽 위에 보이는 Generate new token 버튼을 눌러 새로운 토큰을 생성한다. 그럼 다음과 같은 창이 나온다.

Note항목은 아무렇게나 이름을 적어두면 된다.Expiration의 경우, 해당 기간이 지나면 token이 만료되어 재발급을 해 줘야 한다. 나는 귀찮음을 피하기 위해 No expiration을 선택하였다.Scope의 경우,repo이하의 모든 항목,user이하의 모든 항목을 체크해주면 충분하다.
이후 토큰을 생성하면 다음과 같은 화면이 나오는데, 초록색 박스 안에 있는 키를 잘 복사해서 어디에 넣어두자. 위의 파란 경고문에서 알려주듯, 이 화면을 나가면 이 키를 확인할 방법이 없으므로 잃어버린다면 토큰을 새로 만들어야 한다.

math-jh-staticman의 키는 보이지 않는다. 나는 이를 내 사이트의 로컬 저장소에 Token이라는 이름으로 저장해두고, .gitignore에 Token을 추가하여 원격 저장소로 업로드되지는 않도록 해 두었다.
RSA 암호키의 경우, 터미널에서
ssh-keygen -m PEM -t rsa -b 4096 -C "staticman key" -f ~/.ssh/staticman_key
을 입력하면 암호키 staticman_key와 이에 해당하는 공개키가 사용자 폴더의 숨은 폴더 .ssh에 생성된다.
Heroku 가입 및 staticman 설치
댓글 기능이 정상적으로 작동하기 위해서는 staticman이 항상 돌아가고 있는 상태여야 한다. 집에 가정용 서버가 있거나 하지 않는 한은 이것이 불가능한 일이므로, 우리는 Heroku를 이용한다. 언제나처럼 회원가입을 마친 후 Heroku에서 staticman을 검색하여 들어간다. 혹은 링크를 누르면 Heroku 상의 staticman 페이지로 이동한다.

우측 상단에 Deploy to Heroku 버튼을 누르면 이 앱을 내 Heroku 계정에 설치할 준비가 완료된다. 적당히 앱 이름을 지어준 후, deploy app을 누르고 잠시 기다리면 Heroku 상에서 어플리케이션이 설치된다.
이제 앞서 만들었던 두 개의 키 (Github 토큰과 RSA키)를 Heroku에 설치된 staticman에 입력해주어야 한다. 여기에는 두 가지 방법이 있는데, 나는 둘째 방법으로 하다가 실수를 했는지 실패했고, 첫 번째 CLI를 이용하는 방법으로 성공했다. 이 방법은 실수할 가능성이 거의 없으므로 이 방법부터 소개한다.
Heroku CLI를 이용하는 방법
다음 커맨드를 터미널에 입력해서 Heroku CLI를 설치하자.
brew tap heroku/brew && brew install heroku
설치가 완료된 후, 다음 커맨드
heroku login
을 입력해서 로그인을 진행한다. 이제
- 앞서 지정한 Heroku 상에서의 staticman의 이름을
[[Heroku 앱 이름]] - 봇 계정의 Github personal access 토큰을
[[Github 토큰]]
라고 하자. 다음 명령어
heroku config:add --app [[Heroku app 이름]] "RSA_PRIVATE_KEY=$(cat ~/.ssh/staticman_key | tr -d '\n')"
heroku config:add --app [[Heroku app 이름]] "GITHUB_TOKEN=[[Github 토큰]]"
를 한 줄씩 입력하면 Heroku에 설치된 staticman에 RSA_PRIVATE_KEY와 GITHUB_TOKEN이 입력된다.
Heroku 홈페이지에서 입력하는 방법
혹은 Heroku에 설치된 어플리케이션의 settings에 들어가면 둘째 항목 Config Vars에 앞서 말한 두 개의 키를 직접 입력할 수 있다. Reveal Config Vars를 눌러보면 아직 어떠한 config variable도 없으므로 비어있다.
파인더에서 ⌘+⇧+G를 눌러 ~/.ssh로 이동하자. 그럼 아까 생성한 staticman_key가 보인다. 이를 열어보면
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
와 같은 형식의 키가 나온다. 이제 모든 줄 사이의 줄바꿈을 없애서 이를 한 줄로 만든다. 따라서 대략 키는
---BEGIN RSA PRIVATE KEY-----niwqafno3nqdaws ... -----END RSA PRIVATE KEY-----
처럼 생겨야 한다. 이제 이를 복사하여
KEY:
RSA_PRIVATE_KEY, VALUE:붙여넣기
이렇게 첫 번째 키를 입력한다. 둘째 키는
KEY:
GITHUB_TOKEN, VALUE:앞서 얻은 Github personal access token
을 입력하면 된다.
둘 중 어떠한 방법을 선택하든, 이제 https://[[Heroku 앱 이름]].herokuapp.com에 접속하면
Hello from Staticman version 3.0.0!
한 줄로 이루어진 웹페이지가 나와야 정상적으로 세팅이 완료된 것이다.
reCaptcha 가입
Staticman은 utterances 등과는 다르게 어떠한 계정도 없이 댓글을 등록할 수 있으므로, 상대적으로 스팸에 취약하다. 때문에 reCaptcha 등으로 보호하는 것이 필요하다.
https://www.google.com/recaptcha/admin/create으로 이동하여 구글 계정으로 로그인하면 reCaptcha에 가입할 수 있다. reCaptcha 유형의 경우, 나는 reCaptcha v2, “로봇이 아닙니다.” 체크박스를 이용하고 있다. 도메인은 블로그의 주소를 입력하고 reCaptcha를 생성하면 사이트 키와 비밀 키가 나온다. 이 두 키도 헷갈리지 않게 잘 적어두자.
저장소에서 staticman 사용 설정하기
이제 저장소 바깥에서 해야 할 준비는 모두 끝났으므로, 저장소의 파일들을 수정해주기만 하면 된다.
_config.yml 설정
우선 _config.yml에서 comments: 아래의 provider를 "staticman_v2"로 바꾸어 staticman_v2를 댓글 작성에 사용할 것임을 선언한다. 또 staticman: 아래 항목을
staticman:
branch : "main" 혹은 "master"
endpoint : "https://[[Heroku 앱 이름]].herokuapp.com/v2/entry/"
로 입력해준다. 이 때 branch 태그는 본인 사이트 저장소의 메인 브랜치가 main이면 main으로, master면 master로 입력하면 된다.
바로 밑의 reCaptha의 siteKey는 reCaptcha에서 나온 그대로 입력하면 된다. 다만 공개된 파일인 _config.yml에 비밀키를 그대로 입력할 경우 github 저장소를 통해 모든 사람이 비밀키를 알 수 있게 된다. 따라서 아까 만든 RSA 키를 가지고 비밀키를 암호화한다. 다음 url
https://[[Heroku 앱 이름]].herokuapp.com/v2/encrypt/[[reCaptcha 비밀키]]
으로 이동하면 RSA 키를 이용해 암호화된 reCaptcha 비밀키가 얻어진다. 이를 reCaptcha 밑의 secret에 붙여넣는다. 예컨대 나는
reCaptcha:
siteKey : "6LeiUpkgAAAAAN3VR6VD-g9b10-yf2r8DjRmfiVZ"
secret : "AE8k5EQKTRnYyp/vpHoMUhAH/YVkzv36tfI+ZZZ2N5c/pFI7Afio0TQfD/FJdDUOpJ8UH+n5K1x7Yeqc5tbcfG20rpKpWCXTiehLJSqiMixuj12oPGzg7iD4Qecehc9o02vSk6pRS/lCAoxuO2GU9zTs8EyKYSqgCkLIKEzoZDnnVJKvpWoPhhpqmaogaYG0AvuLyoKwDoN1C8EwZlGg69btCtVgtcIWgCscVb4eOlc/TH+b3FbKAn2XfLlRmRaDhGpsAl9HMWoIFURnqQ4ZDkcD3S6H9tGNrJd1uUtsWuhnXz7iz5nvg2Z82aWqOD8hqAaCH/jLpKWFNJiFVGqjgzVmxn665k9NCGmPx7rFPUkmQN8MFjJi1WO+w1WmUTdf0N6+A253f9Ls5ZKqJHRsoXDubd9c9bTuFI1YmaTiXiU45vJXMQRTgqO2XgWSbIANigWC4fQXa6D42pFZMVlOs9zjQ2dww84hMfNjqA1djAGf7C4oWBMNZqzyDiPbWvwBFZ1MQqSBq1SWrrrxF1l501U9GrYaOpgFlKBm0d1l4BE5Wft176EOzkyGGvgzhKfWMDla8CrOAQD8Qglpz3chIMAVTXucCIjvK74yNuNloZsXcGqGaFQguNsa9EhiAhWwviHFQmVCcxOl2bbMuc1QN0qsQC1TO1J0cQs9kbfWd8c="
댓글남기기