Java8 in Action 정리


12. 새로운 날짜와 시간 API


기존 날짜 API 문제점
  • Date
    • 1900년을 기준으로 하는 오프셋 
    • 0으로 시작하는 달 인덱스
    • 가변 객체
  • Calendar
    • Date 와의 호환성 유지 및 개선을 위해 나온 클래스
    • 오프셋 이슈는 해결했으나 0으로 시작하는 달 인덱스는 유지됨
    • 가변 객체
  • DateFormat
    • Date 클래스에만 작동
    • 스레드 세이프하지 않음.

새로운 날짜/시간 API
  • LocalDate
    • 시간을 제외한 날짜만 표현하는 불변객체
    • LocalDate today = LocalDate.now()// 시스템시계정보를 이용한 현재 날짜정보 획득

      LocalDate date = LocalDate.of(2016313)// 2016-03-13
      int year = date.getYear()// 2016
      Month month = date.getMonth()// MARCH
      int day = date.getDayOfMonth()// 13
      DayOfWeek dow = date.getDayOfWeek()// SUNDAY
      int len = date.lengthOfMonth()// 31 (3월의 일수)
      boolean leap = date.isLeapYear()// true (윤년)

      // TemporalField를 이용한 값 조회
      int year_ = date.get(ChronoField.YEAR)// 2016
      int month_ = date.get(ChronoField.MONTH_OF_YEAR)// 3
      int day_ = date.get(ChronoField.DAY_OF_MONTH)// 13

      int dayOfYear = date.getDayOfYear()// 73

  • LocalTime
    • 시간정보만을 갖는 객체
    • LocalTime time = LocalTime.of(134520)// 13:45:20
      int hour = time.getHour()// 13
      int minute = time.getMinute()// 45
      int second = time.getSecond()// 20

      LocalTime parseTime = LocalTime.parse("13:45:20");


  • LocalDateTime
    • LocalDate와 LocalTime을 쌍으로 갖는 복합 클래스
    • LocalDate localDate = LocalDate.of(2016Month.MARCH13);
      LocalTime localTime = LocalTime.of(134520);

      LocalDateTime dt1 = LocalDateTime.of(2016Month.MARCH13134520)// 2016-03-13 13:45:20
      LocalDateTime dt2 = LocalDateTime.of(localDatelocalTime);
      LocalDateTime dt3 = localDate.atTime(134520);
      LocalDateTime dt4 = localDate.atTime(localTime);
      LocalDateTime dt5 = localTime.atDate(localDate);

      LocalDate date = dt1.toLocalDate();
      LocalTime time = dt1.toLocalTime();


  • Instant
    • 유닉스 에포크 시간(1970-01-01 00:00:00 UTC)을 기준으로 특정 지점까지의 시간을 초로 표현
    • 나노초의 정밀도를 제공
    • Instant now = Instant.now();

      Instant i1 = Instant.ofEpochSecond(3)// 1970-01-01T00:00:03Z
      Instant i2 = Instant.ofEpochSecond(30)// 1970-01-01T00:00:03Z
      Instant i3 = Instant.ofEpochSecond(21000000000L)// 2초 이후의 1억 나노초(1초), 1970-01-01T00:00:03Z
      Instant i4 = Instant.ofEpochSecond(4-1000000000L)// 4초 이전의 1억 나노초(1초), 1970-01-01T00:00:03Z


  • Duration 과 Period
    • LocalDateTime은 사람이 사용하도록, Instant 는 기계가 사용하도록 만들어진 클래스로 두 인스턴스를 혼합해서 사용할 수 없음
    • Duration 클래스는 초와 나노초로 시간단위를 표현
    • Period는 년, 월, 일로 시간을 표현할 때 사용
    • Duration과 Period 클래스가 공통 제공하는 메서드
      • beetween : 두 시간 사이의 간격을 생성
      • from : 시간 단위로 간격을 생성
      • of : 주어진 구성 요소에서 간격 인스턴스를 생성
      • parse : 문자열을 파싱해서 간격 인스턴스를 생성
      • addTo : 현재값의 복사본을 생성한 후 지정된 Temporal 객체에 추가
      • get : 현재 간격 정보값을 읽음
      • isNegative : 간격이 음수인지 확인
      • isZero : 간격이 0인지 확인
      • minus : 현재값에서 주어진 시간을 뺀 복사본을 생성
      • multipliedBy : 현재값에 주어진 값을 곱한 복사본을 생성
      • negated : 주어진 값의 부호를 반전한 복사본을 생성
      • plus : 현재값에 주어진 시간을 더한 복사본을 생성
      • subtractFrom : 지정된 Temporal 객체에서 간격을 뺌

  • 특정시점을 표현(조정)하는 날짜 시간 클래스의 공통 메서드
    • LocalDate.of(2014, 3, 18).withYear(2011).withDayOfMonth(25).with(ChronoField.MONTH_OF_YEAR, 9);
      • 2014-03-18  >  2011-03-18  >  2011-03-25  >  2011-09-25
    • 공통 메서드
      • from : 주어진 Temporal 객체를 이용해서 클래스의 인스턴스를 생성
      • now : 시스템 시계로 Temporal 객체를 생성
      • of : 주어진 구성요소에서 Temporal 객체의 인스턴스를 생성
      • parse : 문자열을 파싱해서 Temporal 객체를 생성
      • atOffset : 시간대 오프셋과 Temporal 객체를 합침
      • atZone : 시간대와 Temporal 객체를 합침
      • format : 지정된 포매터를 이용해서 Temporal 객체를 문자열로 변환 (Instant 는 지원하지 않음)
      • get : Temporal 객체의 상태를 읽음
      • minus : 특정 시간을 뺀 Temporal 객체의 복사본을 생성
      • plus : 특정시간을 더한 Temporal 객체의 복사본을 생성
      • with : 일부 상태를 바꾼 Temporal 객체의 복사본을 생성
  • TemporalAdjusters
    • 날짜 시간 API를 다양한 상황에서 사용할 수 있도록 한 클래스
      • LocalDate.of(2014, 3, 18).with(nextOrSame(DayOfWeek.SUNDAY)).with(lastDayOfMonth());
        • 2014-03-18  >  2014-03-23 (3/18 포함하여 이후로 처음 나타나는 일요일)  >  2014-03-31 (3월의 마지막 일)
    • TemporalAdjuster 함수형 인터페이스를 통해 커스텀 기능을 제공할 수 있다.
    • TemporalAdjusters 의 팩토리 메서드
      • dayOfWeekInMonth : ‘3월의 둘째 화요일’처럼 서수 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfMonth : 현재 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfNextMonth : 다음 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfNextYear : 내년의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfYear : 올해의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstInMonth : ‘3월의 첫 번째 화요일’처럼 현재 달의 첫 번째 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
      • lastDayOfMonth : 현재 달의 마지막 날짜를 반환하는 TemporalAdjuster를 반환
      • next : 현재 날자 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜는 포함하지 않음)
      • previous : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 미포함)
      • nextOrSame : 현재 날짜 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 포함)
      • previousOrSame : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환(현재날짜 포함)
  • DateTimeFormatter
    • 기존 DateFormat과 달리 스레드 세이프한 날짜 시간 포매터 클래스
    • // 패턴을 통한 포매터 생성
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
      String formattedDate = LocalDate.of(2014318).format(formatter)// 18/03/2014
      LocalDate date = LocalDate.parse(formattedDateformatter);

      // 지역화된 DateTimeFormatter
      DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy"Locale.ITALIAN);
      String formattedDateItalian = LocalDate.of(2014318).format(italianFormatter)// 18. marzo 2014
      LocalDate italianDate = LocalDate.parse(formattedDateItalianitalianFormatter);

      // Builder를 통한 Formatter 생성
      DateTimeFormatter formatterByBuilder = new DateTimeFormatterBuilder()
              .appendText(ChronoField.DAY_OF_MONTH// d
              .appendLiteral(". ").appendText(ChronoField.MONTH_OF_YEAR)
              .appendLiteral(" ").appendText(ChronoField.YEAR)
              .parseCaseInsensitive()
              .toFormatter(Locale.GERMANY);

      String formattedDateByBuilder = LocalDate.of(2014318).format(formatterByBuilder)// 18. Marz 2014

  • ZoneId, ZoneRuls, ZonedDateTime
    • TimeZone을 대체하는 불변 클래스로 서머타임(DST) 같은 복잡한 사항이 자동으로 처리 가능
    • ZoneRuls 클래스에는 약 40개 정도의 시간대가 있으며 ZoneId의 getRules 메서드를 이용하여 해당 시간대 규정 획득 가능
    • ZonedDateTime = LocalDate + LocalTime + ZoneId 로 지정한 시간대에 상대적인 시점을 표현.
    • // 기존 TimeZone 클래스에서 ZoneId 변환
      ZoneId zoneId = TimeZone.getDefault().toZoneId()// "Asia/Seoul"

      // 지역ID는 "지역/도시" 형식으로 이루어지며 IANA Time Zone Database에서 제공하는 지역 집합정보 사용
      ZoneId romeZone = ZoneId.of("Europe/Rome");

      // 지정한 시간대에 상대적인 시점을 표현하는 ZonedDateTime
      ZonedDateTime zdt1 = LocalDate.of(2014318).atStartOfDay(romeZone)// 2014-03-18T00:00+01:00[Europe/Rome]
      ZonedDateTime zdt2 = LocalDateTime.of(20143181345).atZone(romeZone)//  2014-03-18T13:45+01:00[Europe/Rome]
      ZonedDateTime zdt3 = Instant.now().atZone(romeZone)// 2016-03-13T11:58:38.743+01:00[Europe/Rome]

      // ZoneId를 활용한 LocalDateTime과 Instnat간의 변환 (LocalDateTime은 ZoneId를 상속한 ZoneOffset을 변수로 받는다)
      LocalDateTime timeFromInstant = LocalDateTime.ofInstant(Instant.now()romeZone)// 2016-03-13T11:58:38.743
      Instant instantFromDateTime = LocalDateTime.of(20143181345).toInstant(romeZone.getRules().getOffset(timeFromInstant))// zoneOffset = "+01:00" (totalSeconds=3600)


      // ZonedDateTime은 Instnat, LocalDateTime, LocalDate, LocalTime 모두 변환 가능
      Instant instant = zdt1.toInstant()// 2014-03-17T23:00:00Z
      LocalDateTime localDateTime = zdt1.toLocalDateTime()// 2014-03-18T00:00

  • ZoneOffset, OffsetDateTime
    • ZoneOffset : ZoneId를 상속한 클래스. 서머타임을 제대로 처리할 수 없으므로 권장하진 않는다
    • OffsetDateTime : ISO-8601 캘린더 시스템에서 정의하는  UTC/GMT와 오프셋으로 날짜와 시간 표현
    • ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
      OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(LocalDateTime.of(20143181345)newYorkOffset)// 2014-03-18T13:45-05:00

  • 대안 캘린더 시스템
    • ISO-8601 캘린더 시스템은 실질적으로 전세계에서 통용되나 자바8에서는 추가로 4개의 캘린더 시스템을 제공
      • ThaiBuddhistDate : 
      • MinguoDate : 
      • JapaneseDate : 일본력 캘린더 시스템
      • HijrahDate : 이슬람력 캘린더 시스템
    • ChronoLocalDate
      • 임의의 연대기에서 특정 날짜를 표현할 수 있는 기능을 제공하는 인터페이스
      • 자바8에서 추가된 캘린더시스템은 모두 이 인터페이스를 상속하고있어 LocalDate를 이용해 4개의 클래스 인스턴스로 변환이 가능
    • Chronology
      • 캘린더 시스템으로 ofLocale을 이용하여 Chronology의 인스턴스 획득이 가능하다.
    • JapaneseDate japaneseDate = JapaneseDate.from(LocalDate.of(2014318))// Japanese Heisei 26-03-18
      Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
      ChronoLocalDate now = japaneseChronology.dateNow()// 2016-03-13


'development > Java' 카테고리의 다른 글

[도서] Java8 in Action - Stream  (0) 2017.04.25
serialize와 serialVersionUID  (0) 2015.06.18
Posted by dreamhopp
,

Java8 in Action 정리


4. 스트림 소개


스트림 API
  • 데이터 컬렉션 반복을 선언형으로 처리하고 조립하는 기능
  • 멀티 스레드 코드를 구현하지 않고도 데이터 처리과정을 병렬화하여 스레드와 락 걱정없이 처리 가능
  • 스트림 API는 매우 비싼 연산이다.

스트림
  • 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소

스트림과 컬렉션 차이
  • 데이터를 언제 계산하는가
    • 스트림 : 요청할 때만 요소를 계산하는 고정된 자료구조 (스트림에 요소를 추가/제거 불가)
    • 컬렉션 : 현재 자료구조가 포함하는 모든 값을 메모리에 저장
  • 딱 한번만 탐색할 수 있다.
    • Awways.asList(“1”,”2”).stream().forEach(System.out::println).forEach(System.out::println)
      • 첫번째 forEach에서 데이터가 모두 소비되어 다음 forEach로 전달된 스트림 요소가 존재하지 않음. (파이프라이닝으로 다음 forEach로 전달된 요소가 없음)
  • 반복과 병렬성
    • 외부반복
      • 사용자가 직접 요소를 반복하는 방식 (컬렉션)
      • 병렬성을 사용자가 직접 관리해야 함 (syncronized)
    • 내부반복
      • 반복을 알아서 처리하고 결과 스트림값을 어딘가에 저장
      • 데이터 표현과 하드웨어를 활용한 병렬성 구현을 자동으로 선택

데이터 소스
  • 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스
  • 스트림을 생성하면 자료구조의 순서와 같은 순서의 스트림이 생성된다.

중간연산
  • filter, sorted와 같이 다른 연산과 연결될 수 있는 연산
  • 중간연산을 이용해서 파이프라인을 구성할 수 있으며 어떤 결과도 생성할 수 있음.
  • 게으른 연산 : 중간 연산을 합친 다음에 합쳐진 중간 연산을 최종으로 한번에 처리
    • List<String> names =
      menu.stream()
               .filter(d -> {System.out.println(“filtering” + d.getName()); return d.getCalories() > 300; })
               .map(d -> {System.out.println(“mapping” + d.getName()); return d.getname(); })
               .limit(3).collect(toList());
      System.out.println(names);
    • filtering pork
      mapping pork
      filtering beef
      mapping beef
      filtering chicken
      mapping chicken
      [pork, beef, chicken]
    • 쇼트서킷 : limit을 통해 필터링 된 여러 요소 중 3개만 선택됨
    • 루프 퓨전 : filter, map 등 다른 연산이지만 한 과정으로 병합되어 처리

연산
형식
사용된 함수 디스크립터
설명
filter
Stream<T> filter(Predicate<? super T> predicate)
T -> boolean
Predicate(boolean을 반환하는 함수)를 인수로 받아
일치(true)하는 모든 요소를 포함하는 스트림 반환
distinct
Stream<T> distinct()

고유 요소 필터링 (상태 있는 언바운드)
hashCode, equals 메서드를 통해 고유여부 판별
skip
Stream<T> skip(long n)

요소 건너뛰기 (상태 있는 바운드)
처음 n개 요소를 제외한 스트림을 반환
limit
Stream<T> limit(long maxSize)

스트림 축소 (상태 있는 바운드)
n개 이하의 크기를 갖는 새로운 스트림을 반환
map
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
T -> R
mapToInt, mapToDouble, mapToLong 메서드를 통해 기본형 특화 스트림(IntStream, DoubleStream, LongStream)으로 변환할 수 있다.
객체스트림으로 복원하려면  숫자 스트림의 boxed 메서드를 호출하면 된다.
flatMap
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
T -> Stream<R>
각 배열을 스트림이 아니라 스트림의 콘텐츠로 매핑한다.
1개 이상의 스트림의 각 요소를 하나의 스트림으로 변환
sorted
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)

