API 문서화

API를 개발 하고 나면 문서화를 해야 한다.
문서화는 작업 했던 것을 기록하는것 뿐만 아니라 함께 작업하는 개발자들에게 공유하는 내용이기도 하다.
회사마다, 사람마다 API 문서화 방식이 다르기 때문에 잘 맞는 것을 찾아서 사용하면 된다.

특히 공유가 되는 내용이라면 코드의 수정이 생겼을 경우 API 문서 역시 수정을 해줘야 한다.
예를들어, Parameter가 String 에서 Integer로 바꼈을 경우에 API 문서 역시 수정을 해야 한다.
하지만 생각보다 API문서의 수정작업을 잊는 일이 많이 발생한다.

그래서 이 책에선 이런 문제를 해결하기 위한 여러가지 프로젝트 중에서 스웨거2를 소개한다.

스웨거 2를 스프링 부트와 같이 사용하기

의존성 추가

스웨거2를 스프링 부트에서 사용하려면 아래 2개의 의존성을 추가해야 한다.

<!--Swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>


라고 책에 나와 있지만 이후 작업을 위해서는 하나가 더 필요하다.

<!--Maven Model-->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>2.2.0</version>
</dependency>


애플케이션이 시작될 때 스웨거 라이브러리에 의해 API 문서가 소스코드로부터 자동으로 생성된다고 한다.

이 처리는 메인 클래스에 선언된 Docket 빈에 의해 조절 되는데 Docket을 사용하기 위해서는 maven-model dependency 가 필요하다.


Docket Bean 등록

그리고 다음과 같이 SwaggerConfig를 @Configuration, @EnableSwagger2 애노테이션을 추가하여 등록한다.

@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}

Docket Bean을 추가했다.

select() 메소드는 ApiSelectorBuilder를 리턴하는데 이것은 스웨거에 의해 노출되는 endpoint를 제어하는 방법을 제공한다.

RequestHandlersSelectors.any(), PathSelectors.any() 로 지정을 하면 전체 API가 스웨거를 통해 문서화 된다.


실행 화면

서버를 실행한다.

그리고 나서 http://localhost:8080/swagger-ui.html 로 이동하면 swagger 화면이 뜰 것이다.




특정 패키지에 있는 API만 문서화 하고 싶다면 아래 코드처럼 설정하면 된다.

.apis(RequestHandlerSelectors.basePackage("com.midasit.msa.controller"))


basic-error-controller 가 사라진 것을 확인할 수 있다.



특정 메소드만 문서화 하고 싶담녀 아래 코드처럼 설정하면 된다.

.apis(RequestHandlerSelectors.basePackage("com.midasit.msa.controller"))
.paths(PathSelectors.ant("/user/name"))



user-controller의 경로가 '/user/name' 만 문서화 된 것을 확인할 수 있다.



API 항목을 클릭해서 자세하게 확인해 보도록 하자.



'/user/name' 을 클릭하면 위의 이미지 처럼 상세 화면이 나온다.

파라미터를 입력할 수 있고, Try it out! 을 통해 요청을 보낼 수 있다. 그리 하면 요청정보와 응답정보를 확인할 수 있다.

매우 간단하면서도 매우 편리하다.


더 자세한 내용은 https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api 이곳을 참고하면 된다.

RESTful 웹서비스 생성하기

스프링 mvc에 대해서 전혀 모른다면 아래 주소로 이동하는 것을 추천한다.


