4. 전송 계층

 

 

 

앞서 배웠던 물리 계층, 데이터 링크 계층, 네트워크 계층으로만 구성이 되어도

데이터의 전송이 가능합니다. 그것도 최종 목적지 까지요 !

 

그러면 이름부터 '전송' 계층인 4계층은 무슨 역할을 하는걸까요?

 

 

전송 계층은,  데이터의 전송간에 패킷의 손실이나 오류 없이 올바른 순서로 도착하게 해주는 역할,

필요한 경우 데이터 패킷을 원활하게 복구하는 역할을 합니다.

 

쉽게 말해 ' 흐름 제어 ' 와 ' 오류 제어' 를 위한 계층이다! 라고 할 수 있습니다.

 

 

전송 계층하면, 빠질 수 없는 내용이 있는데,

그것은 전송계층의 프로토콜인 TCP 와 UDP 입니다.

 


TCP 란?

Transmission Control Protocol 

안전 제일 ! 

TCP는 데이터 전송간에 손실이 거의 발생하지 않는 프로토콜 입니다.

TCP의 통신 과정은 다음과 같습니다.

 

1. 데이터 스트림에서 받은 데이터를 일정 단위로 분할합니다 .

2. 분할된 데이터 단위에 TCP 헤더를 붙여서 TCP 세그먼트를 생성합니다.

3. TCP 세그먼트를 IP 데이터그램으로 변환합니다. 

4. IP 데이터그램을 수신 어플리케이션에 보냅니다.

 

여기서 세그먼트 ! 는 

TCP 가 데이터를 전송할때 사용하는 데이터의 크기 를 말합니다.

 

TCP의 데이터 변환 (세그먼트)

해당 이미지는 데이터를 일정 크기로 쪼개서 header 를 붙여서 세그먼트로 만드는것을 표현합니다.

 

 

이전에 배웠던 

비트  bit  => 1 계층

프레임 frame  => 2계층 

패킷 packet => 3계층

과 같이, 4계층 중 TCP 에서의 데이터 단위는 세그먼트 라고 할 수 있습니다.

아래 나올 UDP 의 경우는 datagram 단위를 사용하기 때문에,

같은 4계층이지만, TCP와 UDP는 서로 다른 데이터 타입을 사용합니다.

 

 

첫 번째 특징은 , A/S 가능!  데이터의 전송이 실패한 경우, 재 전송을 시도합니다.

 

두 번째 특징은 , 고객 맞춤형 서비스로 수신자의 용량에 따라 데이터 전송 속도를 최적화 합니다.

 

세 번째 특징은, 불편한데는 없으시고요?   데이터를 전송할 때, 오류를 검사해 데이터가 

목적지에 온전하게 도달하도록 보장합니다. 

 

네 번째 특징은, 살짝 느립니다. 바로 아래 나올 UDP 와 비교하면 말이죠.

 

위와 같은 특징들은 TCP 가 연결형 방식인것을 나타냅니다.

수신자의 상태를 계속 확인하고 개선하려고 합니다.

 

UDP란?

User Datagram Protocol

속도 제일 !

UDP 는 사용자 데이터 프로토콜의 약자로, 안전성보다는 빠른 속도를 추구합니다.

그 과정에서 데이터가 손실될 가능성 또한 있습니다. 

왜냐하면 비 연결형 방식이기 때문에 수신자의 상태를 고려하지 않고

일단 자기 방식대로 다 보내고 보는겁니다.

UDP 에서는 데이터 단위를 세그먼트로 쪼개지 않습니다.

그저 header 만 붙여서 보냅니다. 이렇게 data에 header 만 붙인 형태의 데이터 단위를

Datagram 이라고 합니다.

 

위와 같은 이유로 UDP 는 불특정 다수에게 데이터를 전송하는데 유리합니다.

TCP 같았으면, 일일이 데이터를 잘 받고있는지 계속 통화상태를 유지하려 하지만,

UDP는 비 연결형 방식이기 때문에, 대상이 불특정 다수여도 그냥 보내기만 하기 때문에

훨씬 효율적 이라는겁니다.

 

이와같은 UPD 의 특징을 브로드 캐스트 라고 합니다 !

UDP 를 사용하여 LAN 에 있는 네트워크 장비에 데이터를 일괄적으로 보낼 수 있습니다.

 

물론 단점도 존재합니다.

 

전송 과정에서 데이터가 손상되어도 A/S 를 해주지 않습니다.

데이터를 받는 입장에서는 받은 데이터가 온전한 상태인지도 당장은 알 수 없습니다.

 

데이터의 전송 순서를 특정하지 않기 때문에, 도착하는 순서 또한 알 수 없습니다.

 

이러한 UDP 의 특징을 가장 잘 나타내는 활용처는

실시간 영상 전송으로,

데이터가 약간 손실된다 하더라도 크게 문제되지않고, 다수에게 빠르게 데이터를 전송할 수 있기 때문에

UDP 방식이 많이 사용됩니다.

 

 

 

 

 

 

내일 점심시간 전까지 과제를 제출해야 하기 때문에, 사실상 오늘이 마지막 날이다. 

 

 

 

도전 과제의 기능까지 구현은 모두 완료된 상태이다.

모아 놓고 보니까 API 의 수가 꽤 많은것을 확인 할 수 있다.

 

