1. HTTP란?

HTTP (HyperText Transfer Protocol)

HTTP는 웹에서 클라이언트(브라우저)와 서버 간 데이터를 주고받는 프로토콜입니다.  
웹 페이지, API 요청, 파일 다운로드 등의 모든 웹 통신에서 사용됩니다.

 

HTTP 특징

  • 비연결성 (Stateless) : 한 번의 요청-응답 후 연결이 종료됨.
  • 무상태성 (Stateless) : 각각의 요청은 독립적이며, 이전 요청 정보를 기억하지 않음.
  • TCP/IP 기반 : HTTP는 기본적으로 TCP(전송 제어 프로토콜)를 사용하여 데이터 전송.
  • 주요 버전 :
- HTTP/1.1: 기본적인 웹 통신 방식.
- HTTP/2: 멀티플렉싱 지원(여러 요청 동시 처리), 헤더 압축.
- HTTP/3: QUIC 기반으로 더 빠른 데이터 전송.

2. HTTP 요청 (HTTP Request)

클라이언트(브라우저 또는 API 클라이언트)가 서버에 정보를 요청하는 방식입니다.

 

HTTP 요청의 기본 구조

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html

 

구성 요소

1) 요청 라인 (Request Line)

  • HTTP 메서드 : GET, POST, PUT, DELETE, PATCH 등.
  • URL (경로, 리소스 위치) : /index.html
  • HTTP 버전 : HTTP/1.1

2) 헤더 (Headers)

요청에 대한 추가 정보를 전달.

예시:

  • Host : 요청을 보내는 서버 도메인 (http://www.example.com)
  • User-Agent : 클라이언트(브라우저, 앱)의 정보 (Mozilla/5.0)
  • Accept : 응답에서 원하는 데이터 형식 (text/html)

3) 바디 (Body, 선택적)

  • POST, PUT 요청에서 데이터를 전송할 때 사용.
  • JSON, XML, Form 데이터 등을 포함할 수 있음.

주요 HTTP 요청 메서드

메서드 설명
GET 리소스를 조회 (데이터 가져오기)
POST 새로운 리소스를 생성 (데이터 전송)
PUT 기존 리소스를 수정 (전체 변경)
PATCH 기존 리소스를 부분 수정 (일부 변경)
DELETE 리소스를 삭제

3. HTTP 응답 (HTTP Response)

서버가 클라이언트의 요청에 대해 반환하는 응답입니다.

 

HTTP 응답 구조

HTTP/1.1 200 OK
Date: Sun, 02 Mar 2025 12:00:00 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html
Content-Length: 1256

<html>
  <body>Hello, World!</body>
</html>

 

구성 요소

1) 상태 라인 (Status Line)

  • HTTP 버전 : HTTP/1.1
  • 상태 코드(Status Code) : 200
  • 상태 메시지(Status Message) : OK

2) 헤더 (Headers)

  • Date : 응답 시간 (Sun, 02 Mar 2025 12:00:00 GMT)
  • Server : 서버 정보 (Apache/2.4.41 (Ubuntu))
  • Content-Type : 응답 데이터 유형 (text/html)
  • Content-Length : 응답 데이터의 크기 (1256 바이트)

3) 바디 (Body)

  • 클라이언트가 요청한 데이터 (HTML, JSON 등)

HTTP 응답 상태 코드

상태코드 의미
200 OK 요청 성공
201 Created 새로운 리소스 생성
204 No Content 요청 성공, 내용 없음
301 Moved Permanently 리소스 영구 이동
302 Found 리소스 임시 이동
400 Bad Request 잘못된 요청
401 Unauthorized 인증 필요
403 Forbidden 접근 금지
404 Not Found 요청한 리소스를 찾을 수 없음
500 Internal Server Error 서버 내부 오류
502 Bad Gateway 게이트웨이 오류
503 Service Unavailable 서버 과부하 또는 점검 중

