- 최종 프로젝트 - 

 

 

로깅 시스템

 

최종 프로젝트에 ELK 로깅 시스템을 적용해보기로 했다.

본문의 내용은 전부 프로젝트에 적용한 내용을 바탕으로 작성하였다.

 

ELK 란? 

Elastic search , Log stash , Kibana 로 구성된 로깅 시스템을 말한다.

 

 

 

 

Log stash

각 서비스에서 발생하는 log 를 logstash 로 수집하고,

logstash 는 수집한 log를 elastic search 에 전달한다.

 

이 과정에서, log stash 는 비정형의 데이터 또한 원하는대로 가공하여

전송할 수 있으며,

 

그 과정은 Input (입력), Filter (가공) , Output (출력) 크게 세 부분으로 구성된다.

 

 

input {
    beats {
        port => ****
    }
}

 

Input 부분에서는 크게는 Port 를 지정하여 데이터를 받고,

필요에 따라 Codec 을 지정하여 Json, Html 등 구조화된 텍스트 또한 쉽게 처리하고 저장할 수 있다.

 

 

filter {
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{WORD:label}\] \[%{WORD:level}\]: %{GREEDYDATA:log_message} %{GREEDYDATA:extra}"
    }
  }
}

 

Filter 는 Log stash의 강력한 핵심 기능으로, 어떤 형태로 데이터를 받던지 간에

원하는 형태로 재 가공하여 데이터를 저장할 수 있다.

설정에 따라 그저 string 이었던 데이터도 규칙을 만들어서 필드를 구분하거나,

필요가 없는 부분을 가공 또는 제거 하는 등 다양한 작업이 가능하다.

 

 

output {
  elasticsearch {
    hosts => "elasticsearch:9200"
    user => "****"
    password => "****"
    index => "logs-%{+yyyy.MM.dd}"
  }

  stdout {
     codec => rubydebug
   }

}

 

Output 에서는 가공한 데이터를 어디로 보낼것인지, 즉 출력을 정하는 부분이다.

 

일반적으로 Elastic search 가  output 이 될 것이며,

해당 부분에 Elastic search에 관한 내용을 기재하게 된다.

 

추가적으로 ,

 

stdout  에서 가장많이 사용하는 rubydebug 는

logstash 에서 데이터를 받았을 때, 해당 내용을 logstash 의 log 에 남길 수 있다.

위 설정 결과, ruby debug로 출력되는 내용

 

 

Elastic Search 

엘라스틱 서치는 루씬 기반으로 개발된  검색 엔진이자 저장소 이다.

ELK 구성에서는 log stash 가 전달한 데이터를 저장하며,

이 수많은 데이터 (log) 에서 원하는 데이터를 빠르게 찾을수 있도록 구분하고 검색하는것이

엘라스틱 서치의 주요 기능이다.

 

엘라스틱 서치는 기본설정만 해도 알아서 잘 작동하기 때문에

기본 설정들 외에는 건들지 않았다.

 

 

 

Kibana 

대미장식인 Kibana 는, Elastic 에서 제공하는 데이터 시각화 툴로,

엘라스틱 서치에서 색인된 데이터를 검색하고 시각화 하는 하는 기능을 제공한다.

 

 

Discover

 

 

기본적으로 데이터를 elasticSearch 가 받은 그대로 다소 지저분하게  확인 할 수 있으며, 

 

필드로 구분하여 원하는 필드의 데이터만 확인 할 수도 있다.

 

 

훨씬 보기 좋다 !

 

여기에 더해 Dash board 를 통해

데이터를 표본삼아 그래프화 하는 등, 여러가지 시각적으로 데이터를 표현할 수 있다.

 

 

 

15분간 발생한 각 서비스의 log들

 

 

 

그러면, Log stash 는 어떻게 data를 수집하는가

 

프로젝트 초기부터, winston 을 통해 log를 생성했는데,

마침 winston-logstash 라는 모듈을 통해 

log stash로 로그 전송이 가능하다는것을 알게되어 적용해보았다.

 

   const logstashTransport = new LogstashTransport({
     host: host,
     port: port,
   }); // Logstash로 로그 전송

 

위 코드를 로그 생성하는 부분에 넣어주면, 로그 생성과 함께 해당 로그를 logstash 에 전송한다.

elk 구성만 잘 되어있다면, 간단한 코드 수정으로 바로 log 를 보낼 수 있기 때문에

접근성이 뛰어났다.

 

그러나 여러가지 부분에서 크고 작은 문제가 계속 발생했다.

 

 

 

트러블 슈팅 = 로그 수집

 

 

문제 1. 프로젝트는 MSA 구성이다.

해당 모듈은 로그를 생성하는데 이어서, 해당 로그를 전송하는 모듈이기 때문에,

로그 생성부분에 부착이 된다고 볼 수 있다.

 

 

현재 프로젝트는 MSA 이고, 로그가 발생하는 서버가 10개 이므로...

10개의 서버가 winston-logstash 를 사용하여 logstash 와 TCP 연결을 맺게 되고

