Users Microservice - JPA ①
VO 생성
RequestUser
@Data
public class RequestUser {
@NotNull(message = "Email cannot be null")
@Size(min = 2, message = "Email not me less than two characters")
@Email
private String email;
@NotNull(message = "Name cannot be null")
@Size(min = 2, message = "Name not be less than two characters")
private String name;
@NotNull(message = "Password cannot be null")
@Size(min = 8, message = "Password must be equal or grater than 8 characters")
private String pwd;
}
RequestUser vo를 DB에 저장하거나 다른 쪽으로 이동하려면 변환 작업이 추가로 필요하다.
Dto 생성
UserDto
import java.util.Date;
@Data
public class UserDto {
private String email;
private String name;
private String pwd;
private String userId;
private Date createdAt;
private String encryptedPwd;
}
service 생성
interface와 그 interface를 구현하는 java 클래스 생성
UserService
public interface UserService {
UserDto createUser(UserDto userDto);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
@Override
public UserDto createUser(UserDto userDto) {
userDto.setUserId(UUID.randomUUID().toString());
return null;
}
}
jpa 생성 (repository)
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
UserEntity
@Data
@Entity
@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;
}
UserRepository
// <데이터베이스랑 연결될 수 있는 Entity 정보, 기본키-클래스 타입>
public interface UserRepository extends CrudRepository<UserEntity, Long> {
}
Service 비즈니스 로직 추가
modelmapper dependency 추가
💡 ModelMapper 라이브러리
하나의 클래스가 또 다른 클래스로 변형을 해주기 위해서 간단하게 사용할 수 있는 라이브러리
Getter, Setter 함수를 이용해서 변환하는건 번거롭다.
<dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>3.1.1</version> </dependency>
UserServiceImpl
ModelMapper 라이브러리를 이용해서 간단하게 UserDto를 UserEntity로 변환한다.
mapper를 사용하기 전에 mapper에 대한 설정을 해야하는데 그 부분은 getConfiguration() 코드로 설정한다. 위 코드는 매칭 전략과 관련한 설정인데, 완벽한 매칭이 되지 않으면(STRICT) 매핑하지 않는다는 설정이다.
직접적으로 UserDto가 UserEntity로 변환되는 코드는 mapper.map(userDto, UserEntity.class)이다.
암호화된 비밀번호는 아직 관련 기능이 추가되지 않았으므로 기본 값을 저장해놓고 save를 한다.
@Service
public class UserServiceImpl implements UserService{
UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDto createUser(UserDto userDto) {
userDto.setUserId(UUID.randomUUID().toString());
// ✔ UserDto -> UserEntity 변환 작업(ModelMapper 사용)
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // 매칭전략을 강력하게(딱 맞아떨어지지 않으면 지정할수 없도록) 설정
UserEntity userEntity = mapper.map(userDto, UserEntity.class); // UserEntity로 변환
userEntity.setEncryptedPwd("encrypted_password"); // 값이 아직 구현이 안됐기 때문에 기본값을 넣어두겠다.
userRepository.save(userEntity);
return null;
}
}
UserController createUser() 추가
컨트롤러에서는 RequestUser 객체를 받고, 서비스 단에는 RequestUser 객체를 UserDto로 전달해줘야한다. 그렇기 때문에 컨트롤러에서도 매핑 작업을 한 번 해줘야한다.
// ... 생략
private UserService userService;
@Autowired
public UserController(Environment env, Greeting greeting, UserService userService){
this.env = env;
this.greeting = greeting;
this.userService = userService;
}
// ... 생략
@PostMapping("/users")
public String 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);
return "Create user method is called";
}
Request → UserDto → UserEntity
서버 실행(혹은 재실행)
유레카 대시보드에서 링크를 클릭 후 포트 번호를 확인한다. POST의 경우 웹 브라우저에서는 프론트 단이 없으면 테스트할 수 없기 때문에 포스트맨에서 확인한다.
200 OK가 떨어졌으면 h2 데이터베이스도 확인해보자.
USERS테이블을 클릭하고 RUN버튼을 클릭하면 데이터가 들어온 것을 확인할 수 있다.
🚨 [ERROR] h2 테이블 생성 안됨
강의에서는 이 정도만 해도 진행하는 데에 오류가 없었지만, 나는 h2에서 테이블이 생성되지 않았다.
알아 보았을 때 application.yml에서 ddl-auto를 create로 바꿨더니 됐다.
jpa:
hibernate:
ddl-auto: create
포스트맨을 보면 요청의 결과 상태 코드를 '200 OK'를 받았는데 POST Method의 경우 응답 상태 코드를 200이 아닌 '201 Created'로 받아야 조금 더 정확하고 명확하다.
creatUser() 타입을 ResponseEntity로 바꿔주자.
@PostMapping("/users")
public ResponseEntity 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);
return new ResponseEntity(HttpStatus.CREATED);
}
서버 재실행 후 포스트맨에서 상태 코드를 확인한다.
ReponseEntity
사용자 추가가 성공적으로 끝났을 때, 사용자에게 이메일과 이름, 그리고 새롭게 자동으로 부여되는 userId를 보여주자.
ResponseUser VO 생성
@Data
public class ResponseUser {
private String email;
private String name;
private String userId;
}
Controller createUser() 수정
@PostMapping("/users")
public ResponseEntity 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);
ResponseUser responseUser = mapper.map(userDto, ResponseUser.class);
return ResponseEntity.status(HttpStatus.CREATED).body(responseUser);
}
service createUser() 수정
@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);
UserDto returnUserDto = mapper.map(userEntity, UserDto.class);
return returnUserDto;
}
UserDto가 반환 타입이므로 UserEntity를 UserDto로 다시 변환해 컨트롤러에 그 객체를 return한다.
서버 재실행 후 확인한다.
ResponseEntity<ResponseUser> 가 반환되었다.
💡 JPA (Java Persistence API)
Query없이 데이터를 다룰 수 있도록 지원