2020-02-22 TIL

할 일 목록

  • 코틀린 녹취록 모두 남기기
  • 알고리즘 한문제 외우기.
  • 네트워크책 읽기

완료하지 못한 목록

  • 코틀린 정리하기
  • 알고리즘 한문제 외우기.

완료 목록

  • 코틀린 녹취록 모두 남기기
  • 네트워크책 읽기
  • 코틀린 정리하기

5Fs

1. Fact

Kotlin Stream

stream 생성

방법 : stream generate

stream generate는 요청을 받을 때 마다 무한히 만들어 줌. generate에 있는 함수는 호출할 때 마다 값을 준다. 밑의 코드의 stream을 실행하면 1부터 20까지 랜덤값을 발행해줌 무한히.

또 stream에 여러개의 메소드 channing을 해도 실행하기 전까지 연산 지연함. collect()를 하면 연산함. 이게 stream의 귀찬음.

asSequence를 쓸때는 sequence의 일이 얼마나 무겁냐에 따라 효율이 달라진다. loop이 100번 인데 chainning 함수가 2000개 라면 무조건 sequence로 다루는게 효율적이다. map에서 하는일이 코루틴을 통해 네트워크에서 받아온다면 ? 이러면 맵다음에 또 filter를 돌면… 어마어마한..

Stream으로 바꾸지 않고 sequence로 하면 코틀린의 콜렉션 메소드와 굉장히 일치함. 대신 sequence는 코루틴을 사용해서 분해하지 않는이상 멀티스레드를 지원하지 않는다. sequence를 코루틴으로 분해해서 사용하면 원소 하나 처리할 때마다 코루틴이 발동된다. 그럼 dispatcher가 어떤 스레드에 넣을지는 알아서 한다. 그래서 멀티스레드 디스패처를 만들어 코루틴으로 sequence를 해소하게 되면 얘는 멀티스레드에서 각 원소가 해소하는 처리를 하게 되고 자바의 stream도 multithread 처리를 할 수 있지만 stream에 대한 기본전략을 잘 잡아줘야함 .

컬렉션의 멀티스레드는 어렵다. 컬렉션의 원소가 다 atomic 해야만 멀티스레드를 구현할 수 있는데 잘 안된다. 실무에선 generator와 asSequence를 굉장히 많이 쓴다.

fun main() {
    val stream = Stream.generate {
        (1..20).random()
    }

    val resultantList = stream
        .limit(10)
        .collect(Collectors.toList())
}

kotlin은 buildSequence를 이용할 수 있는데 그 함수를 이용하면 sequence를 generator 문법으로 만들 수 있다. kotlin에서는 yield가 외부에 출력은 가능하지만 입력은 받지 못한다. while문 안에 yield가 들어갈 때 마다 멈출 수 있기 때문에 걔는 yield를 기준으로 continuation이 만들어 진다. take(10)을 하면 제어문인데 while문을 멈춰버리고 거기서 빠져나올 수가 있는 것이다. 이러면 알아서 continuation을 만들어서 사용함. 하지만 streamGenerate는 실제로는 함수를 작성하는것도 못한다. 제어문 형태로 못만듬.

컬렉션이나 스트림은 메모리를 외부로 이전하는 효과를 갖는다