4. 정리

  • HTTP (HyperText Transfer Protocol)은 웹에서 클라이언트와 서버 간 데이터를 주고받는 프로토콜이다.
  • 요청(Request)은 GET, POST, PUT, DELETE 등의 메서드를 사용하여 서버에 데이터를 요청한다.
  • 응답(Response)은 상태 코드(200 OK, 404 Not Found 등)와 함께 요청된 데이터를 반환한다.
  • HTTP는 기본적으로 Stateless(무상태성)을 가지며, 이를 보완하기 위해 쿠키, 세션, 토큰 등이 사용된다.

'기타 > CS' 카테고리의 다른 글

[HTTP] Chunked Transfer Encoding  (0) 2025.03.02

 

Java에서 정규식(Regex)은 java.util.regex 패키지의 Pattern 및 Matcher 클래스를 사용하여 처리됩니다.

  • Pattern : 정규식 패턴을 정의하는 클래스
  • Matcher : 패턴과 문자열을 매칭하는 클래스
  • PatternSyntaxException : 정규식 구문 오류 예외

1. 정규식 기본 문법

패턴 설명 예제
 .  임의의 문자 (줄바꿈 제외) "a.c""abc", "a3c" 매칭
 ^  문자열의 시작 "^Hello""Hello world" 매칭
 $  문자열의 끝 "world$""Hello world" 매칭
 *  0개 이상 반복 "a*""", "a", "aaa"
 +  1개 이상 반복 "a+""a", "aaa" (빈 문자열 X)
 ?  0개 또는 1개 "a?""", "a"
{n} 정확히 n개 반복 "a{3}""aaa"
{n,} n개 이상 반복 "a{2,}""aa", "aaa", "aaaa"
{n,m} n개 이상 m개 이하 반복 "a{2,4}""aa", "aaa", "aaaa"
 []  문자 집합 (ex: [abc] → a 또는 b 또는 c) "[abc]""a", "b", "c"
[^] 제외 문자 (ex: [^abc] → a, b, c 제외) "[^abc]""d", "e" (a, b, c 제외)
() 그룹화 (ex: (abc)+ → abc 1번 이상 반복) "(abc)""abc"
\d 숫자 [0-9] "\d""1", "5", "9"
\D 숫자가 아닌 문자 [^0-9] "\D""a", "B", "@"
\w 알파벳 + 숫자 + _([a-zA-Z0-9_]) "\w""a", "Z", "9", "_"
\W \w가 아닌 문자 "\W""@", "#", " "
\s 공백 문자 (스페이스, 탭, 개행) "\s"" ", "\n", "\t"
\S 공백이 아닌 문자 "\S" "a", "1", "@"

2. 정규식 활용 예제

문자열이 특정 패턴과 일치하는지 확인

import java.util.regex.*;

public class RegexExample {
    public static void main(String[] args) {
        String pattern = "\\d{3}-\\d{4}-\\d{4}"; // 010-1234-5678 형식
        String input = "010-1234-5678";

        boolean isMatch = Pattern.matches(pattern, input);
        System.out.println("일치 여부: " + isMatch); // true
    }
}

 

패턴에 맞는 문자열 찾기 (find)

import java.util.regex.*;

public class RegexFindExample {
    public static void main(String[] args) {
        String text = "My phone number is 010-1234-5678.";
        String pattern = "\\d{3}-\\d{4}-\\d{4}";

        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(text);

	// find() : 패턴과 일치하는 부분을 찾음
        if (m.find()) {
        // group() : 매칭된 문자열 반환
            System.out.println("찾은 값: " + m.group()); // 010-1234-5678
        }
    }
}

 

여러 개의 패턴 찾기

import java.util.regex.*;

public class RegexMultipleMatch {
    public static void main(String[] args) {
        String text = "Emails: test@example.com, hello@domain.net";
        String pattern = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}";

        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(text);

        while (m.find()) {
            System.out.println("이메일 찾음: " + m.group());
            // 이메일 찾음: test@example.com
            // 이메일 찾음: hello@domain.net
        }
    }
}

 

문자열을 정규식으로 분리 (split)

import java.util.regex.*;

public class RegexSplitExample {
    public static void main(String[] args) {
        String text = "apple,banana;grape orange";
        String[] result = text.split("[,; ]"); // 쉼표(,), 세미콜론(;), 공백( )으로 분리

        for (String word : result) {
            System.out.println(word);
        }
    }
}

 

출력 결과

apple
banana
grape
orange

 

