2018년 11월 7일 수요일

reactJs 에서 function body { } brace 주의..

리엑트 공부 하겠다고 퇴근하고 짬짬히 만지기 시작하는데, 튜토리얼 거의 첫장에서 진도를 못빼게 되는데.. 아무리 눈씻고 비교해도 오타도 모르겠고..

(문제의 코드중... common js와 다른점이 있다?!)

정말 짜증 지대로 빽빽 내면서 한시간동안 이것저것 알아보다 발견한 차이점..

commonJS에서의 arrow function declare
() => {}
reactJS에서의 arrow function declare
() => () 

하 내 한시간..

https://medium.com/@leannezhang/curly-braces-versus-parenthesis-in-reactjs-4d3ffd33128f




2018년 11월 4일 일요일

node.js 내장 apache 로 서버 퍼블릭으로 띄우기. on Windows

김고은 닮은 후배님께서 node.js 에 내장되어 있는 아파치에 대한 언급을 몇번 했었는데, 의아해 하면서 알아 본 적이 있는데, 놀랍게도 진짜로 지원한다고 합니다(!)
급하게 테스트 할때에 웹서버 올릴 필요가 없었던 거네..

그 결과 오늘 잠깐 만져봤고, 그 테스트 과정을 간단하게 공유 해 보겠습니다.


1. 서버 백단을 작성합니다.

(간단하게 express로 작동하는 http 서버를 만들었습니다.)


2.프로젝트 세팅을 하고, 모듈을 설치합니다.

 (app.js 를 만든폴더로 이동해 npm create 로 npm project를 생성했습니다.)

 (이후express 모듈을npm i express --save 로 설치했습니다)


3.포트를 세팅해야 합니다. 방화벽 세팅으로 갑니다.

 (윈10에서 가장 사랑하는 검색기능.. 넘모 편해요오옷)

 (고급 설정을 누릅니다)

( 인바운드 규칙 -> 새규칙 -> 포트 -> 다음 )

(app.js에서 작성할떄 사용된 포트번호, 굳이 따라 8080 할 필요가 없지만 무슨소린지 모르겠으면 그냥 8080쓰세요. 이후는 그냥 다음 다음 다음 누르면 끝이라 스샷음슴)


4. (옵션)상위 네트워크 설정 정책/포트포워드

이부분은 환경에 따라 다릅니다.
그냥 렌 케이블 뽑아다 쓰는 경우엔 필요가 없을거고, 사내/ 내부 인터넷/정책/서비스 에 따라 다양한 설정이 필요로 합니다.
내가 그걸 모두 알아낼 방법도 없고, 귀찮으니 내가 가진 공유기인 iptime 환경에서 세팅법만 알려dream니다. 

공유기 환경에서는 포트포워드를 설정해 주어야 하는데,

(포트포워드 세팅에 보면 가장 주목해야할 부분은 내부IP주소/프로토콜/외부포트/내부포트)

내부 IP라 하면 공유기 내부 네트워크상 자기 자신(서버 돌리는 컴퓨터)의 IP를,
프로토콜은 지금 이걸 읽는 사람의 시점이라면 TCP를,
내부 포트는 아까 app.js를 작성할떄 사용한 포트 번호를 지정해야 하고,
그리고 외부 포트도 나름대로 자유롭게 세팅이 가능하나, 80으로 해 봅니다. 

Note 
 why 80포트? - http 통신에 쓰이는 default 포트가 80이기 때문에, 무슨 소리냐 하면
 www.google.com 의 도메인을 벗겨내면 123.123.123.123:80 인 것입니다.(물론 아이피는 예제니깐 저거 쳐서 왜 안들어가지냐 하면..ㅎ)
 난 80 싫다 하면 바꾸면 됩니다. 물론 뒷처리도 해주어야겠지만.

Note
 AWS? - AWS같은 웹 서버를 통해 구축을 시도하는 분도 계실것 입니다. 열심히 아름답게 3번을 따라 보안정책을 허용하였으나, 4번에서처럼 포트포워딩을 어떻게 해야할지 모를 경우도 있을것 입니다. 당황하지 마시고 웹 서버 제공자의 문서를 찾아보세요. 이런 설정법은 존재합니다. AWS의 EC2 또한 EC2 대쉬보드에서 설정 할 수 있습니다.


5. 재시작

 윈도우 세팅 바꾸고 제시작 없이 하는법이 있는지 모르겠음. 모를떈 재시작이 답이다.


6. 서버 동작!

이걸 모르겠다 하면 나도 어디서부터 어떻게 설명해야 할지 모르겠습니다. 방금 만든 서버를 돌리라는 뜻입니다.


