과제의 끝은 새로운 과제의 시작   이 되어버렸다..

 

 

약간 과장해서

사이드뷰 플랫폼 점프액션 게임 

이라고 할 수 있다.

 

물론 당장 만드는 것은 아니고 , 학습이 우선 !!

 

 

이번 CH4 에서는 총 3개의 강의가 지급되었다.

 

 

 

본격적으로 서버환경을 구성하기 전 알아야 할 CS 강의가 포함되어 있고, (두렵다)

 

사이드뷰 플랫폼 점프액션 게임 

을 만들기 위해서 주특기 심화 강의를 먼저 듣도록 했다.

 

 

Realtime 즉 , 실시간 이다.

 

웹소켓을 이용해 실시간으로 상호작용하는 서버 환경을 구현하는것이 해당 과제의 목표이다.

 

웹소켓의 통신은 http의 통신과 많은 차이가 있는데, 

가장 먼저, 본격적인 연결형 통신이라는 것이다.

 

http는 req 건네고 res 받았으면 끝이다. 즉, 비 연결형 통신이다 .

 

이 말은,

동일한 클라이언트에서 req 를 여러개 보낸다고 해도, 

그것이 동일한 클라이언트 인지는 그 자체로 서버가 구분할 수 없다. 각각 새로운 연결이기 때문 !

 그렇기 때문에, http 에서는 매 req, 매 res 마다 위와 같은

나는 누구고~ 이런사람인데~ 이것 좀 주쇼~  등과 같은 정보가 담긴  

'비교적 무거운' 패킷이 매번 전달되어야만 한다.

 

물론, 이 형태가 무조건 비효율 적이고 도태된 형태라고 할 수는 없다.

어디까지나 http는 웹 페이지를 위해 만들어졌으며, 추가적인 개선도 진행되고 있다. (버전 업)

 

다만, 우리는 새로운 목적이 생겼을 뿐이다. 

 

클라이언트 상에서 실시간으로 데이터를 주고받고 즉각 표시 해 줄 수 있는것이 필요했고,

그것을 위해 개발된 것이 웹 소켓인 것이다.

 

 

추가적으로, 본문에서는 HTTP 의 단점 중 ,

클라이언트의 요청이 없으면 서버가 자체적으로 할 수 있는게 없다는 내용도 있었다.

 

내일부터는 위 사항들을 잘 기억하고 

 

실습에 들어가도록 하자 !

(공룡 ! 공룡 ! )

 

지금 타이밍에 풀기 좋은 문제가 등장 !

 

 

겉보기에는 엄청 쉬운 문제인것 처럼 느껴지지만, 이 문제의 핵심은

 

 

문제에서 주어지는 today, terms, privacies 가 정제되지 않는 형태로 주어진다.

완전 쌩 문자열 이라는것이다.

 

그러나 나는 주어진 문자열을 원하는 형태로 가공하는것에 약하다 !

일단 처음 계획은

date 에 해당하는 부분은 숫자열로 바꿔준다 !

 

terms 이 문제인데, 아무래도 객체 형태로 만드는것이 좋을것 같다 !

{ A : 6 , B : 12 , C: 3 }  이렇게 만들어주면 !

키값을 참고하여 숫자를 어떻게 해먹을 수 있겠다.

 

기초적인 내용이라고 생각되지만...

어째선지, 잘 모르기 때문에 일단 킹st.js 로 !!

 

 

 

가장 기초적인 객체의 사용방법 !

object .  key = value   로, 한번에 빈 객체에 key 와 value를 집어넣을 수 있다 .

 

obj[key] = value 형태를 통해, 원하는 key값을 변수를 사용해 넣어줄 수 있다. 

 

다시 문제로 돌아와서, terms 를 { A : 6 , B : 12 , C: 3 } 로 만들어보자.

 

이렇게 하면

{ A : 6 , B : 12 , C: 3 }  를 만들수  .. 가 없다

 

terms 의 값이 전부 문자열이기 때문이다.

+를 사용하던지, Numer(term)  을 해주던지 하여 숫자열로 변환후 value에 넣어주자.

 

 

그 다음은, 날짜를 계산해야 하는데, 아주 원시적인 방법을 사용하기로 했다. 

