OGP API 서버 Heroku에 배포하기

이 문서는 블로그에서 외부 게시글에 대한 미리보기를 작성하면서 Open Graph를 적용하는 방법을 정리한 것입니다.
Hugo나 Hexo를 기준으로 적용하는데 필요한 OGP 처리용 API Server를 구성하는 내용이므로 OGP에 대한 상세한 내용은 아래의 참고 자료를 활용하시기 바랍니다.
기본적인 개발 환경은 이미 활용하는 것으로 가정하고 git / vscode / macbook을 기준으로 설명합니다

OGP란?

오픈그래프 (Open Graph)는 HTML 메타 태그의 종류 중의 하나로 사용성에 약간의 차이를 가진다. SNS에서 그 개념이 시작되었는데, SNS에 링크를 걸어 놓으면 이 링크가 어떤 데이터를 가지고 있는지를 알 수 있는 방법이 없기 때문에 링크에 대한 미리보기로써 이미지, 설명, 제목등을 나타내기 위한 태그들이다.

오픈그래프에 대해서는 The Open Graph Protocol 사이트를 참고하면 된다.

오픈 그래프의 기본적인 태그들을 다음과 같다.

기본 태그들
Tag Description
og:title 사이트의 제목 태그
og:type 사이트의 종류 스타일 태그 (ex. website, video, movie, …)
og:image 사이트의 대표 이미지 태그
og:url 사이트의 대표 URL 태그

옵션으로 적용할 수 있는 태그들을 다음과 같다.

옵션 태그들
Tag Description
og:audio 사이트의 포함되는 Audio 파일 태그
og:description 사이트의 설명 태그
og:determiner 사이트의 구분자 태그
og:locale 사이트의 언어 태그 (기본 값은 en_US, 한글은 ko_KR, …)
og:locale:alternate 사이트의 다국어일 경우 대체 언어 태그
og:site_name 사이트의 세부 카테고리 의미 태그
og:video 사이트의 포함되는 Video 파일 태그

추가적으로 객체들 (Image, Audio, Video, …)에 대한 구조 속성 태그들은 다음과 같다.

객체 구조 태그들
Tag Description
og:object 또는 og:object:url 객체의 경로 (eg. Image, Audio, Video, …) 태그
og:object:secure_url SSL/TLS(HTTPS) 경로 태그
og:object:type 객체 유형 태그 (eg. image/jpeg, application/x-shockwave-flash, audio/mpeg, …)
og:object:width 객체 너비 태그
og:object:height 객체 높이 태그
og:object:alt 객체 설명 태그

OGP를 위한 API 구성하기

API 구성의 목적은 링크 URL이 지정되면 해당 URL의 HTML 내용 중에서 Head 태그 내의 Open Graph 정보를 추출해서 JSON 형식으로 반환하는 것이다. 이 정보를 기준으로 각 블로그 툴 (SSG : Static Site Generator)에서는 SNS처럼 게시글 링크를 표시하는 Preview Box를 구성하게 된다.

가장 먼저할 것은 API를 구동해서 사용할 수 있는 무료로 지원되는 호스팅 서버를 결정하는 것이다. 처음에는 firebase를 검토해서 구현을 했지만 외부 URL을 긁어와야 하는 부분에서 Firebase는 외부로 연계되는 경우 무료 계정에서는 사용할 수 없는 제한이 존재하기 때문에 Heroku 를 이용하는 것으로 결정했다.

AWS, Azure, GCP 등의 외부에서 호출해서 결과를 받을 수 있는 호스팅을 이미 할 수 있는 계정이 존재한다면 그것을 이용해도 된다.

가장 먼저 시작해야 할 것이 Heroku의 계정을 만드는 것이다.

Heroku 계정설정

Heroku는 Git 기반으로 운영되기 때문에 당연히 로컬 PC에 git가 설치되어 있어야 한다.

Heroku 사이트에 계정 생성

