간단 후기
프론트엔드 개발자가 되기 위해 준비하는 사람으로서 이보다 좋은 기회는 없을 것이라 생각한다.
제목과 같이 프로그래머스에서 진행하는 22년도 하반기 Dev-Matching 중 2번째를 경험해보고 적는 후기이다.
결론은 망한 것 같다. 모든 기능을 다 만들지 못 하고 코드도 더럽게 작성했다.
사실 이런 프로그램이 있다는 것을 알게 된 것은 꽤 됐지만 잊어 먹고 있다가 이번에 처음 진행해봤고 아직 결과는 안 나왔지만 뭐 이미 감이 온다.
전부터 리액트에 너무 익숙해져 있기도 하고 JS 문법을 너무 사용하는 방식만 고집해온 느낌이 들어 JS를 다시 공부했었지만 JS만 이용해서 프로젝트를 만들어 본 것은 이번 데브 매칭의 프로젝트가 처음이었던 것을 깨달았다.
그리고 느낀 것은 확실히 순수 JS만을 사용하면 속도도 느리고 무엇보다 내가 아는게 너무 적다는 것을 알았다. 리액트를 필두로 너무 그동안 라이브러리에 의존해왔다는 것을 다시 한번 느끼게 해준 좋은 경험이라고 생각한다.
테스트 전날 즉, 어제 프로그래머스 페이지를 보니 이전 데브 매칭 프로젝트에 대한 설명과 직접 만들어 볼 수 있는 환경이 준비되어 있길래 급하게 이런게 있었구나는 것을 알고 풀어봤는데 내가 그동안 쓰지 않았던 방식의 문법으로 설명이 되어 있어서 상당히 난해 했다(진짜 너무 리액트에 몸이 절여진것 같다).
그래도 대충 따라 해보니 전체적인 흐름은 리액트와 비슷해서 크게 어렵진 않았지만 그 몇 번해봤다고 시험을 잘 몰리가 없다.
그래서 결론은 순수 JS로 프로젝트 만드는 것을 통해 이번 시험은 망했지만 시험이 이번 한번만 있는 것도 아니고 내가 부족한 점은 어느정도 알았으니 보충하면 된다. 좋은 경험이었다.
순수 JS로 프로젝트 하나 해봤으니 다음엔 리액트 말고 TS로 만들어 보는 것도 좋은 경험이 돼고 재밌을 것 같다.
아래 부터는 깃허브에도 남겨뒀지만 블로그에도 다시 한번 작성한 코드를 남긴다.
문제 풀 때 주의사항을 보긴 했지만 이건 내가 직접 작성한 코드이고 문제 자체나 제공된 파일을 배포한게 아니니 블로그에 코드 작성하는 것은 문제 없을 것 같다.
고칠 부분이 꽤 있어 보이는데 지금 당장은 말고 나중에 보고 고치기 위한 기록(과연 언제 고치나 보자)
App.js
// App.js
export default function App({ target }) {
// 기본적으로 사용될 값들
this.state = {
// 전체 사원 목록
employee: [],
// 현재 페이지 번호 (ex_ 1번 페이지, 2번 페이지 ...등) 사원 목록
nowEmployee: [],
// 사원 리스트를 한 페이지 당 몇 개 보이게 할지 -> 처음은 5개로 고정
count: 5,
};
this.setState = (nextState) => {
// 기존의 값과 새로 바뀐 값 중 변경된 부분만 교체
this.state = {
...this.state,
...nextState,
};
// 현재 페이지 번호가 바뀔 때마다 사원 목록을 바꿈
tableList.setState({
items: this.state.nowEmployee,
});
// 사원 목록 바꾸기 위해 pagination 컴포넌트의 값도 변경
pagination.setState({
...nextState,
});
};
// 드롭 다운
const dropDown = new DropDown({
target,
initialState: "",
// 드롭 다운 값 바뀔 때마다 값 변경을 위한 함수
onChange: async (value) => {
this.setState({
count: value,
});
// 처음 렌더링된 화면은 count : 5로 고정이므로 사원 목록 5개로 고정
this.setState({
nowEmployee: tableSlice(this.state),
});
},
});
// 사원 목록 테이블 표를 제작하기 위한 페이지
const tablePage = new TablePage({
target,
initialState: "",
// 맨 처음 로드돼서 api 값 받을 준비가 됐을 때 api값 전달 받음
onLoad: async () => {
const res = await fetchEmployee();
this.setState({
employee: res,
count: 5,
nowEmployee: [],
});
this.setState({
nowEmployee: tableSlice(this.state),
});
},
});
// 화면에 표시할 사원 리스트
const tableList = new TableList({
target,
initialState: {
items: [],
},
});
// 페이지네이션
const pagination = new Pagination({
target,
initialState: "",
// 번호 버튼 누를 때마다 사원 목록 업데이트를 위한 함수
onClick: (value) => {
this.setState({
nowEmployee: listChange(this.state, value),
});
},
});
}
Dropdown.js
// Dropdown.js
// 드롭 다운 형식의 html 작성
this.render = () => {
this.element.innerHTML = `
<select>
<option value='5' selected>5개씩</option>
<option value='15'>15개씩</option>
</select>
`;
};
this.render();
// 선택 값이 변경 될 때마다 호출, 이벤트 타겟의 값을 꺼냄
this.element.addEventListener("change", (e) => {
this.onChange(e.target.value);
});
ListChange.js
// ListChange.js
const isNotNumber = ["<<", ">>"];
const list = (initialState, pageNumber) => {
// 한 페이지에 표시할 사원 리스트가 5개인지 15개인지 구분
if (parseInt(count) === 5) {
// 클릭된 버튼이 숫자일 경우 (ex_ 1, 2, ...5)
if (!isNotNumber.includes(number)) {
let v = parseInt(number);
// 해당 페이지 번호에 맞는 사원 목록을 꺼내서 nowEmployee에 할당
return (nowEmployee = employee.slice(v * 5 - 5, v * 5));
// 숫자가 아닌 특수 기호일 경우
} else {
if (number === "<<") {
// << = 맨 앞으로
return (nowEmployee = employee.slice(0, 5));
} else if (number === ">>") {
// >> = 맨 뒤로
return (nowEmployee = employee.slice(20, 25));
}
}
} else {
// 위와 동일하되 아래 코드는 15개의 리스트를 출력
if (!isNotNumber.includes(number)) {
let v = parseInt(number);
return (nowEmployee = employee.slice(v * 15 - 15, v * 15));
} else {
if (number === "<<") {
return (nowEmployee = employee.slice(0, 15));
} else if (number === ">>") {
return (nowEmployee = employee.slice(15, 25));
}
}
}
};
export const listChange = (initialState, pageNumber) =>
list(initialState, pageNumber);
Pagination.js
// Pagination.js
this.setState = (nextState) => {
const newCount = nextState.count;
this.count = newCount;
this.render();
};
this.render = () => {
// 5개 15개에 따라 버튼 개수 변경
// 사원 목록 배열을 나눠서 그 크기에 따라 map을 돌려 버튼 개수를 생성하도록 만들 수도 있을 것 같음
if (parseInt(this.count) === 5) {
this.element.innerHTML = `
<button class="arrow"><<</button>
<button style="color:red">1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<button class="arrow">>></button>
`;
} else if (parseInt(this.count) === 15) {
this.element.innerHTML = `
<button class="arrow"><<</button>
<button style="color:red">1</button>
<button>2</button>
<button class="arrow">>></button>
`;
}
};
this.render();
// 버튼 클릭 시 호출
this.element.addEventListener("click", (e) => {
// 버튼 자체를 클릭할 때와 버튼이 아닌 곳을 클릭할 때가 있어서 판단하기 위한 조건문
// 버튼이 아닌 곳을 클릭하면 바로 리턴
if (e.target.innerText.length > 3) return;
// 현재 클릭한 버튼 번호 전달
onClick(e.target.innerText);
// 색 바꾸기 위해 전체 버튼 목록 꺼내옴
const btn = this.element.getElementsByTagName("button");
for (let v of btn) {
// 반복문을 돌려서 일단 다 검은 색으로
v.style.color = "black";
if (v.classList.value === "arrow") {
// 화살표 버튼은 언제나 빨강
v.style.color = "red";
}
}
if (e.target.innerText === "<<") {
for (let v of btn) {
// 맨 처음을 뜻하는 << 버튼 클릭 시 1번 버튼 빨강으로
if (v.innerText === "1") v.style.color = "red";
}
} else if (e.target.innerText === ">>") {
// 맨 뒤를 뜻하는 >> 버튼 클릭 시 마지막 버튼을 빨강으로
btn[btn.length - 2].style.color = "red";
}
// 그 외에는 현재 클릭한 버튼을 빨강으로 표시
e.target.style.color = "red";
});
TableList.js
// TableList.js
this.state = initialState;
this.setState = (nextState) => {
this.state = nextState;
this.render();
};
this.render = () => {
// 현재 받은 값 중에 items(사원 목록이 담긴 배열)만 빼옴
const { items = [] } = this.state;
// map 함수를 이용해서 사원 목록을 테이블 표에 삽입
if (items.length > 0) {
this.element.innerHTML = `
${items
.map(
(item, index) => `
<tr data-index="${index}">
<th>${item.name}</th>
<td>${item.title}</td>
<td>${item.email}</td>
<td>${item.role}</td>
</tr>
`
)
.join("")}
`;
} else {
// 만약 받은 값 중에 사원 목록이 없으면 빈 값 표시
this.element.innerHTML = "";
}
};
this.render();
TablePage.js
// TablePage.js
// 테이블 제목은 고정
this.render = () => {
this.element.innerHTML = `
<table class="tableBody">
<thead>
<tr>
<th class="tableTitle">name</th>
<th class="tableTitle">title</th>
<th class="tableTitle">email</th>
<th class="tableTitle">role</th>
</tr>
</thead>
</table>
`;
};
this.render();
// 테이블이 준비되면 사원 목록을 받아서 표시해야 하기 때문에 로드가 완료됐는지 확인
window.addEventListener("load", (e) => {
onLoad();
});
TableSlice.js
// TableSlice.js
// 드롭 다운과 번호 버튼의 선택 여부에 상관없이 처음 사원의 리스트는 5개를
// 보여주고 15개의 드롭다운 선택을 했을 경우 15개를 보여주기 위한 함수
if (parseInt(count) === 5) {
return (nowEmployee = employee.slice(0, 5));
} else if (parseInt(count) === 15) {
return (nowEmployee = employee.slice(0, 15));
}
};
export const tableSlice = (initialState) => tslice(initialState);
마무리
시험을 볼 때는 위에 작성한 코드보다 훨씬 더럽게 작성했지만 끝나고 아쉬워서 시험 시간내 다 만들지 못한 기능과 주석 등을 만들면서 코드를 조금 보기 좋게 만들었다.
그래서 다 못 만든 기능과 위 코드 보다도 더 보기 안 좋은 코드를 보며 체점하게 될 체점자 분들에게 조금 미안하기도 하면서 시험 시간 동안 다 못 만든 기능을 시험 끝나고 10분 안에 다 만들었기 때문에 여러모로 아쉬움이 많이 남는 시험이기도 했다(막상 시험볼 땐 막막했는데, 이상하게 시험 끝나니까 머리가 잘 돌아감).
아쉬움만 남으면 진짜 끝도 없이 아쉬우니까 그만 끝내고 다음을 위해서 다시 준비하는게 좋을 것 같다.
다음에는 잘 하자