모던 자바 인 액션 1장: 자바 8, 9, 10, 11: 무슨 일이 일어나고 있는가?

dev_314·2023년 1월 8일
0

모던 자바 인 액션

목록 보기
1/6

모던 자바 인 액션 1장 학습하고 정리한 내용입니다.

1장. 자바 8, 9, 10, 11: 무슨 일이 일어나고 있는가?

1.1 역사의 흐름은 무엇인가?

자바 8은 다음의 목표를 달성하기 위해 등장했다.

1. 간결한 코드
2. 멀티코어 프로세서의 쉬운 활용

자바 1.0은 Thread, Lock, Memory Model을 지원 -> 저수준 기능을 온전히 사용하기란 어렵다.
자바5는 Thread pool, Concurrent Collection(병렬 실행 컬렉션) 등의 강력한 도구를 제공
자바7은 병렬 실행에 도움을 줄 수 있는 fork/join 프레임워크를 제공함 -> 여전히 활용 어려움

자바 8은 병렬 실행 환경을 쉽게 관리하고 에러가 덜 발생하는 방향으로 진화했다.
자바 9는 리액티브 프로그래밍이라는 병렬 실행 기법을 지원한다. (RxJava를 표준 방식으로써 지원)


자바8은 위의 목표를 다음과 같은 기능으로 달성했다.

1. 스트림 API

  • 마치 SQL이 표현식을 처리하는 것처럼, 병렬 연산을 지원하는 새로운 API
  • 스트림 라이브러리가 최적의 저수준 실행 방법을 선택하는 방식으로 동작
  • synchronized 키워드를 사용하지 않아도 된다. (context switching 비용 절감)

2. 메서드에 코드를 전달하는 기법

  • 메서드 참조와 람다
  • 새롭고 간결한 방식으로 동작 파라미터화(behavior parameterization)을 구현할 수 있게 됨
    • 약간만 다른 메서드를, 파라미터를 통해 다른 동작을 하는 하나의 메서드로 합칠 수 있다.
  • 이러한 자바 8의 기법은 함수형 프로그래밍에서 위력을 발휘한다.

3. 인터페이스의 디폴트 메서드

1.2 왜 아직도 자바는 변화하는가?

  1. 완벽한 프로그래밍 언어는 존재하지 않는다.
  2. 프로그래밍 언어는 계속해서 진화해야 살아남는다.
  3. 프로그래밍 언어는 장단점을 가진다.

과연 자바는 어떻게 변화하여 지금까지 살아남았는가?

1.2.1 프로그래밍 언어 생태계에서 자바의 위치

자바는 출발이 좋았다.

1. 처음부터 많은 유용한 라이브러리를 포함하는, 잘 설계된 OOP언어로 시작함
	- 캡슐화 -> C에 비해 SW Engineering 문제가 훨씬 적다
    - Object Oriented -> WIMP 프로그래밍 모델에 쉽게 대응 가능
        - WIMP: 웹 서버를 운영하는 데 자주 사용되는 소프트웨어 모음
2. 처음부터 Thread, Lock을 이용한 Concurrency를 지원함
3. '소스 코드를 JVM 바이트 코드로 컴파일하는 특징' 덕분에 과거의 인터넷 환경에 적합했다. (애플릿)
4. 임베디드 컴퓨팅 분야를 장악
	- HW 성능 개선 -> C/C++에 비교되는 한계 극복
    - 실행 시간(성능) 중요성 < 개발 시간 중요성
    

그런데 시간이 지나면서, 프로그래머에게 빅데이터 처리라는 새로운 과제가 등장했다.
멀티코어 컴퓨터, 컴퓨팅 클러스터를 이용해서 빅데이터를 효과적으로 처리해야 하는데, 초기 버전의 자바는 이에 부적합했다.

자바 8은 기존의 자바에는 없던 완전히 새로운 개념과 기능을 제공함으로써 시장이 요구하는 기능을 제공한다.

1.2.2 스트림 처리

스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임이다.

프로그램은 입력 스트림에서 데이터를 한 개씩 읽어 들이며, 마찬가지로 출력 스트림으로 데이터를 한 개씩 기록한다.

어떤 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있다.

자바 8에는 java.util.stream 패키지에 스트림 API가 추가되었다.

스트림 API의 핵심은

  1. 기존에는 한 번에 한 항목을 처리했지만, 이제 자바 8에서는 우리가 하려는 작업을 고수준으로 추상화해서 일련의 스트림으로 만들어 처리할 수 있다는 점이다.
  2. 스트림 파이프라인을 이용해서, 입력 부분을 여러 CPU 코어에 쉽게 할당할 수 있다는 부가적 이득도 있다.
  3. Thread라는 복잡한 작업을 사용하지 않으면서도 병렬성을 얻을 수 있게된 것이다.

1.2.3 동작 파리미터화로 메서드에 코드 전달하기

자바 8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다.
이러한 기능을 이론적으로 동작 파라미터화(behavior parameterization)라고 부른다.

스트림 API는 연산의 동작으로 '파라미터화할 수 있는 코드'를 전달한다는 사상에 기초한다.

1.2.4 병렬성과 공유 가변 데이터

스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 한다.
즉, 공유된 가변 데이터에 접근해서는 안 된다. 다른 말로는 해당 코드는 순수 함수이어야 한다.

이전에는 synchronized키워드를 통해 concurrency problem을 해결했는데, 이는 성능에 좋지 못하다.

자바 8은 스트림을 사용해서 기존의 스레드 API보다 쉽게 병렬성을 활용할 수 있도록 한다.

1.2.5 자바가 진화해야 하는 이유