스프링 웹은 RESTful 웹서비스를 생성하기 위한 몇 가지 애노테이션을 제공한다.

  • @RestController
    • 간단하게 말하면 HTTP 요청을 처리하는 컨트롤러 빈으로 설정하는 것이다. 자세한 설명은 spring 명세를 참조하자.
    • @Controller 에서 앞에 Rest만 붙어있다. Controller 기능에 무엇인가가 추가가 된 것이다.
    • @Controller 에서는 응답을 JSON 형식으로 보내기 위해서는 각각의 메서드에 @ResponseBody 를 붙여야 했는데 @RestController를 사용하게 되면 기본으로 적용이 된다.
  • @RequestMapping
    • Controller 또는 Controller Method와 HTTP 요청 경로를 연결 해주는 것이라고 생각하면 된다.
    • Controller에 설정하게 되면 모든 Method에 적용이 되며 가장 앞의 path이다.
    • Controller Method에 설정하게 되면 해당 Method에 적용 된다.
    • 즉 Controller의 RequestMapping 값 + Controller Method의 RequestMapping 값 이 최종 경로가 된다.
    • 둘 중 하나만 있어도 되지만 최소한 하나는 있어야 한다. (물론 중복되면 안된다.)
  • @GetMapping (@PostMapping, @PutMapping, @DeleteMapping)
    • @GetMapping 은 기존 @RequestMapping(method=RequestMethod.GET) 과 같다.
    • @GetMapping("/index") == @RequestMapping("/index", method = RequestMethod.GET)
    • 원하는 요청 방식에 따라 Get, Pot .. 필요한대로 쓰면 된다.
  • @RequestParam
    • 요청 시 함께 보낸 Parameter의 값을 받을 수 있다. 
    • 3가지 설정이 존재한다. name, required, defaultValue
    • name : 요청 시 Parameter의 key값을 설정할 수 있다. 설정하지 않으면 변수의 이름을 key로 보내야 한다.
    • required : 필수 Parameter인지 설정할 수 있다. false 이면 요청 시 꼭 보내지 않아도 된다. 설정하지 않으면 true가 기본 값이다.
    • defaultValue : 요청 시 해당 Parameter를 받지 못했을 경우 기본 값으로 사용된다. 설정하지 않으면 required 가 false일 경우 빈값이다.
    • @GetMapping("/name")
      public String name(@RequestParam(name = "eman", defaultValue = "kkt", required = false) String name) {
      return name;
      }
    • 기본 자료형을 요청 Parameter로 보낼 때 많이 사용하며, 복잡한 형태의 JSON 데이터의 경우는 아래 설명할 @RequestBody를 사용한다.
  • @RequestBody
    • spring-boot-starter-web 을 통해 함께 추가 된 잭슨 라이브러리를 통해 JSON 형식의 요청 Parameter를 Object로 바인딩한다.
    • @PostMapping
      public User add(@RequestBody User user) {
      user.setName("김경태");
      users.add(user);
      return user;
      }
  • @PathVariable
    • 경로를 반드시 고정값으로 설정해야 하는 것은 아니다.
    • @GetMapping("/{id}") 처럼 설정을 하고 Method 파라미터 설정을 (@PathVariable String id) 로 설정을 하면 경로에 입력한 값이 id에 바인딩 된다.
    • @GetMapping("/{id}")
      public String findById(@PathVariable String id) {
      return id;
      }


위 요청 예제에는 아무런 의미가 없으니 형식만 보면 된다.



컨피규레이션 파일 사용자 정의하기

스프링 부트의 최대 장점은 기본 설정을 해준다는 것이다. 그래서 빠르게 시작할 수 있다.
그런 장점 때문에 기본 설정을 재정의하고 무시하는 법을 배워야 하는 단점. (단점? 당연히 알아야지ㅎ)

스프링 부트는 application으로 시작하는 설정 파일을 자동으로 찾도록 설계되어 있다.
지원하는 파일 타입은
  • .properties
  • .yml
이렇게 두 가지 이다.

따라서 application.properties 또는 application.yml 의 이름을 한 설정 파일을 찾는다.
둘 중에서 아무거나 써도 되지만 .yml 파일이 가독성이 좋아서(개인적으로) 나는 yml 파일을 사용한다.
또한 application-prod.properties 또는 application-dev.yml 처럼 특정 프로파일을 지정하는 설정 파일도 가능하다.

운영체제 환경변수와 커맨드라인 입력값을 사용하여 설정을 외부에서 주입 할 수 있다.
이런 경우에는 .properties 와 .yml 파일은 다음의 위치에 있어야 한다.
  • 애플리케이션의 현재 디렉토리의 /config 하위 디렉토리
  • 애플리케이션의 현재 디렉토리
  • classpath /config 패키지(예를 들어 JAR의 내부)
  • classpath root
설정 파일명으로 application이나 application-{profile} 이외의 특정한 이름을 부여하기 위해서는 시작 시 spring.config.name 환경변수를 제공해야 한다.
string.config.location 설정에 콤마로 분리된 설정 디렉토리 경로와 컨피규레이션 파일 경로 목록을 설정할 수 있다.

Configuration 파일 내부에 두 종류의 설정을 지정할 수 있다.
  • 하위 클래스(주로 spring-boot-autoconfiguration 라이브러리)에서 사용되는 공통 설정, 미리 정의된 스프링 부트 설정의 그룹
  • 자신만의 컨피규레이션 속성을 지정하면 @Value 또는 @ConfigurationProperties 애노테이션을 통해 애플리케이션에 주입된다.

미리 정의된 속성

스프링 부트에서 지원하는 미리 정의된 속성은 아래 주소로 가면 된다.

일반적으로 application.yml 파일은 src/main/resources 디렉토리에 위치한다.

