Window > Preferences > Validation

 

에서 불필요 Validator 체크 해제할 것!

 

반응형

'개발이야기 > Tip&Tech' 카테고리의 다른 글

Git 관리 관련 포스팅 정리.  (0) 2017.03.08
안드로이드 스튜디오 ADB 위치 확인  (0) 2017.01.19
eclipse 톰캣 힙 늘리기  (0) 2017.01.19
eclipse 퀵 서치 설치하기  (0) 2017.01.19
Posted by N'

1. install New Software -> 
     
 Work with : http://dist.springsource.com/release/TOOLS/update/e4.3/

 

2. Core/ Eclipse Integration Commons 밑에  Eclipse Quicksearch 선택

 

 

출처는 이 곳입니다.

 

 

http://egloos.zum.com/chaehun467/v/6065038

반응형
Posted by N'

지난 포스트에 이어서, 계속 Stream API에 대한 포스팅을 하고 있습니다.


지난 포스팅에서는 각 컨테이너를 매핑하여, 원하는 형태의 객체로 출력을 할 수 있는 방법을 소개했었습니다. 

마치 SQL 처럼 특정 필드만을 추출한다던가, 두 개 이상의 컨테이너를 JOIN 할 수도 있었죠.


해당 포스팅은 이 곳에서 확인!! 



오늘은 특정 속성이 데이터 집합에 있는지, 있다면 해당 객체를 추출할 수 있는 다양한 유틸 메소드를 살펴볼까 합니다.


예를 들어, 우리는 이러한 작업을 많이 하곤 합니다.


아래 로은 특정 숫자 컬렉션에서 5의 배수가 있는지 확인하는 로직입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Integer> sampleList = Arrays.asList(5,10,12,38);
        
Boolean isHave = false;
for (Integer number : sampleList) {
    if (number % 5 == 0) {        
        // 5의 배수인 데이터가 있는지 확인
        isHave = true;
        break;
    }
}
        
System.out.println("5의 배수가 존재  : " + isHave);
 
// 출력 : 5의 배수가 존재  : true
cs


루프를 돌면서, 출력할 컬렉션 객체에 if 조건을 사용하여 여부를 업데이트하려 하고 있습니다.


해당 로직은 다음과 같이 수정될 수 있습니다.



1. Predicate 가 적어도 한 요소와 일치하는지 확인 (anyMatch)


anyMatch 유틸 메소드로 다음과 같은 로직을 구할 수 있습니다.


1
2
3
4
5
List<Integer> sampleList = Arrays.asList(5,10,12,38);
        
System.out.println("5의 배수가 존재  : " + sampleList.stream().anyMatch(n -> n % 5 == 0));
 
// 출력 : 5의 배수가 존재  : true
cs


단순히 predicate 형태로 컬렉션 제네릭 객체의 조건만 명시해주면 조금 더 간소화된 코드를 구현할 수 있습니다.


2. Predicate 를 이용한 기타 확인 (allMatch, noneMatch)


Predicate 를 이용하여 컬렉션의 모든 요소가 일치하는지를 확인하려면, allMatch 메소드를 사용할 수 있습니다. 또한 반대로 일치하는 요소가 없는지를 확인하는 기능 역시도 noneMatch 메소드를 사용함으로써 간단하게 구현할 수 있습니다.


1
2
3
4
5
6
7
8
9
List<Integer> sampleList = Arrays.asList(5,10,15,40);
        
System.out.println("모든 요소가 5의 배수인가? : " + sampleList.stream().allMatch(n -> n%5 == 0));
System.out.println("5의 배수는 없는가? : " + sampleList.stream().noneMatch(n -> n % 5 == 0));
 
// 출력
// 모든 요소가 5의 배수인가? : true
// 5의 배수는 없는가? : false
 
cs


3. 요소 검색


Stream API 를 사용하면 쉽게 한 요소에 접근할 수 있습니다. 일련의 중간 연산(filter, distinct 등)을 수행한 후 findAny, findFirst 등의 최종연산 메소드로 조건에 맞는 한 요소를 선택할 수 있습니다. 예제를 한번 볼까요? 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Integer> sampleList = Arrays.asList(17,19,28,37,16);
        
