본문 바로가기
CS/Spring Boot

[스프링 부트] 스프링 부트 2

by Hoozy 2023. 4. 8.

이전 게시글 스프링 부트 1

https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-1

 

[스프링 부트] 스프링 부트 1

스프링 부트 로드맵 https://roadmap.sh/spring-boot 스프링 부트의 로드맵에 따라서 따로 배워보겠습니다. 카테고리 : Spring Boot 스프링 코어(컨테이너) (IoC 컨테이너) 객체를 생성하고 관리하고 책임지고

hoozy.tistory.com

카테고리 : Spring Boot

Spring Security
  • 인증(Authentication) : 해당 사용자가 본인이 맞는지 확인하는 과정
  • 인가(Authorization) : 해당 사용자가 요청하는 자원을 실행할 수 있는 권한이 있는가를 확인하는 과정
  • 이러한 인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용한다.
    • Principal(접근 주체) : 보호받는 Resource에 접근하는 대상
    • Credential(비밀번호) : Resource에 접근하는 대상의 비밀번호
  • 스프링 기반의 어플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다.
  • 보안과 관련해서 체계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서 일일이 보안 관련 로직을 작성하지 않아도 된다는 장점이 있다.
  • 인증과 권한에 대한 부분을 filter 흐름에 따라 처리한다.
  • Filter는 Dispatcher Servlet으로 가기 전에 적용되므로 가장 먼저 URL 요청을 받지만, (웹 컨테이너에서 관리)
  • Interceptor는 Dispatcher와 Controller 사이에 위치한다는 점에서 적용 시기의 차이가 있다. (스프링 컨테이너에서 관리)
Client (request) → Filter → DispatcherServlet → Interceptor → Controller
(실제로 Interceptor가 Controller로 요청을 위임하는 것은 아님, Interceptor를 거쳐서 가는 것)

Authentication

  • 현재 접근하는 주체의 정보와 권한을 담는 인터페이스이다.
  • Authentication 객체는 SecurityContext에 저장되며, SecurityContextHolder를 통해 SecurityContext에 접근하고, SecurityContext를 통해 Authentication에 접근할 수 있다.
public interface Authentication extends Principal, Serializable {
    // 현재 사용자의 권한 목록을 가져옴
    Collection<? extends GrantedAuthority> getAuthorities();

    // credentials(주로 비밀번호)을 가져옴
    Object getCredentials();

    Object getDetails();

    // Principal 객체를 가져옴
    Object getPrincipal();

    // 인증 여부를 가져옴
    boolean isAuthenticated();

    // 인증 여부를 설정함
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;

}

UsernamePasswordAuthenticationToken

  • Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스로, User의 ID가 Principal 역할을 하고, Password가 Credential의 역할을 한다.
  • UsernamePasswordAuthenticationToken의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두번째는 인증이 완료된 객체를 생성한다.
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // 주로 사용자의 ID에 해당
    private final Object principal;

    // 주로 사용자의 PW에 해당
    private Object credentials;

    // 인증 완료 전의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        setAuthenticated(false);
    }

    // 인증 완료 후의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
            Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true); // must use super, as we override
    }
}

AuthenticationManager

  • 인증에 대한 부분은 AuthenticationManager를 통해서 처리하게 되는데, 실질적으로는 AuthenticationManager에등록된 AuthenticationProvider에 의해 처리된다.
  • 인증에 성공하면 두번째 생성자를 이용해 객체를 생성하여 SecurityContext에 저장한다.
public interface AuthenticationManager {

    Authentication authenticate(Authentication authentication) throws AuthenticationException;

}

AuthenticationProvider

  • 실제 인증에 대한 부분을 처리하는데, 인증 전의 Authentication 객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다.
  • 아래와 같은 인터페이스를 구현해 Custom한 AuthenticationProvider를 작성하고 AuthenticationManager에 등록하면 된다.
public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    boolean supports(Class<?> authentication);

}

ProviderManager

  • AuthenticationManager를 implements한 ProviderManager는 AuthenticationProvider를 구성하는 목록을 갖는다.
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

    public List<AuthenticationProvider> getProviders() {
        return this.providers;
    }

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        AuthenticationException parentException = null;
        Authentication result = null;
        Authentication parentResult = null;
        int currentPosition = 0;
        int size = this.providers.size();

        // for문으로 모든 provider를 순회하여 처리하고 result가 나올때까지 반복한다.
        for (AuthenticationProvider provider : getProviders()) { ... }
    }
}

UserDetailsService

  • UserDetails 객체를 반환하는 하나의 메소드만을 가지고 있는데, 일반적으로 이를 implements한 클래스에 UserRepository를 주입받아 DB와 연결하여 처리한다.
