1.  자료형 변환

🐝  코틀린에서는 자료형이 다른 변수에 재할당하면 자동 형 변환이 되지 않고 자료형 불일치 오류 Type Mismatch 발생
        ➡️  의도하지 않게 자료형이 변하는 것을 방지하기 위한 것

fun main() {
    val a: Int = 1
    // val b: Double = a // 자료형 불일치 오류 발생. 자바와 차이점.
    // val c: Int = 1.1 // 자료형 불일치 오류 발생
}

 

    🍯  변수 a는 Int 형이므로 Double 형 변수 b에 다시 할당할 수 없음
    🍯  만일 자료형을 변환해 할당하고 싶다면 코틀린에서는 자료형 변환 메서드를 이용
            ex. Int ▶️ Double :  toDouble() 메서드를 이용

fun main() {
    val b: Double = a.toDouble()
    println("a=$a, b=$b") // a = 1, b = 1.0
}

자료형이 서로 다른 값을 연산하면, 자료형이 표현할 수 있는 범위가 큰 자료형으로 자동 형변환하여 연산
fun main() {

    val result = b + a
    println("result = $result") // result = 2.0

}

 


2.  기본형과 참조형의 비교 원리

🐝   자료형을 비교할 때는 단순히 값만 비교하는 방법 vs 참조 주소까지 비교하는 방법
🐝   단순히 값만 비교할 때는 이중 등호(==)를 사용하고, 참조 주소를 비교하려면 삼중 등호(===)를 사용

이중 등호는 참조와 상관없이 값이 동일하면 true를, 값이 다르면 false를 반환
삼중 등호값과 상관없이 참조가 동일하면 true를 반환, 값이 동일하더라도 참조 주소가 다르면 false를 반환

 

다음은 Int 형으로 선언한 변수 a, b에 128을 대입하고 이중 등호와 삼중 등호로 비교한 것
    비교 결과는 모두 true. 이 때 참조형으로 선언된 a와 b는 코틀린 컴파일러가 기본형으로 변환하여 저장
    즉, 여기서는 삼중 등호가 비교하는 값도 저장된 값인 128.
fun main() {

    val a: Int = 128
    val b: Int = 128
    println(a == b) // true
    println(a === b) // true

}

참조 주소가 달라지는 경우는 null을 허용한 변수
    null을 허용한 변수는 같은 값을 저장해도 이중 등호와 삼중 등호를 사용한 결과값이 다름 
fun main() {  

    val c: Int? = 128
    val d: Int? = 128
    println(c == d) // true
    println(c === d)  // false
    
}

3.  스마트 캐스트 알아보기

🐝  만약 어떤 값이 정수일 수도 있고, 실수일 수도 있는 경우에는 그때마다 자료형을 변환해도 되지만
     '스마트 캐스트'를 사용할 수 있음
🐝  스마트 캐스트 Smart Cast컴파일러가 자동으로 형 변환

    ⚡️  대표적으로 스마트 캐스트가 적용되는 자료형은 Number형이 있음
    ⚡️  Number형을 사용하면 숫자를 저장하기 위한 특수한 자료형 객체를 만듦
    ⚡️  Number형으로 정의된 변수에는 저장되는 값에 따라 정수형이나 실수형 등으로 자료형이 변환됨

fun main() {

    var test: Number = 12.2 // 12.2에 의해 test는 Float형으로 스마트 캐스트
    println("$test") // 12.2

    test = 12 // Int 형으로 스마트 캐스트
    println("$test") // 12

    test = 120L // Long형으로 스마트 캐스트
    println("$test") // 120

    test += 12.0f // Float형으로 스마트 캐스트
    println("$test") // 132.0
    
}

4.  자료형 검사하기

🐝  변수의 자료형을 알아낼떄는 is 키워드를 사용
🐝   is는 왼쪽 항의 변수가 오른쪽 항의 자료형과 같으면 true를, 아니면 false를 반환
🐝   is는 변수의 자료형을 검사한 다음 그 변수를 해당 자료형으로 변환하는 기능도 있음

   

    ⚡️ Any형을 사용하면 자료형을 결정하지 않은 채로 변수를 선언할 수 있음
    ⚡️ Any형은 코틀린의 최상위 기본 클래스로 어떤 자료형이라도 될 수 있는 특수한 자료형
    ⚡️ 이때 is를 사용하여 자료형을 검사하면 검사한 자료형으로 스마트 캐스트

fun main() {

    var num = 256

    if (num is Int) { // num의 자료형이 Int라면 실행.
        println(num) // 256
    } else if (num !is Int) {
        println("No an Int")
    }

    val x: Any
    x = "Hello"
    if (x is String) {
        println(x.length) // 5
    }
    /*
    변수 x는 Any형으로 선언. 그 후에 "Hello"라는 값을 대입. 아직 x의 자료형은 Any형.
    이후 if문으로 is로 x의 자료형을 검사할 때 String으로 스마트 캐스트되어 조건문이 실행.
     */

}

 


5.  묵시적 형변환

🐝  코틀린의 모든 클래스는 Any형이라는 슈퍼클래스(Superclass)를 가짐
🐝  Any는 자바의 최상위 클래스인 Object와 비슷하지만 서로 다른 유형
🐝  Any 형은 무엇이든 될 수 있기 때문에 언제든 필요한 자료형으로 자동변환할 수 있음
        ➡️  이것을 묵시적 변환이라고 함

fun main() {

    var a: Any = 1  // Any형 a는 1로 초기화 될 때 Int형이 됨.
    a = "one" // Int형이었던 a는 변경된 값에 의해 String이 됨.
    println("a: $a type: ${a.javaClass}") // 자바의 기본형을 출력하면 String이 나옴
    // 띄어씌기가 있으면 중괄호 생략 가능
    // a: one type: class java.lang.String

    checkArg("Hello") // x is String: Hello
    checkArg(5) // x is Int: 5
}

fun checkArg(x: Any) { // 인자를 Any형으로 받음
    if (x is String) {
        println("x is String: $x")
    }

    if (x is Int) {
        println("x is Int: $x")
    }

}

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  코틀린의 특징