Generic이 등장하여 ListList<String>으로 변경되었다. 이러한 변화를 통해 컴파일 타임의 애러 검출, 가독성이라는 편리함을 누릴 수 있게 되었다.

자바 8을 통해 자바는 고전적인 객체지향에서 벗어나 함수형 프로그래밍으로 다가섰다. 즉, 자바 8을 통해 두 가지 프로그래밍 패러다임의 장점을 모두 활용할 수 있게 되었다.

언어는 HW, 프로그래머의 기대에 부응하는 방향으로 발전해야 한다. 자바도 이에 따라서 변화해야 한다.

1.3 자바 함수

자바의 함수는 부작용을 일으키지 않는 함수를 의미한다.

왜 함수가 필요할까? 프로그래밍 언어의 핵심은 값을 바꾸는 행위이다.

전통적으로 프로그래밍 언어에서 값을 일급 시민이라고 부른다. 자바에서는 기본값, 객체가 일급 시민에 해당한다.

값 = 변경될 수 있으며, 전달될 수 있는 것 = 기본값, 객체

자바의 다양한 구조체(메서드, 클래스 등)이 값의 구조를 표현하는 데 도움을 준다.
그러나 프로그램을 실행하는 동안, 이러한 모든 구조체를 자유롭게 전달할 수는 없다.
전달할 수 없는 구조체는 이급 시민이라 부른다.

이급 시민 = 전달될 수 없는 것 = 메서드, 클래스

자바 8에서는 함수를 새로운 의 형태으로 추가했다.
멀티코어에서 병렬 프로그래밍을 활용할 수 있는 스트림과 연계될 수 있도록 함수를 만들었기 때문이다.
즉, 자바 8은 메서드를 일급 시민으로 사용할 수 있도록 했다.

1.3.1 메서드와 람다를 일급 시민으로

자바 8은 메서드를 값으로 취급할 수 있도록 했고, 이는 스트림과 같은 다른 자바 8기능의 토대가 되었다.

자바 8의 메서드 참조 (method reference) 기능은 메서드를 일급 시민으로 사용할 수 있도록 한다.

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

Object Referecne를 통해 instance를 전달하는 것 처럼, File클래스의 isHidden 메서드에 대한 Method Reference를 생성하여 메서드를 파라미터로 사용할 수 있도록 한다.

익명함수 (lambda)도 일급 함수로 사용될 수 있다.

(int x) -> x + 1 // 파라미터 int x에 1을 더한 값을 반환

굳이 클래스를 만들어 메서드로 사용하지 않아도 된다는 편리함이 있다.

1.4 스트림

거의 모든 자바 App은 Collection을 활용한다.
그런데 로직(fileter 등)이 복잡해질 경우, Collection을 사용한 코드는 길어지고 중복이 발생한다.

이를 스트림 API를 통해 개선할 수 있다.

for-each loop를 통해 각 요소를 반복하면서 작업을 수행하는 외부 반복(external iteration)에 반해, 스트림 API에서는 라이브러리 내부에서 내부 반복(interanl iteration)을 통해 모든 데이터가 처리된다.

또 다른 문제로 Collection을 사용했을 때의 성능 문제를 꼽을 수 있다.
거대한 크기의 Collection을 단일 CPU로는 처리하기 힘들다.

1.4.1 멀티스레딩은 어렵다

그렇다고 이전 자바의 쓰레드 API로 멀티스레딩 코드를 구현해서 병렬성을 이용하기도 여의치 않다.

스트림 API는 두 가지 문제를 해결했다.

1. Collection을 처리하면서 발생하는 '코드의 모호함', '반복'
2. 멀티코어 활용 어려움

스트림 API는 기존 컬랙션을 사용했을 때 반복 등장하는 패턴을 제공한다.

필터링
추출
그룹화

이러한 동작을 사용함과 동시에 쉬운 병렬화라는 장점을 누릴 수 있다.

1. 포크: 한 CPU는 리스트의 앞 부분, 한 CPU는 리스트의 뒷 부분을 처리하도록 요청

	A B C	| 	D E

2. 필터: 각 CPU는 자신이 맡은 리스트를 처리

	B C		| 	E

3. 결과 합침: 하나의 CPU가 두 결과물을 정리

	B C E

컬랙션은 어떻게 데이터를 저장, 접근할지에 대해 중점을 두는 반면, 스트림은 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점을 둔다.

1.5 디폴트 메서드와 자바 모듈

예전에는 외부에서 만들어진 컴포넌트를 사용하기 위해선 JAR 파일을 이용해야만 했다. 그리고 불러온 컴포넌트가 작업 중인 기존 코드와 호환되도록 인터페이스를 변경해야 했다. 이는 곳 인터페이스를 상속한 코드들의 변경을 의미한다.

  1. 자바 8 이상에 작성된 List.stream()을 사용하는 코드를, 자바 8 이전 환경에서 실행하려고 한다.
  2. 그런데 자바 8 이전에는 스트림이 없다. 그래서 Collection 인터페이스에 stream을 메소드로 추가한다.
  3. 새로운 메서드가 추가되었으므로, Collection을 구현하는 concrete class들이 전부 stream메서드를 override해야 한다.

자바 8은 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메소드를 지원한다.
다만 디폴트 메소드를 직접 구현하는 상황은 흔치 않다.

1.6 함수형 프로그래밍에서 가져온 다른 유용한 아이디어

자바 8에서는 NullPointer Exception을 피할 수 있도록 Optional<T> 클래스를 제공한다.
Optional<T>은 값을 갖지 않을 수 있는 컨테이너 객체다.
Optional<T>은 값이 없는 상황을 어떻게 처리할지 명시적으로 구현하는 메서드를 포함한다.

profile
블로그 이전했습니다 https://dev314.tistory.com/

0개의 댓글