입문주차를 끝내고, Node.js숙련 주차에 해당하는 강의 ! 

 

에 대한 이야기를 하기에 앞서서,

 

CH3 의 개인과제에 해당하는 내용이 오늘 공개가 되었다.

과제의 발제 자체는 내일이지만, 어쨌든 해야할 과제가 무엇인지는 공개가 되었다.

 

그 과제는 이름하야...

 

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

 

 

학습 과제를 끝내고 나면 할 수 있어요!  ==> 이걸 모르면 과제를 할 수 없어요 !

 

발제는 내일이기 때문에 간략하게만 살펴 보았는데,

 

Expreess 로 서버 와 api 구성

AWS RDS -> MySQL 을 사용하여 데이터 베이스를 활용

Prisma를 사용하여 데이터 베이스 컨트롤

AWS EC2 를 이용한 배포

 

와 같은 내용으로 구성되어 있었으며

 

조금 더 구체적으로 들어가면,

1. 회원가입 및 로그인 기능

2. 캐릭터 생성 및 캐릭터의 능력치 설정

3. 캐릭터 상세 조회와 캐릭터 삭제 기능

4. 아이템 생성 

5. 아이템 수정 (강화..?)

6. 아이템 목록 조회 및 아이템의 상세정보 조회

 

도전기능으로는 

1. 아이템 구매 및 판매

2. 인벤토리 기능

3. 장착 아이템 목록 조회

4. 아이템 장착 및 탈착 API

5. 게임머니 획득 API

 

 

이걸 만들라구요 ?!

 

 

어라... 이 감정...?

 

24.08.21 TIL - Roglike 과제를 받은 날

 

 

그렇지만 이번에는 정말로 어려울것 같다는 느낌이 든다...

따로 스켈레톤 코드 같은 것이 지급되지 않는것으로 확인이되어,

더 감이 안잡히는것 같다.

 

 

중요한건 강의를 서둘러서 들어야 한다는거임

 

관계형 데이터베이스 

 

오늘 배운 내용은 전부 여기에 해당한다고 볼 수 있다.

 

저번시간에 학습했던 비관계형 데이터베이스인 mongoDB 와 달리

관계형 데이터 베이스에 해당하는 Mysql을 기반으로 학습을 진행했다.

 

 

관계형 데이터 베이스에 해당하는 Mysql 은 이전에 입문단계에 해당하는 강의를 학습한 적이 있다.

때문에 비교적 이해하는데 큰 어려움은 없었다.

 

차이가 있다면, 입문강의에서 진행했던 내용은  DBeaver 를 통해 이미 작성된 database 를 조회하는

방법 위주로 강의가 진행되었기 때문에, 내용을 따지고 보면 오늘 학습하는 내용과 차이가 꽤 크다고 볼 수 있다.

 

오늘은, 실제로 MySQL 데이터베이스를  AWS RDS 를 통해 직접 구동하여 (대여해서...)

Database 와 table, 그리고 column 에 해당하는 부분을 직접 추가하거나 수정하고, 삭제하는 과정을 학습했다.

VScode 에서  SQL 언어을 사용할 수 있고, 

MYSQL 확장 프로그램을 통해 VScode 내에서 사실 다 할 수 있다.

마이크로 소프트.. 그는 대체...

 

 

 

 

오늘 미약한 공포가 느껴졌던 부분

그것은 바로 Raw Query 라고 할 수 있다.

언뜻 보면, 노드 환경에서 직접 SQL 요청을 보내는 평범한 기능으로 생각이 되지만, 

 

// app.js

/** 테이블 생성 API **/
app.post('/api/tables/', async (req, res, next) => {
  const { tableName } = req.body;

  await connect.promise().query(`
      CREATE TABLE ${tableName}
      (
          id        INT         NOT NULL AUTO_INCREMENT PRIMARY KEY,
          name      VARCHAR(20) NOT NULL,
          createdAt DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP
      )`);

  return res.status(201).json({ message: '테이블 생성에 성공하였습니다.' });
});

 

이런식으로 Raw Query 를 작성하다 보면, 이건 좀 아닌것 같다는 생각이 스멀스멀 들기 시작한다.

 

강의에서는 Raw Query 최대 단점을, 유지보수가 어려운 점을 꼽았는데,

예를 들면,

createdAt 라는 칼럼이  createDate 라는 칼럼명으로 바뀌어야 한다고 가정해 보자.

