> ## Content Index
> Fetch the complete content index at: https://sudormrf.run/llms.txt
> Use this file to discover other available public pages before exploring further.

# Ghost에서 AWS S3 Storage를 이미지 서버로 사용하기
- URL: https://sudormrf.run/2017/01/28/ghost-s3-storage-image-server/
- Published: 2017-01-28T19:54:51.000Z
- Updated: 2022-09-18T08:47:18.000Z
- Author: Junho Cho
- Tags: ghost, AWS, S3, #Import 2026-10-24 03:36

보통 Ghost에서 글 쓰다가 이미지를 첨부 하고싶어 `![]()`을 타이핑하면

![](https://www.dropbox.com/s/pye0z3didhxrmcg/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202017-01-29%2003.43.23.png?raw=1)

요런 이쁜 업로드 창이 preview페이지에 떠서 Drag-and-Drop 같은 방식으로 쉽게 업로드 할 수 있다. 하지만 이미지는 블로그가 돌아가고 있는 로컬 서버안에 저장이 된다.

그런데 그런 방식에서 몇가지 단점이 보이면서 이미지는 다른 서버를 두어서 저장해두고 싶었다. 원래는 Dropbox에 저장하면서 public link를 매번 걸었는데, 별 문제는 없는데 그냥 맘에 안들다가 처음 S3를 이미지 서버로 사용할 수 있는 것을 이 [블로그](http://blog.saltfactory.net/ghost-s3-storage/?ref=sudormrf.run) 에서 알게 되었다.

즉 하고자 하는 것은 다음과 같다.

![s3-store-Custom-Storage-Module](https://sudormrf.run/content/images/2022/09/s3-store-Custom-Storage-Module.jpg)

복잡한 것 같지만, 쉽게 말하면 내가 글을 쓰면서 업로드하는 이미지들은 원래 [Digital Ocean](https://www.digitalocean.com/?ref=sudormrf.run)서버에 저장되던 것을 [AWS S3](aws.amazon.com/ko/s3)로 업로드되도록 한다. 왜 이렇게 하냐면 우선

1. [Digital Ocean](https://www.digitalocean.com/?ref=sudormrf.run)이 이미지를 로드하는 게 느리고, 용량제한이 있으니까
2. 장기적으로 봤을 때 더 scalable하게 관리해주는 [AWS S3](aws.amazon.com/ko/s3)가 좋아보이고 연습삼아 사용해보고 싶어서
3. MarkDown을 많이 사용하면서 나의 모든 이미지 파일들을 쉽게 웹링크로 관리하고 싶어서

이다.

> **Digital Ocean**은 사용하고자 한다면 제 [리퍼럴 코드](https://m.do.co/c/1b8e08bfd017?ref=sudormrf.run)로 부탁드립니다. 저 링크로 가입시 `10$` 드리며 결제시 저도 함께 `25$` 를 받습니다.

그런데 나는 사실 AWS는 EC2밖에 안써봤고 웹지식이 주워들은 수준이라 애를 꽤나 먹었다. 처음에 참고한 블로그는 나와는 다른 것 같아서 고대로 따라하다가 이상해질까봐 포기했고 [Extending Ghost Functionality with a Custom Storage Module](https://www.ghostforbeginners.com/extending-ghost-functionality-with-a-custom-storage-module/?ref=sudormrf.run)를 많이 참고했다. 또 그렇다고 바로 되지는 않아서 전 과정을 공유한다.

**하여튼 결과적으로 이미지 업로드를 S3로 우회시킨다면 `![]()` 에서 이미지 업로드 완료 시 `![](/content/images/2017/01/image.png)`에서 `![](https://s3.ap-northeast-2.amazonaws.com/<버켓이름>/2017/image.png)` 로 S3로 업로드가 되며 링크가 바뀐다!**

# 주의

1. 기존 로컬 이미지는 S3로 업로드 되지않는다.
2. 기존 로컬 이미지에 영향이 없어야하는데 뒤늦게 확인해보니 이미지가 로컬에 있어도 S3로 접근하는 에러가 난다 ㅠㅠ 따라서 지금은 처음 세팅하는 블로그에 해당

# 먼저 정리하면

1. AWS S3 버킷 생성. `bucket, region` 기억.

  - 파일 업로드 해보고 `Make Public`해보면 파일 접근하는 URL 기억.  
  예를 들어, `https://s3.ap-northeast-2.amazonaws.com/tmmsexy/`
2. AWS IAM으로 S3에 업로드 권한 가지는 계정 생성. `accessKeyId, secretAccessKey` 기억.
3. Ghost에서 S3 설정

```bash
mkdir -p content/storage/ghost-s3 	# 폴더만들고
cd content/storage/ghost-s3			# 이동
npm install ghost-s3-storage		# module 설치
npm install aws-sdk-promise
vim index.js						# 없으면 만들어줘야하는 파일

```

- `index.js`에 [code](https://raw.githubusercontent.com/kelyvin/ghost-s3-service/master/index.js?ref=sudormrf.run) 복붙
- Ghost 홈폴더에서 `./configure`를 아까 알던 값으로 채움. 예를 들어,

```
storage: {
  active: 'ghost-s3',
    'ghost-s3': {
      accessKeyId: 'XXXXXXXXXXXXXXXXXXXX',
      secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      bucket: 'tmmsexy',
      region: 'ap-northeast-2',
      assetHost: 'https://s3.ap-northeast-2.amazonaws.com/tmmsexy/'
    }
},

```

- `npm start —production`, 블로그에서 이미지 업로드시 S3로 업로드 되는 것을 확인!

여기서 부터는 헤딩 과정을 상세히 서술.

# AWS S3

당연하게도 Amazon Web Service(AWS) 계정이 필요하다. 만약 S3가 잘 뭔지 모르겠으면 AWS Korea에서 공유한 자료 [AWS 시작하기 및 Amazon S3 살펴보기 (윤석찬) ](http://www.slideshare.net/awskorea/aws-getting-started-and-amazon-s3?ref=sudormrf.run)를 참고하자. 참고로 S3도 요금이 있는데 난 아직도 얼마가 과금이 드는지 몰라서 바들바들하긴한데 그렇게 안많겠지?

우선 슬라이드 자료처럼 24번쨰 슬라이드를 참고해서 버킷을 만든다. 아, Region은 Seoul로 한다.

![](https://ww3.sinaimg.cn/large/006tNbRwgy1fc6xkeablaj30hq09zwfj.jpg)

그리고 심심풀이로 직접 파일을 업로드해보고 `Make Public`해보면 대충 감이 온다. 아무튼 이렇게 S3 버킷을 만들면 이걸로 외부에서 업로드가 가능하게 해줘야하는데 그건 31번째 슬라이드의 AWS Identity and Access Management (IAM) 부분이다.

![](https://ww3.sinaimg.cn/large/006tNbRwgy1fc6xlfbx9dj30hq09z758.jpg)

이 부분의 과정을 통해 S3를 이용하는 AWS(AmazonS3FullAcess 권한을 가진 Policy를 부여)의 유저를 추가해주고 그 `Access Key ID`와 `Secret Access Key`를 적어둔다. 당연히 이 두 개는 외부로 노출되면 안된다.

그것만 되면 이제 Ghost 서버에서 필요한 모듈을 설치해야한다.

# Ghost에서 S3 Storage 세팅

Ghost 홈폴더에서 다음을 실행

```
mkdir -p content/storage/ghost-s3 	# 폴더만들고
cd content/storage/ghost-s3			# 이동
npm install ghost-s3-storage		# module 설치
vim index.js						# 없으면 만들어줘야하는 파일

```

많은 곳에서 `index.js`에서

```
'use strict';
module.exports = require('ghost-s3-storage');

```

를 복붙 해주라하는데, 난 그렇게 해주면 안되었다. 뭐 일단은 여기까지 따라하고, 다시 Ghost 홈폴더에서 `./configure`를 수정해준다.

```
storage: {
    active: 'ghost-s3',
    'ghost-s3': {
        accessKeyId: 'aws_access_key_here',
        secretAccessKey: 'aws_secret_key_here,
        bucket: 's3_bucket_name',
        region: 'bucket_region'
        assetHost: 's3_acces_url'
    }
},

```

위 code를 우리는 `npm start —production`으로 Ghost를 활성화 시키므로 `config = { 요기 }`안에 `production: {요기}`의 끝부분에 넣어주면 된다. 내 code는 다음 처럼 생겼다.

```
storage: {
	active: 'ghost-s3',
    	'ghost-s3': {
        	accessKeyId: 'XXXXXXXXXXXXXXXXXXXX',
            secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            bucket: 'tmmsexy',
            region: 'ap-northeast-2',
            assetHost: 'https://s3.ap-northeast-2.amazonaws.com/tmmsexy/'
	}
},

```

`accessKeyID, secretAcessKey`는 아까 IAM에서 AWS 유저 추가해줄때 기억해둔 것, 그리고 S3에서 만든 `bucket`이름, 그리고 `regeion`은 서울을 뜻하는 `ap-northeast-2`를 넣어준다. 난 바보같이 그냥 `Seoul`했는데 그게 아니였다. 앞서 S3에 파일을 올려보고 `Make Public`해보면 `https://s3.ap-northeast-2.amazonaws.com/<버켓이름>/2017/image.png` 요런 형식이었는데, 이곳의 `ap-northeast-2`가 서울을 뜻한다.

`assetHost`는 사실 [CDN](http://docs.aws.amazon.com/ko%5Fkr/AmazonCloudFront/latest/DeveloperGuide/Introduction.html?ref=sudormrf.run)까지 따져서 넣어주면 좋은데 솔직히 내가 [CDN](http://docs.aws.amazon.com/ko%5Fkr/AmazonCloudFront/latest/DeveloperGuide/Introduction.html?ref=sudormrf.run)도 잘모르고, 그것을 해주는 Amazon CloudFront 설정이 헬 같아서 넘어같다. ~~이 블로그는 내수용 블로그다~~

> Amazon CloudFront는 .html, .css, .php 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 최종 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스입니다. CloudFront는 엣지 로케이션이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠를 제공합니다. CloudFront를 통해 서비스하는 콘텐츠를 사용자가 요청하면 지연 시간이 가장 낮은 엣지로 라우팅되므로 콘텐츠 전송 성능이 뛰어납니다.

![](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/GSG_0001_USMapWithRoute.png)

그냥 쉽게 이해해서 외국에서 접속할때 빠르게 이미지를 띄어준다는 것 같은데 난 깔끔 포기. 설정하고 싶으면 앞서 참고했다는 [여기](https://www.ghostforbeginners.com/extending-ghost-functionality-with-a-custom-storage-module/?ref=sudormrf.run)의 뒷부분을 참고.

여튼 설정이 다 되었다 생각하지만 아까 `index.js`가 나는 문제가 되어 `npm start —prudction` 실행시 다음과 같은 에러가 뜬다.

```
> ghost@0.11.4 start /var/www/ghost
> node index

WARNING: Ghost is attempting to use a direct method to send email.
It is recommended that you explicitly configure an email service.
Help and documentation can be found at http://support.ghost.org/mail.

ERROR: Your storage adapter does not inherit from the Storage Base.

 Error
    at Error.IncorrectUsage (/var/www/ghost/core/server/errors/incorrect-usage.js:3:18)
    at Object.getStorage (/var/www/ghost/core/server/storage/index.js:54:15)
    at setupMiddleware (/var/www/ghost/core/server/middleware/index.js:131:44)
    at /var/www/ghost/core/server/index.js:188:9
    at tryCatcher (/var/www/ghost/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/var/www/ghost/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/var/www/ghost/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/var/www/ghost/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/var/www/ghost/node_modules/bluebird/js/release/promise.js:691:18)
    at Promise._fulfill (/var/www/ghost/node_modules/bluebird/js/release/promise.js:636:18)
    at PromiseArray._resolve (/var/www/ghost/node_modules/bluebird/js/release/promise_array.js:125:19)
    at PromiseArray._promiseFulfilled (/var/www/ghost/node_modules/bluebird/js/release/promise_array.js:143:14)
    at Promise._settlePromise (/var/www/ghost/node_modules/bluebird/js/release/promise.js:572:26)
    at Promise._settlePromise0 (/var/www/ghost/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/var/www/ghost/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/var/www/ghost/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/var/www/ghost/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/var/www/ghost/node_modules/bluebird/js/release/async.js:17:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

```

걍 구글링 해보면 [깃헙 이슈](https://github.com/TryGhost/Ghost/issues/7487?ref=sudormrf.run)가 뜬다. 요약하자면 아까 `index.js`를

사용한 `index.js`를 다음의 [code](https://raw.githubusercontent.com/kelyvin/ghost-s3-service/master/index.js?ref=sudormrf.run)로 대체하고 또 `npm install aws-sdk-promise`를 한다.

안하면 나오는 에러가 `ERROR: Cannot find module 'aws-sdk-promise'`

다시 `npm install —production`으로 제대로 블로그 서버가 돌아가게 되고, S3로 이미지가 업로드 되는 것을 확인하였다.

# 후기

~~그런데, 난 결국 다른 MarkDown 프로그램으로 글을 작성하기 때문에 Ghost에서 바로바로 local image를 업로드하기 귀찮게 되었고 안쓸듯 하다~~

엉엉 내 노력, 하지만 좋은 걸 배웠으니 됬어.

1. 참고로, S3 이미지 업로드 방식으로 바꿨지만 이미 local에 있는 이미지들을 그대로 남는다.
2. 결국 이제 Digital Ocean 서버를 쓸 필요가 없어졌다. 한번 꺼졌다 키면 데이터가 날아가는 Heroku의 무료 계정에 이미지 서버는 S3로 하면 되기 때문에, [참고](http://blog.eyecuelab.com/2015/03/02/ghost-on-heroku-with-s3/?ref=sudormrf.run). 나중에 해봐야겠다.  
![](https://labnotebook.s3.amazonaws.com/2015/Mar/ghost_heroku_s3-1425323932870.png)