(T, T) -> int
상태 있는 언바운드
peek
Stream<T> peek(Consumer<? super T> action)
T -> T
자신이 확인한 요소를 파이프라인의 다음연산으로 그대로 전달

최종연산
  • 스트림 파이프라인을 처리해서 스트림이 아닌 결과를 반환하는 연산
  • 보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환
  • 딱 한번만 탐색할 수 있다.

연산
형식
사용된 함수 디스크립터
설명
anyMatch
boolean anyMatch(Predicate<? super T> predicate)
T -> boolean
프레디케이트가 적어도 한 요소와 일치하는지 확인
boolean 반환
noneMatch
boolean noneMatch(Predicate<? super T> predicate)
T -> boolean
프레디케이트가 모든 요소와 일치하는 요소가 없는지 확인
boolean 반환
allMatch
boolean allMatch(Predicate<? super T> predicate)
T -> boolean
프레디케이트가 모든 요소와 일치하는지 확인
boolean 반환
findAny
Optional<T> findAny()

스트림에서 임의의 요소를 반환
반환값이 없을경우  NPE를 피하기위해 Optional로 반환
findFirst
Optional<T> findFirst()


forEach
void forEach(Consumer<? super T> action)
T -> void
스트림의 각 요소를 소비하면서 람다를 적용.
void를 반환한다.
collect
<R, A> R collect(Collector<? super T, A, R> collector)
<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner)