일단은 말이다 !!

보기만 해도 점수가 깎여나갈것 같은 방법이지만 당장은 가능하다는게 중요

month + year*12 + day/100  이것으로 날짜를 비교할 수 있게 되었다..(흠흠)

 

 이제, 두 방법을 활용 하여

문제에서 제시한 privacies 를 흠흠↑ 의 형태로 바꾸어 비교하기만 하면된다.

 

 

방법은 사실 상 동일하다. 

privacies의 앞부분인 2021.05.02 를 위와 같은 이상한 소수점으로 만들고

여기에 A를  앞에서만든 termType에서 찾아서 value를 더해주면 된다. 

 

그리고 반복문을 통해, 값을 비교해주면 끝 .. !

 

 

 

 

 

팀 과제가 무사히 마무리 되었다. 

 

 

 

 

 

 

 

 

 

 

 

최종 목표였던 브라우저에 canvas 그리기 까지 달성하였다.

 

물론 완성도 면에서는 아쉬움이 남지만, 기획 당시만 해도

 

과연 가능할까? 

어차피 필수 요구사항도 아닌데, 일단 도전기능 마무리가 되면 그때 조금씩 해보죠 !

하는 정도의 분위기 였다.

 

 

불가능을 가능케 하는 힘 !!

 

이번 과제를 진행하면서 가장 크게 느낀것은

팀 내 분위기가 정말 중요하다는 것이다.

과제가 시작되자마자,  마치 사전에 합의된 것 처럼

각자 생각한 기획에 대해 의견을 나누고, 

와이어 프레임과 API 명세서의 초안, ERD 가 자연스럽게 그려지기 시작했다 .

 

초반에 '의문의 변수 발생' 으로 인해,  브라우저 구현과 게임 내용의 구현까지 목표로 하게되어

불안감이 생기기도 했지만,

 

이내 팀장이 정해지고, 역할 분담이 이뤄지자 , 계획은 차츰 진행되기 시작했다.

 

소통 

우리 조는 과제를 진행하는 내내 오픈 마이크를 유지했으며, 팀원 모두가

상대를 배려하는 말투와 태도로, 소통하는데에 부담이 전혀 없었다.

 

문제가 생기면 그때 그때 도움을 요청하기도 하고, 

잠시 여유가 생긴 팀원이 다른 팀원의 진행을 도와주기도 하는 등,

상당히 이상적인 모습으로 협업이 진행되었다.

 

 

 

어떻게 해서 가능했을까?

 

협업에 능통한 5명이 모였기 때문에 가능했을까? 아니다.

리더쉽 이론을 마스터한 지휘관이 팀장이 되었기 때문일까? 아니다 !!

 

5명 모두가 이러한 환경이 될 수 있도록

적극적인 참여서로를 배려하기 위해 노력 했기 때문이다.

 

나 또한 협업에 대한 경험이 부족하기 때문에 망설임이 있었지만,

캠 또는 화면 공유를 통해, '소통이 가능한 상태' 와, ' 진행중인 내용' 을 간접적으로 보여주어,

좀 더 협업에 유리한 분위기가 조성될 수 있도록 노력했다.

 

지금의 경험을 잊지 말고,  적극적인 자세와 태도를 하기 위해 노력하자 !

 

 

 

아쉬운 점이 있다면..

언급한 내용처럼, 최초의 기획과 달리 브라우저 환경을 구성하도록 목표가 바뀌었고,

해당 부분은 과제에서 요구하는 사항이 아닌데다가, 관련 지식도 부족한 상태였기 때문에

결국 브라우저 환경을 구성하는데 있어서 별다른 도움이 되지 못했다. 

때문에, 도전 기능을 완성하고 모두가 브라우저 환경 구현에 몰입할 때에 ,

내가 할 수 있는 일이 없다고 생각되어 아쉬웠다.

 

 

 

그래도 한게 없는것은 아니다 !

거의 사륜안 모드로 코드 작성

 

위의 버튼들은 내가 맡은 선수 관리 파트의 API 를 각각 호출한다.

 

 

 

인솜니아를 표방한 형태를 계획했기 때문에,