문자열 치환 (replaceAll)

import java.util.regex.*;

public class RegexReplaceExample {
    public static void main(String[] args) {
        String text = "Hello 123, this is a test 456!";
        String result = text.replaceAll("\\d+", "[NUMBER]"); // 숫자 제거

        System.out.println(result); // Hello [NUMBER], this is a test [NUMBER]!
    }
}

3. 자주 쓰는 정규식 패턴

용도 정규식
이메일 검사 ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
전화번호 (010-1234-5678) ^010-\d{4}-\d{4}$
URL 검사 ^(https?
한글만 포함 ^[가-힣]+$
영문자만 포함 ^[a-zA-Z]+$
숫자만 포함 ^\d+$
비밀번호 (영문+숫자 조합, 8~16자) ^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$

4. 정규식 플래그

Pattern.compile("pattern", Pattern.CASE_INSENSITIVE);
플래그 설명
Pattern.CASE_INSENSITIVE 대소문자 구분 안 함
Pattern.MULTILINE 여러 줄 모드 ( ^ 와  $ 가 줄마다 적용됨)
Pattern.DOTALL  . 이 개행 문자( \n ) 포함 모든 문자에 매칭됨
Pattern.UNICODE_CASE 유니코드 문자에 대해 대소문자 무시
Pattern.COMMENTS 공백과  이후 주석 무시

5. Stream과 정규식 활용

import java.util.regex.Pattern;

public class StreamRegex {
    public static void main(String[] args) {
        String text = "Java 8, Java 11, Java 17";
        Pattern.compile("\\d+")
                .matcher(text)
                .results()
                .map(m -> "버전: " + m.group())
                .forEach(System.out::println);
    }
}

 

출력 결과

버전: 8
버전: 11
버전: 17

 

Java에서 정규식을 활용하면 문자열 검사, 파싱, 치환 등의 작업을 효과적으로 수행할 수 있습니다. 

 

Java Stream API에서 collect() 메서드는 스트림의 요소들을 컬렉션으로 집계하거나, 다양한 방식으로 결과를 처리할 때 사용됩니다. 주로 Collectors 유틸리티 클래스와 함께 사용되며, 리스트 변환, 그룹화, 조인, 맵핑 등의 기능을 제공합니다. 가장 많이 사용되는 컬렉는 Collectors.toList(), Collectors.toSet(), Collectors.toMap() 등이 있습니다.


주요 Collectors 메소드

1. 리스트(List)나 집합(Set)으로 변환 - toList(), toSet(), toMap()

스트림 결과를 List, Set, Map 등으로 변환 할 수 있습니다.

import java.util.*;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Apple");

        // List로 수집
        List<String> fruitList = fruits.stream()
                .collect(Collectors.toList());
        System.out.println("List: " + fruitList);  // List: [Apple, Banana, Cherry, Apple]

        // Set으로 수집 (중복 제거)
        Set<String> fruitSet = fruits.stream()
                .collect(Collectors.toSet());
        System.out.println("Set: " + fruitSet);  // Set: [Apple, Cherry, Banana]
    }
}

 

스트림의 요소들을 Map으로 변환 하는 예제 입니다. Map은 키 중복시 에러가 발생합니다. 중복 발생 시 mergeFunction을 추가하여 해결 할 수 있습니다.

public class CollectExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "melon");
        
        // 과일 이름을 길이로 매핑하여 Map으로 변환(키값 중복시 에러)
        Map<String, Integer> fruitLengthMap = fruits.stream()
         .collect(Collectors.toMap(fruit -> fruit, String::length)); // 이름을 키, 길이를 값으로

        System.out.println("Map :" + fruitLengthMap); // Map :{Apple=5, Cherry=6, melon=5, Banana=6}
        
    }
}
class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("David", 25)
        );

    	Map<String, Integer> nameToAgeMap = people.stream()
    	        .collect(Collectors.toMap(person -> person.name, person -> person.age));

    	System.out.println(nameToAgeMap); // {Alice=30, Bob=25, Charlie=30, David=25}
    	
    }
}

 