스트림을 리듀스해서 리스트(toList), 맵(toMap), 정수 형식의 컬렉션을 만든다.
reduce
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner)
(T, T) -> T
상태 있는 바운드(내부 상태의 크기가 한정)
count
long count()

스트림의 요소 개수를 반환한다. long을 반환한다


5. 스트림 활용


flatMap

“HelloWorld".stream().map( w -> w.split(“”) ).flatMap(Arrays::stream).distinct().collect(toList())
  • map( w -> w.split(“”)) : “HelloWorld” => String[] {“H”,”e”,”l”,”l”,”o”,”W”,”o”,”r”,”l”,”d”}
  • disticnt() : String[] {“H”,”e”,”l”,”o”,”W”,”r”,”d”}
  • flatMap(Arrays:stream) : Stream<String[]> => Stream<String>
    • Arrays.stream(T[]) : 배열을 스트림으로 변환 

Optional<T>
  • 값의 존재/부재 여부를 표현하는 컨테이너 클래스
  • null값 반환으로 인한 에러를 피하기 위해 만들어진 기능.
  • 제공 메서드
    • isParent() : Optional이 값을 포함하녀 true, 없으면 false
    • ifPresent(Consumer<T> block) : 값이 있으면 주어진 블록을 실행
    • T get() : 값이 존재하면 값을 반환하고, 없으면 NoSuchElementException을 발생시킴
    • T orElse(T other) : 값이 있으면 값을 반환하고, 없으면 기본값(other)을 반환한다.

findFirst와 findAny
  • 병렬 실행에서는 첫번째 요소를 찾기 어려우므로 요소 반환 순서가 상관없다면 병렬스트림에서는 제약이 적은 findAny를 사용


reduce

reduce(초기값, (초기값 or 이전계산의 결과값, 요소값) -> 반환값이 있는 람다식)
  • int sum = numbers.stream().reduce(0, (a,b) -> a + b);
    • numbers의 모든 요소의 합을 구하는 코드
Optional<T> reduce((T, T) -> T)
  • Optional<Integer> sum = numbers.stream().reduce((a, b) ->  (a + b));
  • 초기값을 받지 않도록 오버로드된 메서드
  • 스트림에 아무런 요소가 존재하지 않는경우 초기값으로 할당할 요소가 존재하지 않아 반환값도 존재하게되지 않으므로 Optional을 반환하도록 설계
  • 기본형 특화 스트림에서 값이 없는경우와 실제 결과값이 초기값과 같은경우를 구분하기 위해 기본형 특화 Optional 클래스도 제공한다.
    • OptionalInt, OptionalDouble, OptionalLong

내부 상태를 갖는 연산
  • 종류 : distinct, skip, limit, sorted, reduce
  • 연산을 처리하려면 모든 요소가 버퍼에 추가되어있어야 하거나 결과값을 다음연산의 초기값으로 상태값을 가지고있어야 처리가능한 연산
  • 연산을 수행하는데 필요한 저장소의 크기가 정해져 있는경우 바운드(bounded), 스트림의 요소갯수에 따라 무한으로 늘어날 수 있는경우 언바운드(unbounded)

