지난 주말에 프로그래머스에서 진행하는 웹 프론트엔드 데브 매칭에 참가했다.
프로그래머스 데브 매칭을 간단하게 소개하자면, 참가자는 프로그래머스에서 제공하는 온라인 환경의 VSCode에서 주어진 문제를 해결하고, 참가 기업은 참가자의 해결 능력 및 실무 능력을 판단하여 괜찮다고 생각되면 추후에 면접을 제안할 수도 있다.
회사 입장에서 기술 면접 단계에서 개발 환경을 제공 해 주고, 실제 실무 능력을 검증할 수 있을 정도의 문제를 만들어서 지원자가 얼마나 문제를 잘 해결하는지 검증하고 싶지만 시간상, 여건상 어려운 것이 사실이다.
문제 리뷰
이번 웹 프론트엔드 데브 매칭 때 나왔던 문제를 리뷰하기 위해 노션에 문제를 긁어다가 저장해 두었다. 프로그래머스에 문의한 결과 출처를 남기면 블로그에 게시해도 괜찮다는 답변을 받았기 때문에 맘 편히 정리해 보도록 하겠다.
이하 문제 내용은 프로그래머스 데브 매칭에서 발췌했다.
HTML/CSS 관련
현재 HTML 코드가 전체적으로 div로 이루어져 있습니다. 마크업 구조를 시맨틱하게 변경하세요.
시맨틱 마크업에 대해 알고 있는지, 실제로 시맨틱하게 마크업을 할 수 있는지 묻는 문제였다. 시맨틱 마크업이라고 해서 HTML5의 article이나 section 등으로 사용하라는 문제는 아닌 것으로 느꼈다. 요컨대 순서가 상관 없는 항목의 경우 ul 을 사용할 수도 있기 때문이다.
개인적으로 프론트엔드 엔지니어에게 요구할 수 있는 시맨틱 마크업의 본질은, 적재적소에 알맞은 요소를 사용했는지가 중요하다고 본다. 그래서 이 문제의 핵심은 div로만 이루어진 마크업을 개선하는 것에 초점을 두고 있다고 생각했고, 나름대로 마크업을 변경하는 것으로 하여 넘어갔다.
사용자 디바이스의 화면 가로 크기에 따라 검색 결과의 row 당 column 갯수를 조절 해 주세요.
미디어 쿼리를 사용할 수 있는지 묻는 간단한 문제였다. 특이한 점은 기존에 구현되어 있는 검색 결과가 그리드 레이아웃을 사용하고 있기 때문에, 평소에 그리드를 사용하지 않았다면 처음 보는 CSS 속성에 당황할 수도 있겠다 싶었다.
웹앱이 다크 모드를 지원할 수 있도록 CSS를 수정 해 주세요. 또한 사용자가 테마를 토글할 수 있어야 합니다.
제공되 는 스타일시트 하단에 prefer-color-scheme과 관련하여 주석 처리된 내용이 있었다. 주석을 해제하고 시스템 테마에 알맞게 스타일링을 진행하면 된다. 다만 내 맥북에서는 이상하게 다크 모드인데도 불구하고 정상적으로 감지가 되지 않아서 고생좀 했다.
토글할 수 있다는 말은, 자바스크립트로 상태를 관리할 수 있어야 한다는 것이기 때문에 window.matchMedia로 초기 상태값을 확인한 뒤 토글할 수 있도록 컴포넌트를 추가했다.
스타일시트에 클래스를 정의하고 변경하는 방법을 고민했다가, CSS 변수를 이용하여 구현했다.
이미지 상세 보기 모달 관련
사용자의 디바이스 가로 크기가 768px 이하인 경우, 모달의 가로 크기를 디바이스 가로 크기만큼 늘려야 합니다.
미디어 쿼리로 모달의 가로 크기를 지정해 주면 된다. 가로 크기가 항상 스크롤바 영역의 크기 만큼 벗어나는 문제가 있어서 모달이 켜질 때 스크롤바가 보이지 않도록 overflow: hidden을 추가적으로 지정했던 것으로 기억한다.
모달 영역 밖을 누르거나, 키보드의 ESC 키를 누르거나, 닫기 버튼을 누르면 닫히도록 수정해야 합니다.
일단, window에 keydown 이벤트를 추가하여 visible 이 true일 때 ESC키가 눌렸을 경우 닫힐 수 있도록 구현했다.
모달 영역 밖, 백드롭과 닫기 버튼은 click 이벤트를 추가하였고, 닫기 버튼 이외의 영역을 클릭 할 때 백드롭까지 이벤트가 전파되지 않도록 처리해야 했다.
모달이 표시될 때 고양이의 성격, 태생 정보를 불러와 표시해야 합니다.
모달이 표시될 때 갖고 있는 고양이의 ID를 사용하여 세부 정보를 불러와 표시해야 하는 문제였다.
실제로 프로젝트를 진행해 보면, 목록에서 표시할 수 있는 데이터와 상세 정보에서 표시할 수 있는 데이터에 차이가 있기 때문에 API를 다시 호출해야 하는 경우가 있다. 대부분 목록에서 응답한 데이터가 좀 더 경량화 되어 있기 때문이다. 실무에서 경험해볼 법한 문제가 나와서 마음에 들었다.
검색 컴포넌트 관련
입력 필드에 자동으로 포커스가 가도록 하고, 키워드를 입력한 상태에서 클릭하면 기존에 입력했던 키워드가 삭제되도록 만들어야 합니다.
포커스의 경우 autofocus 속성을 사용해서 구현했다. 클릭 시 입력한 키워드가 삭제하는 것은 클릭 이벤트에서 value 를 초기화 해 주었다.
데이터를 불러오는 중이거나, 검색 결과가 없는 경우에 해당하는 UI를 구현해야 합니다.
API를 요청하는 기능을 만들 때에는 로딩 상태를 사용자에게 알리는 것이 좋은 경험을 제공하는 방법 중 하나다. 로딩 상태에 있는지도 모를 경우 중복된 요청을 만들어낼 가능성이 있기 때문이다.
서버에서 응답한 데이터가 빈 배열이라고 아무것도 표시하지 않으면 이 또한 잘못된 사용자 경험을 제공하게 된다. 최초 상태에 표시할 데이터가 없는 것과, 검색 이후 표시할 데이터가 없는 것은 전혀 다르기 때문이다.
최근에 검색한 키워드를 입력 창 아래에 표시되도록 만들고, 클릭하면 해당 키워드로 검색이 될 수 있도록 만들어야 합니다.
최근 검색 내역을 구현하는 문제였다. 입력 필드에서 검색이 될 때 키워드를 배열에 저장하고, 최대 5개까지만 저장되도록 구현했다. 로컬 스토리지에 저장해야 할 필요는 없는 것 같아서 관련 작업은 구현하지 않았다.
페이지를 새로고침 하면 마지막 검색 결과가 유지되어야 합니다.
검색 결과를 처리할 때 로컬 스토리지에 저장하도록 한 다음, 인스턴스가 생성될 때 로컬 스토리지에서 다시 가져오도록 처리했다.
버튼을 클릭하면 특정 API를 호출하여 무작위 고양이 사진을 표시하는 기능을 추가해야 합니다.
검색 기능과 큰 차이는 없었기 때문에 구현하는 것에 있어서 어려움은 없었다. 다만 실제 제공하는 API 주소와 문제에서 알려주는 API 주소가 달라서 잠깐 당황했던 기억이 있다.
스크롤 관련
Lazy Load 개념을 이용하여, 이미지가 화면에 표시될 때 이미지가 로딩 될 수 있도록 처리해야 합니다. 또한 사용자가 브라우저 스크롤 바를 끝까지 이동시킬 경우, 다음 페이지의 검색 결과를 불러올 수 있어야 합니다.
window에 scroll 이벤트를 추가할까 하다가 Intersection Observer API를 사용해야 하는 게 맞을 것 같다는 고민을 계속 하다가, 결국 두 문제 다 풀지 못했다. 어떻게든 구현 해 볼껄 하는 생각도 들고, 평소에 Intersection Observer를 사용해 볼걸 하는 생각도 들었다.
코드 구조 관련
ES6 Module 형태로 코드를 변경합니다. 단, 번들러는 사용하지 않습니다.
기존에 기능별로 코드가 어느정도 작성되어 있었고, index.html 파일 내에서 각각 불러오는, script 태그의 순서가 중요했던 그때 그 시절의 방식으로 구성되어 있었다.
이 구조를 ES6 Module의 형태로 리팩토링을 해야 했다. 이 문제는 브라우저에서 어떻게 Module을 활성화 하여 스크립트를 실행할 수 있는지 알아야 하고, 자바스크립트에서 모듈을 어떻게 작성하는지 이해해야 하는 문제였다.
따라서 index.html 내에 작성된 script 태그는 단 하나가 되어야 한다.
API 요청 코드를 Async/Await을 사용하여 수정하고, API 응답 코드에 따라 에러 메세지를 분리하세요.
원래는 각각의 문제인데, 작업 자체는 한 번에 진행할 수 있는 문제들이었다. API를 호출할 때 사용하는 함수를 만들어 Async/Await을 적절하게 사용했고, Try/Catch를 사용하여 올바르지 않은 응답이 발생할 경우 에러 메세지를 포함하여 Throw 하도록 처리했다.
Fetch API에 잘못 알고 있던 부분으로는, 200번대 응답이 아닌 경우 자동으로 오류가 발생할 것으로 예상했는데 실제로는 예외가 발생하지 않는다는 것이었다. Response 객체 내의 ok를 확인하거나 수동으로 200번대 코드가 아닌 경우 예외를 발생시켜야 한다.
검색 결과에서 각 아이템을 클릭하는 이벤트를 Event Delegation 기법으로 수정해 주세요.
자바스크립트의 이벤트 버블링, 캡쳐링, 위임에 대한 이해가 필요한 문제였다.
기존 코드는 검색 결과를 렌더링 할 때 마다 새로 추가되는 요소에 대해 이벤트를 일일히 바인딩하고 있었다. 이 코드를 지우고, 검색 목록 자체에 클릭 이벤트를 걸어 event.target 으로 어떤 항목을 클릭했는지 구분할 수 있도록 수정하였다.
테스트 관련
아쉽게도 테스트 관련 문제는 하나도 해결하지 못했다. 시간을 잘 분배하지 못한 것도 있고, 테스트가 가능할 수 있도록 컴포넌트(클래스)내의 메소드를 최대한 작게 나누지 못했기 때문이다.
평소에 테스트 코드를 작성 해왔음에도 불구하고 테스트 관련 문제를 하나도 해결하지 못해서 매우 아쉬웠다.
데브 매칭 후기
데브 매칭은 회사의 일을 덜어내고, 진짜 실력이 있는 개발자를 탐색하는 것에 집중할 수 있도록 도와주고, 지원자에게 부담 없이 자신의 실무 능력을 뽐낼 수 있도록 도와주는 행사라고 느껴졌다.
웹 프론트엔드 데브 매칭 소식을 들었을 때, 내 실력으로 어느 정도까지 문제를 해결할 수 있을지도 궁금했지만, 바닐라 자바스크립트로 테스트가 진행되는 것이 매우 흥미로웠다.
바닐라 자바스크립트로 웹 앱을 만들어 본 적이 언제였는지 기억도 나지 않는다. 군대에서 행정병 PC로 갖고 놀 때에도 제이쿼리가 함께 했었다. 전역 이후 취직을 한 다음 부터 바닐라 자바스크립트로 뭘 만들어 본 적이 없는 것 같다.
문제 수준은 적당했던 것 같다. 평소에 프론트엔드 개발에 관심이 있었다면 충분히 해결할 수 있는 수준의 문제들이었고, 문제 자체도 꽤 상세하게 설명해 주고 있어서 큰 도움이 되었다.
전반적으로 만족스러웠다. 다음에도 프론트엔드 데브 매칭이 진행된다면 다시 참가해볼 것 같다.