스프링 부트로 비즈니스 로직을 구현할 때 엔티티를 DTO*로 변환하거나 리스트 데이터를 가공해야 하는 경우가 많다. 이때 자주 쓰게 되지만 헷갈리는 것이 from(), map(), collect()인데, 이 셋의 스펙과 활용법을 정리해보려 한다.
from() - 객체 간의 전환
보통 DTO 클래스 내부에 정적 메서드**로 구현하며, 어떤 객체로부터 이 DTO가 만들어지는가를 명확히 선언한다.
- Entity → DTO
- 생성자 대신
from을 쓰는 이유****- 읽기 훨씬 자연스러워서 의도가 명확하다. (예:
new QuestionResponseDto(...)보다QuestionResponseDto.from(question)이 읽기 자연스러움) - 서비스 로직에서 일일이
Getter를 호출해 값을 세팅할 필요가 없다. 변환 로직이 DTO 내부에 있으므로 서비스 코드가 깔끔해진다.
- 읽기 훨씬 자연스러워서 의도가 명확하다. (예:
from()
자바 표준 Stream 인터페이스에는 from()이라는 인스턴스 메서드가 없다. 대신 자바의 여러 클래스에서 정적 팩토리 메서드 관례로 사용된다.사례날짜, 시간 관련LocalDateTime.from(temporal)처럼 다른 시
ubukki.tistory.com
map() - 데이터 모양 바꾸기
데이터의 개수는 유지하면서 그 내부 형태만 바꿀 때 사용한다. 지금 작업에서 자주 쓰는 것은 두 종류의 map이다.
- Java Stream의
mapList<Question>을Stream***으로 만든 뒤 하나씩 꺼내 DTO로 변환할 때 사용 - Spring Data Page의
map()
DB에서 페이징 처리된Page<Question>을Page<QuestionResponseDto>로 바꿀 때 사용
- 별도의
collect()과정이 필요 없음.Page객체가 가진 페이지 번호, 전체 개수 같은 메타데이터는 유지하면서 엔티티만 DTO로 바꿔준다.
map()
데이터의 변환map()은 주로 Stream이나 Optional 인터페이스에서 사용된다. 스트림 내부의 요소를 차례대로 다른 형태로 변환하는 역할을 한다. Stream map(Function mapper);입력타입 T를 받아서 결과타입 R
ubukki.tistory.com
collect() - 흩어진 데이터를 모으기
map으로 변환된 데이터들은 아직 스트림 상태다. 이를 실제 우리가 사용할 수 있는 리스트 형태로 바궈주는 것이 collect()다.
- 스트림의 요소들을
List,Set등의 컬렉션으로 취합함
collect()
데이터의 취합collect()는 스트림의 요소들을 모아서 리스트, 셋, 맵과 같은 컬렉션이나 다른 형태의 결과물로 만드는 최종 연산이다. R collect(Collector collector);가변 축소(Mutable Reduction): 스트림의 요
ubukki.tistory.com
요약
public List<QuestionResponseDto> getAllQuestions() {
return questionRepository.findAll().stream()
.map(QuestionResponseDto::from)
.collect(Collectors.toList());
}
| 단계 | 코드 | 상태 |
| Source | repository.findAll() | List<Entity> |
| Stream | .stream() | Stream<Entity> |
| Map | .map(Dto::from) | Stream<Dto> |
| Collect | .collect(Collectors.toList()) | List<Dto> |
이 세 가지 메서드만 잘 활용해도 서비스 레이어의 가독성이 비약적으로 상승한다.
참고
* DTO: Data Transfer Object, 계층 간 데이터 전송을 위한 객체. DB 설계를 그대로 담은 Entity를 클라이언트에 직접 노출하지 않기 위해 사용한다. 필요한 데이터만 골라 담는다.
** 정적 팩토리 메서드(Static Factory Method): 객체 생성을 담당하는 클래스 내의 static 메서드이다. 이름을 가질 수 있어 생성 의도를 드러내기 좋고, 매번 새로운 객체를 생성하지 않아도 되는 등 유연한 설계가 가능하다. 예; from(), of()
*** 스트림 (Stream): Java 8부터 도입된 데이터의 흐름이다. 컬렉션(List, Set 등)에 저장된 요소들을 하나씩 참조하면서 선언적인 스타일(어떻게가 아닌 무엇을)로 가공하고 처리한다.
**** 보통 객체를 만들 때 new QuestionResponseDto(quesion) 처럼 생성자를 직접 부른다. 하지만 from이라는 정적 팩토리 메서드를 쓰면 다음과 같은 차이가 있다. 생성자는 한 클래스의 인스턴스를 만들지만, from은 특정 엔티티로부터 정보를 추출해서 응답용 객체 DTO를 만드는 의도적 작업을 수행한다. 즉 ㄱ객체 생성의 목적이 명확해지고, DTO 내부에서 엔티티의 어떤 필드를 사용할지 결정하므로 서비스 로직이 DTO 내부를 몰라도 되는 캡슐화적인 장점이 있다.
'Java' 카테고리의 다른 글
| from() (0) | 2026.01.26 |
|---|---|
| collect() (0) | 2026.01.26 |
| map() (0) | 2026.01.26 |
| Java의 < > 기호 - 제네릭(Generics) (0) | 2026.01.26 |