👩🏻‍🚀  안드로이드의 개발 환경은 Java 언어를 중심으로 확장되어 있는데, 새로운 개발 언어를 도입함

👩🏻‍🚀  Kotlin 코틀린은 2017년 안드로이드 공식 개발 언어로 지정
      ▶️ 구글이 코틀린을 안드로이드 공식 언어로 지정한 이유는 코틀린이 현대적인 언어로서 갖는 장점들 때문

👩🏻‍🚀  코틀린은 유명 IDE 제작사인 JetBrain이 만든 언어로, 같은 회사에서 만든 Intellij와 Android Studio IDE에서

       매우 잘 지원되는 언어


1) 코틀린의 장점


코틀린은 자바와 100% 상호 호환되면서도 더 현대적이고 간결 

      ▶️  자바와 코틀린 코드를 하나의 프로젝트에서 섞어서 사용할 수 있음
-  Null Pointer Exception이 발생할 수 있는 코드를 금지하고 Null에 대해 안전한 코드를 간결하게 표현 할 수 있도록 지원
'타입 추론'을 지원

      ▶️  정적 타입 지정 언어가 가진 정확성과 성능을 보장하면서도 동적 타입 언어와 같은 간결함을 유지할 수 있음
'람다 표현식'을 지원  ▶️  코틀린의 람다 표현식은 자바8부터 지원되는 람다 표현식보다 더 간결
'객체 지향' 프로그래밍과 '함수형' 프로그래밍 스타일을 모두 지원
-  코틀린의 '확장 함수'는 기존 클래스의 변경없이 클래스의 기능을 추가하는 것이 가능
-  코틀린은 이미 널리 쓰이는 '코드 패턴을 간결화할 수 있도록 설계'
      ▶️ 예를 들면 'getter', 'setter'를 묵시적으로 제공하고 자주 쓰이는 'Singleton 패턴'은 'object'로서 지원
-  코틀린의 함수는 '일급 객체'로서 다른 함수의 파라미터로 전달 가능하고 함수의 반환값을 함수 형태로 반환할 수 있어

    '높은 추상화'가 가능
-  코틀린은 '불변성을 보장하는 변수'와 '변경이 가능한 변수'를 언어 차원에서 분리
       ▶️  불변값을 갖는 변수의 사용은 '다중 쓰레드 애플리케이션 개발'에 보다 유용

 


2.  변수

👩🏻‍🚀  코틀린은 변수명을 먼저 쓰고, 콜론 : 을 쓴 후 자료형을 명시

👩🏻‍🚀  문맥상 추론이 가능하다면 자료명을 생략할 수 있음 ▶️ 코틀린의 형추론이라고 함

👩🏻‍🚀  변수명만 쓸 때는 자료형을 적어줘야 함

💡  val 변수명: 자료형 = 값
fun main() {

    val pi: Double = 3.14

    val name = "tom" // String 으로 형 추론.

    // val은 값을 변경할 수 없는 변수이므로 값을 재할당 하면 다음과 같이 컴파일 오류가 남
    // pi = 3.141593 // 오류 Val cannot be reassigned

    // 값을 변경하고 싶을 때는 var을 사용해야 됨. var로 정의된 변수는 값을 바꿀 수 있음
    var age = 21 // 형추론 Int
    println(age)  // 21
    age = 25 // 재할당
    println(age) // 25
}

 

1)  변수와 자료형


  👾  변수는 val, var 이라는 키워드를 이용하여 선언
        ⚡️  val로 변수를 선언하면 최초로 지정한 변수의 값으로 초기화하고 더 이상 바꿀 수 없는 '읽기 전용 변수'가 됨
        ⚡️  var로 변수를 선언하면 최초로 지정한 변수의 초깃값이 있더라도 '값을 바꿀 수 있음'

💡  변수를 선언한 예 :
       val username: String = "kildong"
       ➡️  변경되지 않는 변수 username이 String 자료형으로 선언되었고 "kildong"이 값으로 할당

 

  👾  코틀린은 자료형을 지정하지 않고 변수를 선언하면 변수에 할당된 값("kildong")을 보고 알아서 자료형을 지정할 수 있음

         ▶️  이것을 '자료형 추론'이라고 함
         ▶️  즉 username 변수에 "kildong"이라는 값만 할당해도 코틀린이 알아서 이 변수의 자료형을 String으로 지정
  👾  단 자료형을 지정하지 않은 변수는 반드시 자료형을 추론할 값을 지정해야 함
  👾  선언만 하고 초기화를 하지 않을 때는 반드시 자료형을 적어줘야 함

fun main() {
    val number = 10 // number 변수는 Int 형으로 추론
    var language = "Korean" // language 변수는 String으롤 추론.
    language = "English" // var 키워드로 선언한 변수는 값을 다시 할당할 수 있음
    // language = 10 // 파이썬처럼 다른 데이터 타입의 데이터는 저장할 수 없음.
    val secondNumber: Int = 20 // secondNumber 변수는 자료형을 Int로 명시적으로 지정.
    // secondNumber = 30 // val로 선언한 변수를 값을 다시 할당할 수 없음.

    println("number: $number") // 10
    println("language: $language") // English
    println("secondNumber: $secondNumber") // 20
}

 


3.  기본 자료형

👩🏻‍🚀  자료형은 크게 기본 자료형 primitive data type참조 자료형 reference data type으로 나누어 진다.
      ⚡️ 기본형은 순수하게 값을 저장하는 자료형이고 자바에서는 int, byte, boolean 등이 해당
      ⚡️ 참조형은 객체를 만들고 변수에는 객체의 참조값을 저장하고 자바에서는 String, Array 등이 해당

👩🏻‍🚀  코틀린의 자료형은 모두 참조형. 즉 모든 자료형이 객체 형태



(1) 정수 자료형  -  Byte, Short, Int, Long


   👾
  코틀린은 정수의 경우에는 숫자가 작아도 Int 형으로 추론 (기본값)

          ➡️  Byte나 Short 같은 작은 범위를 사용할 때는 자료형을 지정해야 함
 
   1)  Int : 정수


      📍  소수점이 없는 정수값을 지정할 때 사용
      📍  가독성을 높이기 위해 언더바(_)로 자릿수를 구분할 수 있음
      📍  다만, 언더바는 개발자가 읽기 쉽게 하기 위한 것으로 컴퓨터는 동일하게 인식