예를들어 전국민 전화번호 만든다 생각해보자. 근데 전화번호부를 반복하면서 뭘 처리하고 싶은데, 모든 전화번호부를 메모리에 할당하면 엄청난 메모리가 필요한데, sequence를 사용하면 내가 필요할때마다 외부 API에 계속 요청하면 됨. 통신을 통해서 각 전화번호를 받으면, 내 컴퓨터의 메모리에는 없는데 외부에서 받아올 수 있다. 그러면 무한의 메모리를 당겨 쓰는 것과 같다. 그래서 시퀀스라는것은 단지 계산을 지연할 수 있는 기능만이 아니라 메모리를 외부로 이전하는 효과가 일어난다. 그렇다는 얘기는 시퀀스의 출처를 시퀀스도 만들 수 있다. 시퀀스가 시퀀스를 출력할 수 있다는말. 어떤 서버가 시퀀스 서버라고 생각하면 어떤 서버를 시퀀스로 가져오는 시퀀스를 만들어서 걔를 . 또한 나한태 시퀀스 형태로 데이터를 주고 있는 그 시퀀스 서버도 자기 데이터의 일부를 다른 시퀀스 서버로부터 가져올 수 있다. 그러면 어떤 요청을 일으키면 자기 메모리에서 일어나는게 아니라 그 요청이 다른 시퀀스 서버의 다음 시퀀스를 요청하게 되고 그 서버는 또 다른 시퀀스 서버의 다음 번쨰 시퀀스를 요청하게 됨. 그중에 어떤 시퀀스는 내가 처리하지만 다른 처리는 다른 서버로 부터 받아오는데 나의 하나의 요청이 서버로 전파하고 나면서 쭉 필요한 만큼만 시퀀스로 댕기고 나머지들은 요청할것이다. 내가 시퀀스 서버로부터 홀수를 공급받을 건데, 홀수일땐 나한태 주고 3의 배수라면 다른 서버로 부터 받아오는것임.그러면 내가 홀수를 댕길때 홀수만 처리하는 서버가 처리하는 양과 3의 배수만 처리하는 서버의 처리양이 다르겠지만, 다들 내가 원하는 만큼만 일하고 더 이상 일하지 않음. 예를들어 15까지 요청했다면 3의 배수만을 처리하는 시퀀스 서버는 딱 5번만 움직이고 움직이지 않는다. 홀수 서버는 그것을 제외한 4번만 움직인다. 그렇다면 7개의 홀수를 얻게 된다. 그렇다면 모든 서버가 다 시퀀스로 되어져 있다면 내가 필요한 정보를 서버별로 분산하는게 자동으로 이루어지고 딱 필요한 만큼만 이루어짐. 이런 시퀀스 chain을 내가 pull로 당겨서 일으키고 있는 것이다. 그래서 sequence는 pull 방식의 분산을 유도할 수 있다. Rx는 push 방식의 분산을 유도한다. push 방식은 당기는게 아니라 밀어주는 것임. 내가 받겠다고 해서 저쪽에서 밀어주는거지만 내가 당기는것은 아님. 이게 언제 유리하냐면 push와 pull의 부하를 분산하고 싶을때 유리하다. 별도의 ec2인스턴스를 만들어서 얘는 thread sleep으로 while 돌면서 감시만 하는 인스턴스가 있음. 그럼 이벤트 리스너에 함수를 붙혔다면 함수는 실행안됨. 이게 부하 분산이 된것임. while은 빡빡하게 돌지만, 리스너에 붙은 함수는 실행이 안되고 있는것이다. 그래서 결국 가 pull인데 pull 과 pull&push로 나누는 이유는 pull 쪽의 부하를 가져가게 분산시킬 수 있다. 같은 머신내에서도 일어나고 다른 머신 내에서도 일어난다. 아마존 simple queue는 pull push 둘다는 제공하지 않는다. 큐에 쓰고 읽는건 되지만 이벤트 알람해주는 기능은 없다. 직접 구현해야된다. 실제로 queue에는 데이터가 많이 들어 가지 않는다. 그래서 bucket에 큰 데이터를 넣고, 큐에는 작은 데이터를 넣는다. pull push 개념을 잘 이해 해야하는데 우리가 사용하는 spring의 컨트롤러도 push 방식이다. 요청을 하면 이벤트 리스너가 발생한것 처럼 실행 되는데, 그 안에선 사실 pull을 겁나게 돌고 있는애가 있다. 왜 이렇게 까지 할까 ? end 포인트 개발이 쉬워지고 부하 분산이 쉽다. pull 루프는 c가 돌고 마지막에 다이얼로그형으로 짜여져 있는 리스너는 느린 자바스크립트로 짤 수 있게 되는것이다.(부하분산) 또한 rx처럼 프레임워크 수준에서 push로 바꿔주는 시스템을 쓰면 어떤 장점이 있냐면 rx에 온 수많은 요청을 종합적으로 내부 스케줄링을 통해 더 효율적으로 만들 수 있는 기회가 생긴다. 코루틴도 비슷한데 디스패처를 공유하게되면 더 많은 suspend함수를 스케줄러 안에 넣고 분산시켜서 처리할 수 있는 여력이 생기게된다. 그래서 중앙 집중화 되어 있는 push 시스템은 무의미하지 않다.

둘중 무엇을 쓸 것이냐는 data의 주도권이 누구한태 있냐에 달려있다.

pull 방식을 쓰는 경우와 장점

  • 우리가 서버한태 당겨오는 쪽이라면 pull 방식을 사용한다.
  • server의 가용성이 높아진다. ? push와 같다고 ? 그럼 네이버 서버가 push 방식을 하기 위해 모든 클라이언트를 모두 기억해야 한다. 요청 응답으로 끝내니까 push 몇만명에게 요청을 받을 수 있는 것임. 기억할 필요없이. push에 비해 pull이 부하분산이 좋다. 왜냐면 요청이 올떄마다 주는게 훨씬 비용이 적다.

