반응형

블로그및 사이트에 후원 위젯을 받을 수 있는 사이트를 소개하겠습니다.

https://www.buymeacoffee.com/

 

Buy Me A Coffee | Where creators make money doing what they love

Buy Me A Coffee is the best way for creators and artists to accept one-off support and membership from their fans.

www.buymeacoffee.com

위 사이트 이동 후

구글 및 페이스북 계정으로 연동계정으로 빠르게 가입~

가입 후 화면

Settings 메뉴 > General > Payments 를 PayPal 로 연동

PayPal 계정이 없다면 계정생성 후 연동관련 메일을 받아 확인하면 됩니다.

 

여기에서 블로그를 작성하고 블로그 처럼 사용하실 수도 있지만,

해당 사이트의 좋은 기능 중 하나가 버튼 위젯을 내 블로그나 사이트에 추가 가능 하다는 점입니다.

 

Overview >

Button generator 로 html code 를 생성후 내 블로그 html 에 붙여넣어 사용하실 수도 있고,

저의 경우 Widget generator 를 제 블로그에 삽입하였습니다.

 

둘의 차이는 Widget generator 는 

코드를 삽입한 블로그 자체에서 결제까지 가능.

 

Button generator 는 링크로 내 블로그로 이동됩니다.

 

 

저의 경우 이 과정 중에서 혼선이 있었던 점은

내가 로그인한 buymeacofee, 즉 본인 후원 결제는 안되기 때문에 왜 안되는지 조금 헤맸습니다.

반응형

'서비스평가 및 사용' 카테고리의 다른 글

[heroku] 시작하기 java 배포  (0) 2021.04.29
Github gist 사용해보기  (0) 2020.05.22
정리잘 된 개발 블로그 정리  (1) 2020.05.15
반응형

STS > File > New > Spring Starter Project
Intellij > File > new > project > Spring Initializr

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootBoilerPlateApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootBoilerPlateApplication.class, args);
  }
}

maven으로 프로젝트 빌드

프로젝트 우클릭 > Run as > maven clean > maven install > maven build.. > Spring boot app 으로 프로젝트 시작

~

~

Application 클래스의 main 메소드가 시작된다.
이 때 해당 클래스 명이 아니더라도 @SpringBootApplication 어노테이션이 있는 클래스의 main 메소드가 실행됨

@SpringBootApplication

어노테이션은 이 3개의 어노테이션을 모두 담고 있는 어노테이션이다.

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

스프링부트가 나오기 전까지는 ViewResolver를 설정하기 위해서 application-context.xml이 servlet-context.xml에
추가하거나, JavaConfig 기반의 환경일 경우, @Configuration 클래스 - WebMvcConfig등의 클래스 - 에 ViewResolver설정을 해줘야 했었다.

먼저 간단한 Controller 클래스를 만드는데, 클래스가 만들어지는 위치가 중요하다.
스프링부트는 메인클래스의 설정에 의해 컴포넌트스캔을 한다고 했었는데,
스프링부트는 컴포넌트 스캔을 할 때, 기본적으로 메인클래스가 있는 위치를 기준으로 스캔을 하게된다.

ComponentScan 

만약, AutoScan이 되어야 하는 컴포넌트 클래스들 - 대표적으로 @Controller, @Service, @Repository, @Component등-의 위치가 메인클래스가 위치한 패키지보다 상위 패키지에 있거나, 하위가 아닌 다른 패키지에 있는 경우, 스캔이 되지 않는다.

이런 문제를 해결하기 위해서는,
명시적으로 ComponentScan을 할 Base Package를 지정해주면 된다.

@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
메인클래스에 Annotation을 추가함으로써 SpringBoot시작시 실행되는 Auto Configuration 작업중 DatatSource 설정부분을 제외시킬 수 있다.

@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})  // Datasource설정은 자동설정에서 제외
@ComponentScan(basePackages = "com.boilerPlate.app")

 

SpringBootApplication문서

 

반응형
반응형