fun main() {    
    var intValue: Int
    intValue = 3
    intValue = 2_147_483_435
}

 


  2) Long : 정수


     📍  Int 보다 큰 범위의 정수를 저장할 수 있음
     📍  Double과 Float의 관계처럼 Int와 구분하기 위해서 숫자의 끝에 L을 붙여줌
     📍  Long 타입의 경우에는 설정한 글꼴에 따라 숫자가 1과 구분이 어려울 수 있기 때문에 대문자를 사용

fun main() {
    var longValue = 3_141592L
}

 


  3) Short와 Byte : 정수


    📍 정숫값을 저장할 때 사용하는데 입력할 수 있는 값의 크기가 Int보다 작음

fun main() {
    var shortValue: Short = 32_767
    var byteValue: Byte = 127
}

 


(2)  실수 자료형 - Double, Float


  👾  실수의 경우 자료형을 명시하지 않으면 Double 형이 됨 (기본값)

  👾  Float 형으로 지정하고 싶다면 값뒤에 F를 추가로 붙여줌

fun main() {
    val numFloat: Float = 100.0F // 자료형 생략 가능
    val numDouble: Double = 100.0 // 자료형 생략 가능
}


  1) Double : 실수 - 소수점이 있는 값을 저장할 때 사용

fun main() {
    var doubleValue: Double
    doubleValue = 3.141592
}


  2) Float : 실수


    📍 Double과 동일한 용도이지만 더 작은 범위의 숫자를 저장할 때 사용
    📍 Double과 구분하기 위해 Float의 경우 숫자 끝에 'F'를 붙여줌

fun main() {   
    var floatValue: Float
    floatValue = 3.141592F
}

 


(3) 문자 자료형


    👾  문자 자료형은 문자 하나를 표현하는 Char형과 문자열을 표현하는 String형으로 분류
    👾  Char 형은 문자를 작은 따옴표로 감싸고, String형은 문자열을 큰 따옴표로 감쌈

fun main() {
    val char: Char = 'a' // 자료형 생략 가능
    val string: String = "abc" // 자료형 생략 가능
}

(4) 논리 자료형


   👾  참, 거짓을 표현하는 true, false 를 사용하고 주로 조건 검사에 사용

fun main() {
    val isTrue: Boolean = true // 자료형 생략 가능
}

 


4.  상수  const

👩🏻‍🚀  상수는 주로 기준이 되는 변하지 않는 값을 입력해 둘 때 사용

👩🏻‍🚀  읽기 전용 변수인 val 앞에 const 키워드를 붙여서 만듦. 전역으로만 사용가능

💡  val과 같이 읽기 전용인 것은 동일하지만, 컴파일 시에 값이 결정되기 때문에 Int, Long과 같은 기본형과 문자열인 String만 입력할 수 있음
const val PI = 3.141592653589793

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  키보드 이벤트

이벤트 설명
keydown 키가 눌릴 때 실행. 키보드를 꾹 누르고 있을 때도, 입력될 때도 실행
keypress 키가 입력되었을 때 실행. 공백이 들어가기 전까지 글자수를 세지 x
웹 브라우저에 따라서 아시아권의 문자를 제대로 처리 못하는 문제가 있음
keyup 키보드에서 키가 떨어질 때 실행

 

<head>
    <script>
        // 남은 글자수 출력하기
        document.addEventListener('DOMContentLoaded', () => {
            const textarea = document.querySelector('textarea');
            const h1 = document.querySelector('h1');

            textarea.addEventListener('keyup', () => { // 키보드에서 키가 떨어질 때 실행
                // value 속성으로 입력양식(form 태그)의 글자(문자열)을 읽을 수 있음.
                const length = textarea.value.length
                h1.textContent = `글자 수: ${length}`;
            })

            textarea.focus();
        });
    </script>
</head>
<body>
    <h1>글자 수: 0</h1>
    <textarea></textarea>
</body>


2.  키보드 키 코드 사용하기

키보드 이벤트 관련 속성
이벤트 속성 이름 설명
code 입력한 키
keyCode 입력한 키를 나타내는 숫자
altKey Alt 키를 눌렀는지
ctrlKey Ctrl 키를 눌렀는지
shiftKey Shift 키를 눌렀는지 


   👩🏻‍💻  code 속성은 입력한 키를 나타내는 문자열이 들어있고
         altKey, ctrlKey, shiftKey 속성은 해당 키를 눌렀는지 불 자료형이 들어 있음

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const h1 = document.querySelector('h1');
            const print = (event) => {
                let output = '';
                output += `alt: ${event.altKey}<br>`; 
                // 이벤트가 발생하면 불 값을 반환
                output += `ctrl: ${event.ctrlKey}<br>`;
                output += `shift: ${event.shiftKey}<br>`;
                // event.code가 있으면 event.code를 출력하고, 
                // undefined라면 event.keyCode를 출력
                output += `code: ${typeof(event.code) !== 'undefined' ? 
                                   event.code : event.keyCode}<br>`;
                h1.innerHTML = output;
            }

            document.addEventListener('keydown', print); // 키가 눌릴 때 출력
            document.addEventListener('keyup', print); // 키가 떨어질 때 출력
        });
    </script>
</head>
<body>
    <h1></h1>
</body>

 


keyCode 속성 활용 

 

    keyCode 속성은 입력한 키를 숫자로 나타냄. 37, 38, 39, 40이 방향키 왼쪽, 위, 오른쪽, 아래를 나타냄

