본문 바로가기
우테코

[6기 프리코스 프론트] 1주차 회고 - 숫자야구

by limew 2023. 10. 26.

 

혼자 공부하면서 텐션이 떨어지고 우테코는 어떤 과정을 중요시 여기는지 궁금해서 6기 프론트엔드에 지원했다.

이 과정에서 새로운 input을 통해 나에게 고착된 잘못된 습관을 고치고 커뮤니케이션, 코드리뷰 역량을 기르고 싶다. 

글솜씨가 없어서 이전엔 간단하게만 기록을 해왔지만 최대한 프리코스를 미션을 수행하면서 고민의 흔적과 배운 것들, 보충해야 할 것들을 기록하겠다.

 

JS 컨벤션

커밋 컨벤션

이전엔 버그수정, 새로운기능 추가, 리팩토링 등 따로 분류를 안 하고 커밋했는데

이번에는 미리 짠 구현목록과 커밋 컨벤션에 따라 커밋했다. 이렇게 하니 집중해서 코드를 짤 수 있고 다른 내용과 섞여있지 않으니 문제가 발생한 위치를 유추하거나 히스토리를 볼 때 쉽게 찾을 수 있었다.

  • Feat : 새로운 기능을 추가하는 경우
  • Fix : 버그를 고친경우
  • Docs : 문서를 수정한 경우
  • Style : 코드 포맷 변경, 세미콜론 누락, 코드 수정이 없는경우
  • Refactor : 코드 리펙토링
  • Test : 테스트 코드. 리펙토링 테스트 코드를 추가했을 때
  • Design : CSS 등 사용자가 UI 디자인을 변경했을 때
  • Rename : 파일명(or 폴더명) 을 수정한 경우
  • Remove : 코드(파일) 의 삭제가 있을 때. "Clean", "Eliminate" 를 사용하기도 함

 

프로젝트 구조

1주차 미션은 정해진 패턴이나 구조 요구사항이 없이 구현만 하면 됐지만 전에 개인프로젝트를 할때 정리도 안 하고 막 추가하다가 후에 리팩토링하는데 무척 애를 먹은 기억 있어서 이번엔 간단한 MVC구조에 맞췄다. 

 

 

먼저 각 파트마다 필요한 필드와 메소드를 노트에 정리하고 한눈에 쉽게 알아보기 위해 drawIO를 사용해서 클래스 다이어그램을 그려보았다.

  • 먼저 각 게임마다 해당 게임의 답과, 볼 스트라이크 결과가 존재한다고 생각해서
    • 컴퓨터가 생성하는 답(answerArr)과 사용자의 입력에 대한 볼과 스트라이크의 결과 필드(ballStrike)를 갖는 Game클래스를 선언했다. (Model)
    • 볼, 스트라이크를 체크하는 메소드도 Game에 추가했다. 전부 controller에 넣으면 복잡해지기 때문이다.
  • Game 인스턴스들을 컨트롤할 수 있는 GameController클래스는 최대한 게임시작, 예외처리, 게임끝, 리플레이 등과 같은 게임의 주요 흐름 메서드만 추가했다.
  • 그리고 콘솔에 찍히는 View는 사용자에게 질문을 하여 입력을 받는 InputView와, 출력하는 OutputView로 구분했다.
  • 이렇게 대략 생각을 해놨지만 막상 코드를 짜니 이걸 어느 부분에 넣어야 할지 고민이 많이 됐다. 예를 들어 사용자에게 입력을 받아 유효성을 체크하거나 입력에 대한 볼스트라이크 결과를 체크해야하는 메서드를 Input View에 넣어야할지 Controller쪽에 넣어야할지 확실하지 않았다.
  • 인터넷을 뒤지고 다른 프로젝트들을 참고하며 이를 나누는 완벽한 기준은 없고 비즈니스과 상황에 따라 정한다는 것을 알게 됐다.
  • 개인적으로 이런식으로 구별하면 괜찮을거 같다
    • view: 입력 자체에 관한 검사 (입력 했는지, 숫자인지, 문자열인지)
    • controller: 비즈니스가 관여된 입력 (패스워드, 이메일, 욕설이나 금지어 식별 등)
  • Input, Output은 말 그대로 입력에 관한 일만 하도록 만들고, 다른 로직 판단은 Controller에서 하기로 정했다. 이렇게 하면 코드가 기능별 분류되어  더 빨리 문제를 해결 할 수 있다고 생각했기 떄문이다.
    • 예를 들어 콘솔조차 나오지 않으면 view를 먼저 확인한다. 콘솔은 나오는데 내용에 문제가 있으면 로직을 다루는 controller부터 먼저 확인한다.

 

