코딩테스트 시간 별 전략

프론트엔드 코딩테스트에는 세가지 유형이 있다.

  • 단순 알고리즘 테스트

    • 그냥 알고리즘을 잘하면 된다!
    • 프론트엔드에서 자주 나오는 유형은 아니다.
  • 시간이 촉박한 구현 테스트 (3시간 ~ 4시간)

    • 효율적인 구조를 신경쓰기보다 ‘빠르게 개발하는데’ 집중하라.
    • 고득점을 위해서 효율적 구조를 빠르게 짜는 방법을 평소에 연습하라.(같은 동작이라면 효율적 구조가 더 높은 점수를 받을 것.)
  • 시간이 널널한 구현 테스트 (1일 ~ 1주일)

    • 현업에서 요구하는 정확한 방법으로 설계하라. (동작만 된다고 중요한게 아니다. 효율적 구조로 설계 못하면 광탈한다.)
    • 웹 프론트엔드 설계 이론을 평소에 공부하라.

ESM


ESM이 모던 JavaScript의 표준으로 지정되면서 바닐라를 사용하는 코딩테스트에서도 ESM 사용을 요구하는 경우가 많아졌다.

1
<script type="module" src="main.js"></script>

script 태그에서 typemodule로 지정하면 된다.

1
2
3
4
5
6
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';

export {
function1 as newFunctionName,
function2 as anotherNewFunctionName
};

이제 알고 있는대로 esm의 importexport 문법을 사용하면 된다.

기존에는 script 태그의 순서대로 코드가 실행됐다면,

이제는 엔트리 포인트(index.js)를 중심으로 모듈 관계에 따라 코드가 실행된다.

여기서 주의할 점은 코딩테스트 환경에 맞춰 주소를 정확하게 기입해야한다.

Node.js 환경에 익숙한 사람들은 모듈을 불러올 때 보통 js를 생략한다.

그러나 esm을 브라우저에서 사용하려면 확장자인 js까지 ‘반드시’ 포함해야함을 잊지 말자.

Fetch API


1
2
3
4
5
6
7
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Example POST method implementation:

postData('http://example.com/answer', {answer: 42})
.then(data => console.log(JSON.stringify(data))) // JSON-string from `response.json()` call
.catch(error => console.error(error));

function postData(url = '', data = {}) {
// Default options are marked with *
return fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json()); // parses JSON response into native JavaScript objects
}

Fetch API는 바닐라 환경에서 XMLHttpRequest보다 현대적이고 유려한 방법을 제공한다.

기본적으로 GET Method를 사용하며 Promise 패턴을 이용한다. (async 문법 가능)

아마 대부분의 코딩 테스트에서 application/json 형태로 request, reponse를 할 가능성이 높다.

fetch의 첫번째 인자는 주소를 입력하고, 두번째 인자에는 코딩테스트에서 요구하는 조건을 구성하면 된다.

Node 생성


DOM API를 읽기 전에 - 상속 관계 이해

바닐라 JS를 하기 위해서는 DOM API의 상속 관계를 반드시 이해하기 바란다.

코딩 테스트는 라이브러리 개발이 아니므로 보통 HTMLElement만을 다루게 된다.

다시말해, 부모 클래스인 EventTarget, Node, Element 타입의 메서드를 모두 골고루 알고 있어야한다.

공부하다보면 Node 클래스와 Element 클래스에서 비슷한 일을 수행하는 메서드들이 존재한다.

두 메서드의 표면적인 차이점을 느낄 수 없는 경우엔 Element에서 정의한 메서드를 사용하라.

분명히 자식 클래스인 Element 클래스에서 새롭게 정의한 이유가 있을 것이다. (보통 더 유려하거나 편리하다.)

document.createElement() - Element

1
let element = document.createElement(tagName[, options]); // HTML**Element

document.createTextNode() - Text

1
let text = document.createTextNode(data); // Text

document.createAttribute() - Attr

1
let attribute = document.createAttribute(name)

Element 탐색


querySelector는 selector 문법을 이용해 상세 검색이 가능하다. (고급 기술)

단순 id, class, tagName으로 검색한다면 getElementBy{Id | ClassName | TagName}을 이용하자.

document.querySelector() - Element || null

1
var element = document.querySelector(selectors);