push 방식을 쓰는 경우

  • 서버가 우리한태 데이터를 밀어내는 쪽이라면 push 방식을 쓸 것이다.
  • 장점

    • 서버의 가용성이 높아짐. 자기 마음대로 보냈다가 안보냈다가 할 수 있기 때문에
    • 서버의 cpu가 과열된다면 잠깐 멈출 수 있음.
    • push 방식이 좋을때는 소수의 커넥션에게 데이터를 대량으로 푸쉬할때 서버가 비용이 많을때 동영상 인코딩 서버, 이미지 서버는 소수의 사용자에게 cpu파워를 너무 많이 줘야하기 때문에.cpu파워를 아끼기 위해서 100명중에 조금씩 cpu를 할당해서 push할땐 유리하다. 그래서 push용 서버로 유리한 경우는 대부분 다 넷플릭스인 것이다.
알고보면 push 는 존재하지 않는다. 모든게 pull한 후에 push로 처리된다

우리가 두 서버간의 직접적인 통신을 막아서 부하분산을 처리하기 위해선 중간에 suspend를 주면 된다. 어떻게 주냐면 Queue에 주면 된다. 서버가 필요한 일이 있으면 queue에 쓰면 된다. 큐에 새로운게 들어왔다는 알람을 다른 서버가 받아서 처리를 다 하고 처리가 다 끝났으면 다른 큐에 보고해 주면된다. 이것이 더블큐 방식이다. 더블큐 방식에서는 요청한 쪽도 큐에 쓰고 완료한 쪽도 큐에 쓴다. 완료했다는 큐를 통보받은 애가 집어가면 된다. 재일 쉬운 방법은 요청을 큐에 쌓을때 이 요청건수에 대한 고유한 uuid를 만들어서 큐에 쌓고 그걸 인수한 애는 그 작업을 처리한 후에 그 uuid로 보고해주면 서로 uuid 키교환이 되기 때문에 내가 원했던 uuid가 들어왔네 하고 캐치할 수 있다. 이방식에서 내가 원하는건 큐에 넣을땐 내 마음대로 넣고 입력 큐에 들어온걸 감시하는건 푸시알람으로 받았으면 좋겠잔아 근데 실제적으로는 while sleep을 돌면서 계속해서 감시하는 것이다. 그런 서버가 중간에 있다면 이 서버는 계속 pull 감시하고 있었지만 나한태 등록된 리스너 한태는 push로 알려줄 수가 있다. push는 pull을 push로 전환을 한번 더 해주는 것임. 마법같은 push는 없다. event listener란 누군가가 루프돌면서 감시해서 그걸 event로 바꿔서 listener에게 준것이다.

java로 네트워크 통신을 제어할 수 있는 방법

jvm 은 순수 자바로 제어하지만 순수 자바로 시스템 스레드를 건드릴 순 없다. java 에서 윈도우 스레드를 어떻게 사용할까 ? 당연히 win 32/64프로그램을 만든 프로그램을 경유해서 사용하게 될것이다. C모듈로 되어있는 네이티브 모듈을 자바 인터페이스로 바꿔주는걸 JNI(Java Native Interface) 레이어라고 부른다. JNI를 자바 인터페이스에 맞는 헤더를 가져와 네이티브로 컴파일 하고 나면 애는 자바 인터페이로 생산됬기 때문에 자바 내부에서 부를 수 있다. 결국엔 jvm 가상 머신 재일 밑에는 JNI 규격을 따르는 수 많은 네이티브 함수들이 널려있다. 네트워크 그래픽 메모리 프로세스 관리. 그렇기 때문에 jvm 수준에서 jni 인터페이스를 쓰는지 않쓰는지 알수가 있음. 어떤 jni를 쓰는지 알 수 있음. 안드로이드는 main 스레드에서 JNI에 제약이 걸려있다. 그래서 어떤 라이브러리를 써도 결국 http 커넥션을 쓸건데 jni 소켓을 사용할 것임. 그러면 안드로이드는 죽일것임.