params 와 Body를 입력하여(필요한 경우에만)  API를 호출할 수 있다.

 

 

 

자신의 편성 조회하기

추가적으로 브라우저 환경 스러움(?) 을 챙기기 위해, 

조회 기능 내에도 버튼을 만들어 API 를 호출하도록 했다.

 

맞다.. 첫 과제에서 사용한 그 button onclick="name('value')" 이다.

 

이전에 진행된 첫 팀 과제에서 비슷한 기능을 사용하기도 했고,

게시판 만들기 강의에서 비슷한 내용을 배웠기 때문에 구현이 가능할것으로 판단했다.

 

그 결과, 선수 편성 API 에 대한부분은,

사실상 보유 선수 관리  하나로 전부 해결 할 수 있게 되었다.

 

보유선수조회-> 보유선수 관리로 변경된 셈.

 

보유 선수 모두 조회 기능에서 선수관리의 모든걸 해결

 

 

저 부분을 구현하면서 가장 힘들었던 부분은... ? 새로고침 !!

 

편성에서 추가, 제외 혹은 판매 했을 때,

데이터가 변경되었어도 보이는 것은 이전 그대로 이기 때문에,

새로고침을 해야하는데, 

 

위 같은 함수를 사용하면 페이지 전체가 새로고침되기 때문에 의미가 없다.

 

특정한 Div의 내용만 새로고침 해야하는데, 

문제는.... 새로고침은 그냥 간단한 갱신 아니라  다시 불러옴에 가까웠고, 

그 새로고침을 하기 위해서는, 저 버튼이 눌렸을 때 실행되는 함수를 다시 호출하거나, 

아니면 수동으로 다시 띄우거나 해야했다. 

 

다른 버튼의 코드 예시

호출(인자).then(res => {})

함수의 구조가 지금까지 봐왔던것들과 달라서, 재귀가 가능하긴 한건지도 잘 모르겠고 ...

해석이 잘 안됐기 때문에 최초에는 내용 갱신에 필요한 코드를 전부 복사해서 붙여 넣었다.

      //새로고침 되어야 할 부분
       resContext.innerHTML = '';
       res.data.forEach(player => {
       resContext.innerHTML += `
            ${player.isPicked === true ? '▼' : ''}
            선수명 [${player.playerName}]
            ${player.isPicked === true ? `[편성중]` : `보유 수량 : [${player.playerQuantity}]`}
            ${player.isPicked === true ? `<button onclick="excludePlayer('${player.playerId}')">편성제외</button>` : `<button onclick="addPlayer('${player.rosterId}')">편성추가</button>`}
            ${player.isPicked === true ? '' : `<button onclick="sellPlayer('${player.rosterId}')">선수 판매</button>`}
            <button onclick="enhancePlayer('${player.rosterId}')">선수 강화</button>
            <br><br>
            `;
      });

 

 

 

예를 들면, 편성 제외 버튼의 함수에서, 편성을 제외한 이후에 저 코드를 복붙한 것이다. (느헉)

 

대충 이런 형태가........ 

 

물론 어디까지나 임시 이지만,

정말 민망하기 그지없는 코드이다.

 

 

 

검색을 하다보니, then() 은 굉장히 특별한 함수이고 어려운듯 해 보이지만,

 

당장은 promise 결과를 다시 반환해주는 함수 라고 이해하기로 했다.

 

그렇다면!!

그렇게 반환받은 결과로 다음 구문을 실행하는 중인 것이었고,

그렇다면 당연히 그 부분에 내용대신 함수가 들어갈 수 있겠다.

 

반복이 필요했던 부분을 함수로 만들어주면 된다

 

 

 

물론, 이것도 그렇게 올바른 방법인지는 모르겠지만,

최초의 코드와 비교했을때 원숭이와 유인원의 차이라고 생각이 되기에

일단 이정도로 만족하기로 했다. 

 

위 내용은 하필 브라우저 부분이라, 

과제 발표의 트러블 슈팅에도 포함 하기가 애매했다.

 

 

추가적인 트러블 슈팅 내용도 많지만 오늘은 일단 여기서 끊도록 하자 !

 

 

과제 제출까지 -1 일

 

 

 

실제 게임에 필요한 선수 데이터를 모아서 보내줘야하는데,