document.querySelectorAll() - NodeList

1
var elementList = parentNode.querySelectorAll(selectors);

document.getElementById() - Element || null

1
var element = document.getElementById(id);

document.getElementsByClassName() - HTMLCollection

1
var elements = element.getElementsByClassName(names);

document.getElementsByTagName() - HTMLCollection

1
var elements = document.getElementsByTagName(name);

Class 조작


element.classList - DOMTokenList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const div = document.createElement('div');
div.className = 'foo';

// our starting state: <div class="foo"></div>
console.log(div.outerHTML);

// use the classList API to remove and add classes
div.classList.remove("foo");
div.classList.add("anotherclass");

// <div class="anotherclass"></div>
console.log(div.outerHTML);

// if visible is set remove it, otherwise add it
div.classList.toggle("visible");

// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );

console.log(div.classList.contains("foo"));

// add or remove multiple classes
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// add or remove multiple classes using spread syntax
const cls = ["foo", "bar"];
div.classList.add(...cls);
div.classList.remove(...cls);

// replace class "foo" with class "bar"
div.classList.replace("foo", "bar");

classListDOMTokenList(유사 배열 객체)이며 class의 목록과 이를 수정하기 위한 모든 메서드를 지원한다.

Attribute 조작


element.attributes - NamedNodeMap

1
2
3
4
let attrs = element.attributes;

attrs[0] // index 방식
attrs['src'] // key 방식

attributes는 해시맵 형태의 NameNodeMap 객체이다.

orderedHashMap이기 때문에 index, key 두가지 방식으로 모두 접근 가능하다.

element.getAttribute() - string

1
attrval = element.getAttribute(attributeName);

element attribute의 값을 반환한다.

element.setAttribute() - string

1
value = element.setAttribute(name, value);

element의 attribute의 값을 설정한다.

element.getAttributeNames() - string[]

1
attributeNames = element.getAttributeNames();

element의 attribute 이름 배열을 반환한다.

element.removeAttribute()

1
element.removeAttribute(attrName);

element의 attribute를 삭제한다.

Element 조작


element.innerHTML, element.outerHTML은 읽기 전용으로만 사용하길 권장

element.insert{something}()

1
2
3
4
5
6
7
8
element.insertAdjacentHTML(where, data);

/**
* 'beforebegin': Before the element itself.
* 'afterbegin': Just inside the element, before its first child.
* 'beforeend': Just inside the element, after its last child.
* 'afterend': After the element itself.
*/

HTML을 삽입하는게 아니라면 insertAdjacent 대신 element.append(), element.prepend(), element.after(), element.before()를 사용해도 동등하다.

element.remove()

1
element.remove()

DOM Tree에서 element를 제거하고 완전히 소멸시켜버린다.

element.replaceWith()

1
element.replaceWith(...nodes)

element자체의 레퍼런스를 완전히 다른 노드로 변경시킨다.

element.children - HTMLCollection

1
children = element.children

element의 자식들을 반환한다. 이때 타입은 HTMLCollection(유사 배열 객체)이다.

  • 첫번째 자식 element: element.firstElementChild
  • 마지막 자식 element: element.lastElementChild

element.replaceChildren()

1
element.replaceChildren(...nodesOrDOMStrings)

element의 자식들이 완전히 교체 된다.

element.childrenCount - number

1
element.childElementCount

element의 자식들의 수를 반환한다.

element.nextElementSibling - Element

1
2
3
4
5
let el = document.getElementById('div-01').nextElementSibling;
while (el) {
console.log(el.nodeName);
el = el.nextElementSibling;
}

같은 계층에 존재하는 element를 순회한다. 반대 방향으로는 element.previousElementSibling()을 사용하면 된다.

만약 Element가 아닌 NodeList를 탐방하고 싶다면 node.nextSibling()`을 사용하라.

element.animate()

애니메이션 관련 CSS를 함께 학습해야한다. 이 링크를 참고하라.

노드(Node) 조작


비고

Element 클래스에서 비슷하게 할 수 있는 작업은 제외함

node.contains() - boolean

1
node.contains(otherNode)

otherNodenode의 자식인지 평가한다.

node.removeChild() - Node

1
child = node.removeChild(child);

node에서 child를 삭제한다. 이때 삭제된 child 노드가 반환된다.

node.replaceChild() - Node

1
oldChild = node.replaceChild(newChild, oldChild);

node의 자식 중에 oldChildnewChild로 교체한다. 이때 교체된 oldChild가 반환된다.

node.nodeName - string

1
node.nodeName

node의 이름을 반환한다. 비슷한 프로퍼티로 node.NodeType`이 존재하는데 integer를 반환한다.

