Rest Operator와 Spread Operator

web/javaScript|2018. 10. 6. 01:20

es6를 사용하면서 보게된 Rest Operator와 Spread Operator에 대해 정리를 해보자.


Rest Operator

기존에 리터널 문법을 사용하여 객체나 배열의 값을 변수로 바인딩하여 사용할 수 있었다.
근데 object라는 객체의 값이 많은경우에는 여기서 값을 모두 하나하나 뽑아내는건 어렵기 때문에 하나의 객체로 뽑아낼때 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 기존에 하나씩 속성을 뽑아서 사용하던 방식
const object = {a : 'wedul', b : 'cjung', c : 'gglee', d : 'babo'};
const {a, b} = object;
console.log(a, b);
 
#출력
wedul, cjung
 
// Rest Operator 를 사용하여 여러 속성을 하나로 묶어서 진행
const object = {a : 'wedul', b : 'cjung', c : 'gglee', d : 'babo'};
const {a, b, ...obj} = object;
console.log(a, b, obj);
 
#출력
wedul cjung { c: 'gglee', d: 'babo' }
 
// 배열일때도 마찬가지로 사용가능한데 배열인 경우에는 뒤에 요소가 배열로 만들어진다.
// Rest Operator
const array = ['wedul', 'cjung', 'gglee', 'babo'];
const [a, b, ...obj] = array;
console.log(a, b, obj);
 
#출력
wedul cjung [ 'gglee', 'babo' ]
cs


Spread Operator

배열이나 Object에 있는 내용들이 열거되어 사용할 수 있도록 해주는 기능
아래의 예에서는 입력된 배열의 요소들이 getlternalTest() 함수수의 내용들이 name, age, address 순서대로 열거된다.

1
2
3
4
5
6
7
8
9
10
11
12
getleternalTest(name, age, address) {
  console.log(name, age, address);
  #출력 결과 cjung 23 seoul
}
 
 
#테스트
it('liternal test', () => {
  // array
  test.getleternalTest(...['cjung', 23, 'seoul']);
});
 
cs


Push로 기존에 배열에 데이터를 추가할 수도 있다.

