1. 학습 주제
- enum을 왜 쓰는지 이해하기
- lambda를 함수형 인터페이스 관점에서 보기
- 메서드 참조를 읽는 법 익히기
- stream을 데이터 가공 파이프라인으로 이해하기
- 실무에서 많이 쓰는 패턴 중심으로 정리하기
2. enum
2.1 enum이 필요한 이유
예제에서 먼저 int 상수 방식의 문제를 보여줬다.
int subject1 = Subject.JAVA;
int subject2 = Subject.HTML;
문제점:
- JAVA와 HTML이 둘 다 0이면 다른 과목인데 같다고 비교될 수 있다
- printSubject(100)처럼 이상한 값도 들어갈 수 있다
- 숫자만 봐서는 의미를 알기 어렵다
- 타입 안정성이 없다
즉 int 상수는 “값은 있지만 의미와 안전성이 부족하다”.
그래서 enum을 쓴다.
2.2 enum의 핵심
public enum Subjects {
JAVA, MYSQL, JDBC, HTML, CSS, JAVASCRIPT
}
enum은 “정해진 값들만 가질 수 있는 타입”이다.
예:
Subjects subject = Subjects.HTML;
이제는:
- Subjects 타입 안의 값만 넣을 수 있다
- 100 같은 잘못된 값은 아예 컴파일 에러
- 코드만 봐도 의미가 분명하다
실무에서 많이 쓰는 곳
enum은 실무에서 꽤 자주 쓴다.
예:
- 회원 권한: ADMIN, USER, GUEST
- 주문 상태: READY, PAID, SHIPPED, CANCELLED
- 결제 수단: CARD, CASH, BANK_TRANSFER
- 게시글 상태: DRAFT, PUBLISHED, DELETED
즉 “선택지가 정해져 있는 값”에는 enum이 잘 맞는다.
2.3 enum이 실무에서 좋은 이유
1. 타입 안정성
printSubject(Subjects.HTML);
- Subjects만 받을 수 있다
- 이상한 값이 들어오는 실수를 줄인다
2. 가독성
숫자 1, 2, 3보다 ADMIN, GUEST, JAVA 같은 이름이 훨씬 읽기 쉽다.
3. 유지보수성
정해진 값 목록이 한곳에 모여 있어서 관리가 쉽다.
2.4 enum은 단순 상수 묶음이 아니다
UserRole 예제가 중요했다.
public enum UserRole {
GUEST("게스트"),
CONSUMER("구매자"),
PRODUCER("판매자"),
ADMIN("관리자");
}
enum은 단순 이름만 가지는 것이 아니라:
- 필드도 가질 수 있고
- 생성자도 가질 수 있고
- 메서드도 가질 수 있다
즉 enum은 “고정된 객체들의 집합”에 가깝다.
private final String description;
이런 식으로 설명도 넣을 수 있다.
실무에서 이 패턴이 많이 쓰인다.
예:
- 상태 코드 + 사용자에게 보여줄 설명
- 권한 이름 + 화면 표시명
- 주문 상태 + 메시지
2.5 enum을 읽는 감각
UserRole.ADMIN
이건 단순 상수가 아니라
“UserRole 타입의 ADMIN 객체 하나”라고 이해하면 좋다.
그래서 아래도 가능하다.
admin.getDescription();
admin.getNameToLowerCase();
즉 enum은 “정해진 객체를 미리 만들어 둔 특수 클래스”처럼 생각하면 이해가 쉬워진다.
2.6 EnumSet
EnumSet<UserRole> userRoles = EnumSet.of(UserRole.ADMIN, UserRole.GUEST);
실무에서 enum 여러 개를 묶어 다룰 때 유용하다.
예:
- 어떤 API에 허용된 권한 목록
- 특정 사용자에게 부여된 역할 목록
- 제외할 상태 목록
즉 Set<UserRole>보다 “enum 전용 집합”으로 더 의도가 잘 드러난다.
3. lambda
처음 람다가 어려운 이유는 “이게 메서드인지, 객체인지, 함수인지”가 애매하게 느껴지기 때문이다.
핵심은 아주 단순하다.
람다식은:
“메서드가 하나뿐인 인터페이스의 구현을 짧게 쓰는 문법”
이다.
3.1 기본 예제 이해
기존 방식:
Calculator c1 = new CalculatorImpl();
익명 클래스:
Calculator c2 = new CalculatorImpl() {
@Override
public int sumTwoNumber(int a, int b) {
return a+b;
}
};
람다:
Calculator c3 = (x, y) -> x + y;
세 개는 결국 같은 목적이다.
차이는 “표현 방식”이다.
즉 람다는
“이 인터페이스의 메서드를 이렇게 동작하게 할게”를 짧게 적는 방법이다.
3.2 함수형 인터페이스
람다를 쓰려면 인터페이스에 메서드가 하나만 있어야 한다.
@FunctionalInterface
public interface Calculator {
int sumTwoNumber(int a, int b);
}
이런 인터페이스를 함수형 인터페이스라고 한다.
실무에서는 직접 만들기도 하지만,
대부분은 자바가 제공하는 표준 함수형 인터페이스를 많이 쓴다.
3.3 실무에서 많이 쓰는 표준 함수형 인터페이스
Consumer
받아서 사용만 하고 끝
Consumer<String> printName = name -> System.out.println(name);
많이 쓰는 곳:
- forEach
- 로그 출력
- 알림 처리
Supplier
아무것도 안 받고 값을 만들어 줌
Supplier<LocalDateTime> timeSupplier = () -> LocalDateTime.now();
많이 쓰는 곳:
- 객체 생성
- 기본값 제공
- 지연 생성
Function<T, R>
하나 받아서 다른 값으로 변환
Function<String, Integer> parseInt = Integer::parseInt;
많이 쓰는 곳:
- DTO 변환
- 문자열 -> 숫자
- 객체 -> 특정 필드 추출
Predicate
조건 검사 후 true/false 반환
Predicate<String> isNotEmpty = str -> str != null && !str.isEmpty();
많이 쓰는 곳:
- 필터링
- 검증
- 조건 분기
실무에서는 Consumer, Function, Predicate가 특히 많이 보인다.
3.4 람다는 보통 어디서 많이 보이나
실무에서 람다는 단독으로 쓰이기보다
다음과 함께 자주 나온다.
- forEach
- sort
- stream().filter(...)
- stream().map(...)
- Optional
- 이벤트 처리
- 콜백 처리
즉 “컬렉션 다루는 코드”에서 특히 자주 등장한다.
3.5 람다를 읽는 법
예:
name -> System.out.println("환영합니다 " + name + "님")
이걸 읽을 때는 문법보다 뜻으로 읽는 게 낫다.
“name을 받아서 환영 메시지를 출력한다”
예:
(x, y) -> x + y
“x와 y를 받아서 더한 값을 반환한다”
즉 람다는 “짧은 함수 설명문”처럼 읽으면 된다.
4. 메서드 참조
메서드 참조는 람다를 더 줄인 문법이다.
System.out::println
Integer::parseInt
String::equals
Account::new
핵심 기준은 하나다.
“받은 값을 그대로 어떤 메서드에 전달만 하는가?”
그렇다면 메서드 참조로 줄일 수 있다.
예:
name -> System.out.println(name)
이건 그냥
System.out::println
로 줄일 수 있다.
4.1 실무에서 자주 보는 메서드 참조
정적 메서드 참조
Integer::parseInt
특정 객체의 인스턴스 메서드 참조
System.out::println
불특정 객체의 인스턴스 메서드 참조
String::equals
생성자 참조
Account::new
실무에서는 특히 아래 두 개가 진짜 자주 보인다.
- System.out::println
- 클래스명::getter/변환메서드
예:
.map(OnlineCourse::getTitle)
5. stream
스트림은 처음 배우면 가장 추상적으로 느껴지기 쉽다.
핵심은 이것이다.
스트림은:
“여러 데이터를 조건에 따라 거르고, 바꾸고, 모으는 작업을 읽기 좋게 연결해서 쓰는 방식”
이다.
즉 스트림은 데이터를 저장하는 자료구조가 아니라
“데이터를 처리하는 파이프라인”이다.
5.1 실무에서 왜 많이 쓰나
예를 들어 이런 요구가 있다고 하자.
- 강의 목록에서
- 프로그래밍 카테고리만 고르고
- 200분 초과인 것만 찾고
- 제목만 뽑아서 출력
기존 방식은 for, if가 길어진다.
스트림은 이렇게 쓴다.
courses.stream()
.filter(c -> "Programing".equals(c.getCategory()))
.filter(c -> c.getDuration() > 200)
.map(OnlineCourse::getTitle)
.forEach(System.out::println);
실무에서는 이런 “목록 가공”이 엄청 많다.
그래서 stream이 자주 쓰인다.
5.2 스트림은 어떻게 읽으면 되나
스트림 코드는 왼쪽에서 오른쪽으로 읽으면 된다.
names.stream()
.filter(name -> name.length() == 3)
.map(name -> "[" + name + "]")
.forEach(System.out::println);
읽는 법:
- names를 스트림으로 만든다
- 길이가 3인 이름만 남긴다
- 이름을 [이름] 형태로 바꾼다
- 하나씩 출력한다
즉 스트림은 “데이터가 흐르면서 변형되는 과정”이라고 보면 된다.
5.3 스트림에서 실무적으로 자주 쓰는 것
filter
조건에 맞는 것만 남김
.filter(c -> c.isFree())
실무에서 가장 많이 쓴다.
map
다른 값으로 변환
.map(OnlineCourse::getTitle)
객체 -> 제목
객체 -> DTO
문자열 -> 숫자
이런 변환에 자주 쓰인다.
sorted
정렬
.sorted((c1, c2) -> Integer.compare(c1.getDuration(), c2.getDuration()))
distinct
중복 제거
.map(OnlineCourse::getCategory)
.distinct()
collect
최종 결과를 리스트나 맵으로 모음
.collect(Collectors.toList())
count
개수 세기
.count()
anyMatch / allMatch
조건 검사
.anyMatch(name -> name.equals("장보고"))
.allMatch(name -> name.length() == 3)
groupingBy
카테고리별 분류
.collect(Collectors.groupingBy(OnlineCourse::getCategory))
이건 실무에서 꽤 많이 쓴다.
“카테고리별”, “상태별”, “작성자별” 묶기가 필요할 때 유용하다.
5.4 stream이 특히 잘 맞는 상황
다음 같은 작업에서 스트림이 강하다.
- 목록에서 일부만 걸러내기
- 특정 필드만 뽑기
- 정렬하기
- 그룹으로 묶기
- 합계, 개수, 평균 구하기
- 중복 제거하기
즉 “리스트를 가공하는 일”에는 스트림이 아주 잘 맞는다.
5.5 stream을 무조건 써야 하나
그건 아니다.
스트림이 좋은 경우:
- 데이터 가공 흐름이 명확할 때
- 필터/변환/수집이 연속적으로 이어질 때
- 읽는 사람이 “무엇을 하는지” 한눈에 알 수 있을 때
그냥 for문이 더 나은 경우:
- 로직이 매우 단순할 때
- 중간 상태를 세밀하게 제어해야 할 때
- 디버깅이 더 쉬워야 할 때
실무에서도 “무조건 stream”보다
“읽기 좋은 쪽 선택”이 더 중요하다.
6. enum, lambda, stream을 연결해서 이해하기
이 셋은 따로 보이지만 실무에서는 함께 자주 등장한다.
enum
정해진 상태/권한/타입을 안전하게 표현
lambda
짧은 동작을 전달
stream
컬렉션 데이터를 선언적으로 가공
예를 들면 실무에서 이런 코드가 자연스럽다.
- UserRole enum으로 권한 표현
- 사용자 목록을 stream으로 필터링
- 조건식은 lambda로 작성
즉 세 개가 따로따로 있는 기술이 아니라
“안전한 값 표현(enum) + 간결한 동작 전달(lambda) + 데이터 가공(stream)” 조합으로 많이 쓰인다.
7. 실무 또는 많이 쓰는 방식으로 다시 핵심만 정리
enum
가장 많이 쓰는 곳:
- 상태값
- 권한
- 카테고리
- 타입 구분
실무 감각:
- 문자열/숫자 상수 대신 enum 선호
- 상태별 설명이 필요하면 필드 추가
- 권한 목록 묶을 때 EnumSet도 유용
lambda
가장 많이 쓰는 곳:
- forEach
- 정렬
- 필터링
- 이벤트/콜백 처리
실무 감각:
- 직접 함수형 인터페이스 만들기보다
- Consumer, Function, Predicate 같은 표준 인터페이스를 자주 사용
stream
가장 많이 쓰는 곳:
- 목록 필터링
- DTO 변환
- 정렬
- 그룹핑
- 통계
실무 감각:
- filter -> map -> collect
- groupingBy
- count, sum, anyMatch 이 조합이 특히 자주 보인다
8. 흐름 정리
8.1 enum
- 잘못된 상수 사용을 막고
- 의미 있는 고정값을 타입으로 다루기 위해 사용
- 상태, 권한, 카테고리 표현에 적합
8.2 lambda
- 메서드 하나짜리 인터페이스 구현을 짧게 쓰는 문법
- 컬렉션 처리, 정렬, 스트림과 자주 함께 사용
8.3 stream
- 여러 데이터를 조건에 따라 걸러내고 바꾸고 모으는 API
- 실무에서는 목록 가공 코드에 매우 자주 등장
9. 핵심 정리
- enum은 정해진 값만 허용하는 안전한 타입이다.
- 실무에서는 상태값, 권한, 카테고리처럼 선택지가 고정된 경우 enum을 많이 쓴다.
- lambda는 함수형 인터페이스 구현을 짧게 쓰는 문법이다.
- 실무에서는 Consumer, Function, Predicate 같은 표준 함수형 인터페이스를 자주 사용한다.
- 메서드 참조는 람다를 더 간단히 줄인 문법이다.
- stream은 컬렉션 데이터를 선언적으로 가공하는 방식이다.
- 실무에서 가장 많이 보이는 스트림 패턴은 filter, map, collect이다.
- groupingBy, count, sum, distinct도 활용도가 높다.
- enum, lambda, stream은 따로 외우기보다 “안전한 값 표현 + 간결한 동작 전달 + 데이터 가공” 흐름으로 같이 이해하면 편하다.
'TIL > [TIL]' 카테고리의 다른 글
| [TIL]SQL 심화: GROUP BY, 서브쿼리, 집합 연산, DML, 트랜잭션, DDL, 제약조건, 데이터 타입, 인덱스 (1) | 2026.05.15 |
|---|---|
| [TIL]SQL 조회 기초: SELECT, ORDER BY, WHERE, DISTINCT, LIMIT, JOIN (0) | 2026.05.14 |
| [TIL]Exception, Checked/Unchecked, 예외 계층 구조, 다형성, IO 스트림 (0) | 2026.05.12 |
| [TIL]Java Collection Framework, List / Set / Map 핵심 정리 (0) | 2026.05.11 |
| [TIL]Java API, Wrapper, 오토박싱/오토언박싱, 제네릭 (1) | 2026.05.08 |