<head>
    <script>       
        document.addEventListener('DOMContentLoaded', () => {
            // 별의 초기 설정
            const star = document.querySelector('h1');
            star.style.position = 'absolute'; // style 속성을 조작하여 position 값을 설정
            star.style.transitionDuration = '1s';

            // 별의 이동을 출력하는 기능
            let [x, y] = [5, 5];
            const block = 20;
            const print = () => {
                star.style.left = `${x * block}px`;
                star.style.top = `${y * block}px`;
            }
            print();

            // 별을 이동하는 기능
            const [left, up, right, down] = [37, 38, 39, 40]; // 방향키 keyCode를 쉽게 사용하기 위해 변수를 사용해 이름을 붙임.
            document.body.addEventListener('keydown', (event) => { // 키보드 눌릴 때 실행

                switch (event.keyCode) {
                    case left:
                        x -= 1;
                        break;
                    case up:
                        y -= 1;
                        break;
                    case right:
                        x += 1;
                        break;
                    case down:
                        y += 1;
                        break;
                }
                print();
            })

            const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
            const size = [10, 20, 30, 40, 50];
            let index = 0;
            setInterval(() => {
                star.style.color= colors[index++ % colors.length];
                star.style.fontSize = String(size[index++ % size.length]) + 'px'
            }, 500)

        });
    </script>
</head>
<body>
    <h1>★</h1>
</body>

 


3.  이벤트 발생 객체

(1) 이벤트 리스너를 외부로 빼낸 경우

<script>
const listener = (event) => {
    const length = textarea.value.length
    h1.textContent = `글자 수: ${length}`
}

document.addEventListener('DOMContentLoaded', () => { // 외부로 분리
    const textarea = document.querySelector('textarea')
    const h1 = document.querySelector('h1')
    textarea.addEventListener('keyup', listener)
})    
</script>

 

  👩🏻‍💻  코드의 규모가 커지면 위와 같이 이벤트 리스너를 외부로 분리하는 경우가 많아짐

 

이벤트를 발생시킨 객체에 접근하는 방법

 

    📌  event.currentTarget 속성을 사용

         - () => {} 와 function () {} 형태 모두 사용 가능

    📌  this 키워드를 사용

         - 화살표 함수가 아닌 function () {} 형태로 함수를 선언한 경우에 사용

 

currentTarget 속성 사용
<script>
const listener = (event) => {
    const length = event.currentTarget.value.length
    // event.currentTarget이 textarea
    h1.textContent = `글자 수: ${length}`
}

document.addEventListener('DOMContentLoaded', () => { // 외부로 분리
    const textarea = document.querySelector('textarea')
    const h1 = document.querySelector('h1')
    textarea.addEventListener('keyup', listener)
})    
</script>

 

this 키워드 사용
<script>
const listener = function (event) {
    const length = this.value.length
    // this가 textarea
    h1.textContent = `글자 수: ${length}`
}

document.addEventListener('DOMContentLoaded', () => { // 외부로 분리
    const textarea = document.querySelector('textarea')
    const h1 = document.querySelector('h1')
    textarea.addEventListener('keyup', listener)
})    
</script>

 


 

(2) 글자 입력 양식 이벤트

 

  👩🏻‍💻  입력 양식 form : 사용자로부터 어떠한 입력을 받을 때 사용하는 요소

            ex. input, textarea, button, select

 

입력 양식을 기반으로 inch를 cm 단위로 변환하는 프로그램
<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const input = document.querySelector('input');
            const btn = document.querySelector('button');
            const p = document.querySelector('p');

            btn.addEventListener('click', () => {
               const inch = Number(input.value); // 입력한 값을 숫자로 변환.

                if (isNaN(inch)) { // 숫자가 아니라면 바로 리턴. isNaN()함수 : 숫자인지 확인. not a number
                    p.textContent = '숫자를 입력해주세요.';
                    return;
                }
               // 변환해서 출력
               const cm = inch * 2.54;
               p.textContent = `${cm}cm`;
            });
        });
    </script>
</head>
<body>
    <input type="text">inch<br>
    <button>계산</button>
    <p></p>
</body>

 


 

이메일 형식 확인하기
<head>
    <script>

        document.addEventListener('DOMContentLoaded', () => {
           const input = document.querySelector('input');
           const p = document.querySelector('p');

           const isEmail = (value) => { // 이메일인지 검사하는 함수
               // 골뱅이를 갖고 있고 && 골뱅이 뒤에 점이 있다면
               return (value.indexOf('@' > 1) && (value.split('@')[1].indexOf('.') > 1));
           };

           input.addEventListener('keyup', function (event) {
               const value = event.currentTarget.value;
               // const value = input.value; 가능
               // console.log(value);

               if (isEmail(value)) {
                   p.style.color = 'green';
                   p.textContent = `이메일 형식입니다: ${value}`;
               }
               else {
                   p.style.color = 'red';
                   p.textContent = `이메일 형식이 아닙니다: ${value}`;
               }
           });
        });
    </script>
</head>
<body>
    <input type="text">
    <p></p>
</body>

 

 

[ 내용참고 : IT 학원 강의 및 책 '혼자 공부하는 자바스크립트' ]


1.  이벤트 설정하기

🐝  이벤트 : 사이트에서 방문자가 취하는 모든 행위
         ▶️  모든 문서 객체는 생성되거나 클릭되거나 마우스를 위에 올리거 나 할 때 이벤트가 발생
🐝  이벤트 핸들러 (= 이벤트 리스너) : 이벤트가 발생했을 때 실행되는 코드 (함수)

 

(1) 이벤트 핸들러 등록 종류


  1) 이벤트 핸들러 어트리뷰트 방식

 

    👩🏻‍💻 이벤트 핸들러 어트리뷰트는 onclick과 같이 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 이루어져 있음
    👩🏻‍💻  주의할 점은 이벤트 핸들러 어트리뷰트 값으로 함수 참조가 아닌 함수 호출문 등의 문을 할당
           ➡️  이때 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미
           ➡️  하지만 HTML과 자바 스크립트는 관심사가 다르므로 혼재하는 것보다 분리하는 것이 좋음

<body>
    <button onclick="sayHi('Lee')">Click me!</button>
    <script>
        function sayHi(name) {
            console.log(`Hi! ${name}`); // Hi! Lee
        }
    </script>
</body>

 



  (2) 이벤트 핸들러 프로퍼티 방식

 

    👩🏻‍💻  window 객체와 Document, HTMLElement 타입의 DOM 노드 객체는 이벤트에 대응하는

          이벤트 핸들러 '프로퍼티'를 가지고 있음
    👩🏻‍💻  이벤트 핸들러 프로퍼티의 키는 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 이루어져 있음
    👩🏻‍💻  이벤트 핸들러 프로퍼티 방식은 이벤트 핸들러 어트리뷰트 방식의 HTML과 자바 스크립트가 뒤섞이는 문제를 해결할 수 있음
           ▶️ 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있다는 단점이 있음

