몇개에 실무 프로젝트를 진행하면서 많이 배우고 느낀 것 중 하나는 데이터 전달 방식이다.
실무에서는 데이터를 주고받을 때 여러 방식이 사용되는데, 주요 방식으로는 Model 클래스 사용, Map<String, Object> 사용, DTO(Entity) 사용 등이 있다. 각각의 방식이 효율적인 경우와 장단점에 대해 생각해봤다.
1. Model 클래스 사용
Model은 Spring MVC에서 View와 데이터를 공유하는 용도로 사용
- 주로 컨트롤러에서 화면(View)으로 데이터를 넘길 때 사용됨.
- Thymeleaf, JSP 같은 템플릿 엔진에서 활용됨.
예시
@Controller
public class UserController {
@GetMapping("/user")
public String getUser(Model model) {
User user = new User(1, "민민", "minmin@example.com");
model.addAttribute("user", user);
return "userView"; // userView.html로 데이터 전달
}
}
@Getter
@Setter
public class User {
private int id;
private String name;
private String email;
public User(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
모델 클래스 만들고 -> getter & setter 로 데이터 주고 받음
- 장점
- Thymeleaf, JSP 같은 템플릿 엔진과 함께 사용하기 편리함.
- 키 값을 잘못 입력할 일이 없고, 정형화된 데이터 구조를 유지할 수 있음.
- 단점
- API 응답으로 사용하기에는 부적절함.
- JSON 응답을 할 때는 DTO를 사용하는 것이 일반적임.
2. Map<String, Object> 사용
Map을 사용하면 유연하게 데이터를 담을 수 있음.
- 컬럼 개수가 일정하지 않거나, 동적으로 데이터를 담아야 할 때 사용됨.
- 공통적으로 데이터를 전달할 때도 활용됨.
예시
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/info")
public Map<String, Object> getUserInfo() {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", 1);
userInfo.put("name", "민민");
userInfo.put("email", "minmin@example.com");
userInfo.put("age", 28); // 나중에 동적으로 추가할 수도 있음.
return userInfo; // JSON 형태로 반환됨
}
}
모델 클래스(엔티티) 만들지 않아도 key-value로 담아서 데이터 전달 가능
- 장점
- 키-값 형태이기 때문에 원하는 데이터를 동적으로 추가할 수 있음.
- API 응답으로 사용할 경우 직관적이고 가볍게 사용 가능.
- 단점
- 타입 안정성이 없음 → 잘못된 타입을 넣어도 컴파일 단계에서 체크되지 않음.
- 오타 등으로 인해 userInfo.get("email")을 사용할 때 문제가 발생할 수 있음.
- 명확한 구조가 없어서 코드 가독성이 떨어질 수 있음.
3. 엔티티(Entity) & DTO 사용
Spring Boot에서는 DTO(Data Transfer Object) 패턴을 많이 사용함.
- API 응답을 JSON으로 줄 때 가장 많이 사용됨.
- DB와 연관된 엔티티(Entity)와 분리하여 사용해야 유지보수에 좋음.
예시
1) 엔티티 (JPA 사용)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String address;
private int age;
}
2) DTO (API 응답용)
@Getter
@Setter
public class UserDTO {
private Long id;
private String name;
private String email;
public UserDTO(User user) {
this.id = user.getId();
this.name = user.getName();
this.email = user.getEmail();
}
}
3) 컨트롤러에서 사용
@RestController
@RequestMapping("/user")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/{id}")
public UserDTO getUserById(@PathVariable Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
return new UserDTO(user);
}
}
엔티티를 만들고 특정 메서드에서 필요한 전송 객체를 DTO에 담아줌. (로그인일 시 User의 id와 pw만 넣는 식)
- 장점
- DTO는 API 응답을 JSON으로 변환하는데 최적화됨.
- 데이터 구조가 명확하여 유지보수가 용이함.
- Entity와 DTO를 분리하면 JPA 연관관계와 무관하게 DTO로만 데이터를 전달할 수 있음.
- 단점
- 데이터를 담기 위해 클래스를 추가로 만들어야 해서 코드가 많아짐.
- 간단한 응답을 할 때도 DTO 클래스를 만들어야 하는 번거로움이 있음.
정리
방법 | 장점 | 단점 | 추천 사용 시기 |
Model 사용 | View와 데이터 공유 용이 | API 응답에는 적절하지 않음 | 템플릿 엔진(Thymeleaf, JSP)에서 사용 |
Map<String, Object> | 동적으로 데이터 추가 가능 | 타입 안정성이 없음, 키 값 오타 가능성 | 응답 데이터 구조가 일정하지 않을 때 |
DTO & Entity 사용 | 유지보수 용이, API 응답 최적화 | 클래스 추가 필요, 코드 증가 | API 개발 시 권장 |
결론
- Spring MVC의 View에서는 Model 사용.
- API 응답에는 DTO를 사용하고, 가벼운 데이터는 Map<String, Object>를 활용.
- DB와 연관된 엔티티를 바로 반환하지 말고, DTO로 변환해서 전달하는 것이 유지보수에 좋음.
개인적인 생각
Model 클래스는 불편함이 많은 것 같다. Map<String, Object>나 DTO&Entity의 장점은 명확하지만 Model 클래스 사용할 때가 가장 불편하게 느꼈다. 매퍼에 파라미터 타입을 그 클래스로 받으면 커스텀할 때 너무 제약적이라고 생각. (차라리 Map<String, Object>가 더 유동적이고 가시성이 좋아보임)
Map<String, Object> 는 SI 프로젝트에서는 자주 사용되는 것을 볼 수 있었다. 규모가 크고 테이블과 제약조건이 많은 차세대 프로젝트 (기존에 ASIS 프로그램이 있음)는 사실 Model이나 Entity 만드는게 일이라고 생각하는 것 같다.
이유로는 음.... 고객에 요구사항 변경(컬럼 삭제, 수정, 추가 등등)이 있을 수도 있고, 데이터는 정말 중요하고 체계적으로 잡아야해서? 유지보수는 힘들지만 개발할 때 유용하기 때문에 많이 사용하는 것 같음. 근데 위에 작성한 내용처럼, 컬럼에 대한 검증이 안 된다. (오타나면 사고)
Entity & DTO는 API명세서가 명확할 때, 유지보수도 편하고 DTO에 커스텀해서 사용해서 사용하면 도메인에 따라 잘 나뉘어져 있어서 가장 좋은 것 같다고 생각이 든다.
프로젝트 상황에 따라 다르겠지만, 데이터를 보내고 받고 하는 방식은 대부분 비슷하기 때문에 찾아보고 이해한다면 무엇을 쓰든 상관없을 것 같다.
'실무' 카테고리의 다른 글
[DB] CONNECT BY와 WITH RECURSIVE 계층형 데이터 조회 | 민민의 하드디스크 - 티스토리 (1) | 2025.03.23 |
---|---|
[DB] 트랜잭션과 데이터베이스 락(Transction & DB Lock) | 민민의 하드디스크 - 티스토리 (0) | 2025.03.13 |
[DB] ROWNUMBER와 RANK의 차이 | 민민의 하드디스크 - 티스토리 (1) | 2025.03.09 |
[DB] ROWNUM 사용 시 주의점/활용법 | 민민의 하드디스크 - 티스토리 (0) | 2025.03.07 |
[DB]Mybatis insert/update 시퀀스 관리 | 민민의 하드디스크 - 티스토리 (0) | 2025.03.07 |