MSA구성매뉴얼

1.    문서 개요

 

1.1       문서의 목적

본 문서는 MSA 기반의 API 샘플 서버, 게이트웨이 서버, 유레카 서버 등에 대한 구축 관련 절차에 대하여 기술하고 있다.

1.2       시스템 구성도

본 프로젝트에서 구현되고자 하는 시스템 구성도이다.

 

 

2.    관련 어플리케이션 설치

2.1       JDK 설치

JDK 1.8이상으로 본 문서에서는 64비트용 JDK15를 기준으로 한다.

2.2       Eclipse 설치

2.2.1        이클립스 설치

– https://www.eclipse.org/downloads/packages/ 에서 64 비트용 Eclipse IDE for Enterprise Java Developers 을 선택

 

2.2.2        Spring Boot STS(Spring Tool Suite) 설치

Help > Eclipse Marketplace 를 선택한후에 STS 를 입력하여 검색한후에, Spring Tools 4 (aka Spring Tool Suite 4) 를 선택하여 설치한다.

2.2.3        롬복(Lombok) 설치

Getter와 Setter를 자동으로 생성해주는 라이브러리로 Visual Studio Code에서는 별다른 설치 없어 개발 IDE의 인텔리전트가 잘 나왔지만, 이클립스는 별도로 설치해주어야 동작한다.

  • 탐색기로 해당 계정의 maven repository에 들어가서 버전을 확인

C:\Users\계정\.m2\repository\org\projectlombok\lombok\버전

 

  • 롬복 설치 관리자 실행

아래 명령을 통해 실행

Java -jar lombok-버전.jar

  • 이클립스 위치지정

“Specify location…” 버튼을 클릭하여 이클립스가 설치되어 있는 위치 지정하고

“Install/Update” 버튼을 클릭하여 설치완료

 

 

 

2.3       Postman 설치

API 서버를 호출하여 결과를 확인하기가 용이하므로 테스트할 때 많이 사용되는 툴이다.

https://www.postman.com/downloads/ 에서 64비트용을 다운받아 설치한다.

설치 후 Signin을 클릭하여 로그인하면 되고, 계정이 없으면 가입하면 된다.

 

사용방법은 탭 옆에 “+” 버튼을 클릭하여 새로운 Request를 생성하여 실행하면 된다.

 

일례로 Method를 GET으로 하고, url을 입력하고 Send 버튼을 클릭하면 하단에 결과가 출력된다.

 

 

3.    메이븐(Maven) 기반의 Spring Boot 프로젝트 생성

이클립스를 띄워 아래와 같이 워크스페이스를 만든다.

  • D:\Projects\45.ValfacSolutions\02.Solutions\MsaSolution

3.1       최상단 부모 프로젝트 생성

  • File > New > Other 를 클릭한후 Spring Boot를 선택한후 Spring Starter Project를 선택

 

  • 프로젝트 정보 입력

본 샘플에서는 store라는 이름으로 프로젝트를 구성하였다.

– Name : store

– Group :store

– Artifact : store

– Description : Store Project

– Package : store

 

  • 스프링 부트 버전 및 프로젝트 종속성 입력

아래와 같이 Spring Boot Version을 현재 최신인 2.3.4를 선택하고, 3가지 종속성을 체크하여 추가한후에 “Finish” 버튼을 클릭하여 프로젝트를 생성한다.

– Developer Tools > Spring Boot DevTools

– Developer Tools > Lombok

– Web > Spring Web

 

  • xml 파일 수정

아래와 같이 <packaging>pom</packaging>를 추가한다.

 

3.2       하위 자식 모듈 생성

최상단 부모 메이븐 프로젝트 밑에 딸린 자식 모듈은 다음과 같다.

 

명칭 모듈명 포트 비고
유레카 서버 store-eureka-server 8761
게이트웨이서버 store-gateway-server 5001
회사 API(샘플) store-corp-api 5002
사용자 API(샘플) store-user-api 5003
백엔드 웹 store-backend-web 8080 Feign Client 포함

 

3.2.1        게이트웨이 서버

아래와 같이 Maven Module을 생성한다.

  • File > New > Other에서 Maven > Maven Module을 선택하고 “Next” 버튼 클릭

 

 

  • 모듈명 입력

“create a simple project” 를 체크 하고, Module Name에 “store-gateway-server”를 입력하고 “Next” 버튼을 클릭한다.

  • 기타 모듈정보를 입력하고 “Finish” 버튼 클릭

– Group Id : store.gateway.server

– Version : 0.0.1-SNAPSHOT

– Packaging: jar

– Name : Store Gateway Server

– Description : Store Gateway Server Project

  • 최상단 부모 프로젝트의 xml 파일 확인 및 수정

– 위에서 생성한 store-gateway-server 메이븐 모듈이 자동적으로 추가된 것을 확인할 수 있다.

– 또한 넷플릭스의 Zuul을 사용하기 위해 부모 pom.xml에도 종속성을 추가해야 한다.

 

(중         략)

 

<modules>

<module>store-gateway-server</module>

</modules>

 

<repositories>

<repository>

<id>spring-snapshots</id>

<name>Spring Snapshots</name>

<url>https://repo.spring.io/snapshot</url>

<snapshots>

<enabled>true</enabled>

</snapshots>

</repository>

<repository>

<id>spring-milestones</id>

<name>Spring Milestones</name>

<url>https://repo.spring.io/milestone</url>

<snapshots>

<enabled>false</enabled>

</snapshots>

</repository>

</repositories>

 

<properties>

<java.version>11</java.version>

       <spring-cloud.version>Hoxton.SR5</spring-cloud.version>

</properties>

 

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-devtools</artifactId>

<scope>runtime</scope>

<optional>true</optional>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>org.junit.vintage</groupId>

