---
# System prepended metadata

title: Scala School - 기초 2

---

# Scala School - 기초 2
Origin: http://twitter.github.io/scala_school/ko/basics2.html


### [기초(계속)](https://hackmd.io/@2minchul/r1szGkqlr)
*   [«Previous](https://hackmd.io/@2minchul/ryPMp0teH)
*   [Next»](http://twitter.github.io/scala_school/ko/collections.html)


이번 강좌에서 다루는 내용은 다음과 같다.

*   [apply 메소드](#apply-메소드)
*   [객체](#객체)
*   [함수도 객체다](#함수도-객체다)
*   [패키지](#패키지)
*   [패턴 매치](#패턴-매치)
*   [케이스 클래스](#케이스-클래스)
*   [try-catch-finally 예외 처리](#try-catch-finally-예외-처리)

## apply 메소드

apply 메소드를 사용하면 클래스나 객체의 용도가 주로 하나만 있는 경우를 아주 멋지게 표현할 수 있다.

```
scala> class Foo {}
defined class Foo

scala> object FooMaker {
     |   def apply() = new Foo
     | }
defined module FooMaker

scala> val newFoo = FooMaker()
newFoo: Foo = Foo@5b83f762
```

위와 같이 사용하거나, 다음과 같이 쓸 수 있다.

```
scala> class Bar {
     |   def apply() = 0
     | }
defined class Bar

scala> val bar = new Bar
bar: Bar = Bar@47711479

scala> bar()
res8: Int = 0
```

apply를 정의하면 메소드를 호출하듯 객체 인스턴스를 호출할 수 있다. 객체 인스턴스를 호출하면 그 객체(클래스)에 정의된 apply()가 호출된다. 자세한 것은 나중에 살펴볼 것이다.

## 객체

객체(여기서는 object 키워드로 선언하는 객체를 말함)는 클래스의 유일한 인스턴스를 넣기 위해 사용한다. 보통 팩토리에 사용된다.

```
object Timer {
  var count = 0

  def currentCount(): Long = {
    count += 1
    count
  }
}
```

위와 같이 정의하면 다음과 같이 사용할 수 있다.

```
scala> Timer.currentCount()
res0: Long = 1
```

클래스와 객체가 같은 이름을 가질 수도 있다. 이런 객체는 ‘짝 객체(Companion Object)’라 한다. 보통 팩토리를 만들 때 짝 객체를 사용한다.

다음 예는 ’new’를 사용하지 않고 새 객체를 만들 수 있음을 보여준다.

```
class Bar(foo: String)

object Bar {
  def apply(foo: String) = new Bar(foo)
}
```

## 함수는 객체이다

스칼라에 대해 이야기할 떄, 객체-함수형 프로그래밍이라는 말을 하고는 한다. 그 말이 무슨 뜻일까? 함수란 실제로 무엇일까?

함수는 트레잇의 집합이다. 구체적으로 말하자면, 인자를 하나만 받는 함수는 `Function1` 트레잇의 인스턴스이다. 이 트레잇에는 앞에서 설명했던 `apply()`가 정의되어 있다. 따라서 함수를 호출하듯 객체를 호출할 수 있다.

```
scala> object addOne extends Function1[Int, Int] {
     |   def apply(m: Int): Int = m + 1
     | }
defined module addOne

scala> addOne(1)
res2: Int = 2
```

스칼라에는 Function이 1부터 22까지 준비되어 있다. 22인 이유는? 그냥 그렇게 정한 것이다. 저자는 인자가 22개 보다 더 많이 필요한 함수를 본 적이 없다. 22개면 충분하리라 본다.

apply를 통한 편리 문법(syntactic sugar)을 통해 객체와 함수 프로그래밍 양쪽을 잘 통합할 수 있다. 여러분은 클래스를 여기저기 넘기면서 함수 처럼 호출해 사용할 수 있고, 함수는 한꺼풀 벗겨보면 단지 클래스의 인스턴스일 뿐이다.

그렇다면 클래스의 메소드를 정의할 때마다 실제로 Function*의 인스턴스가 만들어지는 걸까? 아니다. 클래스 내부의 메소드는 메소드이다. repl(read eval print loop. 입력을 받아 값을 계산하고 결과를 출력하는 루프. 스칼라 인터프리터라 생각하면 대략 맞다)에서 정의한 개별 메소드는 Function*의 인스턴스이다.

Function*을 확장한 클래스를 정의할 수도 있다. 물론 이런 클래스도 ()로 호출할 수 있다.

```
scala> class AddOne extends Function1[Int, Int] {
     |   def apply(m: Int): Int = m + 1
     | }
defined class AddOne

scala> val plusOne = new AddOne()
plusOne: AddOne = <function1>

scala> plusOne(1)
res0: Int = 2
```

`extends Function1[Int, Int]`는 `extends (Int => Int)`라고 더 알아보기 쉽게 쓸 수 있다.

```
class AddOne extends (Int => Int) {
  def apply(m: Int): Int = m + 1
}
```

## 패키지

코드를 패키지로 구성할 수 있다.

```
package com.twitter.example
```

위와 같이 파일의 맨 앞에서 선언하면 파일 내의 모든 것이 위 패키지 안에 포함된다.

값이나 함수는 클래스나 객체 바깥에 존재할 수 없다. 객체(여기서도 object로 선언한 객체를 의미함)를 사용하면 정적인(자바의 정적 함수와 동일) 함수를 관리하기 쉽다.

```
package com.twitter.example

object colorHolder {
  val BLUE = "Blue"
  val RED = "Red"
}
```

이제 직접 객체의 멤버를 사용할 수 있다.

```
println("the color is: " + com.twitter.example.colorHolder.BLUE)
```

여러분이 이렇게 객체를 정의하면 스칼라 repl은 다음과 같이 표시해준다.

```
scala> object colorHolder {
     |   val Blue = "Blue"
     |   val Red = "Red"
     | }
defined module colorHolder
```

모듈이라고 repl이 응답하는 것에 유의하라. 이는 스칼라 언어를 설계시 객체를 모듈 시스템의 일부로 생각하고 설계했음을 보여준다.

## 패턴 매칭

패턴 매치는 스칼라에서 가장 유용한 기능 중 하나이다.

값에 대해 매칭할 수 있다.

```
val times = 1

times match {
  case 1 => "one"
  case 2 => "two"
  case _ => "some other number"
}
```

가드(조건문)를 사용해 매칭할 수 있다.

```
times match {
  case i if i == 1 => "one"
  case i if i == 2 => "two"
  case _ => "some other number"
}
```

변수 ’i’에 어떻게 값을 잡아 넣었는지 주의깊게 살펴보라.

마지막 경우의 `_`는 와일드카드이다. 즉, 모든 경우를 처리한다. 만약 이 부분이 없다면 매치되지 않는 값이 들어온 경우 런타임 에러가 발생할 것이다. 이에 대해서는 나중에 살펴보겠다.

**See Also** 효율적인 스칼라에서 [패턴매치를 사용해야 하는 경우](https://twitter.github.com/effectivescala/#Functional programming-Pattern matching)와 [패턴 매칭을 어떤 형식으로 할지](https://twitter.github.com/effectivescala/#Formatting-Pattern matching)에 대해 설명한다. 스칼라 여행에서도 [패턴매칭](https://www.scala-lang.org/node/120)을 다룬다.

### 타입에 대해 매치시키기

`match`를 사용해 타입이 다른 값을 서로 다른 방식으로 처리 가능하다.

```
def bigger(o: Any): Any = {
  o match {
    case i: Int if i < 0 => i - 1
    case i: Int => i + 1
    case d: Double if d < 0.0 => d - 0.1
    case d: Double => d + 0.1
    case text: String => text + "s"
  }
}
```

### 클래스 멤버에 대해 매치시키기

앞에서 봤던 계산기 예제를 다시 떠올려보자.

타입(계산기의 유형)에 따라 계산기를 구분하자.

```
def calcType(calc: Calculator) = calc match {
  case calc.brand == "HP" && calc.model == "20B" => "financial"
  case calc.brand == "HP" && calc.model == "48G" => "scientific"
  case calc.brand == "HP" && calc.model == "30B" => "business"
  case _ => "unknown"
}
```

아이구, 힘들어 죽겄다. 스칼라는 이런 처리를 쉽게 할 수 있는 도구를 제공한다.

## 케이스 클래스(case class)

케이스 클래스는 손쉽게 내용을 어떤 클래스에 저장하고, 그에 따라 매치를 하고 싶은 경우 사용한다. new를 사용하지 않고도 케이스 클래스의 인스턴스 생성이 가능하다.

```
scala> case class Calculator(brand: String, model: String)
defined class Calculator

scala> val hp20b = Calculator("HP", "20b")
hp20b: Calculator = Calculator(hp,20b)

```

케이스 클래스는 자동으로 생성자 인자에 따른 동등성 검사를 제공하며, 또한 보기 좋은 toString 메소드도 제공한다.

```
scala> val hp20b = Calculator("HP", "20b")
hp20b: Calculator = Calculator(hp,20b)

scala> val hp20B = Calculator("HP", "20b")
hp20B: Calculator = Calculator(hp,20b)

scala> hp20b == hp20B
res6: Boolean = true
```

케이스 클래스 안에도 일반 클래스와 똑같이 메소드를 정의할 수 있다.

###### 케이스 클래스와 패턴 매칭

케이스 클래스는 패턴 매치와 사용하기 위해 설계된 것이다. 앞의 계산기 분류 예제를 간략하게 만들어보자.

```
val hp20b = Calculator("HP", "20B")
val hp30b = Calculator("HP", "30B")

def calcType(calc: Calculator) = calc match {
  case Calculator("HP", "20B") => "financial"
  case Calculator("HP", "48G") => "scientific"
  case Calculator("HP", "30B") => "business"
  case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
```

마지막 매치는 다음과 같이 쓸 수도 있다.

```
  case Calculator(_, _) => "Calculator of unknown type"
```

혹은, 그냥 calc가 계산기인지 아닌지도 명시하지 않아도 된다.

```
  case _ => "Calculator of unknown type"
```

아니면, 매치된 값에 다른 이름을 붙일 수도 있다.

```
  case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
```

## 예외

스칼라에서는 예외 처리시 try-catch-finally 문법에 패턴 매치를 사용할 수 있다.

```
try {
  remoteCalculatorService.add(1, 2)
} catch {
  case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.")
} finally {
  remoteCalculatorService.close()
}
```

`try` 역시 식 중심의 구문이다.

```
val result: Int = try {
  remoteCalculatorService.add(1, 2)
} catch {
  case e: ServerIsDownException => {
    log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.")
    0
  }
} finally {
  remoteCalculatorService.close()
}
```

이렇게 하는게 좋은 프로그램 스타일은 아니다. 위 내용은 단지 다른 대부분의 스칼라 구성 요소와 마찬가지로 try-catch-finally도 결과값을 내는 식임을 보여주기 위한 예일 뿐이다.

finally는 예외가 처리(catch)된 다음에 실행될 것이다. 이 부분은 전체 식의 일부가 아니다. 예외가 발생하지 않으면 try {} 안의 마지막 식의 값이 try-catch-finally 전체의 값이 되고, 예외가 발생하는 경우에는 catch 안의 식의 값이 전체 식의 최종 값이 된다.

<hr />

*   [Next»](collections.html)

Built at [@twitter](https://twitter.com/twitter) by [@stevej](https://twitter.com/stevej), [@marius](https://twitter.com/marius), and [@lahosken](https://twitter.com/lahosken) with much help from [@evanm](https://twitter.com/evanm), [@sprsquish](https://twitter.com/sprsquish), [@kevino](https://twitter.com/kevino), [@zuercher](https://twitter.com/zuercher), [@timtrueman](https://twitter.com/timtrueman), [@wickman](https://twitter.com/wickman), [@mccv](https://twitter.com/mccv) and [@garciparedes](https://github.com/garciparedes); Russian translation by [appigram](https://github.com/appigram); Chinese simple translation by [jasonqu](https://github.com/jasonqu); Korean translation by [enshahar](https://github.com/enshahar);  

Licensed under the [Apache License v2.0](https://www.apache.org/licenses/LICENSE-2.0).