서비스 디스커버리

MSA 에서는 여러 서비스 간의 호출로 구성이 된다.
일반적으로 IP와 포트를 통해 호출을 하는데, 클라우드 환경에서는 IP가 동적으로 변경되는 일이 많기 때문에 정확한(유효한) 위치를 알아내는 기능이 필요하다.
서비스 디스커버리가 이 일을 한다.

책에 나오는 Netflix의 Eureka 를 사용해서 구성해 본다.

유레카와의 통합을 위한 스프링 클라우드 라이브러리는 클라이언트서버의 두 부분으로 구성돼 있다.


서버

  • 스프링 부트 애플리케이션으로 실행한다.
  • 서버 API 구성
    • 등록된 서비스의 목록을 수집하기 위한 API
    • 새로운 서비스를 네트워크 위치 주소와 함께 등록하기 위한 API
  • 서버의 상태를 다른 서버로 복제함으로써 안정성과 가용성을 높일 수 있다.

클라이언트

  • 마이크로서비스 애플리케이션에 의존성을 포함시켜 사용한다.
  • 기능
    • 애플리케이션 시작 후 서버에 등록한다.
    • 종료 전 서버에서 등록 해제를 담당한다.
    • 유레카 서버로부터 주기적으로 최신 서비스 목록을 받아온다.

서버 측에서 유레카 서버 실행하기


Spring Initializr 에서 Search for Dependencies 에 'Eureka Server' 를 검색한 뒤 선택하면 'Selected Dependencies'에 추가가 된다.
그리고 'Generate Project'를 하면 프로젝트가 생성된다. 그리고 실행하자.

그럼 프로젝트에 dependency가 추가되어 있다.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

Main Application에 @EnableEurekaServer를 추가해 유레카 서버를 활성화한다.
@SpringBootApplication
@EnableEurekaServer
//@EnableEurekaClient <-- Client 아님요
public class MsaServerApplication {
public static void main(String[] args) {
SpringApplication.run(MsaServerApplication.class);
}
}

eureka-server의 dependency만 추가 하였는데 @EnableEurekaClient 역시 활성화 가능하다.

하지만 이것은 디스커버리 인스턴스를 고가용성 모드로 동작할 경우 디스커버리 인스턴스 사이의 동료 간 통신(peer-to-peer)일 경우에만 유용하다.


이 상태로 실행을 하면, 에러 로그가 많이 찍힐 것이다.(그래도 브라우저에서 확인은 가능하다.)

책에서는 의존성에서 spring-cloud-netflix-eureka-client를 의존성에서 제외 하는것도 하나의 방법이라고 적혀 있는데 그렇게 하면 특정 Bean을 찾지 못해 에러가 나더라..

아무튼 아래와 같이 yml 파일에서 처럼 디스커버리 클라이언트를 비활성화 하는 것이 가장 좋은 방법이다.


server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false

이제 Spring Cloud Application을 시작한다.

그리고 localhost:{port} 로 이동하자.



위 이미지 처럼 Spring Cloud Application에 대한 정보를 확인 할 수 있다.


그리고 /eureka/apps 로 이동하여 서버에 등록된 마이크로서비스의 목록을 확인 가능하다.



이제 Eureka 서버에 등록될 서비스를 만들 차례다.



클라이언트에서 유레카 활성화 하기

클라이언트용 Spring Boot Application을 생성하자.
Spring Initializr에서 검색해보니 Eureka Discovery를 쓰면 될 것 같았다.
Eureka Discovery를 추가하고 프로젝트를 생성하자.


다음과 같이 dependency가 추가 되어 있다는걸 확인할 수 있다.

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

책에서는

spring-cloud-starter-netflix-eureka-client 만 추가되어 있지만, 실제로 client를 실행 시켜보면 등록 즉시 shutdown 되는 것을 확인할 수 있다. 따라서 아래와 같이 dependency를 하나 더 추가해야 한다.

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


클라이언트는

    1. 자신을 Eureka 서버에 등록하고 호스트, 포트, 상태 정보, URL, 홈페이지 URL을 보낸다.
      • Eureka 서버는 서비스의 각 인스턴스로부터 생존신호(Heartbeat) 메시지를 받는다.
      • 설정된 기간 동안 생존신호 메시지를 받지 못하면 레지스트리에서 서비스가 삭제된다.
    2. 서버로부터 데이터를 가져와서 캐싱하고 주기적으로 변경사항을 점검한다.

유레카 클라이언트 활성화 시키는 방법은
    1. @EnableDiscoveryClient Annotation을 메인 클래스에 추가
    2. 컨설, 유레카, 주키퍼 등 다수의 클라이언트 구현체가 classpath에 있을 경우 @EnableEurekaClient Annotation을 추가
@EnableDiscoveryClient는 spring-cloud-commons에 존재하고,
@EnableEurekaClient는 spring-cloud-netflix에 존재하고 유레카만을 위해 작동한다.


나는 @EnableDiscoveryClient를 추가한다.