뭐 하나 쉬운게 없었다 !!

 

 

최종적으로 과제 제출을 위해선, 

 

 

마무리 단계인 AWS 배포까지 진행해 보도록 하자.

 

윈도우 환경이기 때문에 Git Bash 로 진행했고, 

EC2 구성은 이전에 강의를 듣고 중지시켜놨던 인스턴스를 다시 활성화 시켜서 사용하기로 했다.

 

git을 통해 파일을 받아올 것이기 때문에, gitignore 설정이 잘 되어있는지 확인하고  push 해놓자.

 

.env 파일이 같이 업로드 된다면 대여한 서버에서 무슨일이 생길지 모른다.

 

그렇다면 업로드 하지 않은 .env 는 어떻게 ubuntu 에게 보낼까?

 

 

검색 결과, vim 을 통해서 .env 를 생성하거나 수정 할 수 있다는 사실을 발견 !

 

 

불편하기 짝이없는 linux 환경을 잠시나마 경험할 수 있었다.

 

:wq 를 통해 해당 작업을 저장하고 종료할 수 있다.

 

이제 실행만 app.js를 실행하기만 하면 !

당연히 안된다. 구동에 필요한 패키지를 설치해야 하기 때문이다.

 

yarn  명령어를 통해, 패키지를 설치 하면 본격적으로 

EC2 의 IPv4 주소와 포트를 통해, 배포가 가능하다 !

 

insomnia 를 이용한 요청과 응답이 정상적으로 이루어지는 모습이다.

 

예상치 못한 난관이 있을것만 같았지만, 큰 어려움은 없었다.

방법을 깨달았으니

 

남은 시간동안은 다시 코드를 재정비하고, 업로드 후 재설치하고 배포할 시간이 될 듯 하다.

 

 

다음 내용으로, 

과제 내내 나를 괴롭혔던 부분에 대한 해답이 밝혀졌다 !

 

예상했던대로, header 를 통해서 토큰을 발행하면

클라이언트가 해당 토큰과 함께 요청을 전달해 주는것이 맞는 내용이었다.

 

로그인을 통해, jwt 를 발행하고 전달하는것은 cookie와 크게 다르지 않지만, 

 

 

해당 토큰은 insomnia 기준으로 authorization Headers 를 통해 클라이언트에 발행하고,

 

 

클라이언트는 서버의 인가가 필요한 사항에 대해서, Auth 에 TOKEN을 담아서 서버에 요청을 보내게 된다.

 

요청하며 보낸 토큰이 비정상적이라면, 당연히 정상적인 응답을 받을 수 없다.

 

 

계속 마음에 걸렸던 부분을 해결한 것 같아 한결 편안해졌다.

나머지  미흡한 부분도 많지만, 벽 처럼 느껴졌던 과제를 어느정도 해결 해 낸것 같아서 다행이다.

 

 

 

 

매일 1시간씩 코드 카타 시간을 갖는데, 이 문제를 푸는데 3일이 소요됐다.

 

문제를 이해하고, 어떤 방법으로 구현할까 생각했는데

 

keymap 에 해당되는 문자열을

 

{A : 1, B:2, C: 3} 과 같은 형태로 만들어 키와 밸류를 이용해 문제를 풀어나가면 

될것 같다는 생각을 했다.

 

검색을 통해 알아 본 결과, 위와 같이 배열의 데이터를 

키와 밸류로 각각 나누어 객체 형태로 저장하는 방법을 해시(Hash) 라고 한다 !

 

배열과의 차이는,

 

배열은

0~ 으로 시작하는, 순번을 알려주는 index 와  index의 값인 요소(element) 로 구성된다면,

 

객체는

임의의 key : 임의의 값 value  로 구성이 되기 때문에, 

 key 에 해당하는 부분에 숫자는 물론 문자열 또한 들어갈 수 있기 때문에 자유롭다 !

 

이 둘의 차이는 마치,

관계형 데이터 베이스 (배열) 와 비관계형 데이터 베이스 (객체) 와 비슷하다고 생각된다.

 

일반적으로 JS 에서는 해시 형태를 만들기 위해 그의 단짝인 Map이 사용된다.

 

 

 

라고 하는 내용을 찾을 수 있었으나, 

 

한번에 이해하고 사용하기엔 어려웠기 때문에,

일단은 원시적인 방법으로 해시를 구현해보고자 했다.

 

 

여기까지 오기가 가장 힘들었다

 

객체로 선언된 keykey에, 이중 반복문을 통해

key에 해당하는 keymap의 문자열, value에 해당 문자열의 index 를 넣는 방식으로 만들었다.

 

이것은 사실상, map() 이 작동되는 방식을 반복문으로 표현한 것과 같다.

 

이제, targets 을 순회하며 일치하는 값을 더해주기만 하면 된다.

 

 

마찬가지로, 이중 반복문을 통해 몇 번째 index 에 있는 몇 번째 문자열인지를 참고하여

순회한다. 만약 key에 없는 문자를 한번이라도 마주친다면,

작성이 불가능한 문자열 이므로 -1을 반환하고 다음 i로 넘어간다.

 

이 때, break 를 하지 않는다면

sum= -1 이 되었지만  남은 j 반복문을 계속 진행하여 -1 에서 값이 변경될 여지가 생기게된다.

 

