1.  오브젝트  Object

👩🏻‍💻  오브젝트 object를 사용하면 클래스를 생성자로 인스턴스화 하지 않아도 블록 안의 프로퍼티와 메서드를 호출해서 사용할 수 있다.  ➡️ 자바의 static과 같은 역할
👩🏻‍💻  object 코드 블록 안의 프로퍼티와 메서드는 클래스명에 점 연산자를 붙여서 생성자 없이 직접 호출
👩🏻‍💻  주의할 점은 클래스명을 그대로 사용하기 때문에 호출하는 클래스명의 첫 글자가 대문자


1) 컴패니언 오브젝트


  👾  일반 클래스에 object 기능을 추가하기 위해서 사용
  👾  클래스 코드 안에 companion object 블록으로 감싸주면 생성 과정 없이 오브젝트처럼 사용할 수 있음

object Cat {
    var name: String ="pinky"
    fun printName() {
        println("Cat의 이름은 ${name}입니다.")
    }
}

class Dog {
    companion object {
        var name: String = "None"
        fun printName() {
            println("Dog 이름은 ${name}입니다.")
        }
    }
    fun walk() {
        println("Dog가 뛰어갑니다.")
    }
}

fun main() {
    Cat.name = "mikey"
    Cat.printName() // Cat의 이름은 mikey 입니다.

    // companion object 안의 코드 사용하기
    Dog.name = "Linda"
    Dog.printName() // Dog 이름은 Linda 입니다.
    //Dog.walk() companion object 밖에 선언된 메소드는 사용하지 못함

    // companion object 밖의 코드 사용하기
    val dog = Dog()
    dog.walk() // Dog가 뛰어갑니다.

}

 


2.  데이터 클래스

👩🏻‍💻  코틀린은 간단한 값의 저장 용도로 데이터 클래스 data class를 제공

data class 클래스 이름 (val 파라미터1: 타입, var 파라미터2: 타입)


  ✏️  데이터 클래스를 정의할 때 class 앞에 data 키워드를 사용해야 하고, 생성자 파라미터 앞에 입력하는 var 또는 val 키워드를 생략할 수 없음
  ✏️  생성하는 코드는 일반 클래스와 동일하게 작성

fun main() {
    // 정의 - 주로 코드 블록(클래스 스코프)을 사용하지 않고 간단하게 작성.
    data class UserData(val name: String, var age: Int)

    // 생성 - 일반 class의 생성자 함수를 호출하는 것과 동일
    val userData = UserData("Michael", 21)

    //userData.name = "Cindy" // val로 선언되었기 때문에 변경 불가능

    userData.age = 19 // var로 선언되었기 때문에 변경 가능
}

1) toString() 메소드와 copy() 메소드


  👾  일반 클래스에서 toString() 메소드를 호출하면 인스턴스의 주소값을 반환하지만, 데이터 클래스는 값을 반환하기 때문에 실제 값을 모니터링 할 때 좋음

    println("userData는 ${userData}") // userData는 UserData(name=Michael, age=19)
    println(userData.toString()) // UserData(name=Michael, age=19)

 

  👾  copy() 메소드로 간단하게 값을 복사할 수 있음

    var newData: UserData = userData.copy()

 

  👾  이처럼 클래스와 사용법이 동일하지만 주로 네트워크를 통해 데이터를 주고 받거나, 혹은 로컬 앱의 데이터베이스에서 데이터를 다루기 위한 용도로 사용

 

데이터 클래스는 toString(), copy()와 같은 메소드를 자동으로 만들어줌
    * toString() : 객체에 포함되어 있는 데이터를 출력하여 볼 수 있음. 생성자에 포함된 프로퍼티만 출력.
    * copy() : 객체의 속성들을 복사하여 반환하는 메소드이며 인수로 받은 프로퍼티만 해당 값으로 바뀌어 복사해줌
fun main() {

    data class Memo(val title: String, val content: String, var isDone: Boolean)
    val memo1 = Memo("마트 가기", "계란, 우유, 빵", false)

    val memo2 = memo1.copy(content="칫솔, 과자") // content 프로퍼티만 변경
    println(memo1.toString()) // Memo(title=마트 가기, content=계란, 우유, 빵, isDone=false)
    println(memo2.toString()) // Memo(title=마트 가기, content=칫솔, 과자, isDone=false)

}

 

 


3.  클래스의 상속과 확장

1) 클래스의 상속

 

👩🏻‍💻  상속을 사용하면 부모 클래스의 프로퍼티를 마치 내 클래스의 일부처럼 사용할 수 있음

      상속을 사용하는 예를 들면 안드로이드는 Activity라는 클래스가 미리 만들어져 있고, 이 Activity 클래스 내부에는 글자를 쓰는 기능, 그림을 그리는 기능, 화면에 새로운 창을 보여주는 기능이 미리 정의되어 있음. 상속이 있기에 이런 기능을 직접 구현하지 않고 Activity 클래스를 상속받아 약간의 코드만 추가하면 앱에 필요한 기능을 추가할 수 있음.

 

👩🏻‍💻  코틀린에서 클래스를 상속받으려면 부모 클래스에 open 키워드를 추가해야 함

       ✓ 상속을 받을 자식 클래스에서는 콜론을 이용해서 상속할 부모 클래스를 지정 .다음에 괄호를 입력해서 꼭 부모의 생성자를 호출

    open class 상속될 부모 클래스 {
        // 코드
    }

    class 자식 클래스 : 부모 클래스() {
        // 코드
    }