@SpringBootApplication
@EnableDiscoveryClient
public class MsaClientApplication {

public static void main(String[] args) {
SpringApplication.run(MsaClientApplication.class, args);
}

}


그리고 yml에 다음과 같이 설정을 한다.

spring:
application:
name: client-service
server:
port: ${PORT:8081}
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/


이제 빌드를 하고 실행을 해보자.


java -jar -DPORT=8081 target/msa-client-0.0.1-SNAPSHOT.jar

java -jar -DPORT=8082 target/msa-client-0.0.1-SNAPSHOT.jar


그리고 나서 아까 실행했던 Server의 대시보드 화면을 다시 확인해보자.


포트 8081, 8082로 실행한 두 개의 client 인스턴스가 존재한다.


종료 시 등록 해제

스프링 엑추에이터의 /shutdown API를 이용하여 애플리케이션을 '우아하게' 중지하기

  1. spring-boot-starter-actuator를 pom.xml 에 추가한다.
  2. 기본으로 비활성화 되어 있기 때문에 속성을 통해 활성화 시킨다.
의존성 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

.yml 설정 (책의 버전이랑 달라서 그런지 아래처럼 해줘야 한다.)

management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown,info


이렇게 추가를 한 다음에 다시 클라이언트를 실행한다.


이제 애플리케이션을 종료하려면 /actuator/shutdown API 를 POST 메서드로 호출한다. (포스트맨 등을 사용해서)

문제가 없다면 다음과 같은 Response를 받을 것이다.


{
"message": "Shutting down, bye..."
}

Client의 로그를 확인 해보자.

com.netflix.discovery.DiscoveryClient    : Shutting down DiscoveryClient ...
com.netflix.discovery.DiscoveryClient : Unregistering ...
com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-SERVICE/kkt0929.MIDASIT.local:client-service:8081 - deregister status: 200
com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
o.apache.catalina.core.StandardService : Stopping service [Tomcat]
o.a.c.c.C.[Tomcat].[localhost].[/] : Destroying Spring FrameworkServlet 'dispatcherServlet'


이제 서버 대시보드를 가보면 포트 8082만 남아 있을 것이다.



이 외에도 유레카 서버 대시보드는 등록되고 취소된 서비스 이력을 확인 가능하다.



현실에선 항상 우아할 수 없다.

우아한 종료가 최선의 방법이지만 언제나 그렇듯이 항상 우아한 결과를 얻는 것은 아니다.

예를 들어,
    • 서버 머신이 재시작하거나 애플리케이션의 장애
    • 서버와 클라이언트 간의 네트워크 인터페이스 문제
이를 테스트하기 Intellij 에서 종료를 하고 서버 대시보드에서 확인을 해보면 UP 상태로 남아 있는 것을 확인할 수 있다.


위 이미지 처럼 빨간 박스 안에 있는 버튼을 클릭하면 바로 종료가 된다.
종료하고 유레카 서버 대시보드를 확인해보면 UP 상태로 남아 있다. 한번 해보십셔!

왜 이러는 걸까요??
유레카에는 특별한 메커니즘이 있다.

일반적으로 Eureka 서버에 등록된 instance는 주기적으로 heartbeat를 보내는데, heartbeat를 받지 못하면 서버의 registry에서 instance를 제거 한다. 
하지만 자신의 서비스 등록 상태를 제 시간에 갱신하지 않는 서비스의 수가 일정 수를 넘게 되면 '등록 만료'를 멈춘다.
즉 서버 Registry에서 instance를 제거하지 않는 것이다.

그 이유는 네트워크 장애가 발생했을 때 등록된 모든 서비스가 해제되는 것을 방지하기 위함이다.
그러니까 Eureka로의 네트워크는 단절되었지만, 해당 서비스 자체는 문제가 없는 경우가 있기 때문에 '자기 보존 모드'가 활성화 되어 있으면 registry에서 해당 instance를 정해진 기간 동안 제거하지 않게 된다.
이 메커니즘을 Self-preservation mode(자기 보존 모드)라고 한다.

Self-Preservation-mode 여부는 Expected heartebeats 수와 Actual heartbeats 수를 비교하여 결정된다.

application.yml 의 enableSelfPreservation 속성을 false로 설정하면 '자기 보존 모드'를 비활성화 시킬 수 있다.






하지만 운영 환경에서는 활성화 해야 한다.


프로그램 방식으로 디스커버리 클라이언트 사용하기

Client 애플리케이션이 시작될 때 유레카 서버로부터 등록된 서비스 목록을 가져온다. (자동으로)
그러나 실제로 개발자가 유레카 Client API를 필요로 할 경우가 있을 것이다.

그럴 때 두 가지의 방법이 있다.
    • com.netflix.discovery.EurekaClient
      • 유레카 서버가 노출하는 모든 HTTP API를 구현한다. 유레카 API 영역에 설명돼 있다.
    • org.springframework.cloud.client.discovery.DiscoveryClient
      • 넷플릭스 EurekaClient를 대체하는 스프링 클라우드의 구현체. 이것은 모든 디스커버리 클라이언트용으로 사용하는 간단한 범용 API다. 여기에는 getService와 getInstances의 두 가지 메서드가 있다.