<body>
    <button>Click me!</button>
    <script>
        const btn = document.querySelector('button');
        console.log(typeof btn); // object

        // 이벤트 핸들러 프로퍼티에 이벤트 핸들러 바인딩.
        // 첫 번째로 바인딩 된 이벤트 핸들러는 두 번째 바인딩된 이벤트 핸들러에 의해 재할당되어 실행되지 않음.
        btn.onclick = function () {
            console.log('button click 1');
        };

        btn.onclick = function () {
            console.log('button click 2');
        };
    </script>
</body>

 


 

  (3)  addEventListener 메서드 방식

 

문서 객체.addEventListener(이벤트 이름, 콜백 함수)
      ⚡️  첫 번째 매개변수에는 이벤트의 종류를 나타내는 문자열인 이벤트 타입을 전달
             ( 이 때 이벤트 핸들러 프로퍼티 방식과는 달리 on 접두사를 붙이지 않음 )
      ⚡️  두 번째 매개변수에는 이벤트 핸들러를 전달


     👩🏻‍💻  DOM Level 2에서 도입된 Event.prototype.addEventListener 메서드를 사용하여 이벤트 핸들러를 등록할 수 있음
     👩🏻‍💻  '이벤트 핸들러 어트리뷰트 방식'과 '이벤트 핸들러 프로퍼티 방식'은 DOM Level 0 부터 제공되던 방식

<body>
    <button>Click me!</button>
    <script>
        const btn = document.querySelector('button');

        btn.addEventListener('click', function () {
            console.log('button click 1');
        });  // 재활용 불가

        function btnClick () {
            console.log('button click 2');
        } // 재활용 가능

        btn.addEventListener('click', btnClick);

    </script>
</body>

이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식을 모두 사용하여 이벤트 핸들러를 등록하면?

 

    addEventListener 메서드 방식은 이벤트 핸들러 프로퍼티에 바인딩된 이벤트 핸들러에 아무 영향을 주지 않음
    따라서 버튼 요소에서 클릭 이벤트가 발생하면 2개의 이벤트 핸들러가 모두 호출

<body>
    <button>Click me!</button>
    <script>
        const btn = document.querySelector('button');
        
        // 이벤트 핸들러 프로퍼티 방식.
        btn.onclick = function () {
            console.log('[이벤트 핸들러 프로퍼티 방식] button click');
        }

        // addEventListener 메서드 방식.
        btn.addEventListener('click', function () {
            console.log('[addEventListner 메서드 방식] button click');
        });
    </script>

</body>

💫  addEventListener 메서드는 동일한 요소에서 발생한 동일한 이벤트에 대해 하나 이상의 이벤트 핸들러를 등록할 수 있음
<body>
<button>Click me!</button>
<script>
    const btn = document.querySelector('button');

    function btnClick () {
            console.log(`button click`);
    }

    btn.addEventListener('click', btnClick);
    btn.addEventListener('click', btnClick);

</script>
</body>

응용 예제
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>

        document.addEventListener('DOMContentLoaded', () => {
            const h1 = document.querySelector('h1');
            let counter = 0;

            h1.addEventListener('click', function (event) { 
            // h1 태그에 click 이벤트가 발생할 때 실행할 함수.
                counter++;
                h1.textContent = `클릭 횟수: ${counter}`;
            });
        });
    </script>
    <style>
        h1 { /* 클릭을 여러 번 했을 때 글자가 선택되는 것을 막기 위한 스타일. 드래그가 안됨 */
            user-select: none;
            cursor: pointer;
        }
    </style>

</head>
<body>
    <h1>클릭 횟수: 0</h1>
</body>


(2) 이벤트 핸들러 제거

 

문서 객체.removeEventListener(이벤트 이름, 콜백 함수)

 

  👩🏻‍💻  addEventListener 메서드로 등록한 이벤트 핸들러를 제거하려면

        EventTarget.prototype.removeEventListener() 메서드를 사용 
  👩🏻‍💻  removeEventListener 메서드에 전달할 인수는 addEventListener 메서드와 동일
  👩🏻‍💻  addEventListener 메서드에 전달한 인수와 removeEventListener 메서드에 전달한 인수가 일치하지 않으면
        이벤트 핸들러가 제거되지 않음  ➡️ 
익명함수를 이벤트 핸들러로 등록한 경우에는 제거할 수 없음
       ⚡️ 단, 기명 이벤트 핸들러 내부에서 removeEventListener 메서드를 호출하여 이벤트 핸들러를 제거하는 것은 가능
            (이때 이벤트 핸들러는 단 한 번만 호출)

<body>
    <button>Click me!</button>
    <script>
        const btn = document.querySelector('button');

        const handleClick = () => console.log('button click');

        // 이벤트 핸들러 등록
        btn.addEventListener('click', handleClick);

        // 이벤트 핸들러 제거
        btn.removeEventListener('click', handleClick, true); // 실패
        btn.removeEventListener('click', handleClick); // 성공
    </script>
</body>

기명 이벤트 핸들러 내부에서 removeEventListener 메서드를 호출하여 이벤트 핸들러를 제거
<body>
<button>Click me!</button>
<script>

    const btn = document.querySelector('button');

    btn.addEventListener('click', function foo() {
        console.log('button click');

        // 이벤트 핸들러를 제거. 따라서 이벤트 핸들러는 단 한 번만 호출.
        btn.removeEventListener('click', foo);
    });

</script>
</body>

 

프로퍼티 방식으로 이벤트 핸들러 등록한 경우 removeEventListener 메서드로 제거 불가

 

    💫  프로퍼티에 'null' 할당하여 제거