👩🏻‍💻  메소드도 자식 클래스에서 오버라이딩하려면 부모 클래스의 메소드에 open 키워드를 추가

fun main() {

    open class Flower {
        open fun waterFlower() {
            println("water flower")
        }
    }
    // 콜론 : 을 이용해 상속을 나타내면 됨
    class Rose: Flower() {
        override fun waterFlower() {
            super.waterFlower()
            println("Rose is water flower")
        }
    }

    val rose = Rose() // 객체 생성
    rose.waterFlower() // Rose is water flower

}

 



A. 생성자가 있는 클래스의 상속


👩🏻‍💻  상속될 부모 클래스의 생성자에 파라미터가 있다면 자식 클래스를 통해 값을 전달할 수 있음

   open class 상속될 부모 클래스(value : String) {
        // 코드
    }

    class 자식 클래스(value : String) : 부모 클래스(value) {
        // 코드
    }


   👾 부모 클래스에 세컨더리 생성자가 있다면, 자식 클래스의 세컨더리 생성자에서 super 키워드로 부모 클래스에 전달할 수 있음
   👾 부모 클래스의 세컨더리 생성자를 이용하는 경우에는 부모 클래스명 다음에 오는 괄호를 생략

 


B. 부모 클래스의 프로퍼티와 메서드 사용하기


👩🏻‍💻  부모 클래스에서 정의된 프로퍼티와 메서드는 내 것처럼 사용할 수 있음

    open class Parent {
        var hello: String = "안녕하세요"
        fun sayHello() {
            println(hello)
        }
    }

    class Child: Parent() {
        fun myHello() {
            hello = "Hello"
            sayHello()
        }
    }

    val child = Child()
    child.myHello() // Hello

2) 익스텐션


코틀린은 클래스, 메서드, 프로퍼티에 대해 익스텐션 extensions 을 지원
이미 만들어져 있는 클래스에 다음과 같은 형태로 추가할 수 있음

    fun 클래스.확장할 메서드() {    // 코드    }*

 

    👾  상속이 미리 만들어져 있는 클래스를 가져다 쓰는 개념이라면 익스텐션은 미리 만들어져 있는 클래스에 메서드를 넣는 개념
           ✓ 자신이 만든 클래스에 사용하기 보다는 이미 컴파일되어 있는 클래스에 메서드를 추가하기 위한 용도로 사용
    👾  실제 클래스의 코드가 변경되는 것은 아니며 단지 실행 시에 점 연산자로 호출해서 사용할 수 있도록 해줌

아래 코드는 기본 클래스인 String에 plus 메서드를 확장하는 코드
test 함수에 선언한 original에 문자열을 입력했기 때문에 original은 String의 익스텐션 메서드인 plus를 호출해서 사용
fun main() {

    fun String.addText(word: String) : String {
        return this + word
    }

    fun testStringExtension() {
        val original = "Hello"
        val added = "Guys~"

        println("added를 더한 값은 ${original.addText(added)}입니다.")
    }

    testStringExtension() // added를 더한 값은 HelloGuys~입니다.

}

 

 

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


1.  클래스  Class

간단하게 보면 클래스는 변수와 함수의 모음. 그룹화할 수 있는 변수와 함수를 한 군데에 모아 놓고 사용하기 쉽게 이름을 붙여놓은 것을 클래스 라고 이해하면 된다.


1) 코틀린에서 사용되는 클래스의 기본 구조

    class 클래스 이름 {
       var 변수
       fun 함수() {
           코드
       }
   }


 2) 클래스 코드 작성하기


👻  클래스를 만들기 위해서는 먼저 클래스의 이름을 정하고, 이름 앞에 class 키워드를 붙여서 만들 수 있음
👻  클래스 이름 다음에는 클래스의 범위를 지정하는 중괄호({}) 가 있어야 함
        ➡️  이 중괄호를 스코프 Scope라고 하는데, 클래스에서 사용했기 때문에 클래스 스코프라고 함

 class 클래스 이름 {  // 클래스 스코프 class scope   }


    ✓  '몇몇 예외'는 있지만 대부분의 코드는 클래스 스코프 안에 작성.

 

👻  작성된 클래스를 사용하기 위해서는 생성자라고 불리는 함수가 호출되어야 함

👻  코틀린은 프라이머리 primary와 세컨더리 Secondary 2개의 생성자를 제공


2. 생성자

1) 프라이머리 생성자

 class Person 프라이머리 생성자() { }


