IT/Spring Cloud

User Microservice - 회원 등록

김 정 환 2021. 12. 13. 19:00
반응형

앞으로 구현할 유저 서비스의 개요입니다.

 

APIs 입니다.

기능 URI (API Gateway 사용 시) URI (API Gateway 미사용 시) HTTP
Method
사용자 정보 등록 /user-service/users /users POST
전체 사용자 조회 /user-service/users /users GET
사용자 정보, 주문 내역 조회 /user-service/users/{user_id} /users/{user_id} GET
작동 상태 확인 /user-service/users/health_check /users/health_check GET
환영 메시지 /user-service/users/welcome /users/welcome GET

 

 

 

프로젝트를 생성하겠습니다. 아래 Dependencies를 추가합니다.

  • Spring Boot DevTools : 코드가 변경되면 자동으로 애플리케이션 재시작 등 하기 위해서 추가 ... 참조
  • Lombok : 코드 작성을 편하게 하기 위해서 추가 ... 참조
  • Spring web : REST API 작성하기 위해서 추가
  • Eureka Discovery Client : Eureka discovery 사용하기 위해서 추가

 

 

 

H2 데이터 베이스를 추가하겠습니다. 아래 dependancy를 pom.xml에 추가합니다.

        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
            <scope>runtime</scope>
        </dependency>

 

application.yaml에 가서 아래와 같이 추가합니다.

 

user-service를 재기동하고 eureka 서버에서 확인한 포트 번호로 들어가서 /h2-console을 붙여주면 아래와 같이 h2 데이터베이스에 접속할 수 있습니다.

 

 

 

회원가입 기능을 추가하겠습니다. 개요는 아래와 같습니다.

사용자가 보낸 정보는 JSON 형식으로 전달됩니다. 전달된 데이터는 RequestUser -> UserDto -> UserEntity 객체로 변환해서 사용하겠습니다. 용도에 맞게 객체를 변화시키는 것은 사용자 자유입니다. 전달된 데이터는 비지니스 로직을 거치고 데이터베이스에 접근합니다.

 

 

JPA (Java Persistence API)를 추가하겠습니다. JPA는 데이터베이스를 다루기 위한 query를 직접 java 코드에 작성하지 않고 다룰 수 있게 해주는 API입니다.

 

pom.xml에 추가할 Dependencies는 아래와 같습니다.

        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
            <scope>runtime</scope>
        </dependency>

		<!-- 회원가입에서 데이터를 담은 객체를 계속 변환시킬 때 유용한 라이브러리 -->
        <!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.4.4</version>
        </dependency>

        <!-- 빈 검사를 위한 라이브러리 -->
        <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

        <!-- 인증과 권한 설정 라이브러리-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

 

 

소스코드

 

최종적으로 사용자의 request로 DB에 데이터를 넣는 것까지 해보겠습니다.

 

현재 자바 파일의 위치는 아래와 같습니다.

 

사용자의 request의 body 값을 저장할 RequestUser.java를 먼저 만들겠습니다.

@Data
public class RequestUser {

    @NotNull(message = "Email cannot be null")
    @Size(min = 2, message = "Email cannot be less than two characters")
    @Email
    private String email;

    @NotNull(message = "Name cannot be null")
    @Size(min = 2, message = "Name cannot be less than two characters")
    private String name;

    @NotNull(message = "Password cannot be null")
    @Size(min = 8, message = "Password cannot be less than two characters")
    private String pwd;
}

 

RequestUser 데이터가 전환될 UserDto.java를 만들겠습니다.

@Data
public class UserDto {
    private String email;
    private String name;
    private String pwd;
    private String userId;
    private Date createAt;

    private String encryptedPwd;

}

 

UserDto 데이터가 전환될 UserEntity.java를 만들겠습니다.

@Data
@Entity // DB의 테이블과 매핑
@Table(name = "users") // 테이블 이름 명명
public class UserEntity {

    @Id // 기본키 매핑
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 생성 전략
    private Long id;

    @Column(nullable = false, length = 50, unique = true)
    private String email;
    @Column(nullable = false, length = 50)
    private String name;
    @Column(nullable = false, unique = true)
    private String userId;
    @Column(nullable = false, unique = true)
    private String encryptedPwd;
}

 

