nginx 와 socket.io 연동

nodejs를 이용해 socket.io를 사용하는 경우 보통 포트번호가 다음과 같이 들어가기 마련이다.

somewhere.com:9000

하지만 포트 번호가 노출되는것이 영~ 깨림찍한 경우엔 앞딴에 nginx 나 apache 를 두고 뒷딴의 9000포트로 프록시를 하는 것이 보통이다. 개인적으로는 아파치도 좋치만 nginx를 더 자주쓰고 있다. nginx 를 쓴다면 아래와 같이 설정을 추가한다.

server {
    listen 80;

    root /app/node-server/dist/public;
    index index.html index.htm;

    # Make site accessible from http://localhost/
    server_name somewhere.com;

    location / {

      // A: Proxy 패스를 설정하고 싶은 경우, 
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-NginX-Proxy true;

      proxy_pass http://somewhere.com:9000/;
      proxy_redirect off;

      // B: socket.io 사용시 아래설정 필수!!
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_http_version 1.1;

    }
    ... 이후 생략 ...
}

특히 socket.io를 이용하는 경우 B 설정은 필수다. B설정이 뭘해주는지는 솔직히 귀찮아서 찾아보진 않았다. 하지만 저 설정을 하지 않으면 ws:// 프로토콜로 웹소켓과 통신할때 핸드쉐이킹이 제대로 안되서 에러가 난다.

Swift 공부하면서 새롭게 알게된 내용 정리

sorted VS. sort

“자바스크립트 같은 스크립트 언어를 기존에 써왔던 사람이라면 Swift라는 언어는 비교적 쉽게 배울수있는 것 같다.” 라고 생각했다가 뒤통수 맞는 것들이 몇가지 있는데 그중에 하나가 바로 sorted 함수와 sort 함수다. 둘의 차이는 정렬을 해서 반환값이 있냐? 없냐의 차이!

mutating func sort(isOrderedBefore: (T, T) -> Bool)
func sorted(isOrderedBefore: (T, T) -> Bool) -> [T]

함수 시그니처만 봐도 sort는 반환값이 없다. -_-;;

참고, Javascript 에서 sorted 함수는 당연히 없고, sort 함수는 본래 배열을 정렬시키고 정렬된 값을 slice해서 새로운 값으로 반환한다. 즉, Swift의 sort 와 sorted 두가지 역할을 모두 수행한다.

극강의 타입 추론 능력!

Swift 의 또 다른 매력이라고 하면 타입 추론을 빼놓을수없다. 얼마나 똑똑한 언어인지 개발자의 귀차니즘을 많이 줄여준다(?) 하지만 단점은 이게 뭔소린가하는 느낌을 줄정도다. 하지만 타입 추론 방식을 좀 이해하고 나니 지금은 꾀나 익숙해졌다.

var arr = [6,2,1,2,3,3]
arr.sort(<)   <--- 이게 뭐얌?             --- 1번
println(arr)   // "[1, 2, 2, 3, 3, 6]"

arr.sort(<) 이게 도대체 뭔소리야 하는 사람도 있겠지만 결과는 오름차순 정렬이다. sort 함수는 인자로 오퍼레이터를 받는건가? 라고 생각할수도 있겠지만 사실 위 코드는 아래와 같이 풀어 쓸수도 있다.

var arr = [6,2,1,2,3,3]
arr.sort{$0 < $1} <--- 이건 또 뭐얌?      --- 2번
println(arr)  

당연히 2번은 1번과 같은 코드다. 2번에서 $0과 $1이라는 변수는 Swift에서 인자의 타입이 무엇이 정확하게 추론이 가능할때 타입 이름을 특별히 지정하지 않아도 쓸수있는 키워드 정도로 이해하면 된다. 물론 인자가 3개라면 $2도 쓸수있다. 그렇다면 이 함수의 원형은 어떻길래 추론이 가능한걸까? 추론 되기전 상태로 좀 더 거슬러 올라가보자.

var arr = [6,2,1,2,3,3]
arr.sort{                              --- 3번
   (t1, t2) -> Bool in
   t1 < t2  
}
println(arr)  

3번에서 sort 다음에 {} 중괄호는 일단 넘어가자. 괄호안에 in 이라는 키워드는 이 함수가 클로저 함수임을 나타낸다. 즉 in 앞에 내용이 이 함수의 시그니처다. 그러니까 인자 2개를 받아서 Bool형으로 반환한다는 이야기!! 뚜둥~!! 그런데 또 여기에 return 문이 없다. Swift 에서 함수의 마지막 문장은 return 문이 없어도 그 문장의 결과값이 return 문으로 반환된다. 그러니까 4번 처럼 된다는 얘기다.

var arr = [6,2,1,2,3,3]
arr.sort{                              --- 4번
   (t1, t2) -> Bool in

   return t1 < t2  
}
println(arr)  

그런데 여기서 또 의문이 생겼다. 도대체 t1과 t2는 어떤 타입이길래 서로 비교가 가능한걸까? 이걸 추론해 내는 것이 Swift 컴파일러의 능력인것이다!! arr 변수는 숫자형 배열이기때문에 바로 위에 문장을 기반으로 sort 내부에서 수행되는 t1과 t2가 Int형임을 알수있는 것이다. 그러니까 5번처럼 인식한다는 얘기!

var arr = [6,2,1,2,3,3]
arr.sort{                              --- 5번
   (t1:Int, t2:Int) -> Bool in

   return t1 < t2  
}
println(arr)  