다음 예제는 mergeFunction을 추가하여 중복 문제를 해결 하는 예제입니다.

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("Alice", 25)
        );

    	Map<Integer, String> ageToNames = people.stream()
    	        .collect(Collectors.toMap(
    	                person -> person.age,
    	                person -> person.name,
    	                (existing, newValue) -> existing + ", " + newValue // 중복 Key 처리
    	        ));

    	System.out.println(ageToNames); // {25=Bob, Alice, 30=Alice, Charlie}	
    }
}

 

아래와 같이 List -> Map -> LinkedHashMap로 결과 값을 변환 할 수도 있다.

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("David", 25)
        );

    	Map<String, Integer> nameToAgeMap = people.stream()
    	        .collect(Collectors.toMap(person -> person.name, person -> person.age));
    	
    	LinkedHashMap<String, Integer> changeLinkedHashMap = nameToAgeMap.entrySet().stream()
                .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll);

    	System.out.println(changeLinkedHashMap); // {Alice=30, Bob=25, Charlie=30, David=25}
    	
    }
}

 

2. 특정 타입의 컬렉션으로 변환 - toCollection()

public class CollectExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Apple");

        // ArrayList로 변환
        ArrayList<String> arrayList = fruits.stream()
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println("arrayList: " + arrayList);  // arrayList: [Apple, Banana, Cherry, Apple]

        // treeSet으로 변환
        TreeSet<String> treeSet = fruits.stream()
                .collect(Collectors.toCollection(TreeSet::new));
        System.out.println("treeSet: " + treeSet);  // treeSet: [Apple, Banana, Cherry]

    }
}

 

3.문자열 결합 - joining()

스트림 요소를 하나의 문자열로 합칩니다.

public class CollectExample {
    public static void main(String[] args) {
    	List<String> words = Arrays.asList("Java", "Stream", "API");

    	String result = words.stream()
    	        .collect(Collectors.joining(", "));

    	System.out.println(result); // Java, Stream, API
    }
}

 

4. 값 합산, 평균, 통계 - summingInt(), averagingInt(), summarizingInt()

public class CollectExample {
    public static void main(String[] args) {
    	List<Integer> numbers = Arrays.asList(3, 5, 8, 10);

    	int sum = numbers.stream()
    	        .collect(Collectors.summingInt(Integer::intValue));

    	double avg = numbers.stream()
    	        .collect(Collectors.averagingInt(Integer::intValue));

    	IntSummaryStatistics stats = numbers.stream()
    	        .collect(Collectors.summarizingInt(Integer::intValue));

    	// Sum: 26
    	System.out.println("Sum: " + sum); 
    	// Avg: 6.5
    	System.out.println("Avg: " + avg); 
    	// Stats: IntSummaryStatistics{count=4, sum=26, min=3, average=6.500000, max=10}
    	System.out.println("Stats: " + stats);
    }
}

 

5. 그룹화 - groupingBy()

public class CollectExample {
    public static void main(String[] args) {
    	List<String> names = Arrays.asList("Alice", "Ava", "Bob", "Charlie", "Chan", "David", "Eva");

        // 첫 글자를 기준으로 이름 그룹화
        Map<Character, List<String>> groupedByFirstLetter = names.stream()
            .collect(Collectors.groupingBy(name -> name.charAt(0))); // 첫 글자를 기준으로 그룹화

        System.out.println(groupedByFirstLetter);
        // {A=[Alice, Ava], B=[Bob], C=[Charlie, Chan], D=[David], E=[Eva]}
    }
}
class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("David", 25)
        );

        // 나이별 그룹화
        Map<Integer, List<Person>> groupedByAge = people.stream()
                .collect(Collectors.groupingBy(person -> person.age));
        
        // {25=[Bob, David], 30=[Alice, Charlie]}
        System.out.println(groupedByAge);
    }
}

 

6. 그룹화 + 개수 세기 - groupingBy() + counting()

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("David", 25)
        );

    	Map<Integer, Long> countByAge = people.stream()
    	        .collect(Collectors.groupingBy(person -> person.age, Collectors.counting()));

    	System.out.println(countByAge); // {30=2, 25=2}
    }
}

 

