[우아한테크코스 7기 프리코스] 2주차 회고

유소정·2024년 10월 27일
0
post-thumbnail
  • 어떻게 문제를 해결했나요?
  • 어떻게 문제를 접근했나요?
  • 어떤 생각과 고민이 있었나요?
  • 코드를 만들어가는 과정에서 어떤 경험을 했나요?
  • 어떤 생각의 사고 흐름이 있었나요?
  • 어떤 시행착오와 고민이 있었나요?
  • 지원서에 적은 목표에 가까워지고 있는 것 같나요?
  • 나에게 의미있는 회고를 써야한다.

🚀 지원서에 작성한 목표를 얼마나 달성하고 있다고 생각하나요? 그 이유는 무엇인가요?

83% 달성했다.

목표를 '한 미션 안에서 최소 3개 이상의 오류를 마인드셋 과정을 통해 해결하는 것'으로 잡았는데, 저번 미션에서는 3개를 해결했고 이번 미션에서는 2개를 해결했기 때문이다.

하지만 83%와 상관없이 이번 미션에서는 목표의 목적을 이뤘다.
이유는 저번 미션에서 발생한 오류가 이번 미션에서도 발생했는데, 저번에는 해결하는 데 30분이 걸린 것을 이번에는 1분 만에 해결했기 때문이다. 구글 스프레드 시트에 작성한 기록을 보고 바로 해결했다.

오류를 해결한 과정은 다음과 같다.

1. 내가 어떤 문제를 풀려고 하는지 한 문장으로 적는다.

'[ERROR]'로 시작하는 에러 메시지를 던질 때, Jest의 테스트 케이스에서 'rejects.toThrow'가 해당 에러를 정상적으로 감지하고 테스트가 통과해야 한다.

2. 올바르게 동작한다면 어떤 일이, 어떤 순서로 벌어져야 하는지 적는다.

코드에서 'throw new Error("[ERROR]");'가 실행되면, 이와 관련된 테스트 케이스인 'rejects.toThrow("[ERROR]");'가 통과해야 한다.

3. 원인이 될 만한 옵션들을 적어본다.

  1. 'new Error'를 잘못 사용했거나 올바르게 던지지 않았다.
  2. 코드 로직 오류로 인해 'throw' 구문에 도달하지 못했을 수 있다.
  3. 테스트 대상 함수가 비동기적으로 동작해야 하는데, 동기적으로 실행되고 있을 수 있다.
  4. 'rejects.toThrow'가 어떻게 작동하는지 정확히 이해하지 못했을 수 있다.

4. 옵션 중 하나를 골라서 가설을 세우고 검증한다.

  • 문제: 테스트 대상 함수가 비동기적으로 동작해야 하는데, 동기적으로 실행되고 있을 수 있다.
  • 분석:'rejects.toThrow'는 비동기 함수가 반환하는 'Promise'의 reject 상태에서만 동작한다. 하지만 내가 작성한 예외처리 함수는 동기 함수였기 때문에 이 테스트가 실패했다.

🚀 지원서에 작성한 목표를 변경해야 한다고 생각하시나요? 그렇다면 그 이유와 어떤 목표로 변경하고 싶으신가요?

변경하지 않는다.

이번 미션에서 최소 목표인 3개를 지키지 못했고 (생각보다 오류가 발생하지 않았다), 다음 미션은 더 복잡한 미션으로 예상돼서 3개를 채울 것 같다.

그리고 아직 오류가 발생했을 때, '올바른 동작을 한 줄로 정의하고, 원인을 찾는 연습'이 부족하다고 느껴서 더 많은 오류를 만나봐야 할 것 같다.

🚀 프리코스를 진행하면서 눈에 띄는 변화나 깨달은 점이 있나요?

눈에 띄는 변화

1주차 보다 프로그래밍을 멈추고 생각하는 시간이 자연스러워졌다. 이제는 미션이 어려워도 조급한 마음을 갖지 않고 최소한 당장 해야 할 일에 대해서는 머릿속으로 흐름이 그려질 때까지 멈춘다. 숙련도가 올라갈 때까지는 의식적으로 멈추는 습관을 유지하려고 한다.

깨달은 점

1주차에서는 확장성에 대한 고민이 부족했다. 요구사항에서 커스텀 연산자가 1개가 아니라 2개로 바뀌면 코드를 많이 바꿔야 했다. 그래서 '요구사항에 변화가 생길 때 어떻게 대응할 수 있을까? 어떻게 하면 수정할 때 최소한의 비용이 들지?'에 대해 고민하게 되었다. 그리고 2주차의 학습 목표 2개도 결국에는 '변화에 대응하기 쉬운 코드를 위한 목표들'이라는 것을 깨달았다.

그리고 2주차를 진행하며 질문이 생기고 대답하며 고민했다.

1. 테스트 코드는 의존성을 최대한 덜기 위한 도구인가?

의존성이 높은 함수는 테스트를 하기가 어려웠다. 다른 함수나 외부 모듈에 의존할 경우, 목킹이 필요해서 테스트 코드가 복잡해졌다. 그래서 최대한 한 함수가 다른 함수에 의존하지 않게 작성했더니, 테스트를 위해서뿐만 아니라 함수 자체가 좀 더 유지보수와 재사용성이 향상됐다.

