Scala 언어 문법책 공부 후 핵심만 정리함

 

 

Scala 는 JVM 언어임. 자바 런타임을 사용하여 실행됨

 

 

 

Literal : 숫자 5, 문자 'A', 문자열 "eyeballs" 처럼 소스 코드에 바로 등장하는 데이터

값(value) : 불변의 타입을 갖는 저장 단위. 정의될 때 데이터가 할당되며 재할당 불가능

변수(variable) : 가변의 타입을 갖는 저장 단위. 정의될 때 데이터가 할당되며 재할당 가능

타입(type) : 데이터의 종류, 정의, 분류. Scala 의 모든 데이터는 특정 타입에 대응하며, 모든 Scala 타입은 그 데이터를 처리하는 메소드를 갖는 클래스로 정의됨

 

불변의 값(value) 를 사용하면, 다른 어떤 코드에서 접근하더라도 같은 값을 유지하는 안정성을 갖출 수 있어

코드를 읽고 디버깅하는 일이 더 쉬워짐

동시 또는 멀티 스레드 코드에서 값을 사용하여 에러 발생 가능성을 낮출 수 있음

 

 

 

 

이름 설명 크기 최솟값 최댓값
Byte 부호 있는 정수 1byte -128 127
Short 부호 있는 정수 2byte -32768 32767
Int 부호 있는 정수 4byte -2^31 2^31 - 1
Long 부호 있는 정수 8byte -2^63 2^63 - 1
Float 부호 있는 부동 소수 4byte n/a n/a
Double 부호 있는 부동 소수 8byte n/a n/a

 

 

 

 

 

 

Literal Type 설명
5 Int 접두사/접미사 없는 정수 리터럴은 기본이 Int
0x0f Int 접두사 0x : 16진수
5l Long 접미사 l : Long 타입
5.0 Double 접두사/접미사 없는 소수 리터럴은 기본이 Double
5f Float 접미사 f : Float 타입
5d Double 접미사 d : Double 타입

 

 

 

 

정규 표현식

 

matches : 정규 표현식이 전체 문자열과 맞으면 true

"abc" matches ".*" : true

 

replaceAll : 일치하는 문자열을 모두 치환

"abc" replaceAll ("b|c","a") : "aaa"

 

replaceFirst : 첫번째로 일치하는 문자열을 치환

"abc" replaceFirst ("b|c","a") : "aac"

 

 

 

 

bool 비교 연산자인 &&,|| 은 게을러서 첫 번째 인수로 충분하다면 두 번째 인수는 평가하지 않음

&, | 은 결괏값 반환하기 전에 늘 두 인수를 모두 검사함

 

 

 

 

데이터 타입 연산

예제 설명
val aa = 5
myVal.asInstanceOf[Long]
원하는 타입으로 전환
val aa = 5
myVal.getClass
해당 값의 타입(=Class) 반환
val aa = 5
myVal.isInstanceOf[Int]
해당 값이 넣어준 타입에 해당하는지 확인
val aa = 5
myVal.hashCode
해당 값의 해시코드 반환
val aa = 5
myVal.toDouble
형변환
val aa = 5
myVal.toString
해당 값을 string 으로 변환

 

 

 

 

 

 

Tuple 은 선언 방법이 두 가지이며, 인덱스는 1부터 시작함

0부터 시작하지 않는 문제아. 아주 자기 멋대로야..

 

val myTuple = (1, "2", '3', "four", 5)

myTuple._1 // 1

myTuple._2 // "2"

 

val myTuple = 1 -> "2"

myTuple: (Int, String) = (1, "2")

 

 

 

 

 

 

표현식을 아래처럼 활용 가능

 

val result = { val x = 6*25; x*100 }

result: Int = 15000

 

여기서 x는 표현식 내에서만 사용 가능하며

표현식 바깥에서는 접근 불가

 

 

 

 

 

if ( true ) println("a")

결과 : a

 

if (1 > 2) println("a") else println("b")

결과 : b

 

if 문과 대체하여 사용 가능한 match 표현식은 아래처럼 사용 가능

 

val max = (1>2) match {

  case true => 1

  case false => 2

}

결과 : 2

 

val message = 500 match {

  case 200 => "ok"

  case 400 => { println("400"); "error" }

  case 500 => { println("500"); "error" }

}

결과 : 500 이 출력되고 "error" 가 message 에 들어감

 

val kind = "WED" match {

  case "MON" | "TUE" | "WED" | "THU" | "FRI" => "weekday"

  case "SAT" | "SUN" => "weekend"

}

결과 : weekday

 

val ifstatement = -1 match {

  case x if x > 0 => "plus"

  case x if x < 0 => "minus"

  case x => "zero"

}

결과 : minus

 

val other = 300 match {

  case 200 => "ok"

  case other => "error"

}

결과 : error

 

val wilecard = 300 match {

  case 200 => "ok"

  case _ => "error"

}

결과 : error

 

타입으로도 매칭이 가능

val x:Int = 123

x match {

  case x: String => "String"

  case x: Int => "Int"

}

결과 : Int

 

 

 

 

 

 

for 루프는 일정 범위의 데이터를 반복하며, 반복할 때마다 표현식을 실행함

yield 를 추가하면 반환값들을 컬렉션으로 돌려줌

(각 원소마다 특정 표현식을 적용하고 반환하는 것이 마치 map 과 비슷함)

 

for ( i <- 1 to 5 ) { print(i+" ") }

결과 : 1 2 3 4 5

 

for ( i <- 1 until 5 ) { print(i+" ") }

결과 : 1 2 3 4

 

val result = for ( i <- 1 to 5 ) yield { i }
결과 : results: scala.collection.immutable.IndexedSeq[Int] = Vector(1,2,3,4,5)

 

for ( i <- result ) { print(i+" ") }
결과 : 1 2 3 4 5

 

for ( x <- 1 to 2 ; y <- 1 to 3 } { print(s"($x,$y)") }

결과 : (1,1)(1,2),(1,3)(2,1)(2,2)(2,3)

 

 

아래와 같이 for문 안에 조건식을 넣을 수 있음

 

for ( i <- 1 to 10 if i%2!=0) print(i+" ")

결과 : 1 3 5 7 9

 

아래와 같이 여러개의 조건절을 넣을 수 있으며, 조건들은 서로 and 로 엮임

 

for {

  c <- "a,bc,,d,,ef".split(",")

  if c != null

  if c.size > 1

} print(c+" ")

결과 : bc ef

 

아래와 같이 for 루프 안에서만 쓰이는 임시 변수를 지정하여 사용 가능

아래 예제의 x 는 for 루프가 반복 할 때마다 매번 정의되고 할당됨

 

for ( i <- 1 to 5 ; x = i * i ) print(x+" ")

결과 : 1 4 9 16 25

 

while 루프도 사용 가능함

 

var x = 5

while( x > 0 ) x-=1

결과 : x 는 0이 됨

 

 

 

 

 

 

 

순수 함수는 아래의 조건들을 만족하는 함수임

- 하나 이상의 입력 매개변수를 가짐

- 입력 매개변수만을 이용하여 계산 수행

- 값을 반환함

- 동일 입력에 대해 항상 같은 값을 반환

- 함수 외부의 어떤 데이터도 사용하거나 영향을 주지 않음