각각의 서버에서 로그가 발생할 때마다 log를 무작정 마구마구 보내게 된다.

데이터가 손실될 우려는 적지만, 연결이 많아질 수록 성능이 저하될 수 밖에 없고,

log 를 생성하는 각각의 서버에서도 부담이 가게 된다.

 

 

문제 2.  알 수 없는 syslog 의 발생 ??

해당 문제는, 결국 winston-logstash 모듈을 버리게 된 계기로, 

 

logstash 에서 전송받은 message 를 확인 할 때

난데없이 syslog 형태의 데이터가 삽입되는 문제가 발생했다.

 

message:<30>Dec 18 17:10:11 app-logs[89717]: [ serializeForGate ] data ===>>>: { sessionId: '0c2e5d1e-f234-4f3e-a0cd-2dde306aa2b9' }

 

 

메세지 앞부분에 출처를 알 수 없는 syslog로 추정되는것이 삽입되어있는것을 확인했다.

이로 인해 데이터를 json 으로 정형화 하는것에도 방해가 되어 에러가 발생했고,

애초에 syslog는 내게 필요하지 않은 데이터이다.

 

 

로그를 생성하고 보내는 부분에서 확인해보아도

 

"message": "[ serializeForGate ] data ===>>>: { sessionId: '0c2e5d1e-f234-4f3e-a0cd-2dde306aa2b9' }"

 

syslog 의 흔적을 찾아볼 수없었고,

 

그렇다고 log stash input에서 syslog 를 설정한 것도 아니었다.

 

더욱 이해가 안가는 부분은,

pm2로 서버를 띄우면 syslog 가 안붙어있고 Docker 로 띄우면 syslog가 달려있다는 것이다.

 

 

해결 방법 =  Filebeat 로 선회 

도... 도망이 아니다 !!

위의 에러를 원인을 찾지 못했지만, 원래 Filebeat를 사용하려고 했던 계획이 있었기 때문에,

만약 Filebeat 를 사용해도 동일한 문제가 발생한다면 그 때 가서 해결하려고 했다.

 

 

 

 

 

파일 비트는 위 모듈을 사용했던 형식과 다르게, 중앙 집중식 로깅을 적극적으로 활용할 수 있다.

 

기존 서버들은 각각의 Docker 컨테이너로 구성되며, 

모든 하위 서버는 호스트 서버의 /logs 폴더를 볼륨화 하여 사용하기 때문에

모든 로그는 호스트서버의 logs 에 저장된다고 할 수 있다.

 

Filebeat 는 마찬가지로 볼륨화 한 logs 폴더를 바라보고, log의 변화를 체크하면 된다.

 

전반적으로 잘 작동하며, 위에서 발생한 정체불명의 syslog 문제는 발생하지 않았다.

 

다만, 위와 같이 기록된 log의 경우  한 줄, 한 줄 message 를 따로 보내게 되기 때문에

알아보기도 어렵고 불필요하게 작업량이 늘어나게 된다.

위의 log 또한 사람 보라고 만들어진 log 이니 만큼, 기록되는 방식을 가공하기 보다는

일단 통째로 한번에 보내고, log stash에서 가공하는것이 알맞은 흐름이라고 생각했다. 

 

 

filebeat.yml 파일에서 multiline 을 통해 적절히 내용을 병합하여 전송하고,

 

 

filter {
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{WORD:label}\] \[%{WORD:level}\]: %{GREEDYDATA:log_message} %{GREEDYDATA:extra}"
    }
  }
}

 

위에서 봤던 그 필터를 logstash.conf 에 적용해 주면,

설정한 대로 field 를 구분할 수 있게 되고,

 

그 구분한 필드를 바탕으로 내용을 정렬한다면, 본문 상단에서 확인했던 형태로

log를 조회 하는것이 가능해진다.

 

syslog의 원인은 찾지 못해 아쉽지만,

filebeat 가 일을 잘 해줘서 다행이다.

 

 

 

로그인/ 회원가입

Auth 와 인연이 있는 것인가? 

 

발생한 문제

로그인, 회원가입을 TEST 하던 중, 문제가 발견되었다.

회원가입을 기준으로, 이미 가입된 ID 가 있다면 회원가입 실패 처리가 되도록 구현했는데

 

문제는, 찰나에 같은 아이디로 가입을 요청이 여러번 들어올 경우, 

모두 저 조건을 건너 뛰고 동일한 ID로 여러개의 계정이 만들어져 버리는 것이었다.

 

여러개의 요청이 비동기로 실행될 때, 검증 당시에는 모든 요청에서 없는 ID 였었기 때문이다.

LoginId 가 UNIQUE 로 설정되어있는것도 아니었기 때문에 모두 성공 처리 되어버린것.

 

 

 

무슨 문제?

동시성 접근 문제 혹은 레이스 컨디션 문제

 

하나의 자원에 두개 이상의 프로세스가 접근하면서 예상치 못한 문제가 생기는 것인데,

 

