28. Number

표준 빌트인 객체 Number는 원시 타입인 숫자를 다룰 때 유용한 프로퍼티와 메서드를 제공함

28.1 Number 생성자 함수

표준 빌트인 객체인 Number 객체는 생성자 함수 객체이므로, new 연산자와 함께 호출하여 Number 인스턴스를 생성할 수 있음

Number 생성자 함수에 인수를 전달하지 않고 new 연산자와 함께 호출하면 [[NumberData]] 내부 슬롯에 0을 할당한 Number 래퍼 객체 생성

const numObj = new Number();
console.log(numObj); // Number {[[PrimitiveValue]]: 0}

[[PrimitiveValue]] 라는 접근할 수 없는 프로퍼티가 있는데 이는 [[NumberData]] 내부 슬롯을 가리킴 (ES5에서는 이렇게 불렀음)

Number 생성자 함수의 인수로 숫자를 전달하면서 new 연산자와 함께 호출하면 [[NumberData]] 내부 슬롯에 인수로 전달받은 숫자를 할당한 Number 래퍼 객체를 생성

const numObj = new Number(10);
console.log(numObj); // Number {[[PrimitiveValue]]: 10}

Number 생성자 함수의 인수로 숫자가 아닌 값을 전달하면 인수를 숫자로 강제 변환한 후, [[NumberData]] 내부 슬롯에 변환된 숫자를 할당한 Number 래퍼 객체 생성. 숫자로 변환할 수 없으면 NaN 할당한 Number 래퍼 객체 생성

let numObj = new Number('10');
console.log(numObj); // {[[PrimitiveValue]]: 10}

numObj = new Number('Hello');
console.log(numObj); // {[[PrimitiveValue]]: NaN}

new 연산자를 사용하지 않고 Number 생성자 함수를 호출하면 Number 인스턴스가 아닌 숫자를 반환. 이를 이용해서 명시적으로 타입을 변환하기도 함

// 문자열 타입 => 숫자 타입
Number('0'); // -> 0
Number('-1'); // -> -1
Number('10.53'); // -> 10.53

// 불리언 타입 => 숫자 타입
Number(true); // -> 1
Number(false); // -> 0

28.2 Number 프로퍼티

28.2.1 Number.EPSILON (ES6)

Number.EPSILON 은 1과 1보다 큰 숫자 중에서 가장 작은 숫자와의 차이와의 차이와 같음

부동소수점 산술 연산은 정확한 결과를 기대하기 어렵기 때문에, Number.EPSILON으로 부동소수점으로 인해 발생하는 오차를 해결하기 위해 사용

0.1 + 0.2 === 0.3; // false -> 0.30000000000000004

function isEqual(a, b){
  // a와 b를 뺀 값의 절대값이 Number.EPSLION 보다 작으면 같은 수로 인정
  return Math.abs(a - b) < Number.EPSLION;
}

isEqual(0.1 + 0.2, 0.3); // true 

28.2.2 Number.MAX_VALUE

Number.MAX_VALUE 는 자바스크립트에서 표현할 수 있는 가장 큰 양수 값으로 Number.MAX_VALUE 보다 큰 숫자는 Infinity

Number.MAX_VALUEl // -> 1.7976931348623157e+308
Infinity > Number.MAX_VALUE; // -> true

28.2.3 Number.MIN_VALUE

Number.MIN_VALUE 는 자바스크립트에서 표현할 수 있는 가장 작은 양수 값으로 Number.MIN_VALUE 보다 작은 숫자는 0

Number.MIN_VALUE; // -> 5e-324
Number.MIN_VALUE > 0; // -> true

28.2.4 Number.MAX_SAFE_INTEGER

Number.MAX_SAFE_INTEGER 는 자바스크립트에서 안전하게 표현할 수 있는 가장 큰 정수값

Number.MAX_SAFE_INTEGER; // -> 9007199254740991

28.2.5 Number.MIN_SAFE_INTEGER

Number.MIN_SAFE_INTEGER 는 자바스크립트에서 안전하게 표현할 수 있는 가장 작은 정수값

Number.MIN_SAFE_INTEGER; // -> -9007199254740991

28.2.6 Number.POSITIVE_INFINITY

Number.POSITIVE_INFINITY 는 양의 무한대를 나타내는 숫자값 Infinity와 같음