함수에 있는 코드를 실행하려면 함수를 호출해야 함. 클래스도 마찬가지로 클래스를 사용한다는 것은 곧 클래스라는 이름으로 묶여 있는 코드를 실행하는 것이기 때문에 함수 형태로 제공되는 생성자를 호출해야지만 클래스가 실행

  ⚡️ 프라이머리 Primary 생성자는 마치 클래스의 헤더처럼 사용할 수 있으며, constructor 키워드를 사용해서 정의하는데 조건에 따라 생략할 수 있음
  ⚡️ 프라이머리 생성자도 결국 함수이기 때문에 파라미터를 사용할 수 있음

   class Person constructor(value: String) {    // 코드   }

 

  📍 생성자에 접근 제한자나 다른 옵션이 없다면 constructor 키워드를 생략할 수 있음

  class Person(value: String) {   // 코드   }

 
   📍 클래스의 생성자가 호출되면 init 블록의 코드가 실행되고, init 블록에서는 생성자를 통해 넘어온 파라미터에 접근할 수 있음

fun main() {
    class Person1 constructor (str: String) {
        init {
            println("생성자로부터 전달받은 값은 ${str}입니다.")
        }
    }
    var person1 = Person1("1") // 생성자로부터 전달받은 값은 1입니다.
}

 

  📍   init 초기화 작업이 필요하지 않으면 init 블록을 작성하지 않아도 됨
          ➡️ 대신 파라미터로 전달된 값을 사용하기 위해서는 파라미터 앞에 변수 키워드인 val을 붙여주면 클래스 스코프 전체에서 해당 파라미터를 사용할 수 있음

fun main() {
    class Person2(val str: String) {
        fun process() {
            println("생성자로 생성된 변수의 값은 ${str}입니다.")
        }
    }

    var person2 = Person2("2")
    person2.process() // 생성자로 생성된 변수의 값은 2입니다.
}

    

   ✓ 생성자 파라미터 앞에 var도 사용할 수 있으나, 읽기 전용인 val을 사용하는 것을 권장


 2) 세컨더리 생성자


세컨더리 Secondary 생성자는 constructor 키워드를 마치 함수처럼 클래스 스코프 안에 직접 작성할 수 있음
그리고 다음과 같이 init 블럭을 작성하지 않고 constructor 다음에 괄호를 붙여서 코드를 작성할 수 있음

    class Person3 {
        constructor(str: String) {
            println("생성자로부터 전달받은 값은 ${str}입니다.")
        }
    }

 

  📍 세컨더리 생성자는 파라미터의 개수, 또는 파라미터의 타입이 다르면 (오버로딩) 여러 개를 중복해서 만들 수 있음

fun main() {
    class Sample {
        constructor(str: String) {
            println("생성자로부터 전달받은 값은 ${str}입니다.")
        }

        constructor(value: Int) {
            println("생성자로부터 전달받은 값은 ${value}입니다.")
        }

        constructor(value1: Int, value2: String) {
            println("생성자로부터 전달받은 값은 ${value1}, ${value2}입니다.")
        }
    }

    val sample = Sample(1,"2") // 생성자로부터 전달받은 값은 1, 2입니다.
}

3) Default 생성자


생성자를 작성하지 않을 경우 파라미터가 없는 프라이머리 생성자가 하나 있는 것과 동일

fun main() {
    class Student { // 생성자를 작성하지 않아도 기본 생성자가 동작.
        init {
            // 기본 생성자가 없더라도 초기화가 필요하면 여기에 코드를 작성.
        }
    }
}

 


 3.  클래스의 사용

클래스명()

 

👻  클래스의 이름에 괄호를 붙여서 클래스의 생성자를 호출. constructor 키워드를 호출하지 않음
    ⚡️ 아무런 파라미터 없이 클래스명에 괄호에 붙여주면 생성자가 호출되면서 init 블록 안의 코드가 자동으로 실행
    ⚡️ 세컨더리 생성자의 경우 init 블록이 먼저 실행되고 constructor 블록 안의 코드가 실행

👻  코틀린에서는 자바보다 클래스를 좀 더 편하게 정의할 수 있음
    ⚡️ 자바의 보일러 플레이트 코드가 삭제되고 필요한 코드들만 작성하면 되도록 바뀜
         * 보일러 플레이트 코드 : 상용구 코드. 변형이 거의 또는 전혀 없이 여러 위치에서 반복되는 코드 문구.

fun main() {
   
    class Car(val color: String)

    /*
    객체를 생성. 자바에서는 new 키워드를 사용했지만, 코틀린에서는 필요가 없음.
     */
    var car = Car("blue") // 객체 생성

}

 

 

 

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


1. 셋  Set

👩🏻‍💻  셋은 중복을 허용하지 않는 리스트로 리스트와 유사한 구조이지만 인덱스로 조회할 수 없고, get() 메서드도 지원하지 않음

👩🏻‍💻  같은 값을 추가하더라도 해당 값은 하나만 저장
👩🏻‍💻  리스트와 마찬가지로 읽기 전용 셋과 읽기 쓰기 모두 가능한 셋, 총 두가지를 제공
       ➡️  각각 setOf(), mutableSetOf() 함수로 객체를 생성

    // 읽기 전용 셋
    val immutableSet = setOf(1, 1, 2, 2, 2, 3, 3)
    println(immutableSet) // [1, 2, 3]

 1) 빈 셋으로 초기화하고 값 입력하기

    var set = mutableSetOf<String>()
    set.add("JAN")
    set.add("FEB")
    set.add("MAR")
    set.add("JAN") // 셋은 중복을 허용하지 않기 때문에 동일한 값은 입력되지 않음

2) 셋 사용하기


  👾  인덱스로 조회하는 메서드가 없기 때문에 특정 위치의 값을 직접 사용할 수 없음

    println("Set 전체 출력 : ${set}") // Set 전체 출력 : [JAN, FEB, MAR]

3) 삭제하기


  👾  값이 중복되지 않기 때문에 값으로 직접 조회해서 삭제 할 수 있음

    set.remove("FEB")
    println("Set 전체 출력 : ${set}") // Set 전체 출력 : [JAN, MAR]

  👾  함수사용 결과는 요소 삭제나 추가가 정상적으로 이루어졌을 때 true,

         정상적으로 이루어지지 않았을 때 false로 boolean 형 결과로 나옴

    val mutableSet = mutableSetOf(1, 1, 2, 2, 2, 3, 3)
    println(mutableSet.add(100)) // true
    println(mutableSet.add(3)) // false