무료 계정은 아래와 같은 제한이 있다

  • 30분간 접속이 없으면 사이트를 Sleep 시킨다. (요청이 들어와서 재 구동되는데 10~30초 정도 걸림)
  • 계정 당 한달에 550시간만 사용 가능하다. 계정에 사이트가 여러 개인 경우는 합산 시간을 기준으로 한다.
회원가입
[ 회원가입 ]

아래와 같이 정보를 등록하고 “Create Free Account” 버튼을 눌러서 가입하면 된다. 기본적으로 사용하는 언어는 본인이 주로 사용하는 언어를 선택하면 된다.

회원정보설정
[ 회원정보설정 ]

Heroku CLI 설치

Heroku는 Git기반이기 때문에 로컬 PC에서 작업하고 Heroku로 Push해서 처리하면 된다. 따라서 Heroku 연동을 위한 CLI (Command Line Interface)를 설치해야 한다.

  • heroku 사이트에 로그인한 후에 다운로드로 이동해서 플랫폼에 맞는 것을 다운로드해서 설치 (아래 그림의 빨간색 처리 부분을 통해서 이동)

    CLI 설치 페이지
    [ CLI 설치 페이지 ]

  • 설치 페이지의 내용대로 설치

Heroku CLI 주요 명령 정리

Heroku CLI Commands
Command Description
heroku login 로그인
heroku logout 로그인은 재부팅을 하더라도 지속성을 가지므로 로그아웃을 원할 경우는 이 명령을 수행해야 한다.
heroku create [프로젝트 이름] Git 저장소와 연결될 폴더에서 새로운 프로젝트를 생성하는 명령으로 “프로젝트 이름”을 주지 않으면 랜덤으로 생성되고 URL과 연계되므로 프로젝트 이름을 지정하는 것이 좋다.
heroku remote -v 생성된 프로젝트의 원격 저장소 정보를 보는 것으로 로컬 저장소는 “heroku” 라는 이름으로 생성된다.
heroku git:remote -a 프로젝트 이름 위의 create가 새로운 프로젝트를 생성하는 것이라면 이미 존재하는 프로젝트에 연결하는 것은 이 명령으로 프로젝트를 지정하면 된다.
heroku config:set [키=값] 프로젝트와 연계되는 저장소에 환경 변수를 설정한다.
heroku config 프로젝트와 연계되는 저장소에 설정되어 있는 환경 변수들을 출력한다.
heroku config:get [키] 프로젝트와 연계되는 저장소에 설정되어 있는 환경 변수들 중에서 지정한 키의 값을 출력한다.
heroku config:unset [키] 프로젝트와 연계되는 저장소에 설정되어 있는 환경 변수들 중에서 지정한 키의 환경 변수를 삭제한다.
heroku logs [–tail] Heroku에서 구동된 서버의 Console log를 출력한다. 테스트 중에 오류나 디버그 용도로 사용한다.

Heroku의 프로젝트 이름은 저장소 명칭일뿐만 아니라 향후 외부로 노출될 App 식별명이기도 하므로 반드시 잘 생각해보고 생성해야 한다.
외부에서 접속하는 주소는 [프로젝트 명].herokuapp.com 으로 생성된다.

OGP 처리를 위한 API 구성 (로컬 PC)

아주 간단한 OGP API Server를 구성할 것이기 때문에 node + express + ogp_parser 조합 으로 아래와 같이 소스를 구성한다.

기본 설치 작업
$ npm init    # package.json 구성
$ npm install express --save    # node express 설치
$ npm install ogp-parser --save # ogp-parser 설치

위와 같이 기본적인 설치를 한 후에 package.json 파일을 열고 나머지 부분을 구성하도록 한다.

