추석 연휴 기간동안에도 팀 과제를 진행했지만, 따로 TIL에 기재하지는 않았다.

 

저번 TIL 에서도 말했지만, 모든 휴일과 제출당일을 제외하면 4일밖에 남지 않았기 때문에 (그 중 하루는 오늘 !!)

팀원들과 상의해서 연휴 기간에도 모여서 프로젝트를 진행하기로 했었다 .

 

 

 

현재, 내게 주어진 임무 중, 필수기능에 해당하는 부분은 모두 구현이 완료가 된 상태이다.

 

 

다만, 현재의 roster 테이블의 구성은, 위 그림에서 보는것과 같이

rosterId (PK) ,  계정ID, 선수 Id 로 이루어진  일종의 인벤토리 테이블로 되어있다.

데이터 1개 (행) 당 1개의 선수만을 표시하므로, 선수가 쌓일수록 데이터가 굉장히 많아지는 구조이며,

 

이 형태는 내가 직전 과제인 CH3 아이템시뮬레이터 에서 작성했던 인벤토리 테이블과 정확히 일치하는 구조이다.

 

여기서 !!

차선책 이었던 위 형태의 라이벌이 되는 구조가 있었는데, 

 

그것은 수량 개념 을 갖는 컬럼을 만들고, 데이터 한개가 여러개의 동일한 선수를 보유하고 있음을 표현하는 방식이다.

수량 개념이 있다면 동일 데이터에서 수량만 변할것이므로 사실상 accountId 와 playerId가 일종의 1:1 관계가 되고

데이터의 숫자가 확연히 줄어들게 되므로 장점이 많다.

 

 

그럼 왜 해당 방법을 버리고 데이터가 우후죽순 늘어나는 방법을 채택했는가...?

 

그 이유는

 

 

선수 강화 기능에 대한 고려를 한 결과이다.

 

선수 강화를 적용하는 방식은 크게 두 가지로 나눌 수 있다.

 

 

첫 번째 방법

데이터 베이스에 해당 선수의 강화 결과에 대한 데이터가 전부 넣어버린다.

예를 들면 player 테이블 내에

선수ID1  호날두 +0 공격력 90 수비력78 스태미나 84

선수ID2  호날두 +1 공격력 96 수비력 81 스태미나 86

선수ID3  호날두 +2 공격력 101 수비력 83 스태미나 90 

 

위와 같은 강화 결과에 대한 데이터가 별도의 선수Id 로 '이미' 존재하고 있어야한다.

강화 성공 시, 선수 Id 자체가 변하는 결과가 되는것이다.

두 번째 방법

그림처럼, 선수 테이블에 enhanceCount 를 넣고 강화 수치를 표시해서,

별도로 강화 수치가 기록된 테이블의 데이터를 참고하여...

실제 강화된 능력치가 적용되어야 하는 순간에 해당 수치만큼

능력치를 더해서 적용하는 방법이다.

 

왜 그림 자료가 있는가 하면, 이 두 번째 방법을 사용중이기 때문이지 !!

 

 

아니 그럼 두 번째 강화 방법에 수량까지 추가하면 되는거 아닌가? 하는 의문이 생긴다.

 

그럴싸 하지만, 위 방법대로라면,

수량이 50으로 되어있는 데이터의 선수를 강화를 하면 50명이 전부 강화되는 일이 발생할 것이다.

이런 사태를 피하려면? ==> 강화 결과 선수 ID 자체가 변해야 한다. ===> 강화 결과 새로운 선수ID 필요

=== 위의 첫 번째 강화 방법이 필요

 

위 방법을 사용한다면, 선수 하나를 새로 등록하는데

5강화 까지만 데이터를 추가한다고 해도 6개씩의 데이터를 입력해야한다.

 

따라서, 1선수 1데이터라 해도 데이터가 우후죽순 늘어날 가능성이 희박한 환경(과제) 인 점,