Stream 만들기
  • 값으로 스트림 만들기
    • Stream<T> of(T t)
    • Stream<T> of(T... values)
  • 빈 스트림 만들기
    • Stream<T> empty()
  • 배열로 스트림 만들기
    • Stream<T> stream(T[] array)
  • 파일로 스트림 만들기
    • Stream<String> lines(Path path, Charset cs)
  • 함수로 무한 스트림 만들기
    • 요청할 때마다 주어진 함수를 이용해서 무제한으로 값을 생성할 수 있으며, 보통 limit 함수와 함께 사용해서 무한 실행되지 않도록 한다.
    • Stream<T> iterate(final T seed, final UnaryOperator<T> f)
      • Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println)
      • 상태값이 있는 메서드. 연속된 일련의 값을 만들 때 사용
    • Stream<T> generate(Supplier<T> s)
      • Steam.geneate(Math::random).limit(5).forEach(System.out::println)
      • 상태값이 없는 메서드, 연속되지 않은 값을 만들 때 사용


6. 스트림으로 데이터 수집

Collectors 정적 팩토리 메서드

팩토리메서드
반환형식
사용예제
설명
toList
List<T>
List<Dish> dishes = menuStream.collect(toList());
스트림의 모든 항목을 리스트로 수집
toSet
Set<T>
Set<Dish> dishes = menuStream.collect(toSet());
스트림의 모든 항목을 중복없는 집합으로 수집
toCollection
Collection<T>
Collection<Dish> dishes = menuStream.collect(toCollection(), ArrayList::new);
스트림의 모든 항목을
공급자가 제공하는 컬렉션으로 수집
counting
Long
long howManyDishes = menuStream.collect(counting());
스트림의 항목 수 계산
summingInt
Integer
int totalCalories = menuStream.collect(summingInt(Dish::getCalories));
스트림의 항목에서 정수 프로퍼티 값을 더함
averagingInt
Double
double avgCalories = menuStream.collect(averagingInt(Dish::getCalories));
스트림 항목의 정수 프로퍼티의 평균값 계산
summarizingInt
IntSummaryStatistics
IntSummaryStatistics summary = menuStream.collect(summarizingInt(Dish::getCalories));
// summary = {count=9, sum=4300, min=120, average=477.777778, max = 800}
스트림 내의 항목의 최대, 최소, 합계, 평균 등의 정수 정보 통계를 수집
joining
String
String shortMenu = menuStream.map(Dish::getName).collect(joining(“, “));
스트림의 각 항목에 toString 메서드를 호출한 결과 문자열을 연결
maxBy
Optional<T>
Optional<Dish> fattest = menuStream.collect(maxBy(comparingInt(Dish::getCalories)));
주어진 비교자를 이용해서 스트림의 최대값 요소를 Optional로 감싼 값을 반환.
스트림에 요소가 없는경우 Optional.empty() 반환
minBy
Optional<T>
Optional<Dish> lightest = menuStream.collect(minBy(comparingInt(Dish::getCalories)));
주어진 비교자를 이요해서 스트림의 최소값 요소를 Optional로 감싼 값을 반환
스트림에 요소가 없는경우 Optional.empty() 반환
reducing
리듀싱 연산에서 형식을 결정
int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
누적자를 초깃값으로 설정한 다음 BinaryOperator로 스트림의 각 요소를 반복적으로 누적자와 합쳐 스트림을 하나의 값으로 리듀싱
collectingAndThen
변환함수가 형식을 반환
int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size));
다른 컬렉터를 감싸고 그 결과에 변환 함수를 적용
groupingBy
Map<K, List<T>>
Map<Dish.Type, List<Dish>> dishesByType
= menuStream.collect(groupingBy(Dish::getType), toList());
하나의 프로퍼티값을 기준으로 스트림의 항목을 그룹화하며 기준 프로퍼티값을 결과 맵의 키로 사용
partitioningBy
Map<Boolean, List<T>>
Map<Boolean, List<Dish>> vegetarianDishes
= menuStream.collect(partitioningBy(Dish::isVegetarian));
결과 : {false=[pork, beef], true=[french fries, rice, pizza]}
프레디케이트를 스트림의 각 항목에 적용한 결과로 항목을 분할


Collector.reducing과 Stream.reduce
  • reducing
    • 도출하려는 결과를 누적하는 컨테이너로 바꾸도록 설계된 메서드
    • 가변 컨테이너 작업의 병렬연산에도 안전하다
  • reduce
    • 두 값을 하나로 도출하는 불변형 연산
    • 병렬로 reducing과 같은 연산을 처리하는경우 불변형이 깨지거나 매번 새로운 리스트를 생성하여 값을 할당해야하므로 성능이 저하될 수 있다.

다수준 그룹화
  • menu.stream().collect(groupingBy(Dish::getType, groupingBy(dish -> {
        if (d.getCalories() <= 400) return CaloricLevel.DIET;
        else if (d.getCalorids() <= 700) return CaloricLevel.NORMAL;
        else return CaloricLevel.FAT;
    })));
    • 결과
      {MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]},
       FISH={DIET=[prawns], NORMAL=[salmon]},
       OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}
  • menu.stream().collect(groupingBy(Dish::getType, counting()));
    • 결과 : {MEAT=3, FISH=2, OTHER=4}

Collector Interface

public interface Collector<TAR> {
    Supplier<Asupplier();
    BiConsumer<ATaccumulator();
    BinaryOperator<Acombiner();
    Function<ARfinisher();
    Set<Characteristics> characteristics();
   
    enum Characteristics {
        CONCURRENTUNORDEREDIDENTITY_FINISH
    }
}

  1. supplier
    • accumulator에서 사용할 빈 누적자 인스턴스를 만드는 함수
  2. accumulator
    • 리듀싱 연산을 수행하는 함수(void return) 반환
    • supplier가 생성한 누적자 인스턴스를 받아 함수의 상태값을 저장하고 반환값은 void가 된다. (내부탐색만 가능하며 계속 상태가 바뀌므로 어떤값일지 추적 불가)
  3. combiner
    • 마지막으로 리듀싱 연산에서 사용할 함수를 반환
    • 스트림의 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할지 정의
    • 병렬 처리시 스트림을 분할해야 하는지 정의하는 조건이 거짓으로 바뀌기 전까지 원래 스트림을 재귀적으로 분할하며,
      일반적으로 프로세싱 코어의 개수 이하의 병렬작업이 효율적이다.(p214)
  4. finisher
    • 누적과정을 끝낼 때 호출할 함수를 반환
    • 누적자 객체가 이미 최종결과인 경우 변환과정이 필요없는 항등함수(Function.identity())를 반환하면 된다.
  5. characteristics
    • 스트림을 병렬로 리듀스할 것인지, 병렬로 리듀스 할 경우 어떤 최적화를 선택해야할지 힌트를 제공
    • UNORDERED
      • 리듀싱 결과는 스트림 요소의 방문순서나 누적 순서에 영향을 받지 않는다.
    • CONCURRENT
      • 다중 스레드에서 accumulator 함수를 동시에 호출할 수 있으며 이 컬렉터는 스트림의 병렬 리듀싱을 수행할 수 있다.
      • UNORDERED를 함께 설정하지 않았다면 데이터 소스의 순서가 무의미한 상황에서만 병렬 리듀싱을 수행할 수 있다.
    • IDENTITY_FINISH
      • 리듀싱 과정의 최종 결과로 누적자 객체를 바로 사용할 수 있으며, 누적자 객체를  A -> R로 안전하게 형변환 할 수 있다.