우리는 Raw Query로 작성한 createdAt를 전부 찾아서 createDate 로 수정해야만 할 것이다.

 

그냥 바꾸지 말죠? 에서 부터,

최악의 경우, 한 두가지를 놓치는 바람에 서버가 다운되거나 데이터가 변조될 위험성도 있다.

 

ORM  등장 (Object Relational Mapping) 

 

앞선 문제들에 대한 대책으로 ORM이 개발되었고, 강의에서는 ORM 중 하나인 Prisma 를 소개해 주었다.

 

 

 

 

ORM 의 최대 장점은, 데이터 베이스가 통째로 바뀌더라도 유연한 대처가 가능하며, ( ex MySQL -> Oracle)

위에 언급된 문제인 Table 또는 cullum 의 수정 정도는 어렵지 않게 대처할 수 있다.

 

 

Node에서 Prisma를 설치할 경우,

schema.prisma 을 내용을 토대로 database 를 추가하거나 수정할 수 있게 된다.

Posts 라는 새로운 table 과 각 cullum 의 설정을 작성한다.

 

SQL 과 Raw Query 사이 어딘가....로 보이는 형태의 언어이지만,

성능이 확실하다고 설명했기 때문에 왠지 호감도가 올라간 상태 !!

 

이렇게 schema.prisma에 작성한 내용을 

 

 npx prisma db push 명령을 통해 push 하면,

 

 

node_modules/.prisma/index.d.ts  의 어딘가에

JS 형태로 작성이 되어진다.

(이 외에도 예상했던것 보다 많은것이 기록된다...)

 

 

 

Prisma를 통해 Posts table과 각 컬럼 속성 설정에 성공한 결과 !

 

 

오늘 학습한 내용은 여기까지 인데, 아무래도 과제 발제 까지 시간이 촉박 하기 때문에,

 

며칠정도는  TIL 의 작성시간을 줄이고 강의를 더 듣는다거나, 

주말을 포기한다거나 하는 일이 발생할 것 같다...

 

 

 

 

 

어제에 이어서 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를 통해 로그도 정상적으로 확인 가능하다.

 

 

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

 

 

 

 

상당히 골치아픈 문제가 나와서 글로 작성해 보았다.

이게 level 1 이라니...

 

얼핏보면 수월하게 풀 수 있을것 같은 문제지만 (그렇게 생각했었는데요...)

제한 사항을 보면 X와 Y의 길이가 최대 300만 으로 정해져 있으며,

이로 인해서 이중 for문 등을 사용하면 시간복잡도가 급증해 시간초과의 결과가 나오게 된다.

 

 

풀이 설계

 

1. X 와 Y 를 내림차 정렬을 한다.

2. X 와 Y를 맨 앞에서부터 비교하여 같을 경우 answer 에 추가

3. X[0] Y[0] 이 같지 않을 경우 둘 중 작은 값의 다음 index를 검사

4. "0" 의 결과와 "-1" 의 결과를 따로 리턴

 

늘 그렇지만, 이번에도 대단히 효율적이진 않을것 같은 풀이 방향이다.

내림차 정렬을 위한 sort 2회, X와 Y의 값을 비교하기 위한 반복문이 기본적으로 필요하기 때문.

 

 

풀이 시작

 

    X= [...X].sort((a,b) => b-a)
    Y= [...Y].sort((a,b) => b-a)

시작은 역순정렬 부터 한다. 큰 값으로 정렬하여 비교하면 유리할 거라는 생각이었으나,

사실, sort 과정 자체가 시간복잡도를 많이 잡아먹어버린다.

어쨌든 그렇게 설계했기 때문에 이렇게 시작해 보도록 하자.

 

이 과정에서 저번 알고리즘 때 배웠던 [...str] 을 이용한 배열화를 사용해 보았다.

 

그 후, X와 Y를 비교하기 위해 반복문을 사용해야 하는데, 

while 문을 사용하기로 했다. 

 

    let i = 0;
    let j = 0;
    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        }

X 와 Y의 끝까지 비교를 해야할 것으로 예상되기 때문에, 위 같이 조건을 주었고,

가장 중요한 X[i] ===Y[j] 일 경우 answer에 추가하는 것 까지는 어렵지 않게 진행되었다.

 

 

잘못된 풀이..?

