PDF 다운로드 및 미리보기 적용하기

1. 문제

현재 apt2you에 제공되고 있는 분양공고문을 사용자들에게 제공하고 싶었다. 일반 브라우저에서는 해당 파일을 다운로드 하고 모바일/앱에서는 미리보기를 적용하고 싶었다.

2. 해결방법 생각

1) request, fs 모듈을 사용하여 PDF를 원하는 경로에 저장하는 방법.

  • 각각의 브라우저(chrome, window, firefox 등…)에서 제공하는 기본 다운로드 경로가 변경된다면 해당 경로로 저장 하기가 쉽지 않다.
  • 모바일에서도 역시 경로 문제와 viewer문제가 있어 사용하기 어려운 방법 이었다.

2) get 방식을 이용하여 PDF파일을 다운로드 하는 방법.

  • 기존에 apt2you에서 다운로드 받는 방식이 post방식으로 되어있어 get 방식 즉, query string을 통해 데이터를 찌르면 response가 실패 하는줄 알았다. 하지만 query string을 통해서도 다운로드가 무리없이 실행되었다.
  • <a>를 이용하여 PDF 다운로드 url을 통해 바로 다운받을수 있도록 하였다.

3) PDF 파일을 다운로드 받아 아마존 S3에 저장 후 해당 파일의 주소롤 접근하는 방법.

  • 결론적으로 말하면, 이 방법을 선택하였다. 밑에서 이 방법을 선택한 이유를 설명하도록 하겠다.

3. 여러 시련과 해결시도

첫번째 시련

  • 2번 방식을 사용하여 desktop browser에서 다운로드하는 것을 쉽게 해결하였지만 현재 앱에서 사용하고 있는 inappbrowser를 통해 PDF를 preview할 방법을 찾는 것이 쉽지 않았다.

Webview는 각각의 OS에 내장된 웹 브라우저를 app에서 표현하게 해준다. 쉽게 말해 app안에 html iframe을 넣어놓은 것이다. Webview를 이용하여 웹페이지를 앱에서 호출하여 하이브리드 형태의 앱을 만들수 있다.

cordova의 inappbrowser 라는 플러그인이 있다. 이 플러그인은 앱 내부에서 여러가지 리소스(문서, 동영상..)을 표시해 주는 역할을 한다. 사용자가 앱을 종료하지 않고도 웹 페이지를 볼수 있게 도와주는 플러그인이다.

첫번째 해결시도

  • Google Docs API를 사용하여 해결하기로 했다. 하지만 chrome에서는 잘 동작하지만 어째서인지 safari등 다른 브라우저와 Webview를 204 No cotent 상태가 제공되는 에러가 있었다. 이 문제를 해결하기 위해 여러 개발자들이 구글에 문의 하였지만 아직까지 해결되지 않고 있었고, 용량이 큰(25MB) PDF 파일은 간헐적으로 로드를 실패하여 이 방법의 사용은 포기 하였다.
  • PDFViewer component를 만들어 로드를 실패할 때마다 재요청하는 방법을 사용해보려 했지만, 응답을 너무 오래 걸리는 문제가 있어 사용하지 않았다.

https://docs.google.com/viewer는 구글에서 제공하는 doc api 이다. 파라메터로는 url: preview할 문서 주소, embedded: true 설정시 embedded mode interface 제공를 사용한다. embedded mode 사용시 웹페이지의 주변정보가 집중되며, 사용자가 문서 탐색시 Google로 넘어가지 않는 이점이 있다.

예제

http://docs.google.com/viewer?url=http://geek-easy.com/wp-content/uploads/2012/01/TOP-15-ESSENTIAL-FREE-SOFTWARE.PDF&embedded=true

두번째 시련

  • 3번 방식을 사용하기 위해 task를 만들어 분양공고문이 존재하는 아파트의 DB를 변경하고, aws S3에 PDF를 저장해 두었다. 모바일 Webview에서 PDF를 보여주거나 다운로드 받는것을 쉽게 적용되었지만 아마존 CloudFront에 등록해 둔 DNS로 S3의 PDF로 접근하게 되면 CSP(Content Security Policy) 정책에 위반되어 데이터에 접근할수 없는 문제가 생겼다.
Refused to load plugin data from '...' because it violates the following Content Security Policy directive: "object-src 'none'".

두번째 해결시도

  • 그렇다면 CloudFront에서 데이터를 내려주기전 response 헤더의 값을 바꿔주면 해결되지 않을까?라고 생각하여 aws lambda를 사용해 보기로 하였다.
  • 통신 중간에 aws lambda에서 response헤더의 CSP 정책을 변경하려 하였다. 하지만 정말 안타깝게도 현재 한국 리전에서는 CloudFront trigger를 지원하지 않았다… 미국 동부에서만 지원하여 적용하지 못하였다.

예제

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers || [];
    headers['content-security-policy'] = [{
      key: 'Content-Security-Policy',
      value: "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; object-src 'self'"
    }];

    callback(null, response);
};

4. 해결 및 배운점

  • 다양한 방법을 시도해 보았지만 결국 S3에 올린 PDF파일의 original 주소를 사용하여 접근하는 방법을 사용하였다. 추후에 한국리전에 CloudFront trigger를 지원한다면 두번째 해결 방법을 사용하여 변경해 보고 싶다…
  • 빠르게 적용할수 있을 것이라 생각 했지만, 뜻밖에 시련에 부딪혀 다양한 부분을 공부 할수있어 좋은 경험이었다.
  • aws lambda 사용방법 및 약간의 삽질로 인해 api gateway 사용방법, webview, inappbrowser plugin 등…

5. 참조

6. P.S.

  • cordova inappbrowser에서는 모바일의 시스템 웹 브라우저으로 target 지정하는 _system을 지원한다.