빈 라이프사이클 관리

  1. 인터페이스 기반
    InitializingBean 인터페이스 구현한 afterPropertiseSet 메소드
    모든 인스턴스의 콜백을 한번에 지정 가능, 스프링과 결합을 해야함

  2. 빈 생성시 메소드 실행
    init-method
    빈의 메서드 하나를 지정해 초기화 콜백으로 사용하겠다고 스프링에 설정
    빈이 몇개 안되거나 애플리케이션이 스프링과 결합되지 않게 할 때 유용
    인자를 받지 못하는 것이 단점
    init 메소드는 private 으로 사용 권고

  3. 애너테이션 사용
    @PostConstruct
    사용법은 init-method 와 동일하고 @PostConstruct 만 메소드 상단에 추가
    메소드명은 아무이름이나 사용 가능
    init 메소드는 private 으로 사용 권고
    @Bean으로 초기화메소드 사용
    @Bean 애너테이션에 initMethod 추가
    @Bean(initMethod = "init")

소멸시점

  1. bean 태그에 destroy-method 애트리뷰트 추가하고 메소드 이름 기입
    @Bean 을 사용해 메소드 정의
    @Bean(destroyMethod="destroy")

  2. DisposableBean 인터페이스를 구현한 destroy 메소드

  3. @PreDestroy 애너테이션

    BeanNameAware 인터페이스

    라이프사이클 콜백 호출전 setBeanName() 메소드를 호출
    BeanNameAware 인터페이스를 사용해 생성자등에 로그메시지를 넣기 좋음

반응형
반응형

블로그 플랫폼 관련된 글을 보면 종종 gist 라는 말이 나온다.

 

github 에서 만든 코드 및 글을 공통으로 사용하기 위해 만든 저장소 라고 보면 된다.

https://gist.github.com/

 

Discover gists

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

 

우선 시작으로 아무 글이나 적어봤다.

오른쪽 위의 Embed 로 나타난 스크립트 소스를 내 블로그에 담으면 해당 내용을 사용할 수 있다.

여러 블로그를 운영하는 사람한테 좋은 기능일 것 같다.

 

github 를 안쓰고 개인 pc 에 gitlab 을 호스팅해서 안쓰려고 했는데, github 에는 좋은 기능들이 많다....

 

위에서 만든 test 글 스크립트를 넣었다. 

아래 글을 수정하려면 gist 를 고치면 된다.

반응형
반응형

사전작업

  1. 파이썬 설치
  2. pip 설치
  3. http 요청에 필요한 라이브러리 python requests 라이브러리 설치
    pip install requests

검색어 파싱하기

원래는 html 파일 파싱하려고 했으나
2020 5월 19일 기준 네이버 검색어는 api 로 조회하고 있어, 크롬 네트워크 탭에서 api 주소 확인 후 소스코드에 복붙하고 json 데이터 파싱

import requests
# from bs4 import BeautifulSoup
import json

response = requests.get('https://apis.naver.com/mobile_main/srchrank/srchrank?frm=main&ag=30s&gr=2&ma=0&si=0&en=0&sp=0')

html = response.text

# soup = BeautifulSoup(html, "html.parser")

json_dict = json.loads(html)

for i in json_dict['data']:
  print(i['keyword'])

다음과 같이 소스 파일 작성한 후
python 실행!!

python 파일명.py

 

 

반응형

'Languague > Python' 카테고리의 다른 글

[Python] 파이썬 Non-ASCII character 오류(파이썬 한글 인코딩)  (0) 2020.09.29
[Python] pip 설치  (0) 2020.05.19
반응형

윈도우 설치

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py

위 명령어 전에 파이썬을 설치해야 한다.
http://www.python.org/downloads

 

Download Python

The official home of the Python Programming Language

www.python.org

 

mac 설치

1. pip 설치여부 확인

[1] 파이썬3 및 파이썬2.7.9이상

파이썬3을 사용중이면 pip3가 기본으로 설치되어 있다.

- pip3 설치여부 확인

$ pip3

 

