카테고리 없음

[코테 대비] JavaScript로 코딩테스트 보기 - 기본

일 월 2025. 4. 14. 15:50

보통 Python으로 알고리즘을 준비하는데, 사용 가능 언어에 Python이 없는 코테가 있다. JavaScript로 코딩 테스트를 보려고 할 때, 참고하면 좋을 내용을 적어보려고 한다.

빠르게 익히는 것을 목표로 하고 있어 자세한 설명이 없고, 프로그래머스 자바 스크립트 입문 무료 강의, MDN 문서, 다른 블로그 글을 참고했다. 내용이 길어져서 나눴고 심화 내용은 아래 링크에 있다.

1. 개발환경 구축

코딩 테스트를 보기 전 알고리즘 문제를 풀기 위해 관련 프로그램을 설치한다. 시험에 따라 외부 IDE를 사용할 수 있을 수도 있고, IDE 내의 편리한 기능을 사용할 수 있다.

=> Node.js LTS 설치, IDE는 이미 설치되어 있는 VS Code 사용

 

2. 기본 문법 학습

2-1. 타입, 식별자, 언어의 특징 파악

어떤 타입이 있는지, 어떻게 명명하는지, 변수에 사용하면 안 되는 문자가 있는지 등을 파악한다.

  • JavaScript는 동적 타입 언어이고, var, const, let을 변수 앞에 붙여 선언함
  • let과 const는 ES6부터 등장 (재선언, 스코프, 호이스팅 등의 이유로 var 대신 const, let 쓰는 것을 추천)
  • 선언과 할당을 분리할 수 있고, ;을 사용해 코드를 분리할 수 있음 (한 줄에 명령문이 하나라면 사용하지 않아도 됨)
  • ,를 활용해 여러 변수를 동시에 선언, 초기화 가능
  • 식별자는 하나 이상의 글자로 이루어져야 하며, 첫 번째 글자로 문자, '$', '_'를 사용할 수 있음
  • 식별자를 명명할 때, 숫자는 두 번째 글자부터 쓸 수 있고, '$', '_' 이외의 특수문자는 사용 불가
  • 상수는 const를 이용해 선언 (const로 선언한 객체는 내부 값이 바뀔 수 있음)
  • 변수는 첫 번째 문자를 소문자로 하고 두 번째 단어부터 첫 글자를 대문자로 사용 (관례, camel case)
// 상수 선언
const PI = 3.14;

// 변수 선언
let count;
let average = 2; 
const arr = [1, 2, 3];
const foo = {bar : 3};

// 값 할당
count = 10;

// 값 변경
count = 'count';

// 다중 선언
let a = 1, b = 2;

// 다중 할당
String first, second, third;
first = second = third = "다중할당";

// 구조 분해 할당
// 배열
const [, two, , four] = arr // two는 2, three는 undefined
const [,, three=4] = arr // 값이 존재하지 않을 때 기본값 대입
const [one, ...rest1] = arr // spread 연산자를 통해 배열 대입 가능

// 값 교환
[a, b] = [b, a]

// 객체
const {bar} = foo
const {bar: varName} = foo // 변수명 바꿀 수 있음
const {num1} = foo // 해당값 없을 경우 undefined
const {num2=2} = foo // 해당값 없을 경우 기본값
const {val, ...rest2} = arr // spread 연산자를 통해 객체 대입 가능

// 반복문에서의 구조 분해 할당
const users = [
    {name: '철수', age: 25},
    {name: '영희', age: 30},
]

for (const {name} of users) {
    console.log(name)
}

// 함수의 매개변수로 전달된 객체를 구조 분해 할당
const user = {name: '영희', age: 30}

function printUser({name, age}) {
    console.log(name, age)
}




// 기본형 타입

// 논리형 : 참/거짓 => true/false (1 byte)
let isReal = true;

// 문자 : 작은 따옴표, 큰 따옴표 이용 => String
const munja1 = 'c';
const munja2 = "abc";