UserDto 객체를 다루기 위해서 UserService.java 인터페이스를 만들겠습니다.

public interface UserService {
    UserDto createUser(UserDto userDto);
}

 

UserService 인터페이스를 상속받아 수행할 UserServiceImpl.java를 만들겠습니다.

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDto createUser(UserDto userDto) {
        userDto.setUserId(UUID.randomUUID().toString());

        // UserDto -> UserEntity 변환
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserEntity userEntity = mapper.map(userDto, UserEntity.class);
        userEntity.setEncryptedPwd("encrypted_password"); // 이후에 암호화할 예정
        userRepository.save(userEntity);

        return null;
    }
}

 

UserEntity와 DB를 다룰 UserRepository.java 인터페이스를 만들겠습니다.

@Repository
public interface UserRepository extends CrudRepository<UserEntity, Long> {

}

 

이제 UserController.java에 request를 받아서 DB에 저장하는 코드를 만들겠습니다.

private Environment env;
private UserService userService;

@Autowired
public UserController(Environment env, UserService userService) {
    this.env = env;
    this.userService = userService;
}


////////////////// 중략 //////////////////


@PostMapping("/users")
    public ResponseEntity<ResponseUser> createUser(@RequestBody RequestUser user){

        // RequestUser -> UserDto 변환
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserDto userDto = mapper.map(user, UserDto.class);
        userService.createUser(userDto);

        // userDto -> ResponseUser 변환하여 결과 값으로 반환
        ResponseUser responseUser = mapper.map(userDto, ResponseUser.class);

        // 어떤 정보가 저장되었는지 반환하여 보여줄 수 있다.
        return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); // Status : 201, Body : ResponseUser
    }

 

저장된 값을 받아서 반환하기 위한 Value Object인 ResponseUser.java 만들겠습니다.

@Data
public class ResponseUser {
    private String email;
    private String name;
    private String UserId;
}

 

POSTMAN으로 요청을 보내면 201과 응답이 옵니다.

 

 

 

보안을 위해서 비밀번호를 암호화 하겠습니다.

 

위에서 이미 "spring-boot-starter-security"를 pom.xml에 dependency를 추가했습니다.

 

보안 설정을 위한 WebSecurity.java를 만들어 줍니다.

@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    // 권한 관련 함수를 override 하여 수정
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        // 해당 url로 들어온 요청은 인증없이 서비스 사용 가능 설정
        http.authorizeRequests().antMatchers("/users/**").permitAll();

        http.headers().frameOptions().disable(); // h2 db에 접근이 안됨
    }
}

 

위 코드에서 http.headers().frameOptions().disable()를 하지 않으면 아래와 같이 오류가 뜹니다. 현재 페이지에 다른 HTML 페이지를 포함시켜 띄우는 iframe 옵션 때문입니다. 비활성화 시켜서 띄울 수 있게 합니다.

 

암호화 위치는 개발자가 정합니다. UserEntity에 암호화하여 DB에 저장하겠습니다. 앞서 작성한 UserServiceImpl.java에서 조금만 바꾸어 줍니다.

@Service
public class UserServiceImpl implements UserService{

    UserRepository userRepository;
    BCryptPasswordEncoder passwordEncoder;


    @Autowired
    public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public UserDto createUser(UserDto userDto) {
        userDto.setUserId(UUID.randomUUID().toString());

        // UserDto -> UserEntity 변환
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserEntity userEntity = mapper.map(userDto, UserEntity.class);
        userEntity.setEncryptedPwd(passwordEncoder.encode(userDto.getPwd())); // 비밀번호 암호화
        userRepository.save(userEntity);

        return null;
    }
}

 

POSTMAN으로 요청을 보내고 H2 데이터베이스를 확인해 보면 암호화된 비밀번호를 확인할 수 있습니다.

 

 

 

사용자를 조회하는 서비스를 만들겠습니다.

 

UserService.java에 조회 기능을 추가합니다.

public interface UserService {
    UserDto createUser(UserDto userDto);

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

 

UserServiceImpl.java에 구현합니다.

@Service
public class UserServiceImpl implements UserService{

    UserRepository userRepository;
    BCryptPasswordEncoder passwordEncoder;

