Spring Cloud + MSA 애플리케이션 개발 4(Users Microservice 1)

지원·2024년 2월 13일
0

MSA개발

목록 보기
4/15

앞으로 만들 예제

  • CATALOG-SERVICE(상품조회 , 상품 수량 업데이트) , USER-SERVICE(사용자 조회 , 주문확인) , ORDER-SERVICE (상품 주문 , 주문 조회)
    -> 로그인 , 회원가입 모두 해본다.
  • 상품 수량 업데이트를 할 때는 kafka 를 이용해본다.
    -> 즉 ORDER-SEIRVCE 에서 상품 주문을 했을 때 해당 데이터를 kafka 에게 전달하면 kafaka 를 통해서 CATALOG-SERVICE 에도 데이터가 전달이 되기 되면서 상품 업데이트를 할 수 있도록 한다.

전체 서비스 구성

  • Eureka Server 를 가지고 Registry service 를 만든다.
  • 위에서 말한 3개의 SERVICE 들을 Eureka Server 에 등록한다.
  • 또한 3개의 SERVICE 는 메시지 큐 서버인 kafka 와 연동한다.
  • 외부에서 클라이언트 요청이 들어오면 API Gateway 를 통해 부하 분산 / 서비스 라우팅을 한다.
  • Config Server 를 등록하면서 환경설정 정보 등록 등을 한다.

전체 애플리케이션 구성 요소

  • Git Repository : 마이크로서비스 소스 관리 및 프로파일 관리
  • Config Server : Git 저장소에 등록된 프로파일 정보 및 설정 정보
  • Eureka Server : 마이크로서비스 등록 및 및 검색
  • API Gateway Server : 마이크로서비스 부하 분산 및 서비스 라우팅
  • Microservice : 회원MS , 주문MS , 상품(카테고리)MS
  • Queuing System : 마이크로서비스 간 메시지 발행 및 구독 (kafka)

User Microservice

  • Client - API Gateway - Euraka Server - USER SERVICE + ORDER SERVICE + CATALOG SERVICE (유레카 서버에 모두 등록)
  • 신규 회원 등록 , 회원 로그인 , 상세 정보 확인 , 회원 정보 수정 / 삭제 , 상품 주문 , 주문 내역 확인
  • API Gateway 사용시 : /user-service/users
  • API Gateway 미사용시 : /users
  • 사용할 때는 prefix 처럼 앞에 붙는다.

Lombok , Web , Eureka Discovery Client 의존성 추가

인텔리J 나 이클립스 같은 IDE 은 시스템의 부하를 줄 수 있기 때문에 여러개의 인텔리J 를 띄워서 하는거는 비효율적이다.

  • 그래서 Eureka Server 는 터미널에서 mvn spring-boot:run 명령어를 통해 실행하는 방향으로 진행
  • Eureka Server , USER-SERVICE 는 원래 만들었던 프로젝트를 사용

yml 파일에 있는 변수 사용

// application.yml
greeting:
  message: Welcome to the Support Kim.

// Gretting.class
@Component
@Data
public class Greeting {
    @Value("${greeting.message}")
    private String message;
}
  
// Controller
    // 환경설정 값 가져오는 방법
    // 1. Environment 사용
    // 2. @Value 사용

    // Greeting 에 @Component 를 붙혔기 때문에 스프링 빈에 등록되고 그것을 가져올 수 있다.
    @Autowired
    private Greeting greeting;
    @Autowired
    private Environment env;

    @GetMapping("/welcome")
    public String welcome() {
        // 1. return env.getProperty("greeting.message");
        // 2. return greeting.getMessage();
    }
 
    1. Enviroment 를 주입받아서 사용
    1. Greeting 클래스를 만들고 @Value 를 통해 값을 받은 후 사용하는 쪽에서는 @Autowired 를 통해 불러와서 사용

H2 데이터베이스 연동

  • 현재 Maven 방식을 사용하기 때문에 h2 라이브러리를 가져오기 위해서 MavenRepository.com 에 들어가서 h2 를 검색하고 maven 형식을 복사해서 pom.xml 에 가져온다. (최신버전)
// application.yml
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  • 위에 처럼 작성하고 /h2-console 로 들어가면 h2 콘솔이 뜨고 거기에 url 을 jdbc:h2:mem:testdb 를 입력하고 Test Connection 을 하면 에러가 발생한다.
  • 그 이유는 최신 버전이 되면서 데이터베이스 자동 생성을 막았기 때문인데 이때는 더미 Entity 를 만들어서 DB 생성을 유도하면 된다.
// application.yml
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  jpa:
    hibernate:
      ddl-auto: update
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb
    username: sa
    password:
  • 위와 같이 yml 파일을 수정하고 JPA 라이브러리도 위에 방법처럼 MavenRepository.com 으로 가져온다.
  • 그런후 아무런 Entity 하나를 생성하고 서버를 기동하고 다시 /h2-console 에 접속해서 url 을 입력하고 Test Connection 을 누르면 정상적으로 연결이 된다.