sampleList.
    stream().
    filter(n -> n % 2 == 0).
    findAny().
    ifPresent(n -> System.out.println("2의 배소의 요소 찾기 : " + n));
        
sampleList.
    stream().
    filter(n -> n % 2 == 0).
    findFirst().
    ifPresent(n -> System.out.println("2의 배소의 첫번째 요소 찾기 : " + n));
 
// 출력
// 2의 배소의 요소 찾기 : 28
// 2의 배소의 첫번째 요소 찾기 : 28
cs


두 메소드의 내용을 살펴보면, 동일함을 알 수 있습니다. 왜냐하면, 쇼트서킷 검사(보통 Boolean 연산 특정 조건에 의해 검사결과가 정해지면 추 후 연산은 안하는 행위)를 통해 결과를 찾은 뒤 즉시 실행을 하기 때문이죠.


그렇다면 왜 굳이 메소드를 두 개로 둔 이유가 무엇일까요?


그 이유는 공짜로 얻을 수 있는 병렬성 이 후 후처리 때문입니다. Collection 클래스에서 Stream 추출 시, parallelStream 을 통해 병렬 처리를 쉽게 수행할 수 있습니다. 그러나 병렬처리를 수행 시 컬렉션의 순서가 보장이 되지 않습니다! ㅡㅡ^


즉 병렬처리를 통해 데이터 연산을 수행하였다면, 명시적으로 첫 번째 요소를 찾기 위해 findFirst 를 사용하여야만 합니다! 


아래 예제를 통해 결과를 확인해볼까요? 아래 예제는 병렬처리를 했음에도 findFirst 사용 시, 첫 번째 요소찾기를 보장하고 있음을 보여주고 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Integer> sampleList = Arrays.asList(17,19,28,37,16);
        
sampleList.
    parallelStream().
    filter(n -> n % 2 == 0).
    findAny().
    ifPresent(n -> System.out.println("2의 배소의 요소 찾기 : " + n));
        
sampleList.
    parallelStream().
    filter(n -> n % 2 == 0).
    findFirst().
    ifPresent(n -> System.out.println("2의 배소의 첫번째 요소 찾기 : " + n));
 
// 출력
// 2의 배소의 요소 찾기 : 16
// 2의 배소의 첫번째 요소 찾기 : 28

cs



마지막으로 생각해봐야할 부분은 요소 검사 메소드 활용 시, 결과가 항상 있다는 것을 보장하지 않는다는 점입니다. 보통 이럴 때 기존 자바에서는 null 로써, 데이터를 처리했지만 JAVA8 부터 많은 함수형 프로그래밍에서 볼 수 있는 Optional 의 개념이 생겼습니다. 


Optional값의 존재나 부재 여부를 표현하는 컨테이너 클래스로, 예제에서 사용한 ifPresent 는 값이 존재할 때만 실행을 명령하는 메소드 입니다. (Optional 은 추 후 따로 포스팅을 한 후 연결을 시켜두겠습니다. ㅡㅡ^)



자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기



반응형
Posted by N'

현재 개발하고 있는 앱의 메모리 누수가 감지되고 있습니다. ㅡㅡ^ 


메모리 누수 감지는 


https://github.com/square/leakcanary


를 통해서 프로파일링을 하고 있고, 대부분 문제가 스레드가 활동하는 동안 Context 가 살아남아서 생기는 문제가 있습니다.


뭐 자바의 패러다임으로 볼 때, 메모리는 VM 이 관리해주기 때문에 딱히 개발자가 건들 필요는 없어보이지만, 안드로이드 메모리가 제한적이고 각 객체가 더이상 쓰이지 않을 때 바로바로 해제를 해주어야 한다는 측면에서 적절한 작업이 이뤄줘야하는 것으로 보입니다. 특히 액티비티 인스턴스는 용량이 크기 때문에 잘 해제가 되야 합니다. ㅡㅡ^, 