- 함수 외부 데이터에 영향을 받지 않음

 

순수 함수는 상태 정보를 유지하지 않으며, 외부 데이터에 관계없이 독립적임

본질적으로 순수함수는 변경될 수 없어서 안정적임

(마치 수학에서 사용하는 함수와 동일한 성질을 갖음)

Scala 프로그래밍을 하면서 순수 함수 비율을 많이 늘리는 게 좋다고 함

 

 

 

함수는 def 를 사용하여 선언하며, 선언문 다음에는 표현식이 옴

 

선언 : def hi = "hi"

사용 : hi

 

선언 : def hi = {"hi"}

사용 : hi

 

선언 : def hi:String = {val result = "eyeballs", result}

사용 : hi

결과 : eyeballs


선언 : def hi() = "hi"

사용 : hi 혹은 hi()

 

선언 : def hi(name:String) = "hi "+name

사용 : hi("eyeballs")

결과 : hi eyeballs

 

선언 : def hi(name:String):String = {

  if (name != null) return "hi "+name

  else return "it's null"

}

사용 : hi("eyeballs"), hi(null)

결과 : "hi eyeballs", "it's null"

 

선언 : def hi(name:String):String = {

  if (name != null) return "hi "+name

  "it's null"

}

사용 : hi("eyeballs"), hi(null)

결과 : "hi eyeballs", "it's null"

 

선언 : def hi(name:String, ch:Char) = "hi "+name+ch

사용 : hi("eyeballs", '!')

결과 : hi eyeballs!

 

선언 : def hi(name:String, ch:Char) = "hi "+name+ch

사용 : hi(name = "eyeballs", ch = '!') , hi(ch = '!', name = "eyeballs")

결과 : hi eyeballs!

 

선언 : def hi(name:String, ch:Char = '!') = "hi "+name+ch

사용 : hi("eyeballs"), hi(name = "eyeballs")

결과 : hi eyeballs!

 

선언 : def hi(name:String)(ch:Char) = "hi "+name+ch

사용 : hi("eyeballs")('!')

결과 : hi eyeballs!

 

아래처럼 인수 개수가 가변적일 때 * 를 사용하여 처리 가능

 

선언 : def sum(items: Int*): Int = {

  var total = 0

  for ( i <- items) total += i

  total

}

사용 : sum(0), sum(1,2), sum(1,2,3,4,5)

결과 : 0, 3, 15

 

아래처럼 사용시 표현식을 사용할 수 있음

아래 에제에서 myname 은 호출할 때만 잠깐 사용되는 값

 

선언 : def hi(name:String) = "hi "+name

사용 : hi {val myname="eyeballs"; hi(myname)}

결과 : hi eyeballs

 

프로시저란 반환값을 갖지 않는 함수이며, 이 때 함수의 반환값은 Unit 이 됨

예를 들어 아래 두 함수는 동일한 함수임

def log(d:Double) = println("value : "+d)

def log(d:Double):Unit = println("value : "+d)

 

아래처럼 함수 안에 함수 중첩 가능

Scala 는 오버로딩이 가능하기 때문에, 아래 예제의 파라미터 3개짜리 max와 파라미터 2개짜리 max 는 서로 다른 함수임

파라미터 2개짜리 max는 파라미터 3개짜리 max 내에서만 사용 가능 (max 함수 바깥에서 사용 불가)

 

선언 : def max(a:Int, b:Int, c:Int) = {

  def max(x:Int, y:Int) = if (x>y) x else y

  max(a, max(b, c))

}

사용 : max(1,2,3)

결과 : 3

 

Generic 처럼 함수 내에서 다루는 타입 자체를 사용시 직접 넣어줄 수 있음

이것을 타입 매개변수라고 부르며 [ ] 를 사용하여 선언함

마지막 예제처럼 타입 매개변수를 넣어주지 않아도 추론이 가능한 경우는 에러가 나지 않음

 

선언 : def identity[TYPE] (x:TYPE):TYPE = x

사용 : identity[Int](1), identity[String]("eyeballs"), identity[Double](0.1), identity("eyeballs")

결과 : 1, eyeballs, 0.1, eyeballs

 

 

 

 

 

 

클래스 내 메소드를 호출할 때 dot 을 사용할 수 있지만, white scape 를 사용하는 것도 가능

아래 세 가지는 모두 같은 의미를 갖음

"eyeballs".endsWith("s")

"eyeballs" endsWith("s")

"eyeballs" endsWith "s"

 

 

 

 

 

Scala 에서 함수는 일급 객체임

일급 객체란, 일반적인 데이터 타입처럼 언어의 모든 부분에 사용 가능한 객체를 말 함

일급 함수는 값, 변수 등의 컨테이너에 저장될 수 있고

다른 함수의 매개변수로 사용되거나

다른 함수의 반환값으로 사용될 수 있음

 

다른 함수를 매개변수로 받아들이거나

다른 함수를 반환값으로 반환하는 함수를 고차함수 라고 함

 

함수는 일급객체이기 때문에, 아래처럼 함수를 값에 넣을 수 있음

 

def hi = "hi"

val copyHi = hi

사용 : copyHi

 

def hi() = "hi"

val copyHi = hi

사용 : copyHi()


def hi(name:String) = "hi "+name

val copyHi = hi

사용 : copyHi("eyeballs")


def hi(name:String, ch:Char) = "hi "+name+ch

val copyHi = hi _

사용 : copyHi("eyeballs", '!')

 

def hi(name:String)(ch:Char) = "hi "+name+ch

val copyHi = hi _

사용 : copyHi("eyeballs")('!')

 

함수는 일급객체이기 때문에, 아래처럼 함수의 파라미터에 함수를 넣을 수 있음

 

def reverser(s: String) = s.reverse

def hi(name: String, f:String => String) = {

  if(name!=null) f(name)

  else name

}

사용 : hi("abcde" reverser), hi(null, reverser)

결과 : edcba, null

 

아래처럼 함수 리터럴(익명함수) 를 사용하여 바로 값에 할당 가능

 

선언 : val hi = (s: String) => s.reverse

사용 : hi("abc")

 

선언 : def hi(name: String, f:String => String) = { if(name!=null) f(name) else name }

사용 : hi("abcde", s => s.reverse) , hi("abcde", (s:String) => s.reverse)

 

선언 : val logging = () => "logging..."

사용 : println(logging())

 

하지만 아래처럼 함수 리터럴을 선언에 사용하는 것은 안 됨

def hi(name:String, s=>s.reverse) = {....} //안 됨 

def hi(name:String, (s:String)=>s.reverse) = {....} //안 됨

 

 

 

 

자리표시자 구문은 함수 리터럴의 축약형임

지정된 매개변수를 와일드카드( _ )로 대체함

함수의 명식적 타입이 리터럴 외부에 지정되어 있고, 매개변수가 한 번 이상 사용되지 않는 경우에만 자리표시자 사용 가능

 

예를 들어 다음과 같이 사용 가능

val double: Int => Int = _*2

Int => Int 를 통해 함수에 입력값이 int 인 것을 알려주었고,

함수 내부에서 _ 가 한 번만 사용되었음

 

비슷한 예로 아래와 같이 사용 가능