있으면 이거 쓰면 된다. 업그레이드 해준다.

- pip3 버전 확인 및 업그레이드

$ pip3 -V
$ pip3 install --upgrade pip
반응형
반응형

동작 파라미터화 코드 전달하기

소비자의 요구사항은 항상 바뀌기 마련입니다. 이런 변화하는 요구사항에 대해 효과적으로 대응하기 위해서 동작 파라미터화(behavior parameterization)
을 이용하면됩니다.

동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미합니다. 코드 블록은 나중에 프로그램에서 호출한다. 즉, 코드 블록의 실행은 나중으로 미뤄진다.

EXAMPLE

기존의 농장 재고목록 애플리케이션에 리스트에서 녹색(green) 사과만 필터링하는 기능을 추가한다고 가정하자.

첫 번째 시도 : 녹색사과 필터링

사과 색을 정의하는 ENUM 클래스

enum Color { RED, GREEN }

첫 번째 시도

public static List<Apple> filterGreenApples(List<Apple inventory) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(GREEN.equals(apple.getColor)) {
      result.add(apple);
    }
  }
}

만약 농부가 빨간 사과도 필터링 하고 싶다고하면 어떻게 할까? 메서드를 하나 더만들 수 도 있지만 만약 또 농부가 노란색, 어두운 빨간색등 요구사항이
점차 늘어난다면 메서드가 많이 생길 것이다. 이런경우 다음과 같은 좋은 규칙이 있다.

거의 비슷한 코드가 반복 존재한다면 그 코드를 추상화 한다.

두 번째 시도 : 색을 파라미터화

public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(apple.getColor().equals(color)) {
      result.add(apple);
    }
  }
}

이제 농부가 어떤 색을 원하든 해당 메서드만 호출하면, 색으로 필터링 할 수 있다. 하지만 농부가 색 이외에도 무거운 사과와 가벼운 사과, 무게로
사과를 필터링하고 싶다고하면 어떻게 해야 할까?

색을 파라미터화 한 것처럼 무게를 파라미터화 하면 된다.

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(apple.getWeight() > weight) {
      result.add(apple);
    }
  }
  return result;
}

위 코드도 좋은 해결책이지만, 각 사과에 필터링 조건을 적용하는 부분의 코드가 색 필터링 코드와 대부분 중복된다.

이는 소프트웨어 공학의 DRY(Don't Repeat yourself) 같은 것을 반복하지말 것 원칙을 어기는 것이다. 이를 해결하기 위해서 색과 무게를 filter라는
메서드로 합치는 방법도 있다. 따라서 색이나 무게 중 어던 것을 기준으로 필터링할 지 가리키는 플래그를 추가할 수 있다.
(하지만 실전에서는 절대 이 방법을 사용하지 말아야 한다.)

절대 사용하지 말아야 하는 방법

  • 절대 사용하지 말아야 하는 방법
public static List<Apple> filterApples(List<Apple> inventory, Color color, int weight, boolean flag) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight)) {
      result.add(apple);
    }
  }
  return result;
}

위 메서드를 아래처럼 호출 할 수 있다.

List<Apple> greenApples = filterApples(inventory, GREEN, 0, true);
List<Apple> heavyApples = filterApples(inventory, null, 150, false);

하지만 정말 마음에 들지 않는 코드이다. 대체 true와 false는 무엇을 의미하는 것일까?... 게대가 앞으로 요구사항이 바뀌었을때 유연하게 대처할 수 없다.
에를 들어 사과으 크기, 모양, 출하지 등으로 사과를 필터링 하고싶어지는 경우 어떻게 해야할까?

아래에서 동작 파라미터화를 이용해서 유연성을 얻는 방법에 대해 설명한다.

동작 파라미터화

사과의 어떤 속성에 기초해서 불리언 값을 반환(예를 들어 사과가 녹색인가? 150그램 이상인가?). 참 또는 거짓을 반환하는 함수를 프레디케이트 라고 한다.
선택 조건을 결정하는 인터페이스를 정하자.