observable은 스케줄러가 그다음번 푸쉬할 값을 가져오는 일을 한다. observable의 스케줄러는 한명이라도 구독자가 생겼을때부터 발동한다. 구독자가 없어지면 스케줄러는 멈춘다. 구독자가 생기면 푸쉬 작업을 한다.그러면 이 스케줄러가 전체 스레드풀에서 노(오)는 애들이 그다음번 처리를 해주게끔 계속 작업하게 시킬 수 있다. 그럼 observable을 30개 만들었는데 스레드풀은 스레드에 5개 있다면 5개가 돌아가면서 각각 애들의 푸시작업들을 위임받아서 한다. 정확하겐 suspend 함수가 쌓여있는 코루틴과 똑같이 작동한다. 그래서 그냥 그 자체로서의 stream은 코루틴 처럼 하기 힘든데 까다롭다고 해봐야 sequence로 바꿔서 코루틴 dispatcher를 걸어주면 똑같이 작동된다. 우리가 제너레이터를 만들었다고 해도 suspend 블록들이 만들어졌을 뿐 걔를 아직 소비하고 있지는 않음. 그냥 소비할때 take로 소비했는데 take가 dispatcher로 소비하는게 아니라 현재 스레드를 이용해 소비한다. 그래서 시퀀스를 코루틴이 소비하게 해야지 위에서말한 스케줄러가 멀티스레드를 쓰는것처럼 서스펜드블록을 코루틴에 있는 dispather가 소비하게 된다. yield(내부를 보면 suspend function을 부르고 있음.)를 통해 시퀀스를 만들어주는건 sequence 라는 빌더밖에 없다. 코루틴 시스템이나 코틀린이 제공하는 시퀀스 불리는 서스펜드 함수를 호출할 때만 cps를 나눠준다. 나눠진건 suspend 함수의 컬렉션일 뿐이다. 정확하게 컬렉션이 아니라 컨티뉴에이션과 연결된 형태이다. 지연 실행이 가능한 구조로서만 준비되어져 있는것이다. 그것을 소비할 것임. 소비할건데 시퀀스 입장에선 시퀀스 메소드를 사용해서 소비하는 것이다.take(10)이 sequence linkedlist를 하나씩 전진시켜서 실행하게 하는것이다. 그럼 우리가 시퀀스 블록도 take을 사용해서 실행할 수 있다. 근데 우리는 코루틴 만들때 시퀀스로 만들지 않고 코루틴 빌더로 만들었다. launch같은. 이것은 외부에 job을 리턴한다. 이 스레드가 언제 끝나는지 알려주기 위해 job을 리턴하는데 그에 비해 sequence는 suspend 블록을 다 처리하고 난 다음 뭘 이것을 감싸는 자료구조체로써의 인터페이스를 리턴한다. 컬렉션처럼. 그게 시퀀스 빌더와 코루틴 빌더와의 차이다. 안에 만들어지는건 똑같이 서스펜드에 링크드 리스트인데 얘는 목적이 dispatch로 분산하는 멀티스레드와 스레드 타이밍을 조절하기 위한 job(우린 join 때문에 왜냐면 언제 다른 job이 끝날지 모르니까) 목적이 아니라 컬렉션 처럼 다루는 자료구조를 만드는것이 목적이니까 내부적으로 서스펜드 함수에 링크드리스트가 만들어 지지만 얘는 바깥쪽에 노출하는게 dispatcher를 넣거나 context를 넣는게 아니라 collection 처럼 노출한다. 그럼 얘는 노출된 애를 다시 코루틴 빌더로 집어넣어서 소비하게 되게 하면 되긴한다. sequence를 launch안에 넣고 await으로 take(1) 을 여러번 해주면 된다. 그럼 이 안에서 suspend가 일어날 떄 마다 디스패처가 작동한다. 아까 말했듯이 시퀀스도 서스펜드 블록이고, take(1)도 suspend 함수를 꺼내는 행위라면 코루틴 안에서는 이 행위를 dispatcher가 해주는 것이다. cps 전진시키는것을 dispatcher가 해준다. 그래서 sequence를 만들고 launch를 비롯한 코루틴 스코프에 넣어주면 그떄 그안에서 하는 행위가 dispatcher가 관여한다. 안하면 buildSequence는 코루틴 컨텍스트에 dispatcher를 main으로 지정해 주는거나 마찬가지이다.

함수형을 도입하는 이유는 지연 연산이 굉장히 크다. 대규모 DB 시스템을 프로그램으로 구축할때 메모리가 부족하면 어쩔 건가 ? stream왜 생겼을까 stream을 가장 많이 사용되는 곳은 http connection을 쓰면 inputStream과 outputStream을 사용한다. inputstream으로 받는 이유는 뭘까? 소켓 통신이 지속적으로 데이터를 주기 때문이다. 내장되어있는 null 문자열이 오면 socket을 끊는다. 그러면 inputstream만 조사하면 헤더도 본문도 stream으로 날라오면 끊을 수 없고 매번 끝을 확인하지 않고 buffer가 찰 때까지 stream으로 돌린다. 그럼 우리는 fileBuffer를 stream 동기적으로 돌리는데 그럼 안되는거 아닌가 ? 스트림이 다들어올때 까지 블록킹 하는데? 만약에 통신이 좋지 않다면 계속 블로킹 될탠데 ? inputStream을 while문으로 buffer에 계속 집어넣어주고 싶다면 스레드 루프를 돌거나 중간에 스레드 슬립을 걸어줘야한다. 그냥 while문으로 buffer에 넣어주면 안된다. 따로 스레드로 빼서 해줘야함. 근데 우리가 짠 http 커넥션을 보면 이 전체를 backgroun에 넣을지언정 stream을 처리하는 부분은 따로 스레드로 빼지 않는다. 근데 자바 inputStream과 outputStream은 블로킹 아닌가요(질문)? 모라는지 모르겠음.. NIO 안써도됨 NIO는 오히려 버퍼 전체를 감시하려고 쓰는거다. 근데 왜 stream으로 할까 ? 데이터가 corecursion이기 때문이다. 조금씩 무한히 얼마나 들어올지 몰라서 stream이다. 예전 소켓프로그래밍 하면 앞의 자리수를 지정해서 잘랐는데 http 프로토콜은 리스폰스할때 청크로 들어오면 전체 크기를 모른다. 마지막에 터미널 문자열이 들어와야 끈을 수 있다. 또 stream을 쓰는 이유는 메모리 보호하고 연산을 사용하기 위해서이다. 만약 html 본문이 1기가 들어온다고 생각해보자. stream을 다 기다렸다가 DOMParsor가 돌수 없다. 죽는다. 그러면 stream이 들어올떄 꺽쇠가 나올떄 마다 부분 랜더 파싱을 한다. 이게 바로 삭스 파서이다.