선언 : val rev: String => String = _.reverse

사용 : rev("eyeballs")

 

선언 : def hi(name: String, f:String => String) = { if(name!=null) f(name) else name }

일반 사용 : hi("abcde", s => s.reverse) , hi("abcde", (s:String) => s.reverse)

자리표시자 사용 : hi("abcde", _.reverse)

 

선언 : def combination(x:Int, y:Int, f:(Int,Int)=>Int) = f(x,y)

일반 사용 : combination(2,3, (x,y) => x*y)

자리표시자 사용 : combination(2,3, _ * _)

 

선언 : def combination[A,B](x:A, y:A, f:(A,A)=>B) = f(x,y)

일반 사용 : combination[Int,Double](2,3, (x,y) => x/y*1.0)

자리표시자 사용 : combination[Int,Double](2,3, _ / _ * 1.0)

 

 

 

함수를 val 에 넣을 때 파라미터를 고정시킨 후에 넣을 수 있음

이것을 부분 적용 함수라고 함

예를 들어, 아래 함수에 들어가는 x, y  두 파라미터 중 하나는 고정하고 싶다면,

자리표시자를 사용한 부분에만 파라미터를 받을 수 있게 만들면 됨

 

def factorOf(x:Int, y:Int) = y%x == 0

val multipleOf3 = factorOf(3, _:Int)

사용 : factorOf(3, 10), multipleOf3(10)

 

아래처럼 부분 적용 함수를 만들어서 val 에 넣을 수 있음

 

val f = factorOf _

val f = factorOf(_, _)

val f = factorOf(3, _)

val f = factorOF(3, _:Int)

 

def factorOf(x:Int)(y:Int) = y%x == 0

val multipleOf3 = factorOf(3) _

사용 : multipleOf3(4), multipleOf3 {val a=2; a*2}

 

 

 

 

 

 

함수에 "이름에 의한 매개변수(call by name)"를 사용하면

리터럴 값이 와도 되고, 함수가 와도 됨

예를 들어 f 라는 함수의 매개변수로 이름에 의한 매개변수를 넣음

def doubles(x: => Int) = { print(s"got ${x} from doubles"); x*2 }

 

이 doubles 함수에 넣을 파라미터로 2, 5 등의 리터럴 값이 들어갈 수 있음

doubles(2) //결과 got 2 from doubles, 4

doubles(5) //결과 got 5 from doubles, 10

 

또한 함수 자체가 들어갈 수 있음

def f(a:Int) = {println(s"got ${a} from f"); a}

doubles(f(4)) //결과 got 4 from f, got 4 from doubles, for 4 from f, 8

 

이렇게 들어간 f 는 doubles 내부에서 사용 될 때마다 호출됨

위 출력 결과에도 from f 가 두 번 떴음, 왜냐하면 doubles 내부에 x 가 두 번 사용되기 때문

 

 

 

 

case 를 추가하여 함수에 파라미터를 특정지어 처리할 수 있음

 

선언 :

val caseFunc : Int=>String = {

  case 1 => "one"

  case -1 => "minus one"

  case _ => "the others"

}

사용 : caseFunc(1), caseFunc(-1), caseFunc(3)

 

 

 

 

 

 

 

 

 

Scala 의 class 는 Java 와 마찬가지로 new 로 생성할 수 있음

 

선언 : class User

생성 : val user = new User

확인 : user.isInstanceOf[User]  //true

user.isInstanceOf[AnyRef]  //true

user.isInstanceOf[Any]  //true

 

선언 : 

class User {

  val name : String = "eyeballs"

  def greet : String = s"hello ${name}"

  override def toString = s"user name is ${name}"

}

생성 : val user = new User

사용 : user.greet, new User().greet

 

아래처럼 생성자를 넣을 수 있으며, 이를 클래스 매개변수라고 함

클래스 매개변수 n 은 메소드(greet, toString 등) 내부에서 사용 불가능함

클래스 매개변수n 은 단지 필드를 초기화하거나 메소드에 전달되는 용도로만 사용됨

 

class User(n: String) {

  val name : String = n

  def greet : String = s"hello ${name}"

  override def toString = s"user name is ${name}"

}

 

클래스 매개변수 n 앞에 val, var 를 붙이면

n 은 클래스의 필드가 되기 때문에, 내부 메소드에서도 사용 가능

 

class User(val n: String) {

  def greet : String = s"hello ${n}"

  override def toString = s"user name is ${n}"

}

 

class A {

  override

}

class B extends A

class C extends B {

  override def toString = "C : "+getClass.getName

}

val a:A = new B

bal b:B = new A //에러남. 자식은 부모를 받아줄 수 없음

 

선언 : 

class Car( val make: String, var reserved: Boolean ) {

  def reserve(r: Boolean): Unit = {reserved = r}

}

생성 : val t = new Car("eyeballs company", false), val t = new Car(make = "eyeballs company", reserved = false)

사용 : t.reserve(true)

 

선언 :

class Car( val make: String = "eyeballs company", var reserved: Boolean = true, val year: Int = 2024) {

  def reserve(r: Boolean): Unit = {reserved = r}

}

생성 : val t = new Car(), val t = new Car(year=2025)

 

선언 :

class myClass[A](element: A) {

  val a:A = element

  print(a.isInstanceOf[A])

}

생성 : 아래 모두 true 를 출력

val myClass = new myClass(1)

val myClass = new myClass[Int](1)
val myClass = new myClass("eyeballs")

val myClass = new myClass[String]("eyeballs")

 

 

추상 클래스는 abstract 를 사용하여 선언 가능하며

자기 자신은 인스턴스를 생성하지 않고 오로지 다른 클래스에 의해 상속되어지기만 하는 클래스임

 

선언 : 

abstract class Car {

  val year: Int

  val color: String

}

사용 : 

class myCar extends Car {

  val year = 2024

  val color = "Red"

}

class myCar(val year:Int = 2024) extends Car {

  val color = "Red"

}

 

추상 클래스를 상속하지 않고도, 생성함과 동시에 내용을 구현하는 것으로 사용 가능함

 

선언 : abstract class Car (val year:Int) { def show }

생성 : val myCar = new Car(2024) {

  def show { println(s"this car is ${year}" years old) }

  }

사용 : myCar.show

혹은

new Car(2024) { def show { println(s"this car is ${year}" years old) } }.show

 

class 내 오버로딩도 가능함

class MyClass {

  def print(a:String) = println(a)

  def print(a:Int) = println(a)

  def print(a:String, b:Int) = println(a+" "+b)

}

 

apply 라는 메소드를 구현하면, 사용할 때 메소드 이름을 사용하지 않고 생성한 클래스 이름 그대로 사용 가능

 

선언 : 

class Multiple(factor: Int){

  def apply (input: Int): Int = input * factor

}

생성 : val triple = new Multiple(3)

사용 : triple(7), triple.apply(7)   //둘 다 결과 21

 

필드에 lazy 를 사용하면, 그 필드가 인스턴스 될 때만 생성(구현)되도록 할 수 있음

즉, lazy 필드에 처음 접근 할 때 초기화(initial) 됨

 

선언 :

class Lazy {

  val x = { println("now initial"); 1 }

  lazy val y = { println("lazy initial"); 2 }

}

생성 :

