JPA 초기 구현 시, API 요청 JSON 데이터를 바로 엔티티에 적용하여 개발했다.
하지만, 이렇게 코드를 구현하면서 문득 든 생각이 있었으니...
'만약 API 스펙이 변경되면 엔티티와 매핑이 되는건가...?'
나의 의심은 역시나.. 실무에서 엔티티를 외부에 노출시키거나, 파라미터로 받으면 절대 절대!! 안된다!
그 이유를 한번 살펴보겠다.
1. 보안
회사 및 서비스 데이터베이스 정보는 굉장히 민감한 정보이다. 엔티티를 직접 파라미터로 받거나 노출시킨다면, API를 호출하는 과정에서 엔티티에 대한 정보가 모두 노출될 수 있다. 이는 곧 서비스의 민감한 정보가 노출될 수 있음을 의미한다.
2. 유연성
API 요청 스펙이 변경되면, 엔티티의 구조도 같이 변경해야할 수도 있다. 예를 들어, User라는 엔티티에 uuid라는 컬럼이 있고, API 요청 스펙중 uuid라는 key가 있는 상황에서 uuid -> uuids 로 변경된다면, User 엔티티의 uuid 컬럼도 변경되어야한다. 데이터베이스 구조가 수시로 변경되면... 생각만해도 아찔하다..
하지만, DTO를 사용한다면, DTO의 멤버 변수명만 변경해주면 된다. 어처피 getter를 사용하여 엔티티에 값을 넣어주기 때문에 유연성이 보장된다!
3. 검증 및 가공
API 요청 데이터를 엔티티에 넣어주기 전에 데이터를 검증할 수 있다. 예를들어 @Vaild를 사용하여 DTO에 @NotNull, @Email 등을 사용하여 요청 데이터의 유효성을 검증할 수 있다. 또한 getter로 데이터를 받아오기 때문에 데이터를 필요한 형태로 가공할 수 있다.
4. 예시코드
4-1. Entity를 직접 사용했을 경우
// Entity
@Entity
public class User{
@Id
private String uuid;
private String username;
private String email;
}
// Controller
@RestController
@RequestMapping("/user")
public class UserContoller{
private UserSevice userSerivce;
@Autowired
public UserContoller(UserSevice userSerivce){
this.userSerivce = userSerivce;
}
@PostMapping("/v1/createuser")
public String userCreate(@RequestBody User user){
return userSerivce.createUser(user).getBody();
}
}
4-2. DTO를 사용했을 경우
// DTO
@Data
public class UserDto{
// 해당 컬럼 데이터에 맞는 유효성 검증 제공
@NotNull
private String uuid;
@NotNull
private String username;
@Email
private String email;
}
// Entity
@Entity
public class User{
@Id
private String uuid;
private String username;
private String email;
@Builder
public User(String uuid, String username, String email){
this.uuid = uuid;
this.username = username;
this.email = email;
}
}
// Controller
@RestController
public class UserContoller{
private UserSevice userSerivce;
@Autowired
public UserContoller(UserSevice userSerivce){
this.userSerivce = userSerivce;
}
// DTO를 통해 API 요청데이터를 받아옴 -> @Vaild를 통해 데이터 유효성 검증
@PostMapping("/v1/createuser")
public String userCreate(@RequestBody @Vaild UserDto userDto){
return userSerivce.createUser(userDto).getBody();
}
}
// ServiceImpl
@Service
@Transactional
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository){
this.userRepository = userRepository;
}
public ResponseEntity<String> createUser(UserDto userDto){
try{
User user = User.builder()
.uuid(userDto.getUuid())
.username(userDto.getUsername())
.email(userDto.getEmail())
.build();
userRepository.save(user);
return new ResponseEntity<String>("200", HttpStatus.OK);
}catch(Exception e){
e.printStackTrace();
return new ResponseEntity<String>("400", HttpStatus.BAD_REQUEST);
}
}
}
5. 마무리
위와 같은 이슈는 실무에서 더욱 중요시되는 부분인 것 같다. 단순 기능 구현에만 치중하여 개발해선 안되며, 유연성, 확장성, 보안성 등 개발하는데에 있어 얼마나 중요한 요소인지 다시 한번 깨닫게 되었다.
'웹 개발 공부 : Back-end > JPA' 카테고리의 다른 글
JPA 초기 구현 시, API 요청 JSON 데이터를 바로 엔티티에 적용하여 개발했다.
하지만, 이렇게 코드를 구현하면서 문득 든 생각이 있었으니...
'만약 API 스펙이 변경되면 엔티티와 매핑이 되는건가...?'
나의 의심은 역시나.. 실무에서 엔티티를 외부에 노출시키거나, 파라미터로 받으면 절대 절대!! 안된다!
그 이유를 한번 살펴보겠다.
1. 보안
회사 및 서비스 데이터베이스 정보는 굉장히 민감한 정보이다. 엔티티를 직접 파라미터로 받거나 노출시킨다면, API를 호출하는 과정에서 엔티티에 대한 정보가 모두 노출될 수 있다. 이는 곧 서비스의 민감한 정보가 노출될 수 있음을 의미한다.
2. 유연성
API 요청 스펙이 변경되면, 엔티티의 구조도 같이 변경해야할 수도 있다. 예를 들어, User라는 엔티티에 uuid라는 컬럼이 있고, API 요청 스펙중 uuid라는 key가 있는 상황에서 uuid -> uuids 로 변경된다면, User 엔티티의 uuid 컬럼도 변경되어야한다. 데이터베이스 구조가 수시로 변경되면... 생각만해도 아찔하다..
하지만, DTO를 사용한다면, DTO의 멤버 변수명만 변경해주면 된다. 어처피 getter를 사용하여 엔티티에 값을 넣어주기 때문에 유연성이 보장된다!
3. 검증 및 가공
API 요청 데이터를 엔티티에 넣어주기 전에 데이터를 검증할 수 있다. 예를들어 @Vaild를 사용하여 DTO에 @NotNull, @Email 등을 사용하여 요청 데이터의 유효성을 검증할 수 있다. 또한 getter로 데이터를 받아오기 때문에 데이터를 필요한 형태로 가공할 수 있다.
4. 예시코드
4-1. Entity를 직접 사용했을 경우
// Entity
@Entity
public class User{
@Id
private String uuid;
private String username;
private String email;
}
// Controller
@RestController
@RequestMapping("/user")
public class UserContoller{
private UserSevice userSerivce;
@Autowired
public UserContoller(UserSevice userSerivce){
this.userSerivce = userSerivce;
}
@PostMapping("/v1/createuser")
public String userCreate(@RequestBody User user){
return userSerivce.createUser(user).getBody();
}
}
4-2. DTO를 사용했을 경우
// DTO
@Data
public class UserDto{
// 해당 컬럼 데이터에 맞는 유효성 검증 제공
@NotNull
private String uuid;
@NotNull
private String username;
@Email
private String email;
}
// Entity
@Entity
public class User{
@Id
private String uuid;
private String username;
private String email;
@Builder
public User(String uuid, String username, String email){
this.uuid = uuid;
this.username = username;
this.email = email;
}
}
// Controller
@RestController
public class UserContoller{
private UserSevice userSerivce;
@Autowired
public UserContoller(UserSevice userSerivce){
this.userSerivce = userSerivce;
}
// DTO를 통해 API 요청데이터를 받아옴 -> @Vaild를 통해 데이터 유효성 검증
@PostMapping("/v1/createuser")
public String userCreate(@RequestBody @Vaild UserDto userDto){
return userSerivce.createUser(userDto).getBody();
}
}
// ServiceImpl
@Service
@Transactional
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository){
this.userRepository = userRepository;
}
public ResponseEntity<String> createUser(UserDto userDto){
try{
User user = User.builder()
.uuid(userDto.getUuid())
.username(userDto.getUsername())
.email(userDto.getEmail())
.build();
userRepository.save(user);
return new ResponseEntity<String>("200", HttpStatus.OK);
}catch(Exception e){
e.printStackTrace();
return new ResponseEntity<String>("400", HttpStatus.BAD_REQUEST);
}
}
}
5. 마무리
위와 같은 이슈는 실무에서 더욱 중요시되는 부분인 것 같다. 단순 기능 구현에만 치중하여 개발해선 안되며, 유연성, 확장성, 보안성 등 개발하는데에 있어 얼마나 중요한 요소인지 다시 한번 깨닫게 되었다.