    @Autowired
    public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public UserDto createUser(UserDto userDto) {
        userDto.setUserId(UUID.randomUUID().toString());

        // UserDto -> UserEntity 변환
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserEntity userEntity = mapper.map(userDto, UserEntity.class);
        userEntity.setEncryptedPwd(passwordEncoder.encode(userDto.getPwd())); // 비밀번호 암호화
        userRepository.save(userEntity);

        return null;
    }

	// 유저 ID로 조회
    @Override
    public UserDto getUserByUserId(String userId) {
        UserEntity userEntity = userRepository.findByUserId(userId);

        // 조회할 유저가 없을 경우
        if(userEntity == null){
            throw new UsernameNotFoundException("User Not Found");
        }

        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();
    }
}

 

UserRePository.java에 DB에서 유저 ID로 조회하는 메소드를 만들어 줍니다.

@Repository
public interface UserRepository extends CrudRepository<UserEntity, Long> {

    UserEntity findByUserId(String userId);
}

 

조회할 데이터 항목을 위해서 ResponseUser.java를 만들어 줍니다.

@Data
@JsonInclude(JsonInclude.Include.NON_NULL) // JSON에서 null 값 무시하고 받을 수 있게 함
public class ResponseUser {
    private String email;
    private String name;
    private String UserId;

    private List<ResponseOrder> orders;
}

 

이제 UserController.java에 조회 기능을 수행하는 메소드를 만들어 줍니다.

@RestController // RESTful 웹 서비스 위해서. 객체 데이터를 JSON 형식으로 HTTP로 전송
@RequestMapping("/user-service/")
public class UserController {

    // application.yaml에 있는 설정 가져오는 방법 1 : vo(Value Object)에 주입하여 빈으로 등록하고 Autowired로 받기
    @Autowired
    private Greeting greeting;

    // application.yaml에 있는 설정 가져오는 방법 2 : Autowired와 생성자로 변수에 주입
    private Environment env;
    private UserService userService;

    @Autowired
    public UserController(Environment env, UserService userService) {
        this.env = env;
        this.userService = userService;
    }

    @GetMapping("/health_check")
    public String status() {
        return String.format("It's Working in User Service on PORT %s", env.getProperty("local.server.port"));
    }

    @GetMapping("/welcome")
    public String welcome() {
//        return env.getProperty("greeting.message"); // 생성자로 설정값 가져오기
        return greeting.getMessage(); // @Value로 설정값 가져오기
    }

    // ResponseEntity : Response Status를 설정
    @PostMapping("/users")
    public ResponseEntity<ResponseUser> createUser(@RequestBody RequestUser user){

        // RequestUser -> UserDto 변환
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserDto userDto = mapper.map(user, UserDto.class);
        userService.createUser(userDto);

        // userDto -> ResponseUser 변환하여 결과 값으로 반환
        ResponseUser responseUser = mapper.map(userDto, ResponseUser.class);

        // 어떤 정보가 저장되었는지 반환하여 보여줄 수 있다.
        return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); // Status : 201, Body : ResponseUser
    }

    // 모든 유저 조회
    @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 ResponseEntity.status(HttpStatus.OK).body(result);
    }

    // 유저 조회
    @GetMapping("/users/{userId}")
    public ResponseEntity<ResponseUser> getUser(@PathVariable("userId") String userId){ // PathVariable : userId를 받아서 매개변수로 건내줌
        UserDto userDto = userService.getUserByUserId(userId);

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

        return ResponseEntity.status(HttpStatus.OK).body(responseUser);
    }

}

 

POSTMAN으로 확인해보겠습니다. 먼저 데이터를 넣어줍니다. API-Gateway를 통해서 user-service/users에 접근하여 POST 방식으로 데이터를 저장했습니다.

 

같은 url이지만 GET 방식으로 모든 유저를 조회하겠습니다. 2개가 저장되어 있습니다.

 

userId를 하나 가져와서 사용자 한 명을 조회하겠습니다. 

 

끝.

반응형

'IT > Spring Cloud' 카테고리의 다른 글

Order Service  (0) 2021.12.15
Catalogs Microservice  (0) 2021.12.15
애플리케이션 개요  (0) 2021.12.08
API Gateway  (0) 2021.12.07
Service Discovery  (0) 2021.12.05