이전에 구현했던 코드에서 변경된 사항이 있다. 

 

아군 플레이어와 상대 플레이어를 구분하기 위해

사실상 동일한 과정을 2회씩 반복하고 있다.

슬슬 이런 형태의 코드를 보면 기분이 다운되는 자신을 발견할 수 있었다...

 

 

개선 후

한번의 쿼리문에서 아군과 상대 player 를 한번에 조회하도록 수정해 보았다.

 

이 과정에서 닥친 문제는,

어떻게 아군과 상대 선수를 구분할수 있을까? (피아식별의 어려움)

 

order by accountId asc  ??

=> 아군, 적군에 상관없이 accountId 가 빠른 순으로 정렬되어

피아 식별이 불가능

 

모르긴 몰라도, 분명 이 문제를 해결하는 방법이 있을것 같기에 계속 구글링을 해 보았다.

 

https://priming.tistory.com/68

역시나 답은 있었다 !

 

order by field() 를 사용하면, 

해당 필드의 특정한 값을 우선 조회하도록 할 수 있었다.

단, desc 를 해줘야 맨 위로 오기 때문에 주의 !! (잘못 사용시, 특정 데이터가 오히려 결과에서 제외된다.)

 

 

즉, 본문 내용처럼

위와 같은형태로 자신의 선수들이 최 우선 조회되도록 할 수 있다는 것이다.

 

where 이후 남는 데이터는 6개가 되기 때문에 order by field의 작동 방식이 불량하더라도

크게 문제가 되지 않을것이다. 

 

 

결과적으로 게임에 보내질 데이터...

 

정상적인 경우, 배열의 length 는 항상 6이고,

index 0~2 는 아군 player

index 3~5 는 상대 player 선수를 담게된다 .

추가로 각 팀에서 playerStrength가 낮은 순으로 정렬된다. (선수 포지션 배치에 이용됨)

 

 

 

강화 테이블 개선

강화 1당 모든능력치 10 씩 고정 증가, 고정 성공확률인 강화 형태에서,

강화수치가 늘어날 수록 점점 가충치가 더 붙는 형태로 변경했고,

강화 단계가 높을수록 강화확률이 낮아지게 설정될 수 있도록 enhances 테이블 데이터를 추가했다.

 

 

강화 능력치를 적용해야 하는 시점에서

enhances 테이블의 데이터가 모두 필요하기 때문에,

findMany로 전부 불러와야한다.

위는 조회에 더해질 능력치를 불러와야하기 때문에, 

increaseValue 만 select 하고 있다.

 

 

조회 API 에서,

강화가 0단계 일때는 enhanceId  1 의 increaseValue = 0 을 가져와야 한다

 

 

 

강화 API에서,

0강화 -> 1강화로 강화를 하는 경우 enhanceId 2 의 successRate 를 가져와야한다. 

 

 

 

코드 직관성이 매우 떨어지는 상태이지만, 다듬을 시간이 부족하다 !!

게다가, 선수 강화 부분은 과제 필수기능에서 거리가 멀기 때문에 ! 

 

 

 

 

 

 

 

 

아무래도, 과제 기간엔 과제 외에 뭘 하기가 힘들어서 ! 

TIL 도  온통 과제의 내용이 되어버린다 !

 

 

 

우선, 리팩토링 하고싶은 코드 몇개를 살펴보자.

기존 코드 

 

 

가차 ...또 너야?

 

문제의 원인은

return 하는 메세지에, 랜덤으로 획득한 선수 Name 을 출력하고 싶어서 이다.

 

첫번째 장애물은 createMany 

사용법이 난해해서, 깔끔하게 [{data1,data2 }] 배열로 전달하지 않으면 사용하기가 어렵다.

 

두번째 장애물은 Math.random

랜덤 뽑기의 대상이 된 playerId 와 playerName을 정상적으로 가져와야한다. 

 

세번째 장애물은 accountId 

가차 뽑기의 결과물을 소유하게될 accountId 는 당연히 모든 선수목록 table 에는 없다

 

 

슬프게도, createMany의 반환 결과는 create 로 만들어낸 숫자이기 때문에

