우테코: 프리코스 4주차 미션 복기

드디어 마지막 4주차 프리코스 미션을 완료하게 되었다!!! 4주차는 1,2,3주차에 쌓아온 것들을 모두 종합하여 기능을 구현하는 느낌이었다. 기능구현 설명을 실제로 메일받는 것처럼 작성해서 진짜 개발자가 된 느낌..을 받을 수 있었다..ㅎ

이번 미션은 기능구현을 어떻게 해석하느냐도 중요했던 것 같고, 미션 제출을 fork가 아니라 개인 레포를 만들어서 private로 제한하는 등의 방식으로 진행했다. 여러모로 마지막을 이전과는 달리 새로운 방식으로 진행해서 색달랐다.

4주차: 크리스마스 프로모션 Repository 4주차 프리코스 미션 성공


새롭게 배운점

비즈니스 로직과 UI 로직 분리하기

해당 내용은 3,4주차의 가장 핵심이었던 목표였다고 생각한다.
1,2주차에서는 그냥 한 곳에 다 작성했다면, 3주차에서는 UI로직과 비즈니스 로직을 분리하는 것에만 그쳤고, 4주차에서는 비즈니스 로직을 더 쪼개려고 노력했다.

4주차 미션 폴더

하나의 파일/함수에 하나의 일만 책임질 수 있도록 적당한 선에서 로직들을 분리했고, 이 분리한 로직들을 하나의 파일에 다시 모아 하나의 기능을 하도록 했다.
이렇게 하다가 느낀것은 이게 MVC인건가? 라는 생각이 들었다. 이전에는 MVC가 머리로는 이해되지만.. 어떻게 하라는 건지 잘 모르겠었다. 그래서 적용해본적이 없었는데, 4주차 미션을 진행하면서 이렇게 로직을 분리하고 있는 것이 MVC인거구나를 깨달았다.

아직은 명확하게 Model과 Controller를 분리하는 기준은 잘 모르겠지만, 이번 미션을 통해 MVC는 이래서 사용하는 거구나를 깨닫게되었다.
그리고 분리한 로직들을 하나의 파일에 모아 하나의 기능을 하도록 만든 것이 모듈이라는 것을 깨달았다. 모듈을 이전에도 알고 있었지만, 정말 명확하게 알고 사용하는 용어..는 아니었는데, 이번 경험을 통해 내가 한 행동이 모듈을 만든 것이었다는 걸 알게되었다.

필드가 많으면 왜 안좋고 어떻게 개선할 수 있을까?

3주차 공통 피드백 내용에서 필드의 수를 줄이기 위해 노력해야한다는 내용이 있었다.
필드를 줄여야 한다고 했는데, 그렇다면 필드는 왜 사용하는 것일까? 라는 의문이 들었다. 그래서 필드를 사용하는 이유를 찾아보았다.

필드를 사용하는 이유는 다음과 같았다.

  • 코드가 명확해질 수 있다.
    클래스 내부에 어떤 속성이나 메서드가 속해있는지 명확하게 표현할 수 있기 때문이다.

  • 프로토타입에 직접 추가되지 않는다.
    그렇기에 인스턴스 메모리 사용을 최적화하고 상속관계를 유지하는데 도움을 준다.
  • this 바인딩을 할 필요가 없다.
    this는 constructor 내부나 메소드내에서만 유효하다. constructor 내부어서 this를 사용하는 이유는 생성자의 패러미터 이름과 동일하여 클래스 필드임을 명확하게 하기 위해서이다.
    그렇기에 constructor 내부가 아닌 class 몸체에서는 클래스 필드를 정의할 때 this 바인딩을 사용하지 않는다.
    하지만, 클래스 필드를 참조할때는 반드시 this를 붙여서 사용해야 한다.

  • 캡슐화를 유지할 수 있다.
    클래스 내부에 private한 속성을 생성할 수 있어서 외부에서 직접 접근하지 않도록 하여 캡슐화를 유지할 수 있다.