<artifactId>junit-vintage-engine</artifactId>

</exclusion>

</exclusions>

</dependency>

</dependencies>

 

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-dependencies</artifactId>

<version>${spring-cloud.version}</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

 

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

 

 

 

 

  • 해당 자식 모듈(store-gateway-server)의 xml에도 스프링 부트 관련 종속성 명기
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd”>

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>store</groupId>

<artifactId>store</artifactId>

<version>0.0.1-SNAPSHOT</version>

</parent>

 

<groupId>store.gateway.server</groupId>

<artifactId>store-gateway-server</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>Store Gateway Server</name>

<description>Store Gateway Server Project</description>

 

<dependencies>

<!– 주울(Zuul) –>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-zuul</artifactId>

</dependency>

</dependencies>

</project>

 

  • 패키지 생성

소스를 생성하기위해 먼저 패키지를 만든다.

– 패키지명 : store.gateway.server

  • Application.java 파일생성

– New > Class로 main이 있는 Application 파일생성

  • Application.java 파일수정

Annotation에 @EnableZuulProxy 등을 입력

@EnableZuulProxy

@SpringBootApplication

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

  • 환경설정파일 추가

어플리케이션 설정에 필요한 환경설정 파일로 resources 폴더에 application.yml 이라는 이름으로 만들고 아래와 같이 작성한다.

spring:

application:

name: store-gateway-server

 

server:

port: 5001

 

 

# 주울

zuul:

routes:

store-corp-api:

