월요일이되고, 가장 먼저 캐릭터 접속 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 을 작성하다 보니, 오늘 진행한 내용은 결국 작성하지 못했다.

 

다음 시간에 !!

 

 

 

 

 

 

 

 

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

 

 

이번에 사용한 방법은, 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 하면 아무튼 의도대로 작동하는 상태

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

 

 

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

 

 

 

어제에 이어서 2주차의 마지막이자, 입문주차의 마지막 부분을 학습했다.

 

범위로만 따지면 2.9~ 2.11에 해당하는 부분이라 금방 끝날 것으로 예상하고

오늘 숙련주차를 들어갈까 말까를 생각했었는데,

 

입문주차 마무리를 겨우 할 정도로 빠듯했다.

 

 

2.9 코드 서식 정리하기

에 해당하는 부분은, 이전 과제에서 Prettier 를 간단하게 다루고 넘어갔기 때문에 

어느 정도는 알고있던 부분이다. 

{
    "printWidth": 50,
    "tabWidth": 4,
    "singleQuote": false,
    "trailingComma": "es5",
    "semi" : false,
    "arrowParens": "always"
  }

나름대로 원하는 스타일로 prettierrc.json 을 작성 해 보았다.

singleQuote 부분은 기본적으로 single을 사용할 예정이지만,

한글이 포함되거나 포함될 가능성이 있는 문자열만 "" 을 사용해보려고 한다. (최소한의 개성..?)

물론 변동될 수 있지만 말이다

 

 

 

2.10 배포를 위한 Git 학습 

또한 이전 과제와 그 전에 해당하는 최초의 과제를 진행하며 어느정도 학습한 부분이 있다.

다만, 이전에는 Github Desktop 라는 최신식(?) 사용자 친화적 인터페이스를 갖는 보조도구를 사용했기에

이번에는 강의에 나온 것 처럼 터미널을 이용하여 Git 과 연동하는 작업을 해 보았다.

 

 

 

Github desktop가 알아서 처리해줬던 부분인,

Git 명령어를 먼저 알아보았다.

 

 

 

 

간단하게, 이미 알고있던 desktop 의 방식과 비교하며 정리해볼까? 

 

 

git init 은 현재 위치한 경로를 git repository로 지정하는 명령어 이다.

GUI 에서는 create new repository를 통해 init 기능을 사용할 수 있다. 

(디렉토리를 지정하여 새로운 레포지토리 생성)

아마 Add local repository도 비슷한 기능인듯 하다.

오히려 이쪽에 가까울지도?

 

변경 사항을 스테이징 영역에 올리는 명령어로..

이 부분은 아마 Github desktop를 이용한다면 스킵되어버리는 명령어로 확인이 되어진다.

뭔가 생소한 기능인것 같아서 검색을 해보니, Github desktop 에서는 commit 과 동시에 add가 이루어진다 

라고 설명하는 경우가 많다.

 

개인적으로는 gui의 인터페이스에서

레포지토리로 지정한 폴더의 모든 파일이 자동으로 add 된다는 느낌이긴 한데 맞을지 모르겠다.

(일단 모든 파일이 add 되고, 커밋을 할지 ignore 할지 결정하는 느낌?)

 

 

 

깃의 대표적인 기능중 하나로, 변경사항을 기록하는 것이다.

이 시점으로 얼마나 내용이 수정되었는지, 등록이 되기 때문에 

원한다면 등록했던 많은 커밋 중 하나의 시점으로 파일을 복원하는것이 가능하며,

추가적으로 코멘트를 달아서 해당 커밋의 내용을 직관적으로 설명할 수 있다.

저지른다

 

 

 

본격적으로 git과 github 를 연결하는 명령어로, 

정확히 말하면 로컬 저장소에 원격 저장소를 추가하는 기능이라고 설명되어있다.

commit, push 한 데이터가 어디로 날아갈지 지정하는 명령어 이다.

 

Github desktop 에서는 첫 레포지토리 생성 후

commit 등 원격 저장소를 필요로하는 작업을 하기 전에 

Publish resository를 통해 지정하게 되어있다.

github desktop 에 로그인된 github 계정에 자동으로 연결된다.

 

 

 

클론으로 repository를 생성한다면, 

클론 할 원격 저장소의 주소가 바로 필요하기 때문에, 시작과 동시에 

입력을 하게 되어있다.

git clone 과 remote add 기능이 합쳐졌다고 보면 될 듯 하다.

 

push 는 commit 한 내용을 본격적으로 원격 레포지토리에 업로드 하는 명령어 이다.

 

GUI 에서는 commit 을 하면, 자동으로 push 를 진행하는 버튼이 활성화 된다.

 

 

 

 

push 가 업로드 라면, pull 은 다운로드 라고 할 수 있다.

원격 저장소에 push된 최신 변경사항들을  로컬 저장소로 가져오는 작업을 말한다.

GUI 에서는 해당 명령을 통해, 아직 적용되지 않은 최신 사항을 체크 할 수 있으며,

있을 경우 pull 버튼이 활성화 되어, 비교 및  적용이 가능하다.

 

 

 

2. 11    AWS 배포하기 

오늘 가장 난해했던 부분이다.

 

이전 강의를 통해 만든 서버를 본격적으로 배포하기 위한 수단으로...

기본 사양의 서버인 EC2 에 해당하는 서버를 1년 동안 무료로 사용할 수 있는

대인배  클라우드 서비스 라고 할 수 있다. (물론 프리티어라도 막쓰면 1년 내에 요금이 나올수도 있다)

 

 

강의에서 알려주고는 있지만, 기본적으로 사용한 만큼의 요금이 발생하는 유료 서비스 이며,

잘못 사용 할 경우 금전적인 문제 또는 정보 유출 등의 문제가 발생할 수 있기 때문에, 

추가적으로 알아보고 조심조심소심소심; 하며 진행했고....

시간을 많이 잡아 먹었다 ! !

 

아무튼간에 회원가입과 EC2 인스턴스의 생성은 마쳤고,

 

 