2.  맵  Map

👩🏻‍💻  맵은 키 Key와 값 Value의 쌍으로 입력되는 컬렉션
👩🏻‍💻  맵의 키는 리스트의 인덱스와 비슷한데 맵에는 키를 직접 입력하고 중복되지 않도록 해야 함

    ⚡️  제네릭으로 키와 값의 데이터 타입을 지정해서 맵을 생성
    ⚡️  맵의 요소는 Pair(A, B)를 사용하는 데 이는 A to B로도 표현할 수 있음

// 읽기 전용 맵
val immutableMap = mapOf("name" to "tom", "age" to 28, "age" to 20, "height" to 170)
println(immutableMap) // {name=tom, age=20, height=170} 중복이 있을 경우 마지막 키값이 저장됨

1) 맵 생성하기


  👾  키와 값의 데이터 타입을 모두 String으로 사용

 var map = mutableMapOf<String, String>()

2) 값 추가하기


  👾  값을 추가하기 위해 제공되는 put 메서드에 키와 값을 입력

    map.put("키1", "값1")
    map.put("키2", "값2")
    map.put("키3", "값3")
    println(map) // {키1=값1, 키2=값2, 키3=값3}


3) 맵 사용하기


  👾  get 메서드에 키를 직접 입력해서 값을 사용할 수 있음

println("map에 입력된 키1의 값은 ${map.get("키1")}입니다.") // map에 입력된 키1의 값은 값1입니다.

 4) 맵 수정하기


  👾  put() 메서드를 사용할 때 동일한 키를 가진 값이 있으면 키는 유지된 채로 값만 수정

map.put("키2", "수정한 값")
map["키2"] = "수정한 값"
println("map에 입력된 키2의 값은 ${map.get("키2")}입니다.") 
// map에 입력된 키2의 값은 수정한 값입니다.

5) 맵 삭제하기


  👾  remove() 메서드에 키를 입력해 값을 삭제할 수 있음

map.remove("키1")

// 없는 값을 불러오면 null 값이 출력됨
println("map에 입력된 키1의 값은 ${map.get("키1")}입니다.") 
// map에 입력된 키1의 값은 null입니다.

 

 

 

 

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


1.  컬렉션

자바에서 리스트 List, 셋 Set, 맵 Map 등 여러 자료 구조를 사용하는데, 코틀린에서도 이러한 컬렉션을 모두 사용할 수 있을 뿐만 아니라 몇 가지 편리한 함수를 추가로 제공. 또한 코틀린은 컬렉션을 읽기 전용 immutable 컬렉션읽기-쓰기 mutable 컬렉션으로 크게 두 가지로 나눔


1) 리스트 List


  👻 리스트 List는 저장되는 데이터에 인덱스를 부여한 컬렉션이며 중복된 값을 입력할 수 있음
  👻 코틀린에서 동적으로 리스트를 사용하기 위해서는 리스트 자료형 앞에 뮤터블 Mutable 이라는 접두어 prefix가 붙음
        💫  접두어가 있는 리스트도 있지만 잘 사용하지 않기 때문에 항상 mutableList, mutableMap, mutableSet을 사용

 

 뮤터블과 이뮤터블


프로그래밍 언어에서 뮤터블은 변할 수 있는 데이터 타입을 가르키는 용어변수로는 var이 뮤터블
그리고 반대 개념인 이뮤터블 Immutable이 있는데 이것은 val과 같이 변할 수 없는 데이터 타입을 가르키는 용어.

  ⚡️ 코틀린은 컬렉션 데이터 타입을 설계할 때 모두 이뮤터블로 설계
  ⚡️ 기본 컬렉션인 리스트, 맵, 셋은 모두 한 번 입력된 값을 바꿀 수 없음
  ⚡️ 컬렉션의 원래 용도인 동적 배열로 사용하기 위해서는 뮤터블로 만들어진 데이터 타입을 사용해야 함

 

읽기 전용 리스트는 listOf() 함수를 사용
읽기 쓰기 모두 가능한 리스트는 mutableListOf() 함수를 사용
    // 이뮤터블
    val numList = listOf(1, 2, 3)
    println(numList[1]) // 2
    // numList[0] = 10    // error
   // 뮤터블
   val mutableList = mutableListOf("MON", "TUE", "WED") 
    // 3개의 값을 가진 크기가 3인 동적 배열 리스트가 생성

2) 리스트에 값 추가하기 : add


  👻  mutableList 변수에 add 메서드를 사용해서 값을 추가
  👻  값이 추가되면서 동적으로 리스트의 공간이 자동으로 증가

        mutableList.add("THU") 
        // 입력될 위치인 인덱스를 따로 지정해주지 않아도 입력되는 순서대로 인덱스가 지정.
        println(mutableList) 
        // [MON, TUE, WED, THU]

3) 리스트에 입력된 값 사용하기 : get


  👻  입력할 때와는 다르게 사용할 때는 인덱스를 지정해서 몇 번째 값을 꺼낼 것인지 명시

        var variable = mutableList.get(1) // 두 번째 값을 변수에 저장
        variable = mutableList[1]
        println(variable) // TUE

4) 리스트 값 수정하기 : set


  👻  특정 인덱스 값을 수정

        mutableList.set(1, "수정할 값") // 두 번째 값을 수정
        println(mutableList) // [MON, 수정할 값, WED, THU]
        mutableList[1] = "수정할 값" // 두 번째 값을 수정.