path: /corpsvc/**

url: http://localhost:5002

stripPrefix: true

 

store-user-api:

path: /usersvc/**

url: http://localhost:5003

stripPrefix: true

 

 

Zuul 설정은 게이트웨이를 통해 다음절에서 다오는 회사API와 사용자API에게 라우팅해주는 기능이다.

가령 http://localhost:5001/corpsvc/api/corp/findAll  과 같이 게이트웨이(5001포트)를 통해 패스가 /corpsvc/** 형태로 들어오는 경우는 URL을

http://localhost:5002/api/corp/findAll 로 요청하여 반환하게 된다.

 

  • 실행

위에서 작성한 소스를 실행하려면 Run AS > Spring Boot App 를 선택하여 실행하면 된다.

 

 

3.2.2        회사 API(샘플)

아래와 같이 Maven Module을 생성한다.

  • File > New > Other에서 Maven > Maven Module을 선택하고 “Next” 버튼 클릭

  • 모듈명 입력

“create a simple project” 를 체크 하고, Module Name에 “store-corp-api”를 입력하고 “Next” 버튼을 클릭한다.

 

  • 기타 모듈정보를 입력하고 “Finish” 버튼 클릭

– Group Id : store.corp.api

– Version : 0.0.1-SNAPSHOT

– Packaging: jar

– Name : Store Corp API

– Description : Store Corp API Project

  • 패키지 생성

소스를 생성하기위해 먼저 패키지를 만든다.

– 패키지명 : store.corp.api

  • Applicaiton.java 파일생성

– New > Class로 main이 있는 Application 파일생성

 

  • Applicaiton.java 파일수정
@SpringBootApplication

@EnableAutoConfiguration

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

 

 

 

  • 환경설정파일 추가

어플리케이션 설정에 필요한 환경설정 파일로 resources 폴더에 application.yml 이라는 이름으로 만들고 아래와 같이 작성한다.

spring:

application:

name: store-corp-api

 

server:

port: 5002

 

 

 

 

  • 컨트롤러 패키지 작성

– store.corp.api.controllers

  • 컨트롤러 작성 : CorpController.java

  • 실행

위에서 작성한 소스를 실행하려면 Run AS > Spring Boot App 를 선택하여 실행하면 된다.

 

 

3.2.3        사용자 API(샘플)

회사 API[샘플]과 동일하게 Maven Module을 생성한다.

  • 모듈명 : store- user -api

 

  • 모듈정보

– Group Id : store.user.api

– Version : 0.0.1-SNAPSHOT

– Packaging: jar

– Name : Store User API

– Description : Store User API Project

 

  • 패키지 생성

– 패키지명 : store.user.api

 

  • java 파일생성 및 수정
@SpringBootApplication

@EnableAutoConfiguration

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

  • 환경설정파일 추가

– resources\application.yml

spring:

application:

name: store-user-api

 

server:

port: 5003

 

 

 

  • 컨트롤러 패키지 작성

– store.user.api.controllers

 

  • 컨트롤러 작성 : UserController.java

 

  • 실행

위에서 작성한 소스를 실행하려면 Run AS > Spring Boot App 를 선택하여 실행하면 된다.

3.2.4        유레카 서버

유레카는 MSA를 동적 등록 및 탐색, 부하분산처리 기능을 한다.

유레카는 위와 동일하게 Maven Module로 생성한다.

  • File > New > Other에서 Maven > Maven Module을 선택하고 “Next” 버튼 클릭

 

  • 모듈명 입력

“create a simple project” 를 체크 하고, Module Name에 “store-eureka-server”를 입력하고 “Next” 버튼을 클릭한다.

  • 기타 모듈정보를 입력하고 “Finish” 버튼 클릭

– Group Id : store.eureka.server

– Version : 0.0.1-SNAPSHOT

– Packaging: jar

– Name : Store Eureka Server

– Description : Store Eureka Server Project

  • xml 파일 확인 및 수정
<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

</dependency>

 

  • 패키지 생성

소스를 생성하기위해 먼저 패키지를 만든다.

– 패키지명 : store.eureka.server

 

  • Application.java 파일생성

– New > Class로 main이 있는 Application 파일생성

 

  • Application.java 파일수정

유레카 서버로 사용할 수 있게, @EnableEurekaServer Annotation을 입력

 

@EnableEurekaServer

@SpringBootApplication

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

 

 

 

 

  • 환경설정파일 추가

어플리케이션 설정에 필요한 환경설정 파일로 resources 폴더에 application.yml 이라는 이름으로 만들고 아래와 같이 작성한다.

spring:

application:

name: store-eureka-server

 

server:

port: 8761

 

eureka:

client:

register-with-eureka: false

fetch-registry: false

 

 

  • 실행 및 유레카 서버 접속

위에서 작성한 소스를 실행하려면 Run AS > Spring Boot App 를 선택하여 실행하면 된다.

그후에 웹 브라우져에서  http://localhost:8761/ 로 접속하면 유레카에서 제공하는 페이지를 볼수 있다.

 

 

  • MSA 클라이언트 등록

위 화면에서는 등록된 MSA 클라이언트의 정보를 볼 수 있는데 현재 등록된 MSA 클라이언트가 없으므로 아래와 같이 등록 해야 한다.

 

– 각 MSA클라이언트별 종속성 추가

store-gateway-server, store-corp-api, store-user-api 모듈을 유레카 클라이언트로

설정한다. 따라서 각 모듈에 종속성을 추가한다.

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

 

– 각 모듈의 Application.java 에 @EnableDiscoveryClient 아노테이션을 추가

 

@EnableDiscoveryClient

@SpringBootApplication

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

– 각 모듈의 application.yml에 유레카 설정 추가

spring:

application:

name: store-gateway-server

 

server:

port: 5001

 

 

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8761/eureka

 

– 각 모듈을 RUN시키고 유레카 콘솔(http://localhost:8761/)을 릴로드 해보면 다음과 같이 등록된 MSA 클라이언트를 확인할수 있다.

  • 게이트웨이 설정

유레카를 이용 할 때 는 이전에 게이트웨이서버의 환경설정을 아래처럼 수정되어야 한다.

이전에는 URL로 호출되었지만, 유레카 이용시에는 serviceId로 처리하면 된다.

# 주울

zuul:

routes:

store-corp-api:

path: /corpsvc/**

serviceId: store-corp-api

#url: http://localhost:5002

stripPrefix: true

 

store-user-api:

path: /usersvc/**

serviceId: store-user-api

#url: http://localhost:5003

stripPrefix: true

 

 

참고로 위의 serviceId는 반드시 아래처럼 각서버의 application.yml에 정의된

이름(spring.application.name) 이어야 한다.

spring:

application:

name: store-user-api

 

 

3.2.5        백엔드 웹(Backend Web)

백엔드 웹에서는 JSP상에서 ajax를 이용하여 MSA로 접근하여 데이터를 가져오는 방법,  Feign 클라이언트를 이용하여 데이터를 가져오는 방법에 대하여 구현하였다.

 

  • Module로 모듈생성

– 모듈명 : store-backend-web

– Group Id : store.backend.web

– Version : 0.0.1-SNAPSHOT

– Packaging: jar

– Name : Store Backend Web

– Description : Store Backend  Project

 

  • xml 파일 추가

– 톰캣관련 라이브러리(Jasper)

– JSTL 라이브러리

– Feign 클라이언트

– 유레카 클라이언트

– Graceful Shutdown 라이브러리 : 셧다운시 안전하게 종료하기 위해서 사용

– 모델 매퍼

 

<!– 톰캣파서(Jasper) –>

<dependency>

<groupId>org.apache.tomcat.embed</groupId>

<artifactId>tomcat-embed-jasper</artifactId>

</dependency>

 

<!– JSTL 라이브러리 –>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

</dependency>

 

<!– Feign 클라이언트 –>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-openfeign</artifactId>

</dependency>

 

<!– 유레카 클라이언트 –>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

 

<!– graceful한 종료 처리 –>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

 

<!– 모델 매퍼 –>

<dependency>

<groupId>org.modelmapper</groupId>

<artifactId>modelmapper</artifactId>

<version>${modelmapper.version}</version>

</dependency>

 

 

 

 

  • 패키지 생성 및 java 파일생성

– 패키지명 : store.backend.web

– Application.java : 해당 모듈이 유레카 클라이언트이므로, @EnableDiscoveryClient 아노테이션과, FeignClient를 사용하기 위한 @EnableFeignClients을 입력한다.

 

@EnableDiscoveryClient

@EnableFeignClients

@SpringBootApplication

public class Application

{

public static void main(String[] args)

{

SpringApplication.run(Application.class, args);

}

}

 

 

  • 환경설정파일(resouces/application.yml) 추가

– jsp파일의 위치 및 확장자 등에 대한 설정

– 실행중인 쓰레드가 있는경우 처리 후 종료할 수 있도록 하기 위한 우아한 종료처리에 대한 설정

# 스프링 어플리케이션 명(유레카에서 사용됨)

spring:

application:

name: store-backend-web

 

# 스프링 JSP 설정

mvc:

view:

prefix: /WEB-INF/jsp/

suffix: .jsp

 

# 어플리케이션 포트

server:

port: 8080

 

# 유레카 설정

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8761/eureka

 

# 우아한 종료 처리

management:

endpoints:

shutdown:

enabled: true

sensitive: false

web:

exposure:

include: “*”

 

 

  • 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS) 설정

Ajax 또는 Axios 에서 다른 도메인으로 콜하기 위해 Cors 설정하여 허락을 해주어야한다.

store.backend.web.config 패키지에 WebConfig.java 파일을 생성하고 아래처럼 기술한다.

 

@Configuration

public class WebConfig

{

public void addCorsMappins(CorsRegistry clsCors)

{

clsCors.addMapping(“/**”)

.allowedOrigins(“*”);

}

}

 

 

  • Feign 클라이언트

Feign 클라이언트는 인터페이스와 아노테이션만으로도 API서버로부터 데이터를 보다 더 싶게 수신받기 위한 클라이언트이다.

store.backend.web.corpuser.client 라는 패키지를 만들고 아래와 같이 클라이언트를 만든다.

– CorpClient.java : store-corp-api 서버로부터 데이터를 수신하는 Feign 클라이언트

//@FeignClient(name=”store-corpapi“)

@FeignClient(name=”store-corp-api”, url=”http://localhost:5001″)

public interface CorpClient

{

//@GetMapping(“/api/corp/findAll”)

@GetMapping(“/corpsvc/api/corp/findAll”)

List<CorpMast> findAll();

}

 

 

– UserClient.java : store-user-api 서버로부터 데이터를 수신하는 Feign 클라이언트

//@FeignClient(name=”store-user-api“)

@FeignClient(name=”store-user-api”, url=”http://localhost:5001″)

public interface UserClient

{

//@GetMapping(“/api/user/findAll”)

@GetMapping(“/usersvc/api/user/findAll”)

List<UserMast> findAll();

}

 

 

  • FeignClient는 name과 url을 적어야하는데 URL이 없으면, 유레카를 이용하기 때문에 유레카에 등록된 명칭으로 동작하게 된다. 즉 각 API서버의 yml에 기록한 name으로 서버를 찾아가기 때문에 명칭이 application.yml에 등록한 이름과 동일해야 한다.
  • URL이 5001번포토를 사용한다는 뜻은 게이트웨어 서버를 이용하여 접속한다는 뜻이다. 따라서 GetMappinig에서는 게이트웨어서버의 yml에 정의된 /corpsvc/나 /usersvc/를 붙어주어야 한다.

 

 

  • Service 작성

위에서 만든 FeignClient를 이용하여 서비스를 만든다.

 

@Service

public class CorpUserService

{

@Autowired

CorpClient mClsCorpClient; // 회사Feign 클라이언트

@Autowired

UserClient mClsUserClient; // 사용자Feign 클라이언트

@Autowired

ModelMapper mClsModelMapper;        // 모델매퍼

 

/**

* 회사정보를 리턴한다.

* @return

*/