비슷한 메소드들을 한 곳에 그룹핑하기

 

How do I group similar methods and variables together in an object oriented approach?

I'm looking for standard way to do this.. This post here explores 3 ways. Here to. Which way should you use for a generic case...is it a matter of preference. For example if I use the instantiatio...

stackoverflow.com

 

우테코 Mission Utility

 

GitHub - woowacourse-projects/javascript-mission-utils: Utility library for mission

Utility library for mission. Contribute to woowacourse-projects/javascript-mission-utils development by creating an account on GitHub.

github.com

 

Random 및 Console API를 사용하여 구현해야했다

 

import { Console } from "@woowacourse/mission-utils";

// msg문자열을 콘솔에 출력
Console.print(msg); 

// start, end를 포함한 사이의 랜덤숫자를 반환
Random.pickNumberInRange(start, end); 

// 주어진 질문을 화면에 출력하고, 사용자가 입력한 답변을 Promise를 통해 반환한다.
async function getUsername() {
  try {
    const username = await Console.readLineAsync('닉네임을 입력해주세요.');
  } catch (error) {
    // reject 되는 경우
  }
}

 

 

💎 주의해야 할 점은 readLineAsync가 비동기 함수여서 이에 관련된 함수들을 모두 async await 처리해줘야한다.
(참고로 테스팅 할 때 몇몇 메소드 처리해주는 걸 빼먹어서 하루 종일 어디가 문제인지 찾았다ㅠㅎ )

 

Trim

문자열. trim 메소드는 시작과 끝 부분의 공백을 지워준다

 

숫자가 정수인지 소수인지 확인하기

입력된 숫자가 소수일 예외상황을 고려했다.

number % 1 !== 0; // 1로 나눴을 때 나머지가 0이면 소수이다

 

중첩 try catch (보충)

readLineAsync와 예외처리를 하는 메소드에서 try catch를 썼는데 여러 메소드들이 중첩되면서 

throw new Error가 중첩되면서 이런 웃픈 에러가 출력됐다...ㅋ

 

 

내부catch는 메시지만 throw하고 가장 바깥에 있는 catch에서 new Error()를 생성해서 해결은 했지만

관련된 메소드들을 찾아가며 수정하는데 이렇게 중첩된건 클린코드에 맞지 않다는 생각이 들었다.

하지만 이런 경우에 이렇게 하는 방법 밖에 없는지, 아님 다른 방법이 있는지 추후 보충하겠다.

(개인적으로 throw할 건 독립적으로 빼야 된다고 생각하지만 당시 내 머리로는 중첩이 될 수 밖에 없었다 어떤 방법이 있을까 보충하겠다)

참고로 미션 발표 후 이렇게 보충된 요구사항이 이메일로 통보됐다.

 

중첩 try catch 실행 순서 참고

 

[자바] 다중, 이중, 중첩 try - catch문

첫 번째 외부 try문 실행두 번째 내부 try문 실행, Exception객체 생성 후, 고의로 예외 발생시킴.예외발생되었으니, 내부 catch문 실행문자열 출력 후, 다시 고의로 예외 발생시킴.예외가 다시 발생되

velog.io

 

nested throw는 안 좋은 걸까

 