public interface ApplePredicate {
  boolean test (Apple apple);
}

다음 예제 처럼 다양한 선택 조건을 대표하는 여러 버전의 ApplePredicate를 정의할 수 있다.

public class AppleHeavyWeightPredicate implements ApplePredicate {
  public boolean test(Apple apple) {
    return apple.getWeight() > 150;
  }
}

public class AppleGreenColorPredicate implements ApplePredicate {
  public boolean test(Apple apple) {
    return GREEN.equals(apple.getColor());
  }
}

위 조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상할 수 있다. 이를 전략 디자인패턴(strategy design patter)이라고 부른다.

전략 디자인 패턴은 각 알고리즘(전략이라 부르는)을 캡슐화 하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘을 선택하는 기법이다.

여기서는 ApplePredicate가 알고리즘 패밀리이며, 이를 구현한 클래스들이 전략이다.

네 번째 시도 : 추상적 조건으로 필터링

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(p.test(apple)) {
      result.add(apple);
    }
  }
  return result;
}

첫 번째 코드에 비해 더 유연한 코드를 얻었으며 동시에 가독성도 좋아졌을 뿐 아니라 사용하기도 쉬워 졌다.

예를 들어 농부가 150그램이 넘는 빨간 사과를 검색해달라고 부탁하면 우리는 ApplePredicate를 적절하게 구현하는 클래스만 만들면 된다.

public class AppleRedAndHeavyPredicate implements ApplePredicate {
  public boolean test(Apple apple) {
    return RED.equals(apple.getColor()) && apple.getWeight() > 150;
  }
}

우리가 전달한 ApplePredicate 객체에 의해 filterApples 메서드의 동작이 결정된다. 즉, 우리는 filterApples 메서드의 동작을 파라미터화 한 것이다.

즉, 우리는 전략 디자인 패턴(Strategy Design Pattern)과 동작 파라미터화를 통해서 필터 메서드에 전략(Strategy)을 전달 함으로써 더 유연한 코드를
만들었다.

한 개의 파라미터, 다양한 동작

지금까지 살펴본 것첢 컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있다는 것이 동작 파라미터화의 강점이다.

QUIZ

사과 리스트를 인수로 받아 다양한 방법으로 문자열을 생성(커스터마이즈된 다양한 toString) 메서드와 같이) 할 수 있도록 파라미터화된 prettyPrintApple 메서드를 구현하시오. 예를 들어 prettyPrintApple 메서드가 각각의 사과 무게를 출력하도록 지시할 수 있다. 혹은 각각의 사과가 무거운지, 가벼운지
출력하도록 지시할 수 있다.

  • interface 만들기

참 또는 거짓을 반환하는 함수를 프레디케이트 라고 했습니다. 위 문제에서 String 타입을 반환하라고 했으니 interface 네이밍을 xxxPredicate보다는
Formatter라든지 이 외의 다른 좋은 네이밍으로 정하는게 좋습니다.

public interface AppleFormatter {
  String accept(Apple a);
}

public class AppleFancyFormatter implements AppleFormatter implements AppleFormatter {
  public String accept(Apple apple) {
    String characteristic = apple.getWeight() > 150 ? "heavy" : "light";
    return "A " + characteristic + " " + apple.getColor() + " apple";
  }
}

public class AppleSimpleFormatter implemetns AppleFormatter { 
  public String accept(Apple apple) {
    return "An apple of " + apple.getWeight() + "g";
  }
}

public static void prettyPrintApple(List<Apple> inventory, AppleFormatter foramtter) {
  for(Apple apple : inventory) {
    String output = formatter.accept(apple);
    System.out.println(output);
  }
}

복잡한 과정 간소화

위에서 보여준 예시는(Predicate 인터페이스를 선언하고, 이를 구현하여 인스턴스화 하여 사용하는 것) 상당히 번거로운 작업이며, 시간 낭비이다.

이를 개선 하기 위해서 자바는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스라는 기법을 제공한다.

