ngrinder Mac os 간단 설치 및 테스트 방법

IT 지식/ngrinder|2019. 3. 11. 20:55

api의 성능 테스트를 위해서 네이버에서 만든 ngrinder 설치하고 테스트를 진행해봤다.


ngrinder는 controller와 agent로 구성이 되어 있는데 이에 대한 내용은 https://naver.github.io/ngrinder/ 해당 내용을 체크하자.


1. Controller 설치
- 톰캣을 설치하고 아래 주소에서 war를 다운받아서 실행시킨다.
https://github.com/naver/ngrinder/releases
단, 3.4.2는 테스트 스크립트 실행 시 unexpected token에러가 발생한다. 그래서 3.4.1을 사용하는걸 추천한다.

설치 완료되면 아래 url로 접근 해서 확인 (초기 계정은 admin/admin)
- 뒤에 root path는 편의를 위해서 war 파일을 ngrinder-controller-3.4.1.war => ngrinder.war로 변경해서 ngrinder로 사용

http://localhost:8080/ngrinder

 

2. Agent 설치
Agent는 테스트에서 필요한 worker process를 실행시켜주고 관리하는 역할을 한다.
- agent를 다운받고 내부에 ./run_agent.sh를 실행시킨다.

- 실행이 완료되면 Agent Management에 들어가면 정상적으로 동작하는걸 확인할 수 있다.

주의사항
먼저 자바 1.9이상의 버전에서는 Agent을 지원을 하지 않는다. 1.9에서 agent 실행 시 다음과 같은 오류가 난다.

1
java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader and java.net.URLClassLoader are in module java.base of loader 'bootstrap')
cs
이는 1.9에서 URLClassLoader를 사용하는 방식이 바뀌었으나 ngrinder agent가 아직 지원하지 못해서 발생하는 오류인거 같다. 1.8을 사용하면 괜찮다.



테스트 진행

각 옵션을 설정하고 테스트를 진행하면 아래와 같이 TPS결과가 나온다. 각 설정 옵션에 대해서는 인터넷이나 메인 git에 가면 자세히 나와있다.


Agent, VUser를 조절해가면서 api의 성능을 tps를 확인하면서 조절해서 테스트하면 된다.



'IT 지식 > ngrinder' 카테고리의 다른 글

ngrinder Mac os 간단 설치 및 테스트 방법  (0) 2019.03.11

댓글()

node.js oauth2 server 만들기

web/node.js|2018. 11. 25. 00:47

스프링 프로젝트에서는 자주 oauth2를 사용했었는데 node.js는 해본적이 없어서 공부할 겸 node-oauth2-server 라이브러리를 사용하여 진행해보고 있다.


확실히 힘든 부분이 있고 설명이 어려워서 외국에서 예로 인터넷에 올려논 부분을 실제로 돌려보면서 수정하여 진행하고 있다. 


설명과 소스는 git repository에 계속 수정해서 올릴 예정이다.

현재는 Authorization_code와 password 방식만 올려놓았다.


implict와 refresh_token 하는 기능도 수정해서 올려야겠다.


Git Repository 주소

https://github.com/weduls/node-aouth2-server-example

댓글()
  1. Favicon of https://wedul.site BlogIcon 위들 wedul 2018.11.26 22:51 신고 댓글주소  수정/삭제  댓글쓰기

    refresh_token grant_type도 올려놓았다.

    implicit는 해당 라이브러리에서 주석처리되어있다. 이유는 나와있지 않아 해당 기능은 아직 지원이 어려울듯.. 자세한 사용 설명은 readme.md를 읽어보시길.

마이크로서비스 조립성 및 정리

web/마이크로서비스|2018. 10. 23. 11:17

마이크로서비스의 조립성은 서비스 디자인 원칙의 하나이다. 서비스를 어떻게 디자인해서 효율적으로 할 수 있는 원칙을 이야기하며 크게  두 가지 요소로 나뉜다.