// escape character : 역슬래시를 이용 => 줄바꿈 : \n, 작은 따옴표 \', 큰 따옴표 \", 역슬래시 \\
const escape_char = "문장\n문장";

// 숫자 : 정수, 실수, NaN, Infinity 표현 => Number (64 bit)
const num1 = 5;
const num2 = 01;
const num3 = 0x11;
const num4 = 0b0011;
const float2 = 3.141592;
const float3 = .1234;
const float4 = 3.1E+12;
const float5 = .1e-23;

/*
0, 117, 123456789123456789n             (10진수)
015, 0001, 0o777777777777n              (8진수)
0x1123, 0x00111, 0x123456789ABCDEFn     (16진수)
0b11, 0b0011, 0b11101001010101010101n   (2진수)
*/



// 비어있는 상태 => undefined, null
// undefined : 변수나 속성이 정의되지 않은 경우 => 선언만 하고 초기화되지 않음, 객체의 정의되지 않은 속성의 타입이나 값
let variable1;
// null : 명시적으로 아무 것도 없는 비어있는 상태를 나타낼 때 사용
let variable2 = null;

 

3-2. 연산자

연산자는 언어마다 비슷한 부분이 많아, 주언어와 다른 부분만 짚고 넘어가는 편

자바스크립트에서는 값 비교를 ===, !==로 한다!

  • [부호] +, -
  • [단항 연산자 (부호 제외)] ++, --, !
  • [산술 연산자] +, -, *, /, %
  • [비교 연산자] ===, !==, <, <=, >, >=
  • [시프트 연산자] >>, <<
  • [비트 연산자] &, |, ~
  • [논리 연산자] &&, ||, ^, !
  • [대입 연산자] =, *=, /=, %=, +=, -=

 

3-3. 삼항 연산자

(조건식) ? (참일 때의 값이나 식) : (거짓일 때의 값이나 식)

 

3-4. 조건문, 반복문

조건문, 반복문도 언어마다 비슷한 부분이 많아, 주언어와 다른 부분만 짚고 넘어가는 편

// if문 - 작성한 순서대로 조건을 탐색함 (else if와, else는 필수 아님)
if (조건식) {
    실행문;
} else if (조건식) {
    실행문;
} else {
    실행문;
}


// switch문

    switch(변수){
        case 값1 : 
            실행문; 
            break;
        case 값2 : 
            실행문; 
            break;  
        default;    
    }

// while문

while(조건문){
    실행문; 
}

// do-while문
// 한 번은 코드가 실행됨
do{
    실행문; 
} while(조건문)


// for문

for (초기화식; 조건식; 증감식) {
    실행문;
}

// for in 문

for (variable in object) {
    실행문
}

// for of 문
for (variable of object) {
    실행문
}

// for in과 for of의 차이

let arr = [3, 5, 7];
arr.foo = "hello";

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo" -> 인덱스 또는 키
}

for (let i of arr) {
  console.log(i); // "3", "5", "7" -> 값
}

3-5. 배열

// 1차원 배열 생성
const array1 = new Array(1, 2, 3);
// new 생략 가능, 인자가 2개 이상일 때는 해당 요소를 갖는 배열 생성 => Array.of와 동작 같음
// const array1 = Array.of(10); // [10]
// const array1 = new Array(10) // [], c.length = 10
const array2 = [];
const array3 = [1, true, 3.14, 'string'] // 다양한 자료형이 올 수 있음
const array4 = ['1', , '2', ] // 길이 : 3 -> 두 번째 값은 undefined, 후행(trailing) 쉼표는 무시됨
const str1 = 'Hello World'
const array5 = [...str1] // ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']


// Array.from
Array.from({length: 3, 0:1, 1:2, 2:3}) // [1, 2, 3]
Array.from('Hello World', v => v.toLowerCase()) // ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
Array.from({length: 10}, (_, i) => i + 1) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


// 값 할당/수정
array1[0] = 0;
array2[0] = 2;
array3[1] = 3;

// 배열의 길이
array2.length;

