2018년 9월 18일 화요일

Node Sep 19 공부중 노트

NodeJS를 사용한지 일년이 슬슬 되어가는 요즈음, 소켓 통신과, 두 서버와 클라이언트간의 데이터를 주고받는 부분에서 수많은 애로사항과 싸우고 있는 도중에, 다시금 기초를 볼 필요를 느끼고 있습니다..
급하게 프로젝트를 동작시키기 위해 필요한것만 읽고 넘어갔던 수많은 기초들을 다시 들여다 볼 시간 말이지요!
초반 한달간 시작점이 되었던 책을 다시금 집어들었고, 이번에는 리스너쪽을 읽어보았습니다.

이벤트 리스너는 모든 언어에서 모든 장비에서 쓰일 정도로 중요한 역할을 갖고 있지요, 하지만 운 좋게 지금까지는 이벤트 리스너의 필요성이 크지 않았습니다. 하지만 이제는 아닙니다.

Html단에서 이벤트 리스너는 보통 아래와 같은 객체에 선언합니다.
window.addEventListener(‘eventName’,()=>{})
window 객체에 이벤트를 연결 할 수 있었습니다.

Node에서는
process 객체에서 이벤트를 연결합니다.
process.on(‘exit’,()=>{});
//해당 노드가 종료될때 발생하는 이벤트입니다.

위 처럼 정의가 가능합니다. process 의 다큐먼트를 찾아보면 이미 정의되어잇는 몇가지 이벤트가 보입니다.
이떄 주의할 점은, Uncaught Exception을 사용하기 위해 try catch를 버리는 행위는 삼가하라고 합니다. why?

setMaxeventListener() 는 방금같이 선언하고 사용하는 리스너의 허용 갯수를 이야기 합니다.
노드는 이벤트가 발생될때 event Emitter 에 push되는것 같습니다.

진행했떤 프로젝트인 jellojay 에서 이러한 에러문을 자주 접하게 되었었고, 구글링을 하면서도 깔끔히 이해가 가지 않았었는데, 오늘 정리가 되네요.

이 eventEmitter가 허용하는 갯수 이상의 이벤트가 쌓이면 에러를 띄우는것 이었습니다.
이는 서버를 개발하다보면 어쩔수 없는 부분인거 같습니다.
특정 기능을 위해 이벤트가 발생될 수 밖에 없을것이며, 이를 근본적으로 해결하려 한다면, 하나의 이벤트에서 모든 일을 처리해야 할 터인데, 이렇게 되면 객체지향이 의미가 없어질태니 말이죠.

하지만 싱글 스레드로만 작동하는 Node에서는 이벤트의 갯수를 아주 카운트 할 필요가 없는것은 아닙니다. setMaxEventListener(0)를 이용해 이벤트 에미터의 제한을 풀 수 는 있겠지만, 결코 이것이 해결법이 아니란것은 설명이 없어도 가능하겠죠. 만일 본인이 짠 코드가 잘못작동하여 과도한 이벤트를 호출하는데, 이러한 문제를 인지하지 못한상태로 라이브 서버를 올렸고, 초반엔 문제가 없더라고 - 후반에 메모리 문제 등에 맡닿드릴 여지는 얼마든지 있기 때문입니다.

그러므로 저는 우선 이벤트 에미터의 제한을 조정하지 않는 상태로 두어, 개발을 완료하여 서비스 테스트를 거치며, 어느 기능에서 많은 이벤트 호출을 요하는지 판단하여, 문제의 여지를 확인 할 때까지 두는쪽으로 택했습니다. ( 마침내 에러 이슈 하나를 close!)

9월 18일 노트

현제 webServer 측에서는 userReqManager.js 에서 정의된 데이터 타입이 존재한다.
userReqManager 의 주요 데이터 보관 타입은 Map 이며, reqMap 이라는 변수에
K : SID V : Res 가 담기게 된다.
인증 요청을 한 유저는 이 변수에 store 되며, 걀거ㅘㅣ를 받게되면(설령 그것이 성공하든 실패하든) 해당 map의 값을 반환한다. wioth statuscode.
현제까지 몇가지 에러르 수정했고, 대부분의 메이저 문제는 해결이 되었으나, 인증이 성공했을 때의 경우ㅜ의 처리를 미뤄왔었고, 이제 후폭풍을 맞고 있다., 겨로가적으로 해당 유저의 인증 정보를 받기 위해서는 req.logIn 함수를 실행해야 하는데, 이 req 는 현제는 버려지고 있기 때문에, 해ㅕㄹ방법이 없다.