pm2 를 활용하여 app.js 를 구동하는것도 성공했다.

 

웹 브라우저로 접근했을때도 정상적으로 작동하는 모습 !!

위에 보이는 5252 는, 같은 조원이 해당 페이지에 접속해서 남긴 글이다. 

다른 사람도 접근이 가능한것을 확인 !

 

 

pm2 log를 통해 로그도 정상적으로 확인 가능하다.

 

 

여기까지 봤으면 그래도 오늘 할 일은 다 한듯 하다..

 

 

 

 

 

본문에 앞서서,

오늘은 월요일임에도 불구하고 컨디션이 나쁘지 않았는데

알고리즘 풀이도 뜻대로 되지 않고,

강의는 여전히 새로 접하는 내용의 정의와 활용의 반복으로 이루어졌기 때문에

정신력의 소모가 상당했다...

(하늘에서 정의가 빗발친다...)

 

오늘 학습한 내용은, Node.js 입문주차 2주차에 해당하는 내용이며,

 

영상의 길이만 1주차의 4시간을 뛰어넘은 6시간인 데다가 , 앞서 서술한 내용처럼 

학습이 원활하게 흘러가지 않았기 때문에 전부 듣지 못했다.

2-8 에 해당하는 내용인 Joi 활용에 대한 부분 까지만 학습했다.

 

 

1. MongoDB

강의의 시작은 MongoDB 의 활용이었다.

첫 과제때 잠깐이나마 활용했던 Firebase 처럼, 비관계성 데이터 베이스중 하나로,

이번 강의에서는 MonghDB 를 통해 database 를 구축하고 실습을 진행하게 된다.

 

추가로 Studio 3T 라는 GUI 툴을 활용해 MongoDB의 관리를 용이하게 할 수 있다.

 

그리고 또...

mongoose 라는 라이브러리를 통해

JavaScript 에서 MongoDB에 데이터를 쉽게 읽고 쓰게 해주는 ODM (Object Document Mapper) 도 활용한다.

 

 

2. 할 일 메모 사이트 설계

해당 주차의 메인 실습 내용에 해당하며,

앞서 배웠던 Express를 통해 서버를 열고, MongoDB와 연결하여,

서버가 종료되더라도 데이터가 저장되어 연속성을 보장할 수 있는 환경을 구축하며

 각종 모듈과 미들웨어를 통해 원하는 기능을 구현하는 학습이 진행되었다.

 

배우는 내용이 딱딱하니 TIL도 딱딱할 수 밖에 없는건가...

 

 

Studio 3T 를 통해 DB의 상태를 확인할 수 있다.

 

그런데 할 일 메모 사이트에 해당하는 DB가 보이질 않는다...

어디간거지? mongodb_prac ...은 아닌데...?   DB이름도 가물가물가물치

 

 

index.js 를 확인해보니, 분명 dbName 을 todo_memo 로 지정했고, 

 

데이터도 정상적으로 추가가 되고, get 으로도 확인이 가능한데 db가 없어..?

 

유령 데이터 베이스 ㄷㄷ

 

 

해답은 정말 간단했다.

Studio 3T가 보여주는 데이터 베이스는 실시간 또는 데이터 변동이 생기면 알아서 refresh 되는게 아니라

갱신을 해서 새로운 정보를 내놓게 해야한다... 

사실 Studio 3T를 껐다가 켰는데 Refresh 된거라 따로 Refresh 방법을 아직 모르겠다...

 

아.. 아무튼

 

할 일 메모사이트 만들기의 진행 내용은,

 

Express 를 통한 서버 구동, 

post() 를 통한 새로운 데이터 생성,

.findOne()  한 가지의 데이터를 선정

.sort('order')   오름차순 정렬

.sort('-order')    역순 정렬

.exec();  await 를 적용하기 위한 수단

 

.save();   클라이언트에게 반환

 

.get()   데이터베이스 목록 조회

.patch()   데이터의 내용 수정

.delete()  데이터의 삭제

 

추가로 joi를 활용한 유효성 검증 부분  (class 의 setter 와 비슷한 기능인건가..)

validate()   동기로 검증..? 

validateAsync() 비 동기로 유효성 검증

 

try/catch 를 이용해 에러 대응하기

에러 발생시  단순한 에러라도 서버가 내려가버리기 때문에

이런 경우는 try catch 를 통해 에러의 처리를 따로 해주는 작업이 필요하다

 

 

적어놓고 보니까 꽤 많은 메서드가 사용되었는데, 물론 모두 이해했다고 하기는 힘들다.

 

입문 주차가 이 정도면 숙련 주차엔 무엇을 배우게 되는걸까??

 

 

 

이번 과제.. 벌써부터 걱정이 된다.

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