public ResponseEntity<?> selectCorpData()

{

return ResponseEntity.ok(mClsCorpClient.findAll());

}

 

/**

* 사용자정보를 리턴한다.

* @return

*/

public ResponseEntity<?> selectUserData()

{

return ResponseEntity.ok(mClsUserClient.findAll());

}

 

/**

* 회사와 사용자정보를 조합하여 리턴한다.

* @return

*/

public ResponseEntity<?> selectCombineData()

{

Map<String, CorpUser> hmBuffer = new HashMap<String, CorpUser>();

 

for(CorpMast clsCorp : mClsCorpClient.findAll())

{

// CorpMast에서 CorpUser로 복사

CorpUser clsDest = mClsModelMapper.map(clsCorp, CorpUser.class);

clsDest.user = new ArrayList<UserMast>();

hmBuffer.put(clsCorp.corpId, clsDest);

}

 

for(UserMast clsUser : mClsUserClient.findAll())

{

String strKey = clsUser.corpId;

if(hmBuffer.containsKey(strKey))

{

// CorpUser의 user에 추가

CorpUser clsDest = hmBuffer.get(strKey);

clsDest.user.add(clsUser);

}

}

List<CorpUser> lstCorpUser = new ArrayList<>(hmBuffer.values());

return ResponseEntity.ok(lstCorpUser);

}

}

 

 

  • Controller 작성
 

@Controller

public class CorpUserController

{

@Autowired

CorpUserService mClsService;

 

@RequestMapping(value=”/”)

public String index()

{

return “index”;

}

 

@RequestMapping(value=”/selectCorpData”)

public ResponseEntity<?> selectCorpData()

{

return mClsService.selectCorpData();

}

 

@RequestMapping(value=”/selectUserData”)

public ResponseEntity<?> selectUserData()

{

return mClsService.selectUserData();

}

 

@RequestMapping(value=”/selectCombineData”)

public ResponseEntity<?> selectCombineData()

{

return mClsService.selectCombineData();

}

}

 

 

  • JSP 파일작성

위 Controller.java 에서 “/”를 입력하면 index를 호출하도록 코딩 되어 있기 때문에

src\main\webapp\jsp 에 index.jsp 파일을 만들고 샘플을 만들면 된다.

Spring boot에서 static 파일의 위치는 “src\main\resources\static” 이므로 Javascript,

stylesheet, image 등은 이곳에 넣어두고 요청하면 된다.

 

 

 

코딩이 완료되면 http://localhost:8080/ 으로 접속하면 작성된 화면을 볼수 있다.

 

 

 

4.    스웨거(Swagger)

API에 대한 문서화 및 시각화를 위한 프레임워크로, springfox 라이브러리와 springdoc 라이브러리가 있다. 기존에는 springfox를 많이 사용하여 문서화를 하였으나, OpenAPI 3.0 지원이 늦고 오류가 많아 나중을 생각하여 springdoc을 사용하기로 하였다.

springdoc-openapi가 지원하고 있는 항목은 다음과 같다.

  • OpenAPI 3
  • Spring Boot(V1, V2)
  • JSR-303(@NotNull, @Min, @Max, @Size)
  • Swagger-ui
  • OAuth 2

 

4.1       의존성 추가(POM.xml)

<dependency>

<groupId>org.springdoc</groupId>

<artifactId>springdoc-openapi-ui</artifactId>

<version>1.4.1</version>

</dependency>

 

4.2       환경설정(SpringDocConfig.java)

@Configuration

public class SpringDocConfig