위를 예로 들면 두 클라이언트가 동시에 회원가입 요청을 하면서,

해당 ID가 DB 에 등록되기 이전에 ID 검증을 둘 다 패스해버리는 현상이 생긴 것이다.

 

해결 방법 ?

1.  가장 원시적인 방법으로, 

 

LoginId 를 UNIQUE 로 만들면 결국 db에 업데이트 하는 과정에서 에러가 발생하는데,

그 에러를 catch 해서 response 작업을 하는것 

 

=> 일단 검증을 모두 pass 하고 update 에 가서야 문제를 찾는다는 점에서 타이밍이 맞지 않았고,

코드 적으로도 완성도가 떨어진다고 판단이 된다.

 

 

2. DB 트랜잭션을 시도

트랜잭션을 통해, 회원가입 기능의 일관성과 무결성을 보장할 수 있는데

아쉬운 점은 역시 있다.

회원가입에 트랜잭션을 걸면 모든 회원가입이,  어떤 ID로 가입하던지 순차적으로 처리되는

방식이 될 것이기 때문에, 동기적인 기능이 되어버린다.

 

또한 현재 코드와 다르게, 

회원가입 검증 과 update 가 트랜잭션을 통해 한번에 이루어져야 하기 때문에 

전체적으로 코드의 흐름을 바꾸어야 하는 문제도 있다.

 

 

3. lock Key를 사용

redis를 이용한 방법이다.

검증에 앞서서 Redis set으로 키를 등록하는 방법으로,

 

 

적절한 key 값을 설정해주고, 'NX' 옵션을 넣어서

return 값으로 'OK' 또는 null 을 반환 받을 수 있다.

 

이렇게 할 경우, 가입을 시도하려는 ID < 에만 Lock 이 설정되는 형태이기 때문에,

 

어느정도 비동기성도 유지가 되면서 동시성에 대한 처리가 가능하다.

 

다만, 역시 redis 에 데이터를 넣는 과정이 추가된다는 점이 아쉽고,

해당 회원가입 처리 이후 lock 키를 제거하는 과정 또한 추가를 해줘야 key 반납을 제대로 받을 수 있다.

 

 

Login 의 경우,   중복 로그인 검증을 위한 redis key 를 따로 사용하고 있었기 때문에

이를 적절히 Lock key 의 기능도 할 수 있도록 수정을 하고,

 

회원가입은 위처럼 lock key 를 만들어주어 동시에 같은 내용으로 성공처리가 되는일이 없도록 구현을 했다.

 

 

그 외에도 Queue 를 사용하는 방법도 있지만, 2번과 기능적으로 유사한 방식이라고 생각이 되어 PASS 하도록 했다.

 

드디어 최종 프로젝트가 발제되었다.

 

진행상황을 공유하고자 TIL을 작성하였다.

 

 

 

기획 컨셉은 마리오 파티 와 같은 장르의 

4인 플레이 기반, 개인전과 협동전이 섞인 미니게임을 여러개 만드는 것이다.

 

나는 협동 프로젝트 팀에 편성이 되어서,

같은 트랙의 서버 팀 인원과, 

외부에서 합류한 유니티 클라이언팀과 협업으로 최종과제 결과물을 만들게 된다.

 

초기 목표는 테스트 환경을 구축하는 것

일단, 서버팀에서는 개인이 구상한 아키텍처 패턴에 따라 

각자 서버를 하나씩 만들어보는것으로 시작했다. 

 

서버팀 인원이 4명이니, 4개의 초기 서버 모델이 나오는 것이다.

 

 

초기 구상은 위와 같다.

 

MAIN SERVER 에서 우선적으로 클라이언트의 모든 패킷을 받으며,

 

이것을 각각의 지정된 SUB SERVER 에 전달하여 로직을 처리하고, 

 

RESPONSE 를 반환해야 한다면, 다시 MAIN SERVER -> CLIENT 로 패킷을 전달한다.

 

분산 서버 환경을 만들것을 염두에 두고 위와 같이 설계 했으며, 

 

지금은 SUB SERVER 로 GAME SERVER 하나만 있지만,

추후 계획에 따라 추가되거나, MAIN SERVER 의 역할이 바뀔 수 있다.

 

 

기본적인 디렉토리 구조는 이전에 진행했던 TCP 과제들과 크게 다르지 않다.

 

일단은 분산 서버가 아닌 하나의 SERVER 에서 로직을 모두 처리하는 이전과 같은 방식으로

 

뼈대를 만들었으며, 

 

주말 간, 각자가 만들어온 서버 코드를 비교하여 장점을 살린 하나의 

PROTO TYPE SERVER 가 등장하게 될 것이다.

 

이 프로토 타입을 월요일에 클라이언트와 붙여서 테스트를 하는것이 우선 목표이다.

 

 

 

현재 기획한 미니게임 중

 

미니게임 - 빙판 밀어내기(?) 게임 테스트를 시작으로, 다음주 부터 본격적인 협업 개발이 진행될 예정이다.

 

 

 

 