24.09.05 TIL : Node.js 숙련주차  (0) 2024.09.05
24.09.03 TIL : Node.js 입문주차  (1) 2024.09.03
24.08.30 TIL : Node.js 입문주차  (0) 2024.08.30
[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28

 

 

오늘,  CH3 에 해당하는 내용이 발제되었다.

 

CH 3 의 주 내용은 주특기로, 

즉 Node.js 를 말한다.

 

 

 

 

 

 

1주차에 포함된 내용

 

무려 영상길이만 총 4시간에 달하며 

 

매 순간마다 새로운 개념과 용어가 등장하는데다가

거의 100% 이론 강의였기 때문에 정신이 급속도로 피폐해졌다...

 

 

내용을 전부 글로 작성하는것은 의미가 없기 때문에

짚고 넘어가야할 부분만 정리해야겠다.

 

 

 

 

브라우저의 통신 방식에 관한 내용이다.

 

user : URL 입력 -> DNS 서버로 전달 -> name을 IP형태로 -> IP에 HTTP로  Request

-> 웹 서버에서 Request 처리-> 처리결과 HTTP Response 를 웹 브라우저에게 전달

-> 웹 브라우저가 HTTP Response를 내용을 바탕으로 user에게 표시

 

 

 

 

 

DNS 는  IP주소 ( 8.8.8.8 )  ==== >   도메인 (google.com) 

형태를 변경해 주는 서비스 이다.

 

기존 8.8.8.8 형태는 IPv4 이고,

2001:db8::ff00:42:8329 같은 형태는 IPv6 로, 보안과 성능이 강화된 버전이다.

 

 

 

Node.js 란? 

사실, 이전 과제가 Node 환경에서 게임을 만드는 과제였기 때문에 

어느정도 머리속에서 정리가 된 상태이다. 그나마 아는 부분이기 때문에 따로 정리하지 않겠다 !

Package Manager 에 관한 내용 또한 마찬가지.

 

 

 

 

 

본격적으로 서버에 관한 강의가 시작되었다.

프레임 워크는 웹 서비스를 빠르게 구현할 수 있도록 도와주는 도구이며, 

밑바닥 부터 서버를 만들어 올리는것도 가능하겠지만, 현실적으로 비효율적이기 때문에

프레임워크를 통해 효율적인 개발하는것이 권장된다.

어디까지나 프레임 워크인 것이지, express 자체가 서버인 것이 아니다.

 

 

 

실습 !?

express.js 를 이용하여 만든 서버를 VScode 툴을 이용해 Node 환경에서 구동하고

Insomnia 를 활용하여 해당 서버주소에 접근하고 요청하는 내용을 Test 하는 과정 

 

????? 이거 맞아?????

 

express.js 프레임워크 설치 

해당 모듈을 참조

 

 

 

express 해줘 !!

 

 

/api/about  내용을 담당하는 goods.js

 

 

 

/api/news  주소에 접근시 리턴하는 내용과

/api/news/:newsId 에 접근 시 리턴하는 내용을 담은 news.js

 

 

해당 주소에 대한 접근 결과와 Headers 에 해당하는 내용을 보여주고

잘 작동하고 있는지 쉽게 알아볼 수 있게 해주는 insomnia 

 

newsId에 임의로 user 가 입력한 값을 server 가 전달 받을 수 있다.

 

그 과정에서 사용되는 .params 부분이 아직 이해가 안 안돼서 복습이 필요할 듯 하다.

 

뿐만아니라 사실 오늘 배운 server 에 대한 내용이 전부 낯설고 

제대로 이해가 안된것 같은 느낌이 든다...

 

function이 뭔지도 모른채로 알고리즘 문제를 처음 접했을 때,

이런 느낌이었던 것 같은데.....?

 

이럴 때에 해결 방법은, 이해 될 때 까지 박치기를 하면 된다 !

 

주말이 사라질 듯 하다.

 

 

이론은... 너무 힘들다 !!

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

24.09.03 TIL : Node.js 입문주차  (1) 2024.09.03
24.09.02 TIL : Node.js 입문주차  (0) 2024.09.02
[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27

 

 

 

HIKING- 1000M 는... 서비스 종료다.

 

오늘부터는 알고리즘 풀이에 좀 더 집중하고,

다음 주 부터 진행 될 CH3 과제를 준비하는 시간을 가... 가...

 

가 아니었다...

 

무려 100명이 넘는 수강생 중, 우수 제출자 2명을 선발하여 발표하는데,

그 2명 중 한명이 내가 된 것이다 !!!

 

 

              실화?

 

기왕 이렇게 된 김에,

오늘의 TIL은 발표를 했던 내용을 기반으로

과제의 결과물을 리뷰하는 방향으로 TIL을 작성하는것으로 방향을 틀었다.

 

 

그런데....

 

나의 발표 순서는 두 번째였는데,

앞서 발표한 수강생의 능력치가 심상치가 않았다.

 

 

??????

https://velog.io/@artbiit/NodeJS-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%A1%9C%EA%B7%B8%EB%9D%BC%EC%9D%B4%ED%81%AC-%EA%B2%8C%EC%9E%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0-%EC%B5%9C%EC%A2%85-%EC%84%A4%EB%AA%85

 

NodeJS 간단한 로그라이크 게임 만들어보기 - 최종 설명

여태까지 간단한 기능들의 모음을 먼저 만들고 게임 시나리오를 구현하는 식으로 소개를 했었습니다.이번에는 스크립트 흐름을 통해서 어떻게 구현했는지 완전히 작성해보려 합니다.이전글에

velog.io

 

위는 심상치 않은 발표자의 블로그 이며, 디렉토리 구성만 보더라도 어나더 레벨이라는 것을 알 수 있다....

 

코딩이 곧 생활 방식이신듯 합니다

 

열심히 만든 내 코드가 부스러기로 보이는 순간이었다..

 

 

아무튼... 내 발표는, 

1. 기획

2. 게임 시연

3. 주요 코드 리뷰

 

순서로 진행이 되었다.

 

 

1. 기획

 

기획에서 컨셉을 제외한 나머지 부분은 사실상 과제에서 fix가 되었기 때문에

readme 에 작성했던 컨셉 위주로 설명을 진행했다.

 

 

2. 게임 시연

 

따로 녹화를 한것이 아니기 때문에 영상은 남아있지 않지만, 

게임을 직접 구동하여 주요 기능을 보여주는 식으로 진행을 했다.

목록은 다음과 같다.

 

 1) 메인화면 등장 연출

2) 전투 진행

 

 

3) 지름길 스킬 연출

 

4) 게임 오버 출력

 

 

 

3. 주요 코드 리뷰

내 코드중에 그나마 쓸만한 것을 몇개 골라서 리뷰를 진행했다.

 

 1) 연출? 텍스트

varcolor ...?? 는 넘어가자

이전 TIL 에서 언급했던 !

오프닝, 엔딩, 게임오버, 스킬성공 연출에 사용된 그 함수이다.

 

위의 함수는 역순 나열로 오프닝에 사용된 함수이고, 

나머지는 정방향으로 나열하는 함수이다.

 

함수 내용을 해석하면 매우 간단하다.

 

첫 번째 매개변수 text에 받아온 임의의 문자열

ex " HIKING-1000M" 를

console.clear();  를 통해 모든 내용을 전부 지우고

첫번째 문자 M 출력 (역순)