연산과 메모리는 교환된다. Math.sin을 매번 계산하는 방법이 있다면 라디안 소수 세번째까지의 모든 sin의 계산값을 배열에 넣어놔도 된다. 그럼 라디안 소수 세번째 까지는 배열에서 찾으면 된다. 이게 바로 메모리를 연산 대신 사용하고 있는 것이다. 반대로 내가 메모리를 하나도 안쓴다면 사인에 라디안값 들어올 떄 마다 수식으로 계산해서 리턴하면 된다. 그럼 메모리를 하나도 안쓰는 것이다. 그래서 컴퓨터는 어떤 경우에나 메모리와 연산을 교환할 수 있다. 그럼 stream은 극단적으로 메모리를 한번에 들어오는 stream 양으로 보호하고 연산을 사용하기 위해서이다. 컬렉션은 그에비해 반대로 한다. 메모리를 써서 연산을 줋인다. 메모리와 연산의 교환법칙에서 우리가 메모리를 보호하려면 stream을 쓰고 이러다보니 서버에서 많이 쓴다. 사용자가 많이 달라붙으니까 . 서버가 너무 느려 사람이 짜증내면 stream을 보다 메모리를 쓰는 방법으로 바꾸고 서버를 스캐일아웃하는 방식으로 간다. stream은 또한가지 방법중에 하나가 외부 stream에 의존하는 건데 그렇기 때문에 나한태 없는 데이터를 외부에서 받는 방식으로 외부한태 메모리나 연산을 다 전가시킬 수 있다. 예를들어 내가 stream의 결과로 image processing의결과를 받으려고 할때, 그럼 내가 stream에 원본 이미지를 여러개의 무거운 메소드(라운드, 플립 등 이미지 처리하는 무거운 연산들) 체이닝을 했을때 원본 이미지는 나한태 시작하지만 다음번 stream에 대한 연산은 다른 서버에 갖다가 둔다. 그럼 내가 처음에 이미지 배열을 갖고 있으면 첫번째 이미지는 다른 서버로 가서 요청해서 받아온다. 그러면 이 컴퓨터는 메모리도 아끼고 연산도 아낄 수 있다. 인코딩 갖고 호들갑을 떨 필요가 있나 싶지만 유튜브에 영상 인코딩은 어떻게 할건데 비디오 업로드 리소스를 가지고 mp4에 자기들의 포맷으로 인코딩 한후에 스케일 레벨로 동영상을 3초단위로 잘라서 용량별로 다 바꾼다. 이걸 서버 분산을 스트림으로 짤 수 있음. rx를 짤 수도 있고. 하지만 Rx는 푸쉬할 수 있는 원본 소스가 서버에 있다. 그래서 rx 원본을 만들고 observable로 subscribe를 다른 서버들이 하는것이다. 걔내한태 쏴주는 것이다. 나를 구독한 애들한태 다 쏴주면 구독하는 애들중 자르는애들은 자르고, 인코딩하고 , 인코딩을 구독하는 애는 저장소로 보낼 거고 저장이 되면 저장소를 구독하는애들은 화면 업데이트를 할태고 그럼 클라이언트는 화면 업데이트 하는애를 구독하면서 할것임. 이렇게 하면 push 로 구현할 것이고 반대로 구현하면 pull 로 구현하는것이다. 프론트엔드가 polling 으로 데이터가 업데이트 됬는지 물어보고 데이터 업데이트 서버가 인코딩 저장완료됬는지 그 담당 서버에게 물어보고 인코딩 서버에 또는 서버에 물어보고 하면 pull 방식이 된다. 하지만 위에선 observable subscribe 를 여러개 구성했으니 얘내들 마다 다 스케줄러가 폴링을 하고 있을 것이다. 서버들 마다 polling을 돌고 있는거다. 푸쉬란 마법은 없다. 이거 없이 서버 분산하면 서버 사이마다 다 큐를 둬야한다. 그러다 보니 rx 채인을 하려는 거고 이게 spring flux인 것이다. flux 서버끼리 stream 전환이 되는 이유가 바로 이것이다. 그래서 spring flux 끼리 채이닝을 구성하면 중간에 mq가 필요가 없다. 한개 할땐 mq 껴서 하는게 편할 수 있지만, 여러 채이닝 하면 이방법이 낫다. 클라이언트를 간략하게 하고 싶으니까 push 형으로 데이터 교환하고 싶고 지연시키고 싶고 내가 풀을 구현해서 앞뒤처리하기 싫으니까 rx로 쓰는것임 지가 알아서 polling을 하니까.