7. 병렬 데이터 처리와 성능

Stream.parallel()
  • 스트림 자체에는 아무 변화도 일어나지 않는다
  • parallel을 호출하면 내부적으로 이후 연산이 병렬로 수행해야 함을 의미하는 불린 플래그가 설정된다.

Stream.sequential()
  • 병렬 스트림을 순차 스트림으로 바꿀 수 있다.
  • sequential을 호출하면 이후 연산이 순차적으로 수행해야 함을 의미하는 불린 플래그가 설정된다.

병렬 스트림에서 사용하는 스레드 풀 설정
  • 병렬스트림은 내부적으로 ForkJoinPool을 사용
  • ForkJoinPool은 기본적으로 Runtime.getRuntime().availableProcessors()가 반환하는 값에 상응하는 스레드를 사용(가상 프로세서를 포함한 프로세서 수)

병렬 스트림 사용시 주의해야할 점
  • 자료구조 변화에 따른 auto boxing으로 인한 성능저하
    • 기본형 특화 스트림을 사용하여 auto boxing 회피
  • 병렬실행을 위한 재귀적인 청크 분할, 스레드 할당, 머지 등 작업을 위한 비용과 성능개선비용의 관계
    • 성능측정을 통한 병렬 스트림 사용시의 이점 확인
    • 소량의 데이터의 경우 병렬화 과정으로 인한 비용이 더 크게 발생할 수 있다.
    • 최종연산의 병합과정 비용보다 병렬 스트림으로 얻은 성능의 이익이 더 큰지 확인
  • 순서에 의존하는 limit, findFirst 등 연산은 병렬스트림에서 성능이 저하됨.
  • 공유된 가변상태 변수로 인한 로직 오류 발생
  • 효율적으로 분할 가능한 자료구조로 스트림이 구성되었는지 확인
    • 분해성
      • 훌륭함 : ArrayList, IntStream.range
      • 좋음 : HashSet, TreeSet
      • 나쁨 : LinkedList, Stream.iterate

Fork/Join Framework
(병렬작업의 분할/작업/머지 등을 컨트롤 하고싶은경우 직접 구현가능 - p238~245)

  • 작업 훔치기
    • ForkJoinPool에서 각 스레드는 자신에게 할당된 태스크를 포함하는 이중연결 리스트를 참조하면서 작업이 끝날때 마다 큐의 HEAD 에서 다른 태스크를 가져와 작업을 처리
    • 특정 스레드가 먼저 작업이 완료되었을 ㄷ경우 다른 스레드 큐의 TAIL 에서 작업을 훔쳐와 모든 큐가 빌 때 까지 이 작업을 반복하여 스레드간 작업부하를 비슷한 수준으로 유지
  • Spliterator
    • 자바 8은 컬렉션 프레임워크에 포함된 모든 자료구조에 대해 사용할 수 있는 디폴트 Spliterator 구현을 제공
    • 제공 메서드
      • tryAdvance : 일반적인 Iterator 동작과 동일
      • trySplit : 스트림을 재귀적으로 분할하며 모든 trySplit 결과가 null이면 분할과정이 종료된다.
        • Spliterator의 일부 요소(자신이 반환한 요소)를 분할해서 두번째 Spliterator를 생성하는 메서드
        • estimateSize 메서드로 탐색해야할 요소 수 정보를 제공할 수 있다.(정확성 X)
      • characteristics : Spliterator 자체의 특성 집합을 포함하는 int를 반환하며, 이 특성을 이용해서 Spliterator를 더 잘 제어하고 최적화 할 수 있다
        • ORDERED, DISTINCT, SORTED, SIZE, NONNULL, IMMUTABLE, CONCURRENT, SUBSIZED


'development > Java' 카테고리의 다른 글

[도서] Java8 in Action - 새로운 날짜/시간 API  (0) 2017.04.25
serialize와 serialVersionUID  (0) 2015.06.18
Posted by dreamhopp
,

http://cafe.naver.com/swnara/369
http://kevinx64.net/107


개발하다보면 종종 serialVersionUID를 보게된다. 이값은 어디에 사용하는것일까 궁금해서 찾아보게 되었다.


Serialize (직렬화)

객체를 파일로 저장하거나, 혹은 다른 사람에서 전송하기 위해서는 직렬화(serialize) 및 역직렬화(deserialize)가 필요하다.
직렬화란 메모리나 저장공간에 담을 수 있도록 일렬로 만드는 것을 의미하며, 직렬화를 해야만 객체를 저장하거나 통신할 수 있다.

Serializable 인터페이스는 구현 메소드, 필드가 없는 껍데기 인터페이스로 
JAVA에서는 이 인터페이스를 구현하여 직렬화가 가능한 객체임을 인식한다.
Serializable을 구현한 객체를 저장하면 해당 class에 정의된 private을 포함한 모든 필드와 값, super class의 모든 필드 및 값이 저장된다.


SerialVersionUID

Serializable 인터페이스를 구현하게되면 IDE에서 serialVersionUID를 생성하도록 warning을 표시한다. 이 SerialVersionUID는 무엇에 쓰이는것일까? 그리고 해당 값을 선언하지 않으면 어떻게 되는것일까?

http://kevinx64.net/107 에서 이 의문에 대한 답을 찾을 수 있었다.

 직렬화 가능(Serializable) 클래스가 serialVersionUID를 명시적으로 선언하지 않는 경우, 직렬화 런타임은 「Java(TM) 객체 직렬화 스펙」에서 설명하고 있듯이, 클래스의 다양한 측면에 근거해, 클래스의 serialVersionUID의 Default 값를 계산한다. 다만, 모든 직렬화 가능 클래스가 serialVersionUID를 명시적으로 선언하는 것을 강하게 추천한다. Default의 serialVersionUID 계산이, 컴파일러의 구현에 따라서, 다를 가능성이 있는 클래스의 영향을 받기 쉽고, 직렬화 복원 중에 예기치 않은 InvalidClassException 가 발생할 가능성이 있기 때문이다. 따라서, java 컴파일러의 구현이 달라도 serialVersionUID의 일관성을 확보로 하려면, 직렬화 가능 클래스가 serialVersionUID를 명시적으로 선언하지 않으면 안된다. 또, serialVersionUID 의 명시적인 선언에서는 private 수식자를 사용하는 것을 적극 추천한다. 이러한 선언은 직접적으로 선언하는 클래스에게만 적용되게 하기 위한 것이다. private으로 선언하면, serialVersionUID 필드를 상속되는 멤버와 같이 사용하지 않는다.