Number.POSITIVE_INFINITY; // -> Infinity

28.2.7 Number.NEGATIVE_INFINITY

Number.NEGATIVE_INFINITY 는 음의 무한대를 나타내는 숫자값 -Infinity와 같음

Number.NEGATIVE_INFINITY; // -> -Infinity

28.2.8 Number.NaN

Number.NaN 은 숫자가 아님(Not-a-Number) 를 나타내는 숫자값으로 Number.NaN은 window.NaN과 같음

Number.Nan; // -> NaN

28.3 Number 메서드

28.3.1 Number.isFinite (ES6)

Number.isFinite 정적 메서드는 인수로 전달한 숫자값이 정상적인 유한수, 즉 Infinity나 -Infinity가 아닌지 검사하고 그 결과를 불리언 값으로 반환

// 인수가 정상적인 유한수면 true 반환
Number.isFinite(0); // -> true
Number.isFinite(Number.MAX_VALUE); // -> true
Number.isFinite(Number.MIN_VALUE); // -> true

// 인수가 무한수이면 false 반환, 인수가 NaN이면 언제나 false 반환
Number.isFinite(Infinity); // -> false
Number.isFinite(-Infinity); // -> false
Number.isFinite(NaN); // -> false

빌트인 전역 함수 isFinite 는 전달받은 인수를 숫자로 암묵적 타입 변환하여 검사를 수행하나, Number.isFinite은 전달받은 인수를 암묵적 타입 변환하지 않고 숫자가 아닌 인수가 주어졌을 때 반환값은 언제나 false

// Number.isFinite 는 인수를 숫자로 암묵적 타입 변환하지 않음
Number.isFinite(null); // -> false

// isFinte 는 인수를 숫자로 암묵적 타입 변환. null은 0으로 암묵적 타입 변환됨
isFinite(null); // -> true

28.3.2 Number.isInteger (ES6)

Number.isInteger 정적 메서드는 인수로 전달된 숫자값이 정수인지 검사하여 그 결과를 불리언 값으로 반환, 암묵적 타입 변환 안함

// 인수가 정수이면 true 반환
Number.isInteger(0); // -> true
Number.isInteger(123); // -> true
Number.isInteger(-123); // -> true

// 암묵적 타입 변환 안함
Number.isInteger('123'); // -> false

28.3.3 Number.isNaN (ES6)

Number.isNaN 정적 메서드는 인수로 전달된 숫자값이 NaN인지 검사하여 그 결과를 불리언 값으로 반환

// 인수가 NaN이면 true 반환
Number.isNaN(NaN); // -> true

빌트인 전역 함수 isNaN은 전달받은 인수를 숫자로 암묵적 타입 변환하여 검사를 수행하나, Number.isNaN 메서드는 전달받은 인수를 숫자로 암묵적 타입 변환하지 않고 숫자가 아닌 인수가 주어졌을 때 반환값은 언제나 false

// Number.isNaN은 인수를 숫자로 암묵적 타입 변환하지 않음
Number.isNaN(undefined); // -> false

// isFinte는 인수를 숫자로 암묵적 타입 변환. undefined는 NaN으로 암묵적 타입 변환됨
isNaN(undefined); // -> true

28.3.4 Number.isSafeInteger (ES6)

Number.isSafeInteger 정적 메서드는 인수로 전달된 숫자값이 안전한 정수인지 검사하여 그 결과를 불리언 값으로 반환. 안전한 정수값은 -(253 -1)과 253 - 1 사이의 정수값. 인수를 숫자로 암묵적 타입 변환하지 않음

Number.isSafeInteger(0); // -> true
Number.isSafeInteger(1000000000000000); // -> true
Number.isSafeInteger(10000000000000000); // -> false
Number.isSafeInteger(0.5); // false
Number.isSafeInteger('123'); // false

28.3.5 Number.prototype.toExponential

toExponential 메서드는 숫자를 지수 표기법으로 변환하여 문자열로 반환. 지수 표기법이란 매우 크거나 작은 숫자를 표현할 때 주로 사용하며 e(Exponent) 앞에 있는 숫자에 10의 n승을 곱하는 형식으로 수를 나타내는 방식. 인수로 소수점 이하로 표현할 자릿수 전달할 수 있음