네트워크 인증

Basic 인증
  • 특징

    1. Base64 인코딩 형식을 사용하고 있지만, 암호화가 아니기 때문에 누구나 복호화 할 수 있다.
  • 인증 절차

    1. Basic 인증이 필요한 리소스에 리퀘스트가 있을 경우 서버는 상태코드 401 Authorization Required와 함께 인증의 방식(Basic)과 Request-URI의 보호 공간을 식별하기 위한 문자열(Realm)을 WWW-Authenticate 헤더필드에 포함해서 응답을 반환한다.
    2. ID:PW 형태로 Base64 인코딩을 진행하고, 이 문자열을 Authrization 헤더에 포함해서 리퀘스트를 송신.
    3. Authorization 헤더 필드를 포함한 리퀘스트를 수신한 서버는 인증 정보가 정확한지 여부를 판단한다. 그리고 정확하면 Request-URI 리소스를 포함한 리스폰스를 반환한다.
Digest 인증
  • 특징

    1. 챌린지 리스폰스 방식은 최오세 상대방에게 인증 요구를 보내고 상대방 측에서 받은 챌린지 코드를 사용해서 리스폰스 코드를 계산한다. 이 값을 상대에게 송신하여 인증을 하는 방법이다.
    2. 패스워드의 도청을 방지하기 위한 보호기능은 제공하지만 위장 방지 기능은 제공하지 않는다.
    3. 하지만 basic 인증과 비슷하게 문제가 많기 때문에 사용하지 않는다.
  • 인증 절차

    1. 인증이 필요한 리소스에 리퀘스트가 있을 경우 상태코드 401 Authorization Required와 함께 챌린지 리스폰스 방식의 인증에 필요한 챌린지 코드(nonce : 리스폰스 반환시 생성되는 유일한 문자열)를 WWW-Authenticate 헤더필드에 포함해서 리스폰스를 반환한다.
    2. Authorization 헤더필드를 포함한 리퀘스트를 받은 서버는 인증 정보가 정확한지 판단한 후에 응답한다.
SSL Client 인증
  • 특징

    1. 위장의 대책으로 사용된다.
    2. 단독으로 사용하지 않고 폼베이스를 이용하여 사용한다. 즉 첫 번째 인증 정보로 SSL 클라이언트 인증을 하여 클라이언트의 컴퓨터를 인증하고 다른 인증정보인 패스워드를 사용하여 유저의 본인 확인을 한다.
    3. 하지만 인증 기관에서 클라잉너트 증명서를 구입해야 하고 가격이 비싸다.
  • 인증 절차

    1. 인증이 필요한 리소스의 리퀘스트가 있었을 경우 서버는 클라이언트에게 클라이언트 증명서를 요구하는 “Certificate Request” 라는 메시지를 반환한다.
    2. 유저는 클라이언트 ㅈ으명서를 Client Certificate라는 메시지로 송신한다.
    3. 서버는 클라이언트 증명서를 검증하여 검증 결과가 정확하다면 클라이언트의 공개키를 취득한다.
폼 베이스 인증
  • 특징

    1. 클라이언트가 서버 상의 웹 어플리케이션에 자격 정보(Credential)를 송신하여 그 자격 정보의 검증 결과에 따라 인증을 하는 방식이다.
    2. 클라이언트 증명서는 비용이 많이 들고 웹사이트의 인증 기능으로서 요구되는 표준이 없기 대문에 웹 어플리케이션은 제각각의 폼 베이스 인증을 사용한다.
    3. HTTP는 stateless 프로토콜이기 때문에 방금 전에 인증을 성공 했던 유저라는 상태를 프로토콜레벨에서 유지할 수없기 대문에 세션관리와 쿠키를 사용하여 상태 관리를 보충한다.
    4. 패스워드를 안전하게 저장하기 위해 salt라는 부가정보를 사용하여 해시라는 알고리즘으로 계산한 값을 저장한다.
  • 인증 절차

    1. https를 사용하여 인증 정보(아이디 비번)을 전송한다.
    2. 정보가 검증되면 서버는 세션 아이디를 발행하고, 저장 후 세션을 받아 클라이언트로 전송한다.
    3. 서버측에서 세션 ID를 받은 클라이언트는 쿠키로 저장하고 매 요청마다 세션을 포함하여 요청을 보낸다.