public interface UserDetailsService {

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

}

UserDetails

  • 인증에 성공하여 생성된 UserDetails 객체는 Authentication객체를 구현한 UsernamePasswordAuthenticationToken을 생성하기 위해 사용된다.
  • UserDetails를 implements하여 처리할 수 있다.
public interface UserDetails extends Serializable {

    // 권한 목록
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    // 계정 만료 여부
    boolean isAccountNonExpired();

    // 계정 잠김 여부
    boolean isAccountNonLocked();

    // 비밀번호 만료 여부
    boolean isCredentialsNonExpired();

    // 사용자 활성화 여부
    boolean isEnabled();

}

SecurityContextHolder

  • 보안 주체의 세부 정보를 포함하여 응용프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다.

SecurityContext

  • Authentication을 보관하는 역할을 하며, SecurityContext를 통해 Authentication을 저장하거나 꺼내올 수 있다.
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder.getContext().getAuthentication(authentication);

GrantedAuthority

  • 현재 사용자(Principal)가 가지고 있는 권한을 의미하며, ROLE_ADMIN이나 ROLE_USER와 같이 ROLE_*의 형태로 사용한다.
  • GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.

Spring Security 에 OAuth2, JWT 추가해보기.

https://kingofbackend.tistory.com/227

스프링 부트

스프링 부트 스타터

  • 특정 목적을 달성하기 위한 의존성 그룹으로, 아래에는 자주쓰이는 스타터를 Gradle 빌드로 알아보겠습니다.
  • spring-boot-starter : 스프링 부트의 코어. auto-configuration,logging, yaml 제공
  • spring-boot-starter-aop : 관점 지향 프로그래밍(AOP)을 위한 스타터 (AOP의 관해서는 따로 포스팅을 할 예정이다.)
  • spring-boot-starter-batch : 스프링 배치를 사용하는데 필요한 스타터
  • spring-boot-starter-data-jpa: 스프링 데이터 JPA와 하이버네이트를 사용하는 데 필요한 스타터
  • spring-boot-starter-data-redis : 메모리 저장 방식의 저장소인 레디스와 자바에서 쉽게 레디스를 사용하게끔 도와주는 제디스 설정 자동화 스타터
  • spring-boot-starter-data-rest : 스프링 데이터 저장소 방식에 맞춘 REST API를 제공하는 데 사용하는 스타터
  • spring-boot-starter-thymeleaf : 타임리프 템플릿 엔진을 사용하는 데 필요한 스타터
  • spring-boot-starter-jdbc : 톰캣 JDBC 커넥션 풀에 사용하는 스타터
  • spring-boot-starter-securiy : 각종 보안에 사용하는 스프링 시큐리티 스타터
  • spring-boot-starter-oauth2 : Oauth2 인증에 사용하는 스타터
  • spring-boot-starter-validation : 자바 빈 검증에 사용하는 스타터
  • spring-boot-starter-web : 웹을 만드는 데 사용하는 스타터(스프링 MVC, REST형, 임베디드 톰캣, 기타 라이브러리 포함)

스프링 자동 설정 (Auto Configuration)

  • 프로젝트를 생성하면 실행되는 지점인 메인 어플리케이션의 기본적인 형태는 다음과 같다.
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • 여기서 @SpringBootApplication 어노테이션은 아래 세 개의 어노테이션을 하나로 합쳐놓은 것이다.
    1. @SpringBootConfiguration
    2. @ComponentScan
      • 자기 자신부터 시작해서 하위 패키지를 싹 찾아서 @Component 어노테이션이 붙은 클래스를 찾아서 Bean으로 등록한다.
    3. @EnableAutoConfiguration
      • Bean을 읽어들이기 위한 조건 등이 정의된 파일이다. -> 수많은 Configuration들이 모두 자동으로 설정 대상에 해당된다.

스프링부트 Actuator

  • 실행 중인 애플리케이션의 내부를 볼 수 있게 하고, 어느 정도까지는 애플리케이션의 작동 방법을 제어할 수 있게 한다.
  • 아래와 같은 것을 볼 수 있다.
    1. 애플리케이션 환경에서 사용할 수 있는 구성 속성들
    2. 애플리케이션에 포함된 다양한 패키지의 로깅 레벨(logging level)
    3. 애플리케이션이 사용 중인 메모리
    4. 지정된 엔드포인트가 받은 요청 횟수
    5. 애플리케이션의 건강 상태 정보
// Gradle 추가
compile("org.springframework.boot:spring-boot-starter-actuator")