1
2
3
4
let data = ['dd', 'cg', 'zz', 'cjung'];
data.push(…data);
console.log(data);
-> [“dd", "cg", "zz", "cjung", "dd", "cg", "zz", "cjung"] 
cs


Object도 동일하게 사용가능


궁금사항으로 이렇게 복사된 항목들은 깊은복사가 될까였다. 그래서 테스트를 해봤다.
-> 테스트 결과 해당 객체가 만들어질때 데이터가 값이 복사가 되기 때문에 서로 별개로 동작하게 된다. 아래 예를 보면 동작이 이해가 가능하다.

1
2
3
4
5
6
7
8
> copyBabo // babo 오브젝트에서 복사된 copyBabo의 항목
< {name: "cjung", age: 23, addr: "seoul"}
 
// copyBabo name 속성 변경
> copyBabo.name = 'gglee';
 
> babo // babo의 속성을 확인했을 때 바뀌지 않은걸 알 수있다.
< {name: "cjung", age: 23, addr: "seoul"}
cs




댓글()

스프링 웹플럭스(spring webflux)를 활용한 간단한 리액티브 마이크로 서비스

web/마이크로서비스|2018. 10. 4. 23:50

자바 리액티브 프로그래밍은 리액티브 스트림 명세를 바탕으로 하고 있다.  

리액티브 스트림 명세에는 컴포넌트 사이의 비동기 스트림 처리나 이벤트 흐름을 Non Blocking 방식으로 처리하기 위한 문법을 정의한다. 

일반적인 옵저버 패턴과 달리 리액티브 스트림에는 시퀀스의 처리, 완료 알림, 실패시 backpressure 적용 등이 추가된다. backpressure는 받는 컴포넌트에서 보내는 컴포넌트에게 얼마만큼의 데이터를 소화할 수 있다고 알려줄 수 있다.  그래서 받는 컴포넌트에서 처리될 준비가 됐을 때만 데이터를 받을 수 있다. 

그래서 서로 속도가 다른 컴포넌트 사이의 통신을 할 때 유리하다. 

스프링 프레임워크 5 web flux는 Reactor 리액티브 스트림 명세를 기반으로 되어있다.  



간단한 Spring WebFlux 프로젝트

인텔리제이에서 New Project - Reactive web 선택  
Spring-boot-starter-webflux 가 web 대신 추가되어있는 것을 확인할 수 있다.

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
cs


간단한 RestController를 만들고 Mono를 응답바디로 지정해보자.

1
2
3
4
@RequestMapping("/")
public Mono<Greet> greet() {
    return Mono.just(new Greet("Hello World"));
}
cs

여기서 Body로 Mono를 반환하는데 이는 비동기 논블로킹 모드에서 Mono가 일을 마친 후에만 Greet 객체가 직렬화 된다는 것을 의미한다.

테스트 코드
RestTemplate와 비슷한 WebClient를 사용하여 간단한 web flux를 테스트해보자.


1
2
3
4
5
6
7
8
9
10
11
12
@Autowired
WebTestClient webTestClient;
 
@Test
public void contextLoads() {
    webTestClient.get().uri("/")
            .accept(MediaType.APPLICATION_JSON)
            .exchange() // response값을 가져오기 위해서 사용하는 메서드가 exchange()
            .expectStatus().isOk()
            .expectBody(Greet.class).returnResult()
            .getResponseBody().getName().equals("Hello World");
}
cs


댓글()

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 등을 확인할 수 있다.


댓글()

Spring에서 url 요청하는 RestTemplate 설명

web/Spring|2018. 5. 27. 19:38

Spring 기반 프로젝트 진행 시 URL을 요청할 때가 있다. 

이를 Apache의 HttpClient 라이브로리를 사용하여 할 수 있지만, 스프링 프로젝트에서는 SpringTemplate를 사용하여 쉽게 요청할 수 있다. (httpClient와 RestTemplate의 차이는 하단의 링크참조)

사용법은 아주 간단하다. 각 요청 Method 마다 하단의 메소드를 이용하여 호출하기만 하면된다.

1. Get


1
2
3
4
5
RestTemplate restTemplate = new RestTemplate();
String fooResourceUrl
  = "http://localhost:8080/spring-rest/foos";
ResponseEntity<String> response
  = restTemplate.getForEntity(fooResourceUrl + "/1", String.class);
cs




2. Post


1
2
3
4
5
6
ClientHttpRequestFactory requestFactory = getClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
 
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
 
cs



PUT과 DELETE는 하단 참조페이지에서 확인가능하다.

또한 이런 RestTemplate 기능에서 비동기로 결과값을 받을 수 있는 라이브러리가 추가되었다.
Spring 4에 추가된 AsyncRestTemplate를 이용하면 url을 요청하고 비동기로 결과를 받을 수 있다. 

사용법은 다음과 같다.



1
ListenableFuture<ResponseEntity<Map>> entity = asyncRestTemplate.getForEntity("https://httpbin.org/get", Map.class); 
cs




각 사례별 자세한 설명 (httpClient와 비교) 
https://vnthf.github.io/blog/Java-RestTemplate%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC/ 

Spring API 문서 
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html 

자세한 사용법 
http://www.baeldung.com/rest-template



댓글()

Java Json Library Jacson Annotation 소개

web/Spring|2018. 5. 27. 19:14

restFul API를 만들다 보면, 어떤 항목을 serialize 제외하거나 null인 항목은 제외하거나 하는 작업이 필요로 할 때가 있다.

이를 Jackson 라이브러리의 Annotation을 사용하여 하는 방법을 확인해보자.

[Maven 셋팅]




1
2
3
4
5
6
7
8
9
10
11
<properties>
    <jackson.version>2.9.2</jackson.version>
</properties>
  
<dependencies>       
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
</dependencies>
cs




예제로 사용될 Student Dto 클래스이다.


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
package com.pnpsecure.common.dto;
 
/**
 * 학생 DTO
 *
 * @class StudentDto.java
 * @author cjung
 * @since 2018. 4. 2.
 */
public class StudentDto {
    private String name;
    private String dept;
    private int age;
    
    public StudentDto() {}
    
    public StudentDto(String name, String dept, int age) {
        this.name = name;
        this.dept = dept;
        this.age = age; 
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getDept() {
        return dept;
    }
 
    public void setDept(String dept) {
        this.dept = dept;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
}
cs



그리고 요청을 받을 Controller 이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.pnpsecure.common.controller;
 
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.pnpsecure.common.dto.StudentDto;
 
@RestController
@RequestMapping(value = "/student")
public class StudentCtrl {
    
    /**
     * 학생 정보 얻기
     * 
     * @return
     */
    @PostMapping("/get")
    public ResponseEntity<?> getStudent() {
        return ResponseEntity.ok(new StudentDto("wedul""pnpsecure"28)); 
    }
}
cs


/cjungproject/student/get url을 호출하여 결과를 확인해보면 다음과 같이 나온다.




이 기본적인 기능에서 추가적으로 제공되는 Jackson Library 기능을 확인해보자.


1. Serializer/Deserialize 시 제외시킬 프로퍼티를 지정
@JsonIgnoreProperties을 사용하여 제외시킬 프로퍼티를 설정할 수 있다.

[설정방법]
1
2
3
4
5
@JsonIgnoreProperties(value = {"name", "dept"})
public class StudentDto {
    private String name;
    private String dept;
    private int age;
cs



[결과]





2. 전달될 프로퍼티를 다른이름으로 전송
- @JsonProperty를 이용하여 다른 이름으로 전송이 가능하다.

[설정]
1
2
3
4
5
6
7
8
9
public class StudentDto {
    @JsonProperty("userName")
    private String name;
 
    @JsonProperty("userDept")
    private String dept;
    
    @JsonProperty("userAge")
    private int age;
cs



[결과]




3. 프로퍼티가 notNull 이거나 notEmpty일경우에만 반환되도록 설정

- @JsonInclude을 사용하면 프로퍼티가 null 이나 empty 상태일때 전송되지 않도록 지정할 수 있다.

[설정]


1
2
3
4
5
6
7
8
9
public class StudentDto {
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String name;
 
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private String dept;
    
    @JsonProperty("userAge")
    private int age;
cs



[결과]


위와 같이 전달할 경우. name과 dept가 null과 empty 상태이기 때문에 전송이 되지 않는것을 확인할 수 있습니다.





댓글()

REST API 효율적인 설계 방법

web/Web|2018. 5. 27. 10:43

REST 구성

구성 요소
표현방법
내용
Resource
HTTP URI
자원을 정의
Verb
HTTP Method
자원에 대한 행위를 정의
Representations
HTTP Message Pay Load
자원에 대한 행위의 내용을 정의

ex)
Resource : /wedul/member
Verb : HTTP POST
Representations : 
{
   "id" : "wedul"
}

REST 특성

1. 유니폼 인터페이스
-> HTTP 표준만 따르면 어떠한 기술이든 사용가능 (HTTP/JSON, HTTP XML)

2. 무상태성 (STATELESS)

3. 캐시 가능
-> 웹 캐시, CD을 이용한 캐싱 가능

4. 자체 표현 구조 (SELF-DESCRIPTIVENESS)
-> API 내용만 보고도 별도의 문서 없이도 쉽게 이해가 가능하다.


REST 설계 패턴

- API URL만 보고도 무슨 API인지 쉽게 직간적으로
- URL을 길게 만들지 말고 2 depth 수준 까지만 
  > /dogs/
  > /dogs/uki
- 대상은 명사가 되야한다.
ex) getDogs (X), /dogs/ (O)
- 단수보다는 복수형을 사용
/dog (X), /dogs (O)

HTTP Response code
- 총 70개있는 것 중에 권장하는 Response code 사용
> 200 : 성공
> 400 : Bad Request - Field validation 실패
> 401 : 인증, 인가 실패
> 404 : 해당 리소스 찾을 수 없음
> 500 : Internal Error - 서버에러

에러처리
-> 상세 내용은 HTTP body에 표현해 주어야 한다.
-> Error Stack이 노출되지 않도록 조심해야한다.
{
  "error" : 200,
  "message" : "massage sending failed",
  "detail" : "Id Logouted"
}



API 인증방식
1. API 키 방식
-> API 호출할때, API KEY를 보내는 방식 
-> 키가 한번 뚫리면 문제가 발생

2. API 토큰 방식
-> ID, PASSWORD를 사용하여 일정 기간 유효한 API 토큰을 리턴하고, 매 호출마다 토큰 사용
2.1) HTTP BASIC AUTH
  => ID, PASSWORD를 직접 보낸다.
 2.2) DIGEST ACCESS AUTHENTICATION
  => 패스워드를 네트워크를 통해서 보내지 않고, 클라이언트와 서버에서 공통적인 키를 이용해서 패스
  워드에 대한 해쉬를 생성하고 이를 비교하는 방식 (프로젝트에서 경험해 봄)

3. 사용자 인증 방식
 1.1) 3자 인증 방식
  => 계정 정보를 다른 서비스가 가지고 진행
 1.2) 클라이언트 인증
  => 서비스가 여러 클라이언트에 의해 사용될때, 클라이언트별 접근 토큰을 발급하여 인증을 강화



댓글()