스칼라 언어의 핵심 개념

스칼라의 특성과 설계 철학

스칼라는 객체 지향 프로그래밍과 함수형 프로그래밍을 통합한 다중 패러다임 언어로, 데이터 처리 엔진인 Spark의 주요 개발 언어로 사용된다. 주요 특징은 다음과 같다:

  • 다양한 프로그래밍 스타일 지원: 클래스 기반 객체지향과 일급 함수를 활용한 함수형 프로그래밍 모두 가능
  • 자바와의 완전한 호환성: 자바 라이브러리 사용 및 상호작용이 자유롭게 이루어짐
  • 간결한 문법: 타입 추론, 코드 줄 수 감소, 제어 구조의 고도화 제공
  • 정적 타입 시스템: 컴파일 타임 검사로 안정성 향상, 리팩터링 시 안전성 확보
  • 병렬 처리 강력 지원: 커스텀 제어 구조 생성 및 고성능 동시성 처리 가능

기본 프로그래밍 예제: "안녕하세요, 세상"

스칼라에서 실행 가능한 파일은 반드시 object로 정의해야 하며, class는 단순한 클래스 선언에 해당한다. 문장 종료에 세미콜론은 필요 없으며, 자바의 클래스/메서드도 직접 사용할 수 있다.

object Greeting {
  def main(args: Array[String]): Unit = {
    println("안녕하세요, 세상!")
    System.out.println("또 다른 출력 방법")
  }
}

변수와 상수

값이 변경 가능한 변수는 var, 변경 불가능한 상수는 val 키워드로 선언한다. 타입은 값에 따라 자동 추론되며, 명시적으로 선언할 수도 있다.

자바스칼라
byteByte
shortShort
intInt
longLong
floatFloat
doubleDouble
booleanBoolean
charChar

문자열 조작 방식

문자열 결합에는 + 연산자, StringBuilder, mkString 메서드, 또는 s"${변수}" 형태의 문자열 보간 기능이 존재한다. 후자는 내부적으로 StringBuilder를 사용하여 성능 최적화됨.

파일 입출력 처리

자바 방식으로 파일 읽기:

val reader = new BufferedReader(new FileReader("data/demo.txt"))
var line = reader.readLine()
while (line != null) {
  println(line)
  line = reader.readLine()
}

스칼라 전용 방식:

val source = Source.fromFile("data/demo.txt")
for (line <- source.getLines()) {
  println(line)
}

Source.fromFile는 내부적으로 FileInputStream을 사용한다.

예외 처리

자바와 유사한 구조로 예외를 처리하며, try-catch-finally 문법을 지원한다.

try {
  val result = 10 / 2
  val arr = Array(1, 2, 3)
  // 배열 범위 초과 발생 시 예외 발생
  val input = scala.io.StdIn.readInt()
  if (input == 0) throw new ArithmeticException("0으로 나누기 금지")
} catch {
  case _: ArithmeticException => println("나눗셈 오류")
  case _: ArrayIndexOutOfBoundsException => println("배열 인덱스 오류")
  case _ => println("기타 오류 발생")
} finally {
  println("종료 처리")
}

함수 정의 및 호출

함수는 def 키워드로 정의하며, 반환 타입과 매개변수 형식을 명시한다. Unitvoid와 동일하게 반환 값이 없다는 의미이다.

// 기본 함수 정의
def add(a: Int, b: Int): Int = a + b

// 단일 표현식은 중괄호 생략 가능
def multiply(a: Int, b: Int) = a * b

// 매개변수가 없을 경우 소괄호 생략 가능
def greet = println("안녕하세요!")

클래스와 객체

스칼라에서는 클래스를 통해 상태와 행동을 정의할 수 있으며, 생성자 파라미터는 클래스 본체 내에서 직접 사용된다.

class Person(val name: String, val age: Int) {
  var gender: String = _

  // 추가 생성자
  def this(name: String, age: Int, gender: String) = {
    this(name, age)
    this.gender = gender
  }