7. 외부 Ip로 접속시다!

를 하기전에, 내 외부 ip가 뭔지 모르는데? 할 수 도 있습니다.
그래서 아주 배리배리 쉬운 사이트를 소개합니다.
www.whatismyip.com 
(이렇게 들어가면 외부 ip부터 isp정보나 대략적인 위치도 찍힙니다. 우리는 public ip만 있음 okay!)


8. 접속!

방금 알아낸 외부 ip를 그냥 웹사이트에 입력만 하면 끗!!!
(Hello world! 나의 오랜 친구여...)



마치며...

이렇게 내장 아파치로 노드 서버를 퍼블릭 상태로 웹에 띄우는 법을 같이 보았습니다.

이부분까지 왔으나,안되는 케이스도 분명히 다양하게 있을것 입니다. 그런 경우는 아래와 같습니다.

  1. 정책상 외부에서 당신의 서버로 접근이 불가능하게 상위 네트워크 관리자가 제한을 걸었습니다. (주로 학교, 회사, 공공기관 등이 있습니다. 네트워크 관리자랑 상의를 할 수 밖에 없습니다)
  2. 본인 네트워크 세팅 문제입니다 ( 주로 3번과 4번에서 발생합니다) 
그래서 어캐하냐고요? 구글링 하세요.



2018년 11월 3일 토요일

MariaDB/MySQL insert / update / ON DUPLICATE KEY UPDATE

이미 존재하는 프로젝트를 분석하는 작업을 진행중인데, 아무래도 실무자들이 여럿 붙어 만든 프로젝트이며, 포기했던 스프링이다 보니 신기한 부분이 한둘이 아닙니다.

그중 오늘은 재미난 것을 발견했는데 바로 쿼리문 중에 등장한
ON DUPLICATE KEY UPDATE
입니다!

INSERT INTO MYTABLE (foo,bar,hello,world)VALUES (1,2,3,4) 까지는 아주 친숙한데, 이 뒤에 추가 쿼리가 있었습니다!

INSERT INTO MYTABLE (foo,bar,hello,world)VALUES (1,2,3,4) ON DUPLICATE KEY UPDATE UPDATE  foo = 1 , bar = 2 , hello = 3 , world = 4

베리마취하게 마취되어(?) 검색을 좀 때려봤고 매우 마음에 드는 쿼리문을 찾게 되었습니다.

현제 1.0 jellojay 에서는, 아직까지 등록되지 않은 table row 인 경우, insert를 하고, 이미 존재하는 데이터인 경우 update 문을 하게 설계가 되어있습니다. 즉 백단에서 해당 table row의 존재여부부터 확인한뒤, insert와 update에 해당하는 두개의 함수를 만들어 운용하고 있었습니다.

하지만 이런 수고스러움을해결하고자 하여 사용되는것이 바로 ON DUPLICATE KEY UPDATE
였습니다. 이정도 까지 읽었다면 다들 어느정도 어떤 기능인지 납득이 되기 시작할 것 입니다.

해당 TABLE의 KEY값이 이미 중복으로 존재한다는것이 확인이 되면,
INSERT에서 에러를 뿜고 끝내는게 아닌, 해당 KEY값의 ROW를 UPDATE 시켜버리는것 입니다.

이 얼마나 갓 쿼리문인가..난 이런것도 모르고..흐극..
이런 기능은 인기있는 대부분의 db에 존재하는것 으로 보이니, 현제 사용중인 DB가 다르다고 좌절하지 마시고 본인의 DB에서도 같은 기능을 하는 문법을 알아보심을 추천함미다.

2018년 11월 1일 목요일

Spring Exception starting filter [encodingFilter] 문제

아침에 컴터앞에 앉아서 프로젝트를 실행하는데, 어제랑 다르게 에러가 뿜뿜 뜬다.
톰켓 경로안에 무슨 파일을 읽지 못한다고 하는데, 제부팅해도 못읽는다, 클린해도 못읽는다.. 그냥 지워버렸다 (-_-)

그러자 톰켓이 작동은 하지만 이젠 이러한 에러를 토해내는데 :

심각: Exception starting filter [encodingFilter]
java.lang.NoClassDefFoundError: org/springframework/web/filter/CharacterEncodingFilter

와따 참말로...
블로그 가면 뭐 파일을 넣으라고 raw 파일을 던져주는 블로그가 보이는데, 톰켓 버전도 운영체제도 다 다른데 덩그러니 수상하기 짝이없는 파일 던져놓고 그걸로 잘도 바꾸겠다..