7. 조건에 따른 분할  - partitioningBy()

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CollectExample {
    public static void main(String[] args) {
    	List<Person> people = Arrays.asList(
                new Person("Alice", 30),
                new Person("Bob", 25),
                new Person("Charlie", 30),
                new Person("David", 25)
        );

    	Map<Boolean, List<Person>> partitioned = people.stream()
    	        .collect(Collectors.partitioningBy(person -> person.age >= 30));

    	System.out.println(partitioned);
    }
}

정리

collect()는 Java Stream API에서 매우 중요한 메서드로, 다양한 방식으로 데이터를 집계하는 데 사용됩니다. Collectors.toList(), Collectors.toSet(), Collectors.toMap() 외에도 그룹화, 합산, 평균 계산, 요소 결합 등 여러 가지 유용한 방법들이 제공되므로 필요에 따라 적절히 활용할 수 있습니다.

 

Vue.js에서는 컴포넌트를 정의할 때 Composition APIOptions API 두 가지 방법을 사용할 수 있습니다.

1. Composition API와 Options API 비교

비교 항목 Composition API Options API
코드 구조 setup() 함수 내부에서 로직을 구성 data, methods, computed 등의 옵션을 사용하여 명확한 역할 분리(직관적이고 구조화된 코드)
재사용성 Composition Functions를 활용하여 로직 재사용이 용이 Mixins을 사용하지만, 충돌 문제 발생 가능
타입스크립트 지원 매우 우수 (함수 기반 구조로 타입 적용 용이) 제한적 (Vue 내부에서 자동 추론)
가독성 상태, 로직, 라이프사이클이 한 곳에서 관리되어 일관성 있음. (로직이 하나의 함수 내에서 관리됨) 기능별로 나뉘어져 있어 초보자에게 익숙함
코드 유지보수 복잡한 로직도 Composition Function으로 분리 가능.
(복잡한 로직도 쉽게 관리 가능)
규모가 커질수록 관리가 어려워질 수 있음
반응형 상태 관리 ref(), reactive() 사용 data()에서 객체 반환
계산된 속성 computed() 사용 computed 옵션 사용
이벤트 및 메서드 setup() 내부에서 메서드 정의 후 반환 methods 옵션에서 정의
라이프사이클 onMounted(), onUpdated() 등 Vue 3에서 제공하는 Composition API 사용 created(), mounted() 등 옵션을 사용
Vue 2 지원 여부 Vue 3에서만 사용 가능 Vue 2 및 Vue 3에서 사용 가능

 


2. Composition API와 Options API 예시

Composition API 사용 예시

Composition API는 setup() 내부에서 ref, reactive, computed 등을 활용하여 상태를 관리합니다.

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);

    const doubleCount = computed(() => count.value * 2);

    const increment = () => {
      count.value++;
    };

    return {
      count,
      doubleCount,
      increment
    };
  }
};
</script>

 

아래와 같이 <script setup>을 통해 조금 더 간결하게 소스를 작성 할 수 있습니다.

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const count = ref(0);
const doubleCount = computed(() => count.value * 2);

const increment = () => {
  count.value++;
};
</script>

 

Options API 사용 예시

Options API는 data, methods, computed 등의 옵션을 활용하여 상태를 정의합니다.

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

3. 언제 Composition API를 사용하고, 언제 Options API를 사용 할까?

Composition API

  • 복잡한 상태 관리가 필요한 경우 (예: 여러 개의 computed, watch 활용)
  • 반복되는 로직을 Composition Function으로 재사용하려는 경우
  • TypeScript와 함께 Vue를 개발할 때
  • Vue 3의 최신 기능을 적극 활용하려는 경우

Options API

  • 기존 Vue 2 프로젝트를 유지보수할 때
  • 간단한 컴포넌트를 개발할 때
  • Composition API의 함수형 코드 스타일이 익숙하지 않을 때

4. 정리

Composition API는 확장성과 유지보수가 뛰어나며, Vue 3에서는 추천되는 방식입니다. 하지만, 간단한 컴포넌트나 기존 Vue 2 프로젝트에서는 Options API를 활용하는 것이 더 적절할 수 있습니다.

1. Java Stream API 이란?