문제를 보자 눈앞이 깜깜해졌다..

 

경우의 수 구하기 비슷한 것인데, 어떻게 result 를 구할 수 있을지 막막했다.

 

그래서 일단 예시를 늘려 보았다.

 

칸이 1칸일 때 = 1칸 = 1

칸이 2칸일 때 = 1칸, 1칸 // 2칸 = 2

칸이 3칸일 때 = 1칸 , 1칸 ,1칸 // 1칸 , 2칸 // 2칸 , 1칸 = 3

칸이 4칸일 때 = 1칸 , 1칸, 1칸, 1칸 // 1칸 , 1칸, 2칸  // 1칸 , 2칸, 1칸 // 2칸 1칸 1칸 // 2칸 2칸 1칸 = 5  

칸이 5칸일 때 = 1/1/1/1/1 + 1/1/1/2 + 1/1/2/1 + 1/2/1/1 + 1/2/2 + 2/1/1/1 + 2/1/2 + 2/2/1 = 8

 

어렴풋이 문제를 보고 n 을 구하기 위해선 n-1 에 무언가를 더해야 할 것 같다 정도 느낌이 있었는데, 

저렇게 result 값을 나열해 놓고 보니 어느정도 확신이 생긴다. 

 

이것은 얼마전 풀이했던 피보나치 수열에 해당한다 !

 

즉 , n = n-1 + n-2  라는 것

 

 

 

n이 1 or 2 여도 정상적인 answer 가 출력되게 살짝 손을 봤다.

 

answer  는 잊지 말고 위의 사항을 적용해 준다.

 

(return 값에만 적용할 경우, for문 과정에서 int한계를 넘어가는 경우가 생긴다) 

 

 

 

오랜만에 작성하는 TIL

TCP 학습에 들어간 이후로 TIL을 작성할 시간이 도무지 나질 않았다.

 

 

오늘로, CH 5 팀 프로젝트가 마무리 되었으며, 관련 내용을 작성하려한다.

 

과제의 목표는 완성된 클라이언트에 TCP 서버를 붙이는 것이다.

 

클라이언트는 제공되어있다

 

 

덩그러니 놓여있는 클라이언트에, 이제 서버를 만들어서 붙여야 하는 내용이다.

 

 

이번 프로젝트의 팀장을 맡게 되었으며,  파트는 회원가입과 로그인 구현 !!

 

사실, 로그인과 회원가입은 이전 프로젝트에서도 늘 있던 기능이며,

 

차이점이 있다면 POST 같은 REST API 로 구현하는 것이 아닌, 

그저 TCP 통신간의 패킷 주고받음을 통해 구현되어야 한다는 것.

 

본질은 굉장히 다르지만 코드는 크게 다르지 않다. 

 

 

더 볼 필요도 없이, 

 

그저 payload 를 통해 클라이언트 에게 받은 패킷을 문자열로 파싱한 뒤에

알아서 회원가입 스럽게 처리하면 된다 !

 

클라이언트가 어떻게 보내는줄 알고?

 

 

이미 완성된 클라이언트 이기 때문에, 어떻게 패킷을 받고 보내는지는

패킷 명세를 통해 정해 놓았다.

 

로그인을 통해 날아오는 payload 는 id, password , email 세 개의 데이터 라는 것이다.

 

 

물론 파싱 과정은 Proto.buffer 를 이용하여 진행

 

나는 payload 로 받은  id, password , email 세 개를 가지고 

회원가입을 처리하는 기능을 구현하면 된다.

 

회원가입된 user 의 데이터는 Mysql - AWS RDS 에 저장할 계획이었기 때문에, 

 

적절히 Joi 를 사용해 유효성 검사를 진행 한 뒤

 

DB 에 동일한 id 로 저장된 데이터가 있는지 체크한 후

 

bcrypt로 해싱하여

 

DB에 넣어주고

 

 

 

다시 Proto.buffer 를 통해 buffer 형태로 변환하여 연결되어있던 socket  으로 보내면 끝 !

 

 

클라이언트에 어떤형태로 보내는지?

마찬가지로 패킷 명세에 클라이언트가 어떤 형태로 패킷을 받는지도 정해져있다.

 

 

로그인은 ...  생략한다. 회원가입과 크게 다르지 않다.

 

 

본문은 아래 부터다.

이번 과제를 진행하며 마주한 가장 큰 벽은 Redis의 적용 이었다.

레...디...스...

 

레디스 사용의 목적

 

 

위는 발표PPT 의 일부분이다. (제가 작성한 부분임 !!)

 

아무래도, 360ms 라는 지연시간은 사용자 이슈일 확률이 높지만 , 

결과적으로는 Redis 적용을 철회하고 기존 계획인 인메모리만을 사용한 형태로 과제가 제출되었다.

 

 

그럼 결국 실패했고, 얻은것이 없는가?

기본적으로 Redis 를 적용하기 위해 몸부림친 결과, 

사용방법에 대해 어느정도 파악 할 수 있었고,

 