Are nested Try/Catch blocks a bad idea?

Let's say we have a structure like so: Try ' Outer try code, that can fail with more generic conditions, ' that I know less about and might not be able to handle Try ' Inner try code, ...

stackoverflow.com

 

 

InputView안 catch 처리 (보충 중)

입력에 관한 예외처리를 한단계 밖인 controller에서 처리했다.

그럼 여기 catch는 이렇게 남겨놔도 되나, 아님 예외처리를 view에서 해야하나, 것 두 아님 여기는 입력을 못 받았을 떄에 대한 예외처리를 해야하나 

 

클래스 안의 한 메소드는 어느정도로 분리 해야할까 (보충)

asknumber 안에 asknumber 이렇게 중첩이 된다..

  async startGame() {
    try {
      this.createAGame(); // 게임생성
      OutputView.print(Message.Greeting); // 인사말 출력
      await this.askNumbers(); // 번호 물어보기
    } catch (err) {
      throw new Error(err);
    }
  }

 

테스트 종료 후 노란색 경고 (보충)

테스트는 다 통과했는데 밑의 jest가 안 끝났다는 경고가 떴다

 

첫번째 생각

앱을 종료하는 메서드를 적지 않아서인가 라고 생각했지만 process.exit 이 외에 종료하는 방법을 아직 찾고있는 중이다.

 

두번째 생각

app.play()가 실행되는 코드 중 자체 테스트 하기 위해 App.js에 쓴 밑의 코드를 지웠다

const app = new App();
app.play();

 

두번째 방법을 적용한 뒤

테스트 통과 후 노란색 글이 더이상 안 나타나고 테스트가 종료됐다

 

이런식으로 해결하는게 맞는지는 추후 보충하겠다.

 

process.exit()를 사용하지 않고 Node.js 종료하기 (보충)

오구사항

컨트롤러에 quitGame라는 빈 메소드를 추가하여 "아무 일도 하지 않음" 을 명시적으로 표현하고 앱을 종료했다.

일단 지금은 에러를 던지거나 2를 입력하면 다음 함수가 없어서 자동 종료는 되지만 또 다른 방법이 있는지 찾아보고있다.

 

 

예제 테스트 

예제 테스트를 실행했는데 처음에는 "예기치 못한 오류로.."라고 문구가 떠서 멘붕이 왔다.

로컬 테스트는 다 통과했는데 왜 실행조차 안 되고, 또 문제가 어디서 발생했는지 알 수 없으니까 너무 답답했다.

반나절 동안 원인을 찾았는데 이게 내가 전에 쓴게 문제인지 의심까지 들었다. 몇 일간의 노력의 결과가 나오지 않아 허무했다ㅠ

모든것을 의심하라 작은 것 까지

좀 쉰 다음 머리를 비우고 다시 책상에 앉았다. 제출가이드를 보니 이런 경우는 테스트는 무슨 빌드조차 실패한거라고 적혀있었다.

그래서 난 큰 것들부터 하나씩 확인해서 원인에서 제외시키기로 했다.

1. 내 코드를 다 백업해놓고 초기코드만 남겨놓은걸로 테스트를 실행했을때 빌드가 되는지 확인 (이 조차 빌드가 안 된다면 나의 문제가 아님)

2. 실행시작점인 app에서부터 빌드가 실패할 때까지 단위별로 코드를 추가하며 문제의 원인 찾기

 

그러던 중 오마이갓... 원인을 찾았다.

Message.js로 import 해야할 것이 message.js로 적혀있었던거다

import Message from "../src/utils/message.js";

아니 이런 문제가 있는데 로컬에서 빨간줄 조차 안 나오고 심지어 테스트를 통과했다는게 어이가 없었지만 뭐 내 잘못이지..파일이름 첫 글자를 차라리 대문자로 통일하는게 나을 것 같다.어쨌든 이 부분을 수정한 뒤 실행하니 다행히 통과했다!

 