Java Stream API는 Java 8에서 도입된 기능으로, 컬렉션(List, Set, Map 등)의 데이터를 함수형 스타일로 처리할 수 있도록 지원하는 기능입니다. Stream은 데이터 소스(컬렉션, 배열 등)를 처리하는 데 사용됩니다.


2. Stream API의 주요 특징

  • 데이터 처리 중심: 컬렉션 요소를 반복하지 않고 함수형 프로그래밍 방식으로 데이터를 처리
  • 연산 체이닝(Chaining): 여러 연산을 조합하여 선언적으로 처리 가능
  • 내부 반복(Internal Iteration): 루프 없이 병렬 처리 최적화
  • Immutable(불변성): 원본 데이터를 변경하지 않고 새로운 데이터를 생성

3. Stream 생성

스트림은 컬렉션, 배열, 또는 직접 값을 통해 생성할 수 있습니다.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        // 컬렉션에서 스트림 생성
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        Stream<String> stream1 = list.stream();
        stream1.forEach(System.out::println);

        // 배열에서 스트림 생성
        String[] array = {"dog", "cat", "bird"};
        Stream<String> stream2 = Arrays.stream(array);
        stream2.forEach(System.out::println);

        // 직접 값으로 스트림 생성
        Stream<String> stream3 = Stream.of("one", "two", "three");
        stream3.forEach(System.out::println);
        
        // 무한 스트림 생성
        Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
        infiniteStream.forEach(System.out::println);
    }
}

4. Stream API 사용법(주요기능)

데이터의 필터링, 매핑, 정렬, 집계 등 다양한 작업을 간결하고 가독성 좋게 작성할 수 있습니다.

 

필터링(filter)

특정 조건을 만족하는 요소만 추출합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");

        // 길이가 5 이상인 과일만 필터링
        fruits.stream()
              .filter(fruit -> fruit.length() > 5)
              .forEach(System.out::println); // 출력: banana, cherry
              
        // "apple"만 남김      
        fruits.stream()
              .filter(s -> s.startsWith("a"))  
              .forEach(System.out::println); // 출력: apple
    }
}

 

매핑(map)

요소를 다른 형태로 반환합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry");

        // 모든 과일 이름을 대문자로 변환
        fruits.stream()
              .map(String::toUpperCase)
              .forEach(System.out::println); // 출력: APPLE, BANANA, CHERRY
        
        // 모든 요소를 2배 증가
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
               .map(n -> n * 2)  
               .forEach(System.out::println);
    }
}

 

정렬(sorted)

요소를 정렬합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("banana", "apple", "cherry");

        // 오름차순 정렬(알파벳 순으로 정렬)
        fruits.stream()
              .sorted()
              .forEach(System.out::println); // 출력: apple, banana, cherry
              
        // 내림차순 정렬
        fruits.stream()
             .sorted((a, b) -> b.compareTo(a))
             .forEach(System.out::println); // 출력: cherry, banana, apple
        
        // 오름차순 정렬
        List<Integer> numbers = Arrays.asList(1,2,3);
        numbers.stream()
              .sorted((a, b) -> a - b)  // 또는 sorted((a, b) -> a.compareTo(b))
              .forEach(System.out::println); // 출력: 1,2,3
        
        // 내림차순 정렬
        numbers.stream()
              .sorted((a, b) -> b - a)  // 또는 sorted((a, b) -> b.compareTo(a))
              .forEach(System.out::println); // 출력: 3,2,1
    }
}

 

특정 요소 선택 / 제외(limit(n) / skip(n))

특정 요소 선택 또는 제외 합니다.

import java.util.stream.Stream;

public class StreamExample {
	public static void main(String[] args) {
		Stream.of(1, 2, 3, 4, 5, 6)
	    	  .limit(3)  // 처음 3개 요소만 선택
	    	  .forEach(System.out::println);  // 출력: 1,2,3
		
		Stream.of(1, 2, 3, 4, 5, 6)
	    	  .skip(3)  // 처음 3개 요소 제외
	    	  .forEach(System.out::println);  // 출력: 4,5,6
    }
}

 

 

중복제거(distinct)

