본문 바로가기
TIL&WIL

241113 TIL - 재귀, Promise, 개인과제02

by 나노다 2024. 11. 13.

재귀 recursion

  • 재귀 함수는 어떠한 함수가 특정 조건에 도달할 때까지 자기 자신을 계속 호출
  • 이때 특정 조건을 종료 조건(Base Case)라 하고, 자신을 계속 호출하는 과정을 재귀 단계(Recursive Step)이라 함
  • 재귀 함수가 적절한 종료 조건 없이 재귀 단계를 지속할 시, 스택 오버플로우 문제를 야기함.
    while문 등을 활용해 대체하는 편이 좋음!

스택오버플로우가 발생한 메모리

  • 스택 오버플로우 : 콜스택에 쌓인 스택들이 가용 메모리를 넘어 힙 영역까지 침범한 경우

참고링크 : https://andy-archive.tistory.com/10

 

 

Promise 이해해보자

[프로미스의 상태]

Promise 순환 구조

 

new Promise()로 프로미스를 생성하고 이후 종료될 때까지 프로미스는 세 가지 처리 과정을 거침.

  • Pending (대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
/* 대기 상태(Pending) */
new Promise(); // 호출 시
new Promise(function(resolve, reject) { // 콜백함수 선언 시
  // ... (인자로 resolve와 reject 받음)
});
  • Fulfilled (이행) : 비동기 처리가 완료돼 프로미스가 결과 값을 반환해준 상태, resolve() → then()
/* 이행 상태 (Fulfilled) */
new Promise(function(resolve, reject) {
  resolve(); // 인자를 실행할 시
});

/* 예시 */
function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data); // 100이 담긴 data를 반환
  });
}
// then을 통해 resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태, reject() → then().catch()
/* 실패 상태 (Rejected) */
new Promise(function(resolve, reject) {
  reject(); // reject 호출 시
});

/* 예시 */
function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed")); // Error를 반환
  });
}

// then -> catch를 통해 reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

참고 링크 : https://joshua1988.github.io/web-development/javascript/promise-for-beginners/

 

 

개인과제 LoveLike

[오늘 한 작업]

[update 01]

- Classmate 클래스 속성 추가 (친밀도 증감에 따른 반응 구분을 위한 isIncrease 추가)

- Classmate 클래스 메서드 기본형 추가 (캐릭별 효과 차별화 예정)

[fix 01]

- 일부 클래스명 보다 적합하게 수정(EX: isWorkOut -> workOut)

- 주석 추가 및 수정 (기능 추가 필요한 곳에 표시)

 

[update 02]

1. 상황별 이벤트씬 작동 조건 추가

- 스테이지 내 장면 전환

- 기분 0 됐을 때

- 점심시간 이벤트

- 고백 성공 및 실패

- 자신감 및 친밀도 0 됐을 때

- 스테이지 클리어 실패했을 때

2. 고백 실패 여부 가리기 위한 isFailConfess 속성 추가

- 연인 판별하는 isDate 있지만, 얘로는 고백 상황 특정이 어려웠음

3. 이벤트별 텍스트 기본형 추가

- text.js에 상황별 텍스트 배열 담은 변수 추가 생성

[fix 02]

1. eventScene 함수 개편

- 말썽이던 인터벌 대신 for문으로 교체함. 대신 promise를 반환하는 간단한 시간차 함수를 만들어 투입

- 매개변수 추가 : 출력할 텍스트 배열, 출력 시간차, "1" 입력시 실행할 함수, "2" 입력시 실행할 함수 이렇게 넷 받음

- 분기 선택 함수 NextOrEnd 변경 : 기존 switch문 구조에서 무한루프 while문에 넣고 조건문을 통해 탈출하도록 변경

 

[update 03]

1. gameOver 함수 추가

- figlet 활용해 게임오버 글씨 띄움

- 재시작 선택 시 로비화면으로, 종료 선택 시 프로세스 닫히게 함

[fix 03]

- 게임 시작시 오프닝 이벤트씬을 통해 startGame 함수를 실행했는데, 바로 startGame 함수를 실행하고, 그 안에서 스테이지 시작 전에 오프닝 이벤트씬이 발생하도록 변경

 

[update 04]

1. talk 메서드 개편

- 플레이어의 말솜씨 스텟에 비례해 증가폭은 늘리고 감소폭은 줄임

- num 변수를 추가해 상대방 특성에 따라 증가 감소 결과가 달라지도록 함

[fix 04]

1. Classmate 클래스의 매개변수로 me를 통째로 받음