여기에선 DiscoveryClient를 사용한다.
@RestController
public class MainController {
private static Logger LOGGER = LoggerFactory.getLogger(MainController.class);

@Autowired
private DiscoveryClient discoveryClient;

@GetMapping("/ping")
public List<ServiceInstance> ping(){
List<ServiceInstance> instances = discoveryClient.getInstances("client-service");
LOGGER.info("INSTANCES: count={}", instances.size());
instances.stream().forEach(
it -> LOGGER.info("INSTANCE: id={}, port={}", it.getServiceId(), it.getPort()));
return instances;
}
}

여기서 주의해야 할 사항이 있는데 DiscoveryClient의 package는 org.springframework.cloud.client.discovery  이다.

com.netflix.discovery.DiscoveryClient << 이놈으로 하지 말고..


/ping을 확인하면 다음과 같다.


고급 컨피규레이션 설정

유레카의 컨피규레이션 설정은 세 부분으로 나뉜다.

서버

    • 서버의 행동을 재정의 한다.
    • eureka.server.* 를 접두어로 사용하는 모든 속성을 포함한다.
    • 전체 속성 목록 URL

클라이언트

    • 유레카 클라이언트에서 사용할 수 있는 두 가지 속성 중 하나다. 이것은 클라이언트가 레지스트리에서 다른 서비스의 정보를 얻기 위해 질의하는 방법의 컨피규레이션을 담당한다.
    • eureka.client.* 를 접두어로 사용하는 모든 속성을 포함한다.
    • 전체 속성 목록 URL

인스턴스

    • 이것은 포트나 이름 등의 현재 유레카 클라이언트의 행동을 재정의한다.
    • eureka.instance.* 를 접두어로 사용하는 모든 속성을 포함한다.
    • 전체 속성 목록 URL



레지스트리 갱신하기

이전 내용중에 '우아하게' 종료하기를 하면서 '자기 보존 모드(self-preservation-mode)' 가 기억이 나는가?

'self-preservation-mode'를 비활성화 시켜도 여전히 서버가 임대를 취소하는 시간은 오래 걸린다.

                                                                                                                                                                                                                                             

그 이유는

    1. 모든 클라이언트 서비스가 30초(default)마다 서버로 하트비트를 보내기 때문이다.
      • eureka.instance.leaseRenewalIntervalInSeconds 속성으로 설정 가능하다.
      • 서버가 하트비트를 받지 못하면 레지스트리에서 인스턴스를 제거하기 전에 90초를 기다린다.
    2. 등록을 해제해서 인스턴스로 더 이상 트래픽이 가지 못하도록 차단할 수 있다.
      • eureka.instance.leaseExpirationDurationInSeconds 속성으로 설정 가능하다.
      • leaseExpirationDurationInSeconds 에 지정된 기간 동안 heartbeat가 수신되지 않으면 eureka 서버에서 instance를 제거한다.

클라이언트 Configuration

위에 설명했던 두 속성을 작은 값으로 설정해보자.
eureka:
instance:
lease-renewal-interval-in-seconds: 1
lease-expiration-duration-in-seconds: 2

서버 Configuration

서버 측에서도 변경을 해줘야 한다. 
이유는 Evict(퇴거) 이라는 백그라운드 태스크 때문이다.
이것이 하는 일은 클라이언트로부터 하트비트가 계속 수신 되는지 점검하는 일이다.
기본값으러 60초마다 실행되기 때문에 클라이언트에서 설정했던 위에 두 값을 작은 값으로 설정해도 서비스 인스턴스를 제거하는 데 최악의 경우 60초가 걸린다.
이 값은 evictionIntervalTimerInMs 속성으로 설정 가능하며 millisecond 단위다.
eureka:
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 3000

이제 디스커버리 서버를 다시 실행하고 클라이언트 인스턴스를 다시 실행해보자.

그리고 아까 처럼 클라이언트를 강제 종료한다.

이번에는 비활성화 된 클라이언트가 유레카 서버에서 제거 되는 것을 확인할 수 있다.


다음은 유레카 서버의 로그 일부다.

: Evicting 1 items (expired=1, evictionLimit=1)
: DS: Registry: expired lease for CLIENT-SERVICE/kkt0929.MIDASIT.local:client-service:8081
: Cancelled instance CLIENT-SERVICE/kkt0929.MIDASIT.local:client-service:8081 (replication=false)


이런 속성들을 조작해서 서비스의 등록과 만료에 대한 유지 관리를 정의할 수 있다.

하지만 중요한 것은 정의된 Configuration으로 인해 시스템의 성능에 악영향을 미치지 않도록 하는 것이다.

이런 설정에 민감한 요소에는

    • 부하 분산
    • 게이트웨이
    • 서킷 브레이커
들이 있다.

인스턴스 식별자 변경하기