해당 문제를 해결하기 위해, 아래 포스트처럼 WeakReference 를 통해 해결을 하려 하고 있으며, 이를 적용하기 위한 일괄된 패턴이 필요했습니다.



그래서 이번에 시도해보고 있는 것은 

잔디 블로그 (http://tosslab.github.io/android/2015/03/01/01.Android-mvc-mvvm-mvp.html

에서 소개한 MVP 패턴 사례를 참고하여, 메모리 누수를 막아보려고 하고 있습니다.


제가 하려고 했던 아래 코드입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SplashActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
 
        Handler hd = new Handler();
        hd.postDelayed(() -> {
            // 심플하지만, 이 곳 핸들러의 역할 때문에 메모리 누수가 존재
            setResult(RESULT_OK);
            finish();
 
            overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out);
        }, 1000);
    }
}
cs



핸들러 내부에서 Context 가 살아남아 있으며, 실제 프로파일러에서는 Handler 에서 메모리 릭이 감지되었다고 나오고 있습니다.


이러한 이유로, 해야할 일을 나누어 봅시다.


WeakReference 관리는 Presenter 가 담당합니다. 아래와 같이 말이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SplashPresenter {
    private Handler hd;
    private WeakReference<SplashActivity> splashActivityWeakReference;
 
    public SplashPresenter(SplashActivity splashActivity) {
        splashActivityWeakReference = new WeakReference<>(splashActivity);
        hd = new Handler();
    }
 
    /**
     * 1초 후에 해당 액티비트를 종료한다.
     */
    public void delayFinished() {
        hd.postDelayed(() -> {
            SplashActivity splashActivity = splashActivityWeakReference.get();
 
            if (splashActivity != null) {
                splashActivity.smoothFinishActivity();
            }
 
        }, 1000);
    }
}
cs


잔디 블로그의 경우에는 Presenter 를 인터페이스화 시킨 후 구현화하였지만,


- 저의 경우는 Activity 와 Presenter 가 1:1 관계이고, 

- Activity 에서 View 관련 업데이트를 해야할 종류가 많을 경우 interface 에 그만큼 메소드를 정의해야하기 때문에 불편하다는 생각이 있었으므로, 


Activity 와 Presenter 를 단순 has-a 관계로 처리하고자 하였습니다.


그리하여, Activity 구현 부는 다음과 같이 View 만 업데이트하는 구조를 취하게 되었습니다. Activity 자체가 WeakReference 관리를 할 필요는 없는것이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class SplashActivity extends Activity {
    private SplashPresenter splashPresenter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
 
        splashPresenter = new SplashPresenter(this);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        splashPresenter.delayFinished();
    }
 
    /**
     * Activity 종료
     * <p>
     * <pre>
     *     Activity 를 페이드아웃 애니메이션을 사용하며 종료.
     * </pre>
     */
    public void smoothFinishActivity() {
        setResult(RESULT_OK);
        finish();
        overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out);
    }
}
cs


다음과 같은 구조를 통해, View 를 담당하는 Activity 클래스는 WeakReference 를 고려할 필요가 없게 되었으며, Activity instance 는 GC Time에 메모리 회수가 가능하게 되었습니다.



반응형
Posted by N'

Rx의 도입과 액티비티를 옵저버로 관리하게 되니, 메모리 누수라는 골치아픈게 발생하고 있습니다.


WeakReference 클래스를 사용하면 된다고 나오지만, 정확히 이해가 가지 않아, 테스트 코드를 작성해보았습니다.


일단, WeakReference 를 사용하지 않을 경우.