이 부분을 놓쳐서 꽤 긴 시간을 낭비했다.

( 기본으로 주어지는 테스트 케이스 에서는, sum= -1 이후에 변동되는 케이스가 없기 때문에 )

 

이를테면,

임의로 추가한 위의 테스트 케이스 에서,

2번째 targets 배열인 DADFA 를 보면, 

F가 작성이 불가능한 문자열 이므로 sum = -1 이 되었지만, break 가 없다면

F 다음 index인  A로 넘어가면서 A= +1 의 값을 받아 sum= 0 이 되어버리는 문제가 생기는 것이었다.

 

 

break ;  적용 이후로, 실패했던 테스트가 통과되며 정답처리가 되었다. 

 

 

 

 

어제 고민했던 캐릭터 검증 코드를 미들웨어로 만들기로 했다. 

 

문제는, 실제로 미들웨어를 어떻게 작동되게 하는지 모르고있다는것...

미들웨어에 대한 개념 자체가 부족한 상태이다.

 

그 결과.....

 

해당 미들웨어를 갑자기 API 본문에 함수호출 형태로 작성을 하는 사태가 벌어졌다.

 

굉장히 민망한 상황이지만, 이게 잘못된 방법이라는 것은 바로 느껴졌기 때문에

미들웨어에 관한 학습을 다시 진행한 결과,

 

 

해당 형태로 순차적으로 미들웨어가 실행될 수 있게 하였다.

민망했던 위의 코드와 차이점을 살펴보면, 

 

처음 작성한 코드는 미들웨어(async 핸들러) 내부에서 호출되었기 때문에

return 을 반환 하더라도 다음 코드가 '당연히' 실행된다.

 

하지만,

아래에 작성한 코드는, 호출된 미들웨어가 next()를 받지 못하고  return 된다면

결국 => { } 의 내용으로 넘어가지 못하고 그대로 종료된다.

 

추가로 !

CharacterAuth(req, res);  형태는 그냥 함수의 호출이기 때문에 아예 잘못된, 완전 틀려먹은 접근이라고 생각했는데,

미들웨어 자체가 함수의 호출과 비교해서 크게 다를바가 없기 때문에, 호출한 위치가 잘못된 것이고 !

함수로서 호출하는 발상은 100% 틀려먹은 생각인것은 아니다 ! 라는 피드백을 받을 수 있었다.

 

 

 

다음으로 구현한 API는, 도전 기능에 해당하는 장비 장착 API 이다.

뭔가 많은 과정들이 SKIP 된거 같긴하지만...

 

해당 과제,  그것도 도전 기능의 BOSS 에 해당하는 API 라고 생각되는 부분이다.

 

 

API요구 사항은 위와 같다.

해당 내용을 토대로, 구현하기 전에 구상을 먼저 해보면,

 

우선, 해당 API를 실행하기 위해서는 !!!

 

로그인 인증을 거쳐야 하며, > login DB 조회

param으로 전달받은 characterId 도 검증해야하고 > character DB 조회

인벤토리의 아이템을 장착해야 하기 때문에 > inventory DB 조회

장착한 아이템의 능력치를 참조해야 하기 때문에 > item DB 조회

모든 테이블이 필연적으로 한 번씩, 조회될 수 밖에 없다.

 

그리고, 장비의 장착으로 인한

캐릭터의 능력치가 update, 아이템 상태의 update 또는 delete 등

데이터베이스의 수정이 여러번 발생하게 되기 때문에, 트랜잭션 구성을 생각해 둘 필요가 있다.

 

 

첫 코드부터 난관이었다. 

 

이유는, 장착 대상이 된 인벤토리의 아이템을 특정해야 하는데, 

characterId 와 itemId 로는 정확한 inventoryId 를 특정할 수 없다.

같은 캐릭터가 동일한 아이템 여러개를 인벤토리에 보유할 수 있기 때문이다.

11 캐릭터가 13 아이템 2개를 가지고 있다

제시한 두 가지의 정보로만 검색을 하면, 복수의 값이 결과로 나올 수 있다는 것이다.

어떤 아이템을 장착해야 할 지 

 

해결방안

1. 인벤토리의 PK에 해당하는 inventory_id 를 추가로 입력받아 진행한다.

-> 과제 요구사항에 벗어나며, 별로다 !!

 

2. 딱히 쓸데 없었던 inventory_number 라는 컬럼을 활용해 보자.

--> 예상되는 문제점이 있으나, 현 상황에선 큰 문제는 아니기 때문에 진행 !

 

 

 

사실, 위에 올린 이미지가 inventory_number 를 활용한 이미지 이다.

여기서, inventory_number는, 어떤 아이템이건 획득 당시에 

 

해당 캐릭터가 보유한 아이템들 중, 가장 높은 inventoryNumber +1 의 값을 부여한다. 

 

본래 의도는 가장 최소 값을 찾아 들어가게 할 예정이었으나, 

생각보다 로직이 복잡하고, 시간이 급박하기 때문에

 

일단은, 임시로 같은 캐릭터 id 내에서 중복되지는 않는 값을 갖도록 구현을 해 놓았다....

때문에 그다지 의미있는 컬럼이 아니게 되었지만,