http에 기능을 추가한 프로토콜

http는 HTML로 작성된 문서를 전송하기 위한 프로토콜 HTTP 이다.

HTTP에 많은 기능이 추가됬음에도 불구하고 아직도 기본으로 하는 이유
  • 기업이나 조직에 방화벽에는 기본 기능으로 http 포트만 저장이 되있으므로 새로운 프로토콜을 넣으면 모두 변경해야 하기때문에 새로 만들기에는 무리가 있다.
  • HTTP 클라이언트인 브라우저가 보급되어 있는 것이나 HTTP 서버가 많이 보급되고 있는 것 등의 이유도 있다.
HTTP의 병목이 생기는 이유
  • 1개의 커넥션으로 1개의 리퀘스트만 받을 수 있다.
  • 리퀘스트는 클라이언트에서만 시작할 수 있다. 리스폰스만 받는 것은 불가능하다.
  • 리퀘스트/리스폰스 헤더를 압축하지 않은 체로 보낸다. 헤더의 정보가 많을 수록 지연이 심해진다.
  • 장황한 헤더를 보낸다. 매번 같은 헤더를 보내는 것은 낭비다.
  • 데이터 압축을 임의로 선택할 수 있다. 강제적이지 않다.
병목 현상 해결 방법
  1. Ajax(Asynchronous JavaScript+XML)

    • JavaScript나 DOM(Document Object Model) 조작 등을 활용하는 방식으로, 웹 페이지의 일부분만 고쳐쓸 수 있는 비동기 통신 방법이다.
    • 페이지의 일부분만 갱신되기 때문에 리스폰스로 전송되는 데이터 양은 줄어든다.
    • Ajax의 핵심 기술은 XMLHttpRequest라는 API로 JavaScript등의 스크립트 언어로 서버와 HTTP 통신을 할 수 잇다.
  2. Comet

    • 서버 측의 컨텐츠에 갱신이 있었을 경우 클라이언트로 부터 요청을 기다리지 않고 클라이언트에게 보내기 위한 방법이다.
    • 응답을 연장함으로써, 서버에 통신을 개시하는 서버 푸시 기능을 유사하게 따르고 잇다.
    • 리스폰스를 보류하기 위해 커넥션 시간이 길어지고 리소스를 낭비한다.
  3. SPDY

    • TCP/IP의 어플리케이션 계층과 트랜스포트 계층 사이에 새로운 세션 계층을 추가하는 형태로 동작한다.
    • SPDY가 세션계층으로서 그 사이에 들어감으로써 데이터의 흐름을 제어하지만, HTTP의 커넥션은 확립되어 있다.
    • SPDY는 기본적으로 한 개의 도메인(IP 주소)과의 통신을 다중화할 뿐이기 때문에 하나의 웹사이트에서 복수의 도메인으로 리소스를 사용하고 있는 경우에는 효과가 한정적이다.
    • 기능

      1. 다중화 스트림 : 단일 TCP 접속을 통해 복수의 http 리퀘스트를 무제한으로 처리할 수 있다.
      2. 리퀘스트의 우선 순위 부여 : 각 리퀘스트에 우선 순위를 할당할 수 잇다. 대역폭이 좁으면 처리가 늦어지는 현상을 해결하기 위한 것이다.
      3. HTTP 헤더 압축
      4. 서버 푸시 기능
      5. 서버 힌트 기능
브라우저에서 양방향 통신을 하는 WebSocket
  • 기능

    1. 서버 푸시
    2. 통신량 삭감 : HTTP에 비해 자주 접속을 하는 오버헤드가 적고, 헤더의 사이즈도 작다.
  • 핸드쉐이크/리퀘스트

    1. Upgrade 헤더에 websocket 를 붙혀 핸드쉐이크를 한다. Sec-WebSocket-Key에는 핸드 쉐이크에 필요한 키가 저장되어 SetWebSocket-Protocold에는 사용하는 서브 프로토콜이 저장되어져 있다.
    2. 응답시에는 101 상태 코드로 반환되며 Set-WebSocket-Accept는 Sec-WebSocket의 값에서 생성된 값이 저장된다.
    3. 핸드쉐이크 후에는 HTTP가 아닌 WebSocket 독자적인 데이터 프레임을 이용해 통신한다.

컨텐츠에서 사용하는 기술