그렇다면 이런 장점이 있는데 왜 사용을 줄이라는 것일까?
필드를 사용하는 이유를 알고 나니 해당 피드백 문서에서 필드의 수를 줄이라는 것은 이런것이 아닐까 생각이 들었다.
불필요한 필드의 수를 줄이기 위해 노력할 것.
필드는 장점이 뛰어나기에 단순히 필드의 수를 줄이라는 말이 아닌 중복적으로, 굳이 사용하지 않아도 되는 필드의 수를 줄이라는 것이 아닐까?라고 생각이 들었다.

그래서 클래스를 사용할 때, 무분별하게 필드를 사용하는 것을 자제하기 위해 해당 클래스에서 정말 이 필드가 필요한 것인가? 꼭 private 해야 할까? 라는 의문을 가지며 코드를 작성했다.
해당 피드백 문서가 아니었다면 무분별하게 클래스에 private 필드를 사용했을 것이다. 해당 내용 덕분에 필드를 사용할때 정말 신중하게 생각하며 사용했다.

배열이 아닌 객체를 사용하기

이번 미션은 가지고 있어야 할 데이터가 많았기 때문에 이 데이터들을 보다 효율적으로 꺼내서 쓸 수 있도록 배열이 아닌 객체를 주로 사용했다.

기능 요구사항에 평일 이벤트로 ‘디저트’ 주문시 수량만큼 할인을 하는 요구사항이 존재했다. 이때 ‘디저트’ 종류의 메뉴를 따로 배열로 관리하는 것이 아닌, 메뉴판을 객체로 저장해두고 해당 메뉴판에서 디저트에 해당하는 메뉴만 뽑아 쓰는 것이 좋다고 생각했다.

MENU 객체

나중에 유지보수를 진행하게 될 경우, 뽑아내고자 하는 음식의 종류만 변경하면 되기 때문이다.

객체안의 객체 사용, 객체안의 배열 사용

객체를 사용하여 데이터를 저장하고자 할 때 떠오른 의문점이 있었다.
객체안에 객체를 사용하는 경우와 객체안에 배열을 사용하는 경우는 어떤 때 일까? 메뉴판 데이터를 생성하기 전에 좀 더 알맞는 데이터 구조를 만들고자 찾아보았다.

객체안에 객체를 사용하는 경우는 구조화된 데이터가 필요할때, 읽기 쉬운 코드가 필요할 때 주로 사용한다.
객체 안에 객체를 사용하면 데이터를 계층적으로 구조화할 수 있고, 이에 따라 코드가 읽고 이해하기 쉬워진다. 이러한 경우에 객체 안에 객체를 사용한다는 것을 알게 되었다.

객체안에 배열을 사용하는 경우는 순서가 중요한 데이터거나 동적 데이터일 때 주로 사용한다.
특정 순서로 진행되어야 한다거나 데이터를 쉽게 추가하거나 제거해야 하는, 동적으로 데이터를 다뤄야하는 경우에는 객체안에 배열을 사용하는 것이 더 좋을 수 있다는 것을 알게되었다.

그래서 나는 메뉴판 데이터를 생성할 때, 객체안에 배열을 사용했다. (음?)
key에는 메뉴명을 value는 배열을 사용하여 0번째에는 가격을 1번째에는 종류를 넣어, value에서 0번째 값을 꺼내면 무조건 가격만 나오도록 했다.

사실 객체안에 객체를 사용하는 것이 더 맞지 않을까? 생각하긴 했다.
객체안에 객체를 사용하는 것이 해당 메뉴의 가격과 종류를 명확하게 알 수 있어서 현재도 이것이 가장 좋은 방법일것이라고 생각한다.

그럼에도 객체안에 배열을 선택한 것은 value로 많은 값이 들어있지 않기 때문에 배열로 관리하는 것이 더 편할 것이라고 판단했다.
value의 값이 많이 존재했더라면 객체안에 객체가 훨씬 더 좋았을 것이라고 바로 판단했을 것이다. 하지만 해당 프로젝트에서는 value값으로 가격과 종류 2가지만 가지고 있기 때문에 객체대신 배열을 선택했다.

그래도 역시 장기적으로 보았을때는 객체안에 객체가 맞는 선택인것 같다..^^ 현재의 편안함을 위해 배열을 선택한..ㅎ..
그래서 어차피 복기니까! 만약 객체안의 객체로 메뉴를 만들었다면 어떻게 만들었을까? 생각이 들어서 간단하게 작성해봤다.