거기서 떼 올 수 없다는것

 

 

결국, 가차 과정에서 playerId, playerName, accountId 를 전부 가지고 나온 다음에

 용도에 맞게 다시 쪼개기로 했다.

 

 

 

예시라고 나오는게 이런것 밖에 없어서....

createMany를 원하는대로 사용하기가 어렵다

 

 

 

 

 

map 을 2번 사용해서 각각 쪼개야 하지만, 나름 목적을 달성했고

그렇게 큰 리스크도 아니기에 이렇게 사용하기로 했다.

으음 !

 

문제 2

 

내 수준에서 적당히 기분이 안좋은 코드

 

 

myTeamSearch 라는 동일한 값을 여러번 체크하는데다가 이득도 별로 없는 코드

 

 

일단 가져올 때는 전부 true 로 가져오도록 하자 .

 

 

그래서 일단 map으로 정렬을 하되, 

myTeamSearch 를 여기서 한번 체크해서 false인 경우 일부 결과를 다시 뺏어가버리면 된다 !!

 

 

기능이 잘 작동하는 모습!!!

 

 

 

 

오늘은 !  진행중인 브라우저 구현 부분에 대한 TIL 을 작성해보자.

 

사실, 프론트는 이번 과제의 목표도 아니고 !!

필수도, 도전도 아닌 선택의 영역이다.

 

하지만, 우리팀은 프론트를 진행하는 것으로 결정이 됐기 때문에,

백엔드, API가 어느정도 완성돼가는 인원부터 브라우저를 구현하도록 했다.

 

 

현재, 목표로 했던 팀=> 선수관리 API 관련한 부분은 대부분 브라우저에서 작동하도록 구현했다.

 

구현하고 싶으나 시간도, 방법도 모르겠는

오류 처리 방식, message 출력,  특정 div 영역 새로고침, API 에 맞춤형 Request 창 등등

아직 해야할것이 많지만, 백엔드가 완벽한 것이 아니기 때문에 이쯤에서 다시 백엔드로 돌아가기로 했다.

 

먼저, 어제 고민했던 중복선수 카운트 부분이다.

 

 

로우쿼리.. 돌아왔구나..

 

기존에 사용했던 Prisma 의 distinct는 count 를 하는데 어려움이 있다.

그래서 prisma의 group by 를 사용하려고 했으나, join과 그룹화, 카운트를 동시에 진행하는것이 굉장히 난해했다.

 

오히려 쿼리문을 사용한다면 쉬울것 같은데? 라는 생각에서 작성을 해 보았다.

 

한번에 해결될 리가 없다 

 

count 를 한 결과값이 5n...  아마도, bigint 의 형식으로 출력되는듯 하다.

아무래도, 얼마나 될지 모르는 양의 데이터를 count 하는것이기 때문에

기본적으로 mysql에서는 bigint를 출력하게 되어있는듯 하다. 

 

bigint 는 json 형태로 값을 전달할 수 없는것인지, 에러가 발생한다.

::int  CAST() 등등 으로 제발int를 출력하도록 유도했지만, 계속해서 에러만 발생했다.

 

게다가  boolean 값이어야 할 isPicked 컬럼은 아예 정수로 반환이 되었다.

 

어차피, player능력치에 해당하는 부분은 강화수치를 적용하기 위해 재가공을 해야하는 만큼,

한번에 map으로 재가공을 하기로 했다.

 

 

수량에는 parseInt를, isPicked 에는 Boolean() 을, 능력치 부분에는 강화수치를 적용하는 것으로 처리했다.

 

 

 api 자체는 선수의 조회기능이기 때문에, 능력치를 가독성 있게 문자열로 표현하게했다.

 

결과는 그럴싸 하지만, 탐탁치는 않은 부분이다 !!

보유한선수를 모두 조회하고, 꽤 많은 데이터를 map을 통해 재가공 하기 때문에 리소스 소모가 클 수 밖에 없다.

 

당장 최적화 방법이 떠오르지 않고, 추후 브라우저의 보유선수 조회 기능에서 많은 기능이 연계될 것으로 계획했기 때문에

일단은 이 상태로 진행하기로 했다.

 

사실, 이미 어느정도는 작동한다

 

 

