혼자서 프로젝트를 진행했을때, 기능에 대한 명세서나 순서도 같은걸 만들기보다 무작정 만들어 왔었다.
이는 설계로 시작한 개발보다, 개발을 하면서 설계하는 듯한 느낌이 들었다.
하지만 이번에 우테코에서 기능 목록을 직접 작성해봄으로써 더욱 더 정교한 설계를 해볼 수 있었고, 개발을 하면서도 미리 작성해놓은 기능 목록 덕분에 더욱 깔끔하게 개발할 수 있었다.
먼저 함수형으로 코드를 짜왔던 내가 클래스를 사용해서 과제를 진행해야 한다는 것에서 꽤 깊은 러닝커브가 있었다.
초창기 처음 코딩을 시작했던 언어가 자바였지만, 한달 정도 배웠었고 그마저도 이미 1년이 넘게 지난 후라서 클래스의 개념을 이해하기 상당히 어려웠다.
많은 자료들을 접한 후에 스스로 "클래스는 필요 부품에 관한 내용들을 담고 있는 큰 틀이다." 라고 정의했다.
필요한 큰 틀안에 또 다른 틀끼리 모여서 결국엔 하나의 완성품을 만들어 내는 것이라고 상기하며 만들어보았다.
'하나의 함수는 한가지의 일만 담당 해야한다.' 라는 말을 알고는 있었지만 사실 와닿지 못했고 따르지 못했었다.
조금 부끄러운 이야기이지만, '어차피 나만 보는 코드이고, 나만 이해하면 되지 ! 일단 구현부터 하자' 라는 생각으로 내 자신을 합리화 했었다.
디스코드에서 클린 코드에 관한 글을 보게 되었고, 이 부분에 대해 나도 무작정 따라하는 것이 아니라 스스로 함수는 왜 한가지의 동작만 해야하는가? 를 생각해보았다.
밑의 글은 제가 디스코드에서 직접 작성한 댓글입니다. ⬇️
저는 함수는 한가지의 일만 담당해야한다 라는 문구를 보고 왜 함수는 한가지의 일만 담당해야하는가? 에 대해서 먼저 생각해보았는데요.
그 이유는 '재사용성을 높이기 위해서' 라고 생각합니다.
모듈화 하는것이나, 유틸 함수들을 따로 나누어놓는것도 재사용성을 높이기 위해서라고 생각하는데요.
가령 값을 입력받아 그에 맞는 형변환을 해주어서 값을 리턴 함수가 있다고 가정했을때, 다른 곳에서 형변환에 대한 범위가 방대해서 입력 받은 값만 리턴 받아야 할 부분이 훨씬 많다면, 이 함수를 형변환을 해서 리턴해주기 보다 입력 받은 값만 리턴을 해주는것이 훨씬 함수의 재사용성을 높인다고 생각합니다.
... 중략
좋은 코드는 가독성이 좋은 코드라고 많이들 말씀하시는데, 가독성이 좋다는 것은 곧 읽기 쉬운 코드, 즉 다른 사람이 봤을때 바로 와닿는 코드 라고 생각합니다
이 와닿는 코드의 조건은 함수명만 보더라도 해당 함수의 역할을 알 수 있는거고, 실제로 함수명에 맞게 그 함수가 그 행위를 책임지고 있는거라고 생각합니다.
이렇게 말하고 있지만서도 저도 우테코 시작 전까지 단일 책임의 원리를 지키지 못하고 있었던거 같아서 반성하게 되네요.
정리해서 말씀드려보자면 한가지 일의 구분은 "어느 기준으로 함수를 나누었을때 재사용성이 될 수 있을것인가?" 라고 생각합니다.
그래서 이번에 1주차 과제를 수행하며 함수 분리에 대해 고심해보며 코드를 짜보았고, 실제로 코드 길이가 길어지지만 가독성이 훨씬 좋은 코드를 만들 수 있었다.
앞으로도 이러한 원칙을 지키려고 노력할 것이다.
지금까지 매직 넘버라는 말을 사실 들어보지 못했었고, 에러 메세지들이나 필요한 상수값들은 그 안에서 직접 하드코딩을 했었다.
하지만 이번에 우테코를 진행하며 매직넘버라는 단어를 알게 되었고, 이러한 매직 넘버들을 상수처리 하는 방법을 배웠다.
왜 매직 넘버를 상수로 선언해야하는가 ??
❗️ 상수로 처리함으로써 그 의미를 분명하게 하여 해당 값이 무엇을 의미하는 값인지 분명히 할수 있다❗️ 를 배울 수 있었다.
지금까지 변수명을 작성한다던지, 함수명을 작성한다던지 할때 어떤 컨벤션이 있는지 사실 몰랐고
그저 강의에서 봤던것 그대로 따라서 작성해왔다.
또한 eslint와 prettier 같은 경우, vscode 내에서 이러한 익스텐션들이 필요하다고 해서 무지성으로 설치해서 사용해오고 있었다.
하지만 이번에 우테코를 통해서 자바스크립트 스타일 가이드를 알게 되었고, 이를 적용시키기 위해서 eslint 와 prettier를 사용해볼 수 있었다.
단순한 익스텐션이 아니라 프로젝트내에 직접 eslint와 prettier를 설치해서 사용해보는거라서 당연히 설정부터 상당한 시간이 소요되었다.
하지만 공식문서를 정독해보고, 결국 적용시킴으로써 우테코 가이드에 맞는 airbnb 스타일을 적용시킬 수 있었다.
지금까지 나에게 깃은 단순히 제가 만든 것들을 저장하는 저장소에 불과했다.
하지만 이번에 기존 레포지토리를 포크해오는법, 브랜치를 새로 만들어보기 등 깃의 기능에 대해 체험해 볼 수 있었다.
협업을 해보는 것은 아니지만, 간접적으로 협업의 흐름에 대해서 느껴볼 수 있었다.
또한 단순 저장용도로 지금까지는 커밋 메세지를 다 "msg"로 통합해서 올렸었다.
이번 기회에 커밋 컨벤션이라는 것도 처음 알게 되어서 해당 커밋의 기능에 맞도록, 깔끔하게 커밋 해볼수 있었다.
아직 fix 와 refactor 구분을 못해서 뒤죽박죽 섞인 감이 있지만, 차차 더 컨벤션에 알맞은 커밋을 해보려고 노력해보자.
예외처리를 할때 [ERROR]라는 문자로 시작해야 한다는 제한사항이 있었다.
상수에 아예 각각 값을 넣어주어서 하드코딩 해줄수도 있으나, 커스텀 에러와 에러 확장이라는 글을 보고 직접 예외 처리를 담당하는 클래스를 만들어주었다.
그 안에서 모든 에러메세지가 다르더라도 앞에 우테코의 예외처리 요구사항을 해결할 수 있었다.
class AppError extends Error {
/**
* @type {string}
*/
static PREFIX = '[ERROR]';
/**
* @type {string}
*/
name;
constructor(errorMessage) {
const message = `${AppError.PREFIX} ${errorMessage || '알 수 없는 에러가 발생했어요!'}`;
super(message);
this.name = this.constructor.name;
}
}
export default AppError;
// 다른 클래스에서 import하여 사용
validateOfType() {
if (this.#gongs.some(Number.isNaN)) {
throw new AppError(ERROR_MESSAGES.NOT_A_NUMBER);
}
}
// Opponent 클래스
constructor() {
this.#gongs = Gong.getNewGongs();
}
// Gong 클래스
static getNewGongs() {
const numbers = [];
while (numbers.length < Gong.GONGS_LENGTH) {
const randomNum = MissionUtils.Random.pickNumberInRange(1, 9);
if (!numbers.includes(randomNum)) {
numbers.push(randomNum);
}
}
return numbers;
}
// 입력 받은 값을 공으로 반환해주는 함수
static fromString(gongString) {
const trimmedGongString = gongString.replace(/\s+/g, '');
const gongs = trimmedGongString.split('').map((str) => Number(str));
return new Gong(gongs);
}
위의 fromString 함수는 단순히 입력값을 Gong 클래스의 인스턴스로 반환해주는 함수,
getNewGongs 는 단순한 배열 값을 반환해주는 함수이다.
getNewGongs도 인스턴스를 반환해줄수 있지 않을까? 라는 생각에 인스턴스 값을 반환하도록 해보았지만 실패했다.
아직 해결을 하지는 못했지만 프리코스를 더 진행하면서 고민해보아야할 부분 같다.
안녕하세요 :)
7기 준비중입니다. 잘 보고 갑니다!