Redis 가 어떤 자료구조를 가지는지,

어떤 부분에서 강점이 있는지는 파악 할 수 있는 시간이었다.

 


game 세션에 해당하는 부분을 Hash 형태로 넣어 보았다.

 

실제로, 구현은 완료되어 테스트 까지 잘 진행되는 상태이다.

 

다만, Redis 파악과 테스트를 위해 혼자서 작업을 진행하다 보니, 

Redis 계획 자체가 너무 개인적인 행위가 되어버렸고, 

다른 팀원이 작업한 파트를 뒤집어가며 작업했기 때문에 그대로 제출 할 수는 없었다.

 

다시 되돌아와서 팀적으로 각자 파트에 Redis 적용을 하기에는 시간이 부족했다. 

 

아쉽긴 하지만 절대 낭비된 시간은 아니다.

 

앞으로 Redis를 어떻게 써야할 지 학습한 시간이 되었다.

 

라고 하기 무섭게 발표 당일,  다음 최종 프로젝트가 발제되었다.

 

앞으로는 트랙 종료까지  모든 시간을 최종 프로젝트에 쏟아햐 한다.

 

 

Node - 6기

 

CH 5 타워 디펜스 온라인 팀 프로젝트 3조의 회고입니다.

 

 

 

팀장 - 조웅상

 

Keep

새로운 것에 대한 시도-

아직 사용법을 익히지 못했던 Redis를 프로젝트에 적용하려고 했던 시도가 좋았다.

 

Problem

다만, Redis 적용을 시도하는데 있어서 팀적인 움직임이 아닌, 

개인적인 시도로 끝나버린 아쉬움이 있다.

 

Try
다음에는 새로운 시도에 관한 내용도 작업 초기에 공유하여

어떤 방향으로 진행하면 좋을지 먼저 의견을 나누고 

진행에 따른 상황을 지속적으로 공유하여

더 나은 방향성을 찾아야겠다고 생각함.

 

 

 

팀원 - 이희원

 

Keep
서로 해야하는 것에 좋은 고집이 있는 것이 좋았다.
깃허브 커밋 컨벤션 같은 것들이 정해지니 보기가 좋았다.

 

Problem
중간에 변경사항에 대해 공유하는 것이 부족했던 것 같다. 

일부 브랜치가 의도와 다르게 사용된 부분이 있었다.

 

Try
문제를 확인하고 구조를 다시 잡는 과정은 나쁘지 않았지만, 일부 아쉬운 부분이 있었다.
프로젝트 전역에서 사용되는 상수 등을 미리 정해두는 것을 유념해야 하겠다.

 

 

팀원 - 윤여빈

 

Keep 

잘 모르는 부분이 있다면 소통하여 이해하고 넘어갔던 부분


Problem 

작업분배가 균등하게 이뤄지지 않아 작업량의 차이가 있었던 부분

Try

 내 작업이 아니더라도 소통하며 작업을 함께 진행했으면 좋았을 것 같다

 

 

 

팀원 - 박건순

 

Keep 
자신이 맡은 일을 기한 내에 완료하는 것


Problem 
자신이 맡은 일을 끝낸 후 붕떠버린 것


Try
다른 분들에게 의견을 물어보고 일을 도와주던가 자신이 할 일을 찾아야 할거같습니다.

 

 

 

팀원 - 송인우 

 

Keep 
상황을 소통하고 개개인에서 새로 만들어 보는 것도 좋은 것 같습니다.

 

Problem 
아무래도 클라이언트와의 소통의 문제는 디버깅작업으로 유추하기보다는
클라이언트를 만든 튜터님께
직접 물어보는 게 문제 해결에 있어 훨씬 좋을 것 같습니다.

 

Try
코드를 만들어봐도 단계별 작업이기에 이전 단계 작업이 안되면
검증하는데 어려움이 있었습니다.
단계별 작업을 한 사람 씩 맞춰서 하기보다는
다른 작업의 검증에 필요한 작업은 live shere같은 기능을 통해 함께 빠르게 작업하고,
거기서 세세히 나누어 보는것도 시간확보에 있어 좋은 방법같습니다.

 

 

팀원 - 정동현

 

Keep 

모르는 걸 넘어가지않고 적극적으로 물어볼 수 있는 점이 다행인 부분이라고 생각함.

 

Problem 
특별히 문제되는 부분을 느끼지 못했다.

 

Try
부족한 부분에 대해 강의를 다시 들어보고, 보완하여 최종프로젝트에 진입하는 것이 해결책이라고 생각함.

 

CH5 개인과제가 마무리 되었다. 

오늘은 해당하는 과제의 내용을 기술하며,

 

자연스럽게 과제 중에 발생한 트러블에 대한 내용도 TIL로 작성하려고 한다.

 

 

DB 동기화

DB 설정

 

지금까지 prisma 를 사용하거나 또는 직접 DB를 수정했다면

이제는 .query 메소드를 사용하여 접근한다.

 

 

 

updateUserLocation 함수가 실행되면, query 메소드를 통해 