5) 리스트에 입력된 값 제거하기 : removeAt


  👻  리스트에 입력된 값의 인덱스를 지정해서 삭제

        println(mutableList.get(1)) // 수정할 값
        mutableList.removeAt(1) // 두 번째 값을 삭제
        // 두 번째 값을 삭제하면 세 번째 값부터 인덱스가 하나씩 감소하면서 빈자리의 인덱스로 이동
        println(mutableList.get(1)) // WED

6) 빈 리스트 사용하기


  👻  아무것도 없는 빈 리스트를 생성하면 앞으로 데이터 타입을 알 수 없기 때문에 값의 타입을 추론할 수 없음
  👻  빈 컬렉션의 경우 앞에서처럼 '데이터타입of'만으로는 생성되지 않고 데이터 타입을 직접적으로 알려주는 방법을 사용해야 함

var 변수명 = mutableListOf<컬렉션에 입력될 값의 타입>()
var stringList = mutableLisfOf<String>()
        // 생성
        val stringList = mutableListOf<String>() // 문자열로 된 빈 컬렉션을 생성

        stringList.add("월")
        stringList.add("화")
        println(stringList[1]) // 화

7) 컬렉션 개수 가져오기 : size


  👻  size 프로퍼티를 사용하면 컬렉션의 개수를 가져올 수 있음

        println("stringList에는 ${stringList.size}개의 값이 있습니다.")
        // stringList에는 2개의 값이 있습니다.

 


예제


    문자열을 저장할 수 있는 List 객체를 생성하고 여기에 "a", "b", "c", "d", "e"를 저장한 후 이것을 출력하는 프로그램을 생성
    출력 예:  [a, b, c, d, e]

    1. 뮤터블  2. 이뮤터블 두 케이스 다 작성

fun main() {

    val charList = listOf("a", "b", "c", "d", "e") 
    println(charList) // [a, b, c, d, e]

    val mutableList = mutableListOf<String>()
    // val mutableList: MutableList<String> = mutableListOf()
    mutableList.add("a")
    mutableList.add("b")
    mutableList.add("c")
    mutableList.add("d")
    mutableList.add("e")

    println(mutableList) // [a, b, c, d, e]
}

 

 

 

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


1.  버스 노선 가져오기

html 코드
<body>
<table>
    <thead>
        <tr>
            <th>버스번호</th>
            <th>버스등급</th>
            <th>평일요금</th>
            <th>평일 시간표</th>
            <th>주말 시간표</th>
        </tr>
    </thead>
    <tbody>

    </tbody>
</table>
<div>
    <input type="button" value="대구">
    <input type="button" value="구미">
    <input type="button" value="경산">
    <input type="button" value="포항">
</div>
</body>

 

css 코드
table, td, th {
    border-collapse: collapse;
    border: 2px solid black;
}

table th {
    height: 50px;
}

table td {
    padding: 15px;
}


table th:first-child {
    width: 200px;
}

table th:nth-child(2) {
    width: 100px;
}

table th:nth-child(3) {
    width: 200px;
}

table th:nth-child(4) {
    width: 550px;
}

table th:last-child {
    width: 550px;
}

div {
    margin: 0 auto;
    padding: 20px;
    text-align: center;
    width: 1000px;
}
div input {
    width: 70px;
    height: 40px;
    background-color: #3a4bb9;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    padding: 5px 10px;
}
div input:hover {
    background-color: white;
    color: #3a4bb9;
}

자바스크립트 코드
document.addEventListener('DOMContentLoaded', function () {

    const btns = document.querySelectorAll('[type=button]');

    btns.forEach(function (item) {
        item.addEventListener('click', function () {
            const cityName = item.getAttribute('value'); // value 값 변수에 담기
            let url = getUrl() // 함수값을 변수에 담기
            printlist(url, cityName); // 함수 호출
        });
    });

    // 1, 2 터미널 각각 분류된 평일 및 주말 시간표를 합치는 함수 만들기
    const sortStr = function (string1, string2) { 
        // 기본 데이터는 문자열. 2개의 문자열을 결합하고, ',' 기준으로 배열로 변환
        let tempList = (string1 + ', ' + string2).split(",");
        tempList = [...new Set(tempList)]; // set으로 중복값 제거
        tempList = tempList.map((item) => item.trim()) // 공백제거
        tempList.sort(); // 오름차순 정렬

        // 현재 시간 구해서 지나간 시간이면 연하게 출력.
        const today = new Date();
        const todayTime = `${today.getHours()}${today.getMinutes()}`;
        // console.log(todayTime)
        tempList = tempList.map((item) => {
            console.log(Number(item))
            return Number(item) < Number(todayTime) ? `<span style="color: #cccccc">${item}</span>` : item;
        });

        return tempList.join(", "); // 배열을 문자열로 변환
    }

    const getUrl = function () {
        const service_key = 'uAhO32pV0qa7BDOmkJLhw2ZyOB9i5bGj7cMN8cuuHmKIwyyf29lHLjym8hBXdIaXyvAI1IyLvQZopk5HW233qQ=='
        const para = `?serviceKey=${service_key}&area=6&numOfRows=100&pageNo=1&type=json`
        return 'http://apis.data.go.kr/B551177/BusInformation/getBusInfo' + para
        // console.log(url)
    }

    const printlist = function (getUrl, cityName) {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', getUrl);
        xhr.onreadystatechange = () => {
            
            if (xhr.readyState !== XMLHttpRequest.DONE) return;

            if (xhr.status === 200) {
                console.log(xhr.response);
                jsonData = JSON.parse(xhr.response);
                //console.log(jsonData);

                const routes = jsonData.response.body.items;
                //console.log(routes)
                const tBody = document.querySelector("tbody");

                while (tBody.firstChild) {
                    tBody.removeChild(tBody.firstChild);
                }

                for (route of routes) {
                    //console.log(route["routeinfo"])

                    if (route["routeinfo"].indexOf(cityName) !== -1) {
                        const trTag = document.createElement('tr');
                        console.log(route)

                        trTag.innerHTML = `
                            <td>${route["busnumber"]}</td>
                            <td>${route["busclass"]}</td>
                            <td>${route["adultfare"]}</td>
                            <td>${sortStr(route["t1wdayt"], route["t2wdayt"])}</td>
                            <td>${sortStr(route["t1wt"], route["t2wt"])}</td>`

                        tBody.appendChild(trTag);
                    }
                }

            } else {
                console.error('Error', xhr.status, xhr.statusText);
            }
        }
        xhr.send();
    }
});

 