결과적으로, 현재는 해당 값이 가장 작은 item을 지정하는 방향으로 구현이 된 상태다.

 

 

추가로,

이미 장착중인 아이템 중에,

위에서 특정한 아이템과 같은 타입인 것이 조회될 경우는 

에러를 리턴하도록 하고, 해당 사항이 없다면

 

 

 

아이템DB를 조회하고, 해당하는 능력치를 올려주는 update 문을 작성해서 마무리 했다.

 

일단 작동은 잘 되고 있기에, 여유가 될 때에 구상해뒀던 트랜잭션 부분이나, 

inventoryNumber 가 최소값을 찾아 들어가게 하는 방법을 적용할 예정이다.

 

 

 

 

네트워크 계층이란?

 

OSI 7계층 중, 3계층에 속하는 내용으로, 망 계층 이라고도 불립니다.

 

네트워크 계층의 대표적인 역할은,

라우팅(Routing) 과 포워딩(Fowarding) 입니다.

 

 

라우팅은

길 찾기에 해당하는 내용이며, 네트워크 계층 내에서 Control Plane 으로 나누기도 합니다.

출발지에서 목적지 까지의 경로 중, 가장 효율적인 경로를 탐색하는 것을 말합니다.

 

포워딩은

네트워크 계층 내에서 Data Plane 으로 나누기도 하며,

최선의 경로를 따라서 실제로 데이터를 전달하는 기능을 말합니다.

 

 

데이터 링크 계층과의 차이

데이터 링크 계층은 인접한 두 노드 간의 전송을 담당하는 것이고, 

네트워크 계층은 전체 네트워크 상에서 최종 수신지 까지의 전송을 담당합니다.

 

 

 

IP (인터넷 프로토콜, Internet Protocol)

목적지 까지 데이터를 전달하는 기능을 수행하고, 주소를 관리하는 기능을 수행합니다.

IP를 통해서 포워딩이 이루어진다 라고 볼 수 있습니다.

IP 주소 라고 불리는 인터넷 상의 고유 식별 번호를 통해 실제 데이터의 출발지와

목적지 등 각각의 주소(위치) 를 정의 할 수 있습니다.

 

 

 

DHCP (Dynamic Host Configuration Protocol)

클라이언트에게 동적으로 IP 주소를 할당하는 방법을 제공하는 프로토콜 입니다.

 

DHCP 의 특징

-주소의 대여시간 갱신 (연장)

-동일한 주소의 재 사용 가능

-네트워크에 접속하고자 하는 이동 사용자 지원

 

 

 

NAT ( Network Address Translation)

라우터 등의 장비에서 사설 IP를 외부IP 로 변환하는 기술입니다.

 

만약, 네트워크 내에 엄청나게 많은 호스트가 있다면, 호스트 마다 IP 주소를 할당하는데에는

너무 많은 IP 주소가 필요할 것 입니다. 하지만 실제로는 외부 인터넷이 내부 인터넷에 존재하는

모든 호스트에게 IP 주소를 직접 할당 해주는 것이 아닌, 라우터에게 IP 주소를 할당해줍니다.

결국 라우터 내부의 네트워크는 사실상 모두 같은 IP 주소를 사용하는것 보일 수 있습니다.

 

하지만, 내부 네트워크에서만 사용할 수 있는 또 다른 IP 주소를 부여받게 되고 이를 사설 IP 라고 부릅니다.

 

결과적으로, 내부 네트워크에 존재하는 사설 IP를 외부 IP 로 변환 및 매핑시켜주는것이 NAT 라고 할 수 있습니다.

 

 

월요일이되고, 가장 먼저 캐릭터 접속 authorization 을

추가로 만든것과 관련한 의견을 튜터님께 물었다.

 

 

주말 간 진행한 내용으로,

결론만 말하면, 결과는 폭망이다.

 

오늘은 그 과정에 대한 내용을 서술하도록 하겠다.

 

로그인만 인증하는 기존의 authorization (미들웨어) 에 더해서 

로그인 + 캐릭터 인증까지 검증하는 authorization 를 추가로 만든 것이고,

 

이렇게 만든 이유는,

1. 캐릭터 특정이 필요한 상황 (아이템 획득, 구매 등등) 에서 매번 캐릭터id를 param으로 받지 않아도 된다.

 -> 캐릭터 인증 (접속) 을 통해 해당 캐릭터 id를 고정할 수 있음

 

2.  param으로 캐릭터Id를 받을 경우 매번 해당 캐릭터Id 가 로그인한 계정에 포함이 되어 있는지 

검증을 해야하는데, 그 과정이 캐릭터 접속API에 포함되며, 

이후로는 JWT 복호화를 통해 검증함으로써 보안에도 유리함.

 

 

단점을 따지면,

 

1. 불필요한 코드일까 ? or 잘못된 접근 방식일까? 하는 막연한 생각 정도?

 

 

결론은

단호하게 '안된다' 였고,

해당 코드를 無로 돌릴것을 권장하셨다.

 

물론, 최대한 부드럽게 말씀해주시긴 했지만,

뭔 쓰잘데기 없는 코드를 만들어왔어? 라고 생각하셨음이 ' 제 6의 감각' 으로 느껴졌다...

 

코드 복잡성도 늘어나고, 불필요한 실행도 늘고, 유지보수에 대한 문제 등등을 지적하셨다.

 

 