val l = new Lazy()  // 여기서 "now initial" 이 출력됨

println(l.y)   // 여기서 lazy val y 가 사용되었으므로, "lazy initial" 이 출력됨

 

 

 

 

 

 

 

기본적으로 Scala 는 프라이버시 제어를 추가하지 않음

우리가 작성한 모든 클래스는 누구나 인스턴스를 생성할 수 있고

클래스 내부 필드와 메소드에 접근 가능

 

하지만 원한다면 프라이버시 제어를 추가할 수 있음

바로 필드 메소드 앞에 protected 혹은 private 을 추가하는 것임

 

protected 가 붙은 필드와 메소드는 동일 클래스 혹은 그 클래스의 자식 클래스에서만 접근 가능하게 됨

 

선언 :

class User { protected val password = "12345" }

class CheckUser extends User { def isValid = ! password.isEmpty }

사용 : 

new User().password   // 접근 불가 에러

new CheckUser().isValid   // 결과 true. 자식 클래스인 CheckUser 에서는 User 의 password 에 접근 가능

 

private 이 붙은 필드와 메소드는 이를 정의한 클래스에서만 접근 가능하게 됨

 

선언 :

class User { private val password = "12345" }

사용 : 

new User().password   // 접근 불가 에러

class CheckUser extends User { def isValid = ! password.isEmpty }   // 자식 클래스에서 접근 불가하여 선언 에러가 발생

 

 

패키지 단위로 접근을 제어할 수 있도록 할 수 있음

 

선언 : 

package com.eyeballs {

  private[eyeballs] class Config {   // com.eyeballs 패키지 내부에서만 접근 가능

    val url = "eyeballs.tistory.com"

  }

  class Test { println(new Config().url) }

}

 

사용 : 

new com.eyeballs.Test   // 결과 : eyeballs.tistory.com

new com.eyeballs.Config   // com.eyeballs 가 아닌 외부 패키지에서 Config 에 접근 불가하기 때문에 에러 발생

 

 

final 을 이용하여, 어떤 클래스의 자식 클래스를 만들지 못하도록 하거나

자식이 부모의 필드, 메소드를 재정의 할 수 없도록 할 수 있음

 

final class A

class B extends A   // A 의 자식을 만들 수 없어 에러 발생

 

class A { final val a = "a" }

class B extends A { val a = "b" }   // 부모 클래스의 필드인 A.a 를 재정의 할 수 없어 에러 발생

 

 

 

 

 

 

 

 

class 와 비슷하지만, 용도가 다른 object, case class 에 대해 설명함

 

object 는 하나 이상의 인스턴스를 가질 수 없는 형태의 class

singleton 이 적용된 class 라고 보면 됨

object 는 new 키워드로 인스턴스를 생성하지 않음

대신 이름으로 직접 해당 객체에 접근함

object 에 최초로 접근할 때 (JVM 내에서) 자동으로 인스턴스화 됨

인스턴스화는 자동으로 생성되므로, 초기화를 위한 매개변수는 갖지 않음 (대신 apply 메소드에 넣을 매개변수는 갖을 수 있음)

 

object 는 다른 class 를 상속받을 수 있음

하지만 다른 class 가 object 를 상속받을 수 없음

왜냐면 object 의 필드, 메소드는 전역에서 접근 가능하므로, 자식 클래스를 만들 이유가 없기 때문

 

선언 : object Hi { println("call Hi"); def hi = "hi" }

사용 : println(Hi.hi)

결과 : call Hi, hi

 

Hi.hi 를 여러번 사용시, 최초 생성된 인스턴스가 재사용됨

singleton 성격을 갖기 때문에, 순수 함수를 구현하거나 DB 를 사용하는 I/O 함수, sparkSession 을 설정하는 용도 등으로 사용

 

class 와 이름이 같은 object 를 동반 객체 라고 부름

동반 객체에서는 class 의 private, protected 필드 및 메소드에 접근 가능함

 

선언 : 

class Multiplier(val x: Int) { def product(y:Int) = x*y }

object Multiplier { def apply(x: Int) = new Multiplier(x) }

 

사용 : 

val tripler = Multiplier(3)   // object 사용

val result = tripler.product(10)   // 결과 30

 

 

 

 

 

 

 

 

case class 는 자동으로 생성된 메소드 몇 가지를 갖은 상태로 (인스턴스가) 생성되는 클래스

case class 는 동반 객체도 자동으로 생성하며, 이 동반 객체도 자신만의 메소드를 자동으로 생성해둠

case class 는 주로 데이터를 저장하고 전송하는 역할로 사용되며

계층적인 클래스 구조를 위해 사용되지 않는 편

왜냐하면 자동으로 만든다는 그 메소드들은 상속받은 필드들은 고려하지 않기 때문

 

자동으로 만들어진다는 메소드들은 다음과 같음

 

이름 위치 설명
apply object (동반 객체) case class 를 인스턴스화하는 팩토리 메소드
copy class 요청받은 변경사항이 반영된 인스턴스의 사본을 반환. 매개변수는 현재 필드값으로 설정된 기본값을 갖는 클래스의 필드들
equals class 다른 인스턴스의 모든 필드가 이 인스턴스의 모든 필드와 일치하면 true 반환. 연산자 == 로도 호출 가능
hashCode class 인스턴스의 필드들의 해시 코드를 반환. 해시 기반의 컬렉션에 유용..
toString class 클래스명과 필드들을 모아 String 으로 반환
unapply object (동반 객체) 인스턴스를 그 인스턴스의 필드들의 튜플로 추출하여 패턴 매칭에 케이스 클래스 인스턴스를 사용할 수 있도록 함

 

선언 : case class Character (name: String, age: Int)

사용 : 

val a = Character ("AA", 2)

val b = a.copy(name="BB")

a == b   // false

 

 

 

 

 

 

 

 

리스트(List). 한 번 생성되면 내부 값을 바꿀 수 없음

내부에서 Linked List 로 구현되어 있음

 

val myList = List()

val myList = List(1,2,3)

val myList = List("a", "b", 1, 2)

myList(0)   // "a"

myList(1)   // "b"

myList(-1)   // error

myList(10)   // error

myList.size  // 4

myList.isEmpty  // false

myList == Nil   // false. 여기서 Nil 은 빈 값을 갖는 리스트인 List() 의 싱글톤 인스턴스

Nil == List()   // true

myList.head   // "a"

val tailList = myList.tail   // "b", 1, 2

for ( l <- myList ) { print(l+" ") }   // a b 1 2

 

foreach 는 함수를 취하고, 그 함수를 리스트의 모든 항목으로 호출함

myList.foreach( l => print(l+" ") )   // a b 1 2

 

map 은 단일 리스트 요소를 다른 값이나 타입으로 전환하는 함수를 취함

val newList = myList.map(l => "["+l+"]")   // [a], [b], [1], [2]

 

reduce 는 리스트 요소들을 앞에서부터 차례대로 두 개씩 선택한 후, 단일 항목으로 결합하는 함수를 취함

val combination = myList.reduce((a,b) => a+" "+b)   // "a b 1 2"

 

리스트를 생성하는 또 다른 방법은 :: 를 사용하는 것

val myList = 1 :: 2 :: 3 :: Nil