1. 오케스트레이션(Orchestration)

- 여러개의 서비스를 모아 하나의 완전한 기능을 만드는 서비스가 마이크로 서비스이다. 그럴 경우 중앙에서 두뇌 역할을 하는 오케이스트레이터가 여러 서비스를 묶어서 조율할 수 있어야 한다. 이 오케스트레이터는 외부에 노출되는 접점이다.

- 예를들어 주문이라는 서비스가 들어오면 고객 서비스, 상품 서비스, 배송 서비스에 역할을 분배하여 요청하는 중재자를 의미한다.


2. 연출 (Choreography)

- 특정한 서비스가 실행되면 메시키큐에 producer가 이벤트를 넣고 그 곳을 바라보면서 대기하던 consumer들이 자신에 맞는 이벤트 동작을 실행하는 것을 말한다. 

- ESB에 메시지가 푸시되면 그 이후에 처리되는 모든 결정은 메시지를 소비하는 서버에 의해 자동으로 결정된다.



마이크로 서비스의 현명한 종단점 개수는 몇 개일까?

- 마이크로 서비스를 만들 때 종단점의 수는 그렇게 중요하지 않다. 필요에 따라 여러개 일수도 있고 하나가 될 수도 있다.

- 하지만 서로 연결돼서 공통의 데이터를 사용하거나 비슷한 업무를 처리하는 부분을 모두 종단점으로 구분하게 된다면 결과적으로 동일한 부분을 잘게 쪼개놓은 꼴이된다. 결국 성능적으로 더 떨어진 시스템이 될 수도 있다.

- 결론적으로 종단점의 개수는 중요한 것이 아니고 마이크로서비스 크기에 적합하게 경계 지어진 컨텍스트를 적절하게 설계하는 것이 중요하다.


가상머신에 여러개의 마이크로서비스 

- 하나의 가성머신에 여러 마이크로서비스를 올려놓을 수 있다. 이는 용량을 포함하는 리소스가 충분하고 별도의 서비스로 동작하고 서비스 요구사항 (OS, JDK 등등)이 서로 충돌하지 않는다면 가능하다.

- 운영하는 서비스의 인프라의 가용성 특징, 배포 등을 잘 따져가며 설계해야한다.


마이크로서비스의 데이터 스토어 공유 

- 마이크로서비스에서 데이터 스토어를 공유하는 것은 처음에는 별로 어려움이 없어보이지만, 서비스가 많아지고 테이블이 많아지고 하다보면 서비스간에 결합이 생기게 된다. 그러면 두 개의 마이크로서비스도 결국 결합이 생기게 된다. 그러면 효율적인 마이크로서비스라고 할 수 없다.

- 데이터베이스 서버를 별도로 두기에 여유가 없다면 데이터베이스 서버의 스키마를 구분해서 사용해도 된다.

- 또 다른 방법으로 하나의 트랜잭션 관리가 필요한 데이터베이스는 별도로 분리해서 마이크로서비스로 관리하는 것이다.


분산 트랜잭션 시나리오

- 마이크로서비스에서 로컬 트랜잭션만을 이용하고 분산 트랜잭션을 피하는 것이 이상적이다.

- 하나의 서비스에서 식사 예약을 한다고 가정해봤을 때 식사 예약을 완료하기전에 픽업 예약 서비스를 해당 마이크로서비스에 요청한다고 했을 때 픽업 예약은 완료되었지만 식사 예약이 실패할 경우 픽업만 예약되는 사태가 발생할ㄹ 수 있다. 이런 문제를 해결하기 위해 픽업 예약을 식사 예약이 성공적으로 끝나고 나면 보내는 방법이 있으나 메시지를 보내고 나서 갑자기 실패하는 경우 등에 대한 문제를 해결하기에는 부족하다.


서비스 종단점 설계 고려사항

- 마이크로서비스에서 서비스 설계는 Contract Design (계약설계), Protocol Selection (프로토콜 선택) 이라는 두 가지 핵심요소로 나뉜다.