해당하는 DB의 데이터를 수정할 수 있다.

 

 

query의 첫번째 인자로 쿼리문이 들어가며, 두번째 인자로 ?에 들어갈 변수를 넣어주면 된다.

(꼭, ? 와 인자를 잘 대칭되게 맞춰주자 !)

 

 

위 updateUserLocation 이 실행되는 타이밍은...

 

클라이언트가 'end' 이름으로된 패킷을 보냈을 때, 

 

 

onEnd 를 통해 removeUser가 실행된다.

원래 removeUser 는 유저의 접속이 종료되었을 때 

세션에서 유저 데이터를 날리는 함수였는데,

 

 

같은 타이밍에 updateUserLocation 을 호출하여 자연스럽게 

세션에서 삭제 및 종료 좌표를 저장하게 된다.

 

 

문제

그래서 이걸 어떻게 써먹을까?

 

해당 좌표를 기록하는 이유는, 클라이언트에서 접속 기록이 있는 device_id 로 접속했을 때,

이전 종료 시 기록했던 위치에서 재접속이 되도록 하기 위함이었다.

 

 

 

위에서 본 것처럼, DB 상에 기록은 잘 되고있고,

 

 

 

접속할 때, 해당 정보를 클라이언트에게 보내는 코드 또한 이미 작성했다.

 

 

이제 클라이언트에서 받아서 적용해야 하는데.....

 

클라이언트 쪽은 아직까지는 너무 생소하기 때문에 많이 헤매게 되었다.

 

 

이미 빌드된 클라이언트를 받았는데, 수정을 해야한다는 것 !!

 

우선, 수정을 위해 배포된 코드를 다시 가져와서 작업을 시작했다.

 

이...게.. 어느 나라 언어람

 

너무나도 생소한 코드지만, 강의에서 설명한 내용을 바탕으로 코드를 수정했다.

 

패킷을 받는 부분을 발견 !

 

이제, Player.cs 로 들어가서 위치 동기화를 하는 부분에,

최초 한번만 targetPosition 으로 좌료를 설정하게 만들면 끝이다.

(라고 말은 했지만 엄청 헤맸다)

 

 

targetPosition은, 서버에서 받은 x ,y 좌표를 Vector2 클래스를 이용해서 좌표화 시키는... 그런 녀석이라고 한다.

 

 

사실, 위에 부분보다 힘들었던건.

서버때와 마찬가지로 패킷을 어떻게 처리하냐는 부분이었다.

당연히, 버퍼로 보냈기 때문에 그대로 처리할 수는 없다.

 

 

우선, 패킷을 핸들러 기준으로 나누는 부분부터 수정하여, Init (최초 실행시) 에 적용되는 부분을 만들고,

Handler.InitialHandler는 기존에 있던 게임 시작 로직과 동일하며,

 

Packets.ParsePayload <>  를 통해, 따로 파싱이 적용되도록 만들었고,

 

 

 

해당 부분은 Packet.cs 의 마지막 부분에 처리하는 함수를 따로 만들어 주었다.

 

접속 종료했던 위치로 다시 접속한 오른쪽 캐릭터....!! 

그림만 보면 잘 모르겠지만 아무튼 잘 작동한다.

 

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

24.11.12 TIL 타워 디펜스 온라인  (1) 2024.11.12
타워 디펜스 온라인 - 회고  (0) 2024.11.12
24.10.24 TIL 삼각함수  (0) 2024.10.30
24.10.24 TIL 위치 동기화  (0) 2024.10.24
24.10.23 TIL 기본 세팅  (0) 2024.10.23

 

삼 각 함 수

 

아아.. 결국 수포자인 내가 삼각함수를 배워야 하는 순간이 왔다.

 

챌린지반의 수업 중, 발표 자료를 준비하라는 것이 출발이었지만,

언젠가는 배워야 할 것이었다. 

 

따라서, ppt 에 정리한 내용을 바탕으로 TIL을 작성하였다.

 

 

 

 

수학 지식이 전무한 (흑흑) 나는 삼각함수를 배우기 전에 삼각형 부터 배워야한다...

 

 

초 등 학 생 

삼각형이란, 

3 개의 직선으로 이루어진 도형으로,

세 내각 의 합이 180도 이다 !

 

 

 

다음으로는 , 삼각비를 알아야 하는데,

앞으로 등장할 삼각형은 모두 직각 삼각형으로 설명한다는것을 기억하자.

 

 

 

 

중 학 생

 

킹무위키

 

(마지막으로 본게 20년은 된거같은데..)

 

삼각비는 , 위에 서술한 것 처럼, 삼각형의 두 변을 묶어서 비율로 나타낸 것이다.

 

여기서,

빗변이란, 직각 삼각형에서 가장 길이가 긴 변을 말하며,

직각을 이루는 두 변을 제외한 나머지 하나의 변이 된다.   = 직각과 마주보는 변

 

특정한 각 A를 기준으로 직각을 이루는 쪽으로 가는 변이 밑변이 되고,