SerialVersionUID는 객체 직렬화 시에 동일한 Class 명을 갖고있더라도 실제로 동일한 class가 맞는지 검사하기 위해 version을 지정하는것이며, 이 값을 명시적으로 지정하지 않으면 JVM은 default로 serialVersionUID를 계산하여 사용한다.
여러가지 근거를 통해 자동으로 serialVersionUID를 계산해 낼 때 환경적인 사유 혹은 불특정한 이유로 인해 이 값이 다르게 계산될 수도 있으며, 이 경우 InvalidClassException이 발생할 수 있으므로 명시적으로 선언해주는것을 권장한다.


Transient

직렬화 시에 대상에서 제외하고 싶은 경우 변수 선언 앞에 transient를 선언하면 직렬화 대상에서 제외가 된다.

'development > Java' 카테고리의 다른 글

[도서] Java8 in Action - 새로운 날짜/시간 API  (0) 2017.04.25
[도서] Java8 in Action - Stream  (0) 2017.04.25
Posted by dreamhopp
,

Module

이전에 작성했던 예제들은 모두 전역으로 선언한 생성자 및 변수로 실제 애플리케이션에서는 사용을 지양하고있다.
전역으로 선언할 경우 예상치 못한 부분에서 변수의 값이 변경이 될 수 있으며 가독성 및 테스트용이성이 떨어지는 등 코드의 유지보수가 어려워진다.

AngularJS에는 'angular'라는 전역 네임스페이스가 정의되어있으며, 이 네임스페이스는 다양하고 편리한 유틸함수를 제공한다.
그 중 module은 AngularJS가 관리하는 객체(controller, service 등)의 컨테이너 역할을 한다.


Module을 통한 Controller 생성방법 비교

 전역변수 선언방법

Module 생성방법 

 var HelloCtrl = function ($scope) {
     $scope.name = 'World';
 }

 angular.module('hello', [])
    .controller('HelloCtrl', function ($scope) {
        $scope.name = 'World';
    });

  angular.module("모듈이름", [다른모듈과의 의존관계]).controller("컨트롤러 이름", 생성자함수);

모듈을 정의 후 사용하기위해서 ng-app 속성에 값으로 정의하여 AngularJS에 모듈을 등록하여야 한다.



[실행 결과]


[controller생성 - 전역변수]

위의 코드는 앞서 구현해보았던 전역변수를 통한 생성방식이다. 이 코드를 AngularJS의 module을 통한 생성방식으로 변경하면 다음과 같다.


[controller생성 - module]

ng-app 속성의 값으로 해당 화면에서 사용할 모듈이름을 AngularJS에 알려준다.

angular.module 메소드는 모듈을 생성하고, 생성한 모듈을 반환한다.

아래와 같이 모듈생성 후 반환되는 값을 받아서 재사용할 수 있다.
var module = angular.module('module_name', []) ; // module 생성 및 반환

다음과 같이 모듈생성과 생성과 생성된 모듈을 사용하는부분을 분리하여 구현할 수 있다. 
angular.module('module_name', []); // module 생성
var module = angular.module('module_name'); // AngularJS 컨테이너에 module_name 이름으로 등록된 모듈을 얻어온다.

모듈에 여러개의 컨트롤러를 구현하기 위해서는 생성된 모듈을 변수로 가지고 이를 재사용해야한다. module.controller 메소드의 반환값이 module이 아니기때문에 2번째의 컨트롤러 함수 호출을 위해 module을 얻어야한다.

ng-app에는 1개의 모듈만 등록이 가능하다. 그러므로 여러개의 모듈을 사용하고싶다면 여러개의 모듈에 의존성을 갖는 최상위 모듈을 정의 후 의존성 모듈로 여러개의 모듈을 등록하여 사용할 수 있다.
angular.module('module1',[]); // module1 생성
angular.module('module2',[]); // module2 생성
angular.module('app', ['module1', 'module2']); // 최상위 모듈 app 생성 및 module1, module2 dependency 등록
<body ng-app='app'> // ng-app 속성에 최상위모듈 app 등록하면 의존성 모듈로 등록된 module1, module2 모두 사용 가능하다.

만약 module1과 module2에 동일한 이름의 controller를 생성한 경우 dependency에 마지막으로 등록한 module2의 값으로 덮어씌워져 화면에 노출된다.

'development > angularjs' 카테고리의 다른 글

angularjs - $scope  (0) 2014.08.03
angularjs - hello world  (0) 2014.08.03
Posted by dreamhopp
,

모델에 값을 할당하는 방법은 ng-init을 통한 초기화 외에 $scope에 프로퍼티 할당을 통해서도 가능하다.



[ case1. ng-init 디렉티브를 통한 값 할당 ]


[ case2. $scope 프로퍼티 할당을 통한 값 할당 ]

1. ng-init 디렉티브를 제거하고 ng-controller로 컨트롤러의 스코프 이름을 정해준다.
2. script 에서 controller의 이름을 통해 함수를 정의하며 인자로 $scope를 받는다.
3. $scope.name 이라는 프로퍼티에 값 할당을 통해 HelloCtrl 컨트롤러 내부에서만 동작하는 name 변수에 값을 할당 할 수 있다.


스코프 ($scope)

$scope 객체는 프로퍼티 할당을 통해 뷰(템플릿)에게 도메인 모델을 제공한다.
제공하는 도메인 모델은 데이터 뿐만 아니라 뷰에 대한 특정 동작(function)도 추가할 수 있다.
특정 동메인 모델과 동작을 특정 뷰 레이어에 한정시킬 수 있다. (위의 예제에서는 HelloCtrl의 function의 인자로 받은 $scope는 동작이 HelloCtrl 내로 한정된다.)


컨트롤러 (ng-controller)

초기 모델 값을 지정하는 부분은 ng-init 디렉티브와 ng-controller 디렉티브가 동일하게 동작하므로,
ng-controller를 사용하면 초기화 로직을 HTML 템플릿에 넣지 않고 javascript로만 작성이 가능하다.

* ng-controller의 초기화로직

  1. 초기 모델 값 지정
  2. $scope에 UI동작(함수) 추가

$scope 생성의 비밀!

AngularJS에는 $rootScope 라는 모든 스코프의 부모스코프가 있으며, 이 $rootScope 인스턴스는 애플리케이션이 초기화 될 때 만들어진다. AngularJS는 DOM 트리에서 $scope를 생성하는 디렉티브(예: ng-controller)를 만날 때 마다 Scope.$new() 메소드를 호출하여 상위 스코프를 상속받은 새로운 스코프를 생성하여 전달하는 것이다. 이렇게 생성된 스코프는 부모 스코프를 가리키는 $parent 프로퍼티를 갖고 있다. 생성된 스코프는 상위 스코프를 상속받기 때문에 최상위 스코프에 프로퍼티를 정의해둔 경우, 모든 하위 스코프에서 같은 이름으로 해당 프로퍼티 정의 없이 접근이 가능하다.
(설명에 따르자면 ng-init을 통해 AngularJS가 초기화 될 때 $rootScope 인스턴스가 생성된다. $rootScope와 $scope를 java의 최상위 객체인 Object와 String 클래스와의 관계처럼 비슷한 개념으로 이해하면 어떨까 생각한다. Object의 equals, hashCode와 같은 메소드를 상속받은 하위 클래스(String)에서 사용이 가능하듯 말이다.)
다만, $parent 프로퍼티 사용시 HTML 구조를 조금만 변경해도 애플리케이션이 쉽게 망가질 수 있기 때문에 $parent 프로퍼티는 사용하지 않는것을 권장하고있다.