1. 계약 설계

- 서비스 설계에서 가장 주요한 원칙은 단순함이다. 서비스는 소비자가 소비할 수 있게 설계되어야한다. 복잡한 서비스 계약은 서비스의 사용성을 떨어트린다.


2. 프로토콜 선택

- 마이크로서비스는 SOA(서비스 지향 아키텍처)와 동일하게 HTTP/SOAP와 메시징 서비스 상호작용을 위한 기본 서비스 프로토콜을 따른다.

- 메시지 방식이나 REST 방식을 사용한다.

- API 문서는 Swagger 등을 사용한다.


공유 라이브러리 처리

- 마이크로서비스의 기본원칙은  자율성과 자기 완비성이다. 그래서 다른 서비스에서 같은 라이브러리가 필요한 경우가 있을 있다.

만약 동일 코드나 라이브러리를 서로 다른 마이크로서비스에서 가지게 된다면 중복 문제가 있지만 그렇지 않으면 한쪽 마이크로서비스에 다른 마이크로서비스가 의존하게 되는 문제가 발생할 있다. 

만약 동일한 코드를 둘다 가지고 있게 된다면 모든 지식은 하나의 시스템 안에서 오직 하나의 모호하지 않은 권위를 가진 표현으로 존재해야 한다는 DRY 원칙을 어긋나게 된다.

하지만 그렇다고 공통 서비스를 또다른 마이크로서비스로써 값을 참조하게 한다면 또다른 복잡도를 가질 있기 때문에 상황에 맞게 선택해서 진행하는것이 좋다.


API 게이트웨이

- 출국관리 애플리케이션에서 체크인, 라운지관리, 탑승수속등의 기능이 필요하다고 가정해보자. 각 서비스들은 별도의 API로 개발되어 있다. 하지만 이는 출국 관리 애플리케이션에서 봤을 때 하나의 웹 애플리케이션에 담아서 관리할 필요성이 있다고 느껴진다. 이럴 경우에 마이크로서비스(API들)를 연결해주는 컨테이너 웹 애플리케이션이나 Placeholder가 필요한데 이 경우 별도의 API 게이트 웨이를 만들어서 사용할 수 있다.

- 요청에 따라 모든 데이터를 한번에 전달하는 API는 쓸데없는 오버헤드를 발생시킬 수 있다. 그래서 필요한 데이터만 필드에 보내줄 수 있다. 하지만 그럴 경우 필요에 따라 전달되는 형식이 바뀌어야한다. 그래서 API 게이트웨이에서 데이터 명세에 따라 필요 데이터만 클라이언트 쪽으로 내보내주도록 만들어서 기존의 API들은 원래 하던대로 동작해도 무관하게 설계할 수 있다.







댓글()

간단한 카카오 챗봇 만들기

IT 지식/기타지식|2018. 10. 6. 01:34

node.js 공부한 것으로 한번 테스트 해보기 위해서 카카오 플러스친구를 만들고 그곳에서 호출하는 api를 만들어 연동해봤다. 일부러 wedul 플러스 친구 정보와 친구추가를한 사용자들에 정보를 담을 데이터는 redis에 넣어봤다.

플러스친구 만들기

우선 카카오 플러스친구에 들어가서 플러스친구를 만들었다. 플러스 친구에 wedul이라고 검색하면 이제 나온다. 신기하다.

친구 추가 url : http://pf.kakao.com/_qMxnEj




이제 친구 추가후에 채팅창에서 보여줄 안내 대화를 만들기 위해 api를 만들었다.
api 만들 때 필요한 상세 내용들은 문서에 잘 나와있다. 아주 간단해서 10분이면 다 만들듯??? 그렇게 만든 api를 aws 프론티어에 올려서 카카오 플러스친구에 연동했다.

Developer Document

https://github.com/plusfriend/auto_reply#6-object


짜잔 결과!