중복된 요소를 제거합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "apple", "cherry");

        // 문자열 중복 제거
        fruits.stream()
              .distinct()
              .forEach(System.out::println); // 출력: apple, banana, cherry
              
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4);
        
        // 정수 중복 제거
        numbers.stream()
               .distinct()
               .forEach(System.out::println);  // 출력: 1,2,3,4
    }
}

 

집계 (count, sum, min, max, average)

요소의 개수, 합계, 최소값, 최대값, 평균 등을 계산합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 합계 계산
        int sum = numbers.stream()
                         .mapToInt(Integer::intValue)
                         .sum();
        System.out.println("Sum: " + sum); // 출력: Sum: 15

        // 평균 계산
        double average = numbers.stream()
                                .mapToInt(Integer::intValue)
                                .average()
                                .orElse(0);
        System.out.println("Average: " + average); // 출력: Average: 3.0
    }
}

 

리듀스(reduce)

요소를 결합하여 단일 결과를 생성합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 모든 숫자의 곱 계산
        int product = numbers.stream()
                             .reduce(1, (a, b) -> a * b);
        System.out.println("Product: " + product); // 출력: Product: 120
        
        // 모든 숫자의 합 계산
        int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a + b);
		System.out.println(sum);  // 15
    }
}

 

조건검사(anyMatch, allMatch, noneMatch)

요소가 특정 조건을 만족하는지 검사합니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 모든 요소가 0보다 큰지 확인
        boolean allPositive = numbers.stream()
                                     .allMatch(n -> n > 0);
        System.out.println("All positive: " + allPositive); // 출력: All positive: true

        // 3보다 큰 요소가 하나라도 있는지 확인
        boolean anyGreaterThanThree = numbers.stream()
                                             .anyMatch(n -> n > 3);
                                             
        // 출력: Any greater than 3: true
        System.out.println("Any greater than 3: " + anyGreaterThanThree); 
    }
}

 

그룹화(groupingBy)

요소를 특정 기준으로 그룹화합니다.

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");

        // 과일 이름의 길이로 그룹화
        Map<Integer, List<String>> groupedByLength = fruits.stream()
                .collect(Collectors.groupingBy(String::length));

        System.out.println(groupedByLength);
        // 출력: {5=[apple], 6=[banana, cherry], 4=[date]}
    }
}

 

병렬 스트림(parallelStream)

병렬 처리를 통해 성능을 향상시킵니다.

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");

        // 병렬 스트림으로 처리
        fruits.parallelStream()
              .forEach(System.out::println); // 순서가 보장되지 않음
    }
}

 


5. Stream 활용 예제

짝수만 필터링 후 제곱 값 리스트 생성
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
	public static void main(String[] args) {
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
		List<Integer> result = numbers.stream()
		    .filter(n -> n % 2 == 0)
		    .map(n -> n * n)
		    .collect(Collectors.toList());

		System.out.println(result);  // [4, 16, 36]

    }
}

 

문자열 리스트에서 가장 긴 단어 찾기
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class StreamExample {
	public static void main(String[] args) {
		List<String> words = Arrays.asList("apple", "banana", "cherry", "watermelon");
		String longestWord = words.stream()
		    .max(Comparator.comparingInt(String::length))
		    .orElse("No words");

		System.out.println(longestWord);  // watermelon
    }
}

 

특정 키워드를 포함하는 개수 구하기
import java.util.stream.Stream;

public class StreamExample {
	public static void main(String[] args) {
		long count = Stream.of("java", "javascript", "python", "c++")
			    .filter(s -> s.contains("java"))
			    .count();

			System.out.println(count);  // 2

    }
}

 

forEach, count, collect 사용
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamExample {
	public static void main(String[] args) {
		Stream.of("A", "B", "C").forEach(System.out::println); // 출력: A, B, C
		
		long count = Stream.of(1, 2, 3, 4, 5).count();
		System.out.println(count);  // 출력: 5

		// 스트림에서 List 저장
		List<String> list = Stream.of("A", "B", "C").collect(Collectors.toList()); 
		System.out.println(list);
    }
}

6. 정리

Java의 Stream API는 컬렉션 데이터를 처리하는 데 매우 강력한 도구입니다. 위의 예제들은 Stream의 주요 기능을 보여주며, 이를 활용하면 데이터를 효과적으로 처리하면서 코드를 더 간결하고 가독성 있게 작성할 수 있습니다. 상황에 맞게 적절한 기능을 선택하여 사용하면 됩니다. 또한 병렬 스트림을 활용하면 성능 최적화도 가능합니다.