다시 console.clear(); 

0M -> 클리어 -> 00M -> 클리어 -> 000M -> 클리어 ........ -> IKING -1000M -> 클리어 -> HIKING-1000M

를 출력하는 단순한 반복문이라고 할 수 있다.

 

특이 사항이라면, 반복문 끝에 await delay(setDelay); 인자를 받아서 

반복출력 간격을 조절할 수 있다. (스킬 성공 연출 출력속도와 게임오버 출력 속도가 다른 이유)

 

이제 여기서 튜터님의 도움을 받아 color 매개변수에 chalk.컬러명 을 인자로 받는 것으로

로그로 출력할 텍스트에 color 를 조절 할 수 있게 되었다. 

 

해당 부분은 

https://p-lani.tistory.com/43

 

8.23일 TIL 에서 기술한 바 있다.

 

 

 

2) 새로운 로그를 딜레이 출력

 

 

전투로그를 출력하는 부분에 대한 내용으로, 8.28  TIL 뒷부분에 작성한 내용이다.

 

oldLogs 의 내용은 즉시 전부 출력. -> 

이전 배틀 과정에서 추가된 logs 는 딜레이를 주어 출력하고, oldLogs 로 복사->

해당함수 실행 후 logs를 초기화 하는것으로 완성한 코드이다.

 

맨 아랫줄인 if문은 logs 가 비어있지 않은 경우, 딜레이를 만들어서

이후 내용인 행동 선택지를 띄우는 시간 사이에도 틈을 만들었다.

 

 

다른 코드들은 발표 시간 문제로, 또 그렇게 특별한 코드는 없기 때문에 스킵을 했고

 

그렇게 발표는 마무리 되었다 !

 

 

 

 

 

해당 과제를 진행하며 마주하게된 문제점과 해결 과정은 매일 TIL에 기록하였기 때문에

따로 회고가 필요하진 않을것 같다.

 

여기서 

 

찐찐찐으로 RogLike  과제는 마무리가 되었다.

 

많은걸 배우고, 또 직접 적용해보기도 하고

수많은 문제점을 마주하며 해결해보는 과정이 모두 재밌었고 의미있는 과제였다 !

 

다음주 부터 시작될 CH3의 과제도 잘 되길 바라는 마음이다.

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

24.09.02 TIL : Node.js 입문주차  (0) 2024.09.02
24.08.30 TIL : Node.js 입문주차  (0) 2024.08.30
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23

 

 

기존의 코드에서 수정한 부분이 있다. 

monster 를 생성하는 과정에서