const MENU = {
  양송이수프: {
    price: 6000,
    type: '애피타이저',
  },
  타파스: {
    price: 5500,
    type: '애피타이저',
  },
  시저샐러드: {
    price: 8000,
    type: '애피타이저',
  },
  티본스테이크: {
    price: 55000,
    type: '메인',
  }
  ...
};

일단 메뉴의 이름을 Key로 하고 이에 대한 value로 객체를 넣어서, 해당 객체의 key로 가격과 종류를 넣었을 것 같다. 이러면 나중에 유지보수에도 편할 것 같다. 어차피 MENU 객체는 외부에서 수정할 일이 절대! 없으니까..
과거의 나는 무슨 정신으로 객체안에 배열을 쓴거지?

재입력받기, await 재귀함수가 아니라 return await

지난 3주차에서는 App 클래스 내부에서 await 재귀함수를 통해 정해진 입력형식이 아닐 경우 다시 입력받도록 했다.
이번 4주차에서는 입력받는 함수를 App.js가 아니라 Order 모듈을 생성하여 내부에서 재귀를 통해 재입력받기를 했다.

이때, 3주차와 같이 await 재귀함수를 사용하였는데 이렇게 되면 다시 입력받았던 값을 저장하지 못하고 undefined로 값이 변경되었다.
해당 오류를 수정하고자 챗GPT에게 물어본 결과.. await를 return으로 변경하기만 하면 되는 것이었다! return으로 변경하기 원하는 방향으로 잘 작동했다.

return을 사용하여 재입력받기 코드

3주차에서는 class 내부 함수로 생성했을 때는 await로 재호출하였지만 이번에는 Order이라는 함수를 새로운 파일에 생성하여 재호출을 진행하는 것이기 때문에 return을 사용해야 됐던 것이었다.
단순히 로직이 비슷하다고 해서 이전의 방식을 그대로 차용하는 것은 큰 위험이 될 수 있다는 것을 몸소 깨달았다.. 챗GPT 없었으면 하루종일 해당 에러를 잡고 있었을 생각을 하니..ㄷ

Object.freeze()?

Object.freeze()는 지난 3주차 미션의 다른 분들의 코드를 보면서 가장 많이 보았던 메서드였다.
이번에 해당 메서드를 처음 보았고, 대부분 상수를 만들때 사용하는 것을 보았다. 그래서 이 메서드를 정확히 알아보고 사용해보고자 찾아보았다.

해당 메서드는 객체를 얼릴때 사용하는 것으로 객체의 속성들을 수정할 수 없게 즉, 읽기 전용으로 만들어주는 메서드이다.
이 메서드를 활용하여 메뉴판 객체나 뱃지, 혜택 이름 리스트 등을 얼렸다. 해당 객체들을 외부에서 사용할 경우 변경되지 않는 데이터이기 때문에 Object.freeze()를 사용했다.

Object.freeze() 사용한 코드

객체를 얼리고 따로 파일을 생성하여 보관함으로, 반복되는 문구의 사용을 줄일 수 있어서 유지보수를 하는 것에 있어 큰 도움이 된다는 것을 알게되었다.

실수를 통해 배운점

객체의 길이를 알고 싶다면

객체에는 length를 바로 사용할 수 없다. 따라서 객체의 길이를 알고 싶다면 keys를 뽑아낸 배열에 length를 사용하는 방식으로 알 수 있다.

객체 길이 구하기

객체에 map함수 사용하기

map, reduce와 같은 함수는 배열에 사용하는 것이기 때문에 객체에는 바로 사용이 불가능하다. 따라서 위에 길이를 구한 방식과 동일하게 키, 값, 키-값 쌍의 형태로 배열을 출력하여 map함수를 사용하면 된다.

map함수를 객체에 사용하기

toThrow는 직접 던지면 안된다.

방문날짜 입력 테스트 실패

테스트코드를 작성할 때 toThrow를 사용했는데, 테스트 실패가 발생했다. 아무리 생각해도? 뭐가 이상한지 모르겠어서 영원한 친구 챗GPT에게 물어봤다.

toThrow 사용 에러 해결방법