<body>
    <button>Click me!</button>
    <script>
        const btn = document.querySelector('button');
        const handleClick = () => console.log('button click');

        // 이벤트 핸들러 프로퍼티 방식으로 이벤트 핸들러 등록
        btn.onclick = handleClick;

        // removeEventListener 메서드로 이벤트 핸들러를 제거할 수 없음.
        btn.removeEventListener('click', handleClick);

        // 이벤트 핸들러 프로퍼티에 null을 할당하여 이벤트 핸들러를 제거.
        btn.onclick = null;
    </script>
</body>

예제

 

<body>
    <button id="changeBtn">배경색 변경</button>
    <button id="resetBtn">배경색 초기화</button>
    <script>
        const changeBtn = document.querySelector('#changeBtn');
        const resetBtn = document.querySelector('#resetBtn');

        let i = 0;

        const bodyTag = document.querySelector('body');
        const color = ['red', 'orange', 'yellow', 'blue'];
        const handleColor = () => {
            bodyTag.style.backgroundColor = color[i++ % color.length];
        }

        changeBtn.addEventListener('click', handleColor);
        resetBtn.addEventListener('click', () => {
            changeBtn.removeEventListener('click', handleColor);
            bodyTag.style.backgroundColor = '';
        })
    </script>
</body>

 


응용 예제
<head>
    <script>

        document.addEventListener('DOMContentLoaded', () => {
            const h1 = document.querySelector('h1');
            const btnConnect = document.getElementById('connect');
            const btnDisconnect = document.getElementById('disconnect');
            const p = document.querySelector('p');

            let counter = 0;
            let isConnect = false; // 이벤트를 여러 번 연결되지 않게

            const listener = (event) => { // 이벤트를 제거하려면 이벤트 리스너를 변수 또는 상수로 가지고 있어야 함.
                h1.textContent = `클릭 횟수: ${counter++}`;
            }

            btnConnect.addEventListener('click', function () {
                if (isConnect === false) {
                    // 1. h1에 이벤트 리스너 연결
                    h1.addEventListener('click', listener);

                    // 2. btnConnect 버튼 안 보이도록
                    this.style.display = 'none';
                    btnDisconnect.style.display = 'block';

                    // 3. p의 내용을 '이벤트 연결 상태 : 연결'로 변경
                    p.textContent = '이벤트 연결 상태: 연결';

                    isConnect = true;
                }
            });

            btnDisconnect.addEventListener('click', function () {
                if (isConnect === true) {
                    // 1. h1에 이벤트 리스너 해제
                    h1.removeEventListener('click', listener);

                    // 2. btnDisconnect 버튼 안 보이도록
                    btnConnect.style.display = 'block'
                    this.style.display = 'none';

                    // 3. p의 내용을 '이벤트 연결 상태 : 해제'로 변경
                    p.textContent = '이벤트 연결 상태: 해제';

                    isConnect = false;
                }
            })

        });
    </script>
    <style>
        h1 {
            user-select: none;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>클릭 횟수: 0</h1>
    <button id="connect">이벤트 연결</button>
    <button id="disconnect">이벤트 제거</button>
    <p>이벤트 연결 상태: 해제</p>
</body>

 

 

 

 

 

 

 

[ 내용참고 : IT 학원 강의 및 책 '혼자 공부하는 자바스크립트' ]


1.  글자 조작하기

속성 이름 설명
문서 객체.textContent 입력된 문자열을 그대로 넣음
문서 객체.innerHTML 인력된 문자열을 HTML 형식으로 넣음

 

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const a = document.getElementById('a');
            const b = document.querySelector('#b'); // 아이디 선택자 사용.

            a.textContent = '<h1>textContent 속성</h1>';
            b.innerHTML = '<h1>innerHTML 속성</h1>';
        })
    </script>
</head>
<body>
    <div id="a"></div>
    <div id="b"></div>
</body>


2.  스타일 조작하기

 

👩🏻‍💻  자바스크립트의 style 속성들의 이름이 CSS에서 사용할 때와 차이가 없음
👩🏻‍💻  자바스크립트에서는 -가 연산자이고, 식별자에 사용할 수 없어서 두 단어의 조합은 캐멀 케이스로 표현

CSS 속성 이름 자바스크립트 style 속성 이름
background-color backgroundColor
text-align textAlign
font-size fontSize

 

💡  스타일을 조정하는 방법
       h1.style.backgroundColor      ▶️ 이 형태를 가장 많이 사용
        h1.style['backgroundColor']
        h1.style['background-color'

 

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const divs = document.querySelectorAll('body > div');

            divs.forEach((div, index) => { // div 개수 만큼 반복 출력.
                console.log(div, index);
                const val = index * 10; // index는 0부터 24까지 반복.
                div.style.height = `10px`; // 크기를 지정할 때는 반드시 단위를 붙여줘야 함.
                div.style.backgroundColor = `rgba(${val}, ${val}, ${val})`;
            })
        });
    </script>
</head>
<body>
    <!-- div 태그 25개 -->
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
</body>

 


3.  속성 조작하기

👩🏻‍💻  문서 객체의 속성을 조작할 때는 다음과 같은 메소드를 사용

메소드 이름 설명
문서 객체.setAttribute(속성 이름, 값) 특정 속성에 값을 지정
문서 객체.getAttribute(속성 이름) 특정 속성을 추출

 

<head>    
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const rects = document.querySelectorAll('.rect'); // 클래스 선택자 이용.

           // for (let i = 0; i < rects.length; i++) {
           //     const width = (i + 1) * 100; // 100, 200, 300, 400의 너비를 가짐.
           //     const src = `http://placebear.com/${width}/250`;
           //     rects[i].setAttribute('src', src); // src 속성에 값을 지정.
           // }

           // foreach() 메서드로 동일한 작업
           rects.forEach((item, index) => {
               const width = (index + 1) * 100;
               const src = `http://placebear.com/${width}/250`;
               item.setAttribute('src', src); // src 속성에 값을 지정.
           })

        })
    </script>
</head>
<body>
    <img class="rect" alt="" src="">
    <img class="rect" alt="" src="">
    <img class="rect" alt="" src="">
    <img class="rect" alt="" src="">
</body>