class 를 적절히 활용하고 있다는 느낌이 들지 않았다.

      const monster = new Monster(RandomName[RandomNameChoice], 70, 5);

      //10층 막보
      if ( stage % 10 === 0 && stage !== 0 ) {
      monster.name = "최종BOSS : 정상으로 향하는 길"
      monster.hp = 2999
      monster.attack = 180
      }
      //5층 중보
      else if ( stage % 5 === 0 && stage !== 0 ) {
      monster.name = "중간BOSS : 계단지옥"
      monster.hp = 777
      monster.attack = 77
      }
      //일반 층
      else {
        monster.hp += stage*80+stage**3
        monster.attack += stage*1+stage**2

 

뭔가 마음에 들지 않는 구성이다.

일단 대충 때려만든 다음에 수치를 조작하는 형태라고 느껴짐...

 

특히, 채점 기준에서 벗어날 수도 있다는 생각이 들어서 코드를 수정해보기로 했다.

 

 

1차 시도

원하는 그림은

new Monster 생성 과정에서 new Monster( 정보 ) 인자를 넘겨서 

그것을 class 내에서 처리하게 만드는 것이다.

 

//10층 막보
    if (stage % 10 === 0 && stage !== 0) {
      const monster = new Monster("최종BOSS : 정상으로 향하는 길", 2999, 180)
    }
    //5층 중보
    else if (stage % 5 === 0 && stage !== 0) {
      const monster = new Monster("중간BOSS : 계단지옥", 777, 77)
    }
    //일반 층
    else {
      const monster = new Monster(RandomName[RandomNameChoice], stage * 80 + stage ** 3 + 80, stage * 2 + stage ** 2 + 8)
    }

 

위 처럼 작성을 하고 보니,

 

 

monster 를 찾을 수 없다는 결과가 나왔다.

 

if문을 지나간다면 어느 경우라도 const monster 를 실행하게 되겠지만, 

if 문 내 에서만 정의가 된 변수는 참조가 불가능한 듯 보인다...

 

아니 if문 해보면 무조건 const 된다니까? << 그건 니 생각이고 !!

 

추측하자면, if문이 실행되기 전에 선언된 함수, 변수 (인자 포함)  정보를 호이스팅 하는데

if 문이 아직 실행되지 않았기 때문에 정의 자체가 되지 않은것으로 보인다.

 

2차 시도...라고 하긴 애매하지만

if 문이 실행되기 전에 초기화만 해주면 일단 monster = undifined 상태로 실행이 진행될것이고, 

if 문을 들어가며 원하는 값으로 대입이 될 것이다.

 

하지만 보란듯이 밑줄이 그어졌다.

const 는 선언과 동시에 초기화가 이루어져야 한다...

사실 재선언, 재할당 모두 불가능한 const에 값을 안주고 선언만 하는건 말이 안되는 일이었다.

 

 

3차 시도 

왜 인지...

const 로 선언하지 않으면 안될거같은 느낌이 들지만....

다양한 시도를 해봐도, 원하는 형태로 코드를 짜려면 let 을 써야할 듯 하다..

let 괜찮은거겠지..?

let monster;
    //10층 막보
    if (stage % 10 === 0 && stage !== 0) {
     monster = new Monster("최종BOSS : 정상으로 향하는 길", 2999, 180)
    }
    //5층 중보
    else if (stage % 5 === 0 && stage !== 0) {
     monster = new Monster("중간BOSS : 계단지옥", 777, 77)
    }
    //일반 층
    else {
     monster = new Monster(RandomName[RandomNameChoice], stage * 80 + stage ** 3 + 80, stage * 2 + stage ** 2 + 8)
    }

 

 

결국 위와 같은 형태로 마무리 하기로 했다.

 

class Monster {
  constructor(name, hp, attack) {
    this.name = name
    this.hp = hp
    this.attack = attack
  }
  ........

class 부분은 위와 같다.

 

 

 

그리고 바로 이 시점에서,  제출한 과제에 대한 피드백을 받았다.

피드백의 내용을 요약하면 다음과 같다.

 

우선 참신한 컨셉에 놀랐고,

필수기능과 도전 기능에 해당하는 부분을 모두 성공적으로 구현해내었음.

도박성 기술인 지름길 스킬에 실패 할 경우, 주사위를 표시하여

직관적으로 확인할 수 있는 부분이 좋았음. 

사실 별 생각 없이 만든 코드

 

아쉬운 점으로, 반복되는 코드가 많다는 점 !

 

또, 깃 활용 시 커밋 내용을 확실하고 직관적으로 알 수 있게 작성하여

히스토리로써도 관리되게끔 할 수 있으면 좋겠음

 

 

 

전체적으로 과제 결과물이 괜찮았다는 내용이었다 !

어흐흘

 

헛되지 않았다......

 

즉각적으로 피드백을 적용해보자면,

제출했던 스테이지 클리어 보상 함수 내용이다.

확실히 초 하드코딩으로 반복되어 지저분해 보이는 부분이다.

 

반복문을 통해 간소화를 해볼까?

// 불필요한 반복 간소화
  await delay(0.2)
  console.log(chalk.cyanBright.bold(`${stage === 6 ? `${monster.name} 돌파 !! 보상을 5회 획득합니다.`:` 랜덤 보상을 3회 획득합니다.  `} \n`));
    for(let i = 0; i < (stage ===6 ? 5 : 3); i++) {
      RandomReward(player, stage);
      await delay(0.2)
    }

 

for문을 통해 간소화 하고, 추가로 로그 부분은 삼항 연산자를 통해 출력하도록 개선했다.

 

 

문제는 다음인데, 

일이 커질것 같은 예감이 든다...

 

이미 코드를 짤 때에도, 이게 최선일까 고민했던 부분이다.

 

저렇게 코드를 만들었던 이유는,

전투 로그 출력에 딜레이를 주고싶었던 작은 소망에서 시작된 것인데.. 

 

그건 결코 간단한 일은 아니었다.

전투 로그는 화면을 초기화 하고 logs.forEach ~~ 가 실행 될 때에 갱신이 되기 때문에,

전투 도중에 logs에 push 한다고 해서 즉각적으로 보여지는게 아니다.

 

따라서 내가 선택한 방법은 (현재의 내 머리에서 나올 수 있는 유일한 방법..흑흑)

 

위 처럼 매번 전투 logs가  push 되는 상황마다

console.clear 와 상태창 불러오고, 전투로그를 불러오는

이른바 짜치는 코드를 짜버렸던 것이다.

 

당연히 해당 코드를 간소할 방법 또한 생각 해 낼 수 없었다.

그렇기 때문에 저 상태로 제출한 것이니까 !!

 

 

결국, 그대로 다시 튜터님에게

진실의 방으로

 

 

내가 생각하지 못하고 있었던 부분을 바로 체크해 주셨다.

어....음...... 이 부분을 말로 설명하려니까 굉장히 어려운데,

 

결국 그렇게 보이기만 하면 된다??

 

기존에 작성한 방법은 logs 가 push 될 때마다 초기화 해서 그때 그때 출력하는 방법이고,

 

튜너님은 결국 그런 (짜치는) 과정 필요 없이 기존에 있던 메서드 logs.forEach ~~에서

새로 추가된 logs 에 해당하는 log만 딜레이로 출력되게 하면 보이는 건 똑같다는 말을 해주셨다.

 

      헉 !!!!

 

사실, 생각지도 못한 부분이었기 때문에, 처음 설명을 들었을땐 (???????????) 상태였다...

 

 

결국

이 녀석이 로그를 뱉어낼 때, 새로 추가된 log를 뱉을 때만 딜레이를 주게 하면 된다

이 말입니다 !!

 

그렇게 하면 초기 셋팅 처럼, 전투로그를 턴마다 한번 만 쏟아내게 되지만

눈에 보이게 되는건

로그 초기화(순식간이라 안보임)-> 기존 로그 한번에 쫙( 이전 log와 그대로인 것 처럼 보임)->

 추가된 log 는 딜레이되어 출력 (새로 등장하는것 처럼 보임)

 

이렇게 되는것이다.

 

왜 이 생각을 못했을까?

 

바로 시작해보자.

 

// 화면 초기화
function displayReset(stage, player, monster, logs) {
  console.clear();
  displayStatus(stage, player, monster);
  while (logs.length > 14) { logs.shift(); }
  logs.forEach((log) => console.log(log));

}

이런 흉물은 더 이상 필요 없게 되었다. 

 

 

시작은

 

솔직히, 뭐라고 하는지 잘 모르겠는 위의 친구를 평범한 함수로 바꾸는것 부터 시작이다.

 

 

// **NEW**  전투로그 출력 함수
async function addLogs (logs, oldLogs) {
  for (let log of oldLogs) {
    console.log(log)
  }
  for (let log of logs) {
    await delay(0.2)
    console.log(log)
    oldLogs.push(log)
  }
  await delay(0.3);
}

 

oldLogs 는 전부 즉시 출력, 

logs 는 딜레이로 출력하고, 

logs 에 있던 내용을 전부 oldLogs 에 복사한다 .

해당 함수 실행 후 logs 를 비워주면

 

완성 !!

 

 

이제, 새로 logs 에 push 한 문자열은 알아서 딜레이로 출력되게 된 것이다.

 

 

이랬던 코드가  (한 화면에 담기지도 않음)

 

 

 

 

이렇게 바뀌었다 !! 장족의 발전

 

이 영광을 튜터님께......

 

 

피드백 받은 내용을 바로 내용에 담으려다 보니 

TIL 이 좀 길어진 감이 있는데,

 

아무튼 이번 로그라이크 과제는 여기서 마무리가 되었다 !

추가로 쓰고싶은 내용이 생각나면 한번 더 다룰지도?

 

 

 

 

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

24.08.30 TIL : Node.js 입문주차  (0) 2024.08.30
[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22

 

 

 

과제 진행 상황

구현하고 싶었던 기능은 전부 구현했고, 이쯤에서 이번 과제를 마무리 하기로 했다.

 

https://github.com/P-lani/sparta_RogLike

 

깃 허브에 최종merge 가 완료된 상태이다. 원한다면 플레이가 가능하다 !

아래는 readme 에 쓴 내용이다.

 

## **개발 프로세스**

**도전 기능 부분**
0. 찾기 쉽도록 위쪽에 쓰겠습니다

1. 확률 로직
 1) 플레이어의 공격력과 최대배율에 따른 난수 데미지 / 몬스터의 공격력 또한 1.4배의 난수를 가짐
 2) 축지법 ! 스킬은 일종의 카운터 스킬로, 성공확률이 존재
 3) 필살! 암벽등반! 스킬은 연속공격 스킬로, 5~10회 랜덤횟수를 공격
 4) 지름길 개척 스킬은 도망 스킬로, 10%의 확률(고정)로 성공함
 4) 스테이지 클리어 보상 5가지중 3개를 랜덤 획득하며 (중복 가능)
    각각의 보상은  0.8배~2.0배의 난수를 가짐
 5) 몬스터의 name은, 따로 설정한 배열에서 랜덤으로 선택함
 6) 몬스터의 패턴(체력 소모)는 확률에 따라 상이하게 발생 (빗나감, 약화된 공격, 평범한 공격, 강력한 공격, 아주 강력한 공격)