익명 클래스는 자바의 지역 클래스와 비슷한 개념이다.

다섯 번째 시도 : 익명 클래스 사용

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
  public boolean test(Apple apple) {
    return RED.equals(apple.getColor());
  }
}

익명 클래스의 단점은 아직도 코드의 줄이 길다는 것이며, 많은 개발자들이 익명 클래스 사용에 익숙하지 않는다는 점 이다.

여섯 번째 시도 : 람다 표현식 사용

List<Apple> result = filterApples(inventory, (Apple apple) -> RED.equals(apple.getColor()));

일곱 번째 시도 : 리스트 형식으로 추상화

public interface Predicate<T> {
  boolean test(T t);
}

public static <T> List<T> filter(List<T> list, Predicate<T> p) {
  List<T> result = new ArrayList<>();
  for(T e : list) {
    if(p.test(e)) {
      result.add(e);
    }
  }
  return result;
}
List<Apple> redApples = filter(inventory, (Apple apple) -> RED.equals(apple.getColor()));
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);

Comparator로 정렬하기

java.util.Comparator 객체를 이용해서 sort의 동작을 파라미터화 할 수 있다.

public interface Comparator<T> {
  int compare(T o1, T o2);
}

무게가 적은 순서로 목록에서 사과를 정렬

inventory.sort(new Comparator<Apple>() {
    public int comapre(Apple a1, Apple a2) {
      return a1.getWeight().compareTo(a2.getWeight());
    }
}
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
반응형
반응형

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

자바 역사를 통틀어 가장 큰 변화가 자바 8에서 일어났다.

  • 자바 8에서 제공하는 새로운 기술
    • 스트림 API
    • 메서드에 코드를 전달하는 방법
    • 인터페이스의 디폴트 메서드

스트림을 이용하면 에러를 자주 일으키며, 멀티코어 CPU를 이용하는 것보다 비용이 훨씬 비싼 키워드 synchronized를 사용하지 않아도 된다. 자바 8에
추가된 스트림 API 덕분에 다른 두 가지 기능, 즉 메서드에 코드를 전달하는 기법(메서드 참조와 람다)과 인터페이스의 디폴트 메서드가 존재 할 수
있음을 알 수 있다.

하지만 스트림 API 때문에 메서드에 코드를 전달하는 기법이 생긴것은 아니다.

메서드에 코드를 전달하는 기법을 사용하면 동작 파라미터화(behavior parameterization)를 구현할 수 있다. 메서드에 코드를 전달(뿐만 아니라 결과를
반환하고 다른 자료구조로 전달할 수 도 있음)하는 자바 8 기법은 함수형 프로그래밍(functional-style programming)에서 위력을 발휘한다.

자바 8에서 함수형 프로그래밍을 도입하면서 객체지향 프로그래밍과, 함수형 프로그래밍의 장점을 누릴 수 있게 되었다.

자바 8 설계의 밑바탕을 이루는 세가지 프로그래밍 개념

스트림 처리(stream processing)

첫 번째 프로그래밍 개념은 스트림 처리다. 스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임. 이론적으로 프로그램은 입력 스트림에서
데이터를 한 개씩 읽어 들이며 마찬가지로 출력 스트림으로 데이터를 한 개씩 기록한다. 즉, 어떤 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될
수 있다.

자바 8에는 java.util.stream 패키지에 스트림 API가 추가 되었다. 스트림 패키지에 정의된 Stream는 T형식으로 구성된 일련의 항목을 의미한다.

스트림 API는 파이프라인을 만드는 데 필요한 많은 메서드를 제공한다. 스트림 API의 핵심은 기존에는 한 번에 한 항목을 처리했지만, 이제 자바 8에서는
우리가 하려는 작업을 데이터베이스 질의 처럼 고수준으로 추상화해서 일련의 스트림으로 만들어 처리할 수 있다는 것이다. 또한 파이프라인을 이용해서 입력
부분을 여러 CPU 코어에 쉽게 할당할 수 있다는 부가적인 이득도 얻을 수 있다. 스레드라는 복잡한 작업을 사용하지 않으면서도 공짜로 병렬성을 얻을 수 있다.

동작 파라미터화(behavior parameteriation)로 메서드에 코드 전달하기

자바 8에 추가된 두 번째 프로그램 개념은 코드 일부를 API로 전달하는 기능이다. 기존에 자바는 메서드를 다른 메서드의 파라미터로 전달 할 수 없었다.
물론 정렬기능을 위해서 익명함수 형태로 Comparator를 구현하는 방법도 있지만 복잡하다. 자바 8에서는 메서드를 다른 메서드의 파라미터로 전달 할 수 있다.
이러한 기능을 이론적으로 동작 파라미터화라고 부른다. 동작 파라미터화가 중요한 이유는 스트림 API는 연산의 동작을 파라미터화할 수 있는 코드를 전달한다는 사상에 기초하기 때문이다.

병렬성과 공유 가변 데이터

세 번째 프로그래밍의 개념은 병렬성을 공짜로 얻을 수 있다라는 말에서 시작된다. 병렬성을 공짜로 얻기 위해서는 다른 한가지를 포기해야하는데,
스트림 메서드로 전달하는 코드의 동작 방식을 조금 바꿔야 한다. 처음에는 불편하지만 나중에는 편하게 느껴질 것이다.

스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수있어야 한다.

보통 다른 코드와 동시에 실행 하더라도 안전하게 실행할 수 있는 코드를 만들려면 가변 데이터(shared mutable data)에 접근하지 않아야 한다.
이러한 함수를 순수(pure) 함수, 부작용 없는 함수(side-effect-free), 상태 없는(stateless) 함수 라고 부른다.

자바 함수

일급 시민과 이급 시민

프로그래밍 언어의 핵심은 값을 바꾸는 것이다. 역사적으로 그리고 전통적으로 프로그래밍 언어에서는 이 값을 일급(first-class) 값 또는 시민(citizens)
이라고 부른다. 자바 프로그래밍 언어의 다양한 구조체(메서드, 클래스 같은)가 값을 구조를 표현하는데 도움이 될 수 있다. 하지만 프로그램을 실행하는 동안 이러한 모든 구조체를 자유롭게 전달할 수는 없다. 이렇게 전달할 수 없는 구조체는 이급 시민이다.

자바 8에서는 이급 시민을 일급 시민으로 바꿀 수 있는 기능을 추가했다. 이미 스몰토크, 자바스크립트 같은 다양한 언어에서 일급 시민으로 가득찬 세계를
성공적으로 만들어 가고있다.

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

메서드 참조(method reference) ::

  • 디렉터리에서 모든 숨겨진 파일을 필터링 하는 코드
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
  public boolean accept(File file) {
    return file.isHidden(); // 숨겨진 파일 필터링
  }
});