전체 package.json 파일
 1{
 2  "name": "ogp-to-json",
 3  "version": "0.0.1",
 4  "description": "지정한 URL 정보를 기준으로 Open Graph 정보를 추출해서 JSON 형식으로 반환합니다.",
 5  "main": "index.js",
 6  "scripts": {
 7    "start": "node ./"
 8  },
 9  "keywords": [
10    "Open Graph",
11    "ogp",
12    "Heroku"
13  ],
14  "author": "morris chang",
15  "license": "MIT",
16  "engines": {
17    "node": "v13.2.0",
18    "npm": "6.13.2"
19  },
20  "dependencies": {
21    "express": "^4.17.1",
22    "ogp-parser": "^0.4.4"
23  }
24}

위의 package.json 파일의 내용 중에 아래 서술한 부분은 로컬테스트와 Heroku에서 실행될 정보를 구성한 것이다.

  • scripts : 로컬 및 Heroku에서 npm을 통해서 실행할 명령이다. (eg. npm start)
  • main : scripts/start에 “node ./” 으로 지정하면 main에 설정된 파일을 처리한다. main 지정이 없을 경우는 scripts/start를 “node ./index.js”로 해 줘야 한다.
  • engines : Heroku에서 실행될 node와 npm에 대한 버전 정보를 지정한 것이다. (별다른 문제가 없다면 로컬에 설치된 버전을 그대로 이용하면 된다.)
    • 버전 정보를 모를 경우는 node -v, npm -v 명령으로 현재 설치된 버전 확인이 가능하다.

index.js 파일에 아래와 같이 코드를 구성한다.

index.js
 1const express = require('express');
 2
 3const app = express();
 4const PORT = process.env.PORT || 80
 5
 6// 호출 url : opg-to-json.herokuapp.com/ogp?url=.....
 7app.get("/ogp", (req, res) => {
 8    const parser = require('ogp-parser');
 9    const params = req.query;
10    const cacheControl = 'public, max-age=31557600, s-maxage=31557600';     // cache 1year
11
12    if (!params.hasOwnProperty('url')) {
13        console.error('Error getting ogp data: Please provide url parameter');
14        return res.json({error: 'Error getting ogp data: Please provide url parameter'});
15    }
16
17    return parser(encodeURI(params['url']), false)
18        .then((data) => {
19            console.log(data);
20            console.log(params['url']);
21            if (!data.hasOwnProperty('title')) {
22                console.error('Error getting ogp data: no ogpData returned');
23                return res.json({error: 'No OGP data returned from given url'});
24            }
25            let ogpData = {};
26            ogpData['siteName'] = data.title;
27            for(let prop in data.ogp) {
28                if (/^og:/g.test(prop)) {
29                    ogpData[prop.split(':')[1]] = data.ogp[prop][0];
30                }
31            }
32            return res.set('Cache-Control', cacheControl).json(ogpData);
33        })
34        .catch((err) => {
35            console.error('Error getting ogp data: ' + err);
36            return res.json({error: err});
37        });
38});
39
40app.listen(PORT, err => {
41    if (err) throw err;
42    console.log("%c Server running", "color:green");
43});

위의 코드는 아래와 같이 처리를 위한 정보를 구성하고 있다.

  • line 3 : express 서버 인스턴스 생성
  • line 4 : express가 리스닝할 포트를 지정한다. heroku에서는 환경변수에서 PORT를 할당받는 방식을 선택해야 한다.
  • line 7 : “ogp” 패스로 Request가 오면 처리를 수행하는 핸들러를 구성한다.
  • line 9 : Request로 전달된 Parameter들을 변수에 할당한다.
  • line 12 : Request의 Query String으로 “url”이 전달되었는지를 검사한다.
  • line 13-14 : “url” 이 없다면 오류 처리
  • line 17 : “ogp-parser”를 이용해서 지정된 “Url”의 데이터를 읽어서 OGP 데이터 추출
  • line 18-33 : OGP 데이터의 존재 여부 검증 및 캐시 데이터 및 반환 데이터 처리와 오류 발생시 처리
  • line 40-43 : express가 지정한 포트의 요청을 받을 수 있도록 구동

이제 로컬에서 먼저 실행을 해서 정상적인 OGP json이 반환되고 있는지를 확인해 보면 된다.