(77.1234).toExponential(); // -> '7.71234e+1'
(77.1234).toExponential(4); // -> '7.7123e+1'

숫자 리터럴과 함께 Number 프로토타입 메서드를 사용할 경우 에러가 발생함

77.toExponential(); // SyntaxError: Invalid or Unexpected token

자바스크립트 엔진은 숫자 뒤의 . 을 부동 소수점 숫자의 소수 구분 기호로 해석하기 때문에 77.toExponential()에서 77은 래퍼 객체가 되어 toExponential을 프로퍼티로 해석할 수 없으므로 에러 발생.

77.1234.toExponential(); // -> '7.71234e+1'

숫자의 소수점은 하나만 존재하므로 두 번째 . 은 프로퍼티 접근 연산자로 해석. 숫자 리터럴과 함께 메서드를 사용할 경우 혼란을 방지하기 위해 그룹 연산자를 사용할 것을 권장

28.3.6 Number.prototype.toFixed

toFixed 메서드는 숫자를 반올림하여 문자열로 반환. 반올림하는 소수점 이하 자릿수를 나타내는 0 ~ 20 사이의 정수값을 인수로 전달할 수 있고, 인수 생략하면 기본값 0이 지정됨

(12345.6789).toFixed(); // -> '12346'
(12345.6789).toFixed(1); // -> '12345.7' 

28.3.7 Number.prptotype.toPrecision

toPrecision 메서드는 인수로 전달받은 전체 자릿수까지 유효하도록 나머지 자릿수를 반올림하여 문자열로 반환. 인수로 전달받은 전체 자릿수로 표현할 수 없는 경우 지수 표기법으로 결과 반환. 전체 자릿수를 나타내는 0 ~ 21 사이의 정수값을 인수로 전달할 수 있고, 생략하면 기본값 0 지정

(12345.6789).toPrecision(); // -> '12345.6789'
(12345.6789).toPrecision(1); // -> '1e+4'
(12345.6789).toPrecision(6); // -> '12345.7'

28.3.8 Number.prototype.toString

toString 메서드는 숫자를 문자열로 변환하여 반환. 진법을 나타내는 2~36 사이의 정수값을 인수로 전달할 수 있고, 생략하면 기본값 10진법 지정

(10).toString(); // -> '10'
(16).toString(2); // -> '10000'

29. Math

표준 빌트인 객체인 Math는 수학적인 상수와 함수를 위한 프로퍼티와 메서드를 제공. Math는 생성자 함수가 아니기 때문에 정적 프로퍼티와 정적 메서드만 제공함

29.1 Math 프로퍼티

29.1.1 Math.PI

원주율 PI값(π = 3.14...) 반환

Math.PI; // -> 3.141592653589793

29.2 Math 메서드

29.2.1 Math.abs

Math.abs 메서드는 인수로 전달된 숫자의 절대값을 반환. 절대값은 반드시 0 또는 양수여야함

Math.abs(-1); // -> 1
Math.abs(''); // -> 0
Math.abs(null); // -> 0
Math.abs(undefined); // -> NaN

29.2.2 Math.round

Math.round 메서드는 인수로 전달된 숫자의 소수점 이하를 반올림한 정수 반환

Math.round(1.4); // -> 1
Math.round(-1.4); // -> -1
Math.round(); // NaN

29.2.3 Math.ceil

Math.ceil 메서드는 인수로 전달된 숫자의 소수점 이하를 올림한 정수 반환

Math.ceil(1.4); // -> 2
Math.ceil(-1.4); // -> -1

29.2.4 Math.floor

Math.floor 메서드는 인수로 전달된 숫자의 소수점 이하를 내림한 정수 반환

Math.floor(1.9); // -> 1
Math.floor(-1.9); // -> -2

29.2.5 Math.sqrt

Math.sqrt 메서드는 인수로 전달된 숫자의 제곱근 반환

Math.sqrt(9); // -> 3
Math.sqrt(-9); // -> NaN
Math.sqrt(0); // -> 0

29.2.6 Math.random

Math.random 메서드는 임의의 난수(랜덤한 숫자) 반환. Math.random 메서드가 반환한 난수는 0에서 1 미만의 실수(0포함 1제외)

const random = Math.floor((Math.random() * 10) + 1);
console.log(random); // 1에서 10 범위의 정수

29.2.7 Math.pow