그 전에, 잠깐 뻘짓을 했던 과정을 살펴보자면

    while ( X.length > i && Y.length > j) {
        if ( X[0] === Y[0]) {
            answer += X[0]
            X= X.substring(1)
            Y= Y.substring(1)
        }

위처럼 동일한 조건으로 진행하지만, i j 를 통해 다음 index로 넘어가는것이 아니라,

.substirng(1) 을 통해  X,Y의 0번의 index만 검사하도록 진행해 보려는 시도를 했었는데, 

굳이 substring 이라는 메서드가 사용된다는 점 때문에 폐기 되었다.

 

시도했던 이유와 폐기한 이유??

 

substring 은 문자열의 0번째 ~를 제거하는 메서드이다. 

참조형이 아닌 기본형 타입의 string을 변조하는건 시간복잡도가 1회 일 것으로 예상을 했었으나,

기본적으로 substring() 의 시간 복잡도는 O(n) 에 해당했다.

글 마무리에 substring을 사용했을 때와, index 순번을 사용했을 때의 시간차이를 비교해보도록 하겠다.

 

 

자, 다시 풀이로 돌아와서, 이제 X[i] 와 Y[j] 가 다른 값일 경우만 처리하면 된다.

 

생각이 닿기는 좀 시간이 걸렸지만,

    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        } else if (X[i] > Y[j]){
            i++          
        } else { 
            j++}
        
    }

X[i] 와 Y[j] 의  둘 중 큰 값을 비교해서 

큰 값을 가진쪽의 index 를++ 해서  다음 index를 참조하게 하면,

점점 값이 작아지며 서로 같은 값이 나올때 까지 넘어가게 된다.

 

마무리로, 

    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer

"000000" 만 나온 경우와  일치하는값이 하나도 없을경우만 따로 빼주면 정답이 완성된다.

 

답 코드

function solution(X, Y) {
    var answer = '';
    
    X= [...X].sort((a,b) => b-a)
    Y= [...Y].sort((a,b) => b-a)
    let i = 0;
    let j = 0;
    while ( X.length > i && Y.length > j) {
        if ( X[i] === Y[j]) {
            answer += X[i]
            j++ 
            i++
        } else if (X[i] > Y[j]){
            i++          
        } else { 
            j++}
        
    }
    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer
}

 앞서 말한것 처럼, sort와 반복문이 사용되며 결론적으로 그렇게 효율적인 코드는 아니게 되었다.

 

딱 봐도  11~ 15번 case 가 헬 구간 ( X,Y length가 길게 주어지는듯)

 

 

 

 

 

그렇다면, substirng 의 경우 얼마나 차이가 날까?

function solution(X, Y) {
    var answer = '';
    
    X= [...X].sort((a,b) => b-a).join('')
    Y= [...Y].sort((a,b) => b-a).join('')
    while ( X.length > 0 && Y.length > 0) {
        if ( X[0] === Y[0]) {
            answer += X[0]
            X= X.substring(1)
            Y= Y.substring(1)
        } else if (X[0] > Y[0]){
            X= X.substring(1)       
        } else {
            Y= Y.substring(1)}
        
    }
    if (answer[0] === "0" ) { return "0"}
    return answer === ""? "-1" : answer
}

.substirng(1) 이기 때문에 별로 차이가 없지 않을까 하는 생각에서 완성을 해 보았다.

 

 

유의미한 차이가 있다.

substirng(1) 이라고 해도 결코 상수의 처리시간이 걸리는건 아닌것으로 보인다.

물론 X 와 Y를 추가로 join 해주는 과정에서도 추가적인 시간이 소요됐겠지만 말이다.

 

대충 정리를 하자면 

11~15번 케이스에서 걸린 시간은 

기본 답 코드 ( X[i] Y[j] 로 비교) = 약 2300ms 가량 

두 번째 테스트 ( 기본 답 코드에서 X,Y를 join만 해준 경우)  = 약 2500ms 가량 (join에 걸리는 시간 대략적으로 확인)

세 번째 테스트 ( join 이후 substring을 통해 X[0] Y[0] 으로 비교) =  약 2800ms 가량 ( join과 substring에 걸리는 시간 확인)

 

 

당연히~~~~~

X[i] Y[j] 로 비교하는것이 빠르겠지만, 순전히 궁금증이 생겨나서 

테스트를 해 보게 되었다.

결론은.... 애초에 sort 가 제일 오래 걸리는듯 함 (ㅠ)

 

 

 