납득이 되지 않았다.

 

 

과제에서 요구한 방식인 params 로 캐릭터id를 받아도

기존 JWT인증 + 로그인계정에 있는 캐릭터Id가 맞는지에 대한 검증은 해야하기 때문에,

캐릭터Id params를 받는 모든 API에   로그인계정에 있는 캐릭터Id인가- 에 대한 검증 코드가 필요하다.

 

당연히, 그런 API 는 다수 일 것이기 때문에, 동일한 내용을 복붙을 하거나, 

해당 캐릭터 인증만 하는 미들웨어를 어차피 만들어야 할 것이다.

 

 

실행할 것이 늘어난다? 

1의 내용은 어차피 실행되어야 할 내용 두가지를 합쳤을 뿐이고,

추가적으로 캐릭터 접속 API를 따로 진행해야 한다는 것인데,

그것이 즉시 폐기 라는 대답이 나올 정도로 불필요한 일인지 모르겠다.

 

오히려 캐릭터 접속API는 캐릭터 검증을 위한 데이터 베이스 조회를 한 번만 하고, 

이후로는 JWT 복호화를 통해 진행하므로, 코드 복잡도 면에서 오히려 유리하다.

==> 24.09.10 일 내용 정정

복호화로 얻은 정보를 바탕으로 find를 실행하기 때문에 데이터 베이스 조회 횟수는 같다.

============================================================================

 

심지어 JWT 복호화는 추가적인 작업이 아니라 기존에 계정Id를 복호화 하던것에 더해진 것이기 때문에,

이부분도 크게 차이가 없다.

물론, JWT 코드 자체가 복잡해질 수 있는 문제는 있다.

 

 

 

내가 놓친 부분이 있을까?  

별다른 수가 없다.

일단 피드백 받은 내용대로 갈아엎은 결과와 지금을 비교하는게 최선일듯 하다.

 

 

 

우선 만들어 놓은 아이템 획득 API를 기존의 요구 방식대로 바꿔보자.

 

위 처럼만 해놓으면, 아무계정이나 로그인해서 모든 캐릭터에 접근 할 수 있으니, 

캐릭터 검증 과정이 필요하다.

 

 

해당 코드를 추가하여, 검증을 하여야 한다.

이 과정에서 character.find 조회 과정은 반드시 필요함.

 

결국, 만들었던  캐릭터 접속 과정은, 

기존 로그인 JWT + 저 위에 코드를 합체한 내용일 뿐이다.

 

캐릭터 접속 할때 동일한 검증을 한다.

 

차이는 캐릭터 접속 API를 이용해야 한다는 점...

이 과정 자체가 문제일까?

 

 

JWT에  추가로 characterId 가 들어가기 때문에 JWT 토큰이 무거워진다는 단점도 있을 것이다. 

 

차라리,

늦게 확인한 것은 안타깝지만 해당부분은 과제 요구사항에서 벗어나기 때문에 안된다.

 

라는 대답이 돌아왔다면 머리가 아프진 않았을텐데...

 

심란하다

 

 

 

그렇게 바뀐 아이템 획득 API 이다. 

 

본인 캐릭터 확인 절차는 따로 미들웨로 만들기엔 좀 짧지 않나 생각도 들고...

그냥 복붙 해서 쓰자니, 유지보수가 어려워진다.

 

해결방안?

 

만약, 캐릭터 검증이 필요한 API 의 경우, _auth 가 더해진 param을 받아서

 

 

기존 로그인 미들웨어에 해당 구문을 추가하여, character_id_auth 를 param으로 받은 경우에만

검증을 진행하도록 했다.

 

물론 이 해결방안도 상당히 짜치는 구조이다.

 

굳이 login 검증만 하던 미들웨어에,  조건 한정으로 캐릭터검증을 하는 일을 부여했으니 말이다.

 

말하자면,

때에 따라 캐릭터 검증도 하는 유능한 login 검증 미들웨어가 아니라,

이걸 왜 나한테 시키지? 라는 상황의 login 검증 미들웨어가 된것에 가깝다.

 

login 검증 미들웨어가 실행 되면, 반드시 character_id_auth 의 존재를 체크해야 하고,

필요없다면 undifined를 정의한채로 넘어가는 불필요한 동작이 생긴 것이다.

 

해결방안의 해결방안?

 

1. 비록 긴 코드는 아니지만, 캐릭터 검증만을 위한 미들웨어를 만든다.

 

폐기된 코드가 떠오르는 방안이지만, 엄연히 차이가 있다.

폐기된 코드 => 기존 A 작업 + 새로운 B 작업 을 합쳐놓은 C작업을 만든것이고,

 

해당 방안은 => 새로운 B작업만 만들고, 필요에 따라 A 와 B를 각각 호출하는 것이다.

 

그런데B가 실행되는 상황에서는 반드시 A도 실행되어야 하는 상황인데..?

그래서 A+B 를 합친 C를 만들었던 것인데....??

 

여기서, 의문이 들었던 부분을 추가로 짚고 넘어가보자..

 

JWT 인증 + param으로 characterId를 받는 상황이라면,

해당 API는 전부 캐릭터 검증을 추가로 해야한다는건 이미 여러번 설명한 내용이다.