빗변과 밑변을 제외한 남은 하나의 변을 높이라고 표현한다.

 

주의사항으로,

언뜻 보기에 가로로 되어있다고 밑변, 세로로 되어있다고 높이 라고 하는것을 틀릴 수 있음에 주의 !!

 

 

추가적으로 tan A = sin A / cos A 로 표현할 수 있다. 

 

(높이 = sin A * 빗변의 길이)

(밑변 = cos A * 빗변의 길이) 

 

 

 

 

특수각은 간단히만 정리하고 넘어가도록 하자.

 

비교적 깔끔하게(?) 삼각비를 표현 할 수 있는 각들을 특수각 이라고 부른다. 

 

경우에 따라 120 도, 150 180 210 ... 막 아무거나 집어넣는 모양인데...

 

우리는 0 30 45 60 90 도 만 기억하면 된다.

 

tan 30도를  루트3 /3 으로 표현하고 있는게 마음에 안들기 때문에,

1 / 루트3 으로 표현하는 쪽이 가독성이 더 좋다.

 

 

 


본격적으로 삼각함수를 들어가기....전에 마지막으로 호도법을 알아보자

 

호도법은 각도를 표현하는 방식중 하나이며,

깊이 들어가면 내용이 엄청 심오한듯 보이기 때문에, 필요한 부분만을 우선 이해하자. (수학은 늘 이런식이야)

 

 

우리가 평소에  사용하는 각도는 

육십분법 에 해당하며 , 한바퀴의 회전을 360도   로 표현하는 방식이다.

 

 

그렇다면, 호도법은 위에서 나온 바와 같은데,

 

기억해야할 것은 180도 =  𝝿 (3.141592.......) 라는 것 !

 

왜 또 굳이 무리수인 파이를 가져와서 정의를 내린것인지 의문이 들지만

 

정의를 읽어도 이해가 안됨

 

 

기존 육십분법의 각도와 달리 

어떻게든 단위를 제거하고 숫자로 각도를 표현하기 위해 만든 표현법이라고 보면 될 듯 하다.

 

반지름은 직선... 호는 곡선...

 

이게 대체 뭐람

 

깊이 들어가려면 심연을 봐야 하는 느낌이 강하게 들기 때문에,

일단 호도법 => 180도 = 𝝿  하고 넘어가도록 했다.

 

 

 

 

자.. 그럼 이제 삼각함수이다.

 

 

 

고 등 학 생

 

 

고등학생이 된 우리는

이제부터는 삼각형을 그리기 전에 십자가 + 를 먼저 그려야한다.

 

이 말은, 좌표가 생겼다는 말이고, 

좌표를 통해 마이너스의 값을 표현 할 수 있게 되었다.

 

마이너스 좌표의 삼각형을 그리는 것으로 

우리는 세타 가 90도가 넘는 (둔각) 삼각 함수의 값을 구할 수 있게 된 것이다 !!!!!!!

 

사분면에 관한것은 중학교 과정에 있다. 

삼각 함수도 당연히 위와 마찬가지 이다.

 

 

크게 어려운 내용은 아니지만,

쉽게 이해하려면 아래의 그림을 보자...

 

 

 

(0, 0)을 원점으로  r = 1 인 단위원을 양의 방향으로 그린다면

그림과 같은 형태를 볼 수 있다. 

 

이걸 움직이는 그림으로 볼 수 있다니... 세상이 많이 좋아졌다 !!

 

보는것 처럼, 임의의 P 좌표의 위치에 따라 

sin 세타와 cos 세타가 양수와 음수의 영역을 넘나드는 것을 볼 수 있고,

이를 그래프로 표현하는것 또한 확인할 수 있다.

 

위 그래프 에서는 한바퀴 (360도 = 2파이) 에 해당하는 지점만 표기되어 있지만,

 

 

위 처럼 0도, 90도, 180도, 270도 마다 호도를 표기하여 본다면

어느 사분면에 위치한 삼각 함수 인지를 좀 더 쉽게 파악할 수 있다.

 

 

오늘은 여기까지... 

'내일배움캠프' 카테고리의 다른 글

타워 디펜스 온라인 - 회고  (0) 2024.11.12
24.11.01 TIL 멀티플레이 과제 트러블슈팅  (0) 2024.11.01
24.10.24 TIL 위치 동기화  (0) 2024.10.24
24.10.23 TIL 기본 세팅  (0) 2024.10.23
24.10.18 TIL 버퍼 객체  (0) 2024.10.18

 

 

 

오늘 학습한 내용 모두 중요하지만,

 

가장 어렵고, 중요하다고 생각하는 부분은 위치 동기화 이다.

 

 

 

우선, 레이턴시 ( Latency ) 란?

하나의 데이터 패킷이 출발지에서 출발하여 도착지에 도착할 때 까지 걸리는 시간을 말한다.

 

특히 게임 서버에서 현재의 기술력으로는, 무시하기 힘든 레이턴시가 발생 할 수 밖에 없다.

 

