user 로그인 상태를 관리하기 위해 안전한 방법으로 Server에서 Client에게 쿠키로 sessionID를 발급해주고 이 쿠키를 통해 Server에 접속하면 sessionID값을 활용해 어떤 Client인지 식별하고 관련 정보를 제공하는 방법을 사용한다. 관련 기능을 쉽게 사용할 수 있게 해주는 express-session 모듈을 사용해 보자.
express-session을 미들웨어로 등록한다. sessionID를 발급할 때 username이나 password같은 정보만으로 만들어 발급한다면 외부에서 이 정보를 알아낼 가능성이 있다. 이는 심각한 보안사고이다. 그래서 추가적으로 secret키를 sessionID 생성에 참고해서 이 sessionID를 역으로 해석해서 원래 정보를 알아내기 힘들도록 한다.
이렇게 등록을 하면 Server에 접속했을 때
와 같은 sessionid가 발급된다.
2. Login
로그인을 구현하기 위해서 Client에서 ID와 Password가 Server에 오면 이를 user database에 있는 user정보와 비교해서 제대로 된 유저인지 확인한다. 만약 제대로 된 user라면 sessionId를 발급해주면 된다.
데이터베이스를 외부에(AWS나 NCP, 혹은 다른 PC나 가상환경 등등) 두고 그 곳에 접속하는 환경을 만들 때 고려해야 할 것들을 정리했다.
고려해야할 4가지 그림
1. 접속 가능 포트 설정
먼저 외부 서비스에 접속하기 위해서 Mysql로 접속하기 위한 포트를 포트포워딩 해주어야 한다. NCP에서는 ACG 규칙에 추가해주는 것으로 설정이 가능하다.
2. Mysql에 접속 가능한 IP주소
Mysql에 접속가능한 IP주소는 default로 localhost로 지정되어 있다. 이를 수정하기위해 다음과 같은 경로에 있는 파일을 열어서 bind-address를 localhost에서 0.0.0.0(전역) 으로 수정해주거나 접근가능하게 하고싶은 IP주소를 입력해서 외부에서 접속 가능하게 할 수 있다.
/etc/mysql/mysql.conf.d/mysqld.cnf
위 사진 맨 아래의 bind-address의 값이 초기에 127.0.0.1로 되어있다. 이 것을 원하는대로 바꾸면 된다.
3. user에 접속가능한 IP주소 설정
위 그림을 예로 들면 userA에 접근가능한 IP주소는 127.0.0.1인데 2.2.2.2인 IP에서 접근을 시도해서 user에 로그인 할 수 없다. 로그인 하고싶은 IP에 접속 가능하도록 추가해준다.
CREATEUSER ‘userA’@’%’ IDENTIFIED BY ‘user_password’;
4. 접근 가능한 database인지 확인
user가 접근하고자 하는 database의 접근권한이 없으면 접근할 수 없다. 아래와 같은 명령어를 통해 접근 권한을 부여하자.
grantall privileges on'DB 이름'.'Table 이름'to ‘root’@‘%’ identified by ‘root의 패스워드’
node.js의 웹 프레임워크인 Express를 Express답게 쓰기 위해 조사했다. Express Reference에서는 Express generator를 사용해서 빠르게 앱 스켈레톤을 구성할 수 있게 해준다. 위의 사진은 Express generator를 통해 생성한 스켈레톤 앱의 구조이다.
npm install -g express-generator
명령어를 통해 설치한 후
express —view=pug myapp (pug는 템플릿 엔진. ejs를 사용할 수도 있다)
를 통해 스켈레톤 앱을 생성한다.
빌드
빌드는 npm start 명령어를 통해 빌드한다. package.json파일의 "script"키 값으로 "start" 명령시 빌드 명령어가 앱 생성과정에서 함께 들어가있다.
"scripts": {
"start": "node ./bin/www"
},
bin폴더
// bin/wwwconst app = require('../app');
const http = require('http');
const port = 3000;
app.set('port', port);
const server = http.createServer(app);
server.listen(port);
var o = {
m: function() {
var self = this;
console.log(this === o); // true
f();
functionf() {
console.log(this === o); // falseconsole.log(self === o);
}
}
}
o["m"]();
Output :
생성자 호출
new keyword -> 생성자 호출
인자가 없으면 두 문장은 완전히 같은 것.
var o = newObject();
var o = newObject;
생성자 함수는 보통 return 사용하지 않음. 함수의 마지막에서 객체를 반환, 혹은 return만 명시 되어있으면 거기서 객체 반환 기본 자료형 값(primitive value)을 반환하면 무시되고 새로생성된 객체가 호출 표현식 값.
간접 호출
call() -> 자신에게 주어진 전달인자를 호출할 함수의 전달인자로 사용 apply() -> 값 배열을 전달인자로 사용.
두 메서드 모두 호출 때 this 값을 명시적으로 지정 가능. -> 어떤 함수든지 특정 객체의 메서드로 호출할 수 있다는 의미
함수 전달인자와 매개변수
Javascript는 함수 매개변수타입, 전달인자 개수, 전달하는 인자의 타입 모두 검사하지 않음. 이를 프로그래머가 관리하는 방법들.
생략 가능한 매개변수
본래 정의된 것 보다 적으면 -> 나머지는 undefined 이렇게 두지 말고 생략된 매개변수에는 기본값을 주는 코드를 쓰자.
functiongetPropertyNames(o, /*optional*/ a) {
if(a == undefined) a = [];
//or a = a || []// || -> 첫번째가 true나 true로 반환되는 값이면 그 값 return, 그렇지 않으면 뒤에 것 returnfor(var property in o) {
a.push(property);
}
return a;
}
엄격모드에서는 사용 못함. callee -> 프로퍼티가 현재 실행되고 있는 함수를 참조, 이름없는 함수를 재귀적으로 호출하는데 유용 caller -> 비표준. 이 함수를 호출한 함수를 참조. 호출 스택에 접근할 수 있도록 해줌.
객체의 프로퍼티를 전달인자로 사용하기
easycopy({from : a, to : b, length : 4});
값으로서의 함수
functionsquare(x) {return x * x};
var s = square;
square(4);
s(4);
var o = {square: function(x) {return x * x}};
var y = o.square(16);
var a = [function(x) {return x*x; }, 20];
a[0](a[1]);
functionfactorial(n) {
if(isFinite(n) && n>0 && n == Math.round(n)) {
if(!(n in factorial)) {
factorial[n] = n * factorial(n - 1);
}
return factorial[n];
}
elsereturnNaN;
}
factorial[1] = 1;
네임스페이스로서의 함수
javascript 는 단위 block 내에 유효한 변수를 정의하는 방법을 제공하지 않음. 간단한 임시 네임스페이스 처럼 작동하는 함수를 정의하는 기법 사용.
functionmymodule() {
// 모듈의 지역변수로 사용해서 전역을 어지럽히지 않음
}
mymodule(); // 함수 실행 필수// 하나의 프로퍼티 선언도 과하다면
(function() {
// 모듈 코드가 위치
}()) // 바로 호출하고 끝냄. function앞 뒤 괄호는 반드시 필요 -> 표현식임을 알림
클로저
함수 객체, 함수의 변수가 해석되는 유효범위(변수 바인딩의 집합) 을 아울러 클로저라고 함 모든 자바스크립트 함수는 클로저. -> 함수는 객체이고 함수 자신과 관련된 유효범위 체인을 가지고 있기 때문.
클로저는 자신을 정의한 바깥쪽 함수에 바인딩된 지역변수를 포착한다.
var scope = "global";
functioncheckscope() {
var scope = "local";
functionf() {return scope;};
return f;
}
checkscope()() // local 출력
EX) Counter
내부변수는 private. 오직 count와 reset을 통해서만 제어가능
functioncounter() {
var n = 0;
return {
count: function() {return n++;},
reset: function() { n = 0; }
}
}
var c = counter(), d = counter();
console.log("c : ", c.count());
console.log("d : ",d.count());
c.reset()
console.log("c : ",c.count());
console.log("d : ",d.count());
Output:
Function 생성자
동적으로 자바스크립트 함수를 생성하고 실행 시간에 컴파일 되는 것을 가능하게 한다.
이걸로 생성하는 함수는 언제나 최상위 레벨 함수로, lexical scoping을 사용하지 않는다.
var f = newfunction("x", "y", "return x*y);
// 마지막 인자는 함수의 몸체 텍스트
var empty = [];
var primes = [2,3,5,7,11];
var misc = [ 1.1, true, "a" ];
// 임의의 표현식도 사용 가능 varbase = 1024;
var table = [base, base+1, base+2, base+3];
var count = [1,,3]; // 가운데 값은 undefinedvar undefs = [,,]; // 원소는 2개 (마지막원소에 , 가능)
Array 생성자
var a = newArray();
var aa = newArray(10); // lengthvar aaa = newArray(5,4,3,2,1, "testing, testing"); // 인자값이 배열의 원소console.log(aaa)
var o = newArray(7,5,3,2);
var keys = Object.keys(o);
var values = [];
for(var i = 0; i < keys.length; i++) {
var key = keys[i];
values[i] = o[key];
}
console.log(o);
console.log(keys);
console.log(values);
for(var i = 0, len = keys.length; i < len; ++i) {
// 성능향상 위해 length를 한번만 불러오기
}
Output :
루프 돌릴 때 고려해야 할 점
원소가 null / undefined 일 때 어떻게 처리할 지
상속받은 Property 등의 고유 Property 가 아닌 값들을 어떻게 처리할 지
for in loop에서 원소 반환 순서
정해져있지 않음.
순서가 중요한 알고리즘에서는 for문을 통해 명시적으로 사용할 것을 권장
다차원 배열
var table = newArray(10);
for(var i = 0; i < table.length; ++i) {
table[i] = newArray(10);
}
for(var i = 0; i < table.length; ++i) {
for(var j = 0; j < table[i].length; ++j) {
table[i][j] = i*j;
}
}
console.log(table[5][7]);
Output :
배열 메서드
join()
* 배열의 모든 원소를 문자열로 변환, 변환한 문자를 이어붙인 결과를 return
* seperator를 지정하지않으면 , 가 기본 separator
* String.split() -> join의 반대. 문자열을 배열로
var a = [1, 2, 3];
console.log(a.join());
console.log(a.join(' '));
console.log(a.join(''));
Output :
reverse()
배열의 원소 순서를 반대로 뒤집어 반환.
배열안에서 직접 수행되어 배열 정렬순서 변환됨.
var b = [1,2,3];
b.reverse();
console.log(b);
Output :
sort()
정렬
undefined는 배열의 끝부분으로
정렬 조건도 바꿀 수 있음
var c = [1,2,3,4];
c.sort();
console.log(c);
c.sort(function(a,b) {
return b - a;
})
console.log(c);
Output :
concat()
concat의 전달인자를 추가한 배열 반환
var d = [1,2,3];
console.log(d.concat(4,5));
console.log(d.concat([4,5]));
console.log(d.concat([4,5], [6,7]));
console.log(d.concat([4, [5,6]]));
Output :
slice()
subarray 반환
-1은 마지막 원소
음수는 뒤에서부터 세는 것
var e = [1,2,3,4,5];
console.log(e.slice(0,3));
console.log(e.slice(3));
console.log(e.slice(1,-1));
console.log(e.slice(-3,-2));
temp.newProperty : 식별자가 와야 함 -> 코딩할 때 fix됨. temp.["new Property"] : 표현식을 인자로 받음 -> 프로그램 실행중 입력받은 것 사용 가능
functionaddName(nameList, name, studentNumber) {
nameList["name"] = studentNumber
}
// 이런 코드가 동작 한다는 뜻!
상속
객체에서 Property를 찾을 때, 고유 Property안에 찾고자 하는 Property가 없으면, 상속받은 Prototype 객체에서 해당 Property를 찾는다.
functioninherit(p) {
if (p == null) throwTypeError();
if (Object.create) // ECMAScript 5 이상에 정의되어 있음returnObject.create(p);
}
var o = {};
o.x = 1;
var p = inherit(o); // 객체 o와 Object.prototype를 상속받는 객체
p.y = 2;
var q = inherit(p); // 객체 p와 o, Object.prototype을 상속받는 객체.
q.z = 3;
var s = q.toString(); // q 는 Object.prototype을 상속받았기 때문에 toString 사용가능var result = q.x + q.y; // 값은 3 console.log(result);
Output :
상속받은 Prototype Property를 바꾸고 싶을 때
상속받은 Property 가 read only 라면 수정 불가. 그렇지 않다면 두 가지 경우의 수
고유 Property로 재정의 된다. // 기본
Prototype Property 의 값이 재설정된다. // setter method가 정의되어있을 경우
Property 접근 에러
존재하지 않는 Property에 접근하는 경우
에러 발생하지 않음. -> undefined return
존재하지 않는 객체의 Property에 접근하는 경우
에러 발생
var temp2 = o.a;
console.log(temp2) // undefinedvar length = o.a.length; // undefined에서 Property를 찾으려 해서 error
Output :
에러 방지
var len = temp2 && temp2.a && temp2.length
객체 o에 Property p를 설정할 수 없는 경우
p가 o의 고유 Property이고 readonly일 경우
읽기전용 Property p 에는 값 설정할 수 없다.
p가 상속된 Property 이고 readonly일 경우
동일한 이름의 고유 Property로 감출 수 없음.
p가 고유 Property도, 상속받은 Property도 아닌데 extensible 속성이 false일 경우.
프로퍼티 삭제하기
delete 연산자. Property의 값이 아니라 Property 자체를 지움
delete book.author;
delete book["main title"];
delete 연산자는
고유 프로퍼티만 지울 수 있다.
삭제에 성공, 프로퍼티가 존재하지 않아 영향을 미치지 못한 경우, return true를 한다.
o = {x:1}
delete o.x; // 삭제 성공 returntruedelete o.x; // 존재하지 않아 returntruedelete o.toString; // 고유프로퍼티가 아니라 아무일도 안일어나고 truedelete1; // 말도 안 되지만 returntrue
* configurable 속성이 false 인 프로퍼티는 지우지 않는다.
아래는 nonreconfigurable
deleteObject.prototype; // 지울 수 없음var x = 1;
deletethis.x; // 지울 수 없음functionf() {}
deletethis.f; // 지울 수 없음
var o = {x:1, y:2, z:3};
for(p in o) {
console.log(p, o[p]);
}
Output :
상속받은 Property 생략, method 생략
for(p in o) {
if(!o.hasOwnProperty(p))
continue;
}
for(p in o) {
if(typeof o[p] === "function")
continue;
}
프로퍼티 Getter / Setter
ECMAScript 5 에서는 Property의 값을 getter / setter method로 대체할 수 있음. 단순히 값을 값는 "Data Property" 와 다른 "Accessor Property" 라고 한다.
프로그램이 객체의 Accessor Property에 접근하면 getter method의 반환 값이 Property 접근 표현식의 값이 됨.
프로그램이 객체의 Accessor Property의 값을 바꾸려고 하면 getter method가 실행된다. = 오른쪽의 값이 method의 인자로 전달된다.
getter / setter 존재 유무로 읽기전용, 쓰기전용 결정. 특이한 것 : setter 만 있으면 쓰기전용. read 하려고 하면 있음에도 undefined return
Accessor Property의 정의
var oo = {
// Data Property
data_prop: value,
getaccessor_prop() { /* 함수 몸체 */},
setaccessor_prop(value) { /* 함수 몸체 */ }
}
Example : 2차원 좌표와 극좌표를 가지는 Property
var p = {
x: 1.0,
y: 1.0,
get r() {
return Math.sqrt(this.x*this.x + this.y*this.y);
},
set r(newValue) {
console.log("in setter : ", newValue);
var oldValue = Math.sqrt(this.x*this.x + this.y*this.y);
var ratio = newValue / oldValue;
this.x *= ratio;
this.y *= ratio;
console.log("now x : ", this.x);
console.log("now y : ", this.y);
},
get theta() {
return Math.atan2(this.y, this.x);
}
}
var q = inherit(p);
q.x = 1; q.y = 1;
console.log(q.r);
console.log(q.theta);
q.r = 777
console.log(q.r);
console.log(q.theta);
Output
Example : 일련번호 생성
var serialnum = {
$n: 0, // 프로퍼티 이름의 $는 private 프로퍼티라는 힌트get next() {returnthis.$n++;},
set next(n) {
if(n>=this.$n) this.$n++;
elsethrow"serial number can only be set to a larger value";
}
}
Property 속성
이름, 값, 그리고 속성
value
데이터 프로퍼티의 값도 속성으로 본다.
writeable
값 변경 가능 여부
enumerable
열거가능 여부
configurable
3가지 속성 변경 가능한지.
Property의 3가지 속성 값을 질의하고 설정할 수 있도록 하는 ECMAScript 5 API
라이브러리 만들 때 중요
프로토타입 객체에 method 추가 , 추가된 method를 내장 method처럼 열거할 수 없게 만들기 위해
변경, 삭제 불가한 프로퍼티를 정의, 객체를 고정(lock down) 시키기 위해
접근자 Property의 속성 표현 위해 Property descriptor라는 객체 사용.
데이터 Property의 Property descriptor
value, writeable, enumerable, configurable 등의 Property를 가짐
접근자 Property의 Property descriptor
get, set, enumerable, configurable
객체의 특정 Property에 대한 Property descriptor객체 -> Object.getOwnPropertyDescriptor() 를 통해 얻을 수 있음.
console.log(Object.getOwnPropertyDescriptor({x:1}, "x"));
// 없는 Property거나 상속받은 Property 는 undefinedconsole.log(Object.getOwnPropertyDescriptor({}, "x"));
console.log(Object.getOwnPropertyDescriptor({}, "toString"));
Output
getOwnPropertyDescriptor 는 고유 프로퍼티만 검사 가능. 상속된 프로퍼티를 검사하고 싶으면 프로토타입 chain을 명시적으로 순회해야함.