SMALL

Spring Cloud Config를 통해서 설정 값을 중앙에서 관리할 수 있다.

보통은 기본값으로 git을 통한 관리가 이루어진다.

(https://joomn11.tistory.com/79)

 

설정값들 중에 password와 같은 중요 데이터들은 암호화가 필요할 수 도있다.

이러한 경우에 어떻게 해야하는지 알아보도록 하자.

 

Java Cryptography Extension (JCE) 설치

자바에서 제공하는 encrypt를 사용하기 위해서는 JCE 다운로드하여 classpath에 등록해주어야 한다. 

( 11버전 이후 부터는 default로 포함되어 있다고 한다) (https://www.oracle.com/java/technologies/javase-jce-all-downloads.html)

 

 

application.yml 설정 추가 

대칭키를 이용하기 위해서는 application.yml or bootstrap.yml에 해당 설정 추가 

encrypt:
  key: test_key // set user key !

 

encrypt endpoint 

위의 두 설정을 완료한다면 encrypt endpoint를 이용하여 원하는 property를 암호화 할 수 있다.

 

암호화 property 설정

응답으로 온 값을 spring cloud config의 값으로 사용 할 수 있다.

 

 

실제 spring cloud config 서버에 로드된 값들을 조회해보면 아래와 같이 복호화된 형태로 조회가 된다.

 

추가적으로 암호화한 내용을 client로 전달할때 복호화를 하지 않고 건내고 싶은경우에 대해서 알아보자.

아래의 설정은 default값이 true이다.

해당 설정을 false로 바꾼다면 client에게 설정값을 전달할때 암호화 된 형태로 전달할 수 있다.

spring:
  cloud:
    config:
      server:
        encrypt:
          enabled: false

하지만 저 형태로 설정을 한다면, client 측에서는 encrypt.key값을 가지고 있어야지 spring cloud config로 부터 설정 값들을 받을 수 있다. 

해당 설정이 없다면 아래와 같은 exception이 발생한다.

 

 

 

 

LIST
SMALL

서버 개발을 하다 보면 오류 처리를 해야 될 경우가 매우 많이 존재한다.

오류를 제대로 처리하지 않는다면, 서버가 안정적이지 못하게 동작할 수 있다.

 

보통의 경우에는 Exception 처리를 try-catch를 통해 해결한다.

하지만 이러한 로직들은 비즈니스 로직이 존재하는 곳에 코드가 써져 

코드를 복잡하게 하고 유지보수를 어렵게 만든다.

 

이런 문제를 개선하기 위해 @ExceptionHandler와 @ControllerAdvice를 사용해 보자.

 

@ExceptionHandler

@Controller, @RestController가 적용된 bean 내에서 발생하는 예외를 처리할 수 있게 해주는 어노테이션

 

@RestController 
public class HelloController {
    @GetMapping("/error")
    public void test() {
    	throw new IllegalArgumentException("test error");
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handle(Exception ex) {
        return new ResponseEntity<>(new ResponseError(LocalDateTime.now(), "error"), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

@ExceptionHandler는 등록된 해당 Controller에만 적용이 된다.

다른 Controller에서 발생하는 예외는 잡을 수 없다.

여러 Controller에서 동일하게 발생하는 예외는 Controller에 적용된 @ExcptionHandler가 아닌 다른 방법으로 처리해야 한다.

 

@ControllerAdvice

@Controller, @RestController에서 발생한 예외를 한 곳에서 처리할 수 있는 어노테이션

스프링에서 예외처리를 전역적으로 핸들링하기 위해 사용

@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(IllegalArgumentException.class)
	public ResponseEntity<?> handleIllegalArguException(IllegalArgumentException e) {
		return new ResponseEntity<>(new ResponseError(LocalDateTime.now(), "error"), 
			HttpStatus.INTERNAL_SERVER_ERROR);
	}
}

Controller에 해당하는 모든 예외를 처리할 수 있지만, 사용자가 원한다면 특정 패키지로 제한을 할 수 있다

@ControllerAdvice("com.exam.controller")

 

@RestControllerAdvice

추가적으로 @RestControllerAdvice에 대해서 간단하게 알아보자

해당 어노테이션은 @ControllerAdvice + @ResponseBody 조합 어노테이션이라 생각하면 된다.

( @Controller, @RestController 관계와 비슷하게 @ResponseBody 설정이 추가된 어노테이션)

( https://joomn11.tistory.com/53?category=936835 )

 

다만 위에 예시처럼 Exception을 처리한 메서드의 리턴 값을 ResponseEntity로 지정을 한다면 @RestControllerAdvice 어노테이션을 사용하지 않고도 ResponseBody에 결괏값을 실을 수 있다.

 

@RestControllerAdvice 사용 예시

@RestControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(IllegalArgumentException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public ResponseError handleIllegalArguException(IllegalArgumentException e) {
		return new ResponseError(LocalDateTime.now(), "error"));
	}
}
@Getter
@Setter
@AllArgsConstructor
public class ResponseError {
	private LocalDateTime timestamp;
    private String details;
}

다만, @RestControoerAdvice 어노테이션을 사용하는 경우 각각의 Handler 메서드에 @ResponseStatus 어노테이션을 추가해 주어야 한다.

( 해당 정보를 세팅하지 않는다면 디폴트로 200으로 응답)

LIST
SMALL
  • Spring Cloud Config란
  • 구성도
  • Spring Cloud Config 환경 구축
    • Git Repository 설정
    • Server Side 설정
    • Client Side 설정 ( 이어 설명)

 

위의 목차에 대한 설명은 이전의 포스팅한 내용 ( https://joomn11.tistory.com/79 )을 참고

이번 포스팅에서는 Client Side 설정을 알아보도록 하자.

 

Client Side 설정

 

pom.xml ( dependency 설정 )

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bootstrap</artifactId>
		</dependency>
        
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>

 

bootstrap.yml ( spring boot 설정 )

server:
  port: 8089
spring:
  profiles:
    active: dev # evn 정보
  application:
    name: apptest # appliation 정보 
  cloud:
    config:
      enabled: true
      uri: http://localhost:8000 # Spring Cloud Config Server Side 정보
      label: master 
management:
  endpoints:
    web:
      exposure:
        include: refresh

여기서는 bootstrap.yml 파일을 작성한다.

기존에는 application.yml 파일을 작성했다면,

Spring Cloud Config를 사용하기 위해서는 해당 파일(application.yml) 보다 더 먼저 로드되는 설정 파일을 사용한다. 

 

ServerSide 설정에서 Git Repo에 작성한 yml 파일에 대한 매핑정보를 적어준다고 생각하면 된다.

이전에 apptest-dev.yml 파일을 만들어두었으니 그에 매핑되는 

- application name : apptest

- profiles active : dev로 작성한다.

 

management endpoints에 해당하는 정보는 spring에서 지원하는 actuator를 이용하여 

Spring Cloud Config의 정보가 수정되었을 때, Client에게 해당 내용을 알려주기 위해 사용되는 API이다. 

추후에 curl -X POST "http://localhost:8089/actuator/refresh"와 같은 형태로 호출해 주면 된다.


여기까지 설정하면 Client Side의 설정은 모두 완료되었다.

그럼 여기서 설정이 정확하게 되었는지 확인해보도록 하자.

SpringAppliation을 Run 하면 아래와 같은 로그 정보가 출력된다.

 

 

이제 추가적으로 Spring Cloud Config를 통해서 설정 정보 값을 불러오는 예제 코드를 보도록 하자.

@Service
@RefreshScope // Added For Refresh
public class ConfigLoadService {
	
	@Value("${test.user.age}")
	private String userAge;
	@Value("${test.comm}")
	private String comm;


	public Map<String, String> getConfig() {
		Map<String, String> map = new HashMap<>();
		map.put("test.user.age", userAge);
		map.put("test.comm", comm);
        
		return map;
	}
}

 

아래와 같이 원하는 변수에 원하는 config 값을 매핑하면 된다. ( @Value )

@RefreshScope 어노테이션을 추가하여야지, 추후에 git이나 db를 통해 Config값을 업데이트했을 때 수정내역이 반영된다.

 

만약 설정이 잘못된 경우에는 위와 같은 예제에서 아래와 같은 에러가 발생한다.

org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'configLoadService':
Injection of autowired dependencies failed;
nested exception is java.lang.IllegalArgumentException:
Could not resolve placeholder 'test.user.age' in value "${test.user.age}" 

Spring Cloud Config Server를 통해서 해당 Value를 조회해야 하는데 Server와의 연결이 정확하게 되지 않아서 값을 못 받아와서 발생하는 에러이다.

 

이 문제를 해결하기 위해서는 자신이 설정한 정보를 다시 잘 확인하고 

처음 Appliation이 로드될 때의 log내용을 정확하게 보면 도움이 된다.

( 실패한 경우의 log 내용 ) 

 

 

 

 

 

LIST
SMALL
  • Spring Cloud Config란
  • 구성도
  • Spring Cloud Config 환경 구축
    • Git Repository 설정
    • Server Side 설정
    • Client Side 설정

 

Spring Cloud Config란

Spring Cloud Config는 분산 시스템에서 설정 정보를 외부로 분리하기 위해서 Server-Client 구조를 제공한다.

Config Server를 이용하여 중앙에서 Application들의 설정을 관리할 수 있다. 

Spring Cloud Config의 Default 서버 설정은 GIT Repository이다. 

다만, 다양한 형태의 관리 체계도 지원한다. ( git, db, file, vault, redis...)

 

Spring Cloud Config가 필요한 이유는

MSA 환경에서 여러 Service들의 설정을 중앙화 하여 통일,

설정 수정에 따른 배포를 최소화,

각 Stage(Dev, Test, Prod..) 단계 별로 설정 값 분리하여 관리하기 위해서이다.

 

 

구성도

 

Git Repository 설정

Git lab, GitHub과 같은 환경을 활용하여 Config를 관리할 git repository를 생성한다.

Git Repository를 생성한 이후에 설정 정보를 입력할 파일을 만든다. 

${ApplicationName}-${EnvironmentName}.yml 과 같은 형태로 만들면 된다.

 

 

Server Side 설정

 

pom.xml ( dependency 설정 )

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>

 

application.properties ( spring boot 설정 )

spring.cloud.config.server.git.default-label=master
spring.cloud.config.server.git.uri=https://github.com/USER_INFO/REPO_INFO.git

위와 같이 설정 정보가 존재하는 git의 위치를 설정해 주면 된다.

추가적으로 label이라는 설정은 git에서 사용하는 branch 설정이다.

default값은 master로 되어있다.

만약 자신이 작업하는 git의 branch가 master가 아닌경우 해당 설정을 수정해주어야 한다.

 

Spring Main Application 

@SpringBootApplication
@EnableConfigServer // ADDED!
public class SbaServerApplication {

	private static final Logger log = LoggerFactory.getLogger(SbaServerApplication.class);
	
	public static void main(String[] args) {
		SpringApplication.run(SbaServerApplication.class, args);
	}
}

여기까지 설정하면 Server Side의 설정은 모두 완료되었다.

그럼 여기서 설정이 정확하게 되었는지 확인해보도록 하자.

SpringAppliation을 Run 한 상태에서 아래 명령어를 호출해 보자.

curl -X GET "http://localhost:PORT//APP_NAME/ENV"

ex : curl -X GET "http://localhost:8000/apptest/dev"

 

 

 

 

{
    "name": "apptest",
    "profiles": [
        "dev"
    ],
    "label": null,
    "version": "44fac5ed31dbb04e4ace74f7a34a4763a56fade0",
    "state": null,
    "propertySources": [
        {
            "name": "https://github.com/--/--.git/file:C:\\Users\\TTT\\AppData\\Local\\Temp\\config-repo-3582490668620281315\\apptest-dev.yml",
            "source": {
                "test.user.age": "HeeeLLLeee",
                "test.comm": 123
            }
        }
    ]
}

 

이러한 형태로 응답을 받으면 이제 Server는 Client에게 설정을 줄 준비가 된 것이다.

그럼 다음으로 Client-Side의 환경 세팅을 보도록 하자. 

 

 

 

LIST

+ Recent posts