HTML
  • HTML(Hyper Text Markup Language) 웹 상에서 하이퍼텍스트(Hyper Text)를 보내기 위해 개발된 언어이다. 하이퍼텍스트는 문서 시스템의 하나로서, 문서 중에 임의의 장소의 정보가 다른 정보(문서나 이미지 등)에 관련된 즉 링크되어 있는 문서이다.
  • 디자인을 적용하는 CSS(Cascading Style Sheets)는 HTML 각 요소를 어떻게 표시할지를 지시하는 것으로, 스타일 시트라고 불리는 사양중 하나이다. CSS는 문서의 구조와 디자인을 분리한다는 이념에서 만들어 졌다.
Dynamic HTML

정적인 HTML 내용을 클라이언트 사이드 스크립트를 사용해서 동적으로 변경하는 기술을 말한다. HTML등으로 만들어진 웹 페이지를 JS등의 클라이언트 사이드 스크립트로 조작하여 동적으로 변화시킨다.

DOM(Document Object Model) :HTML 문서와 XML 문서를 위한 API이다. Dom을 사용하면 HTML 내의 요소를 오브젝트로 다룰 수 있기 때문에 요소 내의 문자열을 추출하거나 CSS를 프로퍼티로서 변경해 디자인알 수 있다.

웹 어플리케이션

웹 어플리케이션은 웹 기능을 사용해서 제공되는 프로그램을 지칭한다. 어플리케이션이 발달하면서 프로그램에 의해 생성된 컨텐를 만들어 보낸다. 이것을 동적 컨텐츠라 하고, 사전에 준비된 컨텐츠를 동적 컨텐츠라고 한다.

  1. CGI(Common Gateway Interace) : 는 웹 서버가 클라이언트에서 받은 리퀘스트를 프로그램에 전달하기 위한 구조이다.

    • 특징

      1. 매 요청마다 프로세스를 실행하기 때문에 대량 접속시 문제가 될 수 있다.
  2. java Servlet

    • 설명 : 서버상에 HTML등의 동적 컨텐츠를 생성하기 위한 프로그램이다. java 프로그래밍 언어의 사양의 하나이다.
    • 특징 : 웹서버와 같은 프로세스(컨테이너) 속에서 동작하기 때문에 비교적 부하를 적게 하여 동작시킬 수 있다.
데이터 통신에 이용되는 포맷이나 언어
  1. XML(eXtensible Markup Language) : 목적에 맞게 확장 가능한 범용적으로 사용할 수 있는 마크업 언어이다. HTML과 같은 문서 기술 언어이다. 하지만 HTML에 비해 데이터를 기술하는 것에 특화되어 있다.

    • 특징

      1. XML 구조는 트리 구조로 되어져 있다
      2. XML 구조는 태그로 나뉘어져 있고 트리 구조로 되어 있기 때문에 XML 구조를 해석하고 요소를 뽑아내는 파서 기능에 의해 데이터 추출을 쉽게할 수 있다.
  2. RSS/ATOM

    • 설명 : 뉴스나 블로그의 기사 등의 갱신 정보를 송신하기 위한 문서 포맷의 총칭으로 둘다 XML을 이용한다.
JSON(JavaScript Objsct Notation)

경량 데이터 기술 언어로서 JavaScript에 있어서 오브젝트 표기법을 바탕으로 하고 있다. 데이터 형은 boolean null 오브젝트 수치 문자열 등 7가지 이다.

WebSocket은 웹 브라우저와 웹 서버를 위한 양방향 통신 규격이다. 한번 접속을 확립하면 그 뒤의 통신은 모두 전용 프로토콜로 하는 방식으로 JSON이나 XML, HTML 이나 이미지 등 임의 형식의 데이터를 보낸다.

2.Feelings

  • 코틀린 코루틴을 다시 공부해 보는데 정말 어렵다.
  • 동기 비동기 블록 논블록에 대한 개념이 아직도 명확하지가 않다..
  • 이전에 스터디에서 배웠던 내용을 정리를 해야될지 모르겠다. 머리 아프다.
  • 한권만 깊게 파면 된다고 하는데 코틀린은 … 어디가서 잘한다고 이야기해야되는건가 ..? 지금 당장 취업에 필요한건 아니니 적당히 해야겠다.

3.Findings

  • 컬렉션이나 스트림은 메모리를 외부로 이전하는 효과를 갖는다
  • 실제론 push 는 존재하지 않는다. 모든게 pull한 후에 push로 처리된다. 마법은 없다.
  • java로 네트워크 통신을 제어하기 위해선 JNI 규격을 따라 만들어진 인터페이스(winsocket32/64 로 만들어진)를 사용해야한다.
  • 함수형을 도입하는 이유중에 지연 연산이 굉장히 큰 원인을 차지한다.
  • 연산과 메모리는 교환된다.

4.Future Action Plan

  • 자바 공부를 시작해야겠다.

5.FeedBack


Written by@Zero1
This blog is for that I organize what I study and my thinking, feeling and experience.

GitHub