- 확인해보면 GC 가 강제 실행되었지만, A 내부 B 객체가 살아있음을 알 수 있습니다. (컬렉션이 물고 있기 때문이죠.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Test {
 
    public static class A {
        private B b = new B();
 
        public B getB() {
            return b;
        }
 
        public void setB(B b) {
            this.b = b;
        }
 
        public static final class B {
            public String text = "아직 살아있다.";
        }
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stu
        A a = new A();
        HashSet<A.B> abc = new HashSet<>();
 
        abc.add(a.getB());
        a = null;
        System.gc(); // GC 강제 발생
 
        for (A.B b : abc) {
            System.out.println(b.text);
        }
    }
}
 
// 아직 살아있다.
 
cs


WeakReference 로 참조를 유지할 경우.

- GC 가 돌며, A 의 인스턴스가 삭제되었고, 참조하고 있던 B의 인스턴스도 삭제가 되었음을 알 수 있습니다. (안드로이드에서도 이 힌트를 가지고, 메모리 릭을 방지할 수 있을 것이라 생각합니다. ㅡㅡ^)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
 
    public static class A {
        private B b = new B();
 
        public B getB() {
            return b;
        }
 
        public void setB(B b) {
            this.b = b;
        }
 
        public static final class B {
            public String text = "아직 살아있다.";
        }
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stu
        A a = new A();
        HashSet<WeakReference<A.B>> abc = new HashSet<>();
 
        abc.add(new WeakReference<>(a.getB()));
 
        a = null;
        System.gc();
 
        for (WeakReference<A.B> b : abc) {
            System.out.println(b.get().text);
        }
    }
}
 
// Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:31)
 

cs


반응형
Posted by N'

안드로이드에서 메모리 릭이 발생하고 있는지 아닌지를 다음 코드를 통해 확인합시다.


기종 따라 다르지만, instanceCount 가 무한대로 증가한다면, 문제가 있는 겁니다. ㅡㅡ^


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestInstanceActivity extends Activity {  
           public static int instanceCount = 0;
           public TestInstanceActivity () {
                     super();
                     instanceCount++;
                     Log.d("test""TestInstanceActivity() instanceCount: " + instanceCount);
           }
 
 
           @Override
           protected void finalize() throws Throwable {
                     super.finalize();
                     instanceCount--;
                     Log.d("test""finalize() instanceCount: " + instanceCount);
           }
}
cs


반응형
Posted by N'

안드로이드 N이 등장하면서, 클라이언트의 배터리를 아껴주기 위하여, DOZE 모드가 등장하였습니다.


이 때, 모든 네트워크 작업이 중단 되기 때문에 반드시 이에 대한 처리를 해주어야합니다.

특히 푸시 서비스를 제공하는 앱을 개발하기 위해서는 FCM(이전 GCM 등)을 사용하여, 앱을 깨우는 작업이 필요합니다. (FCM은 유일하게 DOZE 모드 중, 네트워크 수신이 가능합니다.)


그러하기 때문에 테스트를 위해서 DOZE 모드 상태를 강제적으로 만들필요가 있는데, 아래와 같은 명령어를 ADB에서 사용하면 됩니다. (STEP 이 IDLE 상태로 변할 때까지 명령어를 난타합시다. ㅡㅡ^)



- adb shell dumpsys battery unplug (배터리 플러그 빼기)

- adb shell dumpsys deviceidle step (idle 모드 만들기)




반응형
Posted by N'

안녕하세요. 블로그 주인 "초보프로그래머" 입니다.


드디어 일상이란 카테고리에 첫 번째 게시물을 작성해보네요.

 

하지만 일상이라고 해서, 갑자기 큰 주제에서 벗어나는 것 보다는 그래도 개발 블로그니, 개발과 관련된 생각과 일상을 적어보려고 해요. ㅋㅋㅋㅋ


그 중, 첫번째 주제로 잡은 것은 UML을 그려보자라는 것이었습니다.


사실 문서화라는 것이 정말 귀찮은 작업이고, 작성했다 하더라도 누가 찾아볼까라는 생각도 되서 등한시 하고 있었는데, 이번에 인수인계 받은 로직을 리팩토링하며 UML을 그려보는 것이 어떨까에 대한 생각이 들었습니다.


갑자기 그 생각이 든 것은, 어찌보면 간단한 이유입니다.


객체지향적으로 프로그래밍한다? 디자인 패턴을 잘 활용한다? 분명 이 생각을 가지고 코딩을 수행하다보면, 분명 클래스의 개수가 기하급수적으로 늘어납니다. ㅡㅡ^ (리팩토링 전 모든 로직들이 Spring 프레임워크 기반의 서비스 클래스에 정의되어 있지만, 리팩토링 후 클래스들이 많이 생겼습니다. 즉 봐야할 클래스가 많아진 것이죠.)