2. 복잡한 행동 패턴
 1) 필살! 암벽등반 ! 기술은 확률로직 외에도, 충전을 3개를 소모해야 사용할 수 있으며,
    충전은 stage clear 랜덤 보상으로만 획득할 수 있습니다. (원활한 Test를 위해 3충전을 가지고 시작합니다.)
    추가로, 확률 로직과 delay 텍스트 출력 등등 약간의 연출이 추가되어 있습니다.

 2) 지름길 개척 기술은, 성공시 연출효과...를 출력합니다.

 3) 위의 연출 효과를 응용하여 함수로 만들었고, 시작 시(오프닝), 엔딩, 패배 화면에 적용했습니다.

###  게임 기획 분석**


**컨셉 잡기**

1. 등산을 컨셉으로 제작한 게임입니다.
플레이어 : 등산가
플레이어의 체력 : 체력
플레이어의 공격력 : 등산력(...)

몬스터 : 등산로
몬스터의 체력 : 남은 거리
몬스터의 공격력 : 체력 소모량

2. 컨셉만 약간 특이할 뿐, 구현내용은 크게 다르지 않습니다.

다만 !
체력이 0이 된 몬스터는 공격을 못하겠지만,
남은거리가 0이되는 등반은 체력을 소모합니다.
때문에, 마지막 일격에도 반격을 받는 느낌이 되었고 의도된 구현입니다. (더블 KO 는 패배 처리)



**전투 기획**

1. 플레이어= 등산가 의 행동 선택지

    1. 등반하기 : 100% 확률로 등산력에 해당하는 거리를 등반. 이후 체력 소모(상대 턴) 발생합니다.

    2. 축지법 ! : 기본35% 확률로 등산력에 해당하는 거리를 등반. 성공시 체력을 소모하지 않습니다. 실패시 체력소모만 발생합니다.
        35% 확률로 굉장히 저조한 성능이지만, 이후 랜덤 보상을 통해 확률이 증가할 수 있습니다.

    3. 필살! 암벽등반! : 최대 충전3, 초기 충전3, 사용시 충전 3 소모.
        등산력에 해당하는 거리를 5~10회 랜덤횟수 등반합니다.  충전은 랜덤 보상에서 획득 가능합니다.

    4. 지름길 개척 : 도망에 해당하는 기술로, 낮은 확률(10%) 로 성공하여 스테이지 클리어로 판정하고 보상 또한 정상적으로 획득합니다.
        => 이는 사실상 즉사기에 해당하기 때문에, 성공시 등산로의 남은 거리를 0으로 만들고 체력 소모 턴이 발생하지 않습니다.
        실패시 체력 소모만 발생.


2. 플레이어의 능력치
    1. 체력 : 0이 되면 게임오버가 되며, 스테이지 클리어시 확정적으로 일정량을 회복합니다.
        추가적으로 랜덤 보상에서도 획득할 수 있습니다.

    2. 등산력 : 공격력에 해당하며, 등산력 ~ 등산력 x (1+최대배율/100) 의 난수를 가집니다.
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.

    3. 최대배율 : 최대 공격력 배율에 해당하며, 계산은 위와 같습니다. 기본 20의 최대배율로 시작합니다. (1.2배)
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.

    4. 축지법 확률 : 축지법 ! 스킬의 성공확률이며, 35%로 시작합니다.
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.
        최대 75% 의 상한을 가지고 있습니다.

2. 몬스터 = 등산로의 패턴

    1. 엄밀히 따지면, 몬스터가 완전한 턴을 받는 형식의 구현은 아니고,
        플레이어의 행동에 따라 몬스터의 공격 이벤트가 발생하는 방식입니다.

    2. 체력을 소모할 때, 등산로(monster) 의 공격력을 참조하며, 등산로의 공격력은 1 배 ~ 1.4배 난수를 갖습니다.

    3. 추가적으로 확률에 따라 체력 소모가 0.6배 , 1배, 1.3배, 1.6배, 2배, 체력소모 0 이 되는 이벤트가 발생합니다.
        확률은 각각 상이합니다. (낮은 확률로 컨셉이 파괴되는 텍스트가 출력됩니다. )
       
    4. 5층에서는 중간 BOSS 를, 10층에서는 최종BOSS 를 조우합니다.
        BOSS 는 전용 name 을 가지고, 별도의 체력과 공격력을 가집니다.

    5. BOSS 이외의 몬스터는 지정된 값(배열) 중 랜덤한 name을 갖습니다.
        (name중 일부는 컨셉이 파괴되는것도 있습니다. )

    6. 등산로(monster)는 stage 마다 증가한 체력과 공격력을 갖습니다.

    7. 등산로(monster)의 남은거리(hp)가 0 이 될 경우, stage가 클리어 되며,
        랜덤 보상 3가지를 획득하고 다음 stage 로 넘어갑니다.

    8. 중간 BOSS 클리어시엔 랜덤 5가지 보상을 획득하며,
        최종 BOSS 클리어시엔 엔딩이 출력됩니다.