2.  항공 출도착 정보 

html 코드
<body>
<table>
    <div>
        <input type="button" id="NRT" value="나리타">
        <input type="button" id="CTS" value="삿포로">
        <input type="button" id="KIX" value="오사카/간사이">
        <input type="button" id="FUK" value="후쿠오카">
        <input type="button" id="HKG" value="홍콩">
    </div>

    <thead>
    <tr>
        <th>항공사</th>
        <th>편명</th>
        <th>예정시간</th>
        <th>도착지 공항</th>
    </tr>
    </thead>
    <tbody>

    </tbody>
</table>
</body>

 

css 코드
table {
    margin: 20px auto;
}

table, td, th {
    border-collapse: collapse;
    border: 2px solid black;
}

table th {
    height: 50px;
}

table td {
    padding: 15px;
    text-align: center;
}


table th:first-child {
    width: 200px;
}
table th:nth-child(2) {
    width: 60px;
}
table th:nth-child(3) {
    width: 150px;
}
table th:nth-child(4) {
    width: 160px;
}

div {
    margin: 0 auto;
    padding: 20px;
    text-align: center;
    width: 1000px;
}
div input {
    width: 100px;
    height: 40px;
    background-color: #3a4bb9;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    padding: 5px 10px;
}
div input:hover {
    background-color: white;
    color: #3a4bb9;
}

자바스크립트 코드
document.addEventListener('DOMContentLoaded', function () {
    const btns = document.querySelectorAll('[type=button]');

    btns.forEach(function (item) {
        item.addEventListener('click', function () {
            const airport = item.getAttribute("id");
            let url = getUrl(airport)
            printList(url);
        });
    });

    const getUrl = function (airportCode) {
        const service_key = 'uAhO32pV0qa7BDOmkJLhw2ZyOB9i5bGj7cMN8cuuHmKIwyyf29lHLjym8hBXdIaXyvAI1IyLvQZopk5HW233qQ=='
        const para = `?type=json&ServiceKey=${service_key}&airport_code=${airportCode}`
        return 'http://apis.data.go.kr/B551177/StatusOfPassengerFlightsDSOdp/getPassengerDeparturesDSOdp' + para
    }
    //console.log(getUrl())

    const printList = function (getUrl) {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', getUrl);
        xhr.onreadystatechange = () => {
            // readyState 프로퍼티의 값이 DONE : 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
            if (xhr.readyState !== XMLHttpRequest.DONE) return;

            if (xhr.status === 200) { // 서버(url)에 문서가 존재함
                //console.log(xhr.response);
                const jsonData = JSON.parse(xhr.response);
                //console.log(jsonData);

                const airlines = jsonData.response.body.items;
                //console.log(airlines)

                const tBody = document.querySelector("tbody");

                while (tBody.firstChild) {
                    tBody.removeChild(tBody.firstChild);
                }

                for (airline of airlines) {

                    const list = document.createElement('tr');

                    list.innerHTML = `
                    <td>${airline['airline']}</td>
                    <td>${airline['flightId']}</td>
                    <td>${airline['estimatedDateTime']}</td>
                    <td>${airline['airport']}</td>`

                    tBody.appendChild(list);
                }

            } else {
                console.error('Error', xhr.status, xhr.statusText);
            }
        }
        xhr.send();
    }
});

 

 

 

 

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


1.  Ajax ; Asynchronous JavaScript And XML

자바스크립트를 사용하여 브라우저가 비동기 방식으로 데이터를 요청하고, 서버가 응답한 데이터를 수신하여 웹페이지를 동적으로 갱신하는 프로그래밍 방식. (ex. 구글 지도)


  👾  Ajax는 브라우저에서 제공하는 Web API인 XMLHttpRequest 객체를 기반을 동작
  👾  XMLHttpRequest : HTTP 비동기 통신을 위한 메서드와 프로퍼티를 제공

  👾  이전의 웹페이지는 완전한 HTML을 서버로부터 전송받아 웹페이지 전체를 처음부터 다시 렌더링하는 방식으로 동작
          ➡️  화면이 전환되면 서버로부터 새로운 HTML을 전송받아 웹페이지 전체를 처음부터 다시 렌더링

 

