SpringSecuriy new PasswordEncoder.
Spring-Secury 버전이 3.1.4가 되면서 부터 org.springframework.security.authentication.encoding.PasswordEncoder가 @Deprecate 됬다.
이를 교체하기 위해서 org.springframework.security.cryptho.password.PasswordEncoder 사용을 유도하고 있다.
기존 PasswordEncoder에서는 별도의 SaltSource를 만들어서 사용하도록 하고 있었는데, 신규 PasswordEncoder에서는 이를 구현한 클래스(BCryptPasswordEncoder) 같은 친구들이 자동으로 만들어 준다.
국내에 출간된 스프링 시큐리티 3 책에는 이전 PasswordEncoder를 사용하는 예제가 들어있는데, Spring-Security 버전을 3.1.4 이상으로 사용할 려면 새로운 PasswordEncoder 인터페이스를 사용 하도록 하자.
생활 코딩 하면서 만들고 있는 사이트에 encoding.PasswordEncoder를 사용하고 있어서 @Deprecate 된게 거슬렸었는데, 이번 기회에 password.PasswordEncoder로 변경 하였다
아래 링크를 참조 하면 손쉽게 바꿀 수 있다.
http://stackoverflow.com/questions/17444258/how-to-use-new-passwordencoder-from-spring-security
(그냥 빈 패키지를 변경해주고 메서드를 .encode로 변경하고, 두번째 파라미터를 삭제 하면 됨)
SaltSource를 PasswordEncoder가 직접 만들어 주기(랜덤) 때문에 별도로 관리할 필요가 없다. 고로 SaltSoure로 사용한 값이 노출 될 가능성이 없다.
이클립스 4.4를 써야..
Java 8을 제대로 쓸려면 이클립스 4.4를 써야...
지금 쓰고 있는 버전이 STS 3.6.1(eclipse 4.3)이었는데 JDK를 Java 8로 바꿔서 사용할 경우 Comparator를 구현한 클래스에서 Java 8에 새로 추가된 Default 메서드도 구현하라고 컴파일 에러를 만들어 준다.
그래서 eclipse 4.4가 포함된 sts 3.6.1로 바꿨더니 안나옴.
서버 목록에 tomcat 8도 추가 됨.
메뉴에 몇몇 아이콘 들이 추가 됨.
다른 기능들은 한번 보면서 찾아 봐야겠다.
Java 8, 새로운 Date, Time API.
Java 8이 릴리즈 되면서 새로운 Date, Time API도 같이 포함되어 릴리즈 되었다.
아래 링크에 보면 기존 Java의 Date, Time의 문제점에 대해서 잘 설명되어 있다. 한번씩 읽어 보면 좋을 듯 하다.
이런 저런 문제점이 있지만, 그냥 사용하고 있는데가 많다. 나도 그렇고.
정상혁님이 정리한 Java 날짜 및 시간(http://helloworld.naver.com/helloworld/textyle/645609 )
이 내용은 이 아티클(http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html)을 허접하게 번역한 문서이다.
===============================================================================================================
왜 새로운 Date, Time API가 필요할까?
오랜 시간동안 Java에서 제공하는 Date, Time API는 부족한 기능 지원을 포함한 여러가지 문제점을 가지고 있었다.
예를 들어 java.util.Date와 SimpleDateFormatter는 Thread-Safe 하지 않아서 잠재적인 동시성 문제를 가지고 있다. 또한 몇몇 클래스들은 형편없이 디자인 되어있는데 java.util.Date 는 1900년도 부터 시작한다거나 월(month)는 1부터 시작 하지만 일(day)는 0부터 시작하는 등 매우 직관적이지 않도록 설계되어 있다.
이런 이슈들을 포함한 다양한 원인들로 인해 몇몇 개발자들은 Joda-Time 같은 써드파티 라이브러리를 사용 하기도 한다.
JDK 코어에서 이런 문제점들을 해결하고 더 좋고 직관적인 API들을 제공하기 위해 새롭게 재 디자인한 Date, Time API를 Java SE 8부터 제공 하기로 했다.
Joda-Time의 저작자인 Stephen Colebourne 및 오라클의 주도 아래 JSR 310 표준에 맞춰 Java SE 8의 java.time 패키지에 새로운 API를 포함하여 릴리즈 했다.
핵심 아이디어들
새로운 API들은 아래 3가지 핵심 아이디어들을 따르고 있다.
Immutable-value classes(불변 클래스) : 기존 Java에서의 formatter가 가지고 있는 심각한 취약점 중에 하나는 Thread-safe하지 않다는 것이다. 이 문제점은 개발자들에게 formatter를 사용할 때 날짜와 관련된 내용 뿐만 아니라 동시성에 관련된 내용도 함께 생각해야 된다는 문제점이 있다. 새롭게 제공되는 API는 이러한 문제점을 회피하기 위해 핵심 클래스들을 불변 클래스로 만들고 잘 정의된 값을 제공 한다.
도메인 주도 개발 : 새로운 API 모델은 Date, Time을 사용할때 분명하게 다른 방법을 제공하도록 디자인됬다. 이 차이점은 이전 Java 버전에서는 명확하지 않게 정의 되어 있는데 예를 들어 java.util.Date 는 timeline(UNIX 시간을 기준으로 밀리세컨드)을 제공하지만 만약 toString을 호출하면 해당 timezone에 해당하는 결과를 표시함으로써 개발자들을 햇갈리게 한다. 도메인 주도 개발은 장기적으로 봤을때 명확하고 이해하기 쉽도록 해 주지만 기존 애플리케이션에 새로운 API를 제공할 때 기존 Date model과 비교해서 잘 사용할 수 있도록 해야 한다.
연대의 분리 : 새로운 API는 개발자들이 ISO-8601 표준을 사용하도록 강제 하지 않고 일본이나 태국처럼 세계 어디서나 서로 다른 시간대를 가지고 사용할 수 있도록 해준다. 이것은 표준 연대에서 작업해야되는 개발자들에게 다른 추가 부담을 주지 않고 개발에 집중 할 수 있도록 해준다.
LocalDate 와 LocalTime
새로운 API를 처음 접할때 만나게 되는것은 아마도 LocalDate와 LocalTime 클래스일 것이다. 이들은 Date와 Time에 관련된 내용을 제공하고 당신이 사용중인 달력과 시계와 동일하게 동작한다. LocalDateTime 이라는 LocalDate와 LocalTime의 복합 클래스도 존재한다.
시간대는 사람마다 다르게 적용되는데 시간대를 고정해서 사용할 필요가 없다면 Local 클래스를 사용하면 된다. 예를 들어 JavaFx 데스크탑 애플리케이션을 들 수 있는데, 이 애플리케이션에서 사용되는 시간에 일관성있는 시간을 제공할 수 있다.
Creating Objects
새로운 API의 핵심 클래스는 오브젝트를 생성하기 위해 다양한 factory 메서드를 사용한다. 오브젝트 자기 자신의 특정 요소를 가지고 오브젝트를 생성할 경우 of 메서드를 호출하면 되고 다른 타입으로 변경할 경우에는 from 메서드를 호출하면 된다. 이들은 서로 짝을 이루고 String 값을 파라미터로 받는다.
- LocalDateTime timePoint = LocalDateTime.now(); // 현재의 날짜와 시간
- LocalDate.of(2012, Month.DECEMBER, 12); // 2012-12-12 from values
- LocalDate.ofEpochDay(150); // 1970-05-31 middle of 1970
- LocalTime.of(17, 18); // 17:18 (17시 18분)the train I took home today
- LocalTime.parse("10:15:30"); // From a String
자바에서 사용되는 표준 Getter 방식에 따라 아래와 같이 사용할 수 있다.
- LocalDate theDate = timePoint.toLocalDate();
- Month month = timePoint.getMonth();
- int day = timePoint.getDayOfMonth();
- timePoint.getSecond();
날짜를 변경할 때 오브젝트의 값을 변경해야 되는데 신규 API는 불변 클래스이기 때문에 사용할때 새로운 오브젝트로 생성되서 값을 리턴해준다. 아래 setter 사용하는것을 참조 하라.
서로 다른 값들을 가지고 날짜를 수정하는 방법역시 제공한다.
- // 2010-08-10으로 세팅된 신규 Object를 리턴
- LocalDateTime thePast = timePoint.withDayOfMonth(10).withYear(2010);
- // 2010-08-10에서 3주를 더하고 또 3주를 더한 2010-09-21을 리턴
- LocalDateTime yetAnother = thePast.plusWeeks(3).plus(3, ChronoUnit.WEEKS);
신규 API는 adjuster(일반적인 로직을 감싸서 사용되는 코드 블록)의 개념도 가지고 있다. 하나 이상의 필드를 세팅할때 with를 사용하고 날짜나 시간을 더하거나 뺄때 plus를 사용할 수 있다. 값 객체는 조정자 처럼 동작할 수 있는데 객체 자신이 가지고 있는 값을 가지고 갱신한다. API에 정의된 내장 adjuster들도 있지만 사용자가 로직을 정의한 adjuster도 만들어서 사용할 수 있다.
- import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;
- import static java.time.temporal.TemporalAdjusters.next;
- import static java.time.DayOfWeek.*;
- LocalDateTime timePoint = LocalDateTime.now()
- foo = timePoint.with(lastDayOfMonth()); // 2014-08-31
- bar = timePoint.with(previousOrSame(ChronoUnit.WEDNESDAY)); // 2014-08-27
- // Using value classes as adjusters, 2014-08-26
- timePoint.with(LocalTime.now());
Truncation(날짜 자르기)
신규 API는 날짜, 시간, 일 등에 해당하는 값 들을 잘라내서 표현할 수 있도록 truncatedTo 라는 메서드를 제공한다.
- // 20:39:54.073, 20시 39분 54.073초
- LocalTime truncatedTime = LocalTime.now();
- // 20:39:54
- truncatedTime.truncatedTo(ChronoUnit.SECONDS);
- // 20:38
- truncatedTime.truncatedTo(ChronoUnit.MINUTES);
Time Zone Classes
ZonedDateTime은 아래 예 처럼 완벽하게 기술된 날짜와 시간이다. 특정 서버의 컨텍스트에 의존하지 않고 날짜와 시간을 표현하고 싶다면 이 클래스를 사용하면 된다.
- ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]");
OffeSetDateTime은 data를 직렬화 하여 DB에 넣거나 만약 서버가 다른 시간대를 사용한다면 직렬화된 형태로 logging time stamp를 표현하는데 사용할 수 있다.
OffsetTime은 아래와 같은 offset을 해결해준다.
- ZoneOffset offset = ZoneOffset.of("+1");
- OffsetTime time = OffsetTime.now();
- // changes offset, while keeping the same point on the timeline
- OffsetTime sameTimeDifferentOffset = time.withOffsetSameInstant(offset);
- // changes the offset, and updates the point on the timeline
- OffsetTime changeTimeWithNewOffset = time.withOffsetSameLocal(offset);
- // Can also create new object with altered fields as before
- changeTimeWithNewOffset.withHour(3).plusSeconds(2);
기존 자바에서 사용했던 java.util.TimeZone이 있지만 JSR 310를 구현한 클래스들은 전부 immutable 이지만 기존 time zone은 mutable이기 때문에 Java SE 8에서는 사용되지 않는다.
Periods
Period를 사용해서 "3달 하고 1일 뒤"같은 표현을 사용할 수 있다. 이는 타임라인을 사용하는 개념으로 지금까지 살펴봣던 클래스들과는 약간 다르다.
- // 2014-08-26
- LocalDate oldDate = LocalDate.now();
- ZonedDateTime oldDateTime = ZonedDateTime.now();
- // 2년2개월1일
- Period period = Period.of(2, 2, 1);
- // 2년2개월1일뒤, 2016-10-27
- LocalDate newDate = oldDate.plus(period);
- // 2년2개월1일전, 2012-06-25
- ZonedDateTime newDateTime = oldDateTime.minus(period);
Durations
Duration은 시간의 관점에서 측정된 타임라인으로 Period와 유사하지만 서로 다른 정밀도를 보여준다.
- Instant firstInstant= Instant.ofEpochSecond(1294881180 ); // 2011-01-13 01:13
- Instant secondInstant = Instant.ofEpochSecond(1294708260); // 2011-01-11 01:11
- Duration between = Duration.between(firstInstant, secondInstant);
- // negative because firstInstant is after secondInstant (-172920)
- long seconds = between.getSeconds();
- // get absolute result in minutes (2882)
- long absoluteResult = between.abs().toMinutes();
- // two hours in seconds (7200)
- long twoHoursInSeconds = Duration.ofHours(2).getSeconds();
plus, minus, with 기능을 사용할 수 있고 날짜나 시간 값도 수정 할 수 있다.
The Rest of the API
다른 일반적인 상황들에서 사용할만한 클래스들도 몇가지 지원 하는데
MonthDay 클래스는 Month와 Day가 짝을 이루고 있는것으로 생일을 표현할때 유용하다.
YearMonth 클래스는 신용카드의 유효기간 처럼 월과 달로 구성된 것에 사용하는데 적합하다.
Java 8의 JDBC는 새로 추가된 타입들을 지원 하지만 JDBC API의 공개적인 변경점은 없다. 기존에 존재 했던 generic setObject, getObject로 충분히 커버 가능하다.
아래 ANSI SQL의 타입이나 vendor들이 정의한 클래스는 아래 처럼 매핑된다.
ANSI SQL | Java SE 8 |
DATE | LocalDate |
TIME | LocalTime |
TIMESTAMP | LocalDateTime |
TIME WITH TIMEZONE | OffsetTime |
TIMESTAMP WITH TIMEZONE | OffsetDateTime |
JDK8에선 PermGen이 완전히 사라지고 Metaspace가 이를 대신 함.
JDK 관련해서 거의 안봤었는데.. Java 8 출시되고 나고 여러가지 좋은 기능들이 많아서 천천히 보는중.
Java 7까지 PermGen(Permanent Generation)이 있었는데 Java 8이 릴리즈 되면서 PermGen이 완전히 제거 되고 Metaspace라는 네이티브 메모리 영역으로 클래스 메타데이터들이 다 이동하게됬다.
PermGen은 무엇인가?
Permanent Generation은 힙 메모리 영역중에 하나로 자바 애플리케이션을 실행할때 클래스의 메타데이터를 저장하는 영역이다.(Java 7기준)
자바 개발자라면 OutOfMemoryError: PermGen Space error이 발생했던것을 본적이 있을텐데 이는 Permanent Generation 영역이 꽉 찼을때 발생하고 메모리 누수가 발생했을때 생기게 된다. 메모리 누수의 가장 흔한 이유중에 하나로 메모리에 로딩된 클래스와 클래스 로더가 종료될때 이것들이 가비지 컬렉션이 되지 않을때 발생한다.
Java 7 부터 PermGen을 제거하기 위한 준비를 시작
Java 7 부터 Permanent Generation 을 제거 하기 위해 노력했고 아래와 같은 것을이 Java Heap 이나 native heap 영역으로 이동했다.
- Symbols 는 native heap
- Interned String(중복된 문자를 상수화) 는 Java Heap으로
- Class statics 는 Java Heap으로
Java 7의 Metaspace
PermGen은 Java 8부터 Metaspace로 완벽하게 대체 되었고 Metaspace는 클래스 메타 데이터를 native 메모리에 저장하고 메모리가 부족할 경우 이를 자동으로 늘려준다. Java 8의 장정줌에 하나로 OutOfMemoryError: PermGen Space error는 더이상 볼 수 없고 JVM 옵션으로 사용했던 PermSize 와 MaxPermSize는 더이상 사용할 필요가 없다. 이 대신에 MetaspaceSize 및 MaxMetaspaceSize가 새롭게 사용되게 되었다. 이 두 값은 Metaspace의 기본 값을 변경하고 최대값을 제한 할 수 있다.
MetaspaceSize
이 설정은 JVM이 사용하는 네이티브 메모리양을 변경하는데 사용된다. 시스템에서 기본으로 제공되는 것보다 더 많은 메모리를 사용할 것이라고 확신할 경우 이 옵션을 사용하면 된다.
MaxMetaspaceSize
이 설정은 metaspace의 최대 메모리 양을 변경하는데 사용된다. 애플리케이션을 서버에서 동작시킬때 메모리 영역을 조절하고 싶거나 메모리 누수가 발생해서 시스템 전체의 네이티브 메모리를 사용해 버리지 않도록 하기 위해서 사용하면 된다. 만약 native 메모리가 꽉 찾는데도 애플리케이션이 메모리를 더 요구 한다면 java.lang.OutOfMemoryError: Metadata space가 발생한다.
장점
PermGen 영역이 삭제되어 heap 영역에서 사용할 수 있는 메모리가 늘어났다.
PermGen 영역을 삭제하기 위해 존재했던 여러 복잡한 코드들이 삭제 되고 PermGen영역을 스캔 하기 위해 소모되었던 시간이 감소되어 GC 성능이 향상 되었다.
참고 : http://techidiocy.com/metaspace-java8/, http://java.dzone.com/articles/java-8-permgen-metaspace, http://openjdk.java.net/jeps/122
자바 컬렉션 프레임워크 인터뷰 질문 40개
http://www.javacodegeeks.com/2013/02/40-java-collections-interview-questions-and-answers.html
저기 있는거 번역..
맨날 HashMap하고 ArrayList 만 쓰다 보면, 컬렉션 프레임워크가 뭔지 잊어 버릴때가...-_ -;;
======================================================================================================================
컬렉션 프레임워크를 사용함으로써 얻을 수 있는 이점들을 아래와 같다.
- 별도로 컬렉션 클래스를 구현하는 것보다 구현되있는것을 사용함으로써 코딩 시간을 감소 시킬 수 있다.
- 컬렉션 프레임워크들은 잘 테스트 되고 검증되어있기때문에 코드 품질을 보장한다.
- JDK에 포함된 컬렉션 프레임워크들을 사용하여 코드 유지보수 시간을 감소 시킬 수 있다.
- 재사용 가능하고 상호 운용성이 보장 된다.
- Collection 은 가장 기본이 되는 인터페이스이다. 자바는 이 인터페이스를 직접 구현한 클래스는 아무것도 제공하지 않는다.
- Set 은 중복을 허용하지 않는 집합이다.
- List 는 중복을 허용하고 정렬이 가능한 컬렉션이다. 인덱스를 통해 아무런 엘리먼트나 접근할 수 있고, 길이 조정이 가능한 배열과 비슷하다고 할 수 있다.
- Map 은 키/값을 가지고 있는 오브젝트다. 키값은 중복되어선 안되고 하나의 키 값은 하나의 값에 매핑된다.
- Set과 List에 Iterator를 사용할 수 있지만 ListIterator에는 List만 가능하다.
- Iterator 는 앞쪽으로 탐색을 하지만 ListITerator는 양방향 순회가 가능한다.
- ListIterator는 Iterator 인터페이스를 상속받았고 추가적으로 Add, 엘리먼트 교체, 현제 index의 이전, 다음 엘리먼트 가져오기 등 많은 추가 기능을 제공한다.
01 | List<String> strList = new ArrayList<>(); |
02 | //using for-each loop |
03 | for (String obj : strList){ |
04 | System.out.println(obj); |
05 | } |
06 | //using iterator |
07 | Iterator<String> it = strList.iterator(); |
08 | while (it.hasNext()){ |
09 | String obj = it.next(); |
10 | System.out.println(obj); |
11 | } |
- If
o1.equals(o2)
, theno1.hashCode() == o2.hashCode()
should always betrue
. - If
o1.hashCode() == o2.hashCode
is true, it doesn’t mean thato1.equals(o2)
will betrue
.
- 만약 클래스가 equals()를 overrides 했다면 hashCode() 역시 override 해야 한다.
- 18번에 언급된 기본 구현 규칙을 따라야 한다.
- equals() 메서드가 사용되지 않으면 hashCode()도 사용하지 않아야 한다.
- 가장 좋은 방법은 key 클래스를 불변(immutable)으로 만드것이다. 이렇게 하면 hashCode()값은 캐시되어 빠른 성능을 가진다. 또한 불변 클랙스는는 hashCode() 및 equals()의 값이 변하지 않기 때문에 해당 값이 변해서 생기는 문제들을 해결할 수 있다. 예를 들어 아래 HashMap의 key 로 사용될 MyKey 클래스를 살펴봐라.
01 | //MyKey name argument passed is used for equals() and hashCode() |
02 | MyKey key = new MyKey( 'Pankaj' ); //assume hashCode=1234 |
03 | myHashMap.put(key, 'Value' ); |
04 |
05 | // Below code will change the key hashCode() and equals() |
06 | // but it's location is not changed. |
07 | key.setName( 'Amit' ); //assume new hashCode=7890 |
08 |
09 | //below will return null, because HashMap will try to look for key |
10 | //in the same index as it was stored but since key is mutated, |
11 | //there will be no match and it will return null. |
12 |
|
- Set keySet(): 맵에 존재하는 Key 값들을 Set으로 보여준다. 이 set들은 맵과 연결되어 있으며 맵을 바꾸거나 set을 바꾸면 값이 수정 된다. 만약 키 Set을 사용하는중에 map이 변경 되면 Set을 반복할때 나오는 결과값은 undefined 되게 된다. Set은 엘리먼트들을 지울 수 있고 이에 대응하는 값은 맵에서 삭제 된다.(remove, Set.remove, removeAll, retaionAll, clear) add 나 addAll같은 기능은 제공하지 않는다.
- Collection values() : 맵에 존재하는 Value 들을 컬렉션 형태로 보여준다. 이것 역시 맵과 연동되어 있으며 collection을 수정 하면 map의 값이 수정된다.
- Set<Map.Entry<K, V>> entrySet() : 맵의 entry 들을 Set 형태로 보여준다.
- HashMap은 키/값에 null을 허용하는 반면 Hashtable은 이를 허용하지 않는다.
- Hashtable은 synchronized (synchronized) 되어 있지만 HashMap 은 그렇지 않다. 그래서 HashMap 은 단일 스레드 환경에서 더 좋은 퍼포먼스를 보여준다. 반면, Hashtable은 멀티 스레드 환경에 적합하다.
- LinkedHashMap 은 자바 1.4에서 HashMap의 서브클래스로 소개되었다. 그렇기 때문에 iteration 의 순서를 보장받고 싶다면, HashMap에서 LinkedHashMap으로쉽게 변경 가능하다. 그러나 Hashtable 에서는 그럴 수 없으므로 iteration 순서를 예측할 수 없다.
- HashMap은 iterator 키 셋을 제공하므로 fail-fast (12 참고) 기능을 사용하나 Hashtable은 Enumeration 키를 사용하므로 이런 기능을 제공하지 못한다.
- Hashtable은 legacy 클래스로 취급을 받기 때문에 만약 Map에서 iteration을 하는 도중에 수정가능한 Map을 사용하고 싶다면 ConcurrentHashMap을 사용하면 된다.
- 인덱스 기반이고 내부적으로 배열로 백업 할 수 있다.
- 엘리먼트들을 추가한 순서를 가지고 있고 이 순서를 가져 올 수도 있다.
- iterator를 구현하였으므로 fail-fast 방식이다.
- null 값을 가질 수 있고 인덱스 번호를 사용해 랜덤으로 접근 할 수 있다.
- Vector는 synchronized 되어 있지만 ArrayList는 그렇지 않다. 만약 iterating 중에 엘리먼트를 수정 하고 싶다면 CopyOnWriteArrayList를 사용하면 된다.
- ArrayList는 synchronized에 따른 간접비용이 아무것도 없기 때문에 Vector보다 빠르다.
- ArrayList가 좀 더 다재다능 한데 Collection Utility 클래스에서 제공하는 기능으로 synchronized를 시키거나 읽기 전용 리스트를 만들수도 있다.
- 리스트의 크기가 고정되어 있고 값을 저장하거나 탐색 용도로만 쓸 경우
- primitive 타입일 경우
- 만약 다차원 배열을 사용할 경우 [][] 배열을 사용하는게 List<List<>>를 쓰는것보다 쉽다.
- ArrayList는 인덱스 기반의 Array로 구성되어 있어서 랜덤 엑세스를 할 경우 O(1)의 속도를 가진다. LinkedList는 데이터들이 이전, 다음 노드 처럼 서로 연결된 node로 구성되어 있다. 인덱스 번호를 사용해서 엘리먼트에 접근 하더라도 내부적으로는 노드들을 순차적으로 순회하며 엘리먼트를 찾는다. LinkedList 의 속도는 O(n)으로 ArrayList 보다 느리다.
- 엘리먼트의 추가 및 삭제는 LinkedList가 ArrayList보다 빠른데 엘리먼트를 추가 및 삭제하는 중에 array를 리사이즈 하거나 인덱스를 업데이트를 할 일이 없기 때문이다.
- LinkedList의 엘리먼트들은 이전, 다음 엘리먼트들에 대한 정보를 가지고 있기 때문에 LinkedList가 ArrayList보다 더 많은 메모리를 소비한다.
Comparator
인터페이스를 사용하면 되는데 Comparable.compareTo(Object o)는 하나의 필드만 가지고 정렬을 수행하기 때문에 정렬에 필요한 오브젝트들 선텍할 수 있다. Comparator 인터페이스는 두개의 파라미터를 가지고 있는 compare(Object o1, Object o2)
메서드를 제공하는데 이 메서드는 만약 첫번째 변수가 두번째 변수보다 작으면 음수를 리턴하고 만약 두 값이 같으면 0, 더 크면 양수를 리턴한다.Collections.unmodifiableCollection(Collection c)
메서드를 사용해서 읽기전용 커렉션을 생성할 수 있고 만약 컬렉션을 수정할려는 시도가 생기면 UnsupportedOperationException을 발생 시킨다.- 예1 : ArrayList get(index i)는 엘리먼트의 숫자에 영향을 받지 않고 동일한 성능을 보여주기 때문에 Big-O 표기법으료 표시하면 O(1)으로 표기 할 수잇다.
- 예2 : 배열이나 리스트에 대한 선형 탐색은 엘리먼트를 찾는데 엘리먼트들의 숫자에 영향을 받기 때문에 O(n)으로 표시한다.
- 필요에 따라 상황에 맞는 컬렉션을 선택해야 된다. 예를 들어 사이즈가 고정되어 있으면 ArrayList보다 Array를 사용할 수 있다. 만약 맵에 삽입된 순서되로 iterate를 하고 싶으면 TreeMap을 사용하는것이 좋다. 중복을 허용하고 싶으 않으면 Set을 사용하면 된다.
- 몇몇 컬렉션 클래스들을 초기 용량을 지정할 수 있다. 만약 저장할 엘리먼트들의 사이즈를 알 경우에 초기 용량을 지정함으로써 rehashing이나 resizing이 일어나는것을 회피할 수 있다.
- 코드를 작성할때 구현 클래스가 아닌 인터페이스를 기반으로 작성해야 나중에 구현체를 변경할때 코드를 재작성하는 수고를 줄일수 있다.
- 런타임에 발생할 수 있는 ClassCastException을 회피할려면 항상 제너릭스를 사용해서 type-safety 한 상태를 유지하라
- 맵에 키를 사용할때 JDK에서 재공하는 immutable 클래스를 사용하여 사용자 클래스에서 hashCode()와 equals() 구현할 필요가 없게 하라
- 읽기전용 및 동기화, 빈 컬렉션등을 만들때는 자신만의 구현으로 생성하지 말고 Collections에서 제공하는 유틸리티 클래스를 사용하라. 이는 코드 재사용성을 높여주고 안정적이며 유지보수 비용을 줄여 준다.
두가지 역할만 하는 코드
01.
public
class
Calculator
02.
{
03.
private
readonly IStorageService storageService;
04.
private
List<
int
> history =
new
List<
int
>();
05.
private
int
sessionNumber =
1
;
06.
private
bool newSession;
07.
08.
public
Calculator(IStorageService storageService)
09.
{
10.
this
.storageService = storageService;
11.
}
12.
13.
public
int
Add(
int
firstNumber,
int
secondNumber)
14.
{
15.
if
(newSession)
16.
{
17.
sessionNumber++;
18.
newSession =
false
;
19.
}
20.
21.
var result = firstNumber + secondNumber;
22.
history.Add(result);
23.
24.
return
result;
25.
}
26.
27.
public
List<
int
> GetHistory()
28.
{
29.
if
(storageService.IsServiceOnline())
30.
return
storageService.GetHistorySession(sessionNumber);
31.
32.
return
new
List<
int
>();
33.
}
34.
35.
public
int
Done()
36.
{
37.
if
(storageService.IsServiceOnline())
38.
{
39.
foreach(var result in history)
40.
storageService.Store(result, sessionNumber);
41.
}
42.
newSession =
true
;
43.
return
sessionNumber;
44.
}
45.
}
01.
public
class
Calculator_Mockless
02.
{
03.
private
readonly StorageService storageService;
04.
private
readonly BasicCalculator basicCalculator;
05.
06.
public
Calculator_Mockless()
07.
{
08.
this
.storageService =
new
StorageService();
09.
this
.basicCalculator =
new
BasicCalculator();
10.
}
11.
12.
public
int
Add(
int
firstNumber,
int
secondNumber)
13.
{
14.
return
basicCalculator.Add(firstNumber, secondNumber);
15.
}
16.
17.
public
List<
int
> GetHistory()
18.
{
19.
return
storageService.
20.
GetHistorySession(basicCalculator.SessionNumber);
21.
}
22.
23.
public
void
Done()
24.
{
25.
foreach(var result in basicCalculator.History)
26.
storageService
27.
.Store(result, basicCalculator.SessionNumber);
28.
29.
basicCalculator.Done();
30.
}
31.
}
32.
33.
public
class
BasicCalculator
34.
{
35.
private
bool newSession;
36.
37.
public
int
SessionNumber { get;
private
set; }
38.
39.
public
IList<
int
> History { get;
private
set; }
40.
41.
public
BasicCalculator()
42.
{
43.
History =
new
List<
int
>();
44.
SessionNumber =
1
;
45.
}
46.
public
int
Add(
int
firstNumber,
int
secondNumber)
47.
{
48.
if
(newSession)
49.
{
50.
SessionNumber++;
51.
newSession =
false
;
52.
}
53.
54.
var result = firstNumber + secondNumber;
55.
History.Add(result);
56.
57.
return
result; ;
58.
}
59.
60.
public
void
Done()
61.
{
62.
newSession =
true
;
63.
History.Clear();
64.
}
65.
}
비밀번호 해시에 소금치기 - 바르게 쓰기
중요한 경고! 만약 자신만의 비밀번호 해싱 방법을 가지고 있다면 그렇게 하지 말아라! 그 방법은 망가지기 쉽다. 만약 암호학을 전공하고 있다고 해도 이 경고를 무시해서는 안된다. 이 경고는 모두에게 적용된다. 절대 자신만의 암호화 방법을 만들지 말라.비밀번호 저장에 관련된 문제는 이미 해결 되어있다. |
해싱 알고리즘은 단방향성을 가지고 있고 고정된 길이의 "fingerprint" 값을 제공한다. 위의 예제 처럼 한글자만 변경되도 전혀 다른 해쉬 값을 생성한다. 이 방법은 비밀번호가 인코딩 되어 저장될때 디코딩할 수 없기 때문에 비빌번호를 보호하는데 아주 좋은 방법이다. 이 방법과 동시에 사용자가 입력한 패스워드가 동일한지도 검증을 해야 한다.
Platform | CSPRNG |
---|---|
PHP | mcrypt_create_iv, openssl_random_pseudo_bytes |
Java | java.security.SecureRandom |
Dot NET (C#, VB) | System.Security.Cryptography.RNGCryptoServiceProvider |
Ruby | SecureRandom |
Python | os.urandom |
Perl | Math::Random::Secure |
C/C++ (Windows API) | CryptGenRandom |
Any language on GNU/Linux or Unix | Read from /dev/random or /dev/urandom |
Hash functions like MD5, SHA1, and SHA2 use the Merkle–Damgård construction, which makes them vulnerable to what are known as length extension attacks. This means that given a hash H(X), an attacker can find the value of H(pad(X) + Y), for any other string Y, without knowing X. pad(X) is the padding function used by the hash.
This means that given a hash H(key + message), an attacker can compute H(pad(key + message) + extension), without knowing the key. If the hash was being used as a message authentication code, using the key to prevent an attacker from being able to modify the message and replace it with a different valid hash, the system has failed, since the attacker now has a valid hash of message + extension.
It is not clear how an attacker could use this attack to crack a password hash quicker. However, because of the attack, it is considered bad practice to use a plain hash function for keyed hashing. A clever cryptographer may one day come up with a clever way to use these attacks to make cracking faster, so use HMAC.
Why does the hashing code on this page compare the hashes in "length-constant" time?
Comparing the hashes in "length-constant" time ensures that an attacker cannot extract the hash of a password in an on-line system using a timing attack, then crack it off-line.
The standard way to check if two sequences of bytes (strings) are the same is to compare the first byte, then the second, then the third, and so on. As soon as you find a byte that isn't the same for both strings, you know they are different and can return a negative response immediately. If you make it through both strings without finding any bytes that differ, you know the strings are the same and can return a positive result. This means that comparing two strings can take a different amount of time depending on how much of the strings match.
For example, a standard comparison of the strings "xyzabc" and "abcxyz" would immediately see that the first character is different and wouldn't bother to check the rest of the string. On the other hand, when the strings "aaaaaaaaaaB" and "aaaaaaaaaaZ" are compared, the comparison algorithm scans through the block of "a" before it determins the strings are unequal.
Suppose an attacker wants to break into an on-line system that rate limits authentication attempts to one attempt per second. Also suppose the attacker knows all of the parameters to the password hash (salt, hash type, etc), except for the hash and (obviously) the password. If the attacker can get a precisise measurement of how long it takes the on-line system to compare the hash of the real password with the hash of a password the attacker provides, he can use the timing attack to extract part of the hash and crack it using an offline attack, bypassing the system's rate limiting.
First, the attacker finds 256 strings whose hashes begin with every possible byte. He sends each string to the on-line system, recording the amount of time it takes the system to respond. The string that takes the longest will be the one whose hash's first byte matches the real hash's first byte. The attacker now knows the first byte, and can continue the attack in a similar manner on the second byte, then the third, and so on. Once the attacker knows enough of the hash, he can use his own hardware to crack it, without being rate limited by the system.
It might seem like it would be impossible to run a timing attack over a network. However, it has been done, and has been shown to be practical. That's why the code on this page compares strings in a way that takes the same amount of time no matter how much of the strings match.
Java 7 의 Swich 구문에서 문자열이 동작하는 원리.
이번에도 읽어 볼만한 글을 간단하게 번역해봤습니다.
http://javarevisited.blogspot.in/2014/05/how-string-in-switch-works-in-java-7.html
Java 7의 특징중에 하나로 Switch 구문에서 문자열을 사용할 수 있는데요.
이 원리에 대해서 설명한 내용입니다. :)
3줄 요약하면
1. Switch 에서 문자열을 사용했을 경우에 컴파일된 코드를 보면 hash code 와 equals() 를 사용해서 구분을 한것을 알 수 있다.
2. equals()를 사용해서 값을 확인을 한번 더 하기 때문에 숫자형이나 열거형을 사용했을경우 보다 연산이 +1 되고, 대소문자 문제 및 카멜표기법에 따른 문제 등이 발생한다.
3. 그러므로 문자열 보다는 숫자형이나 열거형을 사용하는 것이 좋고 열거형이 가독성과 코드 품질에 더 좋기 때문에 열거형을 사용하는 것이 좋다.
가볍게 읽어보세요
------------------------------------------------------------------------------------------------------------------------------------------------
Java의 switch 구문에서 문자열을 사용할 수 있게 됨으로써 숫자형이나 열거형을 쓰는 것 보다 더 사용하기가 쉬어서 많은 프로그래머들이 이를 사용한다. 이 기능은 자동 리소스 관리 및 다중 오류 catch blocks 를 포함해서 JDK 7의 기능중에 가장 유명한 기능중 하나다.
Java 프로그램에서 문자열을 사용함으로써 주는 한가지 편의점은 간단하게 사용할 수 있다는 것이다. 하지만 새로운 내용에 대한것을 실제 제품 코드에 적용하기전에 나는 이 내용에 대해 조금 더 알아보는것을 선호 한다. 내가 이 기능에 대해서 처음 알았을 때 equals() 와 hashcode()를 사용하면 switch에서 문자열 사용하기를 구현할 수 있을 거라는 생각이 들었다. 그래서 Java 7 에서 문자열이 어떻게 Switch 에서 사용할 수 있게 됬는지 관심을 가지게 됬다. 한가지 더 이슈는 Java 면접을 진행할 때 질문을 하기 위해서 였고, 이런 질문을 가지고 있는 면접이 좀 더 흥미롭게 진행 될 수 있다.(간단하지만 흥미로운 질문이긴 하네요)
테스트는 간단하다. 그냥 코드를 Switch 블럭에서 문자열로 작성하면 되고 이 코드들을 디컴파일 해서 컴파일러가 이 코드들을 어떻게 변환했는지 보면 된다.
컴파일되기 전 코드
아래 코드는 main 메서드에 switch 블럭을 가지고 있고 문자열을 포함한 간단한 테스트 코드다. String 변수가 프로그램이 동작할시 제공된다. 아래 테스트 코드는 3가지 모드(Activce, Passive, Safe)를 가지고 있다.
고정값을 사용할 경우에는 열거형을 사용하는게 더 좋지만 문자열을 사용하기로 결정했다면 소문자 및 카멜 표기법으로 변수를 표시했을때 문제가 발생할 수 있으므로 대문자로 작성 하도록 한다.
/**
* Java Program to demonstrate how string in switch functionality is implemented in * Java SE 7 release.
*/
public class StringInSwitchCase {
public static void main(String[] args) {
String mode = args[0];
switch (mode) {
case "ACTIVE":
System.out.println("Application is running on Active mode");
break;
case "PASSIVE":
System.out.println("Application is running on Passive mode");
break;
case "SAFE":
System.out.println("Application is running on Safe mode");
}
}
}
JDK 1.7을 설치해서 컴파일하고 코드를 실행해본다. JDK 7 어느 버전에서든 동작 한다.
디컴파일 된 코드
아래 코드는 jdk 1.7.0_40 버전에서 컴파일된 소스를 디컴파일 한 것이다. 만약 당신이 자바를 처음 접하고 자바 클래스파일을 디컴파일에 리버스 엔지니어링을 어떻게 하는지 알고 싶다면 이 게시글을 참조하라.
새로운 버전이 계속 나오면서 문섭들이 점점 더 편리해지고 쉬어 지기 때문(설탕 문법, syntactic sugar)에 이를 확실하게 알려면 모든 레벨의 자바 프로그래머들에게 클래스를 디컴파일하는 방법을 아는것은 매우 중요하다.
/** * Reverse Engineered code to show how String in Switch works in Java. */
import java.io.PrintStream;
public class StringInSwitchCase{
public StringInSwitchCase() { }
public static void main(string args[]) {
String mode = args[0];
String s;
switch ((s = mode).hashCode()) {
default: break;
case -74056953:
if (s.equals("PASSIVE")) {
System.out.println("Application is running on Passive mode");
}
break;
case 2537357:
if (s.equals("SAFE")) {
System.out.println("Application is running on Safe mode");
}
break;
case 1925346054:
if (s.equals("ACTIVE")) {
System.out.println("Application is running on Active mode");
}
break;
}
}
}
이 코드에서 문자열이 hashCode()와 equals() 메서드를 사용하는걸 볼 수 있다.
JAVA 7 이전에 swich 구문에서는 byte, short, car 및 int 만 사용가능했던걸 되세겨보자.
코드를 자세히 들여다 보면 swich는 해쉬 코드 와 eqauls()메서드를 통한 비교를 하고 있다. 이 검사는 꼭 필요한데 두개의 유니크한 오브젝트도 같은 hashcode 값을 가질 수 있다. 성능으로 따지면 열거형을 사용하는 것이나 숫자형을 사용하는 것 만큼 빠르지 않지만 모든 부분에서 아주 좋지 않은것은 아니다.
자바 컴파일러는 문자열을 비교할때 매우 빠르게 할 수 있는 equals()를 사용한다.("abc" == "abc"). hashcode()메서드를 사용하는 경우에는 코드가 이미 생성되어 있기 때문에 1번더 추가적인 시간이 소모된다. 문자열은 그들의 hash code를 캐시한다. 그래서 이 코드가 hash code를 호출하는 비용은 크지 않을 것이다.
그럼에도 불구하고 나는 여전히 Switch 에서 문자열을 사용하거나 고정된 숫자값을 사용하는것은 좋은 방법이 아니라고 본다. 자바는 이러한 이유때문에 열거형이라는 타입을 제공하고 모든 자바 프로그래머는 되도록 열거형을 사용하는 것이 좋다.
이게 Java 7의 Switch 구문에 문자열이 어떻게 동작하는지에 대한 것이다.
예상했던 대로 전환을 하기 위해 hashCode()를 사용하고 검증을 하기 위해서 equals()를 사용한다. 이것은 문법적으로는 심플 하지만 오히려 기본 성능보단 느리다.
결국 선택은 사용자의 몫이다. 나는 개인적으로 Switch 구문에서 문자열을 사용하는것(다루기 힘든 코드, 대소문자 문제, 값 검증을 위한 컴파일 시간 문제)에 대해 좋아하는 사람은 아니다.
사실 숫자 정수와 열거형 타입은 성능이 중요한 코드에서 내가 좋아 하는 방법이다. 가독성과 코드 품질이 더 중요하다.
실제로는 99.99%로 열거형을 쓰는게 문자열이나 숫자형을 사용하는 것보다 좋다.
이베이와 월마드테어 Neo4j를 도입. 그래프가 소매 시스템 구조를 바꾸고 있다.
eBay and Wallmart Adopt Neo4j : The Graph is Transforming Retail
오늘(3월 18일) 이베이와 월마트같은 소매 유통 업체에서 Neo4j를 활용한 미션 크리티컬한 비즈니스 애플리케이션을 개발하여 큰 성공을 거두었다고 Neo4의 개발사인 Neo Technology에서 발표 했다. 이베이는 전 세계에 위치한 고객들에게 자사의 인기있는 서비스인 당일 배송 서비스를 개선하기 위해 Neo4j를 사용한다. 월마트는 온라인 쇼핑객 구매 행동을 분석하여 최적화된 연관 상품들을 제공하기 위해 Neo4j를 사용한다.
이베이는 대략 75% 소비가 고객의 집에서 15마일이내에서 발생한다는 것을 알고 지역 상권을 재정의 하는 대담한 목표를 가지고 있었다. 전자 상거래는 빠르고 편리하지만 배송은 불편했다. 당일 배송 시스템의 출현으로 지역 상업의 모든 행위가 마비될 것으로 보인다. 온라인 쇼핑과 오프라인 매장의 소비자 경험간의 융합은 소매 업체와 고객사이의 원할한 관계를 만들어 상거래 혁명을 일으키고 있다. 유연하고 빠르고 사용하기 쉽도록 하기 위해 Neo4j는 소매상과 온라인 고객사이의 가장 큰 방해물을 제거할 수 있도록 했다.
이베이의 폭발적인 성장과 새로운 기능들로 인해 가능한 가장 빠르고 확실한 배송을 위해 이베이의 서비스 플랫폼을 새롭게 구성할 필요가 있었다. 이전 시스템인 MySQL은 너무 느리고 계속 유지하기엔 복잡하고 배송을 위한 최적의 경로를 찾는 쿼리의 수행시간이 너무 길었다. 이베이의 개발팀은 그래프데이터베이스가 성능과 확장에 대한 문제점을 해결하기 위해 기존의SOA 및 서비스 구조에 추가될 수 있다는 것을 알았다. 이 팀은 그래프데이터베이스 시장의 최적의 해결책인 Neo4j를 사용했다.
우린 말그대로 이전 MySQL보다 천배이상 빠르고 10~100번정도 적은 코드로 수행되는 Neo4j를 사용했다. 오늘날 Neo4j는 이전의 이베이에서는 불가능했던 기능들을 지원한다.
- Volker Pacher, Seniro Developer at eBay.
월마트 같은 대형 유통업체도 Neo4j를 사용한다. 과거에 소비자들은 마케팅 형태로 제조업체에서 제공하는 정보에 크게 의존했었다. 요즘은 소비자들이 생성한 제품에 관한 리뷰와 구매에 대해서 소셜 미디어에 상담하는 등 다른 정보를 가지고 있다. 소비자간의 정보의 풍요로움과 전문가의 의견에 따른 전에 없던 가용성은 소비자들을 좀 더 다양한 제품에 대한 경험을 가질수 있게 하고 그들이 사용하거나 소유하려고 고유하는 경향에 대하여 결정할 수 있게 해준다. 이렇게 서로 연결된 모든 데이터들을 이해하기 위하여 월마트는 Neo4j 그래프 데이터베이스를 사용한다.
"Neo4j는 우리가 이해하려고 했던 온라인 고객들의 성향과 고객 및 상품간의 관계에 대하여 실시간 상품 추천을 위한 완벽한 기능을 제공한다. 그래프 데이터베이스 시장의 선두주자이고 확장성과 가용성에 대한 고급 기능들을 제공하는 Neo4j는 우리들의 요구사항을 충족시키는 완벽한 제품이다."
- Marcos Wada, Software Developer at Walmart
그래프는 어디든지..그것은 바로 Neo4j
그래프는 산업 전반에 걸친 다루기 힘든 기술적인 문제에 대한 해결 방안으로 공공연하게 인정받고 있다. DB-Engines에 따르면 Neo4j는 가장 인기있는 그래프데이터베이스다.
firm에 따르면 "데이터베이스 관리 시스템중에 작년에 비해 자신의 인기가 가장 증가했는가 볼때 250% 증가한 그래프 데이터베이스는 명백한 승자라고 볼 수 있다."
이베이의 케이스 스터디는 http://www.neotechnology.com/ebay 을 참조.
그래프 데이터베이스란 무엇인가?
그래프 데이터베이스와 Neo4j
그래프 데이터 베이스와 Neo4j에 대해서는 아래의 그림 한장으로 설명 될 수 있습니다.
차분히 읽어 보면 개념 자체는 어렵지 않습니다. ㅎㅎ
아래 동영상은 Neo4j의 공동 설립자인 Emil Eifrem이 설명하는 그래프 데이터베이스입니다.
그럼 본론으로 들어가서..
아래 내용들은 Neo4j.org 에 있는 글들을 번역한 것입니다.
되게 어렵게 설명해서 발번역이 됬네요. ㅋ
직접들어가서 한번 보는것도 추천 드립니다.
그래프 데이터베이스란 무엇인가?
그래프 데이터베이스는 데이터를 그래프에 저장하고 데이터의 구조가 가장 일반적이고, 우아하고 높은 접근 방식으로 모든 데이터를 표현할 수 있다.
그래프에 대해 설명하는 몇가지 그래프들을 보도록 하자. 다이어그램 주위의 화살표를 따라 읽으면 문장을 형성해서 그래프를 읽을 수 있다.
그래프는 Nodes 와 Relationships 을 가지고 있다.
A Graph-[:RECORDS_DATA_IN]->Nodes-[:WHICH_HAVE]->Properties.
가장 심플한 그래프는 한개의 노드로 표현할 수 있다. 프로퍼티라는 값으로 정의 될 수 있다.
약간 평범하지 않은 이야기이긴 하지만 노드는 한개의 속성을 가지고 시작해서 몇 백만개까지 증가할 수 있다.
이렇게 하는 것 보다는 명시적인 관계로 조직된 다수의 노드로 나누는게 좋다.
위 그래프를 화살표를 따라 읽으면
A Graph는 Nodes 와 Relationships으로 기록 되고, 각각 Node들은 Relationship으로 조직될 수 있다.
또 Nodes와 Relationships 은 각각 별도로 속성을 가질 수도 있다.
순회를 통해 그래프에 조회 하기
A Traversal - navigates -> a Graph; it - identifies -> Paths - which order -> Nodes.
순회는 그래프에 어떻게 질문을 하는가 라고 말할 수 있다.
알고리즘에 따라 시작 노드 부터 관계가 있는 노드까지 탐색하고, 아래와 같은 질문에 대한 답을 찾을 수 있다.
"내 친구가 좋아 하는 음악중에 내가 가지고 있지 않은것은 무엇인가?"
"만약 이 파워 서플라이가 다운되면 웹 서비스에 끼치는 영향은 무엇인가?"
노드와 관계에 대한 인덱스.
An Index - maps from -> Properties - to either -> Nodes or Relationships. It - is a special -> Traversal
가끔 특정 노드나 관계에 저장된 속성들에 대해 찾아야할 때가 있다.
"Account 중에서 사용자 이름이 '그래프 마스터'인 것에 대해 찾아라" 같은 순회 방식은 가장 일반적인 방식으로 인덱스 룩업에 최적화 되어 있다.
Neo4J란 무엇인가?
Neo4j는그래프 데이터베이스로 신뢰할 수 있고 끈끈하게 연결된 데이터들에 대해 빠르게 관리 및 조회할 수 있다.
Neo4j 는 오픈소스 그래프데이터베이스로 Neo Technology에서 제공한다.
Neo4j 속성들과 함께 다양한 관계들과 연결된 노드들을 직접 저장한다.
주요 특징
직관적 : 데이터를 표현하기 위해 그래프 모델을 사용
믿을수 있는 : FULL ACID 트랜잭션을 제공
빠르고 안전한 : 커스텀 디스크와 네이티브 스토리지 엔진
대규모 확장 : 수십억개의 노드/관계/속성
고 가용성 : 다양한 머신에 분산
사람이 읽기 편한 그래프
쿼리
빠른 속도의 그래프 쿼리를 통산 빠른 검색 속도
몇개의 작은 jar 파일들로 서버 세팅 가능
간편 함 : REST interface를 통한 API 제공 및 OOP한 Java API 제공