액추에이터의 기본 경로 구성하기

  • 엑추에이터의 모든 엔드포인트의 경로에는 /actuator가 앞에 붙는다.
  • 액추에이터의 기본 경로는 management.endpoint.web.base-path 속성을 설정하여 변경할 수 있다.
// application.yml 설정
management:
  endpoints:
    web:
      base-path: /management

액추에이터 엔드포인트의 활성화와 비활성화

  • 액추에이터를 추가하면 /health와 /info 엔드포인트만 기본적으로 활성화되는 것을 알 수 있다.
  • 대부분의 액추에이터 엔드포인트는 민감한 정보를 제공하므로 보안 처리가 되어야 하기 때문이다.
  • 이는 스프링 시큐리티를 사용하면 보안 처리할 수 있지만, 액추에이터 자체로는 보안 처리가 안되어 있어서 대부분의 엔드포인트가 기본적으로 비활성화 되어 있다.
management:
  endpoints:
    web:
      exposure:
        include: health,info,beans,conditions // 활성화하는 엔드포인트
        exclude: threaddump, heapdump // 비활성화하는 엔드포인트

자세한 정보는 아래 게시글에 설명되어있습니다.
https://velog.io/@ha0kim/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9D%B8-%EC%95%A1%EC%85%98-16.-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%95%A1%EC%B6%94%EC%97%90%EC%9D%B4%ED%84%B0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

스프링부트 임베디드 서버 (내장 웹 서버)

  • 임베디드 : 기본적으로 내장되어 있다는 뜻.
  • 스프링부트에는 톰캣, 제티, 언더토우 등의 WAS가 내장되어 있다. -> 기본값 톰캣
Hibernate
MSA(MicroService Architecture)
  • 마이크로 서비스란 작고, 독립적으로 배포 가능한 각각의 기능을 수행하는 서비스로 구성된 프레임워크라고 할 수 있다. 마이크로 서비스는 완전히 독립적으로 배포가 가능하고, 다른 기술 스택(언어, 데이터베이스 등)이 사용 가능한 단일 사업 영역에 초점을 둔다.
  • 아래 이미지는 MSA와 Monolithic Architecture의 비교이다.
  • MSA는 API를 통해서만 상호작용 할 수 있다. 즉, 마이크로 서비스는 서비스의 end-point(접근점)을 API 형태로 외부에 노출하고, 실질적인 세부 사항은 모두 추상화한다. 내부의 구현 로직, 아키텍처와 프로그래밍 언어, 데이터베이스, 품질 유지 체계와 같은 기술적인 사항들은 서비스 API에 의해 철저히 가려진다.
  • MSA는 SOA의 특징을 다수 공통으로 가진다.
    • SOA (Service Oriented Architecture)
    • 대규모 컴퓨터 시스템을 구축할 때의 개념으로, 업무상 일 처리에 해당하는 소프트웨어 기능을 서비스로 판단하고 그 서비스를 네트워크상에 연동하여 시스템 전체를 구축하는 방법론이다.
      • 플랫폼에 종속되지 않는 표준 인터페이스를 통해 기업의 업무를 표현한 '느슨하게 연결되고 상호 조합 가능한 소프트웨어'이다.
      • SOA에서는 각각의 서비스가 데이터 계층, 비즈니스 로직, 뷰에 대한 모듈을 모두 가지고 있고, 각 서비스 간의 의존성이 최소화된다.
      • SOA 시스템의 규모가 증가함에 따라 서비스의 중복이 발생할 수 있고, 이를 방지하기 위해 이미 구현된 서비스가 있는 지를 검색할 수 있어야 한다.

MSA의 장점

  1. 각각의 서비스는 모듈화가 되어있으며 이러한 모듈까리는 RPC 또는 message-driven API등을 이용하여 통신한다. 이러한 MSA는 각각 개별의 서비스 개발을 빠르게 하며, 유지보수도 쉽게할 수 있도록 한다.
  2. 팀 단위로 적절한 수준에서 기술 스택을 다르게 가져갈 수 있다. 회사가 java의 spring 기반이라도 MSA를 적용하면 node.js로 블록체인 개발 모듈을 연동함에 무리가 없다.
  3. 마이크로서비스는 서비스별로 독립적 배포가 가능하다. 따라서 지속적인 배포 CD도 모놀로식에 비해서 가볍게 할 수 있다.
  4. 마이크로서비스는 각각 서비스의 부하에 따라 개별적으로 scale-out이 가능하다. 메모리, CPU적으로 상당부분 이득이 된다.

