우아한 테크코스에 지원하여 1차를 합격하고 프리코스에 시작하게 되었습니다.
3주동안 정말 많은것을 배웠고 각 주차마다 배운것에 대해서 작성해보려고 합니다.
처음 시작해보는 프리코스 였기에 먼저 지켜야될 컨벤션부터 정리하여 보았습니다.
이것들을 지키면서 코딩을 해야하는 조건을 가지고 첫 미션을 진행하였습니다.
먼저 깃과 깃허브를 단순히 코드 저장용말고는 써보지 않았기에 깃 커밋 컨벤션과 branch 나누는법, pull request하는법등등 공부를 하였고 위의 코딩컨벤션을 모두 읽어보았습니다.
1주차미션은 지난 1주차와 같은 숫자 야구 게임 이였습니다.
먼저 README.md에 구현할 사항을 미리 작성하였고 이것을 토대로 구현을 진행하였습니다.
일단 구현자체에 큰어려움은 없었으나 기능단위별로 구현하고 그때마다 커밋을 해야하는부분이 어려웠습니다.
평소에 3,4개의 기능을 구현한다음에야 한꺼번에 커밋을 할때가 많았는데 이번 미션을 하며 신경을 쓰며 커밋 컨벤션에 맞춰 하나의 기능마다 커밋을 하게 되었습니다.
그리고 가장 고민을 많이 했던 부분은 모듈화였습니다. README.md에 나와있는거와 별개로
이번주차의 추가적인 목적은 함수를 모듈로 분리해보기였습니다.
물론 import
, export
에 대해서는 알지만 어떤 기준으로 함수들과 변수들을 나눌지 감이 잡히지 않았습니다.
먼저 pull request한분들을 참고하여 같은 기능을 하는것끼리 묶어서 정리하여 제출을 하였습니다.
이벤트 위임
에 대해서 확실하게 알게 된 계기가 되었습니다.이벤트 위임
을 보고 다시 생각해보게 되었습니다.그리고 이벤트위임의 진짜 목적을 깨닫고 부모태그인 result 태그에 이벤트를 위임하여 해결할수 있었습니다.
원래 알고있던 이벤트 위임은 이렇게 이벤트를 자식요소에게 분산한다 그정도가 다였습니다.
이번에 새로 이벤트 위임에 대해서 알게된것은 추가되는 동적요소에도 바로 이벤트가 부여되어서
따로 그 요소에 접근하여 부여할 필요가 없는것입니다. 하지만 사진에서처럼 모든 자식요소에 이벤트가 부여되기 때문에
eventHandler = (event) => {
if(event.target.id !== "new") return;
//동작 구현
}
이런식으로 그 요소가 아니면 return
을 할수있도록 해야합니다.
또한 함수를 15줄로 제한하면서 구현하는것이 정말 큰 도움이 되었습니다.
평소에 한 함수에 거의 모든기능이 돌아가도록 구현하여 굉장히 길어지는 경우가 많았는데
15줄을 맞추려고 신경쓰며 구현하니 부가적으로 하나의 기능만 하는 함수가 따라오게 되었습니다.
처음에 숫자야구를 구현하고 실행시킬때 Class안의 constructor에서 함수를 바로 실행시켜 객체를 생성하는 순간 그 객체가 실행되게 구현하였습니다.
class BaseballGame {
constructor(){
this.init()
}
init = () => {
console.log("init");
}
}
new BaseballGame(); // 게임이 실행됨, 하지만 둥둥 떠다님..
하지만 누가봐도 어색해보이는 코드였습니다. 객체를 생성하는 생성자인데 만들어놓고는 받을것이 없는 상태인것입니다. 단순히 객체의 실행을 위해서 객체를 생성하는것은 객체지향적으로 맞지않아 보였습니다.
class BaseballGame {
constructor(){
}
init = () => {
console.log("init");
}
}
const game = new BaseballGame();//게임이라는 객체가 받아짐
game.init();//그리고 그 게임을 초기화한다.
그래서 이렇게 game이라는 객체가 만들어진뒤 그 게임을 초기화한다 라는 식의 코드가 좀더 자연스러운 코드라고 생각했습니다.
1주차 미션 코드
들어가시면 완성된 코드를 볼수 있습니다.
이번 2주차 미션도 작년과 동일한 자동차 경주 게임이였습니다.
이번에는 추가적으로 클래스 분리
를 사용하여 구현해보라는 조건이 있었습니다.
이번에도 1주차와 동일하게 모듈화를 하는 과정에서 굉장히 오랜시간을 소비하였습니다.
이번에는 1주차의 함수 분리
와 다르게 어떤 기준으로 클래스
를 구분할지가 고민이였습니다.
README.md에 구분해놓은 크게 4개의 항목으로 나눠보자 라고 생각하였습니다.
먼저 차이름을 입력받는 class, 경주 횟수를 입력받는 class, 게임을 진행하는 class, 마지막으로 승자를 판별하고 그에 상응하는 메시지를 생성하는 class로 쪼개어서 구현하였습니다.
이번 1주차 피드백에서 eslint와 prettier을 사용해도 된다고 받았기도 하고 1주차에서 많은 분들이 eslint와 prettier을 설치하여 사용하였길래 저도 이번기회에 사용을 해보기로 하였습니다.
vscode의 eslint,prettier extension이 있어서 큰 차이가 없을거라고 생각했지만 생각보다 eslint의 기능이 컸습니다. 단순히 잘못된 문법을 사용하는것을 잡아내는것 뿐만아니라 설정해놓은 컨벤션에 맞지않아도 경고가 나오는것으로 컨벤션을 잡아주니 컨벤션을 하나하나 지키면서 짜야되는 부담이 줄어들었습니다.
1주차에서 모든 값, 메시지들을 상수화하여 관리하였는데 메시지들이나 값들은 하나의 객체안에 넣어서 관리하였습니다. 그런데 2주차에서도 똑같이 진행하던 도중 객체내부에 있는 요소들이 변경이 가능한데 그러면 진정한 상수화가 아니지 않나라고 생각하엿습니다.
그래서 내부까지 불변하게 만들어주는 Object.freeze
를 배워 객체를 완전한 상수로 관리를 하였습니다.
구현하면서 커밋, 혹은 git add ., 혹은 git push등을 하면서 아직 안고친 요소가 있는데 이미 해버렸을 경우 되돌리는 법을 배웠습니다. git reset 키워드를 사용하여 아직 add하기전으로 되돌릴수가 있습니다. 또한 이미 푸쉬까지 해버린 커밋메시지를 변경하고 싶을때가 있었습니다. 그럴때는 git rebase -i HEAD~(돌아갈 커밋수)를 하여 그 커밋에 접근하여 메시지를 수정이 가능하다는것을 배웠습니다.
제가 Class를 나누려고했던 기준은 상태였습니다. 객체의 기본적인 특성은 상태를 가지고 그 상태를 변경시키는 함수가 들어있는것이라고 생각합니다. 그래서 이번 미션을 할때 자동차의 상태를 관리하는 CarsHandler
, 경주횟수를 관리하는 RacingCountHandler
, 승자를 관리하는 Winner
, 게임을 실행하는 Game
클래스로 나누었습니다. 전체적으로 Game
클래스를 제외하고는 각각의 상태를 가지고 event로 인하여 그 상태를 변경시키는 식으로 진행하였습니다.
class 구조
이번 3주차 미션은 자판기 미션이였습니다. 1, 2주차와는 다르게 완전히 처음보는 미션이였기에 문서를 읽고 이해하는데도 꽤 오랜시간이 걸린 미션이였습니다. 그리고 학교 기말고사 기간과 겹쳐서 선택과 집중이 필요했습니다. 물론 쿨하게 학교 기말을 버리고 우테코를 선택했습니다😅
1주차는 함수분리, 2주차는 함수 + 클래스 분리, 그리고 이번 3주차에는 여러개의 클래스를 분리한후 서로 관계를 맺어 하나의 프로그램을 완성하는 경험을 하는것이 추가로 주어지는 목표였습니다.
극단적인 조건 추가
이번 미션에는 추가적으로 2주차보다 좀더 극단적인 연습을 해보라는 조언을 받았습니다.
이번에 3주차 미션을 하며 좀더 극단적으로 한부분은함수의 길이
를15자
, 최대indent
는2
라는 기본 요구사항을 좀더 엄격하게 설정하여함수의 길이
를10자
로 그리고 최대indent
는1
로 제한하여 이번 미션을 구현하였습니다. 그리고 가능하다면 함수의 길이는5자
이하까지 줄여보려 노력했습니다.
메일에서 고통스러울수록 그만큼 성장할거라고 하였는데 그 말대로 이번 미션에서는 어려운 요구사항 + 스스로의 제한으로 인하여 고통스러웠지만 그만큼 많이 배운 주차가 되었습니다.
1, 2주차는 공통적으로 모듈화에서 큰 어려움을 겪었지만 3주차는 순수한 구현난이도의 어려움이였습니다. 잔돈 반환하는 알고리즘, 동전을 랜덤생성하는 알고리즘, 탭을 누를때마다 기존의 상태를 유지한 상태로 변경되는 페이지등등 구현해야하는 것자체가 굉장히 난이도가 높았습니다.
MVC패턴을 이용하여 크게 coin을 관리하는 coinStorage
, 상품을 관리하는 product
, 구매페이지를 관리하는 purchase
로 하여 페이지별로 크게 3개로 나눠서 구현을 진행하였는데 coinStorage
와 product
는 어렵지 않게 구현할수 있었지만 purchase
에서 꽤 어려움을 겪었습니다. 반환하기 기능을 purchase
안에 넣으려니 가지고 있는 코인정보가 없고 구매기능을 넣으려 해도 상품정보가 없어서 처음에 product
와 coinStorage
를 import해서 새로운 객체를 생성해서 시도했으나 새로운 객체를 만들어버려 기존정보가 남아있지 않아 완전히 다시 구현하였습니다.
가장 바깥쪽의 컴포넌트인 VendingMachine
이라는 앱에 coinStorage
와 product
객체를 만들어 넣고 그 만든 객체들을 purchase
의 인자로 전달하여 purchase
안에서 coinStorage
와 product
를 접근하여 구현할수 있엇습니다.
위에서 말했듯이 어려운만큼 가장 많은것을 배울수 있는 주차였습니다.
Model
은 우리가 흔히 알고있는 객체의 특성 그자체로 이해하였습니다.//model
export default class Model {
constructor(){
this.number = 0;//상태
}
getNumber(){
return this.number;
}
//상태 변경 함수
increase(){
this.number += 1;
}
decrease(){
this.number -= 1;
}
}
이런 식으로 상태를 가지고 그 상태들을 변경시키는 함수들로만 이루어진것을 Model로 구성하였습니다.
다음은 View
입니다.
export default class View{
render(number){
const $app = document.getElementById("app");
$app.innerHTML = `현재숫자는 ${number}`
}
}
이런식으로 Model을 알아서는 안되고 인자로 그 상태를 전달받아 화면단에 innerHTML, insertAdjacentHTML, append, innerText등의 함수로 보여지게 하는 기능등을 View로 구성하였습니다.
마지막으로 서로 알아서는 안되는 Model
과 View
를 중간에서 관리해주는 Controller
입니다.
class Controller{
constructor(){
this.model = new Model();
this.view = new View();
this.$plusButton = document.getElementById("plus-button");
}
setEvent(){
$button.addEventListener("click", this.setClickButtonEvent.bind(this));
}
setClickPlusButtonEvent(){
this.model.increase();//모델의 number 상태를 1증가 시킴
this.updateComponent();
}
updateComponent(){
const number = this.model.getNumber();//모델에서 상태를 받아옴
this.view.render(number);//받아온 상태를 view의 인자로 전달
}
}
Controller
는 주로 이벤트 핸들러
를 통하여 Model
과 View
를 관리하는 함수들이 사용됩니다. 이런식으로 구성하면 model과 view는 서로 모르는상태로 controller에 의해서 2개를 관계를 만들수 있습니다.
MVC 패턴을 이해하고 나서는 1, 2주차에서 가장 어려웠던 부분인 모듈화에 대해서 정답을 찾은 듯한 느낌이였습니다. 덕분에 이번 미션을 구현할때는 어떤 기준으로 나누지라는 고민이 없어 오히려 개발시간이 단축되었습니다.
이번 미션의 MVC 구조
이게 저의 원래 예외 처리 방식이였습니다. createCarNameAlertMessage
라는 함수에서 예외에 걸린다면 Message를 반환하고 아니라면 빈문자열을 반환하여 구현했었습니다.
1, 2주차를 모두 이런식으로 구현하였는데 코드의 어색함이 있었습니다. 메시지를 따로 만들어서 메시지의 여부에 따라 예외를 처리하는것이 마음에 들지않았습니다.
그러던중 2주차 미션을 제출하신분중에 예외에 걸릴시 throw Error(...)
로 에러를 던져 try - catch
로 구현한것을 보았습니다. 그래서 이번 3주차에서는 Model
에서 예외처리를 하여 Error를 던져 Controller
에서 catch하여 alert를 날리는 식으로 구현하였습니다.
1, 2주차 때는 모든 html요소가 index.html파일안에 들어가있어서 이런식으로 DOM들을 상수로 만들어서 관리하는게 가능하였습니다.
하지만 이번 3주차에서는 index.html에 달랑 app컴포넌트만 있고 모든 html요소를 js로 컨트롤을 해야됬습니다. 그렇다고 DOM요소들을 Controller안의 constructor안에서 불러와도 여전히 아직 View에서 렌더를 하기전이기에 접근하면 undefined가 나왔습니다.
그래서 먼저 View에서 렌더링을 한다음에 initDOMS
라는 함수에서 사용하는 모든 돔을 변수로 설정해주었습니다. 원래 모든 변수는 constructor에 넣어야 된다고 생각하고 있었는데 어짜피 this
가 가르키는것이 그 객체이기때문에 객체에 변수를 달아준다고 생각하여 코드를 구성하였습니다.
이번에도 1주차의 이벤트위임과 같이 들어본정도의 개념이였습니다. 실제로 사용해보는것은 이번이 처음이였습니다. localStorage는 웹의 데이터베이스 같은 역할이라서 새로고침할때도 불러오는 것으로 알고있었기에 큰 어려움은 없었으나 dataset은 정말 들어만 보고 아예 처음 사용을 해보았기에 왜 쓰는지 어떻게 쓰는지 몰랐습니다. dataset은 HTML의 DOM요소에 정보를 저장하는 용도로 사용되고 HTML요소에서 부여할때는 data-user-name 이런식으로 작성을 하고 js에서 그 DOM의 dataset요소를 가져오려면 하이픈이 카멜케이스로 바뀌어 $app.dataset.UserName으로 접근할수가 있습니다. 실제로 상품의 정보같은것을 innerText로 가져오는거보다 dataset으로 가져와서 사용하는것이 좀더 목적에 맞는 용도 같았습니다.
이번에 파일명을 바꾸고 싶어 vscode에서 파일명을 바꾸고 git add . 을 하고 commit을 하였지만 아무것도 변하지 않았다고 커밋되지 않았습니다. 알고보니 깃에서 파일네임 변경을 인지할수있도록 하려면 git mv 기존 파일명
바꿀 파일명
이런 식으로 변경을 해야 로컬과 깃에서 모두 변경이 되는 것을 알수 있었습니다.
이번엔 따로 eslint를 설치하여 설정하지 않았습니다. 주차를 진행할수록 클린코드에 신경쓰면서 구현하다보니 머리속이 eslint가 된듯한 느낌이였습니다.. 컨벤션이 어긋나거나 함수가 길어지면 스스로 불편해지게 되어 굳이 eslint가 필요하다고 생각하지 않았습니다.
파일 네이밍 컨벤션
구글 코딩 컨벤션에서 파일 네이밍 컨벤션은 오직 소문자로 이루어지고 하이픈('-') 으로 나눈다고 되어있는데 또 export default
되는 class명과 파일명이 일치해야된다라는 컨벤션도 있어서 어떤 방식을 지켜야될지 헷갈렸습니다. 그래서 export default
가 있는것은 class와 동일하게 파스칼케이스로 작성하였고 없는것은 소문자와 '-'으로 구성하였습니다.
함수 10줄 지키기
3주차에서 스스로 최대 indent를 1, 최대 함수의 길이를 10줄 으로 제한을 하여 구현을 하였는데 대부분의 함수에서 지켰지만 어쩔수없이 변수와 공백때문에 12줄이 된 함수가 있었습니다.
그래도 공백을 없애서 가독성을 줄이는 것보다 12줄을 쓰는게 나을거 같아서 그대로 제출하였습니다. 그래도 10줄을 넘긴거에 대한 아쉬움이 남아있습니다.
Controller에 끼여버린 View요소
위의 사진처럼 입력 폼의 글자들을 초기화하는 폼은 View요소에 들어가는게 맞다고 생각합니다. 그런데 Controller에서 DOM들을 초기화했기때문에 View에서 저런코드를 사용하려면 매개변수를 3개나 사용을 해야합니다. 그런데 코딩컨벤션에서 매개변수는 적으면 적을수록 좋다고 하였는데 3개는 아무리 봐도 너무 많은것 같았습니다. 그렇다고 이미 Controller에서 초기화한 돔을 또 View에서 초기화해서 사용하기는 싫었습니다. 고민을 한 결과 그냥 Controller에 쓰기로 했지만 여전히 애매한 부분인것같아서 아쉽습니다.
아직 본 과정도 아닌 프리코스이지만 정말 많이 배웠습니다. 글로 구체적으로 적은 부분은 저정도지만 실제로 함수,변수 이름짓는법, 함수 나누는법, 구현능력, 깃 이용법, 객체의 특성 등등 굉장히 많은것을 배웠습니다. 그리고 주변에 개발하는 사람이 없어 협업은 커녕 다른사람의 코드를 볼 경험도 없었는데 이번 프리코스를 통하여 같은 기능을 여러가지로 구현할수 있다는것을 보았고 다른 분들의 코드에서 배운점도 많았습니다.
비록 6개월 정도밖에 안되던 개발경험이지만 이때까지의 개발경험중 가장 많이 성장한 3주가 된것 같습니다. 이제 최종 코딩테스트만을 남겨놓고 있는데 꼭 합격해서 1년간 성장할수 있는 기회가 왔으면 좋겠습니다.
위에서 말했듯이 아직 저는 개발경험이 많지 않습니다. 잘못된 설명이 있을수도 있으니 무조건적으로 안 받아드렸으면 합니다. 그리고 잘못된 부분의 피드백은 언제나 환영입니다. 댓글 달아주세요~😀
안녕하세요! baby_dev님의 글 정말 잘 읽었습니다!
혹시 제가 개인프로젝트로 개발 활동 리뷰글을 모으는 프로젝트를 진행하고 있는데
해당 글이 너무 마음에들어서 제 프로젝트에 올려도 될까요!?
https://github.com/junghyeonsu/awesome-dev-activity-review
에서 해당 프로젝트를 보실 수 있습니다!