val newList = 0 :: myList   //0,1,2,3

 

혹은 두 리스트를 ::: 로 붙이는 것

val twoList = List(1,2) ::: List(3,4)   // 1,2,3,4

 

리스트에 ++ 를 사용하여 다른 컬렉션(이를테면 Set)을 붙일 수 있음

val twoCollections = List(1,2) ++ Set(3,3,3)  // List(1,2,3)

 

:+, +: 를 사용하여 간단하게 List 에 요소를 늘릴 수 있음

List(1,2,3) :+ 4   // List(1,2,3,4). Linked List 의 마지막까지 도달해야하기 때문에 성능 이슈 발생 가능

1 +: List(2,3,4)   // List(1,2,3,4)

 

== 를 사용하여 컬렉션(리스트, 집합.. 등) 간 비교 가능. 두 컬렉션의 타입과 내용이 같으면 true

List(1,2) == List(1,3)   // false

 

drop 으로 List 에서 처음의 n 개 요소를 제외함

val droppedList = List(1,2,3,4) drop 2   //List(3,4)

 

dropRight 로 List 에서 마지막의 n 개 요소를 제외함. Linked List 의 마지막 요소까지 순회해야하므로, 성능 이슈 발생 가능

val droppedList = List(1,2,3,4) dropRight 2   //List(1,2)

 

List 에서 distinct 로 중복 제거

List(1,2,1,2).distinct   // 1, 2

 

List 에 filter 추가하여 true 인 것만 남길 수 있음

val filteredList = List(1,2,3,4,5) filter (_>2)   // 3,4,5

 

nested List 가 포함된 경우, flatten 을 이용하여 내부 요소들을 모두 포함하는 단일 리스트를 만들 수 있음

List(List(1,2), List(3)).flatten   //1,2,3

 

근데 List 가 아닌 리터럴 값이 포함되어 있으면 에러가 발생함

List(List(1,2), List(3), 4).flatten   // error

 

partition 을 사용하여 조건의 참에 해당하는 리스트와 거짓에 해당하는 리스트 두 개를 만듦 (결과는 튜플이 됨)

val part = List(1,2,3,4,5) partition (_<3)

part._1 은 List(1,2)  // true 인 값들

part._2 는 List(3,4,5)   // false 인 값들

 

splitAt 을 사용하여 인덱스 기준으로 List 를 좌우로 쪼갬. 결과는 튜플이 됨

val split = List(1,2,3,4) splitAt 2

split._1 은  List(1,2)

split._2 는 List(3,4)

 

reverse 를 사용하여 List 요소의 순서를 뒤집음

List(1,2,3).reverse   // List(3,2,1)

 

slice 를 사용하여 List 요소의 단편만 가져옴. <=_<

List(1,2,3,4,5) slice (0,0)   // List()

List(1,2,3,4,5) slice (0,1)   // List(1)

List(1,2,3,4,5) slice (0,2)   // List(1,2)

List(1,2,3,4,5) slice (1,2)   // List(2)

List(1,2,3,4,5) slice (2,1)   // List()

 

take 를 사용하여 List 의 처음 n 개 요소만 추출함

List(1,2,3) take 2   // List(1,2)

 

takeRight 를 사용하여 List 의 마지막 n 개 요소만 추출함. Linked List 마지막 요소까지 순회해야 하므로 성능 이슈 발생 가능

List(1,2,3) takeRight 2   // List(2,3)

 

sorted 를 사용하여 List 요소를 정렬함. 사전 순 혹은 오름차순

List(3,2,1).sorted   // List(1,2,3)

List('c','b','a').sorted   // List('a','b','c')

 

sortBy 를 사용하여 원하는 기준으로 List 요소를 정렬함

List("abc","de","f") sortBy (_.size)   // List("f", "de", "abc")

 

zip 을 사용하면, 두 List 를 각 인덱스에 해당하는 요소들끼리 묶인 튜플의 리스트로 만들 수 있음

val z = List(1,2) zip List('a','b')

z 는 List( (1,a), (2,b) )

 

collect 와 case 를 사용하면, List 안의 요소들을 case 에 매칭된 것만 남기고, case 의 내용대로 변환함

List("a", "b", "c") collect {

  case "a" => "A"

}

결과 : List("A")

 

List("a", "b", "c") collect {

  case "a" => "A"

  case "b" => "B"

  case _ => "nothing"

}

결과 : List("A", "B", "nothing")

 

map 을 사용하면, List 안의 모든 요소들에 특정 함수를 적용한 결과값으로 치환함

List("a", "b", "c").map(_.toUpperCase)

결과 : List("A", "B", "C")

 

flatMap 을 사용하면, map 처럼 List 안의 각 요소들에 특정 함수를 적용한 결괏값으로 치환하지만, 

map 과 다르게 모든 결과를 평탄화하여 하나의 List 로 만들어줌

 

List("a,b,c","d,e,f").flatMap(_.split(","))

결과 : List("a","b","c","d","e","f")

 

List("a,b,c","d,e,f").map(_.split(","))

결과 : List(Array("a","b","c"), Array("d","e","f"))

 

List(1,2,3).max  // 3 최댓값

List(1,2,3).min  // 1 최솟값

List(1,2,3).product  // 6 모두 곱하기

List(1,2,3).sum  // 6 모두 더하기

 

contains 를 사용하여 List 내 요소를 포함하고 있는지 확인 가능

List(1,2,3) contains 2   // true

 

exists 를 사용하여 List 내 최소 하나의 요소가 조건자에 성립하는지 확인 가능

List(1,2,3).exists(_<2)   // true

List(1,2,3).exists(_<1)   // false

 

forall 을 사용하여 List 내 모든 요소가 조건자에 성립하는지 확인 가능

List(1,2,3).exists(_<2)   // false

List(1,2,3).exists(_<=3)   // true

 

startsWith 를 사용하여 List 의 처음 요소들이 특정 값을 갖는 List 로 시작하는지 확인 가능

List(1,2,3) startsWith List(1)   // true

List(1,2,3) startsWith List(1,2)   // true

List(1,2,3)startsWith List(1,3)   // false

 

endsWith 를 사용하여 List 의 마지막 요소들이 특정 값을 갖는 List 로 끝나는지 확인 가능

List(1,2,3) endsWith List(3)   // true

List(1,2,3) endsWith List(2,3)   // true

List(1,2,3) endsWith List(1,3)   // false

 

아래서부터 reduce 처럼 List의 값을 하나로 축소하는 함수에 대해 설명함

 

foldLeft 를 사용하여, List 를 주어진 시작값과 함께 왼쪽에서부터 축소

List(1,2,3).foldLeft(0)(_-_)   // -6

이유 :

0 - 1 = -1

-1 - 2 = -3

-3 - 3 = -6

List(1,2,3).foldLeft(1)(_-_)   // -5

List(1,2,3).foldLeft(2)(_-_)   // -4

 

foldRight 를 사용하여, List 를 주어진 시작값과 함께 오른쪽에서부터 축소

List(1,2,3).foldRight(0)(_-_)   // 2

이유 : 

3 - 0 = 3

2 - 3 = -1

1 - -1 = 2

 

List(1,2).foldRight(0)(_-_)   // -1

이유 : 

2 - 0 = 2