음.. 그렇다! 함수를 인수로 받아 실행해야 하는데, 클래스를 직접 생성하는 코드를 전달해서 발생한 문제였다.

문제가 발생한 코드 방문날짜 입력 잘못된 테스트코드

따라서 함수를 인수를 받아 실행하도록 화살표 함수를 추가하여 테스트코드를 변경하였다.

문제를 해결한 코드 방문날짜 입력 올바른 테스트코드

어쩐지.. 다들 화살표함수를 추가하더니.. 다 이유가 있는 것이었다. 의문이 들때는 일단 실행해보고 왜 저렇게 사용하는지 알고 사용하도록 하자.

테스트코드 성공 방문날짜 입력 테스트 성공


프리코스를 마치며

이번 4주차까지 프리코스를 진행하면서 정말 많은 것을 배우고 얻고 느낄 수 있었다.
1주차때만 하더라도 포기할까 생각도 들었고 4주차까지 진행할 수 있을까 확신이 들지 않았다. 그런 내가 4주차까지 모든 미션을 예제 테스트 실행을 완벽히 하고 제출했다는 것이 믿기지가 않는다!!

불과 4주전까지만 해도 바닐라 자바스크립트로 기능을 구현해야 한다고 했을 때 정말 자신이 없었다. 클래스를 사용할 줄 몰랐고, 테스트코드를 직접 작성해본 적도 없으며, throw를 사용하여 예외를 던져본 적도 없었다. (과거의 나: throw가 뭐징?)
그러나 4주가 지난 지금, 라이브러리나 프레임워크 없이 기능 구현해라? 지금은 시간이 걸릴지라도 자신있게 완성은 할 수 있다고 말할 수 있는 사람이 되었다!

그리고 코딩 능력 말고 새롭게 얻은 것이 있다.
바로, 챗GPT 활용 능력. 이전에는 코딩하면서 사용한적이 손에 꼽을 정도로 없었는데, 이번 프리코스를 진행하면서 도저히 서칭만으로는.. 해결되지 않는… 문제가 많았기에 챗GPT를 적극 사용했다.
처음에는 물어봐도 뭔가 확실한 답을.. 얻을 수 없었는데, 4주동안 열심히 활용한 결과. 이제는 어느정도 원하는 답을 도출하기 위한 질문을 할 수 있게 되었다. 이제 챗GPT 없이는 못살앙… 내 영원한 코딩 친구💙

이 외에도, 문제 찾기 능력 (초보에서 중수는 된듯하다), 다른 사람 코드를 읽는 능력이 향상된 것 같다. 클래스 사용과 테스트코드 작성을 하는 방법을 도저히 감을 잡을 수가 없어서 다른 분들의 코드를 많이 봤었다. 덕분에 코드를 읽는 능력이 이전보다는 많이 향상된 느낌이랄까? 여러 파일과 폴더로 쪼개져있어도 이제는 코드를 보고 다음 파일을 따라가 읽는 능력이 향상됐다!
그리고 문제 찾기도.. 에러나는 곳을.. 정말.. 몇시간씩 찾았기에.. 안 늘수가 없는 것 같다.. 에러.. 이녀석.. 날 고통스럽게 했지만 얻는게 있었으니..! 봐준다(?)

이 4주는 몰랐던 것을 알고 배울 수 있는 시간이었고, 개발에 대한 두려움과 편견을 없애줬으며, 4주동안 꾸준히 미션을 진행한다는 명목으로 코딩을 하면서 개발에 대한 자신감과 꾸준히 공부해왔다는 자기만족감을 얻을 수 있는 시간이었다.
4주의 시간이 정말 아깝지 않은 시간이었고, 목요일 15시면 오던 메일이 오지 않는다는 것이 시원하면서도 그립다.

누군가에게 프리코스를 할까말까 고민된다면 나는 자신있게 도전해보라고 하고싶다!
프리코스는 우테코에 선발되지 않아도 많은 것을 배울 수 있는 기회니까.
한달동안 열심히 공부하며 성장했다는 것을 느낄 수 있는 기회니까!

학습 참고자료
객체에 해당 key값이 존재하는지 확인하는 방법
TIL | 객체 안의 객체 접근, 객체 안의 배열 접근
💙챗GPT💙

Comments