MSA Proejct[3] 유저 조회

최준호·2021년 9월 24일
0

2021 msa

목록 보기
7/10

user service port번호 확인

서비스를 호출했을때 우리가 만든 서비스들은 랜덤한 포트로 열기 때문에 각 포트번호를 알수가 없었다. 그래서 각 서비스의 호출시 포트번호를 알아내기 위한 설정을 해보자!

우선 우리가 사용하고 있는 health_check url을 통해 포트번호를 확인하려고 한다.

@GetMapping("health_check")
public String status(){
    return String.format("Ok on Port = %s", env.getProperty("local.server.port"));
}

기존 코드에서 포트번호를 출력해주기 위해 위와 같이 변경해주고 env 말고 HttpServletetRequest를 통해 서버의 포트를 가져와도 된다!

그 후에 우리가 예전에 작성했던 apigateway-service를 하나 가져와서 실행시키는데 이때 yml의 설정 내용을 조금 변경해야한다. 그 전에 실행했던 apigateway-service는 first service와 second-service를 실행시키는 용도로 설정해놨기 때문이다 ㅎㅎ

server:
  port: 8000

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      default-filters:
        - name: GlobalFilter  #Global filter class 파일 명
          args:               #파라미터로 전달될 값 정의
            message: hi global gateway
            preLogger: true
            postLogger: true
      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates: #조건절
            - Path=/user-service/**
        - id: first-service
          #          uri: http://localhost:8081/
          uri: lb://MY-FISRT-SERVICE
          predicates: #조건절
            - Path=/first-service/**
          filters:
            #            - AddRequestHeader=first-request, first-request-header2
            #            - AddResponseHeader=first-response, first-response-header2
            - CustomFilter

다음과 같이 내용을 적어주고 나머지 설정은 똑같이 해도 되고 안해도 된다. 왜냐면 그때는 필터를 통해 설정값을 변경하려고 한것이기 때문에 필터를 사용하지 않을 지금 설정에서는 굳이 하지 않아도 된다.

우리가 지금 해야할 가장 중요한 설정은 routes에 user-service 설정 값인데 /user-serivce/ url이 입력됐을 때 lb에 USER-SERVICE라는 서비스로 연결시켜줘야 한다는 설정을 해줘야한다.

그 후 실제 유레카 서버를 실행시킨 뒤 user-serivce와 apigateway-serivce를 실행시켜서 확인해보자!

유레카 서버를 확인했을 때 랜덤한 포트로 생성된 user-serivce와 지정 포트로 생선된 apigateway를 확인할 수 있다. 우선 user-service를 클릭하여 /health_check에서 포트번호를 정상적으로 반환하는지 확인해보자!

랜덤으로 생성된 포트번호를 정상적으로 반환하는 것을 확인할 수 있다. 그럼 apigateway로 접근했을 때도 똑같이 잘 나오는지 확인해보자!

포트를 8000으로 변경해서 /health_check를 입력했는데 404가 나온다. 그 이유는 우리가 apigateway를 설정할때 /user-service/ 라는 prefix를 설정해줬던 것을 잊지 않아야 한다ㅎㅎ /user-service/health_check 로 입력해야 정상적으로 우리가 설정한 health_check로 이동하기 때문이다.

라고 했지만 이래도 404가 뜬다. 이유가 뭘까?

위 그림과 같은 이유인데 이 이유로 인해 우리는 user-serivce에 있는 health_check에 /user-serivce/를 붙여주면 된다...

@GetMapping("/user-service/health_check")
public String status(){
    return String.format("Ok on Port = %s", env.getProperty("local.server.port"));
}

다음과 같이 코드를 수정한 뒤에 재실행 해보자!

재실행 한 뒤 /user-serivce/health_check로 가면 재실행하여 변경된 포트번호로 정상 실행되는 것을 확인할 수 있다. 그럼 이제 apigateway로도 실행을 해보자!

그럼 다음과 같이 결과를 볼수 있다. 실제 웹프로젝트를 구현해봤다면 이 부분은 금방 이해할 수 있을 것이지만 전체적인 eureka-apigateway-service의 흐름을 한번 집고 넘어가고 싶어서 좀 깊게 설명했다. 그리고 혹시나 변경했는데 500 이상 에러가 뜬다면 유레카랑 게이트웨이가 실시간으로 변경한걸 반영하지 못해서 그런것이기 때문에 서버를 전체적으로 재부팅해서 확인해보면 될것이다.

유저 정보 조회를 위한 class 생성

컨트롤러 설정을 하기 전 유저 정보를 조회했을 때 실제 데이터를 가져올 수 있는 기능들을 구현해보자.

먼저 기존에 vo 패키지에 있는 ResponseUser class에 코드를 추가해준다.

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseUser {
    private String email;
    private String name;
    private String userId;

    private List<ResponseOrder> orders;
}

@JsonInclude(JsonInclude.Include.NON_NULL)이란 반환되는 데이터가 NULL일 때 json 데이터에 아예 표시를 하지 않는 것이다.

그리고 리스트에 들어갈 ResponseOrder class의 내용은

@Data
public class ResponseOrder {
    private String productId;
    private Integer qty;
    private Integer unitPrice;
    private Integer totalPrice;
    private Date createdAt;
    private String orderId;
}

다음과 같다.

그리고 service에서도 수정해야하는데. 우선 인터페이스를 수정하자.

public interface UserService {
    UserDto createUser(UserDto userDto);

    UserDto getUserByUserId(String userId);
    Iterable<UserEntity> getUserByAll();
}

이렇게 수정했는데 아래 2개의 코드를 보면 하나는 UserDto로 반환받고 하나는 UserEntity로 반환 받았다. UserDto는 db에서 가져온 값들을 프로그래머가 작성한 dto에 맞게 가공하여 사용하는 것이고 UserEntity는 db에서 나온 내용 그대로 프로그래머가 사용하겠다는 것이다. 둘다 상관은 없으나 UserDto를 사용할 것이면 UserDto로 다 받고 UserEntity로 받을 것이면 다른 코드들도 UserEntity로 받아서 통일성 있게 코드 작성하는 것이 좋다. 하지만 지금은 예제니까 둘다 사용해보겠다.

인터페이스를 수정했으니 상속받은 class에 코드도 수정해야한다.

@Override
public UserDto getUserByUserId(String userId) {
    UserEntity userEntity = userRepository.findByUserId(userId);
    if(userEntity == null) throw new UsernameNotFoundException("적절하진 않지만... 유저 이름 찾을 수 없음");
    UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

    List<ResponseOrder> orders = new ArrayList<>();
    userDto.setOrders(orders);

    return userDto;
}

@Override
public Iterable<UserEntity> getUserByAll() {
    return userRepository.findAll();
}

getUserByAll은 orm에 정의되어있는 findAll()을 그대로 사용하며 반환 값도 UserEntity이기 때문에 따로 수정하지 않고 그대로 반환하면 된다.

getUserByUserId는 userRepository에 findByUserId()를 직접 정의하여 사용할 것인데

public interface UserRepository extends CrudRepository<UserEntity, Long> {

    UserEntity findByUserId(String userId);
}

UserRepository는 CrudRepository를 extedns하고 있기 때문에 인터페이스에 선언만 하고 따로 코드를 작성하지 않아도 된다.

순서가 좀 거꾸로이지만 마지막으로 controller도 작성해보자

@GetMapping("/users")
public ResponseEntity<List<ResponseUser>> getUsers(){
    Iterable<UserEntity> userList = userService.getUserByAll();

    List<ResponseUser> result = new ArrayList<>();
    userList.forEach(v -> {
        result.add(new ModelMapper().map(v, ResponseUser.class));
    });

    return new ResponseEntity(result, HttpStatus.OK);
}

@GetMapping("/users/{userId}")
public ResponseEntity<ResponseUser> getUser(@PathVariable("userId") String userId){
    UserDto userDto = userService.getUserByUserId(userId);

    ResponseUser result = new ModelMapper().map(userDto, ResponseUser.class);

    return new ResponseEntity(result, HttpStatus.OK);
}

Get method로 users를 추가하여 순서대로 eureka > apigateway > service를 실행시켜 postman을 통해 확인해보자!

다음과 같이 8000번 apigateway 포트로 요청하여 정보를 넣고 get으로 method를 변경하여 요청하면

다음과 같이 결과를 가져올 수 있다.

마지막으로 userid를 통해 검색했을 때도 하나의 유저 정보를 잘 가져오는 것을 확인할 수 있다!

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글