MSA의 단점

  1. MSA는 모놀리식에 비해 상대적으로 많이 복잡하다. 서비스가 모두 분산되어 있기 때문에 개발자는 내부 시스템의 통신을 어떻게 가져가야 할지 정해야한다. 또한, 통신의 장애와 서버의 부하 등이 있을 경우 어떻게 transaction을 유지할지 결정하고 구현해야한다.
  2. 모놀리식에서는 단일 트랜잭션을 유지하면 됐지만 MSA에서는 비즈니스에 대한 DB를 가지고 있는 서비스도 각기 다르고, 서비스의 연결을 위해서는 통신이 포함되기 때문에 트랜잭션을 유지하는게 어렵다.
  3. 통합 테스트가 어렵다. 개발 환경과 실제 운영환경을 동일하게 가져가는 것이 쉽지 않다.
  4. 실제 운영환경에 대해서 배포하는 것이 쉽지 않다. 마이크로서비스의 경우 서비스 1개를 재배포 한다고 하면 다른 서비스들과의 연계가 정상적으로 이루어지고 있는지도 확인해야한다.
출처 :&nbsp;https://kr.tmaxsoft.com/info/storyTView.do?seq=345

Monolithic Architecture

  • 소프트웨어의 모든 구성 요소가 한 프로젝트에 통합되어 있는 형태이다. 웹 개발을 예로 들면 웹 프로그램을 개발하기 위해 개발을 하고, 개발이 완료된 웹 어플리케이션을 하나의 결과물로 패키징하여 배포되는 형태를 말한다.
  • 이런 어플리케이션을 모놀리식 어플리케이션이라고 하며, 웹의 경우 war 파일로 빌드되어 WAS에 배포하는 형태를 말한다. 주로 소규모 프로젝트에서 사용된다.
  • 하지만 일정 규모 이상의 서비스처럼 많은 개발자가 투입되는 프로젝트에서 한계를 보인다.
  1. 부분 장애가 전체 서비스의 장애로 확대될 수 있다.
    • 개발자의 잘못된 코드 배포 또는 갑작스런 트래픽 증가로 인해 성능에 문제가 생겼을 때, 서비스 전체의 장애로 확대될 수 있다.
  2. 부분적인 *Scale-out(여러 server로 나누어 일을 처리하는 방식)이 어렵다.
    • Monolithic Architecture에서는 사용되지 않는 다른 모든 서비스가 Scale-out되어야 하기 때문에 부분 Scale-out이 어렵다.
  3. 서비스의 변경이 어렵고, 수정 시 장애의 영향도 파악이 힘들다.
    • 여러 컴포넌트가 하나의 서비스에 강하게 결합되어 있기 때문에 수정에 대한 영향도 파악이 힘들다.
  4. 배포 시간이 오래 걸린다.
    • 최근 어플리케이션 개발 방법은 CI/CD를 통한 개발부터 배포까지 빠르게 반영하는 추세이다. 그러나 규모가 커짐에 따라 작은 변경에도 높은 수준의 테스트 비용이 발생하기도 하며, 많은 사람이 하나의 시스템을 개발하여 배포하기 때문에 영향을 줄 수 밖에 없다.
  5. 한 Framework와 언어에 종속적이다.
    • Spring framework를 사용할 경우, blockchain 연동 모듈을 추가할 때 node.js를 사용하는 것이 일반적이며 강세이다. 그러나 java를 이용하여 해당 모듈을 작성할 수 밖에 없다. 선정했던 framework가 Spring이기 때문이다.
  • 위와 같은 단점들을 보완하기 위해 MSA가 등장하게 되었다.
  • 기존의 특정한 물리적인 서버에 서비스를 올리던 on-promise 서버 기반의 Monolithic Architecture에서 이제는 클라우드 환경을 이용하여 서버를 구성하는 MicroService Architecture로 많은 서비스들이 전환되고 있다.

다음 게시글 스프링 부트 3

https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-3

 

[스프링 부트] 스프링 부트 3

이전 게시글 스프링 부트 2 https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-2 카테고리 : Spring Boot Spring Cloud MSA 구성을 지원하는 스프링 부트 기반

hoozy.tistory.com

참고 자료

https://dev-coco.tistory.com/174
https://velog.io/@woo00oo/SpringBoot-%EC%8A%A4%ED%83%80%ED%84%B0
https://zion830.tistory.com/112
https://velog.io/@ha0kim/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9D%B8-%EC%95%A1%EC%85%98-16.-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%95%A1%EC%B6%94%EC%97%90%EC%9D%B4%ED%84%B0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
https://wooaoe.tistory.com/57

'CS > Spring Boot' 카테고리의 다른 글

[스프링 부트] 스프링 부트 3  (0) 2023.04.09
[스프링 부트] 스프링 부트 1  (0) 2023.04.06

댓글