유레카 서버에 등록된 인스턴스는 이름이 있다.
중요한 것은 각 인스턴스는 서버가 인식할 수 있도록 유일한 ID를 보내야만 한다.
스프링 클라우드 유레카는 다음과 같은 조합으로 식별자를 자동으로 생성한다.
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:$server.port}}}.
우리는 식별자를 재정의 할 수 있다.
eureka.instance.instanceId 속성을 재정의 하면 된다.
책에서는 클라이언트 Application의 application.yml을 다음과 같이 수정해사 테스트 하고 있다.
server:
port: 808${SEQUENCE_NO}
eureka:
instance:
instance-id: ${spring.application.name}-${SEQUENCE_NO}

그리고 실행을 해보자. 한...3개정도 해보자...

예) 

java -jar -DSEQUENCE_NO=1 target/msa-client-0.0.1-SNAPSHOT.jar

java -jar -DSEQUENCE_NO=2 target/msa-client-0.0.1-SNAPSHOT.jar

java -jar -DSEQUENCE_NO=3 target/msa-client-0.0.1-SNAPSHOT.jar


Eureka 대시보드를 확인해보면

설정한대로 instanceId 가 만들어졌고 실행이 되었다. 유레카 서버에 잘 나온다.



IP 주소 우선하기

마이크로서비스 환경을 구성하는 데에는 여러 서버를 위한 DNS가 없는 것이 일반적이라고 한다. 그렇겠지..
이 경우 모든 리눅스 머신의 /etc/hosts 파일에 호스트명과 IP 주소를 추가하는 것이 있는데 불편하단다. 그렇겠지..
이를 위해 유레카의 등록 절차 설정 시 호스트명 대신 서비스의 IP 주소를 사용하는 방법이 있단다. 오호..
eureka.instance.preferIpAddress 속성을 true로 설정하면 서버 대시보드의 서비스 인스턴스는 호스틈여을 담은 instanceId를 사용하지만, 링크를 클릭하면 IP주소 기반으로 redirect 된다고 한다.

그런데 여기엔 문제가 있다. 무엇인고 하면..
한대의 서버에 하나 이상의 네트워크 인터페이스가 있는 경우 발생한다.
예를 들어,
    • eth01 : 내부 네트워크
    • eht01 : 공인 IP
로 연결이 되어 있을 경우, 서버는 다른 IP 접두사를 갖는 두 개의 네트워크 인터페이스를 갖게 된단다.
아래 이미지 거다. 근데 여긴 eth0만 있네


암튼.. 이럴 때는 네트워크 인터페이스를 선택해야 한다. 

application.yml 에 무시할 패턴의 목록을 정의 하면 된다.

spring:
cloud:
inetutils:
ignored-interfaces:
- eth1*
또는 원하는 네트워크 주소를 정의하는 방법도 있다.
spring:
cloud:
inetutils:
preferred-networks:
- 192.168


응답 캐시 

서버

유레카 서버는 기본적으로 응답을 캐시한다. 그리고 30초마다 캐시 데이터를 지운다.


유레카 서버를 실행하고 /eureka/apps API를 호출해보자. 아무것도 안나온다.

그리고 클라이언트 인스턴스를 실행 하고 서버의 /eureka/apps를 다시 호출해보자. 여전히 안나온다.

30초가 지나고 나서 다시 호출해보자 추가된 클라이언트 인스턴스가 조회될 것이다.


Cache Timeout 은 reponseCacheUpdateIntervalMs 속성으로 재정의 가능하다.

eureka:
server:
response-cache-update-interval-ms: 3000

재미있는 것은 REST API 와는 반대로 유레카 서버의 대시보드에 등록된 인스턴스가 표실될 때에는 캐시를 사용하지 않는다.


클라이언트

클라이언트 측에서도 유레카 레지스트리를 캐싱을 한다.
서버에서 Cache Timeout을 변경해도 클라이언트에서 갱신되는 데에는 시간이 걸린다.
서버의 Registry는 30초마다 실행되는 백그라운드 태스크에 의해 비동기로 갱신이 되기 때문이다.
registryFetchIntervalSeconds 속성으로 변경 가능하다.

shouldDisableDelta 속성을 통해 마지막으로 시도한 값에서 변경된 내용만 가져오도록 할 수 있다.
shouldDisableDelta을 false로 지정할 수 있지만 이것은 대역폭 낭비다.


Client의 registryFetchIntervalSeconds 을 3초로 변경해보자.

책에서는 shouldDisableDelta 로 되어 있지만 내가 사용하는 버전에는 아래와 같이 disable-delta로 된다.

eureka:
client:
registry-fetch-interval-seconds: 3
disable-delta: true


클라이언트와 서버 간의 보안 통신 사용학

개발중에는 보안이 크게 필요하지 않지만 그래도 예제에서도 하고 하니 나도 한다.
보안을 위해 Spring Security를 적용하자.

또 추가하자.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>


아래 취소선으로 되어 있는 내용은 책의 내용이다.

이렇게 해도 된다. 하지만 난 이렇게 안할거다.


취소선 내용은 건너뛰자.


그리고 yml 에도 또 추가하자.

spring:
security:
user:
name: admin
password: admin123