다른 해결법을 찾다 나온것이

문제의 project -> properies -> Deployment Assembly -> add -> Java Build Path Entries -> next -> apply

요롷코롬 하면 톰켓의 세팅이 restored되나보다.

으으 이클립스.. 스프링..

2018년 10월 9일 화요일

java Singloeton design pattern

디자인 패턴 강의를 다른 멘토분과 함께 나누어서 진행중이며, 기초적인 자바 디자인 패턴을 파트별로 나누어 수업중입니다.
이중에 제가 맡은 파트들을 하나씩 정리 해 볼 생각입니다.
이 글은 Head first Design patterns 를 상당히 참조하여 쓰인 글 입니다.

Singleton.
싱글턴 혹은 싱글톤 이라고 부릅니다.
이패턴은 단! 하나! 의 클래스 인스턴스만을 편하게 사용하고 관리하게 하기 위해 고안된 방법입니다.

비단 자바뿐만 아니라 모든 언어에서도 상당히 많이 쓰이는 페턴이며,  지금 이 글을 읽는 여러분들도 알게 모르게 사용해 보신 경험이 있을지도 모릅니다.

이제 한번 자세히 알아봅시다.





Jay : MyClass 를 사용하기 위해서는 어떻게 쓸까요?
Bob : new MyClass() 로 객체를 인스턴스로 생성하여 사용합니다.

Jay : 다른 객체에서 해당 클래스를 사용할 수 있는가요?
Bob : 위와 동일한 방법으로 가능합니다.

Jay : 만약 public이 아니라면?
Bob : protected 라고 할 경우, 같은 패키지 내에 있는 클래스들만이 호출이 가능하며,
private 경우는 본인만 가능합니다.

Jay : 그럼 이 코드는 어떨까요
class MyClass{
 private MyClass(){}
}

Bob : 위와 같은 문맥으로, 본인 클래스만 생성 가능한 클래스 입니다.

Jay : 이 코드에 대해서 설명 해 보자면요? 외부에서 해당 메소드를 어떻게 호출하나요?
class MyClass{
 public static void getInstance(){}
}

Bob : MyClass.getInstance() 왜나하면 static이기 때문에, MyClass 객체를 생성하지 않이도 이미 존재하는 메소드 입니다.

Jay : 이렇게 하면 어떨까요, 외부 클래스에서getInstance를 호출하면 무슨일이 일어나죠?
class MyClass{
 private MyClass(){}
 public static MyClass getInstance(){ return new MyClass();}
}
Bob : 오! MyClass.getInstance()를 호출하게 될 경우,
  MyClass 객체 인스턴스를 가져올 수 있습니다.

Jay : 코드로 보겠습니다.

class MySingleton  {
 private MySingleton(){
  syso("MySingleton 객체 생성");
 }
 public static MySingleton getInstance(){
  return new MySingleton();
 }
}
class SingletonRunner {
 public static void main(){
  MySingleton.getInstance();
 }
}

output : MySingleton 객체 생성

Bob : 완벽하군요, 그래서 뭐 어쩌라구요?

Jay : 흥분하지 말고 들어보세요, 이제 위 코드들을 조금 수정해 보겠습니다

class MySingleton  {
 private static MySingleton myInstance = null;
 private MySingleton(){
  syso("MySingleton 객체 생성");
 }
 public static MySingleton getInstance(){
  if( myInstance == null ) {
   syso("생성 시작 MySingleton...");
   return myInstance = new MySingleton();
  }
  syso("이미 MySingleton은 생성되어 있습니다.")
  return myInstance;
 }
}

class SingletonRunner {
 public static void main(){
  syso("mySingleton1 를 초기화 해 봅시당");
  MySingleton mySingleton1 = MySingleton.getInstance();
  syso("mySingleton2 를 초기화 해 봅시당");
  MySingleton mySingleton2 =  MySingleton.getInstance();
 }
}output :
 mySingleton1 를 초기화 해 봅시당
 생성 시작 MySingleton...
 MySingleton 객체 생성
 mySingleton2 를 초기화 해 봅시당
 이미 MySingleton은 생성되어 있습니다.

Bob :오 이런 세상에.. 클래스를 두번 생성시도했지만, 이미 만들어 져 있기 때문에 getInstance를 아무리 호출해도 같은 클래스만 가지고 오는군요!