그리고 TDD 방식을 고집할 필요는 없었다. TDD는 테스트를 먼저 만드는 것인데, 아직 테스트 코드 작성에 대한 숙련도가 떨어져서 테스트 코드를 기능보다 먼저 작성하는 게 조금 막연했다. 그래서 구현에 대한 확신이 있으면 몇 번은 구현 먼저 했다. 그리고 원하는 대로 동작하는지 확인하는 용도로 테스트 코드를 이용했다. 앞으로는 테스트 코드를 더 많이 작성해 보며 TDD 방식에 익숙해지려고 한다.

2. 명확한 의도를 담아서 전달하는 방법이 뭐가 있을까?

1주차 피드백과 프리코스 디스코드 통해서 명확한 의도의 이름을 짓는 단계를 만들었다.

1단계: 축약하지 않는 이름을 작성한다.
2단계: 내부 구현이 드러나는 부분을 감춘다.
3단계: 이름만 보고도 결과가 예상되는 이름을 짓는다.

1주차 피드백에 쓰여 있는 대로 처음에는 이름을 지을 때 축약하지 않았다. 예를 들면, '자동차가 전진하면 자동차의 위치가 1 증가하는 함수'의 이름을 지을 때 'IncreasePositionByOne'으로 지었다. 그런데 '확장성'을 생각하면 'One' 부분이 필요 없다고 느껴졌다. 그래서 'IncreasePosition'으로 바꾸었다. 그리고 내부 구현도 드러낼 필요가 없다고 생각이 들어서, position 값을 증가시키는 내부 구현을 감추며 'MoveForward'로 변경하였다. 이름만 봐도 어떤 동작을 할 것인지 예상이 가는 이름이 되었다.

추가로 이름뿐만 아니라, 커밋 메시지를 작성할 때도 '명확한 의도'를 드러냈다.
이를 위해서 하나의 커밋이 하나의 일만 해야 했고, 기능목록에 있는 대로 커밋하는 게 도움이 됐다.

3. 기능 목록을 좀 더 유연하게 작성할 수 있을까?

1주차에는 '최소한의 버전'의 기능 목록만 작성하고 점진적으로 기능 목록에 추가하는 것이 목표였다. 이번 주차에서는 거기에 더해서 각 기능 자체도 가볍게 작성하는 것을 목표로 두었다. 우선순위가 가깝지 않은데 구체적인 요구사항은 틀이 돼서 변경할 때 유연성이 떨어졌기 때문이다. 그래서 우선순위에 가까워질수록 명확하게 바꾸었고 기능 목록을 좀 더 유연하게 쓸 수 있게 되었다.

4. 관심사별로 파일 구조를 나눠볼까?

1주차에는 App.js 한 파일에 모든 코드를 작성했는데, 이번에는 한 파일에 작성하려니까 가독성이 떨어졌다.
그래서 GPT의 조언으로 관심사별로 파일구조를 나눠봤다. 이렇게 하니까 비지니스 로직과 다른 로직을 명확히 분리할 수 있어서 가독성이 좋았다.

/src
├── App.js (주요 흐름 제어)
├── models: 데이터와 상태를 관리하는 객체 정의
├ └── Car.js (자동차 객체 정의)
├── services: 비지니스 로직을 처리하고 애플리케이션의 흐름을 제어
├ └── RacingGameService.js (게임 로직 처리 - 자동차 이동, 우승자 계산)
├── utils: 재사용 가능한 작은 기능들을 모아 놓은 도구 모듈
├ ├── InputUtils.js (입력 처리 및 검증)
├ └── OutputUtils.js (출력 처리)
└── constants.js (상수 정의 - 매직 넘버, 에러 메시지 등)

프리코스 디스코드에는 MVC 패턴이 가장 많이 보였다. 하지만 이번 미션에 적합하지 않다고 생각했다. 왜냐하면 화면을 동적으로 업데이트 하거나 상태 변화를 반영할 UI가 없어서 View를 따로 만들 필요 없다고 느꼈고, 사용자 입력은 시작할 때 한 번만 받기 때문에 Controller 또한 나눌 필요가 없다고 느꼈다.
따라서 MVC로 나누면 오히려 불필요하게 복잡해질 것 같았고, 대신 유지보수가 쉬운 구조를 선택했다.
다음 주에 미션에 따라서 MVC를 적용해 볼 수도 있을 것 같다.

🤔 좀 더 고민해 봐야 할 것?

  • 함수를 호출하는 부분을 추가만 해도 feat로 봐야 할까? refactor로 볼 수 있을까?
  • 매개변수 순서, 그리고 함수 구현 부의 순서는 어떻게 하는 게 가독성에 좋을까?
  • 모든 문자열과 숫자 값을 상수로 처리하는 게 맞을까? 값이 변경될 수 있으면 상수로 처리하는 게 맞는데, 그냥 뒀을 때 가독성이 높은 게 있다. 그럼 상수는 무조건 constants 파일에 놓는 게 맞을까? 호출 부분과 가까운 곳에 두면 거리 비용이 줄지 않을까?
  • 에러 메시지에 대한 상수를 좀 더 깔끔하게 정리하는 방법이 있을까? 불러오기가 늘어나는 것도 가독성이 떨어지는 것 같다.
  • 테스트 코드도 가독성이 좋게 바꾸면 어떻게 할 수 있을까? describe로 나눠볼까? 파일을 아예 나눠볼까?
  • 테스트 코드를 위한 getter, setter 같은 함수를 짜는 것은 독일까?
  • 다음주에는 디버깅 마인드셋 과정이 하드웨어적인 디버깅으로 이어질 수 있도록 VSC를 이용해볼까?
profile
기술을 위한 기술이 되지 않도록!

0개의 댓글