책에서는 security가 root 지만 옛날 버전간다. spring이 루트다.

그리고 책에서는 basic.enabled 가 있지만 deprecated 된 속성이다.


뭐 이렇게만 추가한 뒤에 Eureka 서버를 다시 실행해보자. 그럼 로그인을 해야 할 것이다.

yml 파일에 정의 한 대로 Username Password 를 입력하고 로그인 하자. 

그럼 대시보드로 잘 들어갈거다.


이제 클라이언트 에서 인증된 상태로 서버에 접근 하려면 다음 설정과 같이 URL 연결 주소에 자격 증명을 제공한다.

eureka:
client:
serviceUrl:
defaultZone: http://admin:admin123@localhost:8761/eureka/


yml에 설정하지 말고 Security 설정을 JavaConfig로 하자.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
PasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder.encode("admin123"))
.roles("SYSTEM");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login", "/logout", "favicon.ico").permitAll()
.and().authorizeRequests().anyRequest().authenticated()
.and().httpBasic().and().formLogin()
.and().csrf().disable();
}
}

사용자 인증은 inMemory를 사용하자. passwordEncoder를 이용하는데 Bcrypt를 사용 할 것이다.


그리고 configure 에서는 인증 방식에 대한 내용을 정의하자.

authorizeRequests().antMatchers("/login", "/logout", "favicon.ico").permitAll()

"login", "/logout", "/favicon.ico" 얘네들은 인증이 필요 없이 접근 가능하다. 말 그대로 전부 허용이다.

authorizeRequests().anyRequest().authenticated()

그 외에는 다 인증이 필요하게 설정했다. 

httpBasic()

httpBasic 을 추가 하면 BasicAuthenticationFilter(Basic Auth) 가 Spring Security Filter Chain에 추가가 되는데 

filter 내용은 다음과 같다. 필요한 부분만 몇줄만 적는다.


@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
final boolean debug = this.logger.isDebugEnabled();

String header = request.getHeader("Authorization");

if (header == null || !header.toLowerCase().startsWith("basic ")) {
chain.doFilter(request, response);
return;
}

~~~~~~~~~~~~~~~~~~블라블라

request 객체의 Header 정보중에 Authorization 을 가져온다.

가져와서 Null 이거나 Authorization으로 시작 되는 것이 아니면 Filter Chain으로 넘긴다.


Authorization 헤더 정보가 존재하면 다음과 같이 진행된다.

String[] tokens = extractAndDecodeHeader(header, request);
assert tokens.length == 2;

String username = tokens[0];

if (debug) {
this.logger
.debug("Basic Authentication Authorization header found for user '"
+ username + "'");
}

if (authenticationIsRequired(username)) {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, tokens[1]);
authRequest.setDetails(
this.authenticationDetailsSource.buildDetails(request));
Authentication authResult = this.authenticationManager
.authenticate(authRequest);

if (debug) {
this.logger.debug("Authentication success: " + authResult);
}

SecurityContextHolder.getContext().setAuthentication(authResult);

this.rememberMeServices.loginSuccess(request, response, authResult);

onSuccessfulAuthentication(request, response, authResult);
}

요약하면 인증을 할 필요가 있는지 체크를 하고 필요하면 Authorization 정보를 가지고 인증을 완료한다.


Authorization 에 어떤 정보가 있는가 하면....이것도 요약하면...

사용자 username과 password 를 가지고 base64로 인코딩 한 다음 앞에 Basic+공백+{인코딩값} 으로 만들어서

그 전체를 Authorization header에 추가해서 요청을 보내는 것이다. 

예) admin:admin123 = Basic YWRtaW46YWRtaW4xMjM=

이야기가 나온김에 조금 더 하면 Postman에서 테스트 해볼수 있다.




요청을 보내기 전에 인증 타입을 Basic Auth 로 선택하고 Username, Password 를 입력하자. 그리고 요청을 보내보자.


그리고 나서 Headers 탭으로 이동해 보면, Authorization Header에 Basic YWRtaW46YWRtaW4xMjM= 이 추가된 것을 확인 할 수 있다.


또 다른 방법이 있는데 다음과 같이 url에 username과 password 를 포함 시키는 것이다.


이렇게 보내면 Basic Auth 방식으로 요청을 보내게 된다.

잠시 후 설명할 Client 에서 이 방식을 사용한다.


갑자기 이야기가 산으로 갔지만 괜찮다. 내가 블로그 주인이니까.

formLogin()

그리고 이건 formLogin을 사용하겠다라는 이야기다.

eureka Server에 로그인 하는 방식이다.


아무튼 SecurityConfig를 만들고 나면 passwordEncoder 에 빨간줄이 생길 것이다.

왜냐하면 Bean으로 추가하지 않았기 때문이다.


PasswordEncoder를  Bean으로 등록하자.

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

@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

이제 실행해보자 그럼 아래처럼 나올 것이다.



yml 파일에 정의 한 대로 Username 과 Password 를 입력하고 로그인 하자. 