그렇게 되면 캐릭터 인증만하는 미들웨어를 추가하던지, API 마다 검증하는 코드를 붙여넣던지 간에,

character 데이터 베이스를 조회해야 한다. API가 실행될 때마다 find를 통해, character Id를 찾아 검증해야 하기 때문이다.

 

폐기된 방법 ( 캐릭터 접속 API를 만들고, 로그인검증+캐릭터검증에 해당하는 미들웨어를 추가) 의 경우,

기존에 있던 로그인 검증과 동일하게, 캐릭터 접속 당시에만 데이터 베이스를 조회하고

문제가 없을경우 JWT를 발급해서, 이후 발생한 검증은 발급한 JWT를 복호화를 통해 진행되기 때문에,

보안적인 면에서도 유리하고, DB조회도 접속시 1회만 하면 되기 때문에 복잡도 면에서도 유리하다.

==> 24.09.10일 정정

DB 조회는 미들웨어에서도 진행하기 때문에 미들웨어 호출당 1회의 조회를 하게 됨

 

또, 한번의 접속으로 캐릭터를 계속해서 특정할 수 있기 때문에,

클라이언트가 API 마다,  캐릭터id 를 param으로 전달해줘야 하는 일도 없어진다.

 

 

나는 틀리지 않았다

라는 말이 하고싶은것이 아니다.

어디가 잘못된건지 아직도 모르는것이 화가난다

 

 

폐기된 코드에 대한 이야기는 그만 하도록 하자.

 다시 다음 방안으로 넘어가서,

 

 

2. 튜터님의 피드백에 해당하는 내용으로 ,

login 검증 함수를 호출할 당시에, 미리 캐릭터 검증을 포함하는지, 안하는지 정보를 줘서

검증이 필요 없을때는 본래의 코드만, 정보가 있을 때에는 추가 코드를 실행하게 만들어서

각각 온전하게 필요한 부분만 작동이 가능하도록 하는 방법 에 대한 내용이다.

 

이 방법은 내가 당장 이해하기엔 어렵지 않을까 생각이된다.

 

 

 

분노의  TIL 을 작성하다 보니, 오늘 진행한 내용은 결국 작성하지 못했다.

 

다음 시간에 !!

 

 

 

 

 

 

즐거운 토요일... 코딩을 시작해보자.

 

오늘의 목표는 인벤토리를 어느정도 구현해보는 것이다.

 

구현에 앞서서...

인벤토리란 무엇일까?

흔히, '가방' 으로 표현되는 인게임 기능으로, 캐릭터가 항상 들고 다니는 

아이템 보관함 같은것 이라고 할 수 있다.

어플리케이션 마다 인벤토리의 형식은 많은 차이가 있지만,

내가 생각하는 인벤토리의 가장 기본적인 기능은,

 

아이템의 소유자를 명시하는 테이블 이라고 보고 있다.

 

가질 수 있는 아이템의 한계치, 혹은 수량의 중첩 등 부가적인 기능들이 있지만,

기본적으로 어떤 아이템을 '누가' 소유하고 있고, 그 소유자만이 해당 아이템을 사용 할 수 있게 하는것이

인벤토리의 가장 기초적인 기능이라고 생각된다.

 

 

 

그것을 중점으로 생각하여 우선 테이블을 만들어 보았다.

 

inventoryNumber 는 아직 어떤 기능을 할 지 모르겠지만 왠지 필요할 것 같기에 일단 넣어둔 것이고,

 

결국 캐릭터의 Id 와 아이템의 Id만 있다면 아이템의 소유를 명시 할 수 있을것이라 생각하고 만들어봤다.

 

해당 인벤토리 테이블은 서버 내의 '모든' 캐릭터가 소유한 '모든' 아이템을 담게된다.

 

때문에, 인벤토리는 동일한 캐릭터가 여러번 들어갈 수 있어야 하며, 

같은 아이템 또한 여러개의 소지가 가능하기 때문에,

itemId 와 CharacterId를 모두 1:N 관계로 설정했다.

 

이를 통해 만들어진 인벤토리의 모습 

 

여기서 한가지 문제점 (한가지 만은 아닐테지만)을 발견했다....

 

 

현재로서는,

캐릭터의 Id를 특정 할 수가 없는 상태이다.

 

왜냐하면 지금 구현된 부분은, 계정 로그인만 하고 캐릭터는 따로 접속하는 개념이 없기 때문에,

계정 외에는 특정할 수 있는 부분이 없다.

 

해결 방안..?

 

1. 한 계정 당 캐릭터 하나로 제한하여 사실상 캐릭터===계정으로 만들어버린다.

    -> 제작 의도와 거리가 멀기 때문에 탈락

 

2. /api/:character_id/ item~어쩌구 저쩌구

   - 캐릭터 id를 params 로 받는 방법.

   -> 거의 모든 api 에서 해당 character_id가 계정내 캐릭터가 맞는지 검사해야한다.

   -> 유저와 서버와 개발자 모두 별로인 방법

 

3. 로그인 처럼 캐릭터 접속 기능을 만든다.

   -> 지금 생각나는 방법 중에선 가장 그럴싸하다.

 

 

당장 해보자. 

계정 로그인 API 와 캐릭터 삭제 API의 형식이 묘하게 합쳐져서 캐릭터 접속 API 가 되었다.

 