회원가입 (사용자 추가)

  • 클라이언트가 USER-SERVICE 에 JSON 포맷으로 정보를 넘긴다.
    -> 해당 예제에서는 name , email , pwd 값을 넘김
  • 그 값이 Front-end(JSON 포맷으로 받는 곳) -> Controller -> Service -> Repository -> DB 따라서 간다.
  • 이때 JSON 포맷으로 받은 정보를 RequestUser(JSON) -> UserDto(Controller , Service , Repository) -> UserEntity(DB) 로 변환시키면서 DB 에 들어가도록 한다.
  • 물론 하나의 Dto 만 만들어도 상관없긴 하다.
  • 실제 코드를 작성해보자.

RequestUser 에는 없지만 Dto , Entity 에는 encryptedPwd 필드를 만들어서 비밀번호를 암호화한 값을 보관하고 , userID 필드도 만들어서 User 끼리 보관한다.

  • Entity 에서는 두 개의 값이 고유해야하기 때문에 unique = true 를 준다.
  • 현재는 SpringSecurity 를 추가하지 않았기 때문에 고정된 값을 넣어준다.
  • RequestUser 에서 @NotNull , @Email , @Size 같은 애노테이션을 통해 Validation 을 했다.
  • Service 는 인터페이스를 만들고 그 인터페이스를 구현하는 클래스도 만들었다.
// ModelMapper 사용 예제
ModelMapper modelMapper = new ModelMapper();
        // 기본 설정
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserEntity userEntity = modelMapper.map(userDto, UserEntity.class);
  • 위와 같이 ModelMapper 를 이용하면 Getter , Setter 로 변환하는게 아닌 한 줄로 변환할 수 있다.
// Controller
    @PostMapping("/users")
    public ResponseEntity<ResponseUser> createUser(@RequestBody RequestUser user) {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserDto userDto = modelMapper.map(user, UserDto.class);
        userService.createUser(userDto);

        ResponseUser responseUser = modelMapper.map(userDto , ResponseUser.class);
        // 201번 성공코드
        return ResponseEntity.status(HttpStatus.CREATED).body(responseUser);
    }
  • 원래는 반환값을 String 으로 해서 "success" 이런식으로 했지만 사실 API 통신을 할 떄는 HttpStatus 를 반환해주는게 더 맞는 설계이다.
  • 반환값을 ResponseEntity< ResponseUser > 로 지정해준다.
  • ResponseUser 에는 응답으로 보내고 싶은 필드를 설정하면 되고 해당 예제에서는 email , name , userId 를 반환했다.
  • return ResponseEntity.status() 로는 HttpStatus 코드를 , body() 에는 객체를 반환함으로써 응답값으로 JSON 형태로 객체가 응답되는 것을 확인할 수 있다.

Spring Security 연동

해야할 것

  1. Spring Security 라이브러리 추가
  2. WebSecurityConfigurerAdapter 를 상속받는 Security Configuration 클래스 생성
  3. Security Configuration 클래스에 @EnableWebSecurity 추가
  4. configure(AuthenticationmanagerBuilder auth) 메서드 재정의
  5. Password encode 를 위한 BCryptPasswordEncoder 빈 정의
  6. configure(HttpSecurity http) 메서드 재정의
@Configuration
@EnableWebSecurity
public class WebSecurity{

    private UserService userService;

    @Bean
    protected SecurityFilterChain configure(HttpSecurity http) throws Exception {

        http.csrf( (csrf) -> csrf.disable());
        http.authorizeHttpRequests( (authz) -> authz
                .requestMatchers("/users/**").permitAll());

        http.headers((headers) -> headers.frameOptions((frameOptions) -> frameOptions.sameOrigin()));
        return http.build();
    }
}
  • 위와 같이 SpringSeucirty6 버전부터는 람다 형식을 사용

BCryptPasswordEncoder 사용방법

// Service
	private BCryptPasswordEncoder passwordEncoder;

    //  그냥 이렇게만 하면 오류가 발생한다.
    // 그 이유는 BCryptPasswordEncoder 는 등록이 되지 않기 떄문에 찾을 수 없어서 오류가 발생한다.
    // 즉 가장 먼저 호출되는 스프링 어플리케이션 기동 클래스에다가 넣어주면 된다.
    @Autowired
    public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }
  
 // 사용 코드 
 userEntity.setEncryptedPwd(passwordEncoder.encode(userDto.getPwd()));
  • Service 계층에서 BCryptPasswordEncoder 를 의존관계 주입 받고 사용하려고 하는데 생성자 주입을 할 때 에러가 발생한다.
  • 위에 주석에 있는 것 처럼 BCryptPasswordEncoder 는 아직 등록이 되지 않았기 떄문에 찾을 수 없어서 에러가 발생하는 것이기 때문에 가장 먼저 호출되는 스프링 애플리케이션 기동 클래스에서 빈으로 등록해줘야 한다.
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
    // 가장 먼저 기동되는 곳에서 빈으로 등록
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • 이처럼 Main 함수가 있는 클래스에 @Bean 으로 등록을 해주면 에러를 해결할 수 있다.

이렇게 BCryptPasswordEncoder 를 활용해서 pwd 를 암호화해서 DB 에 저장할 수 있다.

참고자료

profile
덕업일치

0개의 댓글