[우테코] level-1 / mission2 회고
![[우테코] level-1 / mission2 회고](/assets/images/thumbnail/woowa-lotto.png)
요약 우테코 1단계 2주차 회고 글입니다. 미션을 진행하며 배운 점과 기억하고자 하는 점들을 담았습니다.
목차
미션 2단계
TDD 사용해서 코드 작성하기 후기
-
우선 1주차때보다 미션 구현 난이도가 높았음에도 불구하고 TDD 방식을 사용하니 머리를 조금 덜 써도 되었다,,? 라는 생각이 들었다. 다시말해, 구현에 대한 부담감이 조금 낮아졌다고 하는게 맞는 말인 것 같다. 큰 틀을 계속 생각하면서 구현하지 않아도 당장에 완성시킬 함수에만 신경을 쓸 수 있어서 상대적으로 부담이 덜 되었던 것 같아 좋았다.
-
랜덤 함수와 같이 제어 불가능한 값을 처리하는 방법에 대해 미리 생각할 수 있어 좋았다. 이번 미션의 경우 제어 불가능한 값이 들어오게 되면 해당 값을 mock를 사용하지 않고 test를 작성가능한 순서까지만 작성 하는 것을 목표로 잡았다. 그렇다보니 처음 생각은 Lotto 내에서 랜덤 값을 생성하는게 좋을 것 같다는 생각을 가지고 있었다. 다만, 그렇게 되면 Lotto 객체를 사용하는 모든 함수에서 test가 불가능하다는 것을 미리 알고 나니 Lotto의 생성자로 랜덤 숫자를 전달 받는 형식이 더 좋을 것 같다는 결론을 내릴 수 있었다.
-
단점으로는 처음에 작성한 함수가 파라미터 형식이 변경된다던가, private, public 형식이 변환된다던가 하면 test 코드부터 수정해서 다시 동작하도록 변경해야 하는 점이 조금 번거로웠던 것 같다. 원래는 기존 함수에 따라 test코드를 작성했기 때문에 변경점이 그렇게 크질 않았는데 함수를 수정하게 되면 test코드부터 전부 수정하게 된다는 점이 컸던 것 같다.
공부내용
📌 Q2 ) HTML의 <form>
태그를 꼭 사용해야 할까
📌 Q5) type="text"
와 type="number"
의 차이
📌 Q6) css 속성이 중복되었을 때 뭐가 우선이 될까
📌 Q1 ) 명령형보다 선언형 프로그래밍을 지향해라?
명령형 프로그래밍은 어떤 일을 하는 방법이고 선언형 프로그래밍은 무엇을 하는가에 더 가깝다. 어느 한 예시 를 들어보자면, 우리가 식당에 도착했을 때 프론트 직원에게 말하는 스타일로 볼 수 있다.
- 명령형의 경우엔, “저기 오른쪽 테이블에 자리가 방금 빈 것을 확인했습니다. 저와 제 친구는 그 자리로 가서 앉을께요” 라고 말하는 것과 같다.
- 이와 반대로, 선언형의 경우엔, “2자리 주세요”라고 말하는 것으로 이해할 수 있다.
즉, 명령형 프로그래밍은 실제로 어떻게 자리를 얻을 것인지와 연관이 있는 것이고, 테이블을 얻는 방법을 보여주기 위해 단계를 나열해야 한다는 점에 초점을 둘 수 있다. 이에 반해, 선언형 프로그래밍은 두 사람을 위한 테이블. 즉 무엇을 원하는지에 대해 더 관심히 있는 것이라고 이해할 수 있다.
이를 코드에 대입해 본다면 다음과 같은 예시를 들 수 있다.
명령형 프로그래밍
1
2
3
4
5
6
7
function double(arr) {
let results = [];
for (let i = 0; i < arr.length; i++) {
results.push(arr[i] * 2);
}
return results;
}
- 위의 코드에서 알 수 있는 점은 우선 무엇인가를 하는 방법을 설명하고 있다는 점이다. 배열을 명시적으로 반복하거나 원하는 기능을 구현하는 방법에 대한 단계를 명시적으로 배치하고 있다는 것을 알 수 있다.
선언형 프로그래밍
1
2
3
function double(arr) {
return arr.map((item) => item * 2);
}
- 위의 코드에서 알 수 있는 점은 우리는 위의 동작이 어떻게 흘러가는지는 알 수 없다. map이나 reduce가 어떻게 구현되어 있는지도 모르고, 신경 쓸 필요도 없다는 의미이다. 다만 위의 코드를 통해 무엇이 일어나길 원하는지를 작성했다고 볼 수 있다는 것이다.
실제로, 이번 PR 리뷰를 통해 명령형 보다 선언형으로 작성하라는 피드백을 받았다.
✔️ A1 ) 여기서 명령형보다 선언형으로 작성하라
라는 말의 의미는 그 “구현 내용을 명시적으로 지정하기 보다 어떤 기능을 수행하 것인지 의도가 드러나도록 사용하라” 라는 의미로 이해를 했다. 그리고 이를 효과적으로 잘 나타낼 수 있는 방법이 배열의 내장 메서드를 이용하는게 좋다는 의미인 것 같다.
📌 Q2 ) HTML의 <form>
태그를 꼭 사용해야 할까
input의 값을 전송해야 할 때 <form>
태그를 사용하기보다 addEventListener
를 사용해서 값을 전달했었다. 리뷰어님께선 폼의 값을 제출해야 하는 경우 <form>
태그를 사용하시는 것을 권장해주셔서 해당 내용에 대해 간단히 정리해보았다.
✔️ A2 )form 태그를 사용해야 하는 이유
form
태그 자체에서 제공해주는 유용한 기능들이 존재한다.
첫번째로는 엔터 키 자동 제출 기능이 있다.form
안에 <input>
이 존재하고, submit 타입을 가지는 <button>
이 존재할 때 사용자가 Enter를 누르면 자동으로 제출된다.
HTML5 기본 유효성 검사도 지원해준다.form
태그 안에 required
속성 혹은 type="email"
속성을 준다면 JS 없이도 브라우저가 제출 시 유효성 검사를 자동으로 해준다.
브라우저가 form
내의 입력 데이터를 기억하고 자동 완성을 제공해주며, submit
, reset
과 같은 이벤트를 통해 로직을 쉽게 제어할 수 있다. reset
의 경우 input창을 포함한 입력 필드의 값을 초기화 해준다
📌 Q3 ) class 필드의 변수가 너무 많을땐?
✔️ A3 ) 클래스를 초기화 할때 반드시 넣어야 하는 데이터인지 확인해보자.
class 내에서 관리가 필요한 인스턴스가 아니라면 필요한 함수를 호출할 때 파라미터로 값을 전달받도록 해야겠다.
📌 Q4) inset
속성으로 css 관리를 간단히!
position : absolute
의 경우에 top, right, bottom, left의 속성을 하나하나 주는 방식을 사용하고 있었다.
1
2
3
top: 0;
left: 0;
...
✔️ A3 ) 이렇게 일일이 속성을 지시하는 것 외에도 inset
을 사용해 간단히 나타낼 수 있다.
1
inset: 0px 0px 0px 0px;
📌 Q5) type="text"
와 type="number"
의 차이
이번 미션을 구현할 때 input의 길이를 제한하고 싶어서 숫자를 받아야 함에도 불구하고 아래와 같이 text 타입으로 maxlength 속성을 주었다.
1
<input maxlength="2" />
type="text"
와 type="number"
을 사용했을 때의 차이점에 대해 간략히 정리해보자면 아래와 같다.
✏️ 일반 사용자의 경험
유형 | type="text" (string) |
type="number" |
---|---|---|
입력 방식 | 모든 문자 입력 가능 (숫자, 문자, 특수문자) | 숫자만 입력 가능 |
모바일 키보드 | 기본 키보드 표시 | 숫자 키패드가 표시됨 (모바일 UX 향상) |
잘못된 입력 | 문자 입력 가능 (예: abc , 123abc ) |
일부 브라우저에서 문자 입력 차단 (예: 123abc 불가) |
✏️ 스크린 리더 사용자의 경험
유형 | type="text" (string) |
type="number" |
---|---|---|
스크린 리더 읽기 방식 | 일반적인 텍스트 입력 필드로 안내 | “숫자 입력 필드”로 안내 |
입력 제약 설명 | 없음 (추가 설명 필요) | “숫자를 입력하세요”라고 기본적으로 안내됨 |
✔️ A3 ) 스크린 리더의 입장이나 모바일 환경에서의 유연성에 대해서는 생각을 하지 못한 부분인 것 같다. 물론 input의 글자 제한을 둘 수 있다는 장점이 있지만 조금 더 유연하게 값을 받아올 수 있는건 type="number"
라고 생각이 된다.
📌 Q6) css 속성이 중복되었을 때 뭐가 우선이 될까
내가 적용하고 싶은 속성이 이미 선언되어 있는 속성과 중복되어 적용되지 않는 현상을 겪게 되었다. 기존에 선언되어 있는 css 속성은 .title
이라는 클래스 이름으로 사용이 되어 있었고 나중에 추가된 ccs 속성은 header h1
이라는 선택자로 제공하게 되었다. 결과는 .title
선택자가 우선이 되어 나중에 추가한 headr h1
속성이 적용되지 않았는데 그 이유는 CSS 선택자 특이도때문에 벌어진 일이다.
특이도는 선택자의 강도를 수치화한 개념으로 ID(0,1,0,0) > 클래스(0,0,1,0) > 태그 (0,0,0,1) 순으로 우선순위가 높다. !important` 속성은 특이도와 관계없이 항상 최우선 적용된다.
따라서 위의 .title
과 header h1
의 특이도를 계산해본다면 .title
은 (0,0,1,0) - [ class 선택자 ]가 되며 header h1
의 특이도는 (0,0,0,2) - [ 태그 선택자 2개 ]가 되므로 우선순위가 더 높은 .title
의 속성이 우선적용되게 된다.
이번주 키워드
🔑 K1 ) 잘 모듈화한 코드는 어떤 특성을 가지고 있을까?
🔑 K2 ) 프로토타입 체인, proto 와 prototype
🔑 K5 ) bind(this) vs 화살표 함수의 차이
🔑 K1 ) 잘 모듈화한 코드는 어떤 특성을 가지고 있을까?
묘듈화는 객체를 나누는 것에서 부터 시작된다. 우리는 자율적이고 협력적인 객체를 만들기 위해 고민해야 한다.
객체는 충분히 자율적이여야 한다. 객체들은 공동의 목표를 달성하기 위해 협력하지만, 스스로의 결정과 판단에 따라 행동하는 자율적인 존재다. 객체는 춘분히 협력적이여야 한다.외부와의 소통 없이 모든 것을 스스로 처리하려고 하는 객체는 내부적인 복잡도에 의해 자멸하게 된다.
그렇다면 잘 모듈화된 코드는 어떤 특징을 가지고 있을까?
- 단일 책임 원칙 (SPA)
하나의 모듈은 하나의 역할만 수행해야 한다. 한가지 기능을 변경할 때에는 하나의 클래스만 변경하도록 코드를 작성한다.
- 높은 응집도, 낮은 결합도
모듈 내부의 구성은 서로 강하게 관련되어 있어야 한다. (응집도 ⬆️) 다른 모듈과는 최대한 독립적으로 동작해야 한다. (결합도 ⬇️)
아직 음.. 모듈화를 잘 하기 위한 나만의 지침은 없는 것 같다. 사실 모듈화의 개념도 아직 잘 와닿지가 않아서 앞으로 조금씩 만들어나가야 할 것 같다…
🔑 K2 ) 프로토타입 체인, proto 와 prototype
1
const arr = [];
위의 코드에서 우리는 자연스럽게 arr.push()
매서드를 사용할 수 있다. 그저 배열을 만들기만 했는데도 배열의 내장 메서드를 사용할 수 있는 이유는 Javascript의 프로토타입
시스템 덕분이다.
Javascript에서는 모든 객체가 자신의 부모 역할을 하는 프로토타입 객체를 가리키며, 그 객체가 가진 속성과 메서드를 상속받을 수 있다. 이를 프로토타입 체인이라고 부른다.
1
2
arr.push(1);
console.log(arr.__proto__ === Array.prototype); // true
즉, arr.push(1)
을 호출했을때의 JS 동작은 아래와 같다.
- arr.push() 호출
- arr 자체에
push
메서드 존재하는지 확인 => 없음 arr.__proto__
인Array.prototype
으로 가서push
메서드 탐색 => 발견 & 실행
prototype
은 __proto__
와 비슷해 보여도 전혀 다른 대상을 의미한다. __proto__
는 객체가 가지고 있는 숨은 속성을 의미하며 자신의 프로토타입 객체를 가리킨다. prototype
은 생성자 함수가 가지는 속성으로, 이 함수를 통해 만들어질 객체의 메서드를 담고있다.
따라서
class
문법 없이도prototype
을 이용해서 class를 구현 가능하다.
+) 추가학습
1
2
3
4
5
6
7
8
class Person{
sayHello(){
..
}
}
const p1 = new Person();
p1.sayHello();
위의 코드에서 p1.sayHello()
를 호출하게 되었을 때의 그 과정은
- p1.sayHello() 호출
p1.__proto__
->Person.prototype
에서 sayHello를 찾음- 실행
위의 과정을 따른다.
🔑 K3 ) template
태그, DocumentFragment
template
태그
<template>
태그는 브라우저에 렌더링 되지 않는 HTML 조각을 정의하는 용도로 사용된다. HTML 안에는 존재하지만 화면에 나타나지 않으며 JS 조작을 통해 필요할 때 DOM으로 삽입 가능하다. 간단히 말해 콘텐츠 조각을 나중에 사용하기 위해 담아놓는 컨테이너라고 생각하면 된다.
1
2
3
4
5
<template id="user-template">
<div class="user">
<p class="name"></p>
</div>
</template>
이렇게 html에서 선언되어 있는 구조를
1
2
3
4
5
6
7
8
const users = ["Alice", "Bob", "Charlie"];
const template = document.getElementById("user-template");
users.forEach((name) => {
const clone = template.content.cloneNode(true);
clone.querySelector(".name").textContent = name;
fragment.appendChild(clone);
});
요런식으로 사용가능
DocumentFragment
DocumentFragment
는 DOM에 연결되지 않는 가상 노드 컨테이너역할을 해준다. DocumentFragment
안에 DOM 하위 트리를 조합한 다음 appendChild()
와 같은 메서드로 DOM에 하위 트리를 삽입하는 것이다. 이 방법을 통해 DocumentFragment
안의 노드들이 한번에 DOM으로 이동하므로 여러 노드를 한번에 DOM에 삽입할 때 성능 이점이 있다.
1
2
3
4
5
6
const fragment = document.createDocumentFragment();
const li = document.createElement("li");
fragment.appendChild(li);
document.querySelector("ul").appendChild(fragment);
요런식으로 사용가능
🔑 K4 ) DOM 변경을 했을 때 일어나는 일들
브라우저가 DOM을 변경하게 된다면 단순히 텍스트를 추가하는 것으로 끝나는 것이 아니라 복잡한 과정들을 처리해야한다.
첫번째로 브라우저는 DOM의 tree 구조를 직접 수정해야 한다. 트리 구조가 변경이 되면서 그 안의 요소나 자식 요소들에 대해 CSS 스타일을 새로 계산 해야하며 위치, 크기 등의 박스 모델 속성이 바뀌었다면 DOM 요소의 레이아웃을 재계산해야한다. 그리고 이렇게 변경된 내용들을 화면 픽셀로 다시 그려야 하는 문제가 발생한다.
따라서 DOM 조작이 많을수록 위의 단계가 반복되므로 성능이 저하되는 문제가 발생한다. 따라서 DocumentFragment
를 사용하는 방식을 통해 성능을 향상시키는 방식이 권장된다!
🔑 K5 ) bind(this) vs 화살표 함수의 차이
bind(this) 함수는 호출할 때마다 this가 고정되도록 새로운 함수를 반환한다. 반대로 화살표 함수의 경우는 자신만의 this
를 가지지 않으므로 외부 스코프(정의된 시점)의 this
를 자동으로 챕처해서 사용한다.
그렇다면 this
가 가르키는 값은 어떻게 결정될까? Javascript에서는 this
함수가 정의된 시점이 아니라 실행될 때결정된다. 호출 방식에 따라서도 다르게 동작한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//전역함수
function show() {
console.log(this);
}
show(); // 브라우저: window / Node: global
//객체 내부에서 실행
onst obj = {
name: "hello",
say() {
console.log(this.name);
}
};
obj.say(); // obj 객체의 this 참조
일반 함수의 경우엔 전역 함수에서의 this
는 window
또는 global
이 된다. 객체 내부에서 값을 호출한 경우, 호출한 객체가 this가 된다.
1
2
3
4
5
6
7
const obj = {
name: "hi",
say: () => {
console.log(this.name);
},
};
obj.say(); // undefined (this는 상위 스코프의 this)
화살표 함수는 this
를 바인딩 하지 않으므로 외부 스코프의 this
를 그대로 사용하게 된다.