굳이 +0 +1 +2 +3 +4+5  데이터베이스 노가다가를 할 필요가 없다는 점에서  !!!

본문 초반에 나온 roster 의 방식이 사용되었다고 할 수 있다. 

 

해당 방식을 채택한 이유에 대한 설명은 여기까지 하고,

다음은 오늘 해결된 코드 내용을 간략히 설명해 볼까 한다.

 

 

해당 코드는, 내가 담당(부) 로 되어있는 선수 뽑기 ! gacha에 해당하는 부분이다.

문제의 코드를 살펴보자.

 

 // create 를 반복문으로 돌렸습니다...
    const manyPlayer = async function aabb(accountId, gachaTry) {
        // 가차 선수 범위
        let answer = [];
        for (let i = 0; i < gachaTry; i++) {
            let randomPlayer = Math.round(Math.random() * (findMaximumPlayerId.playerId - 1)) + 1;

            // 랜덤 선수 획득
            const getGacha = await prisma.roster.create({
                data: {
                    playerId: randomPlayer,
                    accountId: +accountId,
                },
            });

가챠 기능 자체에 문제가 있던것은 아니지만, 

위의 코드를 보면 누가 보기에도, create 가 반복문을 통해 돌아가는것은 바람직하지 않아보인다.

 

10번의 뽑기를 진행한다면,

prisma.roster.create 에 해당하는 부분이 10번이 반복되어버리는.... 매우 참담한 상황이다.

create 반복?!?

 

 

어느정도 해답에 가까운 정보를 검색을 통해서 알고있긴했지만....

바로 createMany  prisma메서드를 사용하는 것이다.

 

문제는 createMany의 사용법이 너무 난해했기 때문에 접근을 전혀 하지 못하고 있었고...

튜터님을 통해 문제를 해결하기로 했다.

 

 

그 해답은 매우 심플했다....

 

createMany 사용법에서 알려주는것 처럼,

data 를 [] 배열... 즉 json 의 형태로 때려 넣어주면 알아서 다 생성한다는 것이다 !  

마치 .map()  메서드의 실행 방식처럼 말이다.

 

 

위의 형태로 만든 뒤,

resultGacha  라는 변수가 가진값이 =  [{playerId : 랜덤으로뽑힌number, accountId : 뽑기한유저ID}, {playerId ...}]

의 형태로 만들어 준다면, createMany가 정상적으로 작동하게 된다.

변수부분에 데이터를 잘 가공해서 넘겨줘야 하겠지만, 무려 반복문도 필요없고 2줄 분량의 코드로 줄어들었다.

 

 

여기서 answer = resultGacha 를 반환하는 함수를 따로 만들어줬고, 

약간 조잡해 보이긴 하지만, create 전체를 반복문으로 돌리던 참담했던 과거와 비교했을 때,

무엇보다도 데이터 베이스를 조회하고 create 하는 횟수가 확연히 줄어들었다.

 

오늘도 문제 한가지 해결 !!

 

 

문제가 너무 길기 때문에, 자세한 내용은 생략한다

 

문제가 길지만, 읽어보니 특별한 메소드가 필요하거나 하진 않을 것 같다.

 

우선, 점수를 카운트하기 위해 객체를 만들어 준다. 

 

그 다음, 점수를 얻는 과정을 반복문으로 작성해보자.

 

수준이 좀 떨어져 보이지만, 일단 생각난 그대로 적어 보았다.

짜치긴 해도, 추가 설명이 필요 없을 정도로 직관적인 코드가 아닐까 !!

 

위 의 반복문을 통해서 점수를 기록했으니, 이제 결과를 만들어야한다.

 

 

짜잔!..........

 

 

 

function solution(survey, choices) {
    var answer = '';
// 각 지표에 해당하는 성격 유형 점수표가 필요함
    let score = { R: 0, T:0, C:0, F:0, J:0, M:0, A:0, N:0}
    
    for(let i = 0; i < choices.length ; i++) {
     const [left, right] = survey[i].split("");
        if (choices[i] > 4) {
            score[right] += choices[i]-4
        } else if ( choices[i] === 1) {
            score[left] += 3
        } else if ( choices[i] === 2) {
            score[left] += 2
        } else if ( choices[i] === 3) {
            score[left] += 1
        }        
        }

    if ( score.R >= score.T) {
        answer += "R"
    } else answer += "T"
    if ( score.C >= score.F) {
        answer += "C"
    } else answer += "F"
    if ( score.J >= score.M) {
        answer += "J"
    } else answer += "M"
    if ( score.A >= score.N) {
        answer += "A"
    } else answer += "N"
    return answer;
}

 

정말 짜친다는 말이 절로 나오는 코드이지만 , 큰 하자없이 잘 작동하는 상태이고,

여유가 될 때에 코드를 가다듬으러 다시 이 문제로 돌아와야겠다.

 

 

개인과제가 종료되기 무섭게, 팀 프로젝트가 발표되었다.

 

 

과제 기한은 09.13 ~ 9.25일 이다.

꽤나 길어 보이는 기간에 함정이 있었으니..

 

발제 당일과 제출하는 날과 휴일을 제외하면 19, 20, 23, 24   4일의 기간이다 !

 

크흠........

 

 

오늘의 목표

 

 

와이어 프레임

해당 팀 프로젝트는,  저번 개인과제와 마찬가지로 백엔드 중심의 구현에 초점이 있기 때문에,

프론트 부분은 구현을 안해도된다.

추후, 구현하게 되더라고 최대한 간단하게, insomnia 인터페이스와 유샤한 느낌으로 구성했다.

 

 

API 명세서

팀원과 상의하며, 작성한 API 명세서.

현재 1차적인 역할 분담까지 완료된 상태이다.

작업 진행속도나 상황에 따라 유연하게 바뀔 예정 !

 

 

ERD 

 

최초 계획에서 이미 피드백을 받고 수정이 이루어진 상태다.

 

불필요하다고 판단되는 Accounts_info 가 Accounts 에 병합되었고, 

 

그 외 players (선수) 의 내용을 참조가 아닌 복사해와서 쓰는  Roster (인벤토리 선수목록) 같은 형태는 

있어선 안된다는 피드백 (보안 문제, 유지보수가 불가능 수준이기에 현업에선 있을 수 없는 형태) 

을 통해 players 가 선수의 내용을 담고, 후에 강화정보는 Roster 를 통해 참조되기로 했다.

 

 

관련 지식이 없어서  전혀 모르고 있었지만, 튜터님께 들을 내용은 충격적이었다.

기존에 예상했던 방식 ->

아이템DB -> 해당 DB에서 아이템을 복사해서 인벤토리로 가져옴 -> 그 후 자유롭게 +- 제련 강화 기타 등등 작업하며 사용

 

이런 형태로 아이템의 흐름을 예상 했는데, 완전히 틀린 내용이었다.

 

이유로는, 

 

첫 번째 !!

만약 아이템이 상당량 풀린 상태에서 아이템의 수정 (패치로 조정) 이 이뤄져야 한다면, 

베이스db에서 수치를 조정하는건 당연하고, 

풀려있는 모든 아이템을 추적해서 직접 조정해야한다. 

이와 비슷한 내용의 유지보수가 현실적으로 불가능해 진다.

 

두 번째 !!

위와 비스무리한 이유로, 아이템이 어떤 과정을 통해 이런 결과로 변조되었는지 알 수 없게된다.

이는, 해당 아이템 데이터가 조작되어도 딱히 검증할 방법이 없는것이라 할 수 있다.

같은 이유로, 아이템의 강화 내용을 되돌리는 기능 같은것도 있을 수 없게된다..

 

따라서 ! 위의 ERD 표 처럼,  plalyers 의 값을 참조 하되, 추후에 강화기능을 만들게 된다면

roster 테이블에서 강화와 관련된 데이터를 관리하게 될 것이다.

 

계획은 이 정도 선에서 마무리 되었고 ,

 

내가 맏게된 파트는 팀 편성 관련 API 등등 이다. 

 

 

일정 상, 추석과 주말에 과제를 진행해야 겠지만,

 

이번 팀원들과 호흡이 잘 맞을것 같아 벌써부터 기대가된다 !!!!

 

 

 

 

 

햄버거 만들기

햄버거는 쌓아서 만든다는 특징이 있다... 쌓는다 !

 

이 문제는, stack 의 풀이를 사용할 것을 유도하고 있다.

 

 last in , first Out 

마지막에 들어온 데이터가 가장 먼저 빠져온다. 

그렇기 때문에, 스택 구조는 시간 복잡도 면에서 유리하다.

데이터가 위쪽에 쌓이는 형태이기 때문에 PUSH (O)n 

나가는 것도 맨 위쪽이기 때문에 POP (O)n 이기 때문이다.

 

 

 

우선 stack 배열을 만들고, 

주어진 배열 ingredient 를 순회하며 앞에서 부터 하나씩 쌓는다. Stack !

 

 

하나쌓을 때 마다, stack 배열의  뒤에서부터 4개의 값을 조사한다. 

1231 일 경우, 햄버거를 만들 수 있으므로, answer ++ 를 해주고,

조립한 햄버거 재료를 제외한다.

다시 반복문으로 돌아가 다음 재료 stack에 추가한다 ... 반복

 

이렇게 하면, 문제의 핵심인

 

1231이 아니어서 지나친 재료가  다시 1231의 재료가 될 수 있는 부분 

12  1231 31    << 와 같은 경우 또한 정상적으로 조립 대상에 포함할 수 있다.

12 1231 에서 answer ++  후 1231 제거

12 3 

12 3 1  에서 answer ++  후 1231 제거

 

 

 

 

 

 

늘 그래왔듯이,

과제 제출하는 날이 되면 미처 발견하지 못했던 오작동 코드나, 오류들이 발견된다.

 

발견되는건지 발생하는건지 모르겠다

 

 

 

1. 아이템 판매시 장착된 아이템이 inventoryNumber 가 제일 낮은 경우, 그대로 팔려버린다.

 

이는, 대상 item을 find 하는 과정에서 equippedItem 의 여부를 묻지 않고 조회했기 때문이다. 

으음... 기초적인걸 놓쳤다.

이렇게 뜬금없이 장착된채로 아이템이 팔려버리면,

장착 해제+판매금획득+ 장착했던 능력치는 유지 라는 대환장 사태가 벌어진다.

 

어디까지나 개인 과제이기 때문에 찾는다면 고치면 되지만,

실제 서비스중인 게임이었다면 정말 끔찍하다.

 

조회시, where문에 equippedItem : false 로, 장착중이지 않은 아이템만 조회하게 하면 해결!

 

 

 

2. 해당 캐릭터의 가장 낮은 inventoryNumber를 탐색하는 과정에 문제가 발견됐는데, 

 

 

광분하여 작성한 Rawquery 부분에 오점이 있었다.

 

마음을 가다듬고...

 

무아지경으로 작성한 이 코드를 재 해석 해보면,

우선 findTargetItem 은 단순하게,

inventory 테이블에서 대상이 된 characterId 가 가진 아이템 중, inventoryNumber가 가장 작은 아이템

한 개를 조회한다.

 

조회 결과가 없을경우, 해당 캐릭터는 아이템을 한개도 보유하고 있지 않은 상태인 것이기에

해당 케이스는 따로 inventoryNumber 1을 부여하게 된다.

 

조회 결과가 있을 경우, 가능한 낮은 inventoryNumber  를 부여받기 위해 탐색을 진행하는데

이 부분이 로우쿼리 부분에 해당하며,

쿼리를 해석하면, 

inventory 테이블에서, inventory_number 가 가장 작은 결과 하나를 조회한다. 

그런데, 캐릭터 id가 대상이 된 캐릭터 Id 인것중에서만, 게다가  inventory_number 값에 +1 을 더해서 검색한다.

그 캐릭터 현재 가지고 있는 itemNumber 를 제외하고 

 

 

쉽게 말하면, 

그 캐릭터가 보유중인 아이템 중에서 가장 낮은 inventoryNumber 에서 +1 했을 때 값이 비어있는 값을 찾는다.

 

굉장히 본래 의도와 맞지만 함정이 있다.

탐색 자체를 현재 보유중인 값 부터 시작한다는 말이다.

 

이 말은, 해당 캐릭터가 보유한 itemNumber 의 최소값이 5 라면, 

1~4 가 비어있음에도 불구하고 5부터 탐색을 시작하게 된다.

 

 

만약 인벤토리 사이즈가 10 일 때, 

해당 캐릭터가 itemNumber 10인 아이템 한개만 들고 있다면,

해당 캐릭터는 더 이상 아이템을 획득할 수 없는 상태가 된다.

10부터 탐색하기 때문에 결과가 11이 되기 때문이다.

 

우연히 찾아낸 오류 이지만, 해결 방법은 간단했다.

 

아이템을 보유하고 있지 않을때는 1을 부여했던 것 처럼,

1을 보유하고 있지 않은 경우는 1을 부여하면 된다.

 

 

정말, 너무 만들고 싶어서 만들었지만

왠지 쓸데 없는 코드인것 같고...

로우쿼리 라고는 해도, 탐색 과정이 효율적이지 않은듯 한 데다가,

필수 구현 사항들과는 거리가 먼 코드라서 마음이 아프다...

 

또르르

 

 

아무튼 ..! 추가적으로 위에 있는 내용 처럼, 이전에 만들어놓은 캐릭터의 inventorySize 컬럼을 이용해서 

인벤토리의 한계를 적용해 보았다. 

 

전체 app 상에서, 아이템이 inventory로 추가되는 경우는 

해당 아이템 구매 API가 유일하기 때문에 여기저기 사용되어야 할 일은 없다.

 

 

그 후에, 

알고리즘 코드카타 -> 스탠다드반 강의  콤보로 인해서 과제 제출 마감이 임박하게 되었고,

 

많이 아쉽고 많이 부족한 채로 결과물을 제출하게 되었다.

 

 

부랴부랴 만든 API 명세서

 

 

 

아쉬운 점과 부족한 점

과정의 문제

 

우선, 해당 과제가 발제됐을 때 미처 듣지 못한 강의가 남아있었기 때문에

시작 과정 부터가 험난했다. 꽤 많은 분량의 강의가 남아있었고

과제는 이미 발제가 되었고...

 

빨리 과제를 시작하고 싶다는 기대,

내가 이번 과제를 잘 할 수 있을까? 하는 불안감,

강의 내용은 머리에 잘 들어오지 않아 답답함,

 

오만가지 생각이 교차하며 과제 시작부터 삐그덕 거렸기 때문에, 

만족한 결과가 나오지 않은것은 당연한 지도 모른다.

 

결국 강의를 들으면서 동시에 과제를 진행하게 되었고,

해당 부분에서 학습효과와 집중력이 떨어질 수 밖에 없었다.

 

과제 기간에도

스탠다드반 수업(CS), 챌리지반 강의(복합), 알고리즘 코드카타 모두 평소처럼 병행되었기에

12시간이 짧다고 느껴졌으며,

실제로 과제 기간동안 오후9시에 학습을 마친 날이 없었다.

 

다음 과제부터는 필요한 강의 부분을 최대한 발제 전에 마치도록 시간을 분배하여

이번과 같은 일이 생기지 않도록 해야겠다.

 

부족한 점

이번 과제는 부족한 점이 너무나도 많다

 

처음 사용하는 데이터 베이스, 코드작성을 위한 ORM,

의 서버를 대여하는 AWS RDS

 

express 구성, node 구성,

의 서버를 대여하는 EC2,

 

미들웨어 함수, 오류에 대한 처리, 

인증과 인가.

 

전부 너무 생소한 개념이었고, 결과적으로 이해도, 완성도 모두 부족한 채, 과제를 제출하게 되었다.

 

하지만

이런 핑계는 소용이 없다는 것을 알고있다.

 

앞으로 배울 내용들도 항상 새롭고 전부 생소 할 것이다.

 

언젠가는 아예 다른 언어를 사용해야 할 수 도 있다.

 

물론, 아직 조급해 할 필요 까진 없다고 생각한다.

이제 겨우 본 과정 한 달이 지났을 뿐이다.

 

정말로, 열심히 하고 있다고 당당하게 말할 수 있기 때문에,

지금처럼 학습을 진행하되, 위와 같은 일이 일어나지 않도록 시간 분배를 잘 해야하며,

같은 문제가 반복되지 않기 위해서는 기본기를 꾸준히 단련해야겠다. 

 

 

주눅 들지 말자.

포기하지 말자.

 

코드 카타에 사용할 이미지

 

 

오랜만에 못풀진 않을듯 ? 하는 문제가 나왔다.

 

문제의 포인트는 2가지로 설명할 수 있다.

 

1. 소문자 알파벳으로 이루어진 문자열 s가 , index 의 int만큼 밀려서 다음 알파벳으로 출력됨

=> 이는 아스키 코드 의 문제와 같은 부류이고,

 

2. skip 내에 있는 알파벳은 기본적으로 건너 뛰게 된다.

=> 이 부분이 해당 문제의 난이도를 올리는 부분이다.

 

우선, 아스키 코드 대신 저번처럼 알파벳 문자열과 index를 사용하기로 했다.

 

소문자만 존재하므로, 

 

전체 소문자 문자열을 arr에 할당해주고,

(왜 변수명을 arr로 했지?)

 

반복문을 통해 arr배열.. 아 아니라 문자열에서 s[i] 에 해당하는 알파벳의 인덱스를 뽑아오면, 

s와 동일한 값을 우선 추출할 수 있다.

s.length 만큼 반복시 s와 동일한 값이 나오게 됨

 

문제는  skip 부분인데, 문자열 arr 을 만들면서 생각이 난게 있다. 

skip에 해당하는 알파벳을 다 없애버리면 그게 skip이 아닐까?

 

skip 에 해당하는 알파벳은 조건없이 항상 건너 뛰게 되므로, 사실상 없는것과 같다.

 

 

프로그래머스 치고,  상당히 친절한 조건까지 붙어있다.

 

 

 

반복문과 replace 메서드 를 이용해 하나하나 날려주기로 했다.

뭔가 한번에 날릴 방법이 없을까 생각했지만, 찾아내진 못했다.

 

skip 에 해당하는 문자가 도려내졌다. 

 

이제, 위의 방법에서 주어진 index만큼을 더해서 반환하면 된...

 

 

 

당연히 안된다

 

z 이후를 참조하려 하면 undefined 이기 때문이다  

 

 

arr.length 만큼 나눠서, 나머지에 해당하는 부분으로 적용하면,

arr.length 이하인 경우에 대해서는 기존처럼 작동하고,

넘어가면 자연스럽게 0부터 시작하는 형태가 완성된다.

 

 

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 방식이 많이 사용됩니다.

 

 

 

'스탠다드 반' 카테고리의 다른 글

[스탠다드 반]OSI 7계층 - 물리계층  (0) 2024.09.03
[스탠다드 반]과제 - OSI 7 계층  (1) 2024.08.28
[스탠다드 반]과제 - 서버  (0) 2024.08.26

 

 

 

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

 

 

 

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

모아 놓고 보니까 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 가 최소값을 찾아 들어가게 하는 방법을 적용할 예정이다.

 

 

 

+ Recent posts