Math.pow 메서드는 첫 번째 인수를 밑으로, 두 번째 인수를 지수로 거듭제곱한 결과 반환

Math.pow(2, 8); // -> 256
Math.pow(2, -1); // -> 0.5
Math.pow(2); // -> NaN

Math.pow 메서드 대신 ES7에서 도입된 지수 연산자를 사용하면 가독성이 더 좋음

2 ** 2 ** 2; // -> 16
Math.pow(Math.pow(2, 2), 2); // -> 16

29.2.8 Math.max

Math.max 메서드는 전달받은 인수 중에서 가장 큰 수 반환. 인수가 전달되지 않으면 -Infinity 반환

Math.max(1); // -> 1
Math.max(1, 2); // -> 2
Math.max(); // -> -Infinity

배열을 인수로 전달받아 배열의 요소 중에서 최대값을 구하려면 Function.prototype.apply 메서드 또는 스프레드 문법 사용

Math.max.apply(null, [1, 2, 3]); // -> 3
Math.max(...[1, 2, 3]); // -> 3

29.2.9 Math.min

Math.min 메서드는 전달받은 인수 중에서 가장 작은 수 반환. 인수가 전달되지 않으면 Infinity 반환

Math.min(1); // -> 1
Math.min(1, 2); // -> 1
Math.min(); // -> Infinity

배열을 인수로 전달받아 배열의 요소 중에서 최소값을 구하려면 Function.prototype.apply 메서드 또는 스프레드 문법 사용

Math.min.apply(null, [1, 2, 3]); // -> 1
Math.min(...[1, 2, 3]); // -> 1

30. Date

표준 빌트인 객체인 Date는 날짜와 시간을 위한 메서드를 제공하는 빌트인 객체이면서 생성자 함수.

UTC(협정 세계시) : 국제 표준시. GMT(그리니치 평균시)와는 일상에서 혼용되어 사용하나 기술적인 표기에서는 UTC 사용

KST(한국 표준시): UTC + 9시간

30.1 Date 생성자 함수

Date는 생성자 함수이며 Date 생성자 함수로 생성한 Date 객체는 내부적으로 날짜와 시간을 나타내는 정수값을 가짐. 이 값은 1970년 1월 1일 0시를 나타내는 Date 객체를 0으로 두고 계산된 값. 현재 날짜와 시간이 아닌 다른 날짜와 시간을 다루고 싶은 경우 Date 생성자 함수에 명시적으로 해당 날짜와 시간 정보를 인수로 지정

Date 생성자 함수로 객체를 생성하는 방법은 4가지가 있음

30.1.1 new Date()

Date 생성자 함수인수 없이 new 연산자와 함께 호출하면 현재 날짜와 시간을 가지는 Date 객체를 반환. Date 객체는 내부적으로 날짜와 시간을 나타내는 정수값을 갖지만 Date 객체를 콘솔에 출력하면 기본적으로 날짜와 시간 정보를 출력

new Date(); // -> Mon Jun 27 2022 17:38:23 GMT+0900 (한국 표준시)

Date 생성자 함수를 new 연산자 없이 호출하면 Date 객체를 반환하지 않고 날짜와 시간 정보를 나타내는 문자열 반환

Date(); // -> 'Mon Jun 27 2022 17:38:23 GMT+0900 (한국 표준시)'

30.1.2 new Date(milliseconds)

Date 생성자 함수숫자 타입의 밀리초를 인수로 전달하면 1970년 1월 1일 0시를 기점으로 인수로 전달된 밀리초만큼 경과한 시간을 나타내는 Date 객체 반환

new Date(0); // -> Thu Jan 01 1970 09:00:00 GMT+0900 (한국 표준시)
new Date(86400000); // -> Fri Jan 02 1970 09:00:00 GMT+0900 (한국 표준시)

30.1.3 new Date(dateString)

Date 생성자 함수날짜와 시간을 나타내는 문자열을 인수로 전달하면 지정된 날짜와 시간을 나타내는 Date 객체 반환. 이 때 인수로 전달한 문자열은 Date.parse 메서드에 의해 해석 가능한 형식이어야 함

new Date('May 26, 2020 10:00:00'); // -> Tue May 26 2020 10:00:00 GMT+0900 (한국 표준시)
new Date('2020/03/26/10:00:00'); // -> Thu Mar 26 2020 10:00:00 GMT+0900 (한국 표준시)

