웹 취약점 공격 방법인 XSS, CSRF에 대하여 간단하게 알아보기
Firebase의 Hosting, Functions, Firestore를 이용하여 웹상에 메모장을 만들기
2018-02-13
Explanation
2021-05-12
Firebase의 Functions가 유료로 변경됨에 아래의 샘플 링크는 동작하지 않습니다!
예전부터 일을 하거나 웹서핑을 하다가 메모할 일이 생기면 메모장에 메모를 쓰고 또 메일로 들어가서 다시 그 내용을 나에게 보내두는.. 그런 비효율적인.. 일이 종종 있었습니다.
그래서 그때마다 ‘웹에 간단한 메모장 하나 만들어서 써야겠다’ 라고 생각만 했었는데..(이미 좋은 서비스들이 많이 있지만..) 이번에 간단하게 Firebase의 Hosting과 Functions 그리고 Cloud Firestore를 이용하여 간단하게 웹상에 메모장을 만들어 보았습니다.
샘플코드 : https://github.com/falsy/blog-post-example/tree/master/fireabase-note-sample
샘플링크 : https://fir-note-fab00.firebaseapp.com/?pw=1234
우선 Firebase console로 이동하여 프로젝트를 만듭니다.
저는 ‘cheolguso-note’ 라는 프로젝트를 만들었습니다.
우선 적당한 곳에 디렉토리를 만들고
1 2 3 4 |
ex. $ cd /Users/Cheolguso/Documents/ $ mkdir cheolguso-note $ cd ./cheolguso-note |
필요한 firebase 서비스를 설치하고 로그인합니다.
1 2 3 4 5 6 |
// firebase cli 설치 $ npm install -g firebase-tools // 프로젝트를 만든 계정으로 로그인합니다. $ firebase login // 프로젝트 시작? $ firebase init |
여기에서 Hosting과 Functions를 선택, 설치합니다. 그리고 언어를 저는 javascript로 선택하였습니다.
스페이스바 = 선택, 엔터 = 확인
1 2 3 4 5 6 7 8 9 |
/functions /etc /node_modules index.js package-lock.json package.json /public index.html firebase.json |
그러면 아마도 위와 같은 디렉토리 구조(보여지는 파일)가 생성됩니다.
public 폴더로 이동해서 아래와 같이 3개의 파일을 만들고 작성하겠습니다.
1 2 |
$ cd ./public $ vi ./index.html |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!DOCTYPE html> <html lang="ko-KR"> <head> <title>메모장</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="./style.css" type="text/css"> </head> <body> <textarea></textarea> <button>저장</button> <!-- <script type="text/javascript" src="./script.js"></script> --> </body> </html> |
1 |
$ vi ./style.css |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
body { margin:0; padding:0; } button { display: inline-block; margin:10px; width: 200px; font-size: 14px; line-height: 40px; color: #111; border:1px solid #ddd; border-radius: 0; outline: none; cursor: pointer; } textarea { width: 100%; height: 500px; padding: 20px 40px; display: block; background: #eaeaea; font-size: 13px; line-height: 20px; position: relative; box-sizing: border-box; border: 0; border-bottom: 1px solid #ddd; outline: none; } |
1 |
$vi ./script.js |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$(document).ready(function() { var firestoreUrl = ''; var pw = location.search; $.ajax({ method: "GET", url: firestoreUrl + '/noteText' + pw, success: function(data) { $('textarea').val(data.reuslt); } }); $('button').click(function() { $.ajax({ method: "PUT", url: firestoreUrl + '/updateNoteText' + pw, data: {text: $('textarea').val()}, success: function(data) { if(data.result) alert('저장되었습니다.'); } }); }); }); |
아직 Functions와 Firestore설정을 하지 않아서 index.html에서 스크립트 부분은 주석처리해 두었습니다.
그리고 Hosting 을 적용합니다.
1 |
$ firebase deploy --only hosting |
적용이 완료되면 커멘드창에 해당 호스팅의 URL을 알려줍니다.(Firebase console에서도 확인 가능합니다.)
해당 URL로 브라우저에서 접속이 되면 일단 1단계는 성공입니다.
Firebase console에서 생성한 프로젝트를 선택한 후에 ‘DEVELOP’ 탭의 ‘Database’ 를 선택한 후 ‘Realtime Database’가 선택되어 있다면 ‘Cloud Firestore’를 선택합니다.
그리고 컬렉션과 문서, 필드를 추가합니다.
저는 컬렉션 = note, 문서 = textarea, 필드 = text (string – 이곳에 값으로 스트링이 들어갑니다.)
위와 같이 작성하였습니다. 여기서 ‘note’, ‘textarea’, ‘text’ 제가 임으로 정한 키값입니다.
간단하게 스트링 값만 불러오고 업데이트만 할 것이라 DB설정은 이걸로 끝입니다.
아까 초반에 Firebase를 설치한 ‘cheolguso-note’ 디렉토리 안의 functions로 이동합니다.
그리고 여기에서는 몇가지 모듈을 설치해 줍니다.
1 2 3 4 |
// 편하게 디비에 접근할 수 있도록.. $ npm install --save firebase-admin // 편하게 CORS를 설정할 수 있도록.. $ npm install --save cors |
그리고 index.js 파일을 아래와 같이 수정해줍니다.
– 2017-02-14 수정 –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// 기본 functions 모듈을 불러오고 const functions = require('firebase-functions'); // 다른 서비스에 쉽게 접근하기위한 모듈로 보입니다 const admin = require('firebase-admin'); admin.initializeApp(functions.config().firebase); // api가 추가되더라도 디비 접근이 빈번하게 있을것 같아서 변수에 담아두고 const db = admin.firestore(); // 다른 도메인에서도 통신이 되도록 cors를 열어두고 const cors = require('cors')({origin: true}); // '/noteText' 라는 path 로 접근하면 exports.noteText = functions.https.onRequest((req, res) => { cors(req, res, () => { // 약간의 암호화... url 파람에 ?pw=1234 가 있어야만 하도록 if(req.query.pw !== '1234') return res.status(403).send('권한이 없습니다.'); // 디비에 'note' 컬렉션에 'textarea' 문서를 불러오고 db.collection('note').doc('textarea').get().then(doc => { // 성공하면 문서안의 'text'필드 값을 응답해 줍니다. return res.status(200).send({ reuslt : doc.data().text }); }).catch(err => { console.log('Error', err); }); }); }); // '/updateNoteText' 라는 path 로 접근하면 exports.updateNoteText = functions.https.onRequest((req, res) => { cors(req, res, () => { // 역시 약간의 암호화... if(req.query.pw !== '1234') return res.status(403).send('권한이 없습니다.'); // textarea 문서에 변화를 확인해서 업데이트가 되면 성공 응답을 보내줍니다. db.collection('note').doc('textarea').onSnapshot(doc => { return res.status(200).send({ result: true }); }); // 디비에 'note' 컬렉션에 'textarea' 문서의 'text' 필드의 값을 요청값 body의 text 값으로 업데이트 해줍니다. db.collection('note').doc('textarea').update({ text: req.body.text }); }); }); |
그리고 이제 Functions 를 적용합니다.
1 |
$ firebase deploy --only functions |
만약에 Error: functions predeploy error: Command terminated with non-zero exit code1 이런 오류가 뜬다면, 프로젝트를 만든 폴더(ex. ../cheolguso-note)의 디렉토리 경로 중에 띄어쓰기를 포함한 폴더의 그 이름 사이 공백을 지우면 해결될 수 있습니다.
Functions까지 성공적으로 적용이 되었다면, functions의 url을 커맨드 창에서 알려주는데, 그부분을 복사해서
Hosting 에서 작성했던 script.js 파일의 var firestoreUrl = ”; 이부분에 넣어주고 index.html에서 처음에 주석으로 해놨던 부분의 주석을 해제하고 다시 hosting 을 적용하면 끝입니다!
1 2 3 4 |
$(document).ready(function() { var firestoreUrl = 'https://XXXXXXXXXX.cloudfunctions.net'; var pw = location.search; ... |
1 2 3 4 5 6 |
... <textarea></textarea> <button>저장</button> <script type="text/javascript" src="./script.js"></script> </body> </html> |
1 |
$ firebase deploy --only hosting |