lol stat website using lol api
default branch main
- Home (Search summoner name)
- Summoner's stat
- Worst counters against my champions
- win rate for each enemy champion met
- Best partners of my champions
- Best mid/top - jg combination
- Best ad - sup combination
- items that worked best for my champions
- the most common enemy that I met against my champions
- Worst counters against my champions
[ ] MVC settings [ ] fetch lolapi data and show it on view [ ] build routers [ ] design in detail
- milliseconds to date gogo
- winrate with friends?
- e.g.
- what's your account?
- what's your friend's account?
- what's the winrate when you play with this friend?
- e.g.
- namtat.gg
- worst, best allies when you play mid/top/jg/ad/sup
- worst, best enemies "
- monster.gg (괴물쥐쥐)
- can I res.render an html file? (instead of using pug as view engine)
- 당연히 될듯?
- when using fetch, summoner name is included in url. the problem is, if the name is in korean, error occurs.
- ==> URLs can only be sent over the Internet using the ASCII character-set, so the problem occurs because korean characters are converted into ASCII format.
- use encodeURI() function?
- api fetch를 할때 url을 사용하게 되는데 소환사 이름이 한글일 경우 url encode를 한 값을 넣어야 함.
- matchilist data를 api에 요청하는 방식에 대한 고민 - 일단은 2번 방법으로 진행중.
- champion별로 나눠서 하기:
- 장점:
- played champion마다 matchlist를 따로 관리할 수 있도록 한번에 구현할 수 있다.
- 단점:
- api에 user가 play한 챔피언마다 matchlist를 각각 요청해야 하므로 요청횟수가 챔피언 갯수 배 만큼 증가할 것이다.
- riot api는 api key마다 요청횟수에 제한이 있다고 한다. 요청횟수가 늘어서 좋을 것이 없다.
- 장점:
- user의 모든 matchlist를 한번에 요청한 후 필요에 따라 내가 직접 분류하기.
- 장점:
- user의 모든 matchlist를 한번에 요청하기 때문에 api에 user당 한번만 matchlist를 요청하면 된다.
- 단점:
- 경우에 따라 내 앱(서버)의 연산작업이 늘어날 것이다. (어느 정도로?)
- 장점:
- champion별로 나눠서 하기:
- 단순히 key-value 자료형인 해시맵을 쓰기 위해서 Map으로 championRecords와 encounteredChampionsList를 구현한건데 이게 성능적인 이점이 있는건가? 왜 일반 Object쓸 생각을 안했지? 일반 Object와 Map의 탐색(get) time complexity 알아보자.
- 일반 object의 경우는 각각의 property안에 다른 종류의 값들이 있을것이라고 기대하고 만드는 자료형인 반면
- e.g. car object면 modelName, price, color, materials, 뭐 이런 여러가지 property를 가질 수 있겠지
- map의 경우에는 같은 자료형의 element를 여러개 보관하고 싶은데 이름을 지어서 보관하고 싶을때 사용하는 느낌인거 같다. 그렇기 때문에 object의 용도와는 다른거 같음. 확실히 map을 쓰는게 의미상으로(semantically) 자연스러운듯?
- e.g. car map이면 "myCar": "yellow", "yourCar": "red" 뭐 이런식으로 같은 종류지만 다른 이름과 내용을 보관할때. 즉 내가 원하는 encounteredChampionsList나 champioinRecords는 맵을 쓰는게 맞다. 근데 성능상 차이는 있는지 궁금하긴 하네.
- 일반 object의 경우는 각각의 property안에 다른 종류의 값들이 있을것이라고 기대하고 만드는 자료형인 반면
- 함수는 한개의 동작만 하는 것이 좋다고 주워 들은 적이 있어서 그렇게 하고 있었는데.
- processWinrate과 updateMostEncountered함수는 db의 같은 내용을 사용하기 때문에 한번 접근할때 두개를 동시에 처리하는게 성능상으로는 좋을 거 같은데
- 오직 가독성 / 유지보수 때문에 따로 관리를 하는 건가? 어떤게 더 나은 코드일까?
- 롤api요청 앱을 만들때 db를 쓰는 이유가 뭐지? 내가 원하는 스키마로 데이터를 관리/사용하기 위함인가? 예를 들어서 유튜브는 자기네 앱에 사람들이 동영상을 업로드 하는거니까 당연히 자기네들이 알아서 동영상 정보를(동영상 파일 말고) 저장/관리 해야겠지만 롤 통계 앱 같은 경우에는 라이엇 api에 모든 정보가 다 있고 그걸 가져다가 가공해서 서비스하는건데 반드시 db가 있을 필요가 있을까?
- 매번 api에 요청하는거랑 db에서 가져오는거랑 서버 부담은 별 차이가 없지 않나?
- 게다가 db저장하면 db비용이 또 발생할거 아닌가?
- 내맘대로 스키마 관리 할 수 있다는게 가장 큰듯?
- 그럼 관리 / 개발 편의성을 위한건가? 성능 때문이 아니라?
- 아닌가 예를들어서 매치리스트 요청해서 매번 그때그때 통계값 계산 하고 그러려면 결국에는 어떤 container안에 데이터를 다 보관해야 할텐데 그 작업량이나 데이터는 서버에게 맡기기에는 너무 많을수도. 게다가 새로고침이라도 하면 다시 처리해야되겠네. 새로고침의 경우는 확실히 비효율이 맞는듯. db에 저장해뒀으면 불러오기만 하면 되는데.
- 성능을 어떻게 테스트하지?
- 예를 들어서 map자료형을 쓸 때와 object로 할 때, 두가지 경우의 데이터 처리 시간을 비교하고 싶으면 어떻게 해야 하지.
- morgan 같은 logger에 표시되는 응답 시간을 신뢰할 수 있나? 이럴때 쓰라고 있는건가??
- the maximum matchlist range allowed for one query is 100
- e.g. if you played tf for 431 games, then you need to execute 5 queries in total to cover all 431 games. (0-100, 100-200, 200-300, 300-400, 400-432)
- lol api matchlist index range seems it doesn't count the endIndex.
- which means that if startIndex(beginIndex): 0 and endIndex: 100, then it actually shows the list from 0 to 99, excluding the 100.
- so if you want a list of 100 matches from 0 to 99, then you need to input 0 for startIndex(beginIndex) and 100 for the endIndex.
- subdocument써보고 싶었는데 subdocument형식으로 Map type을 연결하는 방법을 알지 못해서 어쩔 수 없이 ChampionRecord Schema를 하나더 만들어서 ObjectId/ref 방법을 이용했다.
- 그래도
championRecords: { type: Map, of: { type: mongoose.Schema.Types.ObjectId, ref: "ChampionRecord"} } 방법을 알아낸 것은 좋았다.
- 그래도
- winrate with/against 해야함.
- 이후 frontend 기능 구현해야함
- 아이디 이름으로 검색 / 버튼으로 data process 실행되도록.
- 라이엇에 샘플 보내서 api 받아야 하고. 그 전에 시간차 api request해서 최대한 내 계정으로 data proess해보자. 도파걸로도 해볼까.
- 샘플 보내려면 publish까지 완료 되어있어야 하는건가? 그럴려면 좀 빡센데 ..
- webpack / publish작업 할때는 git branch 분기해서 작업하도록 하자. 그전에 웹사이트 자체는 거의 끝내놔야 함.
- processData를 유저 최초 검색시에 실행되도록 위치를 바꿨음.
- winrate 처리 / 저장했음.
- winrate 처리코드를 함수로 만듬. 다른 코드들도 쪼개서 정리할 필요 있을듯. 가독성이 없어서 며칠만에 봤는데도 무슨 코드인지 파악하는데 한참걸림...
- 지금 User가 ChampionRecord를 populate하긴 하는데 ChampionRecord Schema가 User에 연결되어 있다는 사실을 깨달음. 해야하는 이유가 있나? 일단 보류.
- 유튭 클론코딩할때는 User와 Video가 서로 연결되어 있었음. (Objectid, ref)
- 이제 frontend 해야함.
- user.populate("championRecords")를 해주어야 render로 view에 user전달할때 championrecord부분이 구현이 됨. 이부분 확실히 이해하게 됨. (schema정의 시에 ref 해놓은 덕분이라는 점)
- 소환사데이터 화면에 어떤 데이터를 어떤 식으로 어떤 순서로 보여줄지 계획/대충구현 해봤음.
- op.gg같은거 보면 사용자가 표를 조작하면서 원하는 속성?으로 표 개체들을 재정렬 할 수 있도록 되어 있는데. 나도 그렇게 하는게 좋을듯.
- 검색해보니 html table과 aria? 로 구현할 수 있는듯 하다. html파일 따로 만들어서 연습해본 후 pug로 적용해보자.
-
db에 챔피언은 전부 championId로 저장해 놓았기 때문에 view에서 쓰려면 championId를 가지고 필요한 정보들을 불러와야 한다
- 예를들면 챔피언 한글 이름을 불러와서 페이지에 표시해주어야 하고
- ddragon url로 챔피언 이미지 등을 가져오려면 영어 이름도 알아야 한다.
-
근데 문제는 이 json은 여러번 요청하는건 비효율인데. 그럼 어떻게 처리해주어야 하지? app시작할때 한번만 request해서 어디 저장해놓는 방식으로 해야하나?
-
그냥 json으로부터 object를 만들어서 물리적인 js파일로 저장해두고 쓰는게 가장 효율적일지도?
- 이러면 패치때만 업데이트 해주면 됨.
-
일단 로컬에 있는 json파일을 import로 불러오면 object형식으로 자동으로 읽힌다는 것을 알게 되었음.
- 괜히 fs.readFile, JSON.parse() 쓴다고 뻘짓했다.
-
summoner 페이지가 라우터에 요청되면. json을 불러와서 불러온 json내용을 championid(숫자)를 key로, array를 value로 가지는 object로 새롭게 처리해서 render로 넘겨주는 방식을 취함.
- 즉 view pug안에서 championId(숫자)로부터 champion이름 등을 불러올 수 있게 처리해 놓은 것.
- array안에는 ddragon asset불러올때 쓰이는 챔피언 이름(asset이름으로 쓰이기 때문에 띄어쓰기 포함), 챔피언 영어이름(띄어쓰기 포함), 챔피언 한글이름, 이 세가지가 있음.
- summoner페이지를 불러올때마다 이 object처리를 해주어야 하는게 비효율이긴 함. O(numOfChampion)의 시간복잡도.
- 한번만 처리해서 이 object를 외부 js파일에 저장해버릴까? 그게 낫겠다.
-
이제 해야할일 정리해보자.
- 요청시간 제한 걸어서 소환사의 전체 match 처리할 수 있게. 코드 작성/돌려보기.
- 제출할때는 작동이 되는걸 보여주기 위해서 다시 10~50개 match만 처리하도록 바꿔서 제출하자.
- 시험삼아 50개정도 해보는데. 이게 50개도 많네. 1분안에 두명 검색하면 요청 제한 초과해버림.
- front-end: css / js
- 배포
- 라이엇에 api승인받기.
- 최종배포.
- notable enemies/allies(friends) 기능 구현함. db를 순회하는 식으로 했음.
- aria는 특수한 목적이 있는 놈이란걸 알게됨. 스크린리더?랑 관련된거라고 함.
- 결국 interactive table을 만들기 위해서는 html table태그들과 js(eventlistener)를 이용해야 함.
- https://www.delftstack.com/ko/howto/javascript/javascript-sort-html-table/
- 여기 링크에서 기본 소스를 찾음.
- 여러개 테이블을 모두 선택하여(querySelect) js를 각기 적용하는데 어려움을 겪었는데(각각의 테이블이 자기들끼리 잘 sort되도록 기능구현을 해야함) forEach방식으로 해결했음.
- 태그들을 array형태로 저장해서 sort비교 시킬 수 있다는 사실을 알게됨. 물론 비교함수(comparer)도 만들어 줘야 하지만
-
winRate을 표에서 js로 sort할때 50%가 100%보다 더 먼저오는 버그가 있다.
-
원인 추측: %를 붙이다 보니 string취급이 되고 string상으로 descending order일때 첫글자인 5가 1보다 크기 때문에(bx가 axx보다 먼저 오는 것을 생각하면 된다) 그렇게 되는거 같다.
-
근거: %를 제거하고 정렬을 해보면 숫자 크기비교를 하기 때문에 정상적으로 100이 50보다 먼저 오는 것을 확인했다.
-
해결책 구상:
- 데이터를 숫자로 전달한 다음에 sort를 먼저 처리하고 나서 js로 %를 뒤에 붙여주는 방식을 구현할 수 있다면 해결 할 수 있지 않을까?
- sort 알고리즘을 수정해서 %가 있을경우 %를 제외하고 숫자로만 정렬 처리하도록 할 수 있지 않을까?
- parseInt라는게 있던데 이거 쓰면 편하게 구현할 수 있을지도?
-
아닌가?
- 3.14159. toFixed(2); // 3.14 returns a string.
- parseFloat(3.14159. toFixed(2)); // 3.14 returns a number.
- 이런 내용을 봤는데 .toFixed()써서 string취급 되는건가 혹시?
-
-
사용자가 버튼을 눌러서 전적갱신을 명령할 수 있는 기능을 구현해야 함.
- 이유: 앱이 자동으로 검색시마다 갱신하는것보다 사용자가 필요할때만 갱신하도록 하는 것이 서버의 부담이 적음.
-
사용자 입장에서 어떻게 작동해야 하는지 생각해보자.
- 우선 마지막으로 갱신된 시간을 화면에 표시해서 사용자가 갱신을 할지 말지 판단할 수 있어야 한다.
- 그러려면 db의 user개체에 마지막으로 갱신된 시간을 저장해놓아야 함
- 갱신 버튼을 누르면 갱신 처리가 되어야 한다.
- 우선 갱신 가능 여부를 판단해야 함. 즉. 마지막으로 갱신된 시간 이후에 치뤄진 경기(match)가 있는지를 보고나서, 있다면 갱신하고 그렇지 않다면 갱신하지 않아야 겠지.
- 직접 api요청을 해서 확인해본 결과 beginTime에 해당 유저가 플레이한 마지막 매치의 timestamp이후의 time을 입력하여 요청하면 not found error가 나온다.
- RESPONSE BODY { "status": { "status_code": 404, "message": "Not found" } }
- 이런 식으로.
- beginTime에 마지막 갱신시간 값을 넣어서 api에 matchlist요청을 하여 totalGames가 0
- 직접 api요청을 해서 확인해본 결과 beginTime에 해당 유저가 플레이한 마지막 매치의 timestamp이후의 time을 입력하여 요청하면 not found error가 나온다.
- 마지막 갱신 이후의 전적(matchdata)만 불러와서 추가하고 통계값 처리(processdata)를 해주면 될듯?
- method POST, view없는 api를 이용하여 구현할 수 있을듯.
- 우선 갱신 가능 여부를 판단해야 함. 즉. 마지막으로 갱신된 시간 이후에 치뤄진 경기(match)가 있는지를 보고나서, 있다면 갱신하고 그렇지 않다면 갱신하지 않아야 겠지.
- 처음으로 검색된 소환사는 자동으로 갱신을 해주도록 하자.
- 마지막으로 갱신한지 일정시간이상 지나지 않았을 경우 갱신할 수 없도록 해야한다. 1분정도면 적절하지 않을까? op.gg는 2분이네
- 그리고 이게 ajax인건가 갱신 버튼을 누르면 최근전적 부분만 바뀌고 웹사이트 전체가 새로고침 되지는 않음.
- fow의 경우에는 전체 페이지가 새로고침 됨. 난 일단 js locate.reload()로 전체 새로고침하는 방식을 쓰려고 함.
- 우선 마지막으로 갱신된 시간을 화면에 표시해서 사용자가 갱신을 할지 말지 판단할 수 있어야 한다.
-
계정은 있지만 기록된 전적이 없는 소환사의 경우 예외처리를 해주어야 할듯. 아마 status object를 return할테니 그걸로 조건 판단하면 될듯.
-
기능 구현을 머릿속으로만 생각하는게 아니고 적거나 type해서 정리하고 진행하니까 훨씬 이해도 잘되고 효율이 괜찮은거 같다. 실수하거나 무언가 빼먹을 일도 줄고.
- dataset를 활용해서 서버에서 js로 변수 전달이 가능하다?
- ㅇㅇ. updatebutton event listener만들때 username이 필요해서 이때 활용함.
- lastUpdateTime.js도 수정하자. 괜히 getElementById로 뻘짓했네.
- 이게 전적 분석이 시간이 굉장히 오래걸리네. 열개만 하는데도. 천경기 이상 분석하려면 얼마나 걸리는거야 ..
- api요청/응답 속도가 가장 큰 원인인듯? 왜냐면 summoner처음 검색 시에 matchlist length나 numOfGamesPlayed가 콘솔에 찍히는 속도가 굉장히 느린걸 봤음.
- op.gg같은 서비스는 어떻게 그렇게 빠른거지? production api key는 혹시 더 빠른가??
-
db의 user가 matchlist array를 반드시 가져야 하는지 모르겠음.
-
그냥 local matchlist array를 바로 processData할때 쓰면 되는거 아닌가?
- winrate, championrecords등을 처리하고 나면 matchlist를 가지고 있을 필요는 없는거 같은데??
- git 분기해서 진행해볼까.
-
어떤게 더 나은 방법인지 모르겠다.
-
마스터, 그마, 챌린저는 티어 이름 뒤에 숫자 표시되지 않도록 해야함.
-
업데이트 버튼 클릭시에 summonerinfo, rankinfo등은 항상 업데이트 할 수 있도록 해야함.
- 다만 2분제한은 두고
-
하지만 마지막 업데이트 이후 치뤄진 경기가 없다면 경기/championrecords스탯관련 데이터는 업데이트 될 수 없도록 해야함. (processdata말임.)
-
데모버전/테스트 위해서 matchlist도 그냥 10~100개 정도만 찾도록 일단 해서 속도 빠르게 해볼 수는 있을듯.
- match-v4가 deprecated되어서 9월16일 이후 match data는 검색이 안되는듯.
- match-v5에서만 되는 것을 확인했음.
- match-v5에서 2년간의 데이터가 검색이 된다면 아예 match-v5로 코드를 전부 바꿔야 할거같고.
- 그렇지 않다면 9월16일 기준으로 이전 데이터는 v4에서. 이후 데이터는v5에서 각각 불러오는 방식을 취해야 할듯. 아이고.
- 코드 convert?작업을 시작했음. 생각보다는 금방할듯.
- 신챔프(벡스)에 대한 데이터가 내 앱에 없어서 테스트 중에 에러가 발생하였다(최근 전적에 벡스와 함께 플레이한 적이 있는 플레이어의 전적검색을 테스트 했음)
- 이런 신챔프 추가에 대한 업데이트는 어떻게 자동화할 수 있을까?
- 피들스틱이 ddragon에 id: "Fiddlesticks"라고 표기되어 있는데 match api에서는 "FiddleSticks"로(s가 대문자)되어 있기 때문에 에러가 발생했다. 왜이런지는 모르겠다. 현재까지는 다른 챔피언이 이런 경우는 없다. 따라서 내 champion_processed.js의 Fiddlesticks를 FiddleSticks로 바꾸어 주었다. 디스코드에 fiddlesticks로 검색하면 해당 이슈가 다뤄졌던 것을 확인할 수 있다.
-
api match called, matchId: KR_5489084861
-
api call error, status: { message: 'Rate limit exceeded', status_code: 429 }
-
이런식으로 rate limit exceeded라는 에러가 뜰때가 있다. 계산상으로 2초 대기 후 요청하기 때문에 그럴리가 없는데 랜덤으로 종종 발생하는거 같다.
- 내 추측으로는 그냥 api 전체적으로 많은 요청이 발생했을때(나 뿐만 아니라 다른사람들 포함해서) riot api측에서 이런 제어를 하는게 아닌가 싶다.
- 만약 그렇다면 이렇게 누락된 녀석들의 matchId만 모아서 다시 요청해서 추가하는 기능을 추가해야 할듯?
- 뿐만 아니라 다른 error와 status_code가 뭐가 있는지 알아봐야겟다. 아니면 status_code도 함께 저장하도록 코드를 만들어서 직접 확인해보는게 좋을수도 있겠고.
-
괴물쥐쥐의 주요 서비스는 소환사 개인의 전체 챔피언별 상대 전적인데 이
- (아니면 유명 플레이어의 전적이 궁금한 일반 유저들이 검색할 수도 있겠다. 내가 그런 경우가 많으니)
- 아무튼 뭔가 지속적으로 사람들이 방문할 만한 서비스가 필요하다고 느꼈다.
- 그래서 생각한게 최근 몇경기(10, 50, 100, ...)동안 만난 챔피언들을 정리해서 서비스해 보는건 어떨까 하는 아이디어.
- 메타 챔피언, 혹은 그냥 내 티어, 내가 자주 만나는 챔피언을 알 수 있도록 말이다.
- (내가 플레이한 챔피언과 상관없이 말이다)
- 어쩌면 그냥 기존 데이터 가지고 합치는 방식으로 할수도 있을듯?
-
어떤 동작 중에 서버에 에러나면 에러 화면을 보여주도록 해야함 무한로딩 시키지 말고.
- 할거/고민 정리
- 처음 summoner화면이 로딩 되면 encountered champions table이 디폴트로 most played against순으로 정렬되게 하고 싶음.
- 50% 100% 정렬 버그 수정
- 지연시간 주석처리 해놓고 loop max지정하기 getmatchlist는 한번, updatechampionrecords는 5번정도?
- 마지막 갱신시간 표시할때 서버에서 epoch time을 받아온 후 브라우저 js에서 다시 일반 시간형식으로 바꾸는 방법을 쓰고 있는데 이게 시간이 좀 걸리다 보니 epoch time이 그대로 노출됨. 이것도 수정하면 좋을듯.
- 이렇게 했을때 한사람당 대충 얼마나 걸리는지 확인해보기
- 로딩할때 로딩화면 보여주는 등의 해결책을 생각해봐야할지 판단
- 소환사 데이터 처리중에 또다시 검색되었을 경우 현재 에러가 발생하는데 이거 어떻게 처리할지도 판단
- 이것도 production key받고 나서 다시 판단할 수 있을듯.
- 접기 펼치기 화살표 만들어서 좀더 직관적인 디자인 만들기. 아마 fontawesome에서 가져올 수 있지 않을까 대충 삼각형 모양으로
- 서버 에러 발생했을때 페이지 처리
- encounteredChampionsTable에 총 만난 횟수(played against + played with)추가할지 고민
- 최근 ~판동안 만난 챔피언들 기능추가 고민
- 시즌별로 볼 수 있도록 페이지 만들어야 함.
- 당장 11월에 시즌 11끝남. 근데 그렇다고 시즌 12가 당장 시작하는건 아닌데 이 기간의 기록은 어떻게 분류해야 하는거지
- 걍 여전히 시즌 11로 분류해야 맞지 않을까 싶은디
- 아마 전시즌 통합으로 볼 수 있게도 하면 좋겠지?
- 당장 11월에 시즌 11끝남. 근데 그렇다고 시즌 12가 당장 시작하는건 아닌데 이 기간의 기록은 어떻게 분류해야 하는거지
- 티어 엠블럼 op.gg에서 가져오고 있는데 혹시 문제될 수 있으니 소스 바꿔야 할듯.
- 일단 티어 이미지를 다운 받아서 express static이용해서 스태틱 폴더로 만들어서 사용하는 중. 아마 storage에 저장하면 링크로 가져올 수 있을듯.
- 벡스 이미지 깨짐. 그리고 소환사 프로필 아이콘도 이미지 깨지는것들 있더라.
- 이건 패치 버전 문제임.(ddragon링크 11.18.1 => 11.20.1) 이것도 자동화 할 수 있는 방법이 있을거 같다.
- 피들스틱 이미지 깨짐. 이건 패치버전과 무관하게 champion name이슈. 위에 자세하게 적어둠.
- pug에서 피들스틱인 경우 예외처리 함.
- 배포(deploy)
- 배포하기 전에 기능에 대해 좀더 안내 가이드? 같은것을 사이트 내에 표시해야 하는지 생각해 봐야겠음.
- deploy했는데 너무너무 느림. updatechampionrecords할때 matchdata를 count해서 db저장은 한번만 하도록 하면 속도가 많이 개선될 수 있을까? (db update과정을 loop에서 빼고 마지막에 한번만 하도록)
- 라이엇에 production key요청
- 프로덕션 키 받고 나면 처리시간 얼마나 걸리는지 확인하고 만약 여전히 오래걸린다면 로딩 방식/화면 고민좀 해봐야 겠음. 예를들어 일단 소환사프로필만 먼저 띄워주고 chapmionrecords는 로딩완료되면 그때 띄워준다던가.
- winrate을 서버/db에서 계산/저장하지 않고 프론트엔드 js에서 할지 고민.
- 유의미한 시간차이 / 서버 계산처리량 차이 / db저장용량 차이 가 있을까??
- 함께 플레이한 적이 없을때는 승률/wins X표시 시켜야됨.
- 이거 때문에 sort함수도 수정했음. 정확히는 getCellValue 함수를 수정함. 왜냐하면 x표시된 녀석들은 해당사항이 없는 녀석들(N/A)이기 때문에 sort order가 ascending이던 descending이던 항상 맨 아래에 오도록 해야되는데 그러기 위해서 descending인 경우에는 -1을 주고 ascending인 경우에는 infinity를 줘서 항상 맨 아래로 가도록 함.
- 이 과정에 parentNode, children등등 새로 알게된 html dom property등을 활용할 수 있어서 좋았음.
- 이제 안쓰는 파일들 정리. controllers
- champion_processed.js 파일 위치?
- 언랭일때 소환사 프로필 처리 해야함. e.g. 지이0l잉
- 서버, 언어 바꾸는 기능. (언어는 일단은 영어/한국어만 할 수 있겠지. 혹시 문자 달라지면 인코딩도 맞춰서 바꿔야 하려나?)
- 업데이트 제한기능 추가해야 함. (2분으로 하자)
- 소환사 검색 안될때 그냥 홈으로 돌리지 말고 검색 안된다고 설명 해줘야 할듯.
- 티어 엠블럼 외부 링크 통해 가져오는게 바람직할듯 한데. 그렇다고 외부 저장소를 이용하는게 맞나?
- 한사람의 판수가 rate limit을 넘을때? / 어떠한 이유에서든 rate limit에 제한이 걸렸을때 서버에서 이를 제어할 수 있도록 해야함.
- 업데이트 할 경기 없을때 좀더 효율적으로 필요없는 작업 하지 않도록.
- winrate를 서버에서 계산 안해도 되네? 프론트엔드에서 처리하면 되니까.
- 기록 17 부분 deploy를 하기 위해 dev용으로 project를 따로 복사해놨다.
- heroku cli 설치된 상황에서 heroku logs --app=monstergg 이런식으로 터미널에서 명령어를 통해 내 앱에 대해 로그를 확인할 수 있다는 것을 알게됨. (heroku git 안쓰고 github connect하더라도 사용 가능)
- 그리고 뒤에 --tail 붙여주면 실시간으로 서버로그 확인 가능.
- https://blog.uniony.me/lol/game-duration/ 블로그에서 중요한 힌트를 얻었다. api와 db호출을 루프로 순차적으로 하는 대신에 한번에 할 수 있다고 한다. 그러면 당연히 처리속도가 매우 빨라질듯.
- 검색해본결과 Promise.all에 대해 알게 되었다. https://www.youtube.com/watch?v=xWRp1K8ga9s
- Promise.all을 사용함으로서 api응답속도(fetching)는 확실히 빨라진듯.
- 서버 로그로 봤을때 그다음 속도 지연의 큰 원인은 db처리속도 인듯. db요청 횟수를 줄여야 할듯. 루프 안에 db요청을 하지말고 모아서 한번만 처리하는 방향으로 해야함. 그럼 서버의 계산 처리부담이 커질거 같긴 함
- 내 컴퓨터에서 돌려봤을때랑 비교했을때 db를 포함한 루프 진행 속도가 큰 차이가 남.
- 또하나의 속도 저하의 원인은 서버 자체의 응답속도. 헤로쿠 서버가 미국에 있다는데 aws를 써보는 것을 고려해봐야 할듯. db보다는 적은 영향인것처럼 보임.
- db도 역시 promise.all 사용해서 한번에 전부 요청하는 식으로 처리했음. 속도 유의미하게 빨라진듯.
- 또 db를 us east region으로 옮겨봤는데 효과가 있는거 같음. 헤로쿠 서버가 미국에 있기 때문에 서버와 db가 가까운 것이 사이트의 전체 응답속도를 줄이는데 효과가 있는거 같다. 이게 실제로 효과가 있다는게 신기하다.
- api fetch promise.all, db 통신 promise.all, 그리고 db물리적 위치 변경(서버와 가깝도록)
- ==> 이 세가지를 통해 12초?정도 걸리던 속도를(5경기 처리하는데만 그랬고 만약 기존 방식으로 수백경기를 처리한다면 경기수에 비례해서 더 시간이 걸렸을 것) 3초대로 줄였다.
- server log에 service=에 표시된 숫자를 기준으로 말하는거긴 한데 실제로 시간 재보면 그거보다는 조금 더걸리는거 같다.
- 그래도 여전히 더 빨랐으면 좋겠는데 더 생각해볼 수 있는 방법은..
- getSummonerRankInfo 없이 getMatchlist를 할 수 있다면 주요 api fetch지연요소 한가지를 해결할 수 있지 않을까 싶음. 근데 그게 쉽지는 않을듯 promise.all하려면 총 몇경기인지를 알아야 되는데 현재는 그걸 getSummonerRankInfo를 통해 근사값을 구하는 방식으로 해결하고 있기 때문에. 몇경기인지 모르고 promise.all을 할수가 있나?
- 서버와 db를 둘다 한국 가까이로 옮기는게 아마 속도를 크게 올릴 수 있지 않을까 싶은데. aws서버 deploy에 대해 알아봐야 할거 같다.
- 일단 콘솔로깅 해서 정확히 어느 부분에서 시간이 가장 걸리고 줄일 수 있을거 같은지 분석해보자.
- 또한가지 api서버가 어디 있는지도 영향이 있을거 같긴한데 디스코드에 검색해본 결과
- "Are the APIs servers physically located in their relative regions? euw1 in EU, na1 in US, kr in Korea etc... because i noticed that requesting from EU to na1 or kr takes longer than requesting to euw1"
- "Yes to some extent. The API edge proxies live in 3 regions (americas, europe and asia) and when you request data from a league region, it's taken care of by the closest proxy"
- 라고 한다. 그럼 미국에 있는 서버에서 kr api요청을 하면 손해라는건가??
- get username from the user. (search)
- get encrypted accountId
- use the id to get matchlists for 156 champions
- save the matchlists info of the user.
- in js array of objects format
- e.g. userMatchlists = [ { id: 4, played: true, matchIdList: [343543, 344354, 13241, 45324, ... ], }, { id: 266, played: no, matchIdList: [], ... }, ... {} ]
- analyze and calculate played-champions records & winrates and save them
- e.g.
- userRecords_championsPlayed: [ { id: 4, numOfTimesPlayed: 431, encounterList: [ { id: 266, played: true, playedAgainst: 15, playedWith: 21, winAgainst: 6, winWith: 11 winRateAgainst: 0.4, winRateWith: 0.5238095238 }, { id: 2, ... }, ... ] }, { id: 91, numOfTimesPlayed: 124, encounterList: [...] }, ... ]
- show it to user (render on website)
- get valid API key
- get username (by search in real app)
- get encrypted account id from username
- create and save User model data ( username, encrypted account id, level, avatar info, matchlist array( => normal js array of objects, and the object contains matchId, championId, and timestamp), championRecords array( => array of subdocuments/childrenSchemas the subdocument called championRecord will contain championId, numOfTimesPlayed, and encounteredChampionsList( which is again, an array of object, encounteredChampion, which will contain id, playedAgainst, playedWith, winAgainst, winWith, winRateAgainst, and winRateWith ) ) and etc.
- get matchlist data from the encrypted account id
- save them in the array in the User object
- iterate the matchlist, and get match data from the matchId
- => this will result in many many requests.
- with each match data, create(if there isn't one yet) and update the championRecord objects(and should create and update the encounteredChampion object inside).
-
JS map? (hash map)
-
mongoose Map Type
-
mongoose subdocument
-
JS .find(x => x.key == value) method.
- e.g. if(matchData.teams.find(team => team.teamId == 100).win == "Win")
-
15th Sept
- Object.fromEntries(a_Map) 로 Map자료형을 Object로 convert할 수 있다.
- mixin inside a mixin 가능하다.
- Spread syntax (...)
- an_array = [...Map]
-
js 함수에는 object와 array등을 통째로 parameter로서 pass over할 수 있다.
-
js에서 variable을 declare하고 값을 assign하지 않는다면 undefined를 가진다.
-
js object가 const여도 property값은 바꿀 수 있다. 또한 property를 추가하고 제거하는것도 가능하다. 포인터같은 주소할당의 개념인건가?
- JS basic syntax and ES6 syntax
- Object, Map
- js push vs. concat performance
- js undefined vs. null
- js if false conditions
- is null, 0, undefined always false? what elses are falses?
- tableSort함수 syntax 이해하기.
- prototype-oriented language? 개념 (https://stackoverflow.com/questions/256754/how-to-pass-arguments-to-addeventlistener-listener-function)
- lexical this
- forEach
- async await?
- continue, break 사용할 수 없음.
- 4가지 for관련 구문 차이점 / 성능비교
- for (let i = 0; i < arr.length; ++i)
- arr.forEach((v, i) => { /* ... */ })
- for (let i in arr)
- for (const v of arr)
- promise then / async await
- mongoose / mongodb 기본기
- fields? documents? types? schema?
- when to use Subdocument
- how to update subdocument values of a data object
- and when to use Schema.Types.ObjectId ( how are the two different? )
- html dom properties (selectors)
- parentNode
- children vs. childNodes
- querySelector vs. getElementsByTagName