짜임새있게 만들어진 구조라면 "변화에 유연한 개발자가 되자" 라는 것에 부합할 수 있겠지만 아무것도 모르는 사람이 보면 그냥 복잡하게 짠 코드로만 보일 것입니다. 


아래는 트리와 비슷한 개념을 코드로 표현하기 위해서, 컴포짓패턴을 사용한 것인데 모르는 사람이 보면 그냥 난감합니다. 하지만 어떤 트리의 개념이 추가된다면 AbstractApprovalDepth 클래스를 상속받은 구현클래스를 작성하여 위치에 맞게 child를 명시한 곳에 추가하면 됩니다. 모르면 그냥 모르죠.....



무언가 모순이지 않나요? 

유지보수 좋으라고 여러 패턴 도입하며, 개발을 했는데 남이 보면 복잡합니다. ㅡㅡ^


그래서 UML을 문서화하며 모든 부모클래스에 해당 UML을 언급하면 그나마 보기 좋지 않을까에 대해 고민해보게 되었습니다. 


원래 인스턴스를 만드는 것은 클래스 구조를 보았을 때, 가장 하위클래스를 사용하는 것이 적절하기 때문에 모든 비지니스 클래스에 UML을 넣는 것은 조금 그렇고(귀찮아질듯?), 어느정도 틀이 되는 인터페이스나 추상클래스에만 UML을 참고하게 하여 처음보는 사람이 부모클래스를 하나라도 타고 가면 UML을 볼 수 있게하자 라는 전략으로 해보려고 했습니다.


그래서 프로젝트 하나 몰래 만들고, ㅋㅋㅋ (아직 선배님들은 아무도 모르는게 함정..)



말머리는 다음과 같이 해보았습니다. 프로젝트의 취지입니다. 



첫 번째 이슈 작성!



이슈에는 목적을 명시하고 UML 올렸습니다. UML만 올리면 살짝 부족한 느낌이 있더군요. 그래서 살짝 코멘트도 같이 올렸습니다. (DB의 ERD처럼 관리가 잘되었으면 좋겠습니다. ㅜㅡㅜ)


UML을 명시했으니, 코드와 연결을 해야겠죠...


상위 클래스에 가서,




이렇게 연결을 해주었습니다. 문서화가 과연 도움이 될까요?



반응형

'개발이야기 > 기타 개발' 카테고리의 다른 글

블로그 커뮤니티 서비스 "Post Paper"  (0) 2015.02.07
Posted by N'

지난 포스트에 이어서, 계속 Stream API에 대한 포스팅을 하고 있습니다. 


Stream API를 활용하여, 컨테이너(Collection)에서 복잡하고 다양한 조건을 질의형태로 구현할 수 있다는 것을 배웠습니다. 방어코드 없이 말이죠!! 


지난 포스팅은 이곳에서 확인! 



이러한 질의와 같은 방법은 SQL과 비슷해서 우리가 원하는 결과를 깔끔하게 구현할 수 있는데요. 오늘은 검색과 더불어 특정 데이터를 선택하는 작업인 매핑에 대해 알아보려 합니다.


계속해서 알기 쉬운 SQL에 비교하여 알아보겠습니다.


우리는 DBMS에 질의를 할 때, 언제나 테이블의 ROW 데이터 모두를 원하지 않습니다. 질의어 자체에서 출력 결과를 조정할 수 있으며, 선택을 할 수 있습니다. 예를들어 "구성원이라는 테이블에, 이름만 출력" 하고자 한다면 이렇게 사용을 하죠.


1
2
3
4
5
6
SELECT
    member.name
FROM
    member as member
WHERE
    member.age = 40
cs


JAVA8에서는 map 메소드를 사용하여 특정 필드의 결과만을 가지는 Collection을 생성할 수 있습니다. 함수형 인터페이스로 친다면 Function 의 성격을 가지고 있습니다. (T를 받아 R을 출력하고 있으니 말이죠 ㅡㅡ^) 