// 원소 추가
array2.push(7) // 끝에 추가
array2.unshift(7) // 첫 번째 원소

// 원소 삭제
array2.pop() // 마지막 원소
array2.shift() // 첫 번째 원소

// 배열 결합
array1.concat(array2)

// 배열 검색
fruits.indexOf(7)
fruits.lastIndexOf(7)


// 2차원 배열 생성
const array6 = [['a','b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']]; 
const array7 = new Array(5);
for (let i = 0; i < arr.length; i++) {
    arr[i] = new Array(2);
}

const array8 = Array.from(Array(5), () => new Array(2); // ES6에서 추가
const array9 = Array.from(Array(5), () => Array(2).fill(0); // ES6에서 추가

 

3-6 객체

  • Key는 문자열 또는 기호, Value는 모든 유형이 가능함 (예약어를 key로 사용해서는 안 됨)
  • 객체에서 명명된 값을 Properties라고 함
// 선언
// 객체 속성명은 빈 문자열 포함한 문자열
const foo= new Object();
const car1 = { myCar: "Saturn", getCar: carTypes("Honda"), special: sales };
const car2 = { manyCars: { a: "Saab", b: "Jeep" }, 7: "Mazda" };
const unusualPropertyNames = {
  '': 'An empty string',
  '!': 'Bang!'
}

// 접근 - .이나 []를 이용해 접근 가능 (유효한 식별자가 아닌 속성명은 점 속성으로 접근할 수 없음)

car1.myCar // Saturn
unusualPropertyNames.''  // SyntaxError: Unexpected string
unusualPropertyNames[''];  // An empty string
unusualPropertyNames.!    // SyntaxError: Unexpected token !
unusualPropertyNames['!'] // Bang!


// 프로퍼티 추가/수정
foo.name = 'foo';
foo['age'] = '100';
foo.name = 'bar';

// 프로퍼티 삭제
delete foo.name;

// 키 목록
Object.keys(foo);

// 값 목록
Object.values(foo);

// 키 값 존재 여부 확인
'foo' in foo
foo.hasOwnProperty('foo')

// [키, 값] 쌍 목록
Object.entries(foo);

3-7. Map

- 키-값 쌍인 집합

- Map의 key는 모든 값, Object의 key는 string

- Map은 직접 순회 가능, Object는 key, entry, value로 변환 후 순회 가능

- Map의 크기는 .size()로 확인가능하지만, Object는 메소드로 확인 불가능

// 선언
const map1 = new Map()
const myMap = new Map()

// 키, 값 추가
map1.set("a", 1)
map1.set("b", 2)
map1.set("c", 3)

// 접근
console.log(map1.get("a")) // 1

// 값 변경
map1.set("a", 97)

console.log(map1.get("a")) // 97

// 크기
console.log(map1.size) // 3

// 삭제
map1.delete("b");

// 순회
for (const [key, value] of myMap) {
  console.log(`${key} = ${value}`);
}

myMap.forEach((value, key) => {
  console.log(`${key} = ${value}`);
});


// 키 목록
myMap.keys()

// 값 목록
myMap.values()

// [키, 값] 쌍 목록
myMap.entries()

 

3-8. 함수

// 함수 정의
// 파라미터, 반환값은 없을 수도 있음
// return문을 만나면 함수가 즉시 종료됨

// 함수 선언식
function foo(arg1, arg2) {
    return arg2+arg2
}

// 함수를 인자로 사용하는 함수
function calculator(func, a, b) {
    return func(a, b)
}

// 함수 표현식
// 함수를 리턴하는 함수
const onClick = () => () => {
    console.log('hello');
}

// 화살표 함수 - ES6에서 등장
const add = (a, b) => a+b
const print = text => console.log(text)
const addToObj = (a, b) => ({a+b}) // 객체 리턴할 때는 ()로 감싸줘야 함

// 함수를 인자로 사용하는 함수
function calculator(func, a, b) {
    return func(a, b)
}


// 함수 호출
foo(1, 2)
calculator(add, 3, 5)

// 가변인자 사용 - 화살표 함수는 arguments 변수 전달 받지 않음
function main1() {
    console.log(arguments[0])
}

const main2(...args) => console.log(args[0])

main1(1, 2, 3)
main2(1, 2, 3)

 

 

4. 자주 사용하는 함수, 메서드

4-1. 입출력

// 입력
// 입력 파일 가져오기
const fs = require('fs');
let input = fs.readFileSync("/dev/stdin").toString().trim();

// 백준 입력 받기 (여러 줄)
const filePath = process.platform === 'linux' ? '/dev/stdin' : '[입력파일명].txt'?
const input = require('fs').readFileSync(filePath).toString().trim().split('\n');

// 한 줄에 입력값이 여러 개일 때
const input = require('fs').readFileSync(filePath).toString().trim().split(' ');

// 표준 입력
const [x, y] = require("fs").readFileSync(0).toString().trim().split('\n').map(Number);


// 한 줄씩 입력 - fs보다 느림

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.on('line', function(line) {
  //입력받을 값 처리
  console.log(line);

  rl.close();
}).on("close", function() {
  //문제 풀이 로직
  process.exit();
});


// 출력
console.log('print')

 

4-2. 배열 메서드

// 보통 콜백 함수의 첫 번째 인자는 요소, 두 번째 인자는 인덱스

const fruits = [
    {name: 'orange', price: 300},
    {name: 'banana', price: 700},
    {name: 'apple', price: 500},
    {name: 'lemon', price: 1000},
]


// 특정 조건을 만족하는 요소를 찾고 싶을 때 -> 없으면 undefined 반환
배열.find(콜백 함수)

const item = fruits.find((fruit) => {
    if (fruit.price === 500) {
        return true
    }
    return false
})



// 배열에서 특정 조건을 만족하는 요소의 인덱스를 찾고 싶을 때 -> 조건을 만족하는 요소가 없으면 -1을 반환함
배열.findIndex(콜백 함수)

fruits.findIndex((fruit) => {
    if (fruit.price === 500) {
        return true
    }
    return false
})


// 배열에서 특정 조건을 만족하는 요소가 존재하는지 (하나라도 있는지) 확인하고 싶을 때 -> true, false 반환
배열.some(콜백 함수) // 첫 번째 인자는 요소

const doesExists = fruits.some((fruit) => {
    if (fruit.price > 600) {
        return true
    }
    return false
})


// 배열에서 모든 요소가 특정 조건을 만족하는지 확인하고 싶을 때 -> true, false 반환
배열.every(콜백 함수) // 첫 번째 인자는 요소

const isOver = fruits.every((fruit) => {
    if (fruit.price > 100) {
        return true
    }
    return false
})

// 특정 조건을 만족하는 요소로 구성된 배열을 만들고 싶을 때 -> 원본 배열 변경 X
배열.filter(콜백 함수) // 첫 번째 인자는 요소

const cheapFruits = fruits.filter((fruit) => {
    if (fruit.price <= 500) {
        return true
    }
    return false
})


// 원본 배열의 요소를 새로운 형태로 변환하고 싶을 때 -> 원본 배열 변경 X
배열.map(콜백 함수) // 첫 번째 인자는 요소

const priceTags = fruits.map((fruit) => {
    return `${fruit.name}: ${fruit.price}`
})


// 배열 안의 모든 요소르 이용해 하나의 값으로 만들 때 -> 원본 배열 변경 X
배열.reduce(콜백 함수, 초기값) // 첫 번째 인자는 누적된 값, 두 번째 인자는 요소

// 합계
const numbers = [1, 2, 3, 4]
const total = numbers.reduce((accumulator, currentValue) => {
    return accumulator + currentValue
}, 0)


// 최솟값
// reduce의 두 번째 인자 생략 시 초기값이 첫 번째 요소
const numbers = [10, 4, 2, 8]
const smallest = numbers.reduce((accumulator, currentValue) => {
    if (accumulator > currentValue) {
        return currentValue
    }
    return accumulator
})

 

4-3. string 메서드

// 길이 구하기
const str1 = 'javascript '
str1.length

// 문자열 결합
const str2 = 'study'
const str3 = str1.concat(str2)
const str4 = str1+str2


// 특정 위치의 문자열
const str4 = str1.charAt(0); // j
const str5 = str2[3] // d


// 문자열 대체 - 첫번째로 검색된 문자열만 대체하여 새로운 문자열을 반환
const str = 'Hello world';
str.replace('world', 'Lee'); // Hello Lee


// 부분 문자열
// substring(시작 인덱스, 끝 인덱스) - 끝 인덱스 포함되지 않음, 끝 인덱스 생략 시 str.length인 것과 같음, 음수 인자 X
const str6 = str1.substring(1, 4); // ava

// substr(시작 인덱스, 길이) - 시작 인덱스가 음수일 경우 'str.length - 시작 인덱스'로 동작
const str7 = str1.substr(1, 4); // avas

// slice(시작 인덱스, 끝 인덱스) - 끝 인덱스 포함되지 않음, , 끝 인덱스 생략 시 str.length인 것과 같음, 음수 인자 O
const str7 = str.substr(-5); // 'hello world'


// 문자열 검색 - 문자열이 포함되었을 경우 시작 인덱스, 아닐 경우 -1
const str8 = str6.indexOf('a') // 0 -> 문자열 앞에서부터 검색
const str9 = str6.lastIndexOf('a') // 2 -> 문자열 뒤에서부터 검색


// 구분자 기준으로 분해
const str10 = "1,2,3,4,5"
const arr = str.split(",") // ["1", "2", "3", "4", "5"]


// 소문자, 대문자 변경
str1.toUpperCase() // JAVASCRIPT
str1.toLowerCase() // javascript


// 문자열 양쪽 끝 공백 제거
'    foo  '.trim() // foo


// 문자열 반복 -> 0이면 빈 문자열, 정수가 아니면 정수로 변환, 음수이면 오류
'abc'.repeat(2) // abcabc
'abc'.repeat(2.5) // abcabc


// 포함 여부 검사 - 두 번째 인자는 검색 시작할 위치, indexOf로 대체 가능
str.includes('hello') // true
str.includes('hello', 2) // false


// 문자열 길이가 정해진 길이보다 짧다면 특정 문자열로 채우기
'abc'.padStart(10);         // "       abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"


'abc'.padEnd(10);          // "abc       "
'abc'.padEnd(10, "foo");   // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1);           // "abc"


// 문자열 => 아스키코드 - 인자는 인덱스, 생략하면 0

'a'.charCodeAt() // 97
'abc'.charCodeAt(1) // 98


// 아스키코드 => 문자열
String.fromCharCode(72) // H
String.fromCharCode(65, 66, 67) // ABC

 

4-4. Math

  • Math 객체는 정적(static) 속성과 메서드만을 제공함

Math.abs(-1) // 절댓값
Math.round(1.4) // 소수점 이하 반올림
Math.ceil(1.4) // 소수점 이하 올림
Math.floor(1.4) // 소수점 이하 내림 - 음수인 경우 소수점 이하를 떼서 버린 정수 - 1
Math.sqrt(2) // 제곱근
Math.random() // 0 이상 1 미만의 부동 소수점 반환
Math.pow(2, -1) // 거듭제곱 - 첫 번째 인자는 밑, 두 번째 인사는 지수
Math.max(1, 2, 3) // 최댓값
Math.min(1, 2, 3) // 최솟값
Math.log(2) // 자연로그(ln) 값
Math.log2(2) // 2를 밑으로 가지는 로그 값
Math.E // 자연로그의 밑
Math.PI // 원주율

 

4-5. 문자열 포매팅 (템플릿 리터럴)

  • `와 $, {}를 사용해 표현함
//
const sentence = `In JavaScript, template strings can run
 over multiple lines, but double and single
 quoted strings cannot.`
 const name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`;

 

4-6. 형변환

// 타입 확인
typeof "ABC" // string
typeof(1) // number
typeof function() {} // function
typeof 11n // bingint
typeof { name : "Anne"} // object
typeof null // object


// String => int / float
parseInt("101") // 101
parseInt("ff", 16) // 255 -> 16진수를 10진수로 변환
parseFloat("1.1") // 1.1
parseFloat("  3.14   ") // 3.14 -> 선후행 공백 무시
parseFloat("3.14와 숫자가 아닌 문자들") // 3.14
parseFloat("FF2") // NaN -> 공백이 아닌 첫 글자를 숫자로 변환할 수 없는 경우 NaN을 반환


// String => number
Number("12345") // 12345
Number("hi") // NaN



// number/boolean => String
// + 연산자 사용
100 + “점” // "100점"
“10” + false // "100"
100 + "" // "100"

// toString 사용
toString(100) // "100"


// boolean => number
// + 연산자 사용
99 + true // 100
0 + true // 0
Number(true) // 1
Number(false) // 0


// int => Boolean
Boolean(100) // true

 


출처

자바스크립트 입문 강의 - https://school.programmers.co.kr/learn/courses/3/3-hello-javascript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8

MDN Web Docs - https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Grammar_and_types#basics

변수 명명 규칙 - https://ko.javascript.info/variables

const, let, var / 화살표 함수 / 배열 메서드 / 구조분해할당 - https://www.youtube.com/watch?v=_zMVlKxmWHg&list=PLZ5oZ2KmQEYiGLIDlsTQR_wdfCH9ZD6lx

String 메서드 - https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-String-%EB%A9%94%EC%86%8C%EB%93%9C-%E2%9C%8F%EF%B8%8F-%EC%A0%95%EB%A6%AC

아스키코드 <-> 문자열 - https://luvris2.tistory.com/868#google_vignette

아스키코드 <-> 문자열 - https://oliviakim.tistory.com/158

입력 - https://gyyeom.tistory.com/109

입력 - https://velog.io/@pakxe/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EB%B0%B1%EC%A4%80-%EC%9E%85%EB%A0%A5%EB%B0%9B%EB%8A%94-%EB%B0%A9%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC

입력 - https://velog.io/@imysh578/%EB%B0%B1%EC%A4%80-NodeJsJavascript-%EC%9E%85%EB%A0%A5%EA%B0%92-%EB%B0%9B%EB%8A%94-%EB%B0%A9%EB%B2%95

입력 - https://www.acmicpc.net/board/view/108553

Math 메서드 - https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-Math-%EB%A9%94%EC%86%8C%EB%93%9C-%E2%9C%8F%EF%B8%8F-%EC%A0%95%EB%A6%AC#%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8_math_%EB%A9%94%EC%86%8C%EB%93%9C

객체 프로퍼티 추가/수정/삭제 - https://velog.io/@surim014/%EC%9B%B9%EC%9D%84-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EA%B7%BC%EC%9C%A1-JavaScript%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-part-7-Object-35k01xmdfp

2차원 배열 생성 - https://gent.tistory.com/296#google_vignette

배열 생성 - https://tesseractjh.tistory.com/103

객체의 키, 값 목록 - https://velog.io/@james3299/Javascript-%EA%B0%9D%EC%B2%B4-%EB%B0%8F-%EA%B0%9D%EC%B2%B4-%EC%A0%91%EA%B7%BC-%EB%B2%95-%EA%B0%9D%EC%B2%B4-key%EA%B0%92-%EC%B6%9C%EB%A0%A5

객체의 키 값 존재 여부 확인 - https://velog.io/@minong/Javascript-%EA%B0%9D%EC%B2%B4%EC%97%90-%ED%95%B4%EB%8B%B9-key%EA%B0%92%EC%9D%B4-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95

Map - https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Map

타입 확인 - https://hianna.tistory.com/401