그럼 대시보드로 잘 들어갈거다.


이제 클라이언트 에서 인증된 상태로 서버에 접근 하려면 다음 설정과 같이 URL 연결 주소에 자격 증명을 제공한다.

eureka:
client:
serviceUrl:
defaultZone: http://admin:admin123@localhost:8761/eureka/


이렇게 클라이언트에서 serviceUrl의 defaultZone만 변경하면 이제 기본적인 인증이 추가 된 초간단 MSA 샘플이 되었다.

POM & SUPER POM

이전 글에서는 Maven 기본 디렉토리 구조에 대해서 알아봤다.


이번 글에서는 Maven에서 가장 중요한 POM에 대해서 알아보자.

가장 중요한 것이지 어려운 것은 아니다.


POM이 가장 중요한 이유는 POM에 프로젝트에 대한 모든 정보가 들어있기 때문이다.

프로젝트의 이름, 버전, 빌드 프로세스, 참조 라이브러리 등등 매우 많은 정보가 포함되어 있다.

Maven 프로젝트를 개발하게 되었다면, pom.xml 파일을 잘 확인하라. 대부분의 정보가 있을 것이다.


POM

  • POM : Project Object Model
  • Maven 프로젝트의 전부이다.
  • Maven 프로젝트 구성 정보를 xml 형식으로 나타낸다.
  • 빌드 프로세스 중에 사용되는 플러그인 정보가 있다.
  • 의존성 항목들이 있다.


pom.xml


Intellij 에서 Maven 프로젝트를 생성하면 기본 디렉토리 구조와 함께 다음과 같은 pom.xml 파일이 생성된다.

그리고 pom.xml 파일에는 위 이미지와 같은 정보들이 들어 있다.


pom.xml 설명

  • modelVersion : 필수값이라고 생각 하면 된다. Maven2, Maven3 모두에서 지원되는 POM 버전
  • groupId : 이 프로젝트가 어디에 속한 프로젝트인지를 알 수 있는 정보이며 일반적으로 도메인 같은 특정한 식별 정보를 사용한다. Spring Boot 의 groupId가 'org.springframework.boot' 라는 것을 보면 어느정도 느낌이 올 것이다.
  • artifactId : 현재 프로젝트의 Artifact 이름이다. 프로젝트 이름과는 다르다. 현재 프로젝트가 빌드 될 때 artifactId 가 빌드가 완료된 파일의 이름으로 사용된다.(build 파일 이름을 변경 하는것도 가능하다.)
  • version : 프로젝트의 현재 버전 정보다. 일반적으로 버전 뒤에 SNAPSHOT 또는 RELEASE를 붙여서 사용한다.
    • SNAPSHOT : 현재 개발중인 버전이다. 아직 검증된 버전은 아니므로 문제가 발생할 가능성이 있다. 또한 SNAPSHOT 버전을 dependency로 추가 하기 위해서는 해당 버전이 존재하는 Repository를 반드시 추가해야 한다. 이유는 아직 검증이 완료된 버전이 아니기 때문에 Maven Central Repository 에는 아직 업로드 되지 않았기 때문이다.
    • RELEASE : 테스트를 포함한 검증이 완료된 버전으로 Maven Central Repository에 업로드 되어 있다. 안전한 버전으로 되도록이면 RELEASE 버전을 사용하는 것이 안전하다.
  • packaging : 패키징 타입 정보이다. 미입력 시 jar로 packaging 되며, jar, war, pom 등이 존재한다.
  • 프로젝트를 빌드하면 파일 이름은 Test03-1.0-SNAPSHOT.jar  가 되는데 위에서 설명한 artifactId,version,packaging 를 모두 더한 값이다. (파일명을 수정할 수 있다.)
  • name : 프로젝트 이름이다. 큰 의미는 없다. artifactId 와는 관련이 없다.
  • url : 사이트 URL 이다. (보통 프로젝트의 대한 정보가 있는 사이트)
  • dependencies: 현재 프로젝트에서 참조하고 있는 의존성(라이브러리) 목록
  • dependency : 의존(라이브러리) 프로젝트 POM 정보
    • groupId : 의존 프로젝트의 groupID
    • artifactId : 의존 프로젝트의 artifactId
    • version : 의존 프로젝트의 사용할 Version
    • scope : 의존 프로젝트의 적용 범위
  • 형광색 박스로 되어 있는 부분을 보면 groupId, artifactId, version이 같다는 것을 알 수 있다. 눈치 챘겠지만 dependency로 추가한 라이브러리 역시 maven 프로젝트 이기 때문에 같은 형식으로 되어 있는 것이다.
  • 추가 정보

pom.xml 공식 문서


pom.xml에 존재하지 않는 정보


지금까지 pom.xml에 대해서 알아 봤다.
pom.xml 에 대한 내용은 많은 사람들이 알고 있기도 하다.
그런데 pom.xml 에는 나오지 않는 내용들이 있다.
  • 기본 디렉토리 구조에 대한 설정 정보
  • 빌드를 위한 buildPath 정보
  • 빌드 결과 디렉토리(target)에 대한 정보
  • dependency를 추가 했을 때 라이브러리를 다운 받는 곳의 정보(Remote Repository)