물리 계층은 OSI의 7 계층 중 가장 아래에 위치한 1계층으로, 하드웨어로 표현된다.

 

네트워크 장치의 전기적, 기계적 속성 및 전송수단을 정의한다고 할 수 있다.

 

 

전기 신호 하면 가장 먼저 떠오르는 0 과 1 을 이용한 통신이 여기에 해당한다.

 

물리 계층 장비

허브 Hub

전기 신호를 증폭시켜서 연결된 장치간의 통신을 가능하게 해준다.

흔히 말하는 랜선이 허브에 해당된다.

허브는 중계기 역할을 하며, 하나의 장치가 허브로 연결된 모든 장치에게 데이터를 전달하고

이것을 브로드 캐스팅 Broad - casting 이라고 한다.

 

이 방식은 여러 장치가 허브로 연결된 상태에서 1:1 통신이라기 보다는

1: 모든 장치로 통신 데이터를 전송하여 송신 대상만 데이터를 처리하고 

나머지는 데이터가 무시된다고 설명되어있다.

 

리피터 Repeater 

신호의 세기를 증포해서 먼거리의 통신을 가능하게 해주는 장치

 

 

 

물리 계층과 물리 계층 간의 통신은 전기, 빛 , 전파를 통해 통신을 하며 

이러한 것들을 Signaling 이라고 한다.

 

전기

UTP, 전화선, 동축 케이블이 해당

 

광섬유 케이블 (빛의 패턴)

 

전파

wi-fi 와 같은 무선인터넷 (마이크로파 패턴을 신호로 사용)

 

전송 단위

 

Signaling의 방식은 위처럼 다양하지만, 어찌됐던 물리 계층이기 때문에

이 모두는 전기 신호인 0, 1 로 데이터를 인코딩해서 전송한다.

 

 

 

 

 

본문에 앞서서,

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

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

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

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

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

 

오늘 학습한 내용은, 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

OSI 7계층 이란?

OSI(Open System Interconnection) 7계층은 국제 표준화 기구인

ISO(International Standardization Organization)에서 개발한 

컴퓨터 네트워크 프로토콜 디자인과 통신을 계층으로 나누어

설명한 개방형 시스템 상호 연결 모델이다.

 

 

OSI 7계층 레이어의 특징

1. 물리 계층 (physical Layer)

물리계층은 1계층으로써, 두 시스템간의 데이터 전송을 위해 링크를 활성화 하고 관리하기 위한 기계적, 전기적, 기능적, 절차적 특성 등을 정의하며 허브나 라우터, 네트워크카드, 케이블 등의 전송매체를 통해 비트(bit)들을 전송하는 계층입니다. 물리계층의 데이터 단위는 '비트(bit)' 입니다. 송신측에서는 데이터 링크 계층에서 과 1로 구성된 비트열의 데이터를 받아 전기적 신호로변환 후 전송 매체를 통하여 수신측에 보내게 됩니다. 받는 쪽인 수신측은 송신측으로부터 받은 전기 신호를 0과 1로 구성된 비트열로 복원하여 수신측의데이터링크 계층에 전송하게 됩니다. OSI 모델에서 최하위 계층에 속하며 상위계층에서 전송된 데이터를 물리매체를 통하여 다른 시스템에 전기적 신호를 전송한다, 랜카드, 케이블, 허브, 라우터와 같은 물리적인 것과데이터 전송을 위해 사용하는 전압도 물리계층에 속하게 됩니다.

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

2계층인 데이터링크 계층은 물리적 링크를 통해 데이터를 신뢰성 있게전송하는 계층을 말합니다. 하위계층에 속하며 물리계층 바로 위에 위치합니다. 비트들을 프레임(Frame)이라는 논리적 단위로 구성하여 전송합니다. 시스템 간에 오류 없는 데이터 전송을위하여 네트워크 계층에서 받은 데이터 단위(패킷)를 프레임으로 구성하여 물리계층으로 전송합니다. 데이터링크 계층 데이터 단위를 프레임(Frame)' 이라 부릅니다. 즉 프레임은 데이터+ 헤더 + 트레일러로 구성이 되어 있습니다. 송신자의 프레임을 수신자 측에서 받는다면 헤더와 트레일러를 제거한 후에 네트워크 계층으로 전송합니다.

3. 네트워크 계층(Network Layer) 

