[레벨1 - 미션2] 로또 인상 깊었던 피드백

Nine·2022년 3월 6일
1

💪 HTML

modal은 html 어디에 들어가 있어야할까요?

modal은 보통 body의 맨 마지막에 둡니다.

z-index를 굳이 설정하지 않아도 자연히 맨 위로 올라갈 수 있기 때문이지요.


💪 CSS

CSS variable

사용자 지정 CSS 속성 사용하기 (변수) - CSS: Cascading Style Sheets | MDN (mozilla.org)

inline으로 color를 지정해주기보다 variable로 만드는건 어떨까요?

:root {
  --button-color: #00bcd4;
}
background: var(--button-color);

CSS 파일도 분리해볼까요?

  • 각 파일에 맞게 이름을 설정하고 index.css에서 import해볼까요?
@import url('./base.css');
@import url('./contents.css');
@import url('./toggle.css');
@import url('./modal.css');

transition을 통해 간단한 애니메이션을 주는건 어떨까요?🎢

.modal {
	...
	transition: opacity 0.2s linear;
}

.lotto-app-button:hover {
  opacity: 0.7;
}

CSS 네이밍

css 네이밍도 중요해요. BEM(Block, Element, Modifier) 세 가지 요소로 네이밍을 지읍시다!

css naming convention - BEM (velog.io)

https://nykim.work/15

.header__navigation--navi-text {
  color: red;
}

위 코드에서 header는 Blocknaviagtion은 Elementnavi-text는 Modifier가 됩니다.


px, em, rem 알고 쓰시나요?

https://studyingazae.tistory.com/194

  • px : 반응형 웹 페이지에서 사용을 비추합니다.

  • em : 상위 요소의 폰트 사이즈를 상속 받습니다.

    • 픽셀보다는 반응형 웹페이지에 잘 맞습니다만 depth가 깊어질수록 관리가 어렵습니다.
html{ font-size : 10px; } 
div { font-size : 2em; /* 10px의 2배 */ } 
li { font-size : 2em; /* 10px의 2배의 2배 */ }
 
<html> 폰트 10 
	<div> 폰트 20 
		<li>폰트 40</li> 
	</div> 
</html>
  • rem : 최상위 엘리먼트를 기준으로 폰트 사이즈를 상속 받습니다.
html{ font-size : 10px; } 
div { font-size : 2rem; /* 10px의 2배 */ } 
li { font-size : 3rem; /* 10px의 3배 */ } 

<html> 폰트 10 
	<div> 폰트 20 
		<li>폰트 30</li> 
	</div> 
</html>

reset.css

각 브라우저마다 다를 수 있는 부분들을 위해 reset을 해보는 건 어떨까요?

제 기억으로는 CDN으로 연결하는 부분들도 있었던 것 같아요.