맨위에 sort 함수 시그니처를 보면 sort 함수는 같은 제네릭타입 T를 인자로 받는다. 따라서 t1과 t2는 같은 타입이 된다. 만약 t2를 Int가 아닌 다른 타입으로 캐스팅 한다면 어떻게 될까?

arr.sort{                              --- 5-1 번 
   (t1:Int, t2:Float) -> Bool in
   return t1 < t2  
}

당연한 얘기지만 sort 함수의 인자 타입이 달라서 실행할수없다고 에러를 낸다. 역시 똑똑한 녀석!!

“Cannot invoke ‘sort’ with an argument list of type ‘((Int, Float) -> Bool)’

마지막으로 저 뜬금없는 sort 다음에 {} 중괄호!! 너는 도대체 뭐냐? 아래 6번 코드를 보자!

var arr = [6,2,1,2,3,3]
arr.sort() {                              --- 6번
   (t1:Int, t2:Int) -> Bool in
   return t1 < t2  
}
println(arr)  

잉? 이건 또 모야? 앞에 () 괄호를 써도 되는 거네? 맞다! {} 중괄호 앞에 괄호가 와도 된다. 6번에 괄호가 생략된 이유는 괄호 안에 들어가는 마지막 인자가 함수일 경우 Swift는 마지막 함수를 괄호 밖으로 끄집어 내서 쓸수있도록 허용한다. 정리하면 괄호안에 들어가는 인자가 여러개 일때, 중간에 함수가 들어가는 경우는 뺄수없다.

// p는 변수, f는 함수라고 가정한다. 
// 이런 함수는 f1을 끄지어 내지 못한다. 
x.method(p1, f1, p2) ---> X

//  이렇게 맨 마지막 인자가 함수면 끄집어 낼수있다. 
x.method(p1, p2, f1) ---> x.method(p1, p2) {} 

제네릭 타입의 형변환

클로저 함수를 다루다 보면 타입 추론에 의해서 생략되는 녀석들이 많아 가끔씩 헷갈릴때가 있는데 이럴때는 꼭 해당 함수에 마우스를 올리고 커맨드 키와 함께 문서를 열어보는 것이 좋다. 여튼 Swift에서 제공하는 많은 내장 함수들이 Typealias나 제네릭 같은 타입을 많이 쓰고 있어서 클로저 함수에 인자를 받아서 쓰다보면 다음과 같은 에러가 종종 발생한다.

(!) Cannot invoke ‘predicate’ with an argument list of type ‘(T)’

extension Array {
  func myFilter<T>(predicate:(T) -> Bool) -> [T] {
    var result = [T]()

    for i in self {
      if predicate(i) {      // <----- 여기가 문제!!
        result.append(i)
      }
    }
    return result
  }  
}

위에서 myFilter 함수는 predicate 라는 클로저 함수를 인자로 받아서 제네릭 타입인 T를 요소로 갖는 배열을 반환하는 함수다. 내부에서 쓰이는 self는 Array 클래스를 확장했으므로 당연히 내부에서 관리되는 배열일 것이라는 추측을 할수있다. 실제로 출력을 해봐도 배열값은 맞다. 하지만 컴파일러는 self 배열에서 꺼낸 i 타입이 사실 뭔지 모르고, predicate가 T 타입을 받아야한다는 사실만 알고 있기 때문에 “i가 뭔지 모르겠는데 T타입은 아닌거 같아. 제대로 넣어줘~!” 라는 메세지를 준다.

extension Array {
  func myFilter<T>(predicate:(T) -> Bool) -> [T] {
    var result = [T]()

    for i in self {
      if predicate(i as! T) {      // <----- 해결!!
        result.append(i as! T)     // <----- 해결!!
      }
    }
    return result
  }  
}

그래서 이럴때 as! 를 이용해 타입을 확실히 명시해주면 해결된다!

React 사용 1일차 회고

첫만남

F8 컨퍼런스 이후 React Nativce 이야기가 한창 들리더니 이제 다시 잠잠해지는 느낌이다. React를 처음 접한건 지금으로부터 대략 1년전 바르셀로나에서 열렸던 FutureJS에서 였다. 당시 왜들 그렇게 React React 하는지 이해가 안갔다. 그냥 Facebook에서 만들어서 그런건가 싶었다. 그래서 한국에 돌아오고나서 React를 살펴봤다. 역시나 그냥 Facebook이 만들어서 그런건가 싶었다.

첫느낌

Angular에 비해서 개발속도가 엄청 빨라 보이지도 않았을 뿐더라 그때는 한창 Two way binding에 열광하고 있던터라 React의 장점이 크게 다가오지 않았다. 고백하건데 당시 React를 공부해볼까했는데 사실은 연애를 시작하는 바람에 제대로 보지 못했다. ㅎㅎㅎ (핑계는…ㅋㅋㅋ)

첫회고

여하튼 그래서 다시 보기로 맘 먹고 어제 튜토리얼을 한번 쭉 훝어보고 기획해놓은 아이디어에 적용해보고 있다. 써본지 1일차의 느낌은 예전에 네이버에서 근무할때 Jindo Component를 만드는 느낌이다. 돌이켜 생각해보면 Jindo Component에 고민을 좀더 더 했더라면 React 같은 녀석을 훨씬더 일찍 만들어 쓰고 있지 않았을까 싶다. 그나저나 JSX 문법이 처음엔 이상했는데 지금보니 이게 정말 물건이지 싶다. 🙂 역시 사람이나 기술이나 겪어봐야 아는갑다.

오늘은 React에 이어 Flux도 같이 찬찬히 볼 예정이다. 또 적용해보고 생각나는대로 글을 남겨봐야겠다.