하지만, 문제를 해결하려 할 수록 오히려 문제가 늘어가고 있다...

 

접속에 성공했다면, 해당 캐릭터Id를 어디에 저장해야 할까?

 

쿠키를 두개 쓴다? 제대로 참고할 수 있을지도 잘 모르겠고,

모든 매 통신마다 보내는 쿠키가 하나 더 늘어나는것은 좋지 않은데다가,

 

 

해당 과제 요구사항이 뜻하는 것이 아직 제대로 이해가 되지 않기 때문에

어떻게 해야할지 모르겠다.

 

진전이 없기 때문에, 일단은 지금의 cookie 를 계속 사용하고 나중에 방법을 다시 알아보기로 했다.

 

다시 돌아가...자 마자 문제 발생 !!

 

authMiddleware 가 로그인정보 + 캐릭터 정보 모두 검증하게 만들 계획이었지만,

 

캐릭터 접속을 하기 위해서는 로그인이 되어야 하기 때문에  authMiddleware  검증이 필요하다.

 

마치,

자물쇠가 걸린 방 내부에 자물쇠 열쇠가 있어서 못들어가는것 과 같은 상황이다.

 

해결방안

1. 캐릭터 접속까지 authMiddleware 검증 없이 어떻게든 해결한다??

   -> 캐릭터 접속, 생성, 삭제 등 로그인 만 하면 되는 기능들을 전부 직접 검증해야한다

 

2. 로그인 시 임의의 캐릭터 정보 값을 부여하여, 캐릭터 접속을 가능하게 한다.

   -> 임의의  값 일때 캐릭터 접속 이외의 검증은 통과해선 안되기 때문에

        검증을 필요로 하는 모든 API 에 추가 조건을 만들어야 할듯..

 

3. authMiddleware 를 2가지로 나눈다 !! (계정 검증 / 계정+캐릭터 검증) 

  -> 정말 내키지 않는 방법인데 일단 이 방법이 그나마....

 

 

생각해보면 

로그인만 해도 가능해야하는 API (캐릭터 생성, 캐릭터 접속, 캐릭터 삭제) 와 

캐릭터 접속까지 해야 가능한 API 어느정도 분리되어야 하기 때문에,

authMiddleware 2가지로 나누는게 아주 나쁜 선택은 아닐것이다.

( 평일이 되면 관련 내용을 튜터님에게 피드백 받아보자 )

 

어렵진 않으니 만들고보자.

 

그냥 복붙으로 두개로 나눈다음에, 필요한 곳에서 각각 불러오고, 적용하면 끝이다.

 

캐릭터 접속까지 한 경우를 검증하는 connect.auth 에서는

추가로 char 값이 이상할 때 에러를 발생하게 하면 된다.

 

이 작은 차이 때문에 복사를 해야한다고?! 생각이 되긴한데 다른 방법이 당장 떠오르지 않는다..

 

 

 

캐릭터 접속까지 요구하는 API
캐릭터에 접속
접속 후 동일한 API가 잘 처리된 모습

 

 

이 타이밍에..... 엄청난 반전이 있었으니...

 

 

 

 

이럴수가 ...?!?!?!?

 

 

도전기능

에 해당하는 부분에서, 캐릭터ID 를 param 으로 받는다는 내용이 기재되어 있었다...

 

필수 과제만으로 벅찰것 같아서 자세히 확인을 안해본 도전과제에

저런 내용이 있었다니 !!

 

사실 이는 해당 글의 상단에 있는

 

 

2번 해결방안에 해당하는 내용이었다.

그 해결 방안이 과제에서 제시한 방법이었던 것이다.

 

음.. 별로인.. 방법 아닐지도 ? ㅎ

태세 전환

 

 

 

 

 

 

시작에 앞서서, 어제 문제가 됐던 부분을 다시 살펴보자.

 

 

이번에 사용한 방법은, findFirst를 where 까지만 우선 실행해서,

character.userId 를 얻어내는 방법이다.

 

그 다음 한번 더 find 하는 효율적인 작업을 해서, 

Money에 삼항 연산자를 적용하는 방법이다.

 

 

의도한 대로 작동하지만, 조회 한번 하는데 쿼리를 3번이나 불러오는 현상이 목격되었다.

where 로 2번, select 로 한번 불러 오는 듯 하다.

 

 

음...

character.userId 를 참조하는 쉬운 방법이 분명히 있을것 같은데,

상당히 기초적인 것을 놓치고 있는 느낌이다.

그게 아니라면, where 와 select 사이에서 뭔가 해 줄 수 있을것 같은데, 아직 잘 모르겠다.

 

 

 

 

우선 어제 계획한대로, 강의 부터 모두 듣고난 뒤에 

계속해서 과제를 진행해 보았다.

 

 

오늘 진행한 부분은 아이템 관련 부분이다.

 

위의 아이템 생성 API 는, 로그인 없이도 아무나... 만들 수 있으며,

만들어진 아이템은 items에 추가된다. 

 

items 테이블

 

 

 

조회 기능도 만들어 보았다.

마찬가지로 아직 error 잡는 부분 등은 미흡

 

itemId 2의 상세 조회 결과

잘 작동하는 모습 !!

 

오늘 구현한 기능은 많지 않지만, 