1 - 2 = -1

 

reduceLeft 를 사용하여 List 를 첫번째 요소값과 함께 왼쪽에서부터 축소

List(1,2,3).reduceLeft(_-_)   // -4

이유 : 

1 - 2 = -1

-1 - 3 = -4

 

reduceRight 를 사용하여 List 를 마지막 요소값과 함께 오른쪽에서부터 축소

List(1,2,3).reduceRight(_-_)   // 2

이유 : 

2 - 3 = -1

1 - -1 = 2

 

scanLeft 를 사용하여 List 를 주어진 시작값과 함께 왼쪽에서부터 처리한 각 누곗값의 List 를 반환

List(1,2,3).scanLeft(0)(_-_)   // List(0, -1, -3, -6)

이유 : 

처음 주어진 값 = 0 

0 - 1 = -1

-1 - 2 = -3

-3 - 3 = -6

 

scanRight 를 사용하여 List 를 주어진 시작값과 함께 오른쪽에서부터 처리한 각 누곗값의 List 를 반환

List(1,2,3).scanRight(0)(_-_)   // List(2, -1, 3, 0)

이유 : 

처음 주어진 값 = 0

3 - 0 = 3

2 - 3 = -1

1 - -1 = 2

 

reduceLeft, reduceRight 처럼 방향성이 있는 것과

그냥 reduce 처럼 방향성이 없는 것에 차이가 존재함

이를테면, 아래와 같은 연산을 진행할 때

방향이 존재하는 foldLeft, foldRight 는 실행 가능하고 

방향이 존재하지 않는 fold 는 실행이 불가능함

List(1,2,3).foldLeft(false) {(a,b) => if(a) a else b==2}   // true

List(1,2,3).foldLeft(false) {(a,b) => if(a) a else b==4}   // false

List(1,2,3).foldRight(false) {(a,b) => if(b) b else a==2}   // true

List(1,2,3).foldRight(false) {(a,b) => if(b) b else a==4}   // false

List(1,2,3).fold(false) {(a,b) => if(a) a else b==2}   // error

 

 

 

 

 

 

 

 

 

 

집합(Set). 한 번 생성되면 내부 값을 바꿀 수 없음 

 

val mySet = Set()

val mySet = Set(1,2,3)

val mySet = Set("a","b",1,1,2,2)   //"a", "b", 1, 2

 

 

 

 

 

 

 

 

 

 

 

Map (Java 의 HashMap, Python 의 dictionary). 이 역시 생성된 후 내부 값 변경이 불가능

 

val myMap = Map()

val myMap = Map(1->"a", 2->"b")

myMap(1)   // "a"

myMap(2)   // "b"

myMap.contains(1)   // true

myMap.contains(3)   // false

for ( pairs <- myMap ) {

  val key = pairs._1

  val value = pairs._2

  println(key+" "+value)

}

결과 : 1 "a", 2 "b"

 

 

 

 

 

collection 간 전환은 아래와 같이 가능함

 

mkString 를 사용하여 collection 을 구분자로 구분된 String 으로 전환

List(1,2,3).mkString("-")   //"1-2-3"

Set(1,2,3).mkString("-")   //"1-2-3"

 

toBuffer 를 사용하여 collection 을 가변의 List 로 전환

List(1,2,3).toBuffer   // Buffer(1,2,3)

Map(1->1, 2->2, 3->3).toBuffer   // Buffer((1,1), (2,2), (3,3))

 

toList 를 사용하여 collection 을 불변의 List 로 전환

Set(1,2,3).toList   // List(1,2,3)

 

toMap 을 사용하여 튜플이 담긴 collection 을 Map 으로 전환

Set((1,1), (2,2), (3,3)).toMap   // Map(1->1, 2->2, 3->3)

 

toSet 을 사용하여 collection 을 Set 으로 전환

List(1,1,2,2).toSet   // Set(1,2)

 

toString 을 사용하여, collection 의 타입과 내용을 String 으로 전환

List(1,2,3).toString   // "List(1,2,3)"

Set(1,2,3).toString   // "Set(1,2,3)"

 

 

JVM 을 사용하는 Scala 와 Java 간 collection 은 기본적으로 서로 호환되지 않지만

asJava, asScala 를 통해 호환되도록 만들 수 있음

 

import collection.JavaConverters._

val scalaList = List(1,2,3)

scalaList: List[Int] = List(1,2,3)

val javaList = scalaList.asJava

javaList: java.util.List[Int] = [1, 2, 3]

val scalaListAgain = javaList.asScala

scalaListAgain: scala.collection.mutable.Buffer[Int] = Buffer(1,2,3)

 

 

 

 

 

 

collection 을 match 표현식에 사용하는 방법 예제

 

val myList = List(1,2,3)

 

myList(0) match {

  case 1 => "A"

  case _ => "B"

}

결과 : "A"

 

myList(0) match {

  case x if x > 0 => "A"

  case _ => "B"

}

결과 : "A"

 

myList match {

  case x if x contains (2) => "A"

  case _ => "B"

}

결과 : "A"

 