shell에서 실행 검증
$ npm start
$ curl "http://localhost/ogp?url=http://ccambo.gitlab.io/2017/07/09/VSCODE-VSCode%EC%97%90-Git-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/"

정상적으로 JSON 포맷의 OGP 데이터가 출력되는 것을 확인했으면 이제 Heroku에 배포를 하면 된다.

Heroku에 배포하기

Heroku가 git 기반으로 동작한다고 이야기를 했으므로 git 저장소 생성(프로젝트 생성)을 진행한다.

shell에서 git구성 및 Heroku 프로젝트 생성 (로컬 프로젝트 폴더)
$ git init        # 프로젝트 폴더에 git 저장소 구성
$ heroku login    # heroku에 로그인 id/pw를 입력해서 처리 (이미 되어있는 경우는 Skip)
$ heroku create [프로젝트 이름]   # heroku에 프로젝트(저장소) 생성
Creating ⬢ [프로젝트 이름]... done
https://[프로젝트 이름].herokuapp.com/ | https://git.heroku.com/[프로젝트 이름].git     # heroku 서버 접속 URL 및 Git 저장소 URL 표시

생성이 끝나면 Heroku 프로젝트 접속용 URL과 Git 저장소 URL이 출력된다. 이를 이용해서 로컬 저장소를 Heroku 저장소와 연결한다.

shell에서 원격 저장소 연결 (로컬 프로젝트 폴더)
$ git remote add heroku [프로젝트 Git 저장소 URL]   # 위에서 출력된 GIt 저장소 URL 사용

만일 해당 출력을 확인하지 못해서 git 저장소 URL을 모를 경우는 Heroku에 접속해서 Dashboard로 이동한 후에 생성한 프로젝트의 “Settings” 정보를 통해서 확인할 수 있다.

Heroku Dashboard 페이지에서 프로젝트 선택
[ Heroku Dashboard 페이지에서 프로젝트 선택 ]

이제 저장소 연결이 완성되었으므로 로컬의 소스를 Heroku에 올리면 된다. (일반적인 git 처리와 동일하다)

shell에서 Heroku로 소스 올리기
$ git add .   # 변경된 파일들 stage 처리
$ git commit -m "커밋 메시지"   # stage를 로컬 저장소에 commit
$ git push heroku master        # 로컬 저장소(heroku)의 commit 내용을 Heroku (master)로 push (upload)
$ heroku open                   # 저장소에 올려진 소스를 기반으로 Heroku Application 구동

별다른 오류가 없었다면 heroku open 로 실행된 브라우저는 당연히 Cannot GET / 라는 오류가 발생하는 것이 정상이다.

위에서 구동한 소스가 /ogp 패스에 대해서만 동작하도록 구성했기 때문으로 정상적으로 구동된 것을 확인할 수 있다.

정상적인 결과를 확인하기 위해서는 열려있는 브라우저 주소 창에 다음과 같이 입력하면 된다.

브라우저 주소창에 URL 설정
"http://[프로젝트 이름].herokuapp.com/ogp?url=http://ccambo.gitlab.io/2017/07/09/VSCODE-VSCode%EC%97%90-Git-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/"

또는 터미널에서 아래와 같이 curl을 이용해서도 확인 가능하다.

shell에서 URL 설정
$ curl "http://[프로젝트 이름].herokuapp.com/ogp?url=http://ccambo.gitlab.io/2017/07/09/VSCODE-VSCode%EC%97%90-Git-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/"

결론

위와 같이 처음으로 단순한 API Server를 로컬에서 구성해서 Git 기반의 무료 어플리케이션 호스팅인 Heroku에 올려서 처리하는 것을 확인해 보았다.

  • 무료 계정을 일부 제한이 존재한다.
  • Git 기반으로 통합되어 있다.
  • Heroku CLI를 기준으로 Heroku에 각종 처리를 수행한다.

참고자료