하나 하나 쌓여가는 API 들 

 

다음 목표는 트랜잭션과 오류 처리 부분이다 !

 

 

 

 

하루가 끝나가고, TIL 을 쓸 무렵 중대한 오류가 발견되었다...

무려 작성한 스키마 내용중, 

playerDefense 부분만  P !!!

대문자로 시작하는 것이었다.

(암튼 내가한것은 아님)

 

저 대문자 P 를 p로 고치는것만 해도 db push를 새로 해야하며, 

그렇게 되면 데이터가 모두 날라간다는 경고문이 출력된다.

 

현재, 데이터베이스에 존재하는 데이터들은 어디까지나 임시 수준이기 때문에 

일단은 데이터를 초기화하고 진행하는것으로 결정이 되었다.

크헉

 

다음으로는, 개선이 필요한 부분 !

 

어떻게 보면 위의 사건으로 인해 발견된 문제점인데,  어제와 동일한 부분인 

랜덤 뽑기 부분이다.

 

 

만약, 선수 데이터 테이블이 위처럼만 구성이 되어있다면?

모종의 이유로 중간중간 playerId에 구멍이 나있다면?

 

기존의 랜덤 추출 방식이 적용된다면, 최대값인 7 기준으로 1~7의 숫자를 뽑을 것이다.

 

그렇다면, 1 ,2 ,3 이 뽑혔을 경우 에러가 발생하게 된다.

 

위와 같은 사건(?) 이 발생하거나, 

의도적으로 몇몇 선수 데이터를 제거했을 때에도, 유효한 가차뽑기를 만들어야한다.

 

우선, 모든 선수를 조회해서 playerId를 추출해서 배열 형태로 만든다 !

 

알고리즘 풀이 시간인가?

이제 기존처럼 반복문으로 json 형태로 데이터를 가공해야하는데, 

randomPlayer 의 범위를, 위에서 구한 allPlayers 배열의 인덱스 범위로 만들면 된다 .

 

그렇게 해당 인덱스의 playerId 를 넣어주면 완성 !!

 

 

이렇게 만들 경우, 동일한 방법으로 

가차의 대상이 될 playerId를 나름 유연하게 지정할 수 있다.find 의 결과만 있으면 되기 때문에, 

 선수를 find 하는 where 부분에 레어도 라는게 들어간다거나 하는 등.. 일정 범주의 가차를 만들 수 있게된것 !!

 

 

 

그 외에...

보유 선수 조회 기능을 다른 조원이 작업하셨는데 

따지고 보면, roster 기능은 다 내가 맡았어야 되는 상황이라 코드 주도권을 넘겨받았다 !!

 

이전, 로그인 한 계정의 모든 보유 선수를 출력해주는것 에서 

distinct 를 활용하여, playerId 와 enhanceCount 가 동일하게 존재하는 경우 

조회 결과에 포함하지 않도록 했다. 즉, 중복된 것은 버려지고, rosterId 가 가장 낮은선수 하나만을 조회하게 된다 !

 

중복되는 선수는 뭐 하나 다른점이 없기 때문에 깔끔하게 조회할 수 있다.

 

다만, 몇개가 중복되었는지 정도는 표기할 수 있어야 한다.

 

해당 부분은... 내일 알아보도록 하자 !!

 

 

 

 

 

 

 

 

 

문제가 살짝 난해한 내용이다.

 

요점은, #이 표시된 위치에 따라 드래그 할 영역을 정해야 한다는 것.

 

 

정답을 구하는 방법 자체는 금방 떠올랐다. 

 

이제 답을 구하러 가야하는데,

늘 그랬던 것처럼 for반복을 하기로 했다. 

 

 

우리가 필요한건 wallpaper[i] 에  #이 포함된 경우와 wallpaper[i][j] 가 # 인 경우

그중 가장 높은값과 낮은값이 필요하다. 

 

제한사항 자체가 그렇게 빡세지는 않기 때문에 for 기본 반복문을 통해서 해당 값을 구하고, 

answer에 담아 리턴해주면 끝 !

 

 

 

추석 연휴 기간동안에도 팀 과제를 진행했지만, 따로 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;
}

 

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

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

+ Recent posts