따라서, 우리는 이 레이턴시를 적절히 숨기거나, 피하는 기술을 배워야 한다 !

 

 

이러한 레이턴시를 유저가 느끼지 못하게 만드는 기술을 레이턴시 마스킹 이라고 부른다.

 

 

 

강의에서는 추측 항법에 대해서 말해주고 있다.

 

아래에 나올 이미지를 통해서 이해가 빠르게 되었기 때문에

이미지를 가져오도록 하겠다 !

 

기본적으로, 레이턴시가 곧이 곧대로 반영되는 상황에서

서버가 실시간 동기화를 시도한다면,

유저에게 보여지는 움직임은 위와 같이 표현할 수 있다.

글로 표현하면 순간이동을 하며 뚝뚝 끊겨 보일것 이라는 것이다.

 

 

 

선형 보간은,

밀려서 뚝뚝 끊어질 바에 아예 뒤로 밀어서 시작하는 것이다.

끊겨 보이는 현상은 없겠지만, 다 같이 느린 반응속도를 갖게 될 것이다.

 

 

 

 

 

대망의 추측 항법은,

레이턴시의 지연시간 만큼을 미리 계산해서 전송하는 방법이다.

가장 실시간 동기화에 부합하는, 한계를 극복하려는 노력이 보이는(?) 방법인 듯 하다.

 

쉽게 생각하면,

만약 지연시간이 1초로 고정이 되어있다는 가정을 해 보자.

 

이 1초 만큼을 뒤로 밀어서 반영하기 시작하는것을 선형 보간,

 1초를 미리 당겨서 반영하면 추측 항법이라는 것이다.

 

 

 

강의에서 소개한 방법을 적용한 코드 !!

 

각각 다른 레이턴시를 임의로 적용한 뒤, 

user 의 이동속도 =1 로 계산하여 

데이터를 미리 반영하는 추측항법의 결과를 보여주고 있다.

'내일배움캠프' 카테고리의 다른 글

24.11.01 TIL 멀티플레이 과제 트러블슈팅  (0) 2024.11.01
24.10.24 TIL 삼각함수  (0) 2024.10.30
24.10.23 TIL 기본 세팅  (0) 2024.10.23
24.10.18 TIL 버퍼 객체  (0) 2024.10.18
24.10.17 TIL 검증이 뭘까  (0) 2024.10.17

 

 

첫 주차에 간략히 배웠던 TCP를 본격적으로 시작한다.

 

 

오늘 학습한 내용은 위와 같으며,

 

강의 하나 하나의 난이도가 상당하다. 

 

하나에 20분 내외로 끝날 강의가 맞을까 의심이 되는데, 

그럴것도 없이, 추가적인 학습을 유도하는 강의로 보인다.

 

 

시작은 디렉토리 구조 부터 !

 

지난 과제의 디렉토리보다 세분화되어 복잡해 보인다.

아니, 익숙하지 않다고 하는게 더 맞는 말 같다.

 

server.js 를 시작으로

 

무려 Node 의 기본 모듈

net 을 이용하여 TCP 서버를 생성하고

 

 

 

 

initServer 를 통해 본격적으로 서버가 구동된다.

 

 

기본적인 구조는 socket 서버의 형태와 크게 다르지 않지만,

 

 

데이터를 주고 받을때는 반드시 buffer 의 형태 여야 한다는 것 !!

 

때문에, onData => 데이터를 받았을 때

당연히 buffer 형태로 왔을 것이므로, 해당 buffer를 다시 해석하는 과정이 필요하다.

 

 

에서 끝나면 사실 그렇게 복잡할 일은 아니다...

 

 

 

 

 

 

버퍼가 늘 출발한 순서대로,

예상한 시간에 도착하여 딱 딱 완성된다는 보장이없다.

 

따라서, while 반복문으로 헤더에 담긴, 오기로 한 길이만큼 올 때 까지 기다려 주는 상황을 만드는 

코드가 필요하다.

 

버퍼들이 도착하여 이미 약속된 패킷의 길이가 될 때 까지 기다리는 것이다.

 

 

에서 끝나면 사실 그렇게 복잡할 일은 아니다...

 

 

해당 버퍼를 처리하려고 할 때, 추가적인 다른 버퍼들이 이미 도착해 있을 수 있다.

따라서, header 에 담긴 length 만큼만 buffer 에서 잘라서 처리하고, 

나머지 부분은 다시 buffer 에 넣어버리는 코드를 작성한다.

 

 

 

여기가 3-3에 해당하는 내용인데,

이번 과제는 시작조차 쉽지가 않을것 같다.

 

'내일배움캠프' 카테고리의 다른 글

24.10.24 TIL 삼각함수  (0) 2024.10.30
24.10.24 TIL 위치 동기화  (0) 2024.10.24
24.10.18 TIL 버퍼 객체  (0) 2024.10.18
24.10.17 TIL 검증이 뭘까  (0) 2024.10.17
24.10.16 TIL 타워 디펜스 프로젝트 -완-  (0) 2024.10.16

+ Recent posts