강의를 모두 들었기 때문에 다음 주 부터는 조금 더 속도가 날 것 같다.

 

 

 

 

 

 

CH 3 아이템 시뮬레이터 과제 !

 

아직 강의를 다 듣지 못했지만

조금 급한 마음으로 과제를 시작해보았다.

 

내가 할 수 있는게 맞는지 아닌지가 궁금했기 때문이다.

 

 

 

강의를 토대로 만든 결과 나름대로 잘 작동하는 모습이다 !

 

아직 error 처리라던지,  디테일한 DB 셋팅 등등  할 일이 많지만...

 

그래도 못할 건 없겠다 라는 생각이 들어서 안심이 됐다.

 

 

그래서.. 

 

오늘의 문제 !!

 

 

캐릭터 조회 API 만들기 ..

 

우선, params 를 입력받아 해당 캐릭터Id를 조회하게끔 구현을 해 보았다.

 

문제는 캐릭터 조회가 아니라...

 

바로 이 두 줄에 해당하는 부분이다.

 

 

 

 

아무리 생각해도, 로그인했을때  기록된  userId 와,

현재 조회하는 character 가 가진 userId 가 같은지를 비교하는게 맞는 방향 같은데,

 

로그인 했을 때 userId 는 req.user 가 가지고 있고,

문제는 조회한 character 가 가진 userId 인데,

 

방법을 모르겠다.

방법을 왜 모르는지 모르겠다 !!

 

위에 코드는,

일단 userId를 select 하고  그 select 한 userId를 토대로 비교해서 

Money를 삭제할지 말지 정하는 if문을 추가한 방식이다.

 

문제가 되는 사항은,

userId는 조회 할 필요가 없다.

그래서, userId를 셀렉트 하지 않으면? 

아래 if 문에서 userId를 참조할 수가 없게된다..

userId select를 : false 로 바꾸면?

 

그럼 애초에 select 하지 않아서인지, 참조가 되지 않는다.

 

 

사실, 이 단계까지 오는데도 너무 오랜 시간이 소요되었기 때문에...

수리를 할 시간이 부족했다.. 

이미 자정이 지나버린 시간

 

 

결국,

userId 까지 select 하면 아무튼 의도대로 작동하는 상태

에서 마무리가 되었다. 수리는 좀 나중으로 미루고...

 

 

내일은 조급한 마음을 가라 앉히고 다시 강의를 듣는것에 집중해야겠다.

 

저번 시간에 배운 1계층 - 물리 계층에 이어서,

오늘은 2계층인 데이터 링크  계층에 대해 알아보자.

 

 

데이터 링크 계층 (Data Link Layer)

OSI 7 계층 중, 2계층에 해당하는 내용으로,

- 직접 연결된 서로 다른 2개의 네트워킹 장치간의 데이터 전송을 담당

- Network 장비 간에 신호를 주고받는 규칙을 정하는 계층

- Network 기기 간에 Data를 전송 + 물리 주소를 결정

- 장치 간 신호를 전달하는 물리 계층을 이용해서, 네트워크 상의 주변 장치들 간에 Data를 전송

- Data들을 Network 전송 방식에 맞게 단위화 해서, 해당 단위를 전송

  .. 이때 전송되는 Data를 Frame 이라고 함 !

- Data Link Layer 에서 최종적으로 이더넷 헤더/ 트레일러가 붙는다.

 

 

주요 기능

프레이밍 (Framing)

- 물리 계층으로부터 받은 신호들을 조합해서 프레임(Frame) 단위의 정해진 크기의 Data Unit으로 변환

 

오류 제어 (Error Control)

- Frame 전송 시 발생하는 오류들을 처리

 

흐름 제어 (Flow Control)

- 송수신 측 간에 Data를 주고 받을 때, 적당한 양의 Data를 송수신 할 수 있도록 Data의 흐름 제어

 

접근 제어 (Access Control)

- 매체 상에 기기가 여러개 존재할 때, 데이터 전송 여부를 결정

 

동기화 (Synchoronization)

- 수신 노드에서 데이터 시작을 알 수있도록 송신 노드가 헤더 부분이 시작되기 전 일정한 비트 패턴 (Frame 구분자)

를 첨가하여 동기화 한다.

 

 

이더넷 (Ethernet)

- LAN 환경에서 Data 를 정상적으로  주고받기 위한 규칙

- Data 충돌을 막기 위해 CSMA / CD 프로토콜을 사용

- 현재에는 UTP 케이블 및 광케이블을 사용

- 프리앰블 / 이더넷 헤더 / Layer 3패킷 / FCS 를 추가해서 Frame 생성

 

 

 

데이터 링크 계층의 링크 종류

Point - to Point 방식 

- PPP, Ethernet 스위치와 호스트 사이의 Point  to Point 

 

Broadcast 방식

- 매체를 공유하는 방식으로 케이블형 Ethernet, 무선 LAN 이 해당

 

 

MAC 주소 (Multiple Access Control)

MAC 주소는 데이터 링크 계층에서 사용하는 주소로, 하드웨어의 생산 당시에 찍혀서 생산되기 때문에,

변경할 수 없는 물리적인 주소이다. MAC 주소는 중복될 수 없고, 한개의 기기에 한개의 MAC 만 존재한다.

 

 

+ Recent posts