[ $scope의 범위 ]

위의 예제에서 $scope의 상속과 $parent에 대해 이해할 수 있을 것이다.
첫번째, Hello, world! 는 ng-init을 통해 초기화된 name 모델의 값으로 <body>태그에 해당하는 $scope에 저장된 값이다. (이 값이 $rootScope가 아닌 $scope라고 말하는 이유는 HelloCtrl.$scope.$parent를 화면에 출력했을때 노출된 값이 $scope 였기 때문이다. 다만 $rootScope가 아닌지도 명확히 할 수 없는 이유는 HelloCtrl.$scope.$parent.$parent 의 값으로 아무런 값도 출력되지 않았고, <body> 태그에서도 $parent 출력시 아무런 값도 출력되지 않았기때문이다. 마찬가지로 $rootscope, $rootScope 모두 아무런 값도 출력되지 않는다. 아직은 AngularJS에 대해 다 아는것이 아니기때문에 $rootScope의 정체는 차차 밝혀나가야겠다.)
두번째, Hello, world_child! 는 ng-controller를 통해 HelloCtrl 컨트롤러의 $scope로 상위 name 모델을 상속받는다. name 모델의 초기값인 world를 world_child로 변경한 경우 상위값은 변경없이 자신(HelloCtrl 컨트롤러 범위 내)의 스코프 모델 값만 변경되어 반영된 것을 확인할 수 있다.
세번째, Hello, parent's world! 는 HelloCtrl 컨트롤러의 $scope의 상위 스코프($parent)를 가리키며, 이 상위 $parent는 결국 ng-init을 통해 초기화된 name 모델을 가리킨다. HelloCtrl.$scope.name의 값을 변경한 경우, $parent.name과는 다른 네임스페이스에 존재하므로 $parent 에는 영향이 미치지 않으며, $parent.name의 초기값인 world로 노출되는 것이다.



[ ng-repeat : 반복 ]

1. ng-app
  : AngularJS를 초기화한다.($rootScope 생성)
2. ng-controller="WorldCtrl"
  : AngularJS는 DOM 트리 탐색 중 ng-controller 디렉티브를 만나 Scope.$new()를 호출하여 새로운 $scope 인스턴스를 생성한다.
3. ng-repeat="country in countries"
  : ng-repeat은 
컬렉션을 순회하며 컬렉션의 한 요소당 DOM 요소를 하나씩 생성한다.
    ng-repeat은 countries의 요소 마다 <li> 엘리먼트와 $scope를 생성하며, 
    WorldCtrl의 $scope 에 할당된 countries에서 값을 하나씩 빼내어 country라는 이름으로 새롭게 생성된 li태그의 $scope에 추가한다.
4. {{worldPercentage(country.population)}}
  : worldPercentage 메소드는 WorldCtrl controller의 $scope에 정의된 메소드로 하위 li 태그의 $scope에 상속되어 정의없이 사용이 가능하다.
    다만, 
country.$scope에서 메소드가 호출되었지만 메소드 정의시점의 $scope가 WorldCtrl의 $scope였으므로 $scope.population의 값은 7000이 된다. 

    

스코프 생명주기

네임스페이스를 분리하고 변수의 이름 충돌을 피하기 위해 스코프는 꼭 필요하며, 작으면서 계층이 잘 분류된 스코프는 메모리 소모를 관리하기에도 좋다. 한 스코프가 더이상 필요없어지면 스코프에 포함된 모델과 기능도 정리(GC)될 수 있다.
새로운 스코프는 보통 스코프 생성 디렉티브에 의해 생성되기도 하고 정리되기도 한다. 이외에도 $new()와 $destroy() 메소드를 사용하여 스코프를 직접 생성하거나 정리할 수도 있다. (Scope 객체에 선언된 메소드이다.)


 ※ 참고

AngularJS는 유효한 DOM 트리(HTML 태그가 올바르게 열고 닫힌 경우)에서만 동작하며, 유효하지 않은 DOM 트리에서는 동작하지 않고 어떠한 에러 메시지도 나오지 않는다.



** 용어 **

MVVM 패턴 : Model - View - ViewModel 패턴의 약어

ViewModel : View의 이벤트 및 표현을 담당하며, ViewModel의 값이 바뀌면 View에 자동으로 업데이트한다.


참고URL : 

http://atconsole.com/2013/06/05/mvc-mvp-mvvm-%EC%9D%98-%EC%9D%B4%ED%95%B4/

------------------------------------

nodejs와의 차이점

angularjs를 렌더링하기위해서 브라우저의 반응속도가 느려지지 않을까? 느려진다면 얼마만큼 느려질까?


ng-init 디렉티브로는 하나의 모델일때만 초기값을 설정할 수 있는듯하다. 여러개를 선언한 경우 모델로서 동작은 하지만 초기화 값이 화면에 렌더링되어 표현되지 않는다.


javascript에 정의된 이벤트와 angularJS의 이벤트명이 어떻게 맵핑되는걸까? api document에 있나?
(angularjs는 dom 기반의 이벤트 프로그래밍 모델 사용을 지양하고있다. 대신 양방향 데이터 바인딩을 사용할 것을 권장한다.)


$scope API Reference(method, variable)


디렉티브의 종류

ng-disabled

ng-controller

ng-repeat

ng-model

ng-app

ng-init

ng-class

'development > angularjs' 카테고리의 다른 글

angularjs - Module  (0) 2014.08.19
angularjs - hello world  (0) 2014.08.03
Posted by dreamhopp
,

AngularJS

자바스크립트로 작성된 클라이언트 측 MVC 프레임워크

구글직원의 개인프로젝트에서 시작어 현재는 구글 공식지원 프로젝트가 되었다.

브라우저에서 돌아가며, ajax 스타일의 단일 페이지 웹 애플리케이션 작성에 좋은 언어이다.

HTML을 템플릿 언어로 사용하여 HTML 태그와 속성으로 정적인 HTML 문서에 동적인 특성을 추가한다.

의존성 주입(DI)과 테스트에 집중하는 프레임워크이다.


유용한 사이트/도구

대표사이트 : www.angularjs.org

Batarang : AngularJS 웹 애플리케이션의 상태를 시각적으로 살필 수 있는 크롬 확장 프로그램.