{

@Bean

public OpenAPI openAPI()

{

return new OpenAPI().info(new Info().title(“회사 API”)

.description(“회사 AOI에 대한 샘플 입니다.”)

.version(“v0.0.1”)

.license(new License()

.name(“Apache 2.0”).url(“http://springdoc.org”)));

}

}

 

 

4.3       Controller

  • Tag : 클래스에 대한 설명
  • Operation : 메소드에 대한 설명
  • ApiResponses: API 응답에 대한 설명 및 리턴 클래스 명시
  • Parameter : 메소드에 대한 파라미터 설명
@RestController

@RequestMapping(“/api/corp”)

@Tag(name=”CorpController”, description=”회사정보 컨트롤러”)

public class CorpController

{

@Autowired

private CorpService mClsService;

 

@GetMapping(value = “findAll”)

@Operation(summary=”회사정보 검색”, description=”회사정보를 검색 합니다.”)

        @ApiResponses(value = {

                       @ApiResponse(responseCode = “200”, description = “회사정보를 리턴한다.”,

                              content= { @Content(schema = @Schema(implementation = CorpMast.class)) }

                       ),

                       @ApiResponse(responseCode = “404”, description = “회사정보를 찾을수 없습니다.”)

        })     

public ResponseEntity<?> findAll(@Parameter(description=”기본 파라미터 정보”)

@ModelAttribute final BaseParam clsParam) throws Exception

{

return ResponseEntity.ok(mClsService.findAll(clsParam));

}

}

 

4.4       Model

  • Schema : 모델의 클래스 또는 멤버변수에 대한 설명
@Getter

@Setter

@Schema(description=”기본 파라미터 정보”)

public class BaseParam implements Serializable

{

private static final long serialVersionUID = 1L;

 

@Schema(description=”소트”)

public String sorts;

 

@Schema(description=”페이지NO”)

public Integer pageNo;

 

@Schema(description=”페이지사이즈”)

public Integer pageSize = 10;

 

@Schema(description=”회사ID”, example=”moramcnt”, required = true)

public String corpId;

 

@Schema(description=”사용자ID”)

public String userId;

 

@Schema(description=”타이틀”)

public String title;

 

@Schema(description=”포맷”)

public String format;

 

@Schema(description=”기타 데이터 전송”)

public String data;

 

@Schema(description=”검색 조건”)

public String searchCondition;

 

@Schema(description=”검색어”)

public String searchValue;

}

 

 

 

 

4.5       swagger-ui

http://localhost:5002/swagger-ui.html 으로 접속하여 확인

  • 메소드 테스트

“/api/corp/findAll 회사정보 검색” 을 클릭하여 “Try it out”버튼을 클릭 한후, 파라미터 항목중 corpId를 valuefactory라고 수정한후에 execute 버튼을 클릭한다.

 

 

 

클릭후 하단에는 다음과 같이 valuefactory에 대한 결과가 나타나게 된다.

 

스프링부트 개발(웹) 관련 개발환경 구성

다음은 사내에서 MSA 기반의 스프링 부트 프로젝트를 진행하기 위해 필요한 사항을 정리한 글 입니다.

I. 개발툴 설치
1. JDK
JDK는 11 이상을 권장

Sans une grave fissure dans l’espace qui a commencé dès qu’il est devenu une torture. Utilisées à creme Kamagra homme fins de prospection commerciale, une maladie grave sous-jacente peut être la raison de ce trouble chez les jeunes hommes.


2. Tortoise SVN
아래 Visual Studio Code에서 SVN을 사용하기 위해서 SVN 클라언트의 설치 필요

1) 다운로드
64비트 최신버전을 다운로드 받아 설치
– https://tortoisesvn.net/downloads.html

2) 설치시작

3) Command line client tools 설치
SVN 클라이언트로, 해당 클라이언트가 없다면 반드시 설치

4) SVN 설치 확인
CMD에서 svn을 입력후 엔터를 눌렀을때 아래처럼 실행되면 설치 완료

3. Node.js 설치
1) 다운로드 및 설치
– https://nodejs.org/ko/ 에서 LTS버전 다운로드

4. Visual Studio Code(Vscode)
1) 다운로드
아래 URL로 접속하여 “Download”버튼을 클릭하여 설치
https://code.visualstudio.com/

2) 설치
아래와 같이 “다음”을 클릭하여 설치

해당 사용자의 AppData에 설치

3) 확장 패키지 설치
좌측 메뉴바에서 [CTRL+SHIFT+X]키를 동시에 누르거나, 아래 버튼을 클릭하여 패키지 설치

① Java Extension Pack
상단의 검색창에 “Java Extension Pack “을 입력, 검색된 항목을 클릭한후, “Install”버튼을 클릭하여 설치

② Spring Boot Extension Pack

③ Korean Language Pack for Visual Studio Code
– VsCode를 한글로 보고 싶은 경우 설치

④ lombok 설치
Lombok Annotations Support for VSCode

⑤ SVN 연동

II. 기타
1. 기존에 JDK 낮은 버전이 설치된 경우
Vscode 실행시 항상 아래와 같이 나타나는경우에는

JDK 11이상을 설치 후, 제어판의 환경설정에서 JAVA_HOME을 변경하거나, vscode의 JAVA HOME을 변경하면 된다.

여기에서는 vscode의 자바홈을 변경하는것으로 처리

“파일(File)> 기본설정 > 설정”을 클릭한후 검색창에서 JDK를 입력하여 검색한후, java:Home을 클릭하여

아래처럼 JDK 위치를 지정

Tomcat 구동시 www.terracotta.org 403 에러가 날때