전통적 방식의 단점 


    a. 이전 웹페이지와 차이가 없어서 변경할 필요가 없는 부분까지 포함된 완전한 HTML을 서버로부터

        매번 다시 전송받기 때문에 불필요한 데이터 통신이 발생
    b. 변경할 필요가 없는 부분까지 처음부터 다시 렌더링 한다. 이로 인해 화면 전환이 일어나면 화면이 순간적으로

         깜빡이는 현상이 발생한다.
    c. 클라이언트와 서버와의 통신이 동기 방식으로 동작하기 때문에 서버로부터 응답이 있을 때까지 다음 처리는 블로킹 된다.


Ajax의 장점


   a. 변경할 부분을 갱신하는 데 필요한 데이터만 서버로부터 전송받기 때문에 불필요한 데이터 통신 발생 x
   b. 변경할 필요가 없는 부분은 다시 렌더링하지 않는다. 따라서 화면이 순간적으로 깜빡이는 현상이 발생하지 않는다.
   c. 클라이언트와 서버와의 통신이 비동기 방식으로 동작하기 때문에 서버에게 요청을 보낸 이후 블로킹이 발생하지 않는다.

 

Ajax의 단점


    a. 즐겨찾기나 검색엔진에 불리
    b. 개발이 상대적으로 어려움

 

💡  '동기(synchronous)'란       
     -  직렬적으로 태스크를 수행  ▶️  요청을 보낸 후 응답을 받아야지만 다음 동작이 이루어지는 방식 (순차 진행)
💡  '비동기 (asynchronous)'란       
     -  병렬적으로 태스크를 수행  ▶️  요청을 보낸 후 응답의 수락 여부와는 상관없이 다음 태스크가 동작 (멀티 진행)
     
   * 출처 : https://velog.io/@khy226

 


2.  JSON ; JavaScript Object Notation

클라이언트와 서버간의 HTTP 통신을 위한 텍스트 데이터 포맷
자바스크립트에 종속되지 않은 언어 독립형 데이터 포맷으로, 대부분의 프로그래밍 언어에서 사용할 수 있음

  👾  JSON은 자바스크립트의 객체 리터럴과 유사하게 키와 값으로 구성된 순수한 테스트
  👾  JSON의 키는 반드시 큰 따옴표(작은 따옴표 사용 불가)로 묶여야 함
  👾  값은 문자열은 큰 따옴표(작은 따옴표 사용 불가)로 묶여야 함 나머지는 그대로 쓰여도 무관


 

1) JSON.stringify()


     ⚡️ 객체를 JSON 포맷의 문자열로 변환
     ⚡️ 클라이언트가 서버로 객체를 전송하려면 객체를 문자열화해야 하는 데 이를 직렬화 serializing 라고 함

// 기본 형식
JSON.stringify(value, replacer, space)
    1. value : 변환할 객체
    2. replacer : 함수. 값의 타입이 Number이면 필터링이되어 반환되지 않는다.

    3. space : 들여쓰기 몇 칸 할지 Int 형으로 기입
<head>
    <script>
    
        const obj = {
            name: 'Lee',
            age: 20,
            alive: true,
            hobby: ['traveling', 'tennis'],
        };
        console.log(typeof obj); // object

        // 객체를 JSON 포맷의 문자열로 변환.
        const prettyJson = JSON.stringify(obj, null, 2); 
        console.log(typeof prettyJson, prettyJson);
        /*
        string {
           "name": "Lee",
           "age": 20,
           "alive": true,
           "hobby": [
               "traveling",
               "tennis"
           ]
         }
        */
        
         function filter(key, value) {
            //undefined: 반환되지 않음
            return typeof value === 'number' ? undefined : value;
        }
        
        const strFilteredObject = JSON.stringify(obj, filter, 2);
        console.log(typeof strFilteredObject, strFilteredObject);
        /*
        string {
            "name": "Lee",
            "alive": true,
            "hobby": [
                "traveling",
                "tennis"
             ]
        */
}
    </script>
</head>

JSON.stringify() 메서드는 객체뿐만 아니라 배열도 JSON 포맷의 문자열로 변환
<script>
        const todos = [
            { id: 1, content: 'HTML', completed: false },
            { id: 2, content: 'CSS', completed: true },
            { id: 3, content: 'JavaScript', completed: false },
        ];
        // 배열을 JSON 포맷의 문자열로 변환
        const jsonArray = JSON.stringify(todos, null, 2);
        console.log(typeof jsonArray, jsonArray);
        /*
        string [
            {
                "id": 1,
                "content": "HTML",
                "completed": false
             },
            {
                "id": 2,
                "content": "CSS",
                "completed": true
             },
            {
                "id": 3,
                "content": "JavaScript",
                "completed": false
            }
         ]
         */
</script>

 


2)  JSON.parse()


    ⚡️  JSON 포맷의 문자열을 객체로 변환  ▶️  역직렬화 deserializing
    ⚡️  서버로부터 클라이언트에게 전송된 JSON 데이터는 문자열이고, 이 문자열을 객체로 변환

<script>

    const obj = {
        name: "Lee",
        age: 20,
        alive: true,
        hobby: ["traveling", "tennis"],
    };
    console.log(typeof obj); // obj

    // 객체를 JSON 포맷의 문자열로 변환.
    const json = JSON.stringify(obj);
    console.log(typeof json); // string

    // JSON 포맷의 문자열을 객체로 변환.
    const parsed = JSON.parse(json);
    console.log(typeof parsed, parsed); // object

</script>

 


3.  Ajax를 이용해 데이터 가져오기

 