예제
<head>
    <script>
        /*
        버튼을 클릭하면 버튼에 있는 색상으로 과일 목록이 글자색이 변하도록
         */
        document.addEventListener('DOMContentLoaded', () => {
            const btns = document.querySelectorAll('div button');
            const fruits = document.querySelectorAll('ul li');
            const colors = ['red', 'orange', 'yellow'];

            btns.forEach((item, index) => {
                item.addEventListener('click', () => {
                    fruits.forEach((item) => {
                        item.style.color = colors[index];
                    })
                })
            })

        })
    </script>
</head>
<body>
    <ul>
        <li id="apple">Apple</li>
        <li id="banana">Banana</li>
        <li id="orange">Orange</li>
    </ul>
    <div>
        <button>red</button>
        <button>orange</button>
        <button>yellow</button>
    </div>
</body>

 


4.  문서 객체 생성하기

document.createElement(문서 객체 이름)
부모 객체.appendChild(자식 객체)

    

📌  createElement()
    

    -  body 태그 내부에 있는 특정 문서 객체를 읽어들이고 조작하는 것도 가능하나 문서 객체를 생성하는 것도 가능
    -  문서 객체를 생성하고 싶을 때는 document.createElement() 메소드를 사용

📌  appendChild()


   -  문서 객체를 만든 후에는 '문서 객체를 추가'해야 함
   -  appendChild() 메소드를 활용하면 부모 객체 아래에 자식 객체를 추가할 수 있음
    

<script>
    // createElement() 메소드로 h1 태그를 생성하고, appendChild()를 사용하여 객체를 추가.
    document.addEventListener('DOMContentLoaded', () => {
        // 문서 객체 생성하기
        const header = document.createElement('h1');

        // 생성한 태그 조작하기
        header.textContent = '문서 객체 동적으로 생성하기';
        header.setAttribute('data-custom', '사용자 정의 속성');
        header.style.color = 'white';
        header.style.backgroundColor = 'black';
        console.log(header);

        // h1 태그를 body 태그 아래에 추가하기
        document.body.appendChild(header);
    })
</script>

 


5.  문서 객체 이동하기

👩🏻‍💻  appendChild() 메소드를 사용하면 문서 객체를 이동할 수도 있음
        ▶️  문서 객체를 다른 문서 객체에 추가하면 문서 객체가 이동

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // 문서 객체 읽어들이고 생성하기
            const divA = document.querySelector('#first'); // id 속성이 first인 태그를 반환
            const divB = document.getElementById('second'); // id 속성이 second인 태그를 반환

            const h1 = document.createElement('h1'); // h1 태그를 생성
            h1.textContent = '이동하는 h1 태그';

            // 서로 번갈아가면서 실행하는 함수를 구현
            const toFirst = () => {
                divA.appendChild(h1); // h1을 divA에 추가
                setTimeout(toSecond, 1000); // 1초 후에 toSecond() 함수를 실행
            }
            const toSecond = () => {
                divB.appendChild(h1); // h1을 divB에 추가
                setTimeout(toFirst, 1000);
            }
            toFirst();
        });
    </script>
</head>
<body>
    <div id="first">
        <h1>첫 번째 div 태그 내부</h1>
    </div>
    <hr>
    <div id="second">
        <h1>두 번째 div 태그 내부</h1>
    </div>

</body>

 


6.  문서 객체 제거하기

👩🏻‍💻  문서 객체를 제거할 때는 removeChild() 메소드를 사용

부모 객체.removeChild(자식 객체);

 

👩🏻‍💻  appendChild() 메소드 등으로 부모 객체와 이미 연결이 완료된 문서 객체의 경우 parentNode 속성으로
      부모 객체에 접근할 수 있으므로, 일반적으로 어떤 문서 객체를 제거할 때는 다음과 같은 형태의 코드 사용

 문서 객체.parentNode.removeChild(문서 객체);

 

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(() => {
                const h1 = document.querySelector('h1');
                h1.parentNode.removeChild(h1);
            }, 3000);
        })
    </script>
</head>
<body>
   <hr>
   <h1>제거 대상 문서 객체</h1>
   <hr>
</body>


예제

 

<head>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }
    </style>
    <script>
        /*
        1. 버튼을 클릭하면 무지개색 li가 추가
        2. 같은 색깔의 li는 2개가 될 수 없음
         */
        document.addEventListener('DOMContentLoaded', () => {
            const btn = document.querySelector('button');
            const ulTag = document.querySelector('ul');
            const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

            let cnt = 0;
            btn.addEventListener('click', function () {
                const liTag = document.createElement('li');

                liTag.textContent = colors[cnt % colors.length];
                liTag.style.backgroundColor = colors[cnt++ % colors.length];
                liTag.style.color = 'white';

                if (cnt > colors.length) {
                    const firstLi = document.querySelector('ul li:nth-child(1)');
                    firstLi.parentNode.removeChild(firstLi);
                }
                ulTag.appendChild(liTag);
            });
        });
    </script>
</head>
<body>
    <button>무지개색 추가</button>
    <ul>

    </ul>
</body>

 

 

 

 

[ 내용참고 : IT 학원 강의 및 책 '혼자 공부하는 자바스크립트' ]


1.  형변환 Casting

· 변수 또는 상수의 타입을 다른 타입으로 변환하는 것

· 형변환 방법 ; (타입)피연산자

· float 타입의 값을 int 타입으로 변환할 때 소수점 이하의 값은 버림으로 처리됨

 

    ⚡️  자동 형변환 : 값의 범위가 작은 타입에서 큰 타입으로의 형변환은 생략가능

    ⚡️  명시적 형변환 : 변수의 자료형을 강제로 바꾸는 것 ▶️ 숫자크기 상관없이 앞에 형변환 해줘야 함

 
<기본형의 자동 형변환이 가능한 방향>

 

public class Etc0208 {

	public static void main(String[] args) {
		
		byte b = 10;
		int i = b;
	
		System.out.println("i=" + i); // 10
		System.out.println("b=" + b); // 10
		
		int i2 = 10;
		byte b2 = (byte)i2;

		System.out.println("i2=" + i2); // 10
		System.out.println("b2=" + b2); // 10

	}
}

 

public class Exma001 {

