댓글 기능 추가 (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="
댓글남기기