그러므로 내일부터 해야할 일은 ㅎ해당변수(그러니깐 reqMap)의 데이터 V 의 값을 res 와 req 모두 담을 수 있게 설계를 변경 해야할 것 이다. 이왕 모두 바꿔야하는데, 최근에 배운 DICITIONARY 타입으로 바꾸면 조금더 공부가 되겠지?

잠와죽겠다아

2018년 9월 15일 토요일

카카오톡 블라인드 테스트..

를 쳐봤는데, 당연하게도 알고리즘에 a도 안파던 놈답게 2번까지가 한계였습니다..

const testInput =
["Enter uid1234 Muzi", "Enter uid4567 Prodo", "Leave uid1234", "Enter uid1234 Prodo", "Change uid4567 Ryan"]
console.log(solution(testInput));



function solution(record) {
if(typeof(record)!="object" || !record.length || record.length > 100000){
throw new Error('[ERROR] givin parameter record type is not array!');
}
const tempLogArr = stringDivider(record);
const userIdMap = storeUniqueUserInfo(tempLogArr);
let result = [];
convertLog(userIdMap,tempLogArr,(err,res)=>{
if(err){
throw new Error(err);
}else{
result = res;
}
});
return result;
}

function stringDivider(raw){ //주어진 raw data 를 작업이 편하게 나눕니다.
let arrays = new Array();
try{
for(var i in raw){
arrays.push(raw[i].split(' ',3));
}
return arrays;
}catch(e){
throw new Error(`[ERROR][stringDivider func]${e}`);
}
}

function storeUniqueUserInfo(raw){ //현제 주어진 raw data 에 고유한 userId를 map에 K:userId , V:nick 으로 끝단부터 읽습니다.
//거꾸로 읽으며, 중복은 무시하기 떄문에, Chnage 를 해도 문제가 없습니다. 현제 맵에는 최신 nick를 저장하기 때문 입니다.
var uniqueUsers = new Map();
for(var i = raw.length-1 ; i >= 0 ; i-- ){
if(!uniqueUsers.get(raw[i][1])){
uniqueUsers.set(raw[i][1],raw[i][2]);
}
}
return uniqueUsers;
}

function convertLog (userIdMap,tempLogArr,cb){ //최종적으로 주어진 데이터들을 출력물에 맞게 만드는 함수입니다.
//input parameter's data type are like
//tempLogArr = [ ['status','userId','userNick'],['status','userId','userNick'] ] ;
//userIdMap = Map K : userId, V : userNick

var totalLog = new Array();
try{
for(var i in tempLogArr){
const curStatus = tempLogArr[i][0];
if(curStatus !=='Change'){
const curLog = `${userIdMap.get(tempLogArr[i][1])}님이 ${curStatus ==='Enter' ? '들어왔습니다.' : '나갔습니다.'}`;
totalLog.push(curLog);
}
}
cb(null,totalLog);
}catch(e){
cb(new Error(`[ERROR][convertLog func]${e}`));
}
}

testInput의 로그창이 들어오면, 클라이언트가 읽어보기 쉽도록 문자열을 변형하여 출력하는 문제였습니다..
 무식하게 for문만 써서 한시간이나 걸렸네요 -_-;;

var N = 4
var stages = [4, 4, 4, 4, 4];
var expect = [4, 1, 2, 3]
console.log(solution(N,stages));

function solution(N, stages) {
if(typeof(N)!='number' || N < 1 || N >500){
throw new Error(`[ERROR]GIVIN PARAMETER N IS OUT OF RANGE`);
}
if(typeof(stages)!=='object' || stages.length < 1 || stages.length > 200000 ){
throw new Error(`[ERROR]GIVIN PARAMETER stages IS OUT OF RANGE`);
}
const sortedStages = orderUserStages(stages);
let stageFailArr = new Array();
for(var curStage = 1 ; curStage < N+1 ; curStage++){
const curStageUsersLen = findusersArr(sortedStages,curStage);
stageFailArr.push([curStage,calculate(curStageUsersLen)]);
}
var answerMap = [];
answerMap = divideNConqure(stageFailArr);
return extrectStageNumber(answerMap);
}