- 플레이어의 스텟에 따라 상호작용 결과를 달리함에 있어서, 스텟 변화를 실시간으로 적용받기 위함

2. myRoomScene 함수의 불필요한 매개변수를 제거

- 내 방에선 classmate가 끼어들 요소가 없기 때문에 제거!

 

[오늘 마주한 문제 상황]

1. 비동기 작업에 대한 이해 부족

 eventScene 함수의 재사용성을 늘리고 싶어서 노력하던 중 또 setInterval 내부의 콘솔이 찍히지 않는 문제와 마주했다!

콘솔을 이리 빼보고 저리 빼보고 했으나 별 소용이 없었고, 애를 먹다가 튜터님께 도움을 청하러 갔다.

문제의 코드...

 

 코드가 원하는대로 작동하지 않았던 큰 이유는 두 가지로, 먼저 async나 await를 남발하는 등 비동기 작업에 대한 이해가 부족했고, 다음으론 setInterval의 특성을 겉핡기 식으로 이해했다는 점이었다.

 Promise에 대한 온전한 이해 없이 단순히 await를 붙인 구문은 처리 완료까지 기다려준다고 덮어둔 채 코드를 짜니 생각대로 기다려줄리 만무했고, setInterval은 타이머가 시작된 순간 완료 처리가 되는 걸 몰랐으니 보이지 않는 곳에서 열심히 타이머를 돌려대고 있었던 것이었다.

 튜터님은 강제로 시간차를 맥이는 Promise를 반환하는 함수를 생성하고 반복문을 활용해 출력 사이사이 시간차를 주는 방식으로 문제를 해결해주셨고, 코드는 훨씬 간결하고 읽기 쉬워졌다. 강의를 들으며 아리송했던 부분을 좀 더 공부할 수 있는 계기가 돼서 좋았다! 

극복한 코드!!

 

 이후 이벤트 발동 분기 마다 await를 활용함에 있어 보다 자신감이 붙었고, 큰 어려움 없이 목표했던 작업을 마무리할 수 있게 됐다. 역시 기본 원리가 중요하다는 걸 다시금 깨닫는 사건이었다!

 

2. 메모리 저장 방식에 대한 이해 부족

 친밀도가 고정값으로 변하던 기본형 메서드들에 플레이어의 스텟과 스테이지별 특성을 적용할 시간이 됐다... 스테이지는 이미 stage 매개변수를 받아 속성에 저장하고 있었기 때문에 문제가 없었는데, 플레이어의 스텟을 불러오는 것에 애를 먹었다. 친밀도가 변하는 메서드는 Classmate 클래스에 있었고, 플레이어의 스텟과 스텟을 변하게 하는 메서드들은 Player 클래스에 있었기 때문이다!

이 넘들을 다른 클래스에 전해줘야 했다...

 

 처음 한 생각은 Player 클래스의 속성들을 Object.keys()로 뽑아서 classmate 인스턴스에 매개변수로 주는 방법이었다. 그러면 인스턴스 안에서 이 key값들을 통해 각 스텟의 value 값에 접근할 수 있을 거라 생각했다. 물론 실패했다! keys()로 뽑힌 배열에 담긴 녀석들은 더 이상 Player의 속성들이 아닌, 이름만 같은 무언가일 뿐이었기 때문이다.

 다음 한 생각은 Object.entries()로 뽑아서 key랑 value를 다 주는 거였다! 순간 천잰가 싶기도 하고 싱글벙글 바로 시도해봤지만 역시 실패했다. 왜냐면 player 인스턴스가 생성될 때의 value가 고정돼서 들어갔기 때문에, 이후 readBooks같은 메서드들로 스텟이 변하더라도 변한 값을 classmate에서 받을 수 없기 때문이었다.

 애를 먹던 중 혹시나 하는 마음에 이전 조원 분들께 도움!을 외쳤고, 코드 구경을 하며 사담도 나누던 중 문제가 의외로 수월하게 해결됐다! 바로 player 인스턴스를 통째로 classmate 매개변수로 넣는 것이었다!! 이렇게 하니 value에 집중해서 원하는대로 작동하지 않던 코드가, key가 저장된 주소를 참조할 수 있게 됐고, 덕분에 스텟의 변화를 실시간으로 반영할 수 있게 된 것이다!!

그대로 받아서 쏠쏠하게 활용~

 

 이후의 작업은 어렵지 않았고, 잠시 수다를 떨며 숨도 돌릴 수 있었고, 문제도 해결돼서 너무 좋은 마무리를 할 수 있었다. 역시 이야기를 나누는 것이 참 중요하다고 다시금 느끼는 하루였다!