함수형 인터페이스의 성격이 무슨 말인지 모르신다면 해당 포스트를 참고하세요.



다시 돌아와서, map의 활용을 보도록하겠습니다.


일단 테스트VO 클래스에 대한 명시입니다.


1
2
3
4
5
6
public class MemberVo {
    private Integer sn;
    private Integer age;
    private String name;
}
 
cs


위 VO는 구성원을 가르키며, 고유넘버, 나이, 이름 정도의 멤버 변수를 가지고 있습니다. 

상단의 언급한 SQL 쿼리의 로직을 수행해보도록 하죠!


Map의 활용


1
2
3
4
5
6
7
8
9
10
List<MemberVo> memberList = Arrays.asList(
                new MemberVo(127"남두현"), new MemberVo(220"유형주"),
                new MemberVo(320"태재영"), new MemberVo(440"남궁계홍"));
        
memberList.stream()
    .filter(a -> a.getAge().intValue() == 40)
    .map(MemberVo::getName)
    .forEach(System.out::println);
 
console value : 남궁계홍
cs


지난번에 배웠던 검색 메소드인 filter를 이용하여 나이에 대한 조건을 언급하였고, map 메소드를 이용하여 이름이라는 데이터를 출력하였습니다.


보다 다양한 결과를 출력하고 싶다면, map 메소드의 파라미터안에 적절한 람다 혹은 메소드 레퍼런스를 언급해주면 됩니다.


JAVA 8 IN ACTION 에서는 이러한 map 과 더불어, flatMap을 같이 소개했습니다. flatMap의 기능은 스트림의 평면화로, 두 개의 컨테이너 객체의 스트림을 하나로 묶는 것을 말합니다. (쉽게 말해서 SQL의 JOIN과 같다고 볼 수 있습니다. ㅡㅡ^)


flatMap을 이용하여 두 테이블을 합친 로직을 구현해볼까요?


FlatMap의 활용


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
List<MemberVo> memberList = Arrays.asList(new MemberVo(127"남두현"), new MemberVo(220"유형주"),
                new MemberVo(320"태재영"), new MemberVo(440"남궁계홍"));
List<BoardDetailVo> boardList = Arrays.asList(new BoardDetailVo(1"람다게시판입니다.""람다에 관련된 클만 올려주세요."),
                new BoardDetailVo(1"람다란 무엇이니가?""함수형 프로그래밍의 꽃?"),
                new BoardDetailVo(2"물어볼 게 있습니다.""람다공부는 어떻게 하죠?"));
 
memberList.stream().flatMap(i ->
        boardList.stream()
        .filter(j -> j.getSn() == i.getSn().intValue())
        .map(j -> {
                MemberBoardResultVo result = new MemberBoardResultVo();
                result.setSn(i.getSn());
                result.setName(i.getName());
                result.setContents(j.getContens());
                result.setTitle(j.getTitle());
                return result;
        })).forEach(ret -> {
                System.out.println(String.format("[%s]%s:%s"
                    ret.getName(), 
                    ret.getTitle(), 
                    ret.getContents()));
            }
        );
 
- console result -
 
[남두현]람다게시판입니다.:람다에 관련된 클만 올려주세요.
[남두현]람다란 무엇이니가?:함수형 프로그래밍의 꽃?
[유형주]물어볼 게 있습니다.:람다공부는 어떻게 하죠?
 
cs


일단 chain된 Stream 이 살짝 복잡해 보이네요. 파이프 하나씩 한번 살펴보죠!


flatMap 내부부터 한번 살펴보면,


1. memberList의stream이 주가 되어, flatMap 내부에서 boardList의 Stream을 사용하고 있습니다.


1
memberList.stream().flatMap(i -> boardList.stream()....)
cs



2. boardList의 stream 내부 파이프라인 중 filter는 memberList 의 각 객체 중, 본인 객체의 sn이 서로 같은 것만 찾으라는 조건을 명시하고 있습니다.


1
filter(j -> j.getSn() == i.getSn().intValue())
cs