Jay : 자 그럼, 이딴걸 왜, 어디에 쓸지 감이 좀 오나요?
Bob : 단 하나뿐인 클래스를 만들고, 다음에도 다른 클래스에서 해당 유일한 클래스를 사용 할 때에도, 복잡하게 인스턴스를 넘기지 않고, 간단하게 getInstance로 이미 만들어져 있는 클래스 정보를 얻어 올 수 있어요!. 그러면 우리는 MySingleton 클래스에 앱이 동작할때 필요한 고유하고 필수적인 정보( 이른바 DB연결 정보, 로그 따위의)를 중복 생성 걱정없이 안심하고 관리 할 수 있겠네요!

Jay : 세상에 잘도 아시는군요, 맞아요! 그럼 이건 문제점은 전혀 없을까요?
Bob : 물론 있워요! 이 싱글톤 패턴에 너무 의존하게 될 경우, 하나의 클래스가 혼자서 너무 많은 일을 맡게 될 수 있어요. oop 원칙중 하나인 단일 책임 원칙에 위배되는 등 으 문제가 발생할 수 도 있겠네요.


Jay : 쓰레드는 어떨까요? 쓰레드를 돌리면 문제는 없을까요?
Bob : 오..아마도..

Jay : 코드를 보시죠

class MySingleton  {
 private static MySingleton myInstance = null;
  ...}

class SingletonRunner {
 public static void main(){
  Thread th1 = new Thread(){
    @override
    public void run(){
      super.run();
      MySingleton mysingleton = MySingleton.getInstance();
    }
  };
  Thread th2 = new Thread(){
    @override
    public void run(){
      super.run();
      MySingleton mysingleton = MySingleton.getInstance();
    }
  };
  th1.start(); th2.start();
 }
}output :

 생성 시작 MySingleton...
 MySingleton 객체 생성
 생성 시작 MySingleton...
 MySingleton 객체 생성

Bob : 물론 운이 좋아서 똑바로 작동 할 수 있겠지만, 보통은 output 의 로그처럼, 서로 myInstance가 없다고 생각하고 생성시켜 버릴 여지는 충분히 있군요. 이러면 쓸수가 없네요.

Jay : 다행히도 해결법은 존재한답니다.
스판
class MySingleton  {
 private static MySingleton myInstance = null;
 private MySingleton(){
  syso("MySingleton 객체 생성");
 }
 public static synchronized MySingleton getInstance(){
  if( myInstance == null ) {
   syso("생성 시작 MySingleton...");
   return myInstance = new MySingleton();
  }
  syso("이미 MySingleton은 생성되어 있습니다.")
  return myInstance;
 }
}
class SingletonRunner {
 public static void main(){
  ...
 }
}
output :

 생성 시작 MySingleton...
 MySingleton 객체 생성
 이미 MySingleton은 생성되어 있습니다

Jay : 하지만 synchronized 를 저렇게 사용하게 되면 기존의 getInstance 메소드와 비교하면 100배나 작동시간이 증가한다고 합니다.

Bob : 100배는 좀 그런데..물론 MySingleton 클래스의 역활이 별거 없다면 큰 차이는 없겠죠, 하지만, getInstance를 호출하면 항상 동기화를 하기때문에 계속해서

Jay : 네, 이게 만능은 아니겠죠!
또 MySingleton 의 필드값인 myInstance를 필드에서 생성하는 방식도 가능 할 거 같네요.
하지만 이 방식또한, 스레드 스케쥴링 부분에서 예상한 생성 시점과 다를 수 있다고도 하네요. 역시 만능은 아니라고 합니다.

그럼 또 다른방법을 알아봅니다..

class MySingleton  {
 private static volatile MySingleton myInstance = null;
 private MySingleton(){
  syso("MySingleton 객체 생성");
 }
 public static MySingleton getInstance(){
  if( myInstance == null ) {  // 먼저 생성유무를 따지고, 없는 경우엔
   synchronized (MySingleton.class){ // 동기화블럭이 작동하며 생성 작업이 진행됩니다.
    if( myInstance == null ) { //다시한번 생성유무를 따지기 때문에, 두번째로 시작된 스레드는 생성작업을 거치지 않고 끝납니다.
     syso("생성 시작 MySingleton...");
     return myInstance = new MySingleton();
    }
    syso("이미 MySingleton은 생성되어 있습니다.")
    return myInstance;
   }
  }
 }
class SingletonRunner {
 public static void main(){
  ...
 }
}
output :

 생성 시작 MySingleton...
 MySingleton 객체 생성
 이미 MySingleton은 생성되어 있습니다

Bob :  good! 좋아요, 이제 getInstance를 호출한다고 해서 무조건 적으로 동기화 블럭에 갇히지 않기 때문에, 리소스 소모 문제가 해결되겠네요.




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]

를 리턴한다 합니다.