Repository에 대해서는 지금 무슨 말인지 모르더라도 괜찮다. 나중에 설명이 나온다.
간단하게 설명하면, 
Remote Repository : 라이브러리 들이 업로드 되어 있는 원격 저장소 (기본 : Central Repository)
Local Repository : 라이브러리 들이 다운로드 되어 있는 로컬 저장소 (기본 : USER_HOME/.m2/ )

이 정보들은 다음에 나오는 Super POM 에 존재한다.

SUPER POM

SUPER POM

  • Maven의 기본 POM
  • 모든 POM은 Super POM을 상속한다.
  • Super POM에 설정된 내용을 기반으로 동작한다.
  • MAVEN_HOME/lib/maven-model-builder-{version}.jar 에 존재한다.
    • Intellij Bundle을 사용할 경우 : Intellij 설치 위치/lib/maven-model.builder-{version}.jar
  • 의존성 추가 시 다운로드 받을 repository의 정보

SUPER POM 내용

  • build.directory : 빌드가 될 곳의 디렉토리 정보

  • build.outputDirectory : 컴파일 된 클래스 파일의 위치 (기본적으로 classpath로 사용 됨)

  • build.fileName : 빌드(패키징) 파일의 이름

  • build.testOutputDirectory : 컴파일 된 Test 클래스 파일의 위치

  • build.sourceDirector : 컴파일 대상 디렉토리

  • build.testSourceDirectory : Test 컴파일 대상 디렉토리

  • build.resources.resource.directory : 리소스 파일 위치 (기본적으로 classpath로 사용 됨)

  • build.testResources.testResource.directory : Test 리소스 파일 위치 (기본적으로 classpath로 사용 됨)

Pom.xml 에는 없던 디렉토리 구조와 빌드 디렉토리 구조의 정보를 확인할 수 있다.


pom.xml 에 dependency를 추가 하면, 해당 라이브러리가 다운로드 된다.

하지만 pom.xml 에는 어디에서 다운로드 할 것이라는 정보가 존재하지 않는다.

Super POM에서 Repository 정보를 이미 가지고 있기 때문이다.

따라서 pom.xml에 Repository 정보를 추가하지 않아도 Maven Central Repository에서 다운로드가 가능했던 것이다.



실제 프로젝트와 비교


Super POM에 있는 내용과 일치하는 것을 확인할 수 있다.


물론 상속 구조이기 때문에 pom.xml에서 Super POM에 있는 기본 내용을 재정의 할 수 있다.


지금까지 POM과 SUPER POM에 대해서 알아보았다.

그렇다. 

대부분의 프로젝트에 대한 정보는 POM 그리고 SUPER에서 정의하고 있고

우리는 그냥 그대로 사용하면 된다.








'개발 > [MAVEN] MAVEN 이해하기 - 기초' 카테고리의 다른 글

[MAVEN] 프로젝트 기본 구조  (0) 2019.01.25
[MAVEN] Maven 설치  (0) 2019.01.25
MAVEN 시작  (0) 2019.01.25

Maven 프로젝트 기본 구조


이제 프로젝트의 기본구조에 대해 알아보자.


Maven을 이야기 할 때 나오는 글이 있다.

"Convention Over Configuration" 이다.

관습이 설정보다 우선한다. 라는 말이다.

개발자가 모든 것을 설정하는 것보다 기본적으로 정해진 규칙에 따라서 그냥 사용하자. 

라는 말 같다.

필요하면 그때 설정을 통해 변경 하되 그게 아니라면 굳이 신경쓰지 말고 사용하면 된다 라는 것이다.


앞으로 설명하게 될 디렉토리 기본구조, 기본 네이밍 컨벤션, 빌드, 의존성 관리 등을 알아보면 이 말이 무슨 말인지 알 수 있게 될 것이다.


기본 디렉토리 구조


Maven의 기본 디렉토리 구조이다.

아마도 많이 익숙한 구조일 것이다.


프로젝트 하위에 pom.xml 파일과 src 디렉토리가 존재하고, 그 아래로 main과 test라는 똑같은 하위 디렉토리 구조를 갖는 디렉토리가 존재한다.


Maven 프로젝트로 스프링을 개발하는 사람들 중에는 이 구조를 "스프링 개발자들이 사용하는 일반적인 방식" 이라고 생각할 수도 있다. 

하지만 이 구조는 Maven에서 정의한 구조이다.


실제 프로젝트와 비교


실제 프로젝트랑 비교해보면, 기본 구조와 일치하는 것을 알 수 있다.

연두색 박스는 web 프로젝트일 경우에 webapp 폴더 역시 기본 디렉토리 구조에 포함 된다.



기본 구조 설명


각각의 디렉토리는 위 이미지와 같은 역할을 한다.


정리

지금까지 Maven의 기본 디렉토리 구조에 대해 알아보았다.