3. boardList stream의 내부 map에서는 Function 의 역할로 MemberBoardResultVo 를 출력하도록 되어 있습니다. 람다표현식을 사용하여, MemberVo와 BoardDetailVo 에서 각각 필요한 정보를 주입합니다.


1
2
3
4
5
6
7
8
map(j -> {
                MemberBoardResultVo result = new MemberBoardResultVo();
                result.setSn(i.getSn());
                result.setName(i.getName());
                result.setContents(j.getContens());
                result.setTitle(j.getTitle());
                return result;
        })
cs


4. foreach에서는 위의 flatMap으로부터 출력된 컨테이너를 순회하며, 필요한 정보를 출력하고 있습니다.


1
2
3
4
5
6
7
forEach(ret -> {
                System.out.println(String.format("[%s]%s:%s"
                    ret.getName(), 
                    ret.getTitle(), 
                    ret.getContents()));
            }
        );
cs


람다식이 조금 복잡해보이지만, 사실 기존 java8 이전의 구현 방식대로 진행한다고 하면, 2중 for-loop을 통해 보다 복잡한 로직이 될 수 있습니다. 위의 예제는 비교적 간단해보이지만, 컬렉션 3~4개를 합친다거나, 합쳐진 컬렉션에서 limit, skip, order by 등의 추가사항이 붙는다면 기존 구현된 소스를 보고 고민에 빠지는 시간이 조금 길어질것입니다.


하지만 람다식을 통해 다음과 같이 chain 메소드 한개만 출력해주면 간단히 해결이 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
memberList.stream().flatMap(i ->
            boardList.stream()
            .filter(j -> j.getSn() == i.getSn().intValue())
            .map(j -> {
                    MemberBoardResultVo result = new MemberBoardResultVo();
                    result.setSn(i.getSn());
                    result.setName(i.getName());
                    result.setContents(j.getContens());
                    result.setTitle(j.getTitle());
                    result.setAge(i.getAge());
                    return result;
        }))
        .sorted((a, b)-> a.getAge().compareTo(b.getAge()))
        .limit(1)
        .forEach(ret -> {
                System.out.println(String.format("[%s]%s:%s"
                        ret.getName(), 
                        ret.getTitle(), 
                        ret.getContents()));
            }
        );
 
- console result -
[유형주]물어볼 게 있습니다.:람다공부는 어떻게 하죠?
cs


단순히, 두 객체를 머징하는 내부 람다에 age를 추가 하였고, sorted와 limit를 사용하였습니다.


즉 우리는 이번 포스팅을 통해 알 수 있었던 것은 컨테이너를 사용하는 로직에서 


단순히 체인형 stream을 추가하는 방법만으로 변화에 보다 쉽고 유연하게 대처


할 수 있음을 알게 되었습니다.



자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기



반응형
Posted by N'

안녕하세요. 블로그 주인인 초보프로그래머입니당.


오랜만의 블로그 포스팅입니다.  개인적으로 바쁜 일이 있어서(곧 회사내에서 출시를 하기 때문에 버그를 열심히 잡았죠... ㅡㅡ^), 오랜만에 공부를 해보고 내용을 정리하게 되었습니다.


솔직히 블로그할 시간은 있긴 했지만, 게을러지다보니 공부하게 되는 것이 쉽지가 않네요.


하지만 책을 산 것이 아깝고, 곧 진행했던 안드로이드 프로젝트에도 함수형 프로그래밍의 결실을 시도해봐야하니(RxJava or Android N) 다시 한번 붙잡게 되었습니다.


그런 의미에서 오늘은 계속 진행하던 Stream API 의 활용에 대해 보고자 합니다. 

Stream API에 대해 처음보게 된다면 이 곳을 먼저 확인해주세요.



이전에 Stream API를 소개할 때, 컨테이너(Collection)의 활용을 질의 형태로 작성할 수 있다고 언급했었는데요. 마치 고수준 언어인 SQL을 사용하듯 말이죠. (Collection 객체를 테이블이라고 본다면, 질의를 통해 어떤 결과를 얻는다 생각하면 좋겠네요? ㅡㅡ^)