3. 랜덤 보상

    1. stage 를 클리어하여 랜덤한 보상 3개를 획득합니다. (중간BOSS 클리어 시 5개)

    2. 체력 회복 / 등산력 / 최대 배율 / 필살 충전 / 축지법 확률
         종류는 총 5가지 이며 각각 동일한 확률을 가집니다.

    3. 체력 회복, 등산력, 최대배율은 stage 에 따라 기본 보상량이 증가합니다.  
        또한 0.8배~ 2.0배의 난수가 적용됩니다.

    4. 축지법 확률은 stage 에 상관없이 고정된 보상량을 갖지만,
        마찬가지로 0.8배~ 2.0배 난수가 적용됩니다.

    5. 모든 보상은 현재 수치 -> 증가된 수치 , 증가량을 표기합니다.





**추가적인 요소**

1. 전투로그가 14줄을 넘어갈 경우 먼저 출력되었던 로그가 제거되어 14줄이 유지됩니다.

2. 지름길 개척  스킬 성공 시, 연출 효과를 추가했습니다.
    반복문을 통해, 콘솔 클리어와 문자열 출력을 반복합니다.

3. 위의 연출을 함수화 시켜서 GAME-OVER, GAME-CLAER 에도 적용 했습니다.
   
4. setTimeout 을 사용하는 delay 함수를 사용하여, 로그 출력간에 delay를 적용했습니다.


 

readme 에는 기획과 구현기능에 관한 내용을 담아보았다.

 

 

과제 마무리에 관한 내용은 과제 제출날짜인 내일 TIL로 작성할 계획이다.

 

오늘은?

오랜만에 알고리즘 풀이 시간을 가져 보았다. 

물론, 과제 진행중에도 알고리즘 풀이시간을 갖긴 했지만,

TIL로 작성하기엔 시간도 양도 부족했었다.

 

그럼 시작해볼까?

 

 

뭐지 언어 영역인가...

 

시작부터 문제 설명만 10줄이 넘어가는 알고리즘 문제를 만났다.

 

쉽게 설명하면....

[1,3,4,6] 에서

index 0 = 1 개

index 1 = 3 개

index 2 = 4 개

index 3 = 6 개 

의 숫자가 존재하며 이를 풀어서 나열하면 0 111 2222 333333 이 되고, 

이 목록을 0을 기준으로  반으로 쪼개서 1223330333221 을 출력해야하는 상황이다.

이 때, 1은 3개 이기 때문에, 반으로 쪼개고 남은 1개는 버려지게 된다.

 

가장 쉽고 직관적인 방법으로 접근해보자.

1.

index 1~끝까지, 해당 index의 value의 절반만큼(소수점 버림) index를 빈 문자열 answer에 추가

 answer = "122333" 

2.

index 0 을 value 만큼 추가. ( 문제에서 0은 1개로 고정이라고 했기 때문에 arr += 0 을 해도 같다)

3.

answer = "1223330"

그 후, 1에 사용한 방법을 역순으로 적용

index의 끝에서~1 까지, 해당 index의 value의 절반만큼 어쩌구 저쩌구

answer = "1223330333221" 

 

벌써부터 O(n^2 *2 )  급의 코드가 만들어질것 같은 예감이 든다.

당장 해보자 !

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 0; j < food[i]/2; j++ ){
            answer += i
        }
    }
    return answer;
}

console.log(solution([1,3,4,6]))  // "1122333"  실패 !
// 홀수value 를 가진 경우 +1회가 더 출력되는 모습

계획중  1에 해당하는 부분 이다.

위 처럼 코드를 짰더니, food[i] /2 에 홀수가 들어갈 경우 절반 +1 회 출력이 되어버린다.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 1; j <= food[i]/2; j++ ){
            answer += i
        }
    }
    return answer;
}

console.log(solution([1,3,4,6]))  // "122333"

소수점을 고려하지 않도록 위처럼 수정해 주었다.

 

그 후, 0을 추가하고 다시 뒤에서 부터 반복 !

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 1; j <= food[i]/2; j++ ){
            answer += i
        }
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
       for( j = 1; j <= food[i]/2; j++) {
           answer +=i
       }
    }
    return answer;
}

계획한 그대로 코드가 되었다...

잘 작동하는 모습...

 

여기서 끝낸다면 취업의 길은 O=(food.length-1 ^2 *2) 만큼 멀어졌다고 볼 수 있다.

취업과 가까워 지기 위해 검색을 해본 결과, repeat() 함수를 사용하는것이 좋아보였다.

str.repeat(n) 메서드는, str 을 n번 만큼 반복 출력하는 메서드로, 

이 알고리즘 문제에 특화된 메서드라고 볼 수 있다.

심지어 소수점을 과감하게 버리는 대담함 까지 가지고있다.

 

이를 이용해서 2중 반복문을 1중 반복문으로 간소화 할 수 있다.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        answer += i.repeat(food[i]/2);
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
        answer += i.repeat(food[i]/2);
    }
    return answer;
}

 

에러 발생 

i.repeat() is not function 

 

앗... i는 문자열이 아니었다. 어차피 참고용 i이기 때문에 문자열로 전환해주자.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        answer += i.toString().repeat(food[i]/2);
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
        answer += i.toString().repeat(food[i]/2);
    }
    return answer;
}