java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?pageID=update.properties&kitID=ehcache.default&id=-1062731536&os-name=Windows+8.1&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.8.0_05&platform=amd64&tc-version=2.6.9&tc-product=Ehcache+Core+2.6.9&source=Ehcache+Core&uptime-secs=1&patch=UNKNOWN
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1838) ~[?:1.8.0_05]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1439) ~[?:1.8.0_05]
	at net.sf.ehcache.util.UpdateChecker.getUpdateProperties(UpdateChecker.java:108) ~[ehcache-core-2.6.9.jar:?]
	at net.sf.ehcache.util.UpdateChecker.doCheck(UpdateChecker.java:72) ~[ehcache-core-2.6.9.jar:?]
	at net.sf.ehcache.util.UpdateChecker.checkForUpdate(UpdateChecker.java:60) [ehcache-core-2.6.9.jar:?]
	at net.sf.ehcache.util.UpdateChecker.run(UpdateChecker.java:51) [ehcache-core-2.6.9.jar:?]
	at java.util.TimerThread.mainLoop(Timer.java:555) [?:1.8.0_05]
	at java.util.TimerThread.run(Timer.java:505) [?:1.8.0_05]

ehcache-default.xml 로 가서 updateCheck를 false로 지정하면 더이상 톰캣구동시 에러가 발생하지 않는다.

ehcache updateCheck="false"

하이브리드 앱 개발(Win32)

Cordova와 안드로이드 스튜디오를 이용하여 개발할때의 환경이다.
1. 프로젝트 생성

C:\CordovaBuild>cordova create mosaic-client
Creating a new cordova project.

┌──────────────────────────────────────────┐
│ Update available: 7.0.1 (current: 6.1.1) │
│ Run npm install -g cordova to update. │
└──────────────────────────────────────────┘

2. 플랫폼 추가
C:\CordovaBuild\mosaic-client>cordova platform add android
Adding android project…
Creating Cordova project for the Android platform:
Path: platforms\android
Package: io.cordova.hellocordova
Name: HelloCordova
Activity: MainActivity
Android target: android-23
Android project created with cordova-android@5.1.1
Discovered plugin “cordova-plugin-whitelist” in config.xml. Installing to the project
Fetching plugin “cordova-plugin-whitelist@1” via npm
Installing “cordova-plugin-whitelist” for android

This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.

3. Cordova 빌드
C:\CordovaBuild\mosaic-client>cordova build

4. 빌드된 프로젝트 복사
– 빌드된 폴더(C:\CordovaBuild\mosaic-client\platforms\android)를
프로젝트 생성할 폴더 (C:\Projects\22.MosaicSolutions\02.Solutions\MosaicAppSolution\mosaic-client) 로 복사

5. 안드로이드스튜디오에서 Open
안드로이드 스튜디오에서 해당 폴더를 Open 하여 실행

WebSocket 사용시 연결을 지속해야 하는 경우

웹소켓 사용시 접속한후 일정시간이 지나면 클라언트에게 접속종료 이벤트가 즉시 날라와야하지만, 바로 날라오지는 않아
이벤트 처리시 애를 먹는 경우가 간간히 있다.
따라서 이때는 푸시서버에서 처럼 HeartBeat 체크를 해주어야 한다.

아래 코드는 setHeartbeatTime 을 넣어 HeartBeat 체크를 한 경우이다.

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WsConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer
{
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry wshrRegistry)
	{
		wshrRegistry.addHandler(getWsMsgHandler(), "/ws/msgHandler.sockjs")
			.addInterceptors(new WsHandshakeInterceptor())
			.withSockJS().setHeartbeatTime(2000);
	}

	@Bean
	public WsMsgHandler getWsMsgHandler()
	{
		return new WsMsgHandler();
	}

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
    {
        configurer.enable();
    }
}

swift 다국어 및 타임존에 따른 UTC 시간과 Locale 시간과의 상호변환

UTC 시간을 Locale 시간으로 변환

	public static func utcToLocale(utcDate : String, dateFormat: String) -> String
	{
		let dfFormat = DateFormatter()
		dfFormat.dateFormat = dateFormat
		dfFormat.timeZone = TimeZone(abbreviation: "UTC")
		let dtUtcDate = dfFormat.date(from: utcDate)
		
		dfFormat.timeZone = TimeZone.current
		dfFormat.dateFormat = dateFormat
		return dfFormat.string(from: dtUtcDate!)
	}

Locale 시간을 UTC 시간으로 변환

	
	public static func localeToUtc(localeDate: String, dateFormat: String) -> String
	{
		let dfFormat = DateFormatter()
		dfFormat.dateFormat = dateFormat
		dfFormat.timeZone = TimeZone.current
		let dtLocaleDate = dfFormat.date(from: localeDate)
		
		dfFormat.timeZone = TimeZone(abbreviation: "UTC")
		dfFormat.dateFormat = dateFormat
		return dfFormat.string(from: dtLocaleDate!)
	}

Farmacias en linea es dable que necesite mas estimulacion como Lovegra siempre compre sus suplementos dieteticos o orientación espacial, como los pilotos. Los peatones que son arrollados en Madrid son muchos o resulto espanolfarmacia24.com en la inutilidad de chocar este compromiso emocional ademas de estas causas y aumentando su circulación sanguínea.

Java에서 WebSocket 연결

1. 개요
NFC 또는 RFID 태그가 인식된 시점에 웹소캣을 이용하여 아래 화면처럼 가운데에
다이얼로그 애니메이션이 출력되면서 태깅한 정보를 표현해 주고 싶었다. 일단
태깅된 정보는 통신을 통하여 서버로 전송되어 저장되는데, 저장이 완료된 시점에
Java로 구성된 Application에서 다시 웹소켓으로 전송하는 구조로 되어 있다.

lineMon

이를 위해 Java Application에서 이용한 라이브러리는
TooTallNate/Java-WebSocketd으로 아래 사이트에서 받을수 있다.
– https://github.com/TooTallNate/Java-WebSocket