server:
port: 8888
error:
whitelabel:
enabled: false
spring:
application:
name: kennen-service
logging:
pattern:
console: "%d{HH::mm:ss.SSS} %-5level %logger{36} - %msg%n"
file: "%d{HH::mm:ss.SSS} %-5level %logger{36} - %msg%n"
level:
org.springframework.web: debug
file: app.log




기본 속성 값은 이렇게 재정의가 가능하다.

  • 로깅을 위해 log4j.xml이나 logback.xml 등을 설정하지 않아도 설정대로 로깅 기능을 지원한다.
  • 기본 애플리케이션 이름을 kennen-service로 수정했다.
  • HTTP 포트를 8888로 수정했다.


사용자 정의 속성

  • 사용자 정의 Configuration 역시 같은 properties 파일 또는 YAML 파일에 위치해야 한다.
  • 아래 그림 처럼 빈으로 주입이 가능하다.

스프링 부트를 이용해 애플리케이션 개발하기

Spring 의존성 추가하기

Maven

- Parant 를 이용한 방법

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>


- dependencyManagement 를 이용한 방법

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.0.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>


<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>


dependency를 추가 할 때 <version> 이 없어도 되는 이유는 상속하는 parent 또는 dependencyManagement 에 해당 artifact에 대한 Version 정보가 이미 존재하기 때문이며 다른 버전을 사용하고 싶을 경우에는 version을 명시 하면 된다.


Gradle



gradle 의 경우, 부모 의존성을 정의할 필요가 없다.


SpringBootApplication

의존성을 추가 했으면 이제 실제 애플리케이션을 개발하고 실행을 해보자.


먼저 메인 애플리케이션 클래스를 생성하고 @SpringBootApplication 애노테이션을 추가한다. 이게 전부다.
@SpringBootApplication 애노테이션은 @Configuration, @EnableAutoConfiguration, @ComponentScan 이렇게 세 가지 애노테이션을 추가 한 것과 같다.

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}

}

이렇게 작성을 하고 Application을 실행하면 spring-boot-starter-web을 의존성 추가를 했기 때문에 포트 8080을 사용하는 톰캣 컨테이너의 스프링 컨텍스트를 시작한다.


현재 프로젝트에 포함되어 있는 라이브러리 항목은 다음과 같다.


spring-boot-starter-web 하나만 추가 한 것인데 이렇게 많은 라이브러리 들이 추가가 되었다.
어떻게 된 것인지 궁금하다면 아래 링크에 들어가서 Transitive Dependencies를 확인해 보면 된다.
IDE를 사용하지 않는다면 다음과 같이 pom.xml에 spring-boot-maven-plugin 을 추가 해야 한다.
그리고 빌드를 하고 난 뒤 " java -jar {파일이름].jar 를 실행 하면 IDE에서 실행한 것과 마찬가지로 잘 실행이 될 것이다.

웹 컨테이너를 변경하고 싶다면 의존성 항목을 변경하면 된다.
spring-boot-starter-jetty 로 바꾸면 Jetty 서버로 변경된다.



마이크로서비스를 위한 스프링

요즘 개발중인 샘플 프로젝트(SSO 관련) 역시 스프링부트로 진행하고 있고 버전 역시 2.0.1-RELEASE를 사용하고 있다.
마침 이 책에서도 같은 버전을 사용하고 있어서 다행이다.

이 책에서 스프링에 대해서 다루는 이유는 당연히 스프링으로 예제를 만들었기 때문이다.
따라서 이미 스프링에 대해서 잘 알고 있다면 스킵해도 될 것 같다.

하지만 읽는 것을 추천한다.

안다고 생각했던 것들 중에서 잘 모르고 있거나 잘못 알고 있는 것들이 꽤 있기 때문이다.


스프링 부트 소개

    • 애플리케이션을 java -jar 명령어로 실행한다.
    • 표준 스프링 컨피규레이션보다 간단하게 실행 가능하다.

Spring starter

    • 프로젝트 의존성에 포함될 수 있는 아티팩트(Artifact)
    • 애플리케이션에 포함해야 하는 다른 의존성을 제공한다.
    • Auto Configuration을 통해 기본 설정이 적용된다.
    • 예를 들어 spring-boot-start-web을 포함하면 기본 웹 컨테이너를 내장한 애플리케이션으로 시작한다.
      • 기본 웹 컨테이너는 Tomcat이고 8080포트를 사용한다.
      • 포트는 속성 파일에 지정된 필드를 선언하여 쉽게 변경 가능하다.
      • spring-boot-starter-jetty 또는 spring-boot-starter-undertow를 사용하여 웹 컨테이너를 변경 가능하다.



Spring-boot-starter-*


 이름