  override def toString = s"이름:$name, 나이:$age, 성별:$gender"
}

// 객체 생성 및 호출
val person = new Person("홍길동", 25, "남성")
println(person)

샘플 클래스 (Case Class)

스칼라의 case class는 자동으로 생성자, equals, hashCode, toString 등을 생성하여 코드 작성량을 획기적으로 줄여준다.

case class Teacher(name: String, age: Int, var hobby: String)

val teacher = Teacher("김선생", 40, "공부")
teacher.hobby = "영화 보기"
println(teacher)

apply 메서드와 함께하는 객체 팩토리 패턴

객체에 apply 메서드를 정의하면, 객체 이름만으로 인스턴스 생성이 가능하다.

object Book {
  def apply(title: String, price: Int) = new Book(title, price)
}

class Book(var title: String, var price: Int) {
  override def toString = s"제목:$title, 가격:$price"
}

// 사용 예시
val book = Book("스칼라의 세계", 29000)
println(book)

함수형 프로그래밍의 핵심: 함수를 값처럼 다루기

스칼라에서는 함수 자체도 객체이며, 이를 변수에 할당하거나 다른 함수의 인자로 전달할 수 있다.

// 함수 타입 정의: String → Int
val stringToInt: String => Int = _.toInt

// 함수를 인자로 받는 함수
def process(f: String => Int, input: String): Int = f(input) + 100

// 사용 예시
val result = process(stringToInt, "200") // 결과: 300

고차원 함수 (Higher-Order Functions)

스칼라의 컬렉션은 다양한 고차원 함수를 제공한다.

  • foreach: 각 요소에 대해 처리, 반환 없음
  • map: 각 요소를 변환하여 새로운 컬렉션 반환
  • filter: 조건에 맞는 요소만 유지
  • flatMap: 매핑 후 평탄화 (일반적인 변환 + 리스트 병합)
  • groupBy: 특정 기준에 따라 그룹화

컬렉션 유형

  • List: 순서 유지, 중복 허용, 불변
  • Set: 순서 무관, 중복 불허, 불변
  • Map: 키-값 쌍 저장, 키는 유일
  • Tuple: 고정된 크기, 각 요소의 타입이 다를 수 있음 (최대 22개)

가변 컬렉션 사용

불변 컬렉션이 아닌 가변 컬렉션은 scala.collection.mutable 패키지에서 찾을 수 있다.

import scala.collection.mutable.ListBuffer

val buffer = new ListBuffer[Int]
buffer += 10; buffer += 20; buffer += 10
buffer -= 10 // 10 삭제
println(buffer.toList)

맵과 튜플 활용

맵은 키를 통해 값을 가져올 수 있으며, get 메서드는 Option을 반환하여 존재 여부를 안전하게 확인할 수 있다.

val scores = Map("Alice" -> 95, "Bob" -> 87)
println(scores.getOrElse("Charlie", 0)) // 0 반환 (없는 키에 대한 기본값)

실습: 단어 빈도 분석

텍스트 파일에서 각 단어의 등장 횟수를 집계하는 예제.

Source.fromFile("words.txt")
  .getLines()
  .toList
  .flatMap(_.split("\\|"))
  .groupBy(identity)
  .mapValues(_.size)
  .toList
  .foreach { case (word, count) => println(s"$word: $count") }

확장 기능: JDBC 및 JSON 처리

JDBC 연결, 자바 컬렉션과 스칼라 컬렉션 간의 변환, match 식을 통한 패턴 매칭 등 실용적인 기능도 포함되어 있다.

암시적 전환 (Implicit Conversion)

스칼라의 암시적 전환은 타입 간 자동 변환을 가능하게 한다.

  • 암시적 함수: 특정 타입을 다른 타입으로 자동 변환
  • 암시적 클래스: 문자열을 특정 객체로 자동 변환 가능
  • 암시적 변수: 함수의 인자에 암시적 값 제공

태그: 스칼라 함수형 프로그래밍 컬렉션 암시적 전환 apply 메서드

5월 25일 12:38에 게시됨