30.1.4 new Date(year, month[, day, hour, minute, second, millisecond])

Date 생성자 함수연, 월, 일, 시, 분, 초, 밀리초를 의미하는 숫자를 인수로 전달하면 지정된 날짜와 시간을 나타내는 Date 객체 반환. 연, 월은 반드시 지정해야 하고, 지정하지 않은 옵션 정보는 0 또는 1로 초기화

인수내용
year연을 나타내는 1900년 이후의 정수. 0부터 99는 1900부터 1999로 처리됨
month월을 나타내는 0 ~ 11까지의 정수(주의: 0부터 시작, 0 = 1월)
day일을 나타내는 1 ~ 31까지의 정수
hour시를 나타내는 0 ~ 23까지의 정수
minute분을 나타내는 0 ~ 59까지의 정수
second초를 나타내는 0 ~ 59까지의 정수
millisecond밀리초를 나타내는 0 ~ 999까지의 정수

연, 월을 지정하지 않은 경우 1970년 1월 1일 0시를 나타내는 Date 객체 반환

new Date(2020, 2); // -> Sun Mar 01 2020 00:00:00 GMT+0900 (한국 표준시)
new Date(2020, 2, 26, 10, 00, 00, 0); // -> Thu Mar 26 2020 10:00:00 GMT+0900 (한국 표준시)

30.2 Date 메서드

30.2.1 Date.now

1970년 1월 1일 00:00:00(UTC)을 기점으로 현재 시간까지 경과한 밀리초를 숫자로 반환

Date.now(); // -> 1656331097888

30.2.2 Date.parse

1970년 1월 1일 00:00:00(UTC)을 기점으로 인수로 전달된 지정 시간(new Date(dateString)의 동일한 형식)까지의 밀리초를 숫자로 반환

// UTC
Date.parse('Jan 2, 1970 00:00:00 UTC'); // -> 86400000
// KTC
Date.parse('Jan 2, 1970 00:00:00'); // -> 54000000

30.2.3 Date.UTC

1970년 1월 1일 00:00:00(UTC)을 기점으로 인수로 전달된 지정 시간까지의 밀리초를 숫자로 반환. new Date(year, month[, day, hour, minute, second, millisecond]) 형식

Date.UTC(1970, 0, 2); // -> 86400000
Date.UTC('1970/1/2'); // -> NaN

30.2.4 Date.prototype.getFullYear

Date 객체의 연도를 나타내는 정수 반환

new Date('2020/07/24').getFullYear(); // -> 2020

30.2.5 Date.prototype.setFullYear

Date 객체에 연도를 나타내는 정수 설정. 연도 이외에 옵션으로 월, 일 설정 가능

const today = new Date();

// 년도 지정
today.setFullYear(2000);
today.getFullTear(); // -> 2000

// 년도/월/일 지정
today.setFullYear(1900, 0, 1);
today.getFullYear(); // -> 1900

30.2.6 Date.prototype.getMonth

Date 객체의 월을 나타내는 0 ~ 11의 정수를 반환. 1월은 0, 12월은 11

new Date('2020/07/24').getMonth(); // -> 6

30.2.7 Date.prototype.setMonth

Date 객체에 월을 나타내는 0 ~ 11의 정수를 설정. 월 이외에 옵션으로 일도 설정 가능

const today = new Date();

// 월 지정
today.setMonth(0); // 1월
today.getMonth(); // -> 0

// 월/일 지정
today.setMonth(11, 1); // 12월 1일
today.getMonth(); // -> 11

30.2.8 Date.prototype.getDate

Date 객체의 날짜(1 ~ 31)를 나타내는 정수를 반환

new Date('2020/07/24').getDate(); // -> 24

30.2.9 Date.prototype.setDate

Date 객체에 날짜(1 ~ 31)를 나타내는 정수를 반환

const today = new Date();

// 날짜 지정
today.setDate(1);
today.getDate(); // -> 1

30.2.10 Date.prototype.getDay

Date 객체의 요일(0 ~ 6)를 나타내는 정수 반환. 일요일이 0, 토요일이 6

new Date('2020/07/24').getDay(); // -> 5

30.2.11 Date.prototype.getHours

Date 객체의 시간(0 ~ 23)을 나타내는 정수 반환