<script>
    
    document.addEventListener('DOMContentLoaded', function () {
   
       // 1. XMLHttpRequest 객체 생성
       const xhr = new XMLHttpRequest();

       // 2. HTTP 요청 초기화
        xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
        // HTTP 요청 방식(GET, POST), 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소

       // 3. 이벤트 등록. XMLHttpRequest 객체의 readyState 프로퍼티 값이 변할 때마다 자동으로 호출
        xhr.onreadystatechange = () => {
            // readyState 프로퍼티의 값이 DONE : 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
            if(xhr.readyState !== XMLHttpRequest.DONE) return;

            if(xhr.status === 200) { // 서버(url)에 문서가 존재함
                console.log(JSON.parse(xhr.response));
                /*
                {
                    "userId": 1,
                    "id": 1,
                    "title": "delectus aut autem",
                    "completed": false
                }
                */
                
                const obj = JSON.parse(xhr.response);
                console.log(obj.title); // delectus aut autem
            } else {
                console.error('Error', xhr.status, xhr.statusText);
            }
        }
        xhr.send(); // 4. url에 요청을 보냄.
    });
</script>

 


💡 onreadystatechange
서버로 부터 응답이 오게 되어 XMLHttpRequest 객체의 값이 변하게 되면 이를 감지해 자동으로 호출되는 함수를 설정한다.
함수를 등록하게 되면 서버에 요청한 데이터가 존재하고, 서버로부터 응답이 도착하는 순간을 특정할 수 있게 된다.

💡  status
서버의 문서 상태를 표현한다(200 : 존재 / 404 : 미존재)

💡 xhr.send()

send() 메서드를 통해 서버로 객체를 전달한다. 이때 send() 메서드 매개변수를 쓰냐 안쓰냐에 따라 GET / POST 방식으로 다르게 보내게 된다.

💡  readyState 프로퍼티

XMLHttpRequest 객체의 현재 상태를 나타낸다. 이 프로퍼티의 값은 객체의 현재 상태에 따라 다음과 같은 주기로 순서대로 변화한다.

  1. UNSENT (숫자 0) : XMLHttpRequest 객체가 생성됨.
  2. OPENED (숫자 1) : open() 메소드가 성공적으로 실행됨.
  3. HEADERS_RECEIVED (숫자 2) : 모든 요청에 대한 응답이 도착함.
  4. LOADING (숫자 3) : 요청한 데이터를 처리 중임.
  5. DONE (숫자 4) : 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
 

출처: https://inpa.tistory.com/entry/JS-📚-AJAX-서버-요청-및-응답-XMLHttpRequest-방식 [Inpa Dev 👨‍💻:티스토리] 


출처: https://inpa.tistory.com/entry/JS-📚-AJAX-서버-요청-및-응답-XMLHttpRequest-방식
         [Inpa Dev 👨‍💻:티스토리]

응용 예제 - 현재 날씨 가져오기
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>

        document.addEventListener('DOMContentLoaded', function () {
            // url 인코딩된 키 사용
            const serviceKey = 'uAhO32pV0qa7BDOmkJLhw2ZyOB9i5bGj7cMN8cuuHmKIwyyf29lHLjym8hBXdIaXyvAI1IyLvQZopk5HW233qQ==';

            // 위치 값
            const nx = 89
            const ny = 90

            // 현재 시간 구함
            const today = new Date();

            const baseDate = `${today.getFullYear()}${('0' + (today.getMonth() + 1)).slice(-2)}${('0' + today.getDate()).slice(-2)}`;
            // 현재 분이 30분 이전이면 이전 시간(정시)을 설정.
            let baseTime = today.getMinutes() <= 30 ? `${today.getHours() -1}00` : `${today.getHours()}00`;
            baseTime = (baseTime.length === 3) ? `0${baseTime}` : baseTime; // 10시 전이면 0을 하나 붙임.

            const parameter = `?serviceKey=${serviceKey}&base_date=${baseDate}&base_time=${baseTime}&nx=${nx}&ny=${ny}&dataType=JSON`
            const url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst' + parameter
            console.log(url+parameter);


            const xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.onreadystatechange = () => {
                // readyState 프로퍼티의 값이 DONE : 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
                if(xhr.readyState !== XMLHttpRequest.DONE) return;

                if(xhr.status === 200) { // 서버(url)에 문서가 존재함
                    console.log(xhr.response);
                    jsonData = JSON.parse(xhr.response);
                    console.log(jsonData);

                    const weatherItems = jsonData.response.body.items.item;
                    let jsonStr = `[발표 날짜: ${weatherItems[0]["baseDate"]}]\n`;
                    jsonStr += `[발표 시간: ${weatherItems[0]["baseTime"]}]\n`;
                    for (let k in weatherItems) {
                        let weatherItem = weatherItems[k];
                        obsrValue = weatherItem['obsrValue']
                        // T1H: 기온, RN1: 1시간 강수량, REH: 습도 %
                        if (weatherItem['category'] === 'T1H') {
                            jsonStr += ` * 기온: ${obsrValue}[℃]\n`;
                        } else if (weatherItem['category'] === 'REH') {
                            jsonStr += ` * 습도: ${obsrValue} [%]\n`;
                        } else if (weatherItem['category'] === 'RN1') {
                            jsonStr += `* 1시간 강수량: ${obsrValue} [mm]\n`;
                        }
                    }

                    document.querySelector('div').innerText = jsonStr;


                } else {
                    console.error('Error', xhr.status, xhr.statusText);
                }
            }
            xhr.send();
        });

    </script>
</head>
<body>
<div>


</div>

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 및 inpa_dev 티스토리 ]

+ Recent posts