function orderUserStages(rawstages){
var sortedArr;
sortedArr = rawstages.sort(function(a,b){
return b - a;
});
return sortedArr;
}//userStage 를 정렬합니다. 오름차순 입니다.

function calculate(sortedStagesLen){
try{
return sortedStagesLen[1] / sortedStagesLen[0];
}catch(e){
return 0;
}
}//계산부 입니다.

function findusersArr(sortedStages,curStage){
for(var i in sortedStages){
if(sortedStages[sortedStages.length-1] === curStage){
return countUserArr(sortedStages,curStage);
}else if(sortedStages[i] < curStage){
sortedStages=sortedStages.slice(0,i);
return countUserArr(sortedStages,curStage);
}
}
}//정렬된 userStage 배열을 전체적으로 스켄하여, 선택한 스테이지의 유저들을 모두 불러옵니다.

function countUserArr(sortedStages,curStage){
var nCount = 0 ;
for(var i in sortedStages){
if(sortedStages[i] ===curStage)
nCount += 1;
}
return [sortedStages.length,nCount];
}// 정렬된 userstage 를 [깬사람, 풀고있는사람] 으로 리턴합니다.

function divideNConqure(arr){
if(arr.length > 3){
const half = Math.floor(arr.length / 2);
const left = divideNConqure(arr.slice(0,half));
const right = divideNConqure(arr.slice(half,arr.length));
var temparr = new Array();
var leftPiv = 0, rightPiv = 0;
while(temparr.length != arr.length){
try{
if(left[leftPiv][1] >= right[rightPiv][1]){
temparr.push(left[leftPiv]);
leftPiv += 1;
}else if(left[leftPiv][1] < right[rightPiv][1]){
temparr.push(right[rightPiv]);
rightPiv += 1;
}
}catch(e){
if(left[leftPiv] ==undefined){
temparr.push(right[rightPiv]);
rightPiv += 1;
}else{
temparr.push(left[leftPiv]);
leftPiv += 1;
}
}
}
return temparr;
}else{
for(var i = 0 ; i < arr.length ; i ++){
for(var j = i+1 ; j < arr.length ; j++){
if(arr[i][1] < arr[j][1]){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
}// array[ array[ stageNum, stageUsers ],] 를 파라메터로 받습니다. 분할정복법으로 퀵소트 합니다. 오름차순 정렬입니다.

function extrectStageNumber(arr){
var result = [];
for(var i in arr){
result.push(arr[i][0]);
}
return result;
} //최종적으로 stage 번호를 출력합니다.

1부터 N번째 스테이지까지의 클리어 실패율을 도출 해 내는 문제였습니다.
stage 배열은, 각 유저별로 몇번째 스테이지에 있는지 정보입니다.
이 두 값을 통해, 실패율이 가장 높은 스테이지부터 차례로 배열에 넣어 출력하는 문제입니다.
고수님들 답지를 보니 20라인만에 다 짜버리는데 100라인짜리 내 코드를 보니 자괴감 들고 괴로워..

(숙련된 개발자의 코드. 효율성 테스트도 다 통과했다고 합니다)

이 코드를 분석해서 내껄로 만들어 응용하는 단계정도는 되야, 어느정도 js를 하는 개발자가 되겠거니 하며 기록을 남겨둡니다..

2018년 9월 12일 수요일

c++ namespace


처음 c++를 배울때에 우리는 보통 <iostream> 을 임포트 하여 시작했습니다.
iostream 에서는 printf 가 없기 때문에, 우리는 std::cout 을 사용하는데, 왜 이래야 하는가 에 대해 정확히 살펴 본 적은 없습니다. 그저 두루뭉실하게
java 따위에서 타 클래스를 명시하여 함수를 이용하는것이다 정도 였는데요,

마침 수업중에 namespace 에 대해서 정리해 오라는 과제를 던지는것을 봤습니다.
나도 궁금한 사항이니 이에 대해 알아보고, 정리해 보았습니다.

개발환경에 학습 이상으로 넘어가게 되면, 각 개발자마다 맡아 작업하는 cpp 파일들이 따로 있게 될 것 입니다.

따로 작업한 cpp 파일들을 합쳐, 작동시키는데에는 문제가 없을것 입니다.

하지만 변수 혹은 함수의 이름이 동일하게 되면 어떨까요?

class a
 int a = 0 ;

class b
 int a = 0;


메인 :  "a ?"

이제 골치가 아파지기 시작합니다. 하지만 놀랍게도 실무에서는 변수나 함수 이름을 고의로 통일시키는 경우도 있습니다. 전역적으로 두 소스파일에서 같은 변/함수를 사용할때 같이 말이죠.

이런 경우에는 선언할 변수를 가진 소스파일은 그대로 두나, 타 소스파일의 변수를 같이 사용하고지 하는 소스의 경우는 데이터 타입 앞에 extern 을 붙히게 됩니다.
calss a
 int a = 0
class b
 extern int a

메인 : "a 는 class a 에서 만든 변수이며, class b는 class a의 변수를 참조한다!"

이렇게 사용이 가능하다는 말이죠.
이떄 extern을 명시한 변수는 초기화를 선언과 동시에 해서는 안됩니다. 이럴경우 extern이 아닌 class b 에서 생성된 변수 a로 바뀌어 버립니다.

그럼 의도적으로 동일한 이름의 변/함수가 아닌 경우에는 어떻게 할까요?
가장 쉬운 방법은 이름을 바꾸는것 입니다. 하지만 이건 너무 무식하지요, 조금더 쓸모있게 해 봅니다.
class a
 static int a = 0
class b
 static int a = 10
메인 : "class a 에서만 존재한 변수 a 와, class b 에서만 존재하는 변수 a 두개가 있다!"

근본적인 문제는 해결했으나, 만일 class a 나 class b 가 a 변수를 서로 다른 class에게 공유를 할 계획이라거나, 더 많은 소스파일들로 인해, extern도 사용하기가 힘들어 진다면?
static은 해당 class 내에서만 작동하기 때문에, 지금 코드로는 문제가 있습니다.

이를해결하기 위해 나온것이...
namespace 가 등장했습니다!

namespace 는 변수나 함수, 심지어 class에 사용이 가능합니다.
namespace first {
 int a = 0;
}
namespace second {
 int a = 1;
}
namespace third {
 int a = 2;
}

main(){
cout << first::a << second::a << third::a << endl;
//expect result is 0 1 2
}

c+ 을 해보신 분이라면 어느정도 파악은 하고 계시겠지만, namespace를 명시하지 않아도, 묵시적으로 namespace가 선언되어 있습니다.
그럼 어디로? 전역 네임스페이스 공간으로 되어있습니다. 우리가 보통 명시하지 않은 변수 함수 선언은 전역 네임스페이스 공간으로 선언이 되기 때문에,

좋아요, 그럼 변수와 함수를 여러개 사용하는 예제를 보시죠

namesapce myNamespace{
 void func(){}
 int a = 0 ;
}
using myNameapce ::func
main(){
 cout << myNamespace::a << endl
 // myNamespace는 using이 아니기 때문에 명시가 필요
 func()
// using으로 myNamespace 내부의 func를 스코프 했기 때문에, myNamespace :: func()
}

이즘되면 클래스랑 동일하다는 생각을 가질수 도 있는데, 그렇지는 않습니다.
클래스와 네임스페이스는 다르다는것을 명심해야 합니다.


( 그낭 글씨치기엔 귀찮아서 코드 사진으로 대체)
위코드는 myNamespace 를 변수로 선언해서 사용해 부며,
myNamespace 안의 class를 사용하는 예제입니다.
위에서 언급됬다싶이, namespace 와 class는 비슷해 보이지만 다르기 떄문에, 혼동하지 않도록 해야할 것 입니다!

2018년 9월 4일 화요일

기록보관용 jellojay에서 ajax 기본 틀

최근 작업중 ajax작업을 다시 만지게 되었는데, 예전 ajax작업을 비교하니 너무나 빈약하고 outdate 기록도 많은상태!

하나의 틀을 정해서 모든 js 페이지를 통일할 계획..

하지만 지금은 바쁘니깐..계획만 해놓고 기본틀을 여기다가 적어놓습니다.

$.ajax({
type : "GET",
url : "/?",
data : {
"foo" : "bar"
},
dataType : "JSON",
success : (data)=>{

},
statusCode : {
400 : {},
401 : {},
500 : {},

},
error : ()=>{

}
});

2018년 9월 3일 월요일

Difference between arrow func and func declare, plus three-dot-operator


지금까지 js를 공부하면서 arrow function에 대해 파편적으로 배웠습니다.
애초에 js를 따로 공부한적도 없기 때문에, 아는것만 아는 수준입니다.
 
Arrow function은 비교적 최근(ES2015)에 나온 방법인데, 문법은 이러합니다. ()=>{}

그런데 최근에 들어, arrow function과 일반 function 선언 두가지 방식에 대한 차이점이 궁금해 지기 시작합니다.

이리하여 arrow function 과 function declare에 대해 알아보았습니다

Function expression(함수 표현식)
우선 들어가기에 앞서, function expression 에 대해 알아보도록 합니다.

const func = function(foo){ return foo; }

이것이 바로 function expression(함수 표현식) 입니다.

Function declaration
일반적인 function선언은 아래 코드와 같습니다.

function func(foo){ return foo; }

이 둘의 차이는 끌어올려지는가(is hoisting) 입니다.

Hoisting?
hoisting은, 해당 function을 선언을 둘러싼 함수의 최상부, 혹은 전역범위(global scope)로 끌어올려진다 입니다.

console.log(func(1));  // return 1
function func(foo){return foo;} // this function is hoisted.

그리고, 위 코드에서와 같이, 비 순차적으로(선언을 사용한 뒤에 되어있는)코드가 있을 때, 해당 함수를 작동하면, hoisting이 됩니다. 이러한 Hoisting이 된 코드의 경우, 선언된 위치에 구애받지 않고, 작동을 보장합니다.

Console.log(func(1));  // return undefind.
Const func = (foo)=>{return foo;} // this function is not hoisted.
하지만 function declare 로 선언이 된 함수는, hoisting이 되지 않습니다.

이제 Arrow function을 보도록 합니다. Arrow functionthis,args,super 혹은 new.targer을 바인딩하지 않는다 라고 합니다.
그 말은 arrowanonymouse function 입니다.

함수안에 또 다른 함수(recursion)를 쓰기 위해서는 이전버전(ES2015)에는 arguments.callee 를 이용했었다고 하나, 여러가지 문제로 인해 named function을 사용한다 합니다.
(그래도 지난 게시물을 해결할 열쇠로 보이진 않네요)


Arrow function
arrow function의 사용은 this 의 범위가 바인딩 되지 않는 것이 큰 차이점으로 보입니다.
const test = {
 a : 10,
 b : ()=>{ console.log(this.A);}
 c : function(){ console.log(this.A);}
}
test.b()// return undefined
test.c()// return 10

의 간단한 예제로 바인딩의 범위를 볼 수 있습니다.

Declaring Arrow function
Arrow function은 두가지의 선언방법이 있으며,

1. concise 바디 (중괄호가 없는)
const foo = bar => bar * 2; //return은 생략 가능하며, bar*2가 들어있다.

2. Block 바디(중괄호가 있는)
const foo = (bar) => { return bar * 2 }//return을 생략할 수 없다

가 있습니다.

이 이외에도 상당히 다양한 차이가 존재하나, 가장 눈여겨 볼 것은 bind의 차이로 불 수 있어 보입니다. 무엇이 더 좋은가를 굳이 따지기 보다, 언제 쓰는게 올바른가를 알아야 할거 같습니다.
(물론 짧은게 최고 라고 합니다만)




ps
추가적으로 알아보다가 얻었던 궁금증인  :
Three-dot-operator (…) 
arguments에서 길이가 가변적인 array로 볼수 있습니다.

function func(args1, args2, ...args3) { console.log(args3)} ;
func(1, 2, 3, 4, 5, 6, 7); // returning (array)[3,4,5,6,7]

를 리턴한다 합니다.