그냥 재미삼아 만든 것이기 때문에 별다른 기능은 없다.

자세한 소스는 git에 node.js 공부하는 소스에 포함시켜 올려놓았다. 

https://github.com/weduls/nodejs_test



댓글()
  1. dani 2019.08.19 11:40 댓글주소  수정/삭제  댓글쓰기

    간단한 말을 참 잘 쓰는 군요!?

node.js에 swagger 적용

web/node.js|2018. 10. 5. 00:13

Spring Boot에 적용했었던 swagger를 node.js에도 적용해보자.

spring boot에서는 자동으로 만들어졌으나, node.js에서는 Definition을 적용해줘야해서 귀찮다.
설정 방법을 알아보자.

설치 패키지

1
2
"swagger-jsdoc": "^3.0.2",
"swagger-ui-express": "^4.0.0"
cs



Definition정의

swagger에 대해 적용할 프로그램에 대한 정보와 path, api들 위치등에 대해 정의한 definition을 정의한다.
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * Created by wedul on 2018. 8. 30.
 */
'use strict';
 
module.exports = {
  swaggerDefinition: {
    // 정보
    info: {
      title: 'node js test app',
      version: '1.0.0',
      description: 'Make For node js test.'
    },
    // 주소
    host: "localhost:3000",
    // 기본 root path
    basePath: "/",
    contact: {
      email: "rokking1@naver.com"
    },
    // 각 api에서 설명을 기록할 때 사용할 constant들을 미리 등록해놓는것
    components: {
      res: {
        BadRequest: {
          description: '잘못된 요청.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        },
        Forbidden: {
          description: '권한이 없슴.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        },
        NotFound: {
          description: '없는 리소스 요청.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        }
      },
      errorResult: {
        Error: {
          type: 'object',
          properties: {
            errMsg: {
              type: 'string',
              description: '에러 메시지 전달.'
            }
          }
        }
      }
    },
    schemes: ["http""https"], // 가능한 통신 방식
    definitions:  // 모델 정의 (User 모델에서 사용되는 속성 정의)
      {
        'User': {
          type: 'object',
          properties: {
            id: {
              type: 'string'
            },
            age: {
              type: 'integer'
            },
            addr: {
              type: 'string'
            }
          }
        }
      }
  },
  apis: ['./routes/**/*.js'// api 파일 위치들 
};
cs


api 주소
각 라우터에 대한 정보를 적어주어야 swagger-ui에서 정의된대로 나온다.

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
38
39
40
41
/**
 * @swagger
 * tags:
 *   name: User
 *   description: 사용자 정보 가져오기
 */
module.exports = router;
 
/**
 * @swagger
 * /user/:
 *   get:
 *     summary: 사용자 정보 가져오기
 *     tags: [User]
 *     parameters:
 *       - in: query
 *         name: id
 *         type: string
 *         enum: [cjung, gglee, etc..]
 *         description: |
 *          사용자 아이디 전달
 *     responses:
 *       200:
 *         description: 성공
 *       403:
 *         $ref: '#/components/res/Forbidden'
 *       404:
 *         $ref: '#/components/res/NotFound'
 *       500:
 *         $ref: '#/components/res/BadRequest'
 */
router.get('/', async (req, res, next=> {
  const {id} = req.query;
  let data = await userService.findUser(new UserDto.ParamBuilder(id).build());
 
  if (data) {
    res.json(data);
  } else {
    return next(new NotFoundError('잘못된 요청입니다.'));
  }
});
cs


app.js
app.js에 Definition을 정의하고 swaggerSpec이랑 swaggerUI관련 설정을 해주면된다.

1
2
3
4
5
6
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerOption = require('./routes/swagger');
const swaggerSpec = swaggerJSDoc(swaggerOption);
const swaggerUi = require('swagger-ui-express');
 
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
cs


접속
http://localhost:3000/api-docs/ 에 접속하면 접속한 화면이 나온다.


#
설정한 router 정보

각 router에서 정의한 api 주소 정보가 확인된다.


또한 테스트도 진행할 수 있다. 파라미터 값을 선택하고 execute 버튼을 누르면 실제 테스트를 진행하고결과까지 보여 준다.


그리고 함께 정의하였던 response 정보도 확인할 수 있다.


마지막으로 Definion 문서 마지막에 정의하였던 각 Model의 Definition도 확인할 수 있다. 


자세한 문서는 여기 참고
https://frontalnh.github.io/2017/11/22/nodejs-swagger-api-doc-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0/

https://github.com/swagger-api/swagger-editor


위의 예제 코드는 github 참고
https://github.com/weduls/nodejs_test


## 참고로 swagger를 쓰지 않고 테스트에서 super-test 라이브러리를 사용해서 테스트할수도 있다. 하지만 ui가 없고 front나 다른 개발자한테 api 정보를 넘겨주고 테스트까지 가능하게 해주는 swagger가 좋은거 같다. (super-test는 하단의 링크 참고)


댓글()
  1. ㅇㅇ 2019.01.16 16:02 댓글주소  수정/삭제  댓글쓰기

    위의 예제코드 깃헙 링크가 404 not found 뜹니다..

  2. lll 2019.10.09 15:10 댓글주소  수정/삭제  댓글쓰기

    에구. ㅠㅠ 혹시 다시 예제를 올려주실 생각 없나요? 설명 잘 적으셨지만, 그래도 소스로 참고하고 싶네요

Swagger 라이브러리를 사용하여 API 내용 정리하기

web/Spring|2018. 8. 7. 00:08

Web 개발을 진행하다보면 다양한 API를 개발하게 된다.

하지만 정리하기가 쉽지 않고 이를 위해서 인터페이스정의서를 계속 정리하기도 사실 쉽지 않다. 
이를 위해서 한번에 API 내용이 정리되어 한눈에 볼 수 있도록 제공해주는 library가 있다. 이 library인 swagger를 적용해보자.

1. 라이브러리 추가
Swagger 사용을 위한 라이브러리를 gradle에 추가하자.

1
2
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0'
cs


2. Swagger 설정 관련 내용 Configuration 클래스 생성

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
package com.wedul.common.config;
 
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
/**
 * Swagger 사용을 위한 설정 클래스
 */
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
 
    /**
     * 설정
     *
     * @return the docket
     */
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any()) // 현재 RequestMapping으로 할당된 모든 URL 리스트를 추출
                .paths(PathSelectors.ant("/user/**")) // /user/** 인 URL들만 추출
                .build();
    }
 
}
cs


Docket의 더 많은 사용법은 아래 사이트 참고.
https://swagger.io/tools/swaggerhub/hosted-api-documentation/


3. Spring Security 사용시 권한 허용
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
// 권한 허용 리스트
@Override
public void configure(WebSecurity web) throws Exception {
    // 허용되어야 할 경로들
    web.ignoring().antMatchers("/resources/**"
                               "/dist/**"
                               "/weather"
                               "/user/password/find",
                               "/user/join",
                               "/user/email",
                               "/user/nickname",
                               "/user/send/temppw",
                               "/findpw"
                               "/user/findpw",
                               "/user/cert/check",
                               "/join"
                               "/getLanguage/**",
                               "/getMessage",
                                "/api/**"); // #3
}
 
 
// resource 허용    
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/dist/**").addResourceLocations("/dist/");
 
    registry.addResourceHandler("/resources/**")
            .addResourceLocations("/WEB-INF/resources/");
 
    registry.addResourceHandler("swagger-ui.html")
            .addResourceLocations("classpath:/META-INF/resources/");
 
    registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
}
cs

이렇게 한 후 실행 시키면 다음과 같이 swagger-ui를 확인할 수 있다.


또한 각 상황에 대해 API를 호출하면서 header, body, response 등을 확인할 수 있다.


댓글()

Tistory 외부 Editor API Tistory Editor 사용후기

IT 지식/기타지식|2018. 6. 15. 22:22

티스토리를 사용하다보니 에디터가 너무 옛날이라 사용하기가 힘들었다.

그래서 찾아보던중 외부 API를 이용하여 개발한 프로그램이 있어서 사용해보았는데 너무 좋았다.

가장 좋았던 이유는 캡처프로그램을 통해 캡처 후 Ctrl + C와 Ctrl + v를 통해 등록이 가능하다.

이 부분이 너무 편하다. 그리고 드래그 앤 드롭으로 사진을 옮겨서 첨부할수 있어서 좋은 것 같다.

다운로드 주소 http://tistory-editor.tistory.com/

지원 OS 윈도우, mac

댓글()

Java8 스트림(stream) 연산

JAVA/Java 8|2018. 5. 31. 07:37

리덕션 연산
Stream reduce 메소드는 2개의 인자를 받아 인자와 같은 값을 리턴하는 (T,T) -> T 메소드이다.
 
 번째 인자는 누적되는 값이 되고, str2 foreach처럼 순서대로 요소가 들어오는 방식이다.



public static void main(String args[]) { List<String> list = Arrays.asList(new String[] {"a", "b", "c"}); Stream<String> stream = list.stream(); Optional<String> opt = stream.reduce((str1, str2) -> str1 + str2); opt.ifPresent(System.out::println); } =>> 리턴 값 "abc"



 다른 형태의 reduce 메소드 
위에서 소개한 reduce 메소드 형태와 다르게 이번에 소개할 Reduce메소드는 3개의 인자를 받는다.
 
 메소드는 반환 받으려는 
타입과 연산하려는 타입이 다를 경우에 사용한다.
 
 reduce 형식은 reduce(U, 함수1, 함수2) 유형이고C
리턴 타입은  번째 인자인 U 타입을 리턴한다.
 
 번째 인자는 시작 값으로 사용된다.
 번째 인자인 함수 1 (U, T) -> U 형식이다인자 2개를 받아  번째 인자의 타입으로 리턴한다.
 번째 인자인 함수2 함수 1 연산 결과들을 모아 연산한다타입은 (U, U) -> U이다.
 
예를 들어서,
WedulObject이라는 객체의 나이를 모두 더해서 값을 출력하고 싶을  사용한다.





1
2
3
4
List<WedulObject> objects = Arrays.asList(new WedulObject("kim"1"dev"), new WedulObject("jun"2"insa"), new WedulObject("jung"3"kakao"));
int ageSum = objects.stream().reduce(0, (result, object) -> result + object.getAge(), (result, age) -> result + age); 
        
System.out.println(ageSum);
cs




 번째 인자에서는  번째 인자인 시작값과 입력된 person age값을 더한다
0 + 10 + 11 + 12
 
 번째 인자에서는  번째 인자에서 나온 결과를 모두 더한다.
10 + 11 + 12 = 33
※여기서 시작값을 1 바꾸면 결과는 34, 2 바꾸면 35 출력된다.
 
이러한 방식을 사용하는 이유는 
입력된 person age값을  번째 인자에서 병렬로 계산하고 번째 인자에서 병렬로 처리한 결과들을 모두 통합해서 반환하고자 하는 타입으로 반환하고자  
사용된다.
 
만약 단순하게 입력된 여러 person age값을 뽑아  병렬로  덧셈처리한 결과를 반환하고 싶을 경우에는 다음과 같이 처리하는게 더욱 효과적이다.




1
2
3
4
5
6
7
public static void main(String args[]) {
    
    List<WedulObject> objects = Arrays.asList(new WedulObject("kim"1"dev"), new WedulObject("jun"2"insa"), new WedulObject("jung"3"kakao"));
    
    int ageSum = objects.stream().filter(x -> x.getAge() > 2).mapToInt(WedulObject::getAge).sum();
    System.out.println(ageSum);
}
cs


댓글()