설명 

spring-boot-start 

자동 컨피규레이션 지원, 로깅, YAML을 포함하는 핵심 스타터 

spring-boot-start web

RESTful과 스프링 MVC를 포함하는 웹 애플리케이션 개발,  톰캣(Tomcat)을 기본 컨테이너로 내장

spring-boot-start web-jetty

기본 내장 서블릿 컨테이너로 제티(Jetty)를 프로젝트에 포함 

spring-boot-start web-undertow

 기본 내장 서블릿 컨테이너로 언더토우(Undertow)를 프로젝트에 포함 

spring-boot-start web-tomcat

내장 서블릿 컨테이너로 톰캣을 프로젝트에 포함. spring-boot-starter-web에 사용되는 기본 서블릿 컨테이너 

spring-boot-start web-actuator

애플리케이션 모니터링 및 관리 기능을 제공하는 스프링 부트 액추에이터 프로젝트를 포함 

spring-boot-start web-jdbc 

Tomcat Connection Pool을 포함하는 스프링 JDBC를 포함. 특정 데이터베이스의 드라이버는 직접 제공해야 함

spring-boot-start web-data-jpa

JPA 또는 Hibernate를 이용해 관계형 데이터베이스에 상호작용하기 위해 필요한 모든 아티팩트를 포함 

spring-boot-start web-data-mongodb

몽고디비와 상호작용하고 로컬 호스트의 몽고에 대한 클라이언트 연결을 초기화하기 위한 모든 아티팩트를 포함

spring-boot-start web-data-security

프로젝트에 스프링 시큐리티를 포함하고 애플리케이션에 기본 시큐리티를 활성화 

spring-boot-start web-data-test 

JUnit, Hamcrest, Mockito 와 같은 라이브러리를 활용한 단위 테스트의 생성을 허가

spring-boot-start web-data-amqp 

스프링 AMQP를 프로젝트에 추가하고 기본 AMQP 브로커로서 래빗엠큐를 시작


스프링 프레임워크의 표준 Configuration과 스프링 부트의 주요 차이점

웹 애플리케이션의 경우,
    • 스프링 부트를 사용하면 웹 컨테이너를 애플리케이션에 포함할 수 있다.
      • 애플리케이션 속에 있는 웹 컨테이너
    • 표준 스프링 Configuration 을 사용하면 애플리케이션을 WAR 형태로 웹 컨테이너에 배포한다.
      • 웹 컨테이너에 속해 있는 애플리케이션(WAR)


마이크로서비스 소개


마이크로서비스 장점

  1. 확장성, 유연성, 독립적 배포
  2. 상대적으로 작은 프로젝트의 규모
    • 새로운 개발자가 이해하기 쉽다.
    • 하나의 비즈니스 영역만 구현하기 때문에 코드 변경 시 영향도가 적다.
    • 코드의 품질을 유지하기 쉽다.

스프링 프레임워크로 마이크로서비스 만들기

스프링 프레임워크를 사용하는 이유

    • Service Registry, Configuration Server, Circuit Breaker, Cloud Bus, Oauth2 패턴, API 게이트웨이 같은 검증된 패턴을 구현한다.
    • 강력한 커뮤니티를 기반으로 유지가 잘 되고 있다.
    • 많이 사용되고 있는 프레임워크다.
    • 문서화가 잘 되어 있고 많은 예제들을 쉽게 찾을 수 있다.

클라우드 개발의 장점

    • 확장성과 신뢰성, 낮은 유지보수 비용
    • 유연한 확장, 변하지 않는 배포, 폐기 가능한 인스턴스
    • 즉 시간과 비용 절감

모놀리식(전통적) 개발 방식의 단점

    • 코드 베이스 증가
    • 수정과 유지보수 복잡
    • 새로운 기능, 프레임워크, 기술 적용의 어려움
    • 새로운 아이디어를 적용하고 혁신하는데 부정적인 영향을 미침
"네이티브 클라우드 애플리케이션이란 단순히 클라우드로 이전한 프로그램이 아니라 클라우드 환경을 위해 잘 설계된 프로그램이다."


마이크로서비스 아키텍처 배우기

API 게이트웨이

    • 특정 서비스 호출 숨김(복잡도 숨김)
    • 동적 라우팅
    • 시스템으로의 진입점으로 다음과 같은 일을 한다.
      • 중요한 데이터 추적
      • 요청 메트릭 수집
      • 통계
      • 어플리케이션에서 사용될 부가 정보 삽입을 위한 요청 및 응답 헤더 조작

서비스 디스커버리의 필요성 이해하기