	public static void main(String[] args) {

		byte a = 127; // byte가 가질 수 있는 최대값
		int b = a; // 자동 형변환 (byte -> int)
		System.out.println(b); // 127
		
		float c = b; // 자동형변환 (int -> float) 값손실이 없기 때문.
		System.out.println(c); // 127.0
		
		//int d = c; // float 자료형을 int 변수에 대입하면 자동형변환이 안됨.

	}

}

 


문자열을 기본 타입으로 강제 타입 변환


  📌  String은 int나 char처럼 기본 자료 타입이 아닌 클래스의 객체 형태라서 일반적인 형변환과 다름

public class MyVar_09 {
    public static void main(String[] args) {
		
		String str = "1a";
		//int value = Integer.parseInt(str); // 문자열에 숫자외의 문자가 있으면 변환이 되지 않음
		
		// String -> byte
		String str1 = "10";
		byte value1 = Byte.parseByte(str1);
		System.out.println(value1); // 10
		
		// String -> short
		String str2 = "200";
		short value2 = Short.parseShort(str2);
		System.out.println(value2); // 200
		
		// String -> int
		String str3 = "300000";
		int value3 = Integer.parseInt(str3);
		System.out.println(value3); // 300000
		
		// String -> long
		String str4 = "400000000";
		long value4 = Long.parseLong(str4);
		System.out.println(value4); // 400000000
		
		// String -> float
		String str5 = "12.345";
		float value5 = Float.parseFloat(str5);
		System.out.println(value5); // 12.345
		
		// String -> double
		String str6 = "12.345";
		double value6 = Double.parseDouble(str6);
		System.out.println(value6); // 12.345
		
		// String -> boolean
		String str7 = "true";
		boolean value7 = Boolean.parseBoolean(str7);
		System.out.println(value7); // true
		
	}
}

 


기본 타입과 문자열 간의 변환


  📌  기본 타입의 값을 문자열로 변경하는 경우에는 String.valueOf() 메소드를 이용

String str = String.valueOf(기본타입값);
public class MyVar_10 {
	public static void main(String[] args) {
		
		// 문자열을 기본 타입으로 변환
		int value1 = Integer.parseInt("10");
		double value2 = Double.parseDouble("3.14");
		boolean value3 = Boolean.parseBoolean("true");
		
		System.out.println("value1 : " + value1); // value1 : 10
		System.out.println("value2 : " + value2); // value2 : 3.14
		System.out.println("value3 : " + value3); // value3 : true
		
		// 기본 타입을 문자열로 변환
		String str1 = String.valueOf(10);
		String str2 = String.valueOf(3.14);
		String str3 = String.valueOf(false);
		
		System.out.println("str1 : " + str1); // str1 : 10
		System.out.println("str2 : " + str2); // str2 : 3.14
		System.out.println("str3 : " + str3); // str3 : false
	}
}

 


연습 문제

 

   📌  'int result = '에 1) 변수 4개를 모두 사용하고 2) 사칙연산중 + 연산만 사용해서 9가 나오도록 코드를 완성

public class Ex_01 {
	public static void main(String[] args) {
		
		long var1 = 2L;
		float var2 = 1.8f;
		double var3 = 2.5;
		String var4 = "3.9";
		
		int result = (int)var1 + (int)(var2 + var3) + (int)Double.parseDouble(var4); 
        // 이 부분만 수정. 0대신 코드 입력.
		System.out.println(result); // 9
	}
}

 


2.  연산자  Operator

 

· 연산자 : 연산을 수행하는 기호

· 피연산자(operand) : 연산자의 작업 대상 (변수, 상수, 리터럴, 수식)

· 단항 연산자의 우선순위가 이항 연산자보다 높다.

· 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽

 

 

(1)  산술 연산자

 

    a) 사칙 연산자 :  +(덧셈), -(뺄셈), *(곱셈), /(나눗셈)

    b) 나머지 연산자 :  %  ▶️ a를 b로 나눈 후 나머지를 반환, 주로 짝수 홀수 구분에 사용

public class Exam004 {
	public static void main(String[] args) {
		
		int num1 = 10; // 10을 대입
		int num2 = 3; // 3을 대입
		
		System.out.println(num1 + num2); // 13
		System.out.println(num1 - num2); // 7
		System.out.println(num1 * num2); // 30
		System.out.println(num1 / num2); // 3
		System.out.println(num1 % num2); // 1
		System.out.println(num1 % 2); // 0
		System.out.println(num2 % 2); // 1
	}
}

 


 

(2) 논리부정 연산자 : !

 

    -  피연산자가 true이면 false를, false면 true를 결과로 반환

public class Exam006 {
	public static void main(String[] args) {
		
		boolean a = true;
		boolean b = false;
		boolean c = !b; // b의 값을 반대로 바꾸어 입력
	
		System.out.println(a); // true
		System.out.println(!a); // false
		
		System.out.println(b); // false
		System.out.println(c); // true
	}
}

 


(3) 정수와 실수 연산

 

    - 정수와 정수의 연산의 결과 값은 정수. 정수와 실수 연산의 결과 값은 실수 

public class MyVar_11 {
	public static void main(String[] args) {

		byte kor, math, eng;
		kor = 55;
		math = 99;
		eng = 87;
		
		int sum = kor + math + eng;
		System.out.println("총점 : " + sum); // 총점 : 241
		
		int avg_01 = sum /3;
		System.out.println("평균 : " + avg_01); // 평균 : 80
		
		double avg_02 = sum /3; // 정수 / 정수
		System.out.println("평균 : " + avg_02); // 평균 : 80.0 -> 정수 연산으로 정수 값이 나온 후에 실수에 저장.
		
		double avg_03 = (double)sum / 3; // 실수 / 정수
		System.out.println("평균 : " + avg_03); // 평균 : 80.33333333333333
		
		double avg_04 = sum / 3.0; // 정수 / 실수
		System.out.println("평균 : " + avg_04); // 평균 : 80.33333333333333
		
		double avg_05 = (double)sum / 3.0; // 실수 / 실수
		System.out.println("평균 : " + avg_05); // 평균 : 80.33333333333333
		
	}
}

 

 

 

 

[ 내용 참고 : IT 학원 강의 및 자바의 정석 3rd ]

+ Recent posts