하지만 코드가 마음에 들지 않는다. 단 세 행의 코드지만 각 행이 무슨 작업을 하는지 투명하지 않다. 자바 8에서는 위 코드를 아래처럼 구현할 수 있다.

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

자바 메서드 참조 :: (이 메서드를 값으로 사용하라는 의미)를 이용해서 listFiles에 직접 전달할 수 있다.

람다 : 익명함수

자바 8에서는 메서드를 일급 값으로 취급할 뿐 아니라 람다(또는 익명함수 anonymous functions)를 포함하여 함수도 값으로 취급할 수 있다.

코드 넘겨주기 : 예제

Apple 클래스와 getColor 메서드가 있고 Apples 리스트를 포함하는 변수 inventory가 있다고 가정하자. 이때 모든 녹색 사과를 선택해서 리스트를
반환하는 프로그램을 구현해보자.

  • 자바 8 이전 방식
public static List<Apple> filterGreenApples(List<Apple> inventory) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(GREEN.equals(apple.getColor())) {
      result.add(apple);
    }
  }
  return result;
}

하지만 누군가는 사과를 무게 150그람 이상으로 필터링 하고 싶을 수 있다. 그러면 우리는 다음처럼 코드를 구현할 수 있을 것이다.

public static List<Apple> filterHeavyApples(List<Apple> inventory) {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(apple.getWeight() > 150) {
      result.add(apple);
    }
  }
  return result;
}