서비스 디스커버리

    • 필수 메커니즘
    • 컴퓨터 네트워크상의 디바이스가 제공하는 디바이스와 서비스를 자동으로 감지하는 서비스
    • 모든 서비스는 시작 후 다른 중앙 장소에 자신을 등록
    • 등록 키는 전체 시스템에서 유일한 서비스이거나 유일한 식별자여야 한다.
      • 이름을 통해 서비스를 찾고 호출하기 위함
      • 모든 개별 키에는 할당된 값이 있다.(서비스의 네트워크 위치를 나타냄)
      • 하나의 키에 같은 서비스의 여러 인스턴스가 등록될 수 있다.
    • 서비스는 특정 디스커버리 서버에 등록된 다른 서비스의 전체 목록을 얻는다.
    • 등록 목록의 변경 사항을 알기 위해 마이크로서비스가 원격 디스커버리 서버에 의해 이전에 수집된 컨피규레이션을 주기적으로 갱신한다.

서비스 디스커버리를 서버 컨피규레이션 기능과 함께 사용 가능


서비스 간 통신

시스템의 신뢰성 보장을 위해 최소 두 개의 인스턴스를 실행하는 것이 좋다.

    • 최적의 성능을 위해 인스턴스의 수는 적게 유지한다.
    • 부하 분산기는 대개 API 게이트웨이에 내장되어 있다.
    • 분산기는 디스커버리 서버에 등록된 인스턴스 목록을 가져와야 한다.
    • 라운드 로빈 규칙을 적용하여 트래픽을 50/50으로 분배한다.
    • 같은 규칙으로 마이크로서비스 측의 부하 분산기에 적용된다.

장애와 서킷 브레이커

    • 장시간 응답을 기다려서 스레드를 점유하게 하는 대신 에러 응답을 보내는 것이 좋다.

서킷 브레이크 패턴

    • 성공 및 실패 요청의 횟수를 센다.
    • 에러의 비율이 가정된 임계치를 넘으면 차단이 발생하고 이후의 시도는 즉시 실패한다.
    • 지정된 기간이 지난 후 다시 요청을 다시 시작하고 성공하면 서킷을 닫고 정성화 시킨다.
    • 다수의 인스턴스 중에 일부가 다른 것보다 느리게 동작하면 해당 인스턴스는 무시된다.

폴백(fallback)

    • 요청이 실패했을 때 수행되는 로직
    • 캐싱된 데이터나 기본값, 빈 결과 목록을 반환할 수 있다.
    • 캐싱된 데이터나 기본값을 반환하는 것보다 에러 코드를 전파하는 것이 확실하고 좋은 방법이다.


MSA(microservice architecture)에 대해서 아는 것이 별로 없었다.

개발 블로그에서, 개발 커뮤니티에서, 개발자들의 대화 내용 속에서...

이런 경로를 통해 보고 들은것이 전부다.

그런데 최근들어 이런 글과 이야기를 듣게되는 빈도수가 높아졌다.


그러다보니 자연스럽게(불안감에) 알아보게 되었는데, 

지금 회사에서 앞으로 꼭 필요한 내용이었다.


그리하여 회사 동료들과 함께 스터디를 하기로 하였다.

'마스터링 스프링 클라우드' 라는 책의 내용을 각자 정리 하기로 했고,

매 주 발표자를 선정해 진행하는 방식으로 정했다.


발표자는 스터디를 이끌어 가는 것이지 내용을 학습해서 다른 사람들에게 가르치는 것은 아니다.

발표자가 아닌 참석자들은 본인이 이해하지 못했던 내용이나 추가적으로 더 알고 있는 내용을 공유하고

그 이야기를 가지고 토론이나 정보 공유를 하는 진행 방식이다.


최대 목표는 회사 제품에 적용하기

최소 목표는 MSA에 대한 학습 및 지식 공유이다.



책의 목차를 쭉 훑어 보았는데, 

이 책을 완벽하게 이해하기 위해서 필요한 기반지식들이 꽤 있다는 것을 알 수 있었다.

반대로 이야기 하면 이책을 완벽히 이해 했다면

이 책에 나와 있는 기반지식들을 다 알고 있다는 이야기이다.

조금은 부담스러울 수 있지만 신입 개발자에게 많은 도움이 될 것 같다.


잘 정리 해야겠다.


'팀보다 위대한 선수는 없다.'

'위대한 팀에는 위대한 선수들이 많다. 내가 그 선수이고 싶다.'


'케넨 > 멍멍이' 카테고리의 다른 글

말을 알아듣는 기언  (1) 2019.01.25
애국견  (0) 2019.01.23
약육강식  (1) 2019.01.23

+ Recent posts