'BackEnd > JAVA' 카테고리의 다른 글

[JAVA] 정규식 사용 정리  (0) 2025.02.20
[JAVA] Stream API Collect 메소드  (0) 2025.02.20
[JAVA] Checked Exception과 Unchecked Exception 차이점  (0) 2025.02.13

 

자바에서 예외(Exception)는 크게 Checked Exception과 Unchecked Exception으로 나뉩니다.

1. Checked Exception

개념

- 컴파일 시점에 반드시 예외 처리를 해야 하는 예외
- try-catch 문으로 처리하거나 throws 키워드를 사용하여 호출자에게 예외 처리를 위임해야 함
- 예외 처리를 하지 않으면 컴파일 오류 발생

 

대표적인 Checked Exception

- IOException → 파일 입출력 시 발생
- SQLException → 데이터베이스 관련 예외
- ClassNotFoundException → 클래스 로드 실패
- InterruptedException → 스레드 인터럽트 발생

 

예제

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class CheckedExceptionExample {
    public static void main(String[] args) {
        try {
            File file = new File("test.txt");
            FileReader fr = new FileReader(file); // FileNotFoundException 발생 가능
        } catch (IOException e) { // IOException을 반드시 처리해야 함
            System.out.println("파일을 찾을 수 없습니다: " + e.getMessage());
        }
    }
}

 


2. Unchecked Exception

개념

- 런타임(Runtime)에서 발생하는 예외
- 컴파일 시점에서는 예외 처리를 강제하지 않음
- 대부분 프로그래머의 실수로 인해 발생

 

대표적인 Unchecked Exception

- NullPointerException → null 객체에 접근
- ArrayIndexOutOfBoundsException → 배열의 인덱스를 초과
- ArithmeticException → 0으로 나누기
- ClassCastException → 잘못된 형 변환

 

예제

public class UncheckedExceptionExample {
    public static void main(String[] args) {
        String text = null;
        System.out.println(text.length()); // NullPointerException 발생
    }
}

 


3. Checked vs Unchecked 차이점 비교

 

구분 Checked Exception Unchecked Exception
예외 처리 여부 반드시 처리해야 함 (try-catch 또는 throws) 강제되지 않음 (개발자가 직접 처리 가능)
컴파일 단계 예외 처리를 하지 않으면 컴파일 오류 발생 컴파일 단계에서 예외 검사 없음
발생 시점 파일 I/O, 네트워크, DB 작업 등 외부 시스템과 연관된 예외 null, 배열 범위 초과, 0 나누기 등 코드 문제로 발생
예제 IOException, SQLException, ClassNotFoundException NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException

 


4. 언제 Checked vs Unchecked를 사용해야 할까?

1) Checked Exception을 사용할 때

- 외부 시스템(파일, DB, 네트워크 등)과 관련된 오류를 예상할 수 있는 경우
- 개발자가 반드시 예외 처리를 강제해야 하는 경우

 

2) Unchecked Exception을 사용할 때

- 프로그래머의 실수로 인해 발생할 가능성이 높은 경우
- NullPointerException, IndexOutOfBoundsException 등 논리적인 오류가 주된 원인인 경우

 


5. 요약

- Checked Exception → 개발자가 반드시 처리해야 하는 예외, 컴파일 할 때 예외에 대한 처리를 강제.
- Unchecked Exception → 프로그램 로직 오류로 발생하는 예외, 강제 예외 처리는 필요 없음, 예외에 대한 처리를 강제하지 않음.

 

Checked Exception은 외부 시스템과의 통신 오류, Unchecked Exception은 개발자가 잘못된 코드를 작성했을 때 발생한다고 기억하면 됩니다!

'BackEnd > JAVA' 카테고리의 다른 글

[JAVA] 정규식 사용 정리  (0) 2025.02.20
[JAVA] Stream API Collect 메소드  (0) 2025.02.20
[JAVA] Stream API 생성과 사용법 정리  (2) 2025.02.15

+ Recent posts