3계층인 네트워크 계층에서는 패킷을 송신측으로부터 수신측으로 전송하는 구간이며 상위계층에 연결하는데 필요한 데이터 전송과 경로를 선택하는 기능을 제공합니다. 데이터가 전송될 수신측의 주소를 찾고 수신된 데이터의 주소를 확인하여 자신의 것이면 상위 계층인 전송계층으로 전송합니다. 라우팅 프로토콜을 사용하여 최적의 경로를 선택하는 계층구간이기도 합니다. 데이터를 패킷(Packet)' 단위로 분할하여 전송한 후 재결합하는 방식을 쓰고, 데이터 링크 계층이 인접하는 두 노드간에 전송을 담당한다고 하면 네트워크 계층은 각 패킷이 송신지에서 수신지까지 정확하게 전송되도록 경로를 책임지는 구간이다. 네트워크 계층 데이터 단위는 패킷(Packet)' 이라 합니다.

4. 전송계층(Transport Layer)

4계층인 전송계층은 시스템 종단간에 투명한 데이터 전송을 양방향으로 행하는 계층이며 두 시스템간의 신뢰성있는 데이터 전송을 보장합니다. 프로토콜은 TCP, UDP, SPX 등과 관련있습니다. 오류복구와 흐름제어 기능을 담당하는 계층이기도 합니다. 데이터 헤더에는 포트주소 또는 소켓(socket) 주소를 포함하고 있고 순서(squence) 또는 세그먼트 번호가 포함되어 있습니다. 이는 세션계층으로부터 온 데이터를 수신할 때 데이터를 전송할 수 있는 세그먼트로 나누고 수신측에서 수신자가 재조립할 수 있도록 순서를 헤더에 표시하게 됩니다. 네트워크 계층은 전송해야 하는 시스템에게 각 패킷을 전송하는 일을 하고 전송계층은 해당 시스템의 응용 프로그램에게 모든 데이터를 전송하는 역할을 합니다. 전송계층에 있어서 데이터 단위는 '세그먼트(segment)' 라 합니다.

5. 세션계층(Session Layer)

세션계층은 5계층으로써, 응용 프로그램 계층간의 통신에 대한 제어 구조를 제공하기 위해 응용 프로그램 사이의 접속을 설정, 유지, 종료시켜주는 역할을 하는 계층입니다. 통신 장치들 간의 설정을 유지하며 동기화하고, 데이터 단위를 전송계층으로 전송할 순서를 결정하고 데이터에 대한 점검 및 복구를 위해 동기를 위한 위치를 제공합니다. 또한 세션을 종료할 필요가 있을 때 적절한 시간을 수신자에게 알려 줍니다. 세션계층의 데이터 단위는 'n메시지(message)' 입니다.

6. 표현계층(Presentation Layer) 

6계층인 표현계층은 데이터 표현차이를 해결하기 위하여 서로 다형식을 변환해 주거나 공통형식을 제공해 주는 계층을 말합니다. 송신측과 수신측 사이에 표준화된 데이터 형식에 대해 규정합니다. 예를 들면 이미지가 bmp인지, jpg 인지, 압축이 14었는지 등의 표현과 관련된 구분을 지을 수 있습니다. 이는 두 시스템간에 서로 다르게 사용하는 문자 및 그래픽 문자 등을 위해 번역을 수행하며 전송데이터를 서로 이해할 수 있도록 하는 것이 목적입니다. 보안을 위하여 송신측에서 암화하고 수신측에서 복호화하며 전송률을 높이기 위하여 압축을 합니다. 표현계층에서 데이터 단위는 메시지(message)' 입니다.

7. 응용계층(Application Layer) 

OSI 모델에서 마지막모델인 응용계층은 가장 상위계층에 속하며 실제 통신의 최종 목표에 해당하는 가장 중요한 계층입니다. 사용자로부터 정보를 입력받아 하위계층으로 전달하고 하위계층에서 전송한 데이터를 사용자에게 전달하는 기능을 합니다. 응용프로세스(사용자, 응용프로그램)가 네트워크에 접근하는 수단을 제공하여 서로 간에 데이터를 교환할 수 있는 창구 역할을 합니다. 파일 전송, DB, 원격접속, 메일전송 등 응용서비스를 네트워크에 접속시키는 역할을 담당하는 계층입니다. FTP, SMTP, POP3, HTTP와 같은 프로토콜이 관련있고 응용계층의 데이터 단위는 메시지(message)' 입니다.

 

 

 

 

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

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

+ Recent posts