본문 바로가기
Develop/Java

stream() - 함수형 프로그래밍

by ys2ys2 2025. 6. 22.

 

함수형 프로그래밍

함수형 프로그래밍이란

데이터를 바꾸지 않고, 함수에 값을 넣어서 결과를 얻는 것에 집중하는 프로그래밍이다.

함수는 "입력"을 받아서 "출력"을 만들고 그 과정에서 프로그램의 다른 부분(변수 등등)에 영향을 주지 않는다.

이렇게 되면 어떤 값이 주어졌을 때 항상 같은 결과를 반환한다.

 

함수형 프로그래밍은

  • 순수 함수 : 동일 입력에는 항상 동일 출력
  • 불변성 : 데이터를 변경하지 않고 새 데이터 생성
  • 고차 함수 : 함수를 인자로 전달, 함수 반환 가능
  • 선언적 프로그래밍 : "어떻게"가 아니라 "무엇을"에 집중

이러한 특징이 있다.

 


 

 

Stream()

Stream()은 자바에서 제공하는 기능으로

리스트나 배열 같은 데이터 묶음을 한 줄씩 반복하지 않고 한 번에 여러 작업(map, filter 등)을 함수처럼 연결해서 처리할 수 있게 해 주며 데이터를 직접 바꾸지 않고 결과만 새로 만들어진다.

 

 


 

 

for문 → stream() + map()

for문 방식

여러 개의 데이터에서 원하는 조건으로 묶은 후, 묶은 정보들을 한 번에 모아서 리턴하려고 했었다.

기존에는 for문으로 데이터를 하나씩 꺼내서 각각의 데이터를 원하는 조건으로 다시 가공하고

가공한 데이터를 하나의 DTO로 만들어서 result에 쌓아서 return 하는 방식이였다.

이렇게 절차형으로 백엔드를 짰었다.

 

stream/map, filter

이걸 함수형 프로그래밍을 써서 stream/map 방식으로 바꾸는 게 더 좋을 것 같다는 리뷰를 받았다.

기존 for문으로는 데이터 반복을

for (Data data : dataList) {
 // 각 데이터 처리
}

이렇게

 

stream에서는

dataList.stream()

이렇게 데이터를 stream으로 변환해서 처리하게 했다.

 

그런 후 특정 필드 기준으로 그룹화를 하기 위해

기존 for문

Map<Long, List<Data>> groupMap = new HashMap<>();

for(Data data : dataList) {
	Long infoId = data.getInfoId();
    if(!groupMap.containsKey(infoId) {
    	groupMap.put(infoId, new ArrayList<>());
    }
    groupMap.get(infoId).add(data);
}

 

stream으로 바꿨다.

.collect(Collectors.groupingBy(Data::getInfoId))

 

 

그룹핑된 데이터를 반복 처리하기 위해

for문에서는 각 데이터마다 별도로 처리했지만

stream에서는

.entrySet().stream()

을 통해 그룹핑된 각 infoId, List<Data> 쌍을 stream으로 변환했다.

 

그 후 Info 정보 조회 및 ResponseDTO로 변환했다.

기존 for문에서는

Info info = infoRepository.findById(infoId).orElse(null);
if (info == null) continue;
ResponseDTO dto = new ResponseDTO();
dto.setInfoId(...);
// 필요한 정보 셋팅
result.add(dto);

 

이렇게 했지만

stream에서는 map을 사용해서 entry(info, List<Data> 쌍)을 ResponseDTO 객체로 변환했다.

.map(entry -> toResponseDTO(entry.getKey(), entry.getValue()))

 

 

그 후 잘못된(없는) info에 대한 필터링 조건은

기존 for문

if (info == null) continue;

 

stream의 filter를 사용해서

.filter(Objects:nonNull)

 

로 바꿨다.

 

이런 결과를 List로 수집하는 부분은

for문

result.add(dto);
//...
return result;

 

stream

.collect(Collectors.toList())

 

로 처리했다.

 

최종 stream

return dataList.stream()
	.collect(Collectors.groupingBy(Data::getInfoId))
    .entrySet().stream()
    .map(entry -> toResponseDTO(entry.getKey(), entry.getValue()))
    .filter(Objects:nonNull)
    .collect(Collectors.toList());

 

 

 


 

이렇게 하니까 코드가 훨씬 짧고 명확해졌다.

stream의 .groupingBy(), .map(), .filter()등을 함수로 연결해서 사용하니까 데이터의 흐름이 한눈에 보여서 가독성도 좋아졌다.

또 for문은 result 리스트 등 상태를 직접 관리하니까 실수가 발생할 수 있는데

stream()은 원본 데이터 변경 없이 처리하기 때문에 더욱더 안정성이 생겼다.

또 stream에다가 filter, map, group등으로 확장과 변경이 쉬워졌다.

List<DTO> result = new ArrayList<>();
for (...) {
//1. 조건
//2. 변환
//3. 예외처리
//4. 직접 add
}

 

return DataList.stream()
	.filter(...)
	.map(...)
    .collect(Collectors.toList());

 


 

 

이렇게 코드리뷰 받아서 새로운 방식도 찾아보면서 공부하고 상황마다 어떤 방식이 좋은지 고민하면서 배우는 게 재밌다!

코드리뷰 받을때 기본적인 걸 놓쳐서 받는 경우엔 조금 민망하고 그렇지만,

이렇게 다양한 방법으로 공부할 수 있다는 게 좋은 것 같다!

'Develop > Java' 카테고리의 다른 글

MVC 이해하기  (2) 2024.10.12
0903  (2) 2024.09.07
0902  (0) 2024.09.06