보다 깔끔하고, 2중 for문을 사용하지 않게되어 효율적이게 되었다.

작동도 잘 된다.

 

function solution(food) {
    var answer = [];
    for(let i = 1; i < food.length; i++) {
        answer.push(i.toString().repeat(food[i]/2));
    }
    return answer.join("")+"0"+answer.reverse().join("")
}

 

억지로 줄인다면 위처럼 배열로 제작한 뒤 조립하여 return 할 수도 있다.

코드가 줄어든 것 같지만 메서드도 많이 쓰고 배열이 문자열 보다 많은 메모리를 쓰기 때문에

효율적인 코드는 아니라고 할 수 있다.

 

 

다른 사람이 작성한 코드

 

헉... 위에 작성한 나의 코드와 굉장히 유사한 형태의 코드가 눈에 띄었다.

다만, 문자열 방식을 그대로 사용하고

리턴 과정에서  [...res].reverse().join('') 이라는 엄청난 기술로 

즉각적으로 문자열을 뒤집어서 출력하는 방법을 사용했다.

 

주목할 점은

[...res] 인데, 아주 간단한 방법으로 문자열 쪼개서 배열로 만드는 최신 기술(?) 이 존재했다.

난 왜 이걸 모르고 있었던 걸까???

 

let arr = [1,2,3]

...arr = 1 2 3

... 을 통해서 배열을 분해하여 나열할 수 있는것은 알고 있었지만

거기에  [...arr] 을 한다고 다시 배열이 될 거란 생각은 못했다.

당연히 arr = [1 2 3] 이라는 이상한 녀석이 될 거라고 생각했었기 때문이다.

 

이상하다.. 구조 분해 할당 강의를 분명히 들었는데

머리속에서 undefined를 출력하고있다. (버그발생)

 

남는 시간을 활용해서 해당 부분을 다시 복습해야겠다.

 

 

 

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

[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21

 

 

오늘은 구현하고 싶었던 기능을 하나 구현 해보고,

지저분한 코드를 깔끔하게 정리하는 시간을 가져보았다.

 

 

상당히(?) 개선된 메인 화면

어제 예상보다 오래걸렸던 setTimeout.. 이녀석을 좀 더 활용해 보기로 했다.

 

 

4. 지름길 개척에 성공할 경우 출력되는 화면이다.

figlet 모듈을 사용하여 간단하게 문자열을 표현하는 코드인데,

 

시작 화면에서 출력되던 HIKING 의 그것이다

 

이 figlet 출력에 딜레이를 줘 보자.

일종의 연출효과를 얻을 수 있고, 코드에 대한 학습도 될테니 일석 이조 !

 

크게 어려운 부분은 없었다.

delay 함수는 이미 전역에 뿌려두었고, 어제 했던것 처럼, 호출부에 await 를 걸어주고

async를 함수선언 앞에 달아준 뒤 await delay를 적당히 써 주면 된다.

 

문자 출력 부분은, for 문을 통해 구현했고,

주어진 문자열을 0인덱스부터 하나씩 추가하며

console.clear 와 출력을 반복하면 된다 . 

 

 

 

만들고 보니 꽤 그럴싸한듯 !

 

 

그래서, 패배시 game-over 의 표현이나,

엔딩에도 적용하면 좋을것 같다는 생각이 들었다.

 

하드코딩 결과물을 다시 함수로.. 만들어보자.

변동이 필요한 부분만 인자로 받게 만들면 끝이겠죠? 

 

 

// TEST 문자 출력
async function TestText (text, setDelay, color) {
  let TestTextMes = ""
  for (let i = 0; i < text.length ; i++){
    console.clear()
    TestTextMes += text[i]
    console.log(chalk.red(('='.repeat(75))));
    console.log(chalk.red(figlet.textSync(TestTextMes)));
    console.log(chalk.red(('='.repeat(75))));

await delay(setDelay);
}
await delay(0.5);
}

 

뭔가... 뭔가 부족하다 !

 

위에 보이는 것 처럼, color 또한 임의로 지정할 수 있었으면 좋겠는데...

 

불이 꺼지는 매개변수 color .. 

매개변수 : 아 되겠냐고 ㅋㅋ

 

어떻게 이해해야 하는 부분일까?

 

쉬울줄 알았는데...

콜백함수 쓰면..? 아니면..메서드를 통쨰로? 아니면.. 

되긴 해?

 

 

결국 검색과 고민 끝에, 튜터님에게 질문하기로 했다 !

 

chalk.color 를 통째로 인자로 받아오면 가능하다는 것이다 !!

 

저 위의 되겠냐고의 방법은,

변수chalk와 실행() 사이에 매개변수가 들어가서 뭐 어쩌라는건지 모르겠는 상황이 되지만, 

 

chalk.color 를 받아넣으면 함수() 와 같은 방식으로 실행처리가 가능해지기 때문에, 

정상적으로 기능을 한다는 것이었다 !!

 

 

이것은 고차 함수의 작동에 해당하는 방법으로 

내 머리로 이해할 수 있는 부분은

매개변수가 함수명을 인자로 받아오면

본문에서 매개변수() 를 통해  그 함수를 실행시킬 수 있다고 이해가 되어진다.

 

 

맞..나?

 

 

결론

 

JS의 펑션.. 당신은 대체...

이해가 다 안되는데도 놀라운 힘이야 !

(솔직히 이것도 의도한 기능인지 의심됨)

 

 

추가로, 조심해야 할 부분이 있다면,

모듈의 함수마다 바인딩하는 대상이 다를 수 있다고 하셨는데, 

그럴 땐 바인드까지 해버려서 인자로 넣어버리면 green이 집을 찾는것을 도와줄 수 있다고 하셨다 !

 

위 과정으로 만든 함수로, 게임 오버 출력과, 게임 클리어 출력까지 해먹으면 될 듯 하다.

 

음.. 초기 시작 화면 (오프닝) 까지 날로 먹으면 좋을것 같다 !

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

[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20

+ Recent posts