이걸 알았으니 이제 어떤 Maven 프로젝트를 개발하게 되더라도 구조가 한번에 보일 것이다.


기본 구조를 변경하지 않은 프로젝트라면 항상 지금 구조와 같다고 생각하면 된다.

그리고 당신이 Maven 기본 디렉토리 구조로 작업을 했다면 다른 사람들도 역시 쉽게 파악이 될 것이다.

'개발 > [MAVEN] MAVEN 이해하기 - 기초' 카테고리의 다른 글

[MAVEN] POM & SUPER POM  (0) 2019.01.25
[MAVEN] Maven 설치  (0) 2019.01.25
MAVEN 시작  (0) 2019.01.25

Maven 설치


잠깐만요!!

Intellij 를 사용하고 있는 사람이라면 Intellij 가 Bundle로 가지고 있기 때문에 설치를 하지 않아도 Maven 프로젝트를 시작할 수 있다.

Intellij Settings 에서 Maven을 검색하면 확인 가능하다.




실제 jar파일 위치




하지만 그래도 설치를 하자. 



설치 요약

  1. http://maven.apache.org/download.cgi 파일 다운로드
  2. 압축 해제 후, 해당 경로 시스템 환경 변수 추가 “MAVEN_HOME”
  3. 시스템 환경 변수 “PATH”에 “MAVEN_HOME/bin” 추가
  4. 설치 확인

설치 순서 및 확인


마지막 이미지는 Maven 명령어를 이용한 버전 확인으로 설치가 잘 되었는지 확인하는 것이다.

Maven은 Java 위한 프로젝트이기 때문에 반드시 Java가 설치되어 있어야 한다.


설정 파일 및 라이브러리 위치


  • Maven 전역 설정 파일과 Maven 사용자 설정 파일은 필수는 아니다.

    • 사내 Repository를 사용 할 때 필요하다.

  • USER_HOME/.m2/repository에는 의존성 항목들이 존재한다.
    • Dependency 내용에서 자세하게 다룬다.
  • POM.xml 파일은 maven의 핵심 파일이다.




'개발 > [MAVEN] MAVEN 이해하기 - 기초' 카테고리의 다른 글

[MAVEN] POM & SUPER POM  (0) 2019.01.25
[MAVEN] 프로젝트 기본 구조  (0) 2019.01.25
MAVEN 시작  (0) 2019.01.25

계기 

Maven 에 대해서 자세히 알아보게 된 계기는 신입사원 교육에서 Maven을 담당하게 되었기 때문이다.

Maven 에 대한 사용법은 뭐 워낙 쉽다보니 누구나 잘 사용하겠지만

"필요한 라이브러리를 추가하면 됩니다." 라고 말하면 교육이 너무 빨리 끝나서 교육실에 왔다 돌아가는 시간보다 짧을 것 같았다.

그래서 기본적인 내용들에 대해서 자세히 설명해 주기 위해서 정리를 하게 되었다.

Gradle 역시 많이 사용하지만 현직장에서는 Maven을 사용하기 때문에 Maven을 교육하게 되었다.


목표

  • Maven 문서를 읽는데 필요한 기초 지식을 쌓는다.
  • Maven을 사용하여 프로젝트를 시작할 수 있다.
  • Maven 관련 문제 해결에 도움이 되도록 한다.
  • 후배 또는 선배 개발자들에게 Maven에 대해 설명할 수 있도록 한다.
  • maven에 대한 품격 있는 질문을 할 수 있도록 한다.
이정도 목표를 가지고 시작한다.

내용

  • Maven 기본 구조
  • dependency
  • Nexus
  • Build
에 대한 사용법과 기본적인 메커니즘에 대해 정리하고자 한다.

이를 위해서 Maven 공식 사이트를 가장 많이 참고 했으며, 다른 분들이 작성하신 블로그도 참고를 했다.

Maven이란

Maven이란...한마디로 말하면 그냥 개편하다.
처음부터 Maven을 사용했다면 너무나도 당연한 기능이겠지만 의존성 관리부터 빌드를 정말 쉽고 빠르게 해준다.
도대체 뭐가 빠르고 편한지는 내용에 조금 들어있다.

공식문서에 있는 소개 내용의 마지막을 보면 
자바 개발자의 일상적인 작업을 보다 쉽게 해주고 자바 기반 프로젝트의 대한 이해를 돕기 위해 만들었다고 한다.

Maven의 목표

  • Making the build process easy
  • Providing a uniform build system
  • Providing quality project information
  • Providing guidelines for best practices development
  • Allowing transparent migration to new features

자세한 내용은 아래 주소로 가면 된다.

https://maven.apache.org/what-is-maven.html


'개발 > [MAVEN] MAVEN 이해하기 - 기초' 카테고리의 다른 글

[MAVEN] POM & SUPER POM  (0) 2019.01.25
[MAVEN] 프로젝트 기본 구조  (0) 2019.01.25
[MAVEN] Maven 설치  (0) 2019.01.25

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 파일에 위치해야 한다.
  • 아래 그림 처럼 빈으로 주입이 가능하다.

+ Recent posts