소프트웨어공학적인면에서 복붙의 단점은 어떤 코드에 버그가 있다면 복붙한 모든 코드를 고쳐야 한다.

자바 8을 기준으로는 아래처럼 고칠 수 있다.

public static boolean isGreenApple(Apple apple) {
  return GREEN.equals(apple.getColor());
}

public static boolean isHeavyApple(Apple apple) {
  return apple.getWeight() > 150;
}

// 명확히 하기위해 적어놓음 
// 보통은 java.util.function에서 임포트함
public interface Predicate<T> {
  boolean test(T t);
}

// 메서드가 p라는 이름의 프레디케이트 파라미터로 전달됨
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p {
  List<Apple> result = new ArrayList<>();
  for(Apple apple : inventory) {
    if(p.test(apple)) {
      result.add(apple);
    }
  }
  return result;
}

// 아래처럼 메서드를 호출할 수 있다.
filterApples(inventory, Apple:isGreenApple);
filterApples(inventory, Apple:isHeavyApple);

프레디케이트(Predicate) 란?

수학에서는 인수로 값을 받아 true나 false를 반환하는 함수를 프레디케이트라고 한다. 나중에 설명하겠지만 자바 8에서도 Function<Apple, Boolean> 같이 코드를 구현할 수 있만 Predicat을 사용하는 것이 더 표준적인 방식이다.(또한 boolean을 Boolean으로 변환하는 과정이 없으므로 더 효율적이다.)

메서드 전달에서 람다로

메서드를 값으로 전달하는 것은 분명 유용한 기능이다. 하지만 isHeavyApple, isGreenApple 처럼 한 두 번만 사용할 메서드를 매번 정의하는 것은
귀찮은 일이다. 자바 8에서는 이 문제도 간단히 해겨랄 수 있다. 바로 람다를 이용하면 된다.

filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));

filterApples(inventory, (Apple a) -> a.getWieght() > 150);

즉, 한 번만 사용할 메서드는 따로 정의를 구현할 필요가 없다. 하지만 람다가 몇 줄 이상으로 길어진다면 익명 람다 보다는 코드가 수행하는 일을 잘 설명하는 이름을 가진 메서드를 정의하고 메서드 참조를 활용하는 것이 바람직 하다. 코드의 명확성이 우선시 되어야 한다.

스트림

거의 모든 자바 애플리켕션은 컬렉션을 만들고 활용한다. 하지만 컬렉션으로 모든 문제가 해결되는 것은 아니다.

예를 들어 고가의 트랜잭션(transaction)(거래) 만 필터링한 다음에 통화로 결과를 그룹화 해야 한다고 가정하자. 아래와 같은 많은 기본 코드를 구현해야한다.

Map<Curreny, List<Transaction>> transactionByCurrencies = new HashMap<>(); // 그룹화된 트랜잭션을 더할 Map 생성
for(Transaction transaction : transactions) {
  if(transaction.getPrice() > 1000) {
    Curreny curreny = transacation.getCurrency(); // 트랜잭션의 통화를 추출
    List<Transcation> transactionsForCurrency = transactionsByCurrencies.get(currency);
    if(transactionsForCurrency == null) {
      transactionsForCurrency = new ArrayList<>();
      transactionsByCurrencies.put(currenc, transcationsForCurrency);
    }
    transactionsForCurrency.add(transacation);
  }
}

위의 예제는 중첩된 제어 흐름 문장이 많아서 코드를 한 번에 이해하기 어렵다.

스트림 API를 이용하면 다음처럼 문제를 해결할 수 있다.

import static java.util.stream.Collectors.groupingBy;
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
                                                              .filter((Transcations t) -> t.getPrice() > 1000); // 고가의 트랜잭션 필터링
                                                              .collect(groupingBy(Transcation::geturrency)); // 통화로 그룹화

