"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " General Setting """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Sets history line sethistory=500
" Mapping <leader> => , let mapleader=","
" Show current position at bottom-right set ruler
set lazyredraw
set magic
" Show matching brackets when text indicator is over them set showmatch
" How many tenths of a second to blink when matching brackets setmat=2
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Color Setting """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
하지만 뭔가 세련된 방법이 없을까 고민다하가 튜터님의 조언으로 JWT라는 것을 사용해 보기로 했다.
JSON Web Token
들어가기 전에 JSON Web Token의 개념을 알아두자.
JWT??
JSON Web Token은 공개된 업계 표준인 RFC 7519 방식으로 양 측의 클레임(Claims)을 안전하게 한다. JWT.IO를 사용하여 JWT를 디코드, 검증 및 생성할 수 있다.
Claim이라는 용어가 처음에 낯설었지만 서버와 클라이언트가 주고받는 정보나 메세지 정도로 생각하면 된다.
JSON 객체로된 클레임은 디지털로 서명되었기 때문에 검증되고 신뢰할 수 있다고 한다.
또한 다음과 같은 개념을 가진다.
Compact: JWT는 크기가 작기 때문에 URL, POST 파라미터 또는 HTTP Header에 포함될 수 있다. 크기가 작아 전송 속도 또한 빠르다고 할 수 있다.
Self-contained: JWT는 Payload에 필요한 데이터를 포함한다. 따라서 토큰을 생성할 때 데이터베이스에서 정보를 가져오면 그 다음부터는 데이터베이스에 질의를 하지 않아도 된다.
언제 JWT를 사용할까?
Authentication: JWT를 사용하는 가장 일반적인 시나리오다. 유저가 처음 로그인을 하면, 유저 정보를 포함한 JWT를 생성해 발급해준다. 그 후 유저는 각 요청에 발급받은 토큰을 포함해 보내는데, 서버에서 토큰을 기반으로 유저의 정보, 허용되는 경로 등의 정보를 데이터베이스의 접근없이 알 수 있다. 또한 세션을 유지하지 않아도 되므로 서버의 비용이 줄어들 수 있다.
Information Exchange: JWT는 공개 키나 비밀 키로 서명할 수 있기 때문에 안전하게 정보를 교환할 수 있다. 게다가 서명이 헤더와 페이로드를 사용하여 계산되므로 내용이 변경되었는지도 판별할 수 있다.
JWT의 구조
JWT는 점(.)으로 구분되는 세가지 요소를 가진다.
Header
Payload
Signature
그래서 결과적으로 xxxxx.yyyyy.zzzzz 형태를 가지게 된다.
Header
JWT의 첫 번째 부분은 Header 로, 헤더는 2가지 속성 값을 가진다.
alg: 해싱 알고리즘. 보통 HMAC SHA256 혹은 RSA가 사용된다.
typ: 토큰의 타입 = JWT
다음과 같이 Header를 생성할 수 있다.
1 2 3 4
{ "alg": "HS256", "typ": "JWT" }
Payload
두 번째 부분은 처음에 클레임을 포함하는 Payload가 들어가게 된다.
클레임은 3가지로 구분할 수 있는데, 다음과 같다.
Reserved claims: 이것은 필수는 아니지만 JWT를 사용함에 있어 권장되는 미리 정의된 클레임 세트이다. iss(발행자), exp(만료 시간), sub(주제), aud(고객) 등을 포함한다.
Public claims: 이것은 JWT사용자끼리 충돌을 방지하기 위한 값을 포함한다. 보통 충돌을 방지하기 위한 네임스페이스를 사용한다.
자바스크립트는 지난 몇 년간 꽤 많은 변화가 있었습니다. 지금 당장 쓸 수 있는 새로운 12가지 기능은 다음과 같습니다.
1. JavaScript History
이번에 자바스크립트에 추가된 기능을 ECMAScript 6 이라고 부릅니다. ES6 나 ES2015+ 라고도 불리죠.
1995년 자바스크립트 개념이 도입된 이래로, 자바스크립트는 서서히 진화해 오고 있었습니다. 기능을 추가하는 작업은 몇 년에 한번씩 일어나곤 했습니다. ECMAScript는 자바스크립트의 이러한 방향을 바로 잡기 위해 1997년에 등장했습니다. ECMAScript는 ES3, ES5, ES6 등과 같은 버젼을 발표해 왔습니다.
보시다시피, ES3, ES5 와 ES6은 각각 10년과 6년의 간격이 있습니다. 새 모델은 매년 작고 점진적인 변화를 만드는 것입니다. ES6 처럼 한 번에 엄청난 변화를 하는것 대신에요.
Chrome, MS Edge, Firefox, Safari, Node와 많은 다른 환경에서 이미 자바스크립트 ES6의 기본적인 기능을 지원합니다. 그래서 당신이 이제 배울 모든 튜토리얼은 당장 사용해 볼 수 있습니다.
ECMAScript 6를 시작해봅시다!
3. Core ES6 Features
브라우저 콘솔에서 모든 코드들을 테스트 할 수 있습니다.
그러니 글만 읽지 말고 ES5와 ES6 예제를 테스트 해보길 바랍니다. 어서 해보죠 💪
3.1 Block scope variables
ES6에서 변수를 선언하기 위해 var 대신 let/const를 사용합니다.
var에 무슨 문제가 있었을까요?
var의 문제는 for문이나 if문과 같은 코드 블록에서 변수가 누출되는 것입니다.
ES5
1 2 3 4 5 6 7 8 9 10
var x = 'outer'; functiontest(inner) { if (inner) { var x = 'inner'; // scope whole function return x; } return x; // gets redefined because line 4 declaration is hoisted } test(false); // undefined 😱 test(true); // inner
왜냐하면 if 블록이 실행되지 않더라도 네째 줄의 var x가 호이스트(hoisted)되기 때문입니다.
var 호이스팅:
var는 함수 범위(scope)입니다. 이것은 선언되기 전에도 전체 함수에서 사용할 수 있습니다.
선언(Declarations)은 호이스트(Hoisted)됩니다. 그래서 선언되기 전에도 변수를 사용할 수 있습니다.
초기화(Initializations)는 호이스트되지 않습니다. 만약 var를 사용한다면 항상 맨 위에 변수를 선언하세요.
호이스팅 규칙을 적용해 본 후에 무슨 일이 일어났는지 더 잘 이해할 수 있습니다. ES5
1 2 3 4 5 6 7 8 9 10
> var x = 'outer'; > functiontest(inner) { > var x; // HOISTED DECLARATION > if (inner) { > x = 'inner'; // INITIALIZATION NOT HOISTED > return x; > } > return x; > } >
ECMAScript 2015가 도와주러 왔습니다:
ES6
1 2 3 4 5 6 7 8 9 10
let x = 'outer'; functiontest(inner) { if (inner) { let x = 'inner'; return x; } return x; // gets result from line 1 as expected } test(false); // outer test(true); // inner
var를 let으로 변경하면 예상대로 작동합니다. if 블록이 호출되지 않는다면, 변수 x는 블록 밖으로 호이스트되지 않습니다.
호이스팅(hoisting) 과 “temporal dead zone”
ES6에서 let은 블록의 맨 위로 호이스트 됩니다.(ES5 처럼 함수의 맨위가 아닌)
그러나, 변수가 선언되기 전에 블록에서 변수를 참조하는 것은 ReferenceError를 초래합니다.
let은 차단된 스코프를 가집니다. 선언되기 전에 사용할 수 없습니다.
“Temporal dead zone”은 블락의 처음부터 변수가 선언되기 전까지의 영역입니다.
IIFE
IIFE를 설명하기 전에 예제를 봅시다. 여길 봐주세요:
ES5
1 2 3 4
{ var private = 1; } console.log(private); // 1
보시다시피, private은 유출됩니다. 그것을 감싸기 위해 IIFE (immediately-invoked function expression)을 사용할 필요가 있습니다:
ES5
1 2 3 4
(function(){ var private2 = 1; })(); console.log(private2); // Uncaught ReferenceError
jQuery/lodash 나 다른 오픈소스 프로젝트를 살펴보면 그것들은 전역 환경의 오염을 피하기위해 IIFE를 사용하고, 단지 _, $ 및 jQuery만 전역으로 정의합니다.
ES6에서 더 깔끔합니다. 블록과 let을 사용하면 더이상 IIFE를 사용할 필요가 없습니다.
ES6
1 2 3 4
{ let private3 = 1; } console.log(private3); // Uncaught ReferenceError
Const
변수를 전혀 변경하지 않는다면 const를 사용할 수 있습니다.
밑줄: var를 버리고 let과 const로.
모든 참조에 var의 사용을 피하고 const를 사용하세요.
만약 참조를 재 정의해야 한다면 const 대신 let을 사용하세요
3.2 Template Literals
템플릿 리터럴을 사용하면 더이상 중첩된 연결을 하지 않아도 됩니다. 여길 보세요:
ES5
1 2 3
var first = 'Adrian'; var last = 'Mejia'; console.log('Your name is ' + first + ' ' + last + '.');
이제 우린 역 따옴표( ` )와 문자열 보간 ${}을 사용할 수 있습니다:
ES6
1 2 3
const first = 'Adrian'; const last = 'Mejia'; console.log(`Your name is ${first}${last}.`);
배열 비구조화를 사용하여 요소를 가져오거나 값을 바꾸길 바랍니다. 임시 참조를 만들지 않아도 되게 도와줍니다.
여러 값을 반환할 때 배열 비구조화를 사용하는 대신 객체 비구조화를 사용하세요.
3.5 Classes and Objects
ECMAScript 6에서, “constructor functions” 🔨 대신 “classes” 🍸 를 사용합니다.
자바스크립트에서 모든 단일 객체는 또 다른 객체인 프로토타입을 가지고 있습니다. 모든 자바스크립트 객체는 그들의 프로토타입의 메서드와 프로퍼티를 상속받습니다.
ES5에서, 우리는 다음과 같이 객체를 생성하기 위해 생성자 함수를 사용하여 객체 지향 프로그래밍(OOP)를 했습니다.
ES5
1 2 3 4 5 6 7 8 9 10 11
var Animal = (function () { functionMyConstructor(name) { this.name = name; } MyConstructor.prototype.speak = functionspeak() { console.log(this.name + ' makes a noise.'); }; return MyConstructor; })(); var animal = new Animal('animal'); animal.speak(); // animal makes a noise.
ES6에는 몇가지 달콤한 문법이 존재합니다. 우리는 적은 보일러 플레이트와 새로운 키워드인 class와 constructor 를 사용해 같은 작업을 수행할 수 있습니다. 또한 우리가 constructor.prototype.speak = function () 와 speak() 메서드를 얼마나 명확하게 정의했는지 확인해보세요:
ES6
1 2 3 4 5 6 7 8 9 10
classAnimal{ constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } const animal = new Animal('animal'); animal.speak(); // animal makes a noise.
보시다시피, 두 스타일 (ES5/6)은 동일한 결과를 만들어 내고 같은 방식으로 사용됩니다.
모범 사례:
항상 클래스 문법을 사용하고 프로토 타입을 직접 조작하는 것을 피하세요. 왜냐구요? 왜냐하면 코드가 더 간결해지고 이해하기도 더 쉬워지기 때문입니다.
빈 생성자를 가지는 것을 피하세요. 클래스는 생성자가 지정되지 않았다면 기본 생성자를 가집니다.
3.6 Inheritance
이전 Animal클래스를 기반으로 진행하겠습니다. 우리는 그것을 확장하여 Lion 클래스를 정의하기를 원합니다.
ES5에서, 이것은 프로토 타입 상속과 조금 더 관련이 있습니다.
ES5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
var Lion = (function () { functionMyConstructor(name){ Animal.call(this, name); } // prototypal inheritance MyConstructor.prototype = Object.create(Animal.prototype); MyConstructor.prototype.constructor = Animal; MyConstructor.prototype.speak = functionspeak() { Animal.prototype.speak.call(this); console.log(this.name + ' roars 🦁'); }; return MyConstructor; })(); var lion = new Lion('Simba'); lion.speak(); // Simba makes a noise. // Simba roars.
모든 디테일한 사항들을 검토하지 않을겁니다:
셋째 줄에서 우리는 Animal 생성자를 매개변수와 함께 명시적으로 호출합니다.
7-8 줄에서 Lion 프로토 타입을 Animal의 프로토 타입으로 지정했습니다.
11 줄에서 부모 클래스인 Animal에서 speak 메서드를 호출합니다.
ES6에서 새 키워드인 extends와 super가 있습니다.
ES6
1 2 3 4 5 6 7 8 9
classLionextendsAnimal{ speak() { super.speak(); console.log(this.name + ' roars 🦁'); } } const lion = new Lion('Simba'); lion.speak(); // Simba makes a noise. // Simba roars.
이 ES6 코드가 같은 동작을 하는 ES5 코드와 비해 얼마나 읽기 쉬운지 보세요. 승리!
보시다시피, promise를 사용하면 다른 함수가 완료된 후에 무언가를 하기 위해 then을 사용할 수 있습니다. 더이상 중첩 함수를 사용할 필요가 없어집니다.
3.8 Arrow functions
ES6는 함수 표현식을 제거하지 않았지만, 화살표 함수(arrow functions)라고 불리는 것을 새로 추가했습니다.
ES5에서 this의 문제가 몇가지 있었습니다:
ES5
1 2 3 4 5 6 7
var _this = this; // need to hold a reference $('.btn').click(function(event){ _this.sendData(); // reference outer this }); $('.input').on('change',function(event){ this.sendData(); // reference outer this }.bind(this)); // bind to outer this
함수를 내부에서 참조하거나 bind를 사용하기 위해 임시로 this를 사용해야만 합니다. 하지만 ES6에서 화살표 함수를 사용할 수 있습니다.
ES6
1 2 3 4 5
// this will reference the outer one $('.btn').click((event) =>this.sendData()); // implicit returns const ids = [291, 288, 984]; const messages = ids.map(value =>`ID is ${value}`);
3.9 For…of
for나 forEach 대신 for...of를 사용합니다.
ES5
1 2 3 4 5 6 7 8 9 10
// for var array = ['a', 'b', 'c', 'd']; for (var i = 0; i < array.length; i++) { var element = array[i]; console.log(element); } // forEach array.forEach(function (element) { console.log(element); });
ES6 for...of 또한 반복 작업을 수행할 수 있습니다.
ES6
1 2 3 4 5
// for ...of const array = ['a', 'b', 'c', 'd']; for (const element of array) { console.log(element); }
3.10 Default parameters
변수가 정의되었는지 확인하는 것 대신 default parameters로 값을 지정할 수 있습니다. 전에 이렇게 한 적이 있나요?
ES5
1 2 3 4 5 6 7 8 9 10
functionpoint(x, y, isFlag){ x = x || 0; y = y || -1; isFlag = isFlag || true; console.log(x,y, isFlag); } point(0, 0) // 0 -1 true 😱 point(0, 0, false) // 0 -1 true 😱😱 point(1) // 1 -1 true point() // 0 -1 true
아마도 변수가 값을 가지고 있는지 확인하거나 기본 값을 할당하는 일반적인 패턴입니다. 그러나 몇 가지 문제가 있는 것을 확인하세요:
여덟째 줄, 0, 0을 넘겨주고 0, -1을 받게됩니다.
아홉째 줄, false를 넘겨주고 true를 받게됩니다.
기본 매개변수로 부울 값을 갖거나 값을 0으로 설정한다면 작동하지 않습니다. 왜 그런지 아시나요? ES6 예를 들어 설명해 드리겠습니다. ;)
개발 중 에러가 발생하면 로그에 에러메세지가 뜨지만 슬랙에서는 아무런 반응이 없어서 이것이 오래 걸리는 작업이라 그런건지 에러가 난건지 알 수 없었다. 그래서 에러가 발생하면 에러 메세지를 슬랙으로 전송해 개발 효율을 높일수 있었다.
Django
봇 기능중에 온습도를 측정하는 기능이 있었는데 센서에서 값을 실시간으로 불러오면 시간이 조금 걸렸다. 그래서 어딘가에 주기적으로 기록하고 그 값을 가져오야 겠다 싶기도 했고, 옵습도 값 변화를 한눈에 보면 좋겠다싶어 웹 서버 하나를 만들어 그래프를 만들었다. 그래서 웹서버로 Django를 사용했다.
Django-crontab
온습도 값을 센서에서 읽어와 데이터베이스에 저장하는 스크립트를 crontab으로 하려했는데 찾아보니 Django-crontab이란것을 발견했다. 이것의 장점은 Django의 ORM기능을 그대로 사용할 수 있다는 것이다. 덕분에 아래와 같이 코드량이 매우 감소해 개발시간을 줄일 수 있었다. Django-crontab 역시 Github에 문서화가 잘 되어있어 사용법을 참고하면 된다.
1 2 3 4 5 6 7 8 9 10
import Adafruit_DHT from common import models from datetime import datetime