myList match {

  case List(1,

 

 

 

 

 

 

 

call by name, call by ref 차이는?

 

 

still remember I said I'was strong and wan't tired after swimming.

but, turns out, It was really wrong.

after finishing my work and dinner at 7:30, I don't have any energy to do something.

Well, I thought 1 hour between dinner and swimming class was enough to study English or Computer Science.

Now I just lay down on my sofa and watch youtube. thats all.

since Thursday, I didn't want to go to the swimming class....

9PM class ruins my schedule and sucks my energy a lot.

but while taking the class It's very fun to swim.

Thank God It's Friday. really.

during the weekend I don't need to take the class and make time to study English.

Now I'm at a cafe to review this weekend and rearrange my life's dirention.

 

 

 

this is the result chatgpt reviewed.

 

I still remember saying I was strong and wasn't tired after swimming.

It turns out I was really wrong.

After finishing my work and dinner at 7:30PM, I don't have any energy to do anything.

I thought one hour between dinner and my swimming class would be enough to study English or Computer Science.

But now, I just lay down on my sofa and watch youtube. That's all.

Since Thursday, I haven't wanted to go to the swimming class.

The 9PM class ruins my schedule and drains my energy.

But, while taking the class, It's very fun to swim.

Thank God It's Friday. Really.

During the weekend, I don't need to take the class and can make time to study English.

Now, I'm at a cafe to review this weekend and rearrange my life's direction.

 

'English' 카테고리의 다른 글

Study English 24.06.29-07.02  (0) 2024.07.02
Study English 24.06.28  (0) 2024.06.29
Study English 24.06.27  (0) 2024.06.27
Study English 24.06.26  (0) 2024.06.26
Study English 24.06.25  (0) 2024.06.25

 

< Athena >

Athena 는 s3 버킷에 저장된 데이터 대상으로 쿼리를 날릴 수 있는 서비스

Athena 는 SQL 언어를 사용하는 Presto 엔진에 빌드됨

S3 데이터를 RDB 등에 넣지 않고도, s3 데이터 그대로 쿼리 실행이 가능

csv, json, orc, avro, parquet 등 다양한 형식으로 저장된 s3 데이터를 지원

Athena 는 Amazon QuickSight 라는 도구와 함께 사용하는 일이 많음

QuickSight 를 통해, 보고서와 대시보드를 생성함...!

Athena 를 통해, AWS 서비스에서 발생하는 모든 로그를 쿼리하고 분석 가능

 

Athena 성능을 향상하려면, 읽는 데이터 크기를 줄이면 됨.

열 기반 포맷인 Parquet, Orc 를 사용하면 Athena 성능이 향상됨

Parquet, Orc 를 사용하려면 Glue 를 사용해야 함

Glue 는 ETL 을 통해 csv 와 parquet 간 데이터 변환하는데 매우 유용함

성능 향상의 또 다른 방법은, 테이블을 파티션으로 나눠 저장하는 것.

예를들어

s3://mybucket/date=20240625/...

s3://mybucket/date=20240626/...

s3://mybucket/date=20240627/...

s3://mybucket/date=20240628/...

s3://mybucket/date=20240625/...

성능 향상의 다른 방법은, 큰 파일을 사용하여 오버헤드를 최소화 하는 것

크기는 작지만 수가 많은 파일들을 읽는 것보다

크기는 128mb 이상이지만 수는 적은 큰 파일들을 읽을 때 성능이 더 좋음

왜냐면 파일이 클수록 스캔과 검색이 쉽기 때문.

(HDFS 의 block size 같은 느낌인데?)

 

Athena 는 s3 뿐 아니라 어떤 곳의 데이터도 쿼리가 가능함

RDB, NOSQL, on premises 등 어떤 곳이든 가능

이게 어떻게 가능하냐? Data Source Connector 를 사용하면 됨

 

 

쿼리 결과는 s3 버킷에 저장 가능하다고 함

 

 

 

 

 

 

< EMR >

 

Elastic MapReduce 의 약자.

하둡 클러스터를 생성해 줌

EMR 은 EC2 인스턴스의 모음인 클러스터를 구성함

그리고 인스턴스들은 아래처럼 각자 역할을 부여받고 동작함

- Master Node : 클러스터를 관리하고 다른 모든 노드의 상태를 조정함. 오랫동안 실행되는 인스턴스

- Core Node : 태스크를 실행하고, 데이터를 저장함. 이것도 오랫동안 실행되는 인스턴스. cluster 에 처음부터 존재하며, cluster 가 확장될 때는 늘릴 수 있지만 줄이진 못 함.

- Task Node : 태스크를 실행함. Spot instance 사용이 가능. optional 인 노드임. Core Node 와 다른 점은, 일시적으로 데이터 처리 작업을 도와주는 노드라는 것임. (core node 가 할 수 있는 기능인)데이터를 저장하는 작업도 task node는 할 수 없음.  단지 작업 처리를 위한 cpu, ram 등의 리소스를 추가하고 분산 작업해주는 역할을 함. 필요에 따라 늘리고 줄일 수 있음

 

 

 

 

 

< Glue >

 

위에서 언급했듯, Glue 는 ETL 서비스를 관리하는 fully serverless 서비스임

분석을 위한 데이터 변환과 준비에 사용할 수 있음

 

사용례를 살펴보자.

S3 버킷이나 RDS 에 있는 데이터를 DW 인 Redshift 에 로드하는 경우

Glue 를 사용해 추출한 다음, 일부 데이터를 필터링하거나

열을 추가하는 등 원하는 대로 데이터를 변형할 수 있으며

그 최종 결과를 Redshift 에 저장(로드)할 수 있음

 

다른 사용례를 살펴보자.

Glue 는 S3 에 올라간 csv 형태의 데이터를 parquet 으로 변형하고

그 결과를 S3 에 저장할 수 있음

이후 Athena 를 통해 parquet 파일을 쿼리할 수 있음

(물론 Athena 가 csv 도 쿼리 가능하지만,

parquet 처럼 column 기반 데이터 포맷 대상으로 실행하는 쿼리 속도에서 차이가 발생함)

 

Glue Data Catalog 에 대해 알아보자.

Glue Data Catalog 는 datasets 의 카탈로그임.

S3, RDS, DynamoDB, JDBC 등으로부터 데이터를 크롤링 한 다음

Glue Data Catalog 에 데이터에 대한 정보(메타데이터)를 저장함

예를 들면 데이터베이스의 테이블, 열, 데이터 포맷 등등. 모든 메타데이터를 Glue Data Catalog 에 기록

이러한 메타데이터들은, Glue 의 ETL 작업에 활용됨

또한, Athena, EMR 이 데이터와 스키마를 검색할 때 백그라운드에서 Glue Data Catalog 의 데이터를 활용함

예를 들어 Athena 가 어떤 테이블의 파티션 컬럼이 무엇인지 확인할 때

Glue Data Catalog 에서 파티션 컬럼 정보를 갖고옴

(EMR 에서 데이터와 스키마 검색할 때 Data Catalog 를 사용한다고...?)

 

Glue 에 대해 알아야 하는 것들을 살펴보자

- Glue Job Bookmarks 는 새 ETL 작업을 실행할 때 이전 데이터의 재처리를 방지해 줌

- Glue Elastic Views 는 여러 데이터 스토어의 데이터를 (SQL 을 사용하여) 결합하고 복제함

가령 RDS 와 Aurora DB, S3 세 데이터 스토어들 내 테이블들을 SQL 을 사용하여 결합한 View 를 생성 가능함

Glue 가 원본 데이터의 변경 사항을 모니터링 한다고 함

(바로 위에서 설명했듯이) 여러 데이터 스토어에 분산된 가상 테이블(view) 생성 가능함

- Glue DataBrew 는 사전 빌드된 변환을 사용하여 데이터를 정리하고 정규화 함

- Glue Studio 는 ETL 작업들을 생성하고, 실행하고 모니터링하는 GUI 를 제공함

- Glue Streaming ETL 은 ETL 작업을 (배치 작업이 아니라) 스트리밍 작업으로 실행 가능하도록 하며,

실행시 Spark Structured Streaming 위에 빌드되어 작업이 진행됨

 

 

 

 

< Lake Formation >

Data Lake 란, 데이터 분석을 위해 모든 데이터를 한 곳에 저장한 중앙 집중식 저장소임

그리고 Lake Formation 은 Data Lake 생성을 수월하게 해주는 완전 관리형 서비스임

Lake Formation 을 사용하면, 보통 수개월씩 걸리는 작업을 며칠만에 완료할 수 있다고 함

 

Lake Formation 은 Data Lake 에서의 데이터 검색(discover), 정제(cleanse), 변환(transform), 삽입(ingest) 등의 작업을 도와줌

그리고 데이터 수집, 정제나 카탈로깅, 복제 및 복잡한 수작업을 자동화하고

기계 학습 변환 기능으로 중복 제거 수행을 진행할 수 있음

 

Data Lake 에서는 정형 데이터와 비정형 데이터 소스를 결합할 수 있으며, 블루 프린트를 제공함

여기서 말하는 블루프린트는 데이터를 Data Lake 로 migrate 하는 것을 도와주며

S3, RDS, NoSQL 등에서 지원된다고 함

 

Lake Formation 을 사용하는 이유는, 모든 데이터를 한 곳에서 처리할 수 있기 떄문이며

더불어 애플리케이션에서 행, 열 수준의 세분화된 액세스 제어를 할 수 있기 때문임

Lake Formation 에 연결된 애플리케이션에서는 세분화된 액세스 제어가 가능함

 

Lake Formation 은 Glue 위에 빌드되어 실행된다고 함

하지만 Glue 와 직접 상호작업 하지는 않는다고 함

 

예를 들어보자.

Data Lake 로 S3 storage 를 사용하는 상황임

Data Lake 에 넣는 원본 데이터는 RDS, S3, Aurora, NoSQL 등에서 오게 됨

Lake Formation 은 블루프린트를 통해 (바로 위) 원본 데이터들을 Data Lake(s3)로 ingest(주입)함

Lake Formation 에는 Source Crawlers 와 ETL  및 데이터 준비 도구, 데이터 카탈로깅 도구가 포함되어 있어서

크롤러를 통해 원본 데이터 스토리지에서 원본 데이터를 가져와 ETL 을 통해 Data Lake 에 데이터를 주입할 수 있음

Lake Formation 에는 Access Control 기능도 포함되어 있어서

Lake Formation 을 활용하는 Athena, RedShift, EMR, Spark 등의 서비스가 Data Lake 데이터에 접근하는 것을 컨트롤 할 수 있음

 

 

 

Lake Formation 이 사용되는 주요 기능은 바로 access control 기능임

Athena 등에 접근한 사용자들이 Athena 를 통해 Data Lake 의 모든 데이터를 보는 상황을 방지하기 위해

Lake Formation 에서 Athena 에 접근한 사용자별로 어떤 데이터에 읽기 권한을 줄 지, 쓰기 권한을 줄지 설정할 수 있음

 

Lake Formation 이 바라보는 Data Lake 데이터는 S3 에 저장되고

이렇게 저장된 데이터에 접근할 때 필요한 모든 액세스 제어, 행, 열 수준 보안은 Lake Formation 내에서 관리됨

Lake Formation 한 곳에서 보안 관리를 할 수 있다는 것이 큰 장점임

 

따라서 Lake Formation 에 연결하는 사용하는 모든 서비스는 읽기 권한이 있는 데이터만 볼 수 있음

 

 

 

 

 

 

 

 

 

I was invited by my Indian friend to introduce his mother who has stayed for 2 weeks in Kor.

I wanted to show my best to my friend's mother so I bought some Korean tea and 죽 without any meats because she is a vegetarian.

I went to his house at noon and met her mother.

She already prepared Indian food for guests.

the other guests arrived and we had lunch together talking about many nice topics.

Evenyone was from India except for me, Thankfully they used English only for me to understand them.

we also ordered mango 빙수 and enjoyed together.

It was her last day. Hoped she came back home safe.

 

I started to take a swimming lesson for beginners.

The class started at 9pm but I arrived 30 mins earlier just in case.

I made a register card, and entered.

I didn't know how to use 시설 at all, so I just followed how people did.
people visited the locker room first, and took off their cloths.

They took a shower, and then went to the swimming pool.

I couldn't wear my glasses, I saw nothing.

even I didn't know where was my class lane.

Thankfully I found by chance.

We, the beginners, started to walk and sink doing umm-pa umm-pa in the water at first day

50 mins were gone very fast. it meant I enjoyed a lot!

water was cold, so I took a shower with warm water.

I heared people get hungry a lot after swimming but I didn't feel it.

maybe I didn't use my energy a lot... Or I'm very strong lol

Let's see how much I will be different at the end of the class!

 

 

 

 

 

 

ChatGPT revied my diary.

 

I was invited by my Indian friend to meet his mother, who has been staying in Korea for two weeks.

I wanted to make a good impression, so I bought some Korean tea and vegetarian porridge since shw is a vegitarian.

I went to his house at noon and met her. 

She had already prepared Indian food for the guests.

The other guests arrived, and we had lunch together discussing many interesting topics.

Everyone was from India except for me, and thankfully, they spoke in English so I could understand.

We also ordered mang 빙수 and enjoyed it together.

It was her last day, and I hoped she would return home safely.

 

I also started taking a beginner's swimming lesson,

The class began at 9pm but I arrived 30 mins early just in case.

I registered and went in.

Not knowing how to use the facilities, I simply followed what others did.

People visited the locker room first, took off their clothes, showered, and then went to the swimming pool.

I couldn't wear my glasses, so I saw nothing and didn't even know where my class lane was.

Thankfully, I found it by chance.

As beginners, we started by walking and sinking, doing "umm-pa umm-pa" in the water on the first day.

The 50 mins passed very quickly which meant I enjoyed it a lot!

The water was cold, so I took a warm shower afterward.

I had heard that people get very hungry after swimming, but I didn't feel it.

Maybe I didn't use a lot of energy, or I'm just very strong lol

Let's see how much I improve by the end of the class.

 

 

 

 

'English' 카테고리의 다른 글

Study English 24.07.03-05  (0) 2024.07.06
Study English 24.06.28  (0) 2024.06.29
Study English 24.06.27  (0) 2024.06.27
Study English 24.06.26  (0) 2024.06.26
Study English 24.06.25  (0) 2024.06.25

 

 

I joined a puppy owner's group for fun even I don't have my dog now.

One day I would have a nice dog so I wanted to learn how to treat and play them in deep.

Also, I wanted to play many cute puppies to releave my stress lol

The owners were very professional, they know many good tips for them.

They asked me why you joined here even I don't have a dog

I answered, "I think I was born to be an onwer and always have wanted to live with my dog happily!

I took care of my friend's dog for a while temporaly before, and It made me feel very good with him

so I'm here to prepare to be a better and nicer owner"

Their puppies looked very cute.

They were obedient, good at following commands from their owners.

People showed how to walk with dogs and put a harness on.

Well, I already watched that from online but It was very fun to see and learn it in person.

We played some games without alcohole.

Some dogs liked me but couldn't play with them a lot... I should have patting them more.....

It was a nice time and I'd like to hang out with them, they were good people.

 

 

 

 

and this is the one which is ChatGPT reviewed.

 

I joined a puppy owner's group for fun, even though I don't have a dog right now.

One day, I hope to have a nice dog, so I wanted to learn how to treat and play with them.

Also, I wanted to play with many cute puppies to relieve my stress!

The owners were very professional and knew many good tips for caring for dogs.

They asked me why I joined the group if I didn't have a dog.

I answered, "I think I was born to be a dog owner and have always wanted to live happily with a dog!

I took care of my friend's dog for a while, and it made me feel very good.

So, I'm here to prepare to be a better and nicer owner."

Their puppies were very cute.

They were obedient and good at following commands from their owners.

People showed how to walk with dogs and put a harness on.

Although I had already watched that online, it was very fun to see and learn in person.

We played some games without alcohol.

Some dogs liked me, but I couldn't play with them a lot...

I should have petted them more....

It was a nice time, and I'd like to hang out with them again.

They were good people.

 

 

'English' 카테고리의 다른 글

Study English 24.07.03-05  (0) 2024.07.06
Study English 24.06.29-07.02  (0) 2024.07.02
Study English 24.06.27  (0) 2024.06.27
Study English 24.06.26  (0) 2024.06.26
Study English 24.06.25  (0) 2024.06.25

+ Recent posts