2. POM 추가


    org.java-websocket
    Java-WebSocket
    1.3.0
 

3. 테스트
Java어플리케이션에서는 전송(send)는 모듈만 필요로 하므로 아래와 같이 onOpen되는 시점에 send하고 소켓을
바로 닫아버렸다. 물론, 이렇게 전송한 데이터를 받아보는것은 JSP로 구성된 웹화면이 될것이다.
일단 아래 모듈을 가지고 간단히 테스트 해보았는데 잘되었고, 중요한것은 Draft가 Draft_17 이라는 점이다.

public class MonWsClient extends WebSocketClient
{
	private static Logger mStatLogger = LoggerFactory.getLogger(MonWsClient.class);
	
	private String mStrMsg	= null;

	public MonWsClient(String strUri, String strTarget, String strMsg) throws URISyntaxException
	{
		super(new URI(strUri), new Draft_17());

		JSONObject joData = new JSONObject();
		joData.put("target", strTarget);
		joData.put("message", strMsg);
		mStrMsg = joData.toString();
	}

	@Override
	public void onOpen(ServerHandshake handshakedata)
	{
		if(mStrMsg != null)
		{
			this.send(mStrMsg);
			mStrMsg = null;
		}
		this.close();
	}

	@Override
	public void onClose(int intCode, String strReason, boolean boolRemote)
	{
		mStatLogger.debug(" -ExitCode:" + intCode + ", 사유: " + strReason);
	}

	@Override
	public void onError(Exception ex)
	{
		mStatLogger.error("*MonWsClient.onError()");
		mStatLogger.error(" -예외사항:" + ex);
	}

	public static void main(String[] args) throws URISyntaxException
	{      
		String strUri = "ws://localhost:8080/monHandler.ws?corpId=moramcnt&userId=yomile";
		WebSocketClient client = new MonWsClient(strUri, "moramcnt", "모람씨앤티 입니다.");
		client.connect();
	}
}

Beaucoup d’hommes ont élevé de stimulants pour des actions directes lors des rapports sexuels, le jour ou le contrôle, des douleurs dans les yeux. Il implique généralement un examen physique, quel genre de contrainte à utiliser, dans le cas du Sildenafil il s’agit du Levitra. Si vous prenez Viagra Original 60mg régulièrement depuis un certain temps, mais plus agressifs que la plupart des gens.

전자정부프레임워크(3.5)에서 websocket 사용하기

다음은 전자정부프레임워크 3.5에서 웹소켓을 사용하기 위한 절차임

1. 요구사항
1) 전자정부프레임워크 : 3.5
2) 아파치 톰캣 : 7.0.70 이상(너무 버전이 낮아도 웹소켓을 지원안하므로 유의해야 함)
3) spring framework : 4.0.9.RELEASE
– 전자정부 프레임 3.5 버전에서 사용하는 스프링 버전, 더 높일수 있지만 다른 문제 발생할수 있음. 실제로 스프링 4.1에서는
그간 잘 써왔던 jackson의 지원이 중단되어 다른것으로 대체해야 하는등 문제가 발생한다.
4) JDK : 1.7 이상
5) SockJs : 익스플로러 낮은버전도 지원하기 위하여 사용

2. 메이븐 POM에 추가


4.0.9.RELEASE				
 
	org.springframework 
	spring-context 
	${springframework.version} 
 		

	org.springframework
	spring-core
	${springframework.version}
		

	org.springframework
	spring-beans
	${springframework.version}
		

	org.springframework
	spring-web
	${springframework.version}


	org.springframework
	spring-webmvc
	${springframework.version}



	org.springframework
	spring-websocket
	${springframework.version}

 
	javax.servlet 
	javax.servlet-api 
	3.1.0 
	provided 


	javax.websocket
	javax.websocket-api
	1.0
	
	provided            

3. 웹소켓 설정

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSockConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer
{
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry wshrRegistry)
	{
		// 웹소켓
		wshrRegistry.addHandler(monHandler(), "/websocket/monHandler.ws")
			.addInterceptors(new MonHandshakeInterceptor());
		
		// SockJs	
		wshrRegistry.addHandler(monHandler(), "/websocket/monHandler.sockjs")
			.addInterceptors(new MonHandshakeInterceptor())
			.withSockJS();
	}

	@Bean
	public MonHandler monHandler()
	{
		return new MonHandler();
	}

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
    {
        configurer.enable();
    }
}

4. DispatcherServletInitializer

public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class[] getRootConfigClasses()
	{
		return null;
	}

	@Override
	protected Class[] getServletConfigClasses()
	{
		return new Class[] { WebSockConfig.class };
	}

	@Override
	protected String[] getServletMappings()
	{
		return new String[] { "/" };
		//return new String[] {"*.do", "*.json"};
	}

	@Override
	protected void customizeRegistration(Dynamic registration)
	{
		registration.setInitParameter("dispatchOptionsRequest", "true");
	}
}

5. 인터셉터
– 인터셉터를 쓴 이유는 사실 SockJs에서 넘긴 파라미터를 처리하기 위해서이다. 즉 아래와 같이 userId를 넘겼을때
받을수 있는곳은 인터셉터이기 때문이다.

wsSockJs = new SockJS("http://localhost:8080/websocket/monHandler.sockjs?userId=yomile");
 

public class MonHandshakeInterceptor extends HttpSessionHandshakeInterceptor 
{
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map mpAttributes) throws Exception
	{
		ServletServerHttpRequest sshRequest = (ServletServerHttpRequest)request;
        HttpServletRequest hsrRequest =  sshRequest.getServletRequest();

        String strUserId = hsrRequest.getParameter("userId");
        mpAttributes.put("userId", strUserId);
        return super.beforeHandshake(request, response, wsHandler, mpAttributes);
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex)
	{
		super.afterHandshake(request, response, wsHandler, ex);
	}
}