Plunker(http://plnkr.co), jsFiddle(http://jsfiddle.net) : 브라우저에서 자바스크립트, CSS, HTML 등의 코드를 작성하고 공유할 수 있는 도구.


Tutorial

<html>

  <head>

    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>

  </head>

  <body ng-app ng-init="name='Plunker'">

    This World is whose world? <input type="text" ng-model="name">

    <h1>Hello {{name}} World!</h1>

  </body>

</html>

AngularJS를 사용하려면 

1. script에 ajgularjs 라이브러리를 추가하고, 
2. ng-app HTML 사용자 정의 속성을 통해 AngularJS에 대한 초기화를 진행하며,
3. ng-init 을 통해 템플릿 렌더링 전 모델을 초기화 해준다. (변수선언, 객체생성의 개념으로 이해하면 된다.)
4. ng-model 을 통해 변수와 객체를 연결하여 변수값을 동적으로 사용자에게 입력받을 수 있다.
   (테스트해보니 ng-model이 적용되면 input 객체에 value 속성을 할당해도 적용되지 않는다.)
5. {{변수명}}의 형식을 통해 모델의 특정값을 렌더링 하여 화면에 노출한다.



[ Tutorial 실행화면 (Plunker) ]



** 용어 **

디렉티브(directive) : AngularJS 프레임워크가 알아볼 수 있는 특별한 HTML 태그와 속성을 가리킨다. (예 : ng-app, ng-init 등)


'development > angularjs' 카테고리의 다른 글

angularjs - Module  (0) 2014.08.19
angularjs - $scope  (0) 2014.08.03
Posted by dreamhopp
,

서비스의 서버 구성을 보다보면 모든 서버가 WEB(httpd)과 WAS(tomcat)를 따로 구성하고있었다.

내가 많이 본 서버는 Tomcat과 Apache httpd로 구성된 케이스인데 아래와 같이 2개의 형태로 나뉘어볼 수 있었다.

   타입1. web과 was서버가 하나의 물리적인 서버에 함께 있고, httpd만 L4로 로드밸런싱 하는 형태

   타입2. web과 was서버가 각각 물리적인 서버에 나뉘어있고 web서버끼리, was서버끼리 L4로 로드밸런싱하는 형태

  

그러다 보니 왜 서버를 분리해야하는지, 그리고 두가지 타입중 어떤 방식이 더 좋은지 궁금증이 생겼다.

여러모로 찾아보긴 했으나 어느것이 답인지도 모르겠지만 비용적인 측면과 보안적인 측면에 그 이유가 있는것같다.

어떤것이 더 좋은지, 어떤 상황에 무얼 고려하여 서버를 구성해야할지 의문이다.


[타입1] 

  • tomcat으로의 접근은 httpd에서 특정포트만을 오픈하여 구성하고 L4를 web서버에만 연결함으로써
    L4장비를 하나만 사용하여 비용감소(?)
  • httpd가 down되는경우 정상적으로 동작하는 tomcat서버에 트래픽이 전달되지 않음.
    (보통 이런경우 L7 check를 통해 빠르게 감지하여 장애대응)
[타입2]
  • web서버와 was 서버 각각 L4를 통해 로드밸런싱을 구성하여 특정서버가 장애발생하였을경우에도 다른 서버들은 정상적으로 서비스 가능하다.
  • 물리적으로 서버를 분리하여 web서버가 해킹되더라도 was 서버까지 영향이 미치지 않음
  • 특정서버로 부하가 몰리는것을 방지할 수 있음.



참고URL : 

http://blog.naver.com/sangmanla/100199297270


'development > Infra' 카테고리의 다른 글

패키지관리도구 - YUM, HOMEBREW  (0) 2014.06.18
Posted by dreamhopp
,

YUM (Yello dog Updater, Modified)

RPM (Redhat Package Manager) 기반 시스템을 위한 자동 업데이터 겸 패키지 설치/제거 도구.

YUM 사용법

패키지 설치 : yum install {package}
- 그룹패키지 설치 : yum groupinstall {group}
- 패키지 재설치 : yum reinstall {package}
- 패키지 삭제 : yum remove {package}
- 설치된 패키지 업데이트정보 체크 : yum check-update
- 패키지 업데이트 : yum update 
{package}
- 패키지 업그레이드 : yum upgrade {package}
- 캐시데이터 제거 : yum clean all
- 캐시데이터 생성 : yum makecache
- 패키지 의존성 테스트 : yum deplist
- 패키지 다운그레이드 : yum downgrade 
{package}
- 패키지 삭제 : yum erase 
{package}
- 패키지 검색 : yum search 
{package}
- (그룹)패키지 정보 : yum info 
{package | group}
- 그룹패키지 정보 : yum groupinfo {group}
- 특정단어가 포함된 패키지검색 : yum list {term}
- 그룹리스트 정보 확인 : yum grouplist 
{group}
- 그룹리스트 삭제 : yum groupremove 
{group}
- 특정파일을 제공한 패키지검색 : yum whatprovides {filename}
- 커널패키지에 설치된 모든 패키지 업데이트 : yum -y update

    

YUM Q&A

1. update와 upgrade의 차이점
 - yum update : 모든 의존성이 안전한경우 현재 설치된 패키지들을 업데이트한다.
 - yum upgrade [= yum update -obsolete] : 패키지를 최신으로 업데이트하고 구 패키지를 제거한다.
                      upgrade 명령어는 필요한 패키지를 지울수도 있기때문에 사용하지 않는것을 권장한다.

HOMEBREW

Apple에서 제공하지 않지만 필요한 패키지를 설치하는 OS X용 패키지 관리자.
brew에서는 package가 아닌 formula 라고 지칭한다.

BREW 사용법

- brew 패키지 설치경로 확인 : brew --cellar
- 패키지 설치 : brew install {formula}
- 특정버전 패키지 설치
   > 사용가능한 버전정보 확인 : brew versions {formula}
   > 버전별 소스 다운로드 (brew versions를 통해 조회한 버전 중 원하는 버전의 git checkout 명령어를 실행)
   > 패키지 설치 : brew install {formula}
   > 여러버전이 설치되어있는경우 원하는 버전의 패키지로 연동 : brew switch {formula} {version}
- 패키지 검색 : brew search {formula}
- 설치된 패키지 조회 : brew list
- 최신 패키지정보로 갱신(brew의 리스트만 갱신) : brew update 
- 최신 패키지로 재설치(설치된 패키지) : brew upgrade {formula}
- 설치된 패키지 삭제 : brew remove {formula}

참고URL : 
http://ko.wikipedia.org/wiki/Yum
https://access.redhat.com/site/ko/node/82093
http://nowonbun.tistory.com/114
http://unix.stackexchange.com/questions/55777/in-centos-what-is-the-difference-between-yum-update-and-yum-upgrade
http://brew.sh/index_ko.html
http://floss.zoomquiet.io/data/20111223160257/index.html
https://gist.github.com/gcatlin/1847248
http://stackoverflow.com/questions/13477363/how-can-i-brew-link-a-specific-version

'development > Infra' 카테고리의 다른 글

WEB와 WAS 서버를 분리하는 이유는?  (0) 2014.07.02
Posted by dreamhopp
,