📖 우테코 7기 프리코스 2주차 회고
📌 프리코스 2주차를 통해서 배웠던 점
프리코스를 진행하면서 초기에 설정했던 목표를 달성하고, 더 나아가 예상했던 것보다 많은 성장을 이룰 수 있었습니다. 1주차 과제를 통하여 많은 부족한 부분들을 채우고 제 스스로 더 훌륭한 프론트엔드 개발자로 성장할 수 있었던 큰 경험이 된 계기가 되었습니다.
1주차 'Calculator' 과제에서는 MVC 패턴에 대한 이해가 부족해 View, Model, Controller로 각 역할에 맞춰서 나누지 않고, 각 역할대로 쪼개려고 노력을 했음에도 불구하고, 하나의 클래스에 여러 역할을 처리를 한 부분이 있었습니다. 하지만 1주차 과제의 PR에 대한 리뷰를 통하여 다른 참가자들의 코드 리뷰와 피드백을 통해 각 컴포넌트의 역할과 책임에 대해 깊이 있게 고민하게 되었고, 이를 2주차 '자동차 경주' 게임에서 배운 점과 깨달은 점들을 적극 반영했습니다.
구체적으로, Model은 자동차의 상태와 게임 진행을 담당하고, Controller는 사용자 입력 검증과 게임 진행 흐름 제어, 데이터 가공을, View는 순수하게 입출력만 담당하도록 분리했습니다. 특히 우승자 배열을 쉼표로 구분된 문자열로 변환하는 로직을 OutputView에서 Controller로 이동시키면서, "출력을 위한 데이터 가공은 Controller의 책임이고, View는 단순 출력만 담당한다"는 원칙을 명확히 이해하게 되었습니다.
이러한 구조 개선을 통해 코드의 유지보수성과 테스트 용이성이 크게 향상되었고, 각 컴포넌트의 역할이 명확해져 코드를 이해하기도 더 쉬워졌습니다.
기술적인 면에서는 JavaScript에 대한 이해도가 크게 향상되었습니다. 특히 클래스를 활용한 객체지향 프로그래밍과 MVC 패턴을 실제로 적용하면서, private 필드를 활용한 캡슐화, 메서드 체이닝, 콜백 함수 등 JavaScript의 다양한 기능을 효과적으로 활용하는 방법을 배웠습니다. 자동차 경주 게임에서 Game 클래스의 play 메서드에 콜백 함수를 적용하여 진행 상황을 출력하는 부분을 구현하면서 콜백 함수에 대해서도 깊게 이해할 수 있었던 계기가 되었습니다.
테스트 주도 개발의 가치도 깊이 이해하게 되었습니다. Jest를 활용한 테스트 코드 작성을 통해 코드의 신뢰성을 높이고, 예상치 못한 버그를 사전에 방지할 수 있었습니다. 2주차에 새롭게 적용한 'test.each'를 사용한 'Table-driven testing'을 구현하면서 테스트 코드의 재사용성과 가독성을 높일 수 있었고, 이를 통해 다양한 Edgy case들를 효과적으로 검증할 수 있었습니다.
1주차와 비교하여 가장 큰 변화는 코드 설계 능력의 향상입니다. 단일 책임 원칙을 철저히 적용하여 클래스와 메서드를 더 작은 단위로 분리하였고, 의존성 주입을 통해 결합도를 낮추었습니다. 예를 들어, 'Calculator' 게임에서 처음에는 하나의 클래스에 여러 로직을 넣었지만, 2주차에서는 testUtils, validator 등으로 책임을 분리하면서 코드의 유지보수성과 재사용성을 크게 향상시킬 수 있었습니다.
또한 Airbnb JavaScript Style Guide와 AngularJS commit convention 등 실무에서 사용되는 다양한 컨벤션을 학습하고 적용하면서, 협업과 코드 품질 관리의 중요성을 깊이 이해하게 되었습니다. 특히 "feat", "refactor", "test" 등의 명확한 커밋 컨벤션을 통해 코드의 변경 이력을 효과적으로 관리할 수 있다는 것을 배웠습니다.
프리코스를 통해 가장 크게 깨달은 점은 "좋은 코드"란 단순히 동작하는 코드가 아니라, 읽기 쉽고, 유지보수가 용이하며, 확장 가능한 코드라는 것입니다.
🔧 새롭게 배웠거나 보충한 개념들!
static의 기본 개념
static은 '정적'이라는 의미로, 클래스의 인스턴스가 아닌 클래스 자체에 속하는 멤버를 정의할 때 사용
프로그램이 시작될 때 메모리에 한 번 할당되어 프로그램이 종료될 때까지 유지
주요 특징:
메모리 관리
static 멤버는 모든 인스턴스가 공유
인스턴스를 생성하지 않아도 접근 가능
메모리에 한 번만 할당됨
접근 방법
static 멤버: 클래스명.멤버로 접근
일반 멤버: 인스턴스.멤버로 접근
static을 사용할 경우
class OutputView {
static print(message) {
console.log(message);
}
}
// 사용 방법
OutputView.print('안녕하세요'); // 클래스의 인스턴스를 생성하지 않고 바로 사용 가능
static을 사용하지 않는 경우
class OutputView {
print(message) {
console.log(message);
}
}
// 사용 방법
const outputView = new OutputView(); // 인스턴스 생성이 필요
outputView.print('안녕하세요'); // 인스턴스를 통해 메서드 호출
주요 차이점:
인스턴스 생성 여부
static: 클래스의 인스턴스를 생성하지 않고도 메서드를 사용할 수 있음
non-static: 반드시 new 키워드로 클래스의 인스턴스를 먼저 생성해야 함
메모리 사용
static: 클래스 레벨에서 한 번만 메모리에 할당됨
non-static: 인스턴스가 생성될 때마다 메모리에 새로 할당됨
용도
static: 유틸리티 함수처럼 인스턴스의 상태와 관계없이 동작하는 기능에 적합
non-static: 인스턴스의 상태에 따라 다르게 동작해야 하는 기능에 적합
예제의 OutputView 같은 경우는 단순히 메시지를 출력하는 기능이므로, 인스턴스의 상태와 관계없이 동작한다. 따라서 static으로 선언하는 것이 더 적절할 수 있다.
MAGIC number 대신 상수로 변경!
매개변수명을 더 명료하게 나타내기 위하여 상수로 변경하기!
// MAGIC NUMBER 대신 상수로 변경
const START = 1;
const END = 45;
const NOT_DUPLICATED_NUMBER = 6;
class generateNumber {
createRandomNumbers() {
return Random.pickUniqueNumbersInRange(START, END, NOT_DUPLICATED_NUMBER);
}
}
export default generateNumber;
JSdoc 주석
JSDoc는 JavaScript 코드에 주석을 달아 코드에 대한 설명을 제공하고, 함수, 변수, 객체 등에 대한 문서화를 돕는 도구입니다. 특히 협업 시 코드의 가독성을 높이고, 코드의 의도를 쉽게 이해할 수 있도록 하는 역할을 합니다.
JSDoc 주석의 기본 구조는 /**로 시작하며, 보통 다음과 같은 정보를 포함할 수 있습니다:
- 함수 설명: 함수가 수행하는 기능을 설명합니다.
- 매개변수 설명 (@param): 함수의 매개변수를 정의하고, 매개변수의 타입과 설명을 작성합니다.
- 반환값 설명 (@returns): 함수의 반환값이 무엇인지 설명하고, 반환값의 타입을 정의합니다.
- 타입 정의 (@type): 변수나 객체의 타입을 지정할 수 있습니다.
- 기타 태그: @example(사용 예시), @deprecated(비추천), @async(비동기 함수 표시) 등 다양한 태그로 추가 정보를 제공할 수 있습니다.
/**
* 두 숫자를 더하는 함수
* @param {number} a - 첫 번째 숫자
* @param {number} b - 두 번째 숫자
* @returns {number} 두 숫자의 합
* @example
* // 반환값: 3
* add(1, 2);
*/
function add(a, b) {
return a + b;
}
JSDoc을 통해 코드의 유지보수성과 가독성을 높일 수 있어 많은 프로젝트에서 활용됩니다.
JSDoc은 문서화와 코드 설명에만 초점이 맞춰져 있으며, 코드의 동작에는 영향을 주지 않습니다. JSDoc은 주석 형식으로 작성되기 때문에 JavaScript의 실제 실행에는 개입하지 않고, 다음과 같은 편리함을 제공하는 역할을 합니다.
문서 자동 생성: JSDoc 주석을 기반으로 HTML 문서나 Markdown 형식의 API 문서를 자동 생성할 수 있습니다. 이를 통해 별도의 설명 문서를 작성할 필요 없이 코드와 문서가 항상 일치하게 유지됩니다.
코드 편집기 지원: 많은 IDE(Visual Studio Code, WebStorm 등)에서 JSDoc을 인식하고 코드 자동 완성 기능을 강화해줍니다. 함수 설명, 매개변수 타입, 반환 타입 등을 자동으로 표시하여 개발자가 실수를 줄이고 이해하기 쉽게 해줍니다.
타입 검사: TypeScript를 사용하지 않아도 JSDoc을 통해 어느 정도 타입 정보를 제공할 수 있습니다. IDE나 일부 빌드 도구에서 JSDoc 타입 정보를 기반으로 기본적인 타입 오류를 감지할 수 있지만, 완전한 타입 검사는 아니기 때문에 TypeScript와 같은 정적 타입 검사만큼 강력하지는 않습니다.
즉, JSDoc은 개발자가 코드를 이해하고 유지보수하는 데 유용한 정보를 제공하지만, 코드의 로직이나 실행에는 영향을 주지 않는 설명용 도구입니다.
클래스형 컴포넌트 vs 함수형 컴포넌트
리액트 컴포넌트의 종류에는 클래스형 컴포넌트와 함수형 컴포넌트가 있습니다.
클래스형 컴포넌트는 ES6의 클래스 문법을 사용하며, 상태와 생명주기 메서드를 가집니다.
따라서 클래스형 컴포넌트는 생명주기와 관련된 복잡한 로직을 구현할 때 장점이 있지만, 자칫 코드가 길어져 가독성이 떨어지고 재사용성이 낮아질 수 있습니다.
클래스 기반 컴포넌트
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={() => this.increment()}>Increment</button>
</div>
);
}
}
반면, 함수형 컴포넌트는 간단한 함수로 정의됩니다.
기존의 자바스크립트 함수 표현식으로 쓸 수 있고, ES6 문법인 화살표 함수를 사용해서 정의할 수도 있습니다.
함수형 컴포넌트 자체로는 상태와 생명주기 메서드를 사용할 수 없지만, 리액트 훅을 통해 상태나 생명주기와 관련된 기능을 사용할 수 있습니다.
함수형 컴포넌트
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default Counter;
두 코드는 동일한 기능을 하지만, 작성 방식이 다릅니다.
함수형 컴포넌트가 더 현대적인 React 방식이며, Hooks를 사용하여 더 간결하고 읽기 쉬운 코드를 작성할 수 있습니다.
함수형 컴포넌트는 클래스형 컴포넌트에 비해 코드가 간결하고, 테스트와 디버깅이 용이하다는 장점이 있습니다.
이러한 이유로 현재 리액트 공식 문서에서는 클래스 컴포넌트 대신 함수형 컴포넌트를 사용할 것을 권장하고 있습니다.