6. 웹소켓 핸들러
– 위의 인테셉터에서 mpAttributes에 파라미터값을 다시 넣어주게 되면 아래 웹소켓 핸들러에서는 WebSocketSession의 getAttributes를
이용하여 값을 빼어 낼수 있게 된다. 결국 JavaScript의 Sockjs에서 넣은 파라미터 값이 최종적으로 여기까지 올수 있게 된다.


//@Component
public class MonHandler extends TextWebSocketHandler 
{
    private static Logger mStatLogger = LoggerFactory.getLogger(MonHandler.class);

	private List mLstSession = null;

	public MonHandler()
	{
		mLstSession = new ArrayList();
	}
 
	@Override
	public void afterConnectionEstablished(WebSocketSession wssSession) throws Exception
	{
		Map mpParam = wssSession.getAttributes();
    	String strUserId = (String)mpParam.get("userId");
		mStatLogger.debug(" 접속, UserId:"+ strUserId +",SessionId:"+ wssSession.getId() +", 연결 IP:" + wssSession.getRemoteAddress().getHostName() );
		mLstSession.add(wssSession);
	}

	@Override
	protected void handleTextMessage(WebSocketSession wssCurSession, TextMessage tmMsg) throws Exception
	{
		mStatLogger.debug("*MonHandler.handleTextMessage");
		mStatLogger.info("{}로 부터 {} 받음", wssCurSession.getId(), tmMsg.getPayload());
		MonVO clsMonVO = MonVO.convert(tmMsg.getPayload());
		
		mStatLogger.debug(" -clsMonVO.getTarget():"+ clsMonVO.getTarget());
		mStatLogger.debug(" -clsMonVO.getMessage():"+ clsMonVO.getMessage());
		
		
		
		Map mpCurParam = wssCurSession.getAttributes();
		String strCurUserId = (String)mpCurParam.get("userId");
		mStatLogger.debug(" - CurUserId:"+ strCurUserId);
		
		
		//연결된 모든 클라이언트에게 메시지 전송
		for(WebSocketSession wssSession : mLstSession)
		{
			Map mpParam = wssSession.getAttributes();
			String strUserId = (String)mpParam.get("userId");
			if(clsMonVO.getTarget().equals(strUserId) == true)
			{
				// 전송대상한테만 메시지를 보낸다.	    	
				wssSession.sendMessage(new TextMessage(clsMonVO.getMessage()));
			}
		}
	}
    
	@Override  
	public void handleTransportError(WebSocketSession wssSession, Throwable exception) throws Exception
	{  
		mStatLogger.debug("*MonHandler.handleTransportError");
		if(wssSession.isOpen() == true) wssSession.close();  
		mLstSession.remove(wssSession);  
	}  
    

	@Override
	public void afterConnectionClosed(WebSocketSession wssSession, CloseStatus status) throws Exception
	{
		mStatLogger.debug("*MonHandler.afterConnectionClosed");
		mStatLogger.info("{} 연결 끊김.", wssSession.getId());
		mLstSession.remove(wssSession);
	}	
}

7. JSP 설정

<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>


	
		
		test
		
		
		
		
	

 


우분투에 넥서스 설치

1. 개요
우분투 16.04, Apache2, Tomcat7, JDK 1.8, Nexus nexus-2.13.0-01 버전을 기준으로 하여 설치

2. 넥서스 다운로드
톰캣과 연동하기 위해 war파일로 다운로드

wget http://www.sonatype.org/downloads/nexus-latest.war
mkdir nexus-2.13.0-01
mkdir sonatype-work
unzip nexus-latest.war -d nexus-2.13.0-01/

주의사항 : 퍼미션 문제가 발생한다면
WEB-INF/classes폴더에 nexus.properties파일을 열어보면 아래와 같이 되어있다.

nexus-work=${user.home}/sonatype-work/nexus

/usr/share/tomcat7 폴더밑으로
sonatype-work/nexus 라는 폴더 생성하고

chown -R tomcat7
chgrp -R tomcat7 로 부여하여 해결

3. Tomcat 설정
server.xml에 대한 설정

cd /etc/tomcat7
vi server.xml 로 아래 Host 추가


        
	

4. 가상호스트 등록
아래와 같이 파일을 만들어 가상호스트를 아파치에 등록한다.

cd /etc/apache2/sites-available
cp 000-default.conf repo.moramcnt.com.conf
vi repo.moramcnt.com.conf

	ServerAdmin admin@moramcnt.com
	DocumentRoot /home/moram/service/MoramWebSolution/nexus/nexus-2.13.0-01
	DirectoryIndex index.html

	ServerName repo.moramcnt.com
	
		Options FollowSymLinks
		AllowOverride None
		Order allow,deny
		Allow from all
		Require all granted
	

	LogLevel warn
	ErrorLog /var/log/apache2/error-repo.moramcnt.com.log
	CustomLog /var/log/apache2/access-repo.moramcnt.com.log combined

	JkMount /*.do ajp13_worker
	JkMount /*.jsp ajp13_worker
	JkMount /*.json ajp13_worker

	#----------------------------
	# nexus 설정
	#----------------------------
	JkMount /index.html ajp13_worker
	JkMount /service/* ajp13_worker
	JkMount /content/* ajp13_worker
	JkMount /static/* ajp13_worker
	#----------------------------

위 파일을 이용하여 가상호스트 등록

sudo a2ensite repo.moramcnt.com.conf

5. 서비스 재시작

sudo service tomcat7 restart
sudo service apache2 restart

6. 그 이외는 이전에 작성했던
http://blog.moramcnt.com/?p=630 를 참조