html,
body,
div,
span,
h1,
h2,
h3,
h4,
h5,
h6,
p,
a,
ol,
ul,
li,
form,
label,
table,
footer,
header,
nav,
button section {
  font-size: 100%;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

💪 JS

HTML에서 반복적으로 생성해야하는 요소들!

반복🔁을 줄이기 위해 javascript에서 map을 사용해서 그려주면 좋지 않을까요? (template.js를 만들어 봅시다.)

  • 예를 들어볼까요?
<form id="user-lotto-form">
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-lotto-number" type="number" min="1" max="45" />
	<input class="user-bonus-number" type="number" min="1" max="45" />
	<button id="user-lotto-result-button" type="submit">결과 확인하기</button>
</form>
  • 위 대신에 아래처럼 한다든지..
new Array(6).map(()=>{
	...insertAdjacentHTML('afterbegin',`<input class="user-lotto-number" type="number" min="1" max="45" />`)
})
  • javascript라는 언어 자체가 DOM을 조작하기 위해 사용하는 언어인데 조금 더 적극적으로 사용해도 좋을 것 같다는 피드백입니다.

Action, Reducer 사용한 로직

오호! 시도 자체가 너무 대단하다고 개인적으로 생각합니다.


private와 public 메서드의 순서를 정리합시다.

깔끔하게 모아두면 보기가 더 좋을 것 같아요.


DRY 원칙

DRY 원칙, 즉 “반복하지 마라(Don’t Repeat Yourself)”라는 말 역시 좋은 소프트웨어 개발 습관의 근원이라 할 만하죠?

“모든 지식 조각은 딱 한 번만 나와야 한다 ”라는 말이기도 합니다.


Math.floor vs parseInt

[Javascript] parseInt()과 Math.floor()의 차이 (velog.io)

양수일 경우는 대부분 비슷하게 동작해요. 하지만... 아래와 같은 경우도 있다는 사실!

Math.floor( "-12.34" ); // -13
parseInt( "-12.34" ); // -12

Math.floor( "12  34  56" ); // NaN
parseInt( "12  34  56" ); // 12

e.target은 너무 못 생겼어.

  • e.target 을 변수로 하면 더 좋을 것 같아요.

customEventHandler

커스텀 이벤트 핸들러를 작성할 수도 있을 것 같아요. 인자가 많아지면 파라미터로 객체를 활용해볼 수 있을 것 같네요.

function registerEvent(target, eventType, callback) {
	//...
}

registerEvent(this.winningNumberForm, 'submit', e =>
      this.handleWinningNumberFormSubmit(e),
);

validator 좀 다른 방법 보실까요?

이런 방법도 있구나.. 놀랐습니다😲😲

const purchasedMoneyValidator = [
  { test: isZero, errorMsg: ERROR_MESSAGE.ZERO_PURCHASE_MONEY },
  { test: isNotNumber, errorMsg: ERROR_MESSAGE.INVALID_PURCHASE_MONEY_TYPE },
  ...
]
const validatePurchaseMoney = value => purchasedMoneyValidator.every(({ test, errorMsg }) => {
  if (test(value)) throw new Error(errorMsg)
  return true
})

DOM register는 init할 때 한 번에!

init(초기화)는 constructor에서 수행하는 것이 맞습니다.


💪 사용성에 대한 고민

  1. 사용자가 무엇을 해야할지 직관적으로 바로 알 수 없다면 개발자의 잘못이라고 생각합니다
  1. 직관적으로 알 수 있는 UI가 먼저이고 그럴 수 없는 경우라면 어떻게 해야할지 구체적이고 상세하게 안내하자!

  2. 악의적인 초등학생이 깨트리려고 해도 절대 깨지지 않는 앱을 만들자!

🎯 그 목표를 위해 에러를 더 많이 발생시키고 그에 대한 예외처리를 상세히 기술해서 사용성이 깨지지 않도록 노력합시다!


💪 Test

Test에서 은닉화를 지키고 있나요?

const controller = new LottoController();
controller.winningLottos = [4,15,25,36,41,27,33];

controller.winningLottos로 Class 내의 변수에 바로 접근(get)해서 수정(set)할 수 있다는 말은 은닉화가 이루어지고 있지 않다는 거죠!


테스트 기본에 충실합시다.

이 테스트를 통과한다면 어지간해선 잘 동작할 것이라고 안심해도 될까?

이 질문을 계속 던져보세요.


test하려는 메서드가 private이네요?

원래부터 private method는 테스트할 수 없도록 하는게 객체지향에서의 본래 취지입니다.

"객체 내부 로직에 대해서는 궁금하지 않고 오직 밖으로 드러난 것만 잘 보이면 된다"는 차원이거든요.

'단위테스트'라고 할 때의 '단위'가 함수 내지 메소드단위일 경우도 있지만, '객체'를 하나의 단위로 인식하는게 일반적입니다.

이 객체(인스턴스)에 대한 테스트는 오직 public method를 통해서만 이뤄지도록 하면 되는 것이에요. public method만으로 기존 테스트를 정상적으로 수행할 수 없다면, 테스트 케이스를 수정하셔야 합니다.

profile
함께 웃어야 행복한 개발자 장호영입니다😃

0개의 댓글