node.parentElement - Element

1
node.parentElement

nodeparentElement에 접근한다. 비슷한 프로퍼티로 node.parentNodeNode` 타입을 반환한다.

node.textContent - string

1
node.textContent = 'replace'

node의 textContent를 반환한다. 내용을 업데이트 하는 용도로 사용할 시 innerText보다 빠르다고 알려져 있다.

HTMLElement 조작


element.focus()

호출한 element를 포커싱 한다. 일부 이벤트는 포커싱 이후에 발생한다.

코딩 테스트에서는 주로 HTMLInputElement에 대한 포커싱을 요구한다.

element.blur()

호출한 element를 포커싱 해제한다.

element.click()

호출한 element를 클릭한다. 버튼, 앵커의 경우 별도의 이벤트를 설정하지 않았다면 기본 동작이 실행된다.

element.style

element의 CSS 스타일을 설정하는 property이다.

주의할점은 CSS는 보통 kebab-case를 사용하지만 JS style은 lowerCamelCase를 사용한다.

그 외에 API들


실제 코딩 테스트에서 유용한 메서드들 위주로 소개하였다.

허나 실제로 잘 사용하지 않더라도 많이 알고 있으면 나쁠게 없다.

Node, Element, HTMLElement 문서를 한번씩 읽어보자.

Event 조작


DOMContentLoaded

1
2
3
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
});

DOM이 안벽하게 로딩되었을때 발생하는 이벤트이다.

EventTarget.addEventListener()

1
2
3
eventTarget.addEventListener(type, listener);
eventTarget.addEventListener(type, listener, options);
eventTarget.addEventListener(type, listener, useCapture);

이벤트를 수신하는 리스너를 추가할 수 있다.

이벤트 버블링과 캡처는 이 글을 읽어보길 추천한다.

  • 주요 이벤트 (이벤트 목록)
    • load (리소스가 로딩 되었을 때 - img 태그 등에서)
    • focus (엘리먼트가 포커싱 인 되었을 때)
    • blur (엘리먼트가 포커싱 아웃 되었을 때)
    • submit (폼이 제출 되었을 때)
    • reset (폼이 초기화 되었을 때)
    • resize (다큐먼트 뷰가 리사이즈 되었을 때)
    • scroll (엘리먼트가 스크롤 되었을 때)
    • select (선택 가능한 영역에서 무엇인가 선택되었을 때)
    • copy / cut (선택 가능한 영역에서 무엇인가 복사, 잘라내기 되었을 때)
    • paste (수정 가능한 영역에서 무엇인가 붙여넣기 되었을 때)
    • keydown / keyup / keypress (키가 눌렸을 때)
    • mouseenter / mouseover / mousemove / mousedown / mouseup / mouseleave / mouseout (마우스 관련)
    • click / dbclick / contextmenu (엘리먼트가 클릭 되었을 때 / 더블 클릭 / 오른쪽 클릭)
    • drag / dragstart / dragend / dragenter / dragover / dragleave / drop (드래그&드롭 관련)
    • touchstart / touchend / touchmove / touchcancel (터치 관련)

EventTarget.removeEventListener()

1
2
3
eventTarget.removeEventListener(type, listener);
eventTarget.removeEventListener(type, listener, options);
eventTarget.removeEventListener(type, listener, useCapture);

이벤트를 삭제한다.

EventTarget.dispatchEvent()

1
eventTarget.dispatchEvent(event)

이벤트를 스크립트로 발생 시킬 수 있는 방법이다.

Scroll 조작


스크롤 동작을 이해하기 전에 보면 좋은 문서

스크롤 프로퍼티

스크롤 메서드

  • scroll() == scrollTo() - 엘리먼트의 절대적 위치를 기준으로 스크롤 발생
  • scrollBy() - 엘리먼트의 상대적 위치를 기준으로 스크롤 발생
  • scrollIntoView() - 엘리먼트가 속한 뷰를 기준으로 스크롤 발생

스크롤 제어 (overflow)

1
2
3
4
5
.someting {
overflow: visible | hidden | scroll | auto;
overflow-x: visible | hidden | scroll | auto;
overflow-y: visible | hidden | scroll | auto;
}

visible은 기본 값으로 컨테이너의 최대 길이를 초과해도 스크롤 하지 않고 그냥 보여준다.

hidden은 컨테이너의 최대 길이를 초과한 영역은 보여주지 않는다. (스크롤 되지 않음)

scroll은 컨테이너의 최대 길이를 초과한 것과 상관 없이 스크롤바를 항상 보여준다.

auto는 컨테이너의 최대 길이를 초과했을 때만 스크롤바를 보여준다.

xy는 횡 스크롤, 종 스크롤을 의미한다.

스크롤 맨 위, 아래로 옮기기

1
2
element.scrollTop = 0 // 맨 위로
element.scrollTop = element.scrollHeight // 맨 아래로

채팅창 구현(메신저 개발)이 과제로 나온다면 반드시 필요한 스킬이다.

기타 디자인 요소


DotDotDot 처리 (Elipsis)

1
2
3
4
5
6
.elipsis {
width: [width];
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}

width 값을 넘어가는 텍스트에 대하여 ・・・ 처리를 한다.

여러줄 Elipsis

1
2
3
4
5
6
7
8
9
.elipsis-multiline {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: [num-line];
-webkit-box-orient: vertical;
word-wrap:break-word;
height: [height];
}

지정한 -webkit-line-clamp 만큼 텍스트를 출력하고 넘어가는 텍스트에 대해서는 ・・・ 처리를 한다.

이 때 height를 적절하게 설정해줘야 정상적으로 표시된다.

라인 수를 동적으로 지정하고 싶으면 css 팩토리 함수를 만들면 된다.

Image LazyLoad

1
<img src="..." loading="lazy">

사용자 경험을 높이기 위해 lazy-loading을 요구할 수 있다.

주로 이미지 로딩에 대하여 이 기능을 요구하는데 loading="lazy"라는 간단한 속성으로 구현할 수 있다.

저 태그가 있다고 무조건 lazy loading을 하는 것은 아니다.

브라우저마다 다르겠지만 만약 이전에 불러온적이 있다면 색인을 해뒀다 불러오는게 더 빠르다.

만약 lazy loading을 다시 보고 싶다면, Hard Reload(shfit+cntl/cmd+r)를 하라. (색인을 초기화 한다.)

또한, loading="lazy"만 한다고 언제나 능사는 아니니, 각 상황별로 대처하기 위해 아래 문서를 읽어보길 바란다.

웹 성능 최적화를 위한 Image Lazy Loading 기법

Image Scroll Loading

1
2
3
4
5
6
element.addEventListener('scroll', evt => {
if (evt.target.scrollHeight - evt.target.scrollTop === evt.target.clientHeight) {
// ... api 요청
}
})

scroll이 최대로 내려왔을 때 새로운 Request를 보내는 방법이다.

<img loading="lazy">와 함께 사용하면 시너지가 좋다.

W3Schools HowTo

W3Schools의 HowTo 항목에는 CSS와 VanillaJS를 조합하여 다양한 디자인 컴포넌트를 만드는 방법을 소개한다.

image overlay image slide

반응형 미디어 쿼리

기본 문법은 @media [media-type] and ([media-type-rule]) and ... 이다.

반응형 웹을 위해 필수로 미디어 쿼리에 대해 학습할 필요가 있다. 미디어 쿼리 학습

  • 논리곱 (and) 예시

@media screen and (min-width: 400px) and (orientation: landscape)

→ 화면의 최소 폭이 400px이고 회전방향이 landscape일 경우

  • 논리합 (,) 예시

@media screen and (min-width: 400px), screen and (orientation: landscape)

→ 화면의 최소 폭이 400px이거나 회전방향이 landscape일 경우

다크 모드 (Dark Mode)

1
@media (prefers-color-scheme: dark) {}

시험 전에 PC를 미리 다크모드로 설정해두면 확인하기 편리하다.

출처


MDN
W3Schools