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

SLF4J

Java에는 다양한 오픈소스 로깅 프레임워크가 존재한다.

이러한 다양한 종류의 프레임워크를 통합하기 위해서는 로깅 구현체를 추상화한 인터페이스 레이어가 필요하다.

해당 역할을 하는 것이 SLF4J이다. ( Simple Logging Facade For Java )

SLF4J는 인터페이스만 제공하고, 여러 구현체는 logback, log4j 등등이 있다.

구현체는 컴파일 시점에 지정된다.

Spring Boot 2.x 이후 버전에서는 SLF4J를 사용하고 아무 log관련 설정을 하지 않은 경우에는 default 설정으로 logback이 사용된다고 한다.

 

With Zero-Configuration Logging, the application will use underlying logging implementation Logback for logging. 

( https://www.baeldung.com/spring-boot-logging - 참고 )

 

 각 클래스마다 logger를 설정하기 위해서는 아래의 코드 선언해야 한다.

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CLASSNAME.class);

 

lombok

이러한 코드를 모든 클래스에 선언하는 것은 비효율적 일수 있다.

lombok에서는 이러한 코드를 생성해주는 어노테이션을 제공한다.

해당 어노테이션을 원하는 클래스에 추가해 주면 된다. 

(lombok 관련 dependency 추가 필요) 

@Slf4j
public class LoggingTestService {

}

 

Custom Log Configuration

이러한 형태로 사용하면 default로 제공하는 log기능을 사용할 수 있다.

다만, 기본으로 제공하는 형태 이외에 추가적인 설정을 하고 싶은 경우에는 설정 파일 조작이 필요하다. 

spring boot config인 application.yml 설정 또는 logback-spring.xml 설정이 존재한다. 

 

application.yml

application.yml에서 제공하는 추가적인 설정은 공식 홈페이지에서 확인하면 자세하게 나와있다.

( https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.custom-log-configuration )

 

logging:
  level:
    org.springframework: ERROR
    com.test.ttt: DEBUG
  pattern:
    console: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
    file: "%d %p %c{1.} [%t] %m%n"
  file: apptest.log

 

해당 설정은 개발단계에서 로그를 확인하기 적당하다.

다만, 실제 production단계에서는 log파일 관리가 중요하기 때문에 file rolling 등의 디테일한 설정이 필요하다.

 

이러한 경우에는 logback-spring.xml설정 파일을 사용하게 된다. 

해당 파일에 대한 디테일한 설정은 다음 포스팅에서 알아보도록 하자. 

 

 

LIST
SMALL

Spring Security에서 현재 로그인한 사용자의 정보를 가져오는 방법을 알아보도록 하자.

 

SecurityContextHolder 이용

Spring Security에서 전역으로 선언된 SecurityContextHolder를 통해서 정보를 가져올 수 있다. 

SecurityContextHolder는 Spring Security Architechture에서 간단하게 알아본 적이 있는데

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

해당 클래스를 통해서 인증을 통해 발급된 Authentication을 얻을 수 있다. 

 

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userName = auth.getName();

 

Controller 메서드 인자 이용 

 

Controller로 선언된 클래스의 메서드에서 인자로 Authentication 객체에 접근을 할 수 있다. 

@Controller 
public class UserController { 

    @GetMapping("/user") 
    public String userName(Authentication authentication) { 
        
        String userName = authentication.getName();
        
        return userName;
    } 
}

 

추가적으로, Principal 객체도 얻을 수 있다. 

@Controller 
public class UserController { 

    @GetMapping("/user") 
    public String userName(Principal principal) { 
        
        String userName = principal.getName();
        
        return userName;
    } 
}

 

LIST

+ Recent posts