Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240716 단일 항목에 Lambda표현식을 쓴 이유? 본문

개발Article

[TIL]20240716 단일 항목에 Lambda표현식을 쓴 이유?

최밤빵 2024. 7. 17. 01:17

나는 그냥 람다식이 좋은건데..!! 기술 매니저님이 질문하실 때 마다 강사님이 썼기때문에? 그거아니여도 코드가 간결해져서 좋다! 그거 하나 뿐이라 얼마전에 람다식에 대해서 개발일지를 썼지만, 내가 이 질문을 받게 된 서비스 계층 코드를 보면서 특정 ID로 조회한 단일 항목에 대해서도 람다표현식을 사용한 부분에 대해 이유를 생각하면서 이해를 위해 정리를 시작했다. (사실 gpt도 나한테 람다표현식만 주구장창 보여주는 이유도 있다..!)

 

▶ 람다 표현식 (Lambda Expression) 이란? 

람다 표현식은 Java 8에서 도입된 함수형 프로그래밍의 한 기능으로, 익명 함수(Anonymous Function)를 간결하게 표현하는 방법이다. 람다 표현식을 사용하면 불필요한 코드 작성 없이 함수형 인터페이스를 구현할 수 있고, 코드의 가독성을 높이고 유지보수를 쉽게 해준다. 예를 들어, 리스트를 돌면서 각 요소를 처리하는 경우 람다 표현식을 사용하면 코드가 간결하고 명확해진다.

 

▶ 단일 항목에 람다를 사용한 내 코드 

MemoService 클래스에서 단일 메모 항목을 조회할 때도 람다 표현식을 사용했다. getMemo() 메서드에서는 findById() 메서드를 사용하여 Optional<Memo> 객체를 반환받고, 여기에 람다 표현식을 사용해 데이터를 변환하거나 예외를 처리했다.

public MemoResponseDto getMemo(long id) {
    return memoRepository.findById(id)
            .map(MemoResponseDto::new)
            .orElseThrow(() -> new IllegalArgumentException("Memo not found"));
}

 

→  코드에서 findById() 메서드는 Optional<Memo> 객체를 반환한다. Optional은 Java 8에서 도입된 컨테이너 클래스로, 값이 존재할 수도 있고 존재하지 않을 수도 있는 경우를 안전하게 처리하는 데 사용된다.

 

▶ "단일 항목에 람다 표현식을 사용한 이유가 뭔가요?" 라는 질문에 내가 해야하는 대답?

 

1. 간결한 데이터 변환 및 예외 처리?

→ 람다 표현식을 사용하면 Optional의 map() 메서드를 통해 조회된 엔티티를 직접 MemoResponseDto로 변환할 수 있다. 이는 코드의 가독성을 높이고, 데이터 변환 로직을 간결하게 유지할 수 있게 해준다.

→ orElseThrow() 메서드를 통해 엔티티가 존재하지 않을 경우 예외를 발생시킬 수 있다. 이를 통해 예외 처리 로직을 일관되게 관리할 수 있다.

2. 안전한 null 처리?

→ Optional을 사용한 람다 표현식은 null 처리를 안전하게 한다. 만약 조회된 메모가 존재하지 않는다면, map() 메서드는 실행되지 않고, orElseThrow()가 실행되어 IllegalArgumentException 예외를 던진다. 이를 통해 NPE(NullPointerException)와 같은 문제를 예방할 수 있다.

3. 가독성 및 유지보수성 향상?

→ 람다 표현식을 사용하면 코드의 구조가 간결해지고 명확해진다. 조건문이나 불필요한 로직을 제거할 수 있어서, 코드의 유지보수성이 향상된다. 특히, 예외 처리와 데이터 변환 로직을 한 줄에 표현함으로써 코드의 흐름을 쉽게 파악할 수 있다.

4. 함수형 프로그래밍 스타일의 일관성?

→ 서비스 계층의 다른 메서드들과 동일한 함수형 스타일로 작성하여 일관성을 유지할 수 있다. getMemos()와 같은 메서드에서도 stream()과 map()을 사용해 리스트를 처리하는 것처럼, 단일 항목도 Optional의 map() 메서드를 통해 일관되게 처리할 수 있다. 이는 코드 스타일의 통일성을 유지하고, 코드베이스 전체의 가독성을 높이는 데 도움이 된다.

5. 명시적인 의도 표현?

람다 표현식을 사용함으로써 "이 엔티티를 DTO에 매핑하거나, 존재하지 않는 경우 예외를 발생시킵니다"라는 명시적인 의도를 코드에서 바로 읽을 수 있다. 코드의 의도를 명확히 하고, 유지보수 시 코드의 목적을 쉽게 파악할 수 있게 된다.

 

▶ 코드 예시를 통해 장점을 찾는다면? 

코드 예시를 통해, 람다 표현식이 어떻게 단일 항목에 대한 데이터를 처리하고, 예외를 처리하며, 코드의 간결성과 가독성을 높이는지 살펴보았다. 람다 표현식을 사용함으로써 반복적인 코드 작성을 피할 수 있고, 명확한 의도를 가진 일관된 예외 처리 방식을 알 수 있다. 

 

▶ 정리 

왜 나는 ... 람다 표현식을 썼을까. 애초에 모르는 걸 쓰는게 아니였는데, 코드가 너무 간결해지니까 단지 좋다고 그 이유 하나로만 썼다. 사실 for로 풀어도 그렇게 길어지지않는 코드기 때문에 굳이 람다를 쓸 필요는 없었다. ( 사실 간결서 라고 했다가 직접 풀어서써봐주시더니 그렇게 안 길어지는데 굳이? 란 소리를 들음). 본인 코드에 대해 설명하지 못하면 본인 꺼 아니라고 리뷰 때 마다 듣는 터라 꼭꼭 알아내야 했다. 람다표현식은 코드의 가독성을 높이고, 안전한 데이터 처리와 예외처리를 가능하게한다. 간결하고 유지보수성이 좋으니까... 앞으로는 람다표현힉을 쓰면 꼭 설명할 수 있기를..! 

 

추가 +)

예시 코드 조각의 서비스클래스 전체코드 

package com.sparta.memo.service;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class MemoService {
    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }

    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        Memo memo = new Memo(requestDto);
        memoRepository.save(memo);
        return new MemoResponseDto(memo);
    }

    public List<MemoResponseDto> getMemos() {
        return memoRepository.findAll().stream()
                .map(MemoResponseDto::new)
                .collect(Collectors.toList());
    }

    public MemoResponseDto getMemo(long id) {
        return memoRepository.findById(id)
                .map(MemoResponseDto::new)
                .orElseThrow(() -> new IllegalArgumentException("Memo not found"));
    }

    public Long updateMemo(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Memo not found"));
        memo.update(requestDto);
        memoRepository.save(memo);
        return memo.getId();
    }

    public Long deleteMemo(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Memo not found"));
        memoRepository.delete(memo);
        return memo.getId();
    }
}