앞으로 빌드 문제를 만나면 이런 작은 것들을 다시 한번 체크해봐야겠다

  • import, require 방식이 맞는지
  • import 하는 파일 이름이 맞는지

그리고 조급해하기보다는 찬찬히 내가 짠 코드를 한 줄 한 줄 읽으면서 문제를 찾는게 더 빠를 수도 있다는 걸 배웠다.

 

소감

보다시피 아직 보충할 것들이 많다ㅋ 그래도 몇몇 문제는 해결했을 때는 너무 좋았다.

단순 구현만 하는게 아니라 과정 중 만나는 문제를 분석하고 원인을 찾는 과정에서 "아 이게 우테코가 바라는 경험을 통한 학습 방법이구나" 라는 것을 느꼈고 이전에 보편적인 프로젝트를 할때보다 더 재밌게 느껴진 내 자신이 새삼 신기했다. 이전에는 비슷한 걸 해보는 식이여서 쉽게 지루해졌기 때문이다.

 

그 전엔 혼자 한 프로젝트에선 손 가는대로 코드를 짰지만, 이번엔 먼저 구현목록을 세우고 그에 맞춰 진행해서 중간에 길을 잃지 않고 집중할 수 있었다. 코드 한 줄 한 줄을 쓰면서 직접 겪어보면서 생각나는 질문들이 너무 많았고 특히 "어떻게 해야 누구나 쉽게 이해할 수 있을까"를 가장 고민하다가 개인적으로 괜찮다고 생각하는 기준으로 진행했다. 어서 미션이 끝나고 이 부분에 대해서 다른 분들은 어떤 식으로 하셨는지 토론하고 싶다. 또한 코드분할과 async await 비동기함수와 try catch에 대해 깊게 고민할 수 있었다

 

처음에는 단순히 혼자하는게 공부하는게 외로워서 시작한 프리코스이지만 커뮤니티에서 열심히하는 분들을 보며 자극을 얻을 수 있었고 무엇보다 "역시 나는 개발을 좋아하구나"를 다시 느꼈다는게 제일 큰 성과이다.

앞으로 남은 과정에서 새로운 걸 채울 수 있다고 생각하니 두근거리고 뿌듯하다. 모두 화이팅합시다!!!

 


공통피드백 주의사항

    • 커밋 메시지를 의미 있게 작성한다 (커밋을 기능별 더 세세하게 나누고 내용을 더 구체적으로 작성하도록 해야겠다)
    • Pull Request를 보내기 전 main branch가 아닌 자신의 아이디로 만든 브랜치에서 작업한 후 PR을 보낸다.
git checkout -b 아이디
    • 축약하지 않는다
      • 클래스 이름이 Order라면 shipOrder라고 메서드 이름을 지을 필요가 없다. 짧게 ship()이라고 하면 클라이언트에서는 order.ship()라고 호출하며, 간결한 호출의 표현이 된다. 
      • 의도를 드러낼 수 있다면 이름이 길어져도 괜찮다.
    • JavaScript에서 제공하는 API를 적극 활용한다
      • join, map같이 JavaScript API에서 제공하는 기능이면 최대한 사용하고, 없을 시에 직접 구현한다.
      • 이건 의외였다 직접 구현하는게 기술적으로 더 어려워서 선호하는 줄 알았는데 API를 더 적극 활용해야겠다

 

코드리뷰

감사하게 리뷰를 해주신 분들을 통해 알게 된 내용과 다음에 주의해야할 점을 정리해봤다.

 

  • 상수object를 Object.freeze메서드로 객체를 불변으로 만들어 코드의 안정성을 높일 수 있다..
const constants = {
  QUIT: "2",
};

Object.freeze(constants);
constants.QUIT = "3"; // throw error

 

참고

eslint 설정하기

'우테코' 카테고리의 다른 글

[6기 프리코스 프론트] 4주차 크리스마스  (0) 2023.11.12