내부 반복과 외부 반복

외부 반복(external iteration)은 for-each루프를 이용해서 각 요소를 반보갛면서 작업을 수행하는 것들을 말한다. 반면 내부 반복(internal iteration)은 스트림 API와 같이 루프를 신경 쓸 필요 없이, 스트림 API라는 라이브러리 내부에서 모든 데이터가 처리되는 것을 말한다.

멀티 스레딩은 어렵다

자바 8은 스트림(API, java.util.stream)로 컬렉션을 처리하면서 발생하는 모호함과 반복적인 코드 문제멀티코어 활용 어려움이라는 두 가지 문제를 모두 해결했다. 컬렉션은 어떻게 데이터를 저장하고 접근할지에 중점을 두는 반면 스트림은 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점을 둔다.

포킹단계(forking step)

예를들어 두 CPU를 가진 환경에서 리스트를 필터링할 때 한 CPU는 앞 부분을 처리하고, 다른 CPU는 리스트의 뒷 부분을 처리하도록 요청할 수 있는데 이 과정을 포킹 단계라고 한다. 각각의 cpu는 자신이 맡은 절반의 리스트를 처리하고, 마지막으로 하나의 cpu가 두 결과를 정리한다.

자바 8에서 제공하는 두 가지 요술방망이

흔히 사람들은 자바의 병렬성은 어렵고 synchronized는 쉽게 에러를 일으킨다고 생각한다. 자바8은 어떤 요술방망이를 제공할까?

자바 8은 두 가지 요술 방망이를 제공한다. 우선 라이브러리에서 분할을 처리한다. 즉, 큰 스트림을 병렬로 처리할 수 있도록 작은 스트림으로 분할한다.
또한 filter 같은 라이브러리 메서드로 전달된 메서드가 상호작용을 하지 않는다면 가변 공유 객체를 통해 공짜로 병렬성을 누릴 수 있다.
상호작용을 하지 않는다는 제약은 프로그래머 입장에서 상당히 자연스러운 일이다. 함수형 프로그래밍에서 함수란 함수를 일급값으로 사용한다라는 의미도 있지만, 부가적으로 프로그램이 실행되는 동안 컴포넌트 간에 상호작용이 일어나지 않는다라는 의미도 포함한다.

디폴트 메서드와 자바 모듈

자바 9의 모듈 시스템은 모듈을 정의하는 문법을 제공하므로 이를 이용해 패키지 모음을 포함하는 모듈을 정의할 수 있다.
또한 자바 8에서는 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메서드를 지원한다.

디폴트 메서드는 특정 프로그램을 구현하는 데 도움을 주는 기능이 아니라 미래에 프로그램이 쉽게 변화할 수 있는 환경을 제공하는 기능이다.

어떻게 기존의 구현을 고치지 않고도 이미 공개된 인터페이스를 변경할 수 있을까라는 딜레마를 디폴트 메서드가 해소시켜준다.

기존에는 인터페이스에 메서드가 하나 추가되면 인터페이스를 사용하는 모든 곳에서 메서드를 추가해야하지만, 디폴트 메서드는 구현하지 않아도 되는 메서드이다. 메서드 본문(bodies)은 클래스 구현이 아니라 인터페이스 일부로 포함된다.(그래서 이를 디폴트 메서드라고 한다.)

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

1965년에 널 참조를 발명했던 일을 회상하며 그 결정은 정말 뼈아픈 실수였다고 반성하고 있다.. 단지 구현이 편리하단 이유로 널 참조를 만들어야 겠다는
유혹을 뿌리치지 못했다. -> 토니 호아레(Tony Hoare)는 2009년 QCon London의 프레젠테이션에서 위 와같은 말을 했다.

자바 8에서는 NullPointer 예외를 피할 수 있도록 도와주는 Optional<T> 클래스를 제공한다. Optional는 값을 갖거나 갖지 않을 수 있는 컨테이너 객체이다.

반응형

+ Recent posts