new Date('2020/07/24/12:00').getHours(); // -> 12

30.2.12 Date.prototype.setHours

Date 객체에 시간(0 ~ 23)을 나타내는 정수 설정. 시간 이외에 옵션으로 분, 초, 밀리초도 설정할 수 있음

const today = new Date();

// 시간 지정
today.setHours(7);
today.getHours(); // -> 7

// 시간/분/초/밀리초 지정
today.setHours(0, 0, 0, 0); // 00:00:00:00
today.getHours(); // -> 0

30.2.13 Date.prototype.getMinutes

Date 객체의 분(0 ~ 59)을 나타내는 정수 반환

new Date('2020/07/24/12:30').getMinutes(); // -> 30

30.2.14 Date.prototype.setMinutes

Date 객체에 분(0 ~ 59)을 나타내는 정수 설정. 분 이외에 옵션으로 초, 밀리초도 설정 가능

const today = new Date();

// 분 지정
today.setMinutes(50);
today.getMinutes(); // -> 50

// 분/초/밀리초 지정
today.setMinutes(5, 10, 999); // HH:05:10:999
today.getMinutes(); // 5

30.2.15 Date.prototype.getSeconds

Date 객체에 초(0 ~ 59)를 나타내는 정수 반환.

30.2. 16 Date.prototype.setSeconds

Date 객체에 초(0 ~ 59)를 나타내는 정수 설정. 초 이외에 옵션으로 밀리초도 설정 가능

30.2.17 Date.prototype.getMilliseconds

Date 객체의 밀리초(0 ~ 999)를 나타내는 정수 반환

30.2.18 Date.prototype.setMilliseconds

Date 객체에 밀리초(0 ~ 999)를 나타내는 정수 설정

30.2.19 Date.prototype.getTime

1970년 1월 1일 0시를 기점으로 Date 객체의 시간까지 경과된 밀리초 반환

new Date('2020/07/24/12:30').getTime(); // -> 1595561400000

30.2.20 Date.prototype.setTime

Date 객체에 1970년 1월 1일 0시를 기점으로 경과된 밀리초 설정

30.2.21 Date.prototype.getTimezoneOffset

UTC와 Date 객체에 지정된 locale 시간과의 차이를 분 단위로 반환. KST - 9H = UTC

const today = new Date(); // today의 지정 로캘은 KST

today.getTimezoneOffset(); // -540

30.2.22 Date.prototype.toDateString

사람이 읽을 수 있는 형식의 문자열로 Date 객체의 날짜를 반환

const today = new Date('2020/7/24/12:30');
today.toString(); // -> 'Fri Jul 24 2020 12:30:00 GMT+0900 (한국 표준시)'
today.toTimeString(); // -> '12:30:00 GMT+0900 (한국 표준시)'

30.2.23 Date.prototype.toTimeString

사람이 읽을 수 있는 형식으로 Date 객체의 시간을 표현한 문자열 반환

30.2.24 Date.prototype.toISOString

ISO 8601 형식으로 Date 객체의 날짜와 시간을 표현한 문자열 반환

const today = new Date('2020/7/24/12:30');
today.toString(); // -> 'Fri Jul 24 2020 12:30:00 GMT+0900 (한국 표준시)'
today.toISOString(); // -> '2020-07-24T03:30:00.000Z'

30.2.25 Date.prototype.toLocaleString

인수로 전달한 로캘을 기준으로 Date 객체의 날짜와 시간을 표현한 문자열 반환. 인수를 생략한 경우 브라우저가 동작 중인 시스템의 로캘 적용

const today = new Date('2020/7/24/12:30');
today.toString(); // -> 'Fri Jul 24 2020 12:30:00 GMT+0900 (한국 표준시)'
today.toLocaleString(); // -> '2020. 7. 24. 오후 12:30:00'
today.toLocaleString('ko-KR'); // -> '2020. 7. 24. 오후 12:30:00'
today.toLocaleString('en-US'); // -> '7/24/2020, 12:30:00 PM'

30.2.26 Date.prototype.toLocaleTimeString

인수로 전달한 로캘을 기준으로 Date 객체의 시간을 표현한 문자열을 반환. 인수를 생략한 경우 브라우저가 동작 중인 시스템의 로캘 적용

profile
frontend developer

0개의 댓글