오늘은 그 중 필터링과 슬라이싱(즉 검색.. ㅡㅡ^)을 해보려 합니다.


1. 필터링

SQL의 where 절과 같은 역할입니다. Collection 객체 중 프리디케이트(Predicate)에 부합하는 객체들을 따로 추출하는 역할을 합니다. 그 메소드는 이전부터 많은 예제로 사용되었던 filter 입니다. 


몸풀기로 시작해보겠습니다.


1
2
3
4
5
6
7
8
9
List<Integer> numberList = Arrays.asList(22345688910);
 
for (Integer a : numberList.stream().filter(a -> a % 2 == 0).
                collect(Collectors.toList())) {
    System.out.println(a);
}
 
console result : [2,2,4,6,8,8,10]
 
cs


filter 메소드를 이용하여, 짝수만을 조회하였습니다. SQL로 치자면, 이정도??


1
SELECT number FROM NUMBERLIST WHERE number % 2 = 0;

cs


2. 고유 요소 필터링

위의 결과 중, 중복된 결과가 있습니다. 원하는 결과가 중복된 값이 없기를 바란다면 distinct 메소드를 사용해주면 됩니다.


1
2
3
4
5
for (Integer a : numberList.stream().filter(a -> a % 2 == 0).
                distinct().collect(Collectors.toList())) {
    System.out.println(a);
}
 
cs


생각해보면 SQL에서 DISTINCT 를 사용하여, 고유 결과를 출력할 수 있다는 것을 알고 있습니다.


1
SELECT DISTINCT number FROM NUMBERLIST WHERE number % 2 = 0;
cs


3. 결과값 제한과 스킵

보통 게시판을 만들 때, limit offset 등을 사용하여 페이징 처리를 하곤 합니다. 왜냐하면 많은 데이터를 한번에 다 보여줄 수도 없거니와, 사용도 불편하기 때문이죠.


Stream API에서는 limit offset 과 같은 역할을 해줄 수 있는 메소드 역시 가지고 있습니다.


일단 Stream을 잘 살펴보면, limit 라는 메소드를 가지고 있습니다. 아래 코드는 Stream의 결과 중, 3개를 반환하는 메소드입니다.


1
2
3
numberList.stream().filter(a -> a % 2 == 0).distinct().limit(3).forEach(System.out::println);
 
console result : [2,4,6]
cs


재미있는 점은 limit를 사용하지 않은 결과보다, limit의 파라미터 값이 크더라도 에러를 출력하지 않습니다. 이 점은 우리가 최대 몇개를 가져온다라는 비지니스 로직을 구현할 때, 방어코드를 할 필요가 없어졌음을 말합니다.


마지막으로 skip을 이용하여, 요소를 건너뛸 수 있습니다. 예를들어 3번째 결과부터 2개 출력이라 하면 이렇게 응용을 할 수 있습니다.


1
2
3
numberList.stream().filter(a -> a % 2 == 0).distinct().skip(3).limit(2).forEach(System.out::println);
 
console result : [8,10]
cs


skip역시 입력받는 파라미터에 대해 방어코드를 할 필요가 없습니다. 조건에 만족하지 못한다면 빈 배열을 출력합니다.



오늘 포스팅에서는 Stream API를 이용한 필터 및 슬라이싱하는 방법을 알아봤습니다. 


어느정도 함수형 프로그래밍의 감이 오시나요? 


오늘 포스팅으로 부터 알 수 있는 점은 함수형 프로그래밍으로 비지니스 로직을 구현 시,


1. 간편하고, 직관적인 선언형 위주의 구현


2. 방어코드 없이, 오류가 적은 코드의 구현


이라는 장점을 볼 수 있었음을 알 수 있었습니다.




자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기


반응형

'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글

Stream API 활용편3 (검색과 매칭)  (0) 2017.01.18
Stream API 활용편2 (매핑)  (0) 2016.08.27
Stream과 Collection 차이  (0) 2016.08.04
Stream API  (0) 2016.08.03
람다 조합  (0) 2016.08.02
Posted by N'