add user agent parser and update build configuration
This commit is contained in:
145
docs/2D 그래픽스.md
Normal file
145
docs/2D 그래픽스.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# **자바 2D 그래픽스 쉽게 배우기**
|
||||
|
||||
## **1. 자바 2D 그래픽스란?**
|
||||
자바에서 2D 그래픽을 그리려면 `java.awt`와 `javax.swing` 패키지를 사용한다.
|
||||
특히 **`Graphics`, `Graphics2D` 클래스**를 활용하면 **선을 긋고, 도형을 그리고, 색상을 채우고, 이미지를 출력할 수 있다.**
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `Graphics` 클래스 (기본 그래픽 처리)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `drawLine(x1, y1, x2, y2)` | (x1, y1) → (x2, y2) 직선 그리기 |
|
||||
| `drawRect(x, y, width, height)` | 사각형 그리기 (테두리만) |
|
||||
| `fillRect(x, y, width, height)` | 사각형 그리기 (채우기) |
|
||||
| `drawOval(x, y, width, height)` | 타원 그리기 (테두리만) |
|
||||
| `fillOval(x, y, width, height)` | 타원 그리기 (채우기) |
|
||||
| `drawPolygon(xPoints, yPoints, nPoints)` | 다각형 그리기 |
|
||||
| `fillPolygon(xPoints, yPoints, nPoints)` | 다각형 채우기 |
|
||||
| `setColor(Color c)` | 그리기 색상 변경 |
|
||||
| `setFont(Font f)` | 글꼴 변경 |
|
||||
| `drawString(String str, x, y)` | 문자열 그리기 |
|
||||
|
||||
**예제 코드 (사각형과 문자열 그리기)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MyPanel extends JPanel {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
g.setColor(Color.BLUE);
|
||||
g.fillRect(50, 50, 100, 100); // 파란색 사각형
|
||||
|
||||
g.setColor(Color.RED);
|
||||
g.drawString("Hello, Graphics!", 60, 180); // 문자열 출력
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame();
|
||||
frame.setSize(300, 300);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.add(new MyPanel());
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **`paintComponent(Graphics g)`를 오버라이드하여 원하는 그래픽을 그릴 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Graphics2D` 클래스 (고급 그래픽 처리)**
|
||||
`Graphics2D`는 `Graphics`의 확장판으로, **더 정밀한 그래픽을 그릴 수 있다.**
|
||||
`Graphics2D` 객체는 `Graphics`에서 다운캐스팅하여 얻을 수 있다.
|
||||
```java
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
```
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setStroke(Stroke s)` | 선 두께 설정 |
|
||||
| `setRenderingHint(RenderingHints.Key, Object)` | 안티앨리어싱 적용 (부드러운 그래픽) |
|
||||
| `draw(Shape s)` | `Shape` 객체 그리기 |
|
||||
| `fill(Shape s)` | `Shape` 객체 채우기 |
|
||||
| `rotate(theta, x, y)` | (x, y)를 중심으로 회전 |
|
||||
| `translate(x, y)` | (x, y)만큼 이동 |
|
||||
| `scale(sx, sy)` | x, y 방향으로 확대/축소 |
|
||||
|
||||
**예제 코드 (둥근 사각형과 선 두께 조절)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MyPanel extends JPanel {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
|
||||
g2.setStroke(new BasicStroke(5)); // 선 두께 설정
|
||||
g2.setColor(Color.MAGENTA);
|
||||
g2.drawRoundRect(50, 50, 100, 100, 20, 20); // 둥근 사각형
|
||||
|
||||
g2.setColor(Color.GREEN);
|
||||
g2.drawLine(50, 200, 200, 200); // 굵은 선
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame();
|
||||
frame.setSize(300, 300);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.add(new MyPanel());
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **`Graphics2D`를 사용하면 더 정밀한 그래픽 조작이 가능하다!**
|
||||
|
||||
---
|
||||
|
||||
### **(3) `Color`와 `Font` 클래스 (색상과 글꼴 설정)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `new Color(r, g, b)` | RGB 값으로 색상 생성 |
|
||||
| `setColor(Color c)` | 현재 색상 변경 |
|
||||
| `setFont(Font f)` | 글꼴 설정 |
|
||||
| `new Font("FontName", style, size)` | 글꼴 생성 |
|
||||
|
||||
**예제 코드 (다양한 색상과 글꼴 적용)**
|
||||
```java
|
||||
g.setColor(new Color(255, 100, 100)); // 연한 빨간색
|
||||
g.fillOval(50, 50, 100, 100);
|
||||
|
||||
g.setColor(Color.BLUE);
|
||||
g.setFont(new Font("Arial", Font.BOLD, 20));
|
||||
g.drawString("Hello!", 70, 180);
|
||||
```
|
||||
✅ **`Color`와 `Font`를 조합하면 더 다양한 표현이 가능하다!**
|
||||
|
||||
---
|
||||
|
||||
### **(4) `Image` 클래스 (이미지 출력)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `Image img = Toolkit.getDefaultToolkit().getImage("image.png")` | 이미지 로드 |
|
||||
| `drawImage(img, x, y, this)` | 이미지 그리기 |
|
||||
|
||||
**예제 코드 (이미지 출력)**
|
||||
```java
|
||||
Image img = Toolkit.getDefaultToolkit().getImage("image.png");
|
||||
g.drawImage(img, 50, 50, this);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **3. 정리**
|
||||
✅ **기본 도형을 그리려면 `Graphics` 클래스 사용! (`drawLine()`, `drawRect()` 등)**
|
||||
✅ **더 정밀한 그래픽을 원하면 `Graphics2D` 사용! (`setStroke()`, `rotate()` 등)**
|
||||
✅ **색상은 `Color`, 글꼴은 `Font` 클래스로 설정 가능!**
|
||||
✅ **이미지 출력도 가능! (`drawImage()`)**
|
||||
|
||||
자바의 2D 그래픽을 활용하면 **간단한 그림부터 UI 요소, 차트, 애니메이션까지 다양한 그래픽을 만들 수 있다!**
|
||||
536
docs/BitSet.md
Normal file
536
docs/BitSet.md
Normal file
@@ -0,0 +1,536 @@
|
||||
# **BitSet 클래스란?**
|
||||
|
||||
`BitSet`은 **비트(bit) 단위의 데이터를 효율적으로 저장하고 조작할 수 있도록 설계된 클래스**이다.
|
||||
기본적인 `boolean[]` 배열과 유사하지만, **각 요소를 1비트로 저장**하기 때문에 **메모리를 훨씬 적게 사용**한다.
|
||||
|
||||
이 클래스는 **대량의 불리언 데이터를 다룰 때** 유용하며, **비트 연산을 활용하는 알고리즘**(예: 비트마스크, 소수 판별, 집합 연산 등)에서도 사용된다.
|
||||
|
||||
---
|
||||
|
||||
## **1. BitSet의 주요 특징**
|
||||
✔ **1비트 단위로 저장** → `boolean[]`보다 메모리 절약
|
||||
✔ **자동 확장 가능** → 크기를 명시할 필요 없음
|
||||
✔ **비트 연산 지원** → AND, OR, XOR 등 논리 연산 가능
|
||||
✔ **빠른 검색** → 특정 비트의 ON/OFF를 빠르게 확인 가능
|
||||
|
||||
---
|
||||
|
||||
## **2. BitSet의 주요 메서드 정리**
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `BitSet()` | 빈 `BitSet` 생성 (기본 크기 64비트) |
|
||||
| `BitSet(int nbits)` | 최소 `nbits` 크기의 `BitSet` 생성 |
|
||||
| `set(int index)` | 특정 위치의 비트를 `1(true)`로 설정 |
|
||||
| `set(int fromIndex, int toIndex)` | 특정 범위의 비트를 `1(true)`로 설정 |
|
||||
| `clear(int index)` | 특정 위치의 비트를 `0(false)`로 설정 |
|
||||
| `clear(int fromIndex, int toIndex)` | 특정 범위의 비트를 `0(false)`로 설정 |
|
||||
| `flip(int index)` | 특정 위치의 비트를 반전 |
|
||||
| `flip(int fromIndex, int toIndex)` | 특정 범위의 비트를 반전 |
|
||||
| `get(int index)` | 특정 위치의 비트 값을 반환 (`true/false`) |
|
||||
| `get(int fromIndex, int toIndex)` | 특정 범위의 비트 값을 `BitSet`으로 반환 |
|
||||
| `and(BitSet set)` | AND 연산 수행 |
|
||||
| `or(BitSet set)` | OR 연산 수행 |
|
||||
| `xor(BitSet set)` | XOR 연산 수행 |
|
||||
| `cardinality()` | `1(true)`로 설정된 비트의 개수 반환 |
|
||||
| `length()` | 가장 높은 `1(true)` 비트의 인덱스 + 1 반환 |
|
||||
| `size()` | 내부 비트 배열의 전체 크기 반환 |
|
||||
| `isEmpty()` | 모든 비트가 `0(false)`인지 확인 |
|
||||
| `nextSetBit(int index)` | `index` 이후 첫 번째 `1(true)` 비트의 위치 반환 |
|
||||
| `nextClearBit(int index)` | `index` 이후 첫 번째 `0(false)` 비트의 위치 반환 |
|
||||
|
||||
---
|
||||
|
||||
## **3. BitSet 사용 예제**
|
||||
|
||||
### **(1) 기본적인 비트 설정 및 조회**
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bitSet = new BitSet(); // 기본 크기 64비트
|
||||
|
||||
bitSet.set(0); // 0번째 비트 ON
|
||||
bitSet.set(2); // 2번째 비트 ON
|
||||
bitSet.set(4); // 4번째 비트 ON
|
||||
|
||||
System.out.println("BitSet: " + bitSet); // {0, 2, 4}
|
||||
|
||||
// 특정 비트 확인
|
||||
System.out.println("0번째 비트: " + bitSet.get(0)); // true
|
||||
System.out.println("1번째 비트: " + bitSet.get(1)); // false
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `BitSet`을 출력하면 **1(true)인 비트의 위치만 표시**된다.
|
||||
|
||||
---
|
||||
|
||||
### **(2) `flip()`을 활용한 비트 반전**
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetFlipExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bitSet = new BitSet();
|
||||
bitSet.set(1);
|
||||
bitSet.set(3);
|
||||
bitSet.set(5);
|
||||
System.out.println("초기 BitSet: " + bitSet); // {1, 3, 5}
|
||||
|
||||
bitSet.flip(0, 6); // 0~5까지 비트 반전
|
||||
System.out.println("flip(0,6) 후: " + bitSet); // {0, 2, 4}
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `flip()`을 사용하면 **지정된 범위의 비트가 반전**된다.
|
||||
|
||||
---
|
||||
|
||||
### **(3) 비트 연산 (AND, OR, XOR)**
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetOperations {
|
||||
public static void main(String[] args) {
|
||||
BitSet set1 = new BitSet();
|
||||
BitSet set2 = new BitSet();
|
||||
|
||||
set1.set(0);
|
||||
set1.set(1);
|
||||
set1.set(2);
|
||||
System.out.println("Set1: " + set1); // {0, 1, 2}
|
||||
|
||||
set2.set(1);
|
||||
set2.set(2);
|
||||
set2.set(3);
|
||||
System.out.println("Set2: " + set2); // {1, 2, 3}
|
||||
|
||||
BitSet andSet = (BitSet) set1.clone();
|
||||
andSet.and(set2);
|
||||
System.out.println("AND 연산: " + andSet); // {1, 2}
|
||||
|
||||
BitSet orSet = (BitSet) set1.clone();
|
||||
orSet.or(set2);
|
||||
System.out.println("OR 연산: " + orSet); // {0, 1, 2, 3}
|
||||
|
||||
BitSet xorSet = (BitSet) set1.clone();
|
||||
xorSet.xor(set2);
|
||||
System.out.println("XOR 연산: " + xorSet); // {0, 3}
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ **AND 연산:** `1(true)`인 비트만 유지
|
||||
✔ **OR 연산:** 하나라도 `1(true)`이면 유지
|
||||
✔ **XOR 연산:** `1(true)`가 서로 다르면 유지
|
||||
|
||||
---
|
||||
|
||||
### **(4) `cardinality()`, `nextSetBit()`, `nextClearBit()` 활용**
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetMethods {
|
||||
public static void main(String[] args) {
|
||||
BitSet bitSet = new BitSet();
|
||||
bitSet.set(1);
|
||||
bitSet.set(3);
|
||||
bitSet.set(5);
|
||||
|
||||
System.out.println("BitSet: " + bitSet); // {1, 3, 5}
|
||||
System.out.println("1의 개수: " + bitSet.cardinality()); // 3
|
||||
|
||||
System.out.println("다음 1(true) 비트 위치: " + bitSet.nextSetBit(0)); // 1
|
||||
System.out.println("다음 0(false) 비트 위치: " + bitSet.nextClearBit(0)); // 0
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `cardinality()`는 `1(true)`의 개수를 반환
|
||||
✔ `nextSetBit()`는 **다음 `1(true)`인 비트 위치** 반환
|
||||
✔ `nextClearBit()`는 **다음 `0(false)`인 비트 위치** 반환
|
||||
|
||||
---
|
||||
|
||||
## **4. BitSet vs. boolean 배열**
|
||||
| 비교 항목 | `BitSet` | `boolean[]` |
|
||||
|----------|---------|------------|
|
||||
| 메모리 사용 | **1비트(압축됨)** | 1바이트(8배 큼) |
|
||||
| 크기 조정 | **자동 확장** | **고정 크기** |
|
||||
| 비트 연산 | AND, OR, XOR 지원 | 직접 구현 필요 |
|
||||
| 성능 | 비트 연산이 빠름 | 단순한 경우 유리 |
|
||||
|
||||
✔ `BitSet`은 **메모리를 절약하면서도 대량의 비트 연산을 효율적으로 수행**할 수 있다.
|
||||
✔ 다만, **단순한 배열이 필요하다면 `boolean[]`이 더 직관적**일 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## **5. 결론**
|
||||
`BitSet`은 **대량의 불리언 데이터를 비트 단위로 저장하고 처리하는 데 유용**하며,
|
||||
비트 연산이 필요한 **비트마스크, 소수 판별, 집합 연산, 압축 데이터 처리** 등에 많이 활용된다.
|
||||
단순한 `boolean[]`보다 **메모리를 절약**하면서도 **연산 속도가 빠르다**는 점이 강점이다.
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
### `BitSet` 클래스의 메서드 정리 및 설명
|
||||
|
||||
자바의 `BitSet` 클래스는 `java.util` 패키지에 포함된 클래스로, 비트 단위로 데이터를 효율적으로 관리하기 위한 자료 구조입니다. 이는 이진수 데이터를 다룰 때 메모리를 절약하며, 비트 연산(AND, OR, XOR 등)을 쉽게 수행할 수 있도록 설계되었습니다. `BitSet`은 가변 크기의 비트 배열로 동작하며, 인덱스는 0부터 시작합니다.
|
||||
|
||||
아래에서 `BitSet`의 주요 메서드를 표로 정리하고, 각 메서드의 사용법과 예시를 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### `BitSet` 클래스 메서드 표
|
||||
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|------------------------------------|-------------|------------------------------------------------------------------------------------------|
|
||||
| `BitSet()` | - | 빈 `BitSet` 객체를 생성 (기본 크기 64비트) |
|
||||
| `BitSet(int nbits)` | - | 지정된 초기 크기(`nbits`)로 `BitSet` 생성 |
|
||||
| `set(int bitIndex)` | `void` | 지정된 인덱스의 비트를 `true`(1)로 설정 |
|
||||
| `set(int bitIndex, boolean value)`| `void` | 지정된 인덱스의 비트를 주어진 값(`true` 또는 `false`)으로 설정 |
|
||||
| `set(int fromIndex, int toIndex)` | `void` | 지정된 범위(`fromIndex`부터 `toIndex-1`까지)의 비트를 `true`로 설정 |
|
||||
| `clear(int bitIndex)` | `void` | 지정된 인덱스의 비트를 `false`(0)로 설정 |
|
||||
| `clear(int fromIndex, int toIndex)` | `void` | 지정된 범위의 비트를 `false`로 설정 |
|
||||
| `clear()` | `void` | 모든 비트를 `false`로 설정 |
|
||||
| `get(int bitIndex)` | `boolean` | 지정된 인덱스의 비트 값을 반환 |
|
||||
| `get(int fromIndex, int toIndex)` | `BitSet` | 지정된 범위의 비트를 포함하는 새로운 `BitSet` 반환 |
|
||||
| `flip(int bitIndex)` | `void` | 지정된 인덱스의 비트를 반전 (`true` ↔ `false`) |
|
||||
| `flip(int fromIndex, int toIndex)`| `void` | 지정된 범위의 비트를 반전 |
|
||||
| `and(BitSet set)` | `void` | 현재 `BitSet`과 주어진 `BitSet` 간 비트 단위 AND 연산 수행 |
|
||||
| `or(BitSet set)` | `void` | 현재 `BitSet`과 주어진 `BitSet` 간 비트 단위 OR 연산 수행 |
|
||||
| `xor(BitSet set)` | `void` | 현재 `BitSet`과 주어진 `BitSet` 간 비트 단위 XOR 연산 수행 |
|
||||
| `andNot(BitSet set)` | `void` | 현재 `BitSet`에서 주어진 `BitSet`의 비트를 제거 (AND NOT 연산) |
|
||||
| `cardinality()` | `int` | `true`인 비트의 개수 반환 |
|
||||
| `length()` | `int` | 마지막 `true` 비트의 인덱스 + 1 반환 (논리적 크기) |
|
||||
| `size()` | `int` | 내부적으로 할당된 비트 수 반환 (물리적 크기, 64의 배수) |
|
||||
| `isEmpty()` | `boolean` | 모든 비트가 `false`인지 확인 |
|
||||
| `intersects(BitSet set)` | `boolean` | 주어진 `BitSet`과 공통된 `true` 비트가 있는지 확인 |
|
||||
| `nextSetBit(int fromIndex)` | `int` | 지정된 인덱스 이후 첫 번째 `true` 비트의 인덱스 반환, 없으면 -1 |
|
||||
| `nextClearBit(int fromIndex)` | `int` | 지정된 인덱스 이후 첫 번째 `false` 비트의 인덱스 반환 |
|
||||
| `previousSetBit(int fromIndex)` | `int` | 지정된 인덱스 이전의 마지막 `true` 비트의 인덱스 반환, 없으면 -1 |
|
||||
| `previousClearBit(int fromIndex)` | `int` | 지정된 인덱스 이전의 마지막 `false` 비트의 인덱스 반환 |
|
||||
| `toByteArray()` | `byte[]` | `BitSet`을 바이트 배열로 변환 |
|
||||
| `toLongArray()` | `long[]` | `BitSet`을 `long` 배열로 변환 |
|
||||
| `valueOf(byte[] bytes)` | `BitSet` | 바이트 배열에서 `BitSet` 생성 (정적 메서드) |
|
||||
| `valueOf(long[] longs)` | `BitSet` | `long` 배열에서 `BitSet` 생성 (정적 메서드) |
|
||||
| `clone()` | `Object` | `BitSet`의 복사본 반환 |
|
||||
| `equals(Object obj)` | `boolean` | 주어진 객체와 동일한지 비교 |
|
||||
| `toString()` | `String` | `true` 비트의 인덱스를 포함한 문자열 반환 (예: "{0, 2, 4}") |
|
||||
|
||||
---
|
||||
|
||||
### `BitSet` 설명 및 예시
|
||||
|
||||
`BitSet`은 비트 단위로 데이터를 표현하므로 메모리 효율성이 높고, 비트 연산이 필요한 경우 유용합니다. 주요 사용 사례는 플래그 관리, 집합 표현, 데이터 압축 등입니다. 아래에서 주요 메서드의 사용법을 예시로 설명합니다.
|
||||
|
||||
#### 1. 비트 설정 및 확인
|
||||
비트를 설정하고 값을 확인합니다.
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetBasicExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(0); // 0번 비트 = true
|
||||
bits.set(2); // 2번 비트 = true
|
||||
bits.set(4, 6); // 4~5번 비트 = true
|
||||
|
||||
System.out.println("Bit at 0: " + bits.get(0)); // true
|
||||
System.out.println("Bit at 1: " + bits.get(1)); // false
|
||||
System.out.println("Bits: " + bits); // {0, 2, 4, 5}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Bit at 0: true
|
||||
Bit at 1: false
|
||||
Bits: {0, 2, 4, 5}
|
||||
```
|
||||
|
||||
#### 2. 비트 연산
|
||||
`and`, `or`, `xor` 연산을 수행합니다.
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetOperationExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits1 = new BitSet();
|
||||
bits1.set(0);
|
||||
bits1.set(1);
|
||||
BitSet bits2 = new BitSet();
|
||||
bits2.set(1);
|
||||
bits2.set(2);
|
||||
|
||||
BitSet andResult = (BitSet) bits1.clone();
|
||||
andResult.and(bits2);
|
||||
System.out.println("AND: " + andResult); // {1}
|
||||
|
||||
BitSet orResult = (BitSet) bits1.clone();
|
||||
orResult.or(bits2);
|
||||
System.out.println("OR: " + orResult); // {0, 1, 2}
|
||||
|
||||
BitSet xorResult = (BitSet) bits1.clone();
|
||||
xorResult.xor(bits2);
|
||||
System.out.println("XOR: " + xorResult); // {0, 2}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
AND: {1}
|
||||
OR: {0, 1, 2}
|
||||
XOR: {0, 2}
|
||||
```
|
||||
|
||||
#### 3. 비트 탐색
|
||||
다음 `true` 또는 `false` 비트를 찾습니다.
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetSearchExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(2);
|
||||
bits.set(5);
|
||||
|
||||
int nextSet = bits.nextSetBit(0); // 2
|
||||
int nextClear = bits.nextClearBit(2); // 3
|
||||
System.out.println("Next set bit from 0: " + nextSet);
|
||||
System.out.println("Next clear bit from 2: " + nextClear);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Next set bit from 0: 2
|
||||
Next clear bit from 2: 3
|
||||
```
|
||||
|
||||
#### 4. 바이트 배열 변환
|
||||
`BitSet`을 바이트 배열로 변환하고 역변환합니다.
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetToByteExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(0);
|
||||
bits.set(3);
|
||||
bits.set(7);
|
||||
|
||||
byte[] byteArray = bits.toByteArray();
|
||||
System.out.print("Byte array: ");
|
||||
for (byte b : byteArray) {
|
||||
System.out.printf("%02X ", b); // 09 80 (00001001 10000000)
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
BitSet fromBytes = BitSet.valueOf(byteArray);
|
||||
System.out.println("From bytes: " + fromBytes); // {0, 3, 7}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Byte array: 09 80
|
||||
From bytes: {0, 3, 7}
|
||||
```
|
||||
|
||||
#### 5. 상태 확인
|
||||
`BitSet`의 크기와 개수를 확인합니다.
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetStatusExample {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(2);
|
||||
bits.set(5);
|
||||
|
||||
System.out.println("Cardinality: " + bits.cardinality()); // 2
|
||||
System.out.println("Length: " + bits.length()); // 6
|
||||
System.out.println("Size: " + bits.size()); // 64
|
||||
System.out.println("Is empty: " + bits.isEmpty()); // false
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Cardinality: 2
|
||||
Length: 6
|
||||
Size: 64
|
||||
Is empty: false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `BitSet`의 특징과 주의점
|
||||
- **장점**:
|
||||
- 메모리 효율성: `true` 비트만 저장하며, 필요한 만큼 동적으로 확장.
|
||||
- 비트 연산 지원: 집합 연산(합집합, 교집합 등)을 쉽게 구현.
|
||||
- **주의점**:
|
||||
- 인덱스 음수 불가: `IllegalArgumentException` 발생.
|
||||
- 동기화 없음: 멀티스레드 환경에서 `Collections.synchronizedSet`으로 감싸야 함.
|
||||
- 크기: `size()`는 물리적 크기(64비트 단위), `length()`는 논리적 크기.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`BitSet`은 비트 단위 작업을 효율적으로 처리하며, 설정(`set`), 확인(`get`), 연산(`and`, `or`, `xor`), 변환(`toByteArray`) 등 다양한 기능을 제공합니다. 위 예시를 통해 비트 관리, 집합 연산, 데이터 변환 등에 활용해 보세요! 필요 시 크기 조정이나 동기화 처리를 추가해 사용하면 더욱 강력한 도구가 됩니다.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
### `BitSet`의 인덱스 순서에 대한 설명
|
||||
|
||||
자바의 `BitSet` 클래스는 비트 단위로 데이터를 관리하는 자료 구조로, 각 비트는 0부터 시작하는 정수 인덱스에 의해 식별됩니다. 이 인덱스 순서는 비트의 위치를 나타내며, `BitSet`의 동작 방식과 데이터 표현에 중요한 영향을 미칩니다. 아래에서 `BitSet`의 인덱스 순서에 대해 자세히 설명하고, 예시를 통해 이해를 돕겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### 1. **인덱스 순서의 기본 개념**
|
||||
- **`BitSet`의 인덱스**: 0부터 시작하는 정수로, 비트의 위치를 나타냅니다.
|
||||
- 인덱스 0은 가장 낮은 비트(least significant bit, LSB)를 의미하며, 인덱스가 증가할수록 더 높은 비트(most significant bit, MSB)로 이동합니다.
|
||||
- **순서**: 논리적으로 왼쪽에서 오른쪽으로 증가하는 순서가 아니라, 내부적으로는 오른쪽에서 왼쪽으로 비트가 배치됩니다. 하지만 사용자가 접근할 때는 인덱스 0이 시작점으로 간주됩니다.
|
||||
- **크기**: `BitSet`은 필요에 따라 동적으로 확장되며, 초기 크기는 64비트 단위로 할당됩니다.
|
||||
|
||||
#### 비트와 인덱스 매핑 예시
|
||||
- 8비트 기준으로 생각하면:
|
||||
```
|
||||
인덱스: 7 6 5 4 3 2 1 0
|
||||
비트: [0, 0, 0, 0, 1, 0, 1, 0] // 10진수 10
|
||||
```
|
||||
- 인덱스 1이 `true`이고, 인덱스 3이 `true`이면 값은 `00001010` (10진수로 10).
|
||||
|
||||
---
|
||||
|
||||
### 2. **인덱스 순서와 메서드 동작**
|
||||
`BitSet`의 메서드는 이 인덱스 순서를 기반으로 동작합니다. 주요 메서드와 인덱스 순서의 관계를 살펴보겠습니다.
|
||||
|
||||
#### `set(int bitIndex)`와 `get(int bitIndex)`
|
||||
- `set(3)`은 인덱스 3의 비트를 `true`로 설정하며, 이는 오른쪽에서 네 번째 비트에 해당합니다.
|
||||
- `get(3)`은 인덱스 3의 비트 값을 반환합니다.
|
||||
|
||||
#### `nextSetBit(int fromIndex)`와 `previousSetBit(int fromIndex)`
|
||||
- `nextSetBit(0)`은 인덱스 0부터 시작해 첫 번째 `true` 비트의 인덱스를 반환합니다. 순서는 낮은 인덱스에서 높은 인덱스로 진행.
|
||||
- `previousSetBit(5)`는 인덱스 5 이전의 마지막 `true` 비트의 인덱스를 반환하며, 높은 인덱스에서 낮은 인덱스로 역방향 탐색.
|
||||
|
||||
#### `toByteArray()`와 바이트 순서
|
||||
- `BitSet`을 바이트 배열로 변환할 때, 낮은 인덱스가 낮은 바이트에 먼저 매핑됩니다.
|
||||
- 예: 인덱스 0~7이 첫 번째 바이트, 8~15가 두 번째 바이트로 변환.
|
||||
- 바이트 내부에서는 비트가 LSB에서 MSB로 배치됩니다 (즉, 인덱스 0이 바이트의 최하위 비트).
|
||||
|
||||
---
|
||||
|
||||
### 3. **인덱스 순서 예시**
|
||||
#### 예시 1: 비트 설정과 확인
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetIndexOrder {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(0); // 인덱스 0 = true
|
||||
bits.set(3); // 인덱스 3 = true
|
||||
bits.set(7); // 인덱스 7 = true
|
||||
|
||||
System.out.println("Bits: " + bits); // {0, 3, 7}
|
||||
System.out.println("Bit at 0: " + bits.get(0)); // true
|
||||
System.out.println("Bit at 1: " + bits.get(1)); // false
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Bits: {0, 3, 7}
|
||||
Bit at 0: true
|
||||
Bit at 1: false
|
||||
```
|
||||
- **설명**: 인덱스 0, 3, 7이 `true`로 설정되며, 이는 8비트 기준으로 `10001001` (10진수 137)과 같습니다.
|
||||
|
||||
#### 예시 2: 바이트 배열 변환
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetToByteOrder {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(0); // 00000001
|
||||
bits.set(3); // 00001000
|
||||
bits.set(7); // 10000000
|
||||
|
||||
byte[] bytes = bits.toByteArray();
|
||||
for (byte b : bytes) {
|
||||
System.out.printf("%02X ", b); // 09 (00001001)
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**: `09`
|
||||
- **설명**:
|
||||
- 인덱스 0~7이 첫 번째 바이트로 변환.
|
||||
- `00001001` (인덱스 7이 MSB, 인덱스 0이 LSB) = 16진수 `09`.
|
||||
|
||||
#### 예시 3: 비트 탐색
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BitSetSearchOrder {
|
||||
public static void main(String[] args) {
|
||||
BitSet bits = new BitSet();
|
||||
bits.set(2);
|
||||
bits.set(5);
|
||||
bits.set(10);
|
||||
|
||||
int next = bits.nextSetBit(0); // 2
|
||||
int nextFrom3 = bits.nextSetBit(3); // 5
|
||||
int prevFrom10 = bits.previousSetBit(10); // 10
|
||||
|
||||
System.out.println("Next set from 0: " + next);
|
||||
System.out.println("Next set from 3: " + nextFrom3);
|
||||
System.out.println("Previous set from 10: " + prevFrom10);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력**:
|
||||
```
|
||||
Next set from 0: 2
|
||||
Next set from 3: 5
|
||||
Previous set from 10: 10
|
||||
```
|
||||
- **설명**: 인덱스 순서에 따라 낮은 인덱스에서 높은 인덱스로(또는 그 반대로) 탐색.
|
||||
|
||||
---
|
||||
|
||||
### 4. **인덱스 순서와 바이트 순서의 관계**
|
||||
- **내부 표현**: `BitSet`은 `long[]` 배열로 비트를 저장하며, 각 `long`은 64비트를 표현합니다.
|
||||
- 첫 번째 `long`은 인덱스 0~63, 두 번째는 64~127을 다룹니다.
|
||||
- 바이트로 변환 시, 낮은 인덱스가 낮은 바이트에 매핑되며, 바이트 내에서는 LSB에서 MSB로 진행.
|
||||
- **예**: 인덱스 0~15 설정 시:
|
||||
```
|
||||
BitSet: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
Byte array: FF FF (11111111 11111111)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. **주의점**
|
||||
- **음수 인덱스**: `BitSet`은 음수 인덱스를 허용하지 않으며, `IllegalArgumentException` 발생.
|
||||
- **바이트 순서**: `toByteArray()` 결과는 little-endian 스타일로, 인덱스 0이 첫 바이트의 LSB에 매핑.
|
||||
- **논리적 vs 물리적 크기**: `length()`는 마지막 `true` 비트의 인덱스 + 1, `size()`는 할당된 비트 수(64의 배수).
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`BitSet`의 인덱스 순서는 0부터 시작하며, 낮은 인덱스가 LSB, 높은 인덱스가 MSB에 해당합니다. 메서드는 이 순서를 기반으로 비트를 설정, 확인, 연산하며, 바이트 변환 시에도 인덱스 순서가 반영됩니다. 위 예시를 통해 인덱스 순서가 데이터 표현과 탐색에 어떻게 적용되는지 확인할 수 있습니다. 비트 단위 작업 시 이 순서를 이해하면 `BitSet`을 더 효과적으로 활용할 수 있습니다!
|
||||
156
docs/ByteBuffer.md
Normal file
156
docs/ByteBuffer.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# **ByteBuffer란?**
|
||||
|
||||
`ByteBuffer`는 Java NIO(Non-blocking I/O)에서 **바이트 데이터를 효율적으로 다룰 수 있도록 제공하는 클래스**이다.
|
||||
기존의 `InputStream` 및 `OutputStream` 기반의 **blocking I/O**와 달리, **비동기적(Non-blocking)**으로 데이터를 처리할 수 있는 구조를 제공한다.
|
||||
|
||||
이 클래스는 **버퍼(Buffer)**라는 개념을 사용하여 데이터를 읽고 쓸 때 **직접 메모리에서 조작**할 수 있도록 하며, 성능 최적화에 유리하다.
|
||||
특히 **파일 I/O, 네트워크 전송, 바이너리 데이터 처리** 등에서 활용된다.
|
||||
|
||||
---
|
||||
|
||||
## **1. ByteBuffer의 주요 특징**
|
||||
✔ **고정된 크기의 버퍼**를 사용하여 데이터를 읽고 쓴다.
|
||||
✔ **Direct Buffer(직접 버퍼)와 Heap Buffer(힙 버퍼)** 두 가지 방식이 있다.
|
||||
✔ **데이터를 읽고 쓰는 포인터(위치, 한계, 용량)가 존재**하여 데이터 흐름을 효율적으로 관리한다.
|
||||
✔ **채널(Channel)과 함께 사용하여 고성능 I/O 처리**가 가능하다.
|
||||
|
||||
---
|
||||
|
||||
## **2. ByteBuffer의 구조**
|
||||
`ByteBuffer`는 데이터를 저장하는 **고정된 크기의 메모리 공간**을 가지며, 다음과 같은 **3가지 중요한 속성**을 갖는다.
|
||||
|
||||
| 속성 | 설명 |
|
||||
|------|------|
|
||||
| **position** | 현재 읽거나 쓸 위치 (0부터 시작) |
|
||||
| **limit** | 읽거나 쓸 수 있는 최대 위치 |
|
||||
| **capacity** | 버퍼의 전체 크기 (변경 불가) |
|
||||
|
||||
### **🔹 주요 속성 변화 예시**
|
||||
```java
|
||||
ByteBuffer buffer = ByteBuffer.allocate(10);
|
||||
System.out.println("초기 상태: " + buffer);
|
||||
// 출력: java.nio.HeapByteBuffer[pos=0 lim=10 cap=10]
|
||||
```
|
||||
위 상태에서 데이터를 `put()`하면 `position`이 증가하고, `flip()`을 호출하면 `position`이 0으로 초기화되어 데이터를 `get()`으로 읽을 준비를 한다.
|
||||
|
||||
---
|
||||
|
||||
## **3. ByteBuffer의 주요 메서드 정리**
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `allocate(int capacity)` | 힙 메모리에 지정한 크기의 버퍼 생성 |
|
||||
| `allocateDirect(int capacity)` | OS의 직접 메모리에 버퍼 생성 (성능 최적화) |
|
||||
| `put(byte b)` | 한 바이트 추가 |
|
||||
| `put(byte[] src)` | 바이트 배열을 버퍼에 추가 |
|
||||
| `get()` | 현재 위치의 바이트를 가져오고 `position`을 증가 |
|
||||
| `get(byte[] dst)` | 데이터를 바이트 배열로 가져옴 |
|
||||
| `flip()` | **쓰기 모드 → 읽기 모드** 변경 (`position=0`, `limit=현재 position`) |
|
||||
| `clear()` | 버퍼를 초기화 (`position=0`, `limit=capacity`) |
|
||||
| `rewind()` | **읽기 모드에서 처음부터 다시 읽기** (`position=0`, `limit` 유지) |
|
||||
| `compact()` | 아직 읽지 않은 데이터는 유지한 채, 새로운 데이터를 쓸 수 있도록 `position`을 조정 |
|
||||
| `mark()` | 현재 `position`을 저장 |
|
||||
| `reset()` | 저장한 `mark` 위치로 `position`을 되돌림 |
|
||||
|
||||
---
|
||||
|
||||
## **4. ByteBuffer 사용 예제**
|
||||
|
||||
### **(1) ByteBuffer 생성과 데이터 추가 (`put()`, `flip()`, `get()`)**
|
||||
```java
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteBufferExample {
|
||||
public static void main(String[] args) {
|
||||
// 10바이트 크기의 버퍼 생성
|
||||
ByteBuffer buffer = ByteBuffer.allocate(10);
|
||||
|
||||
// 데이터 추가 (쓰기 모드)
|
||||
buffer.put((byte) 1);
|
||||
buffer.put((byte) 2);
|
||||
buffer.put((byte) 3);
|
||||
System.out.println("데이터 추가 후: " + buffer);
|
||||
|
||||
// 읽기 모드로 변경 (flip)
|
||||
buffer.flip();
|
||||
System.out.println("flip() 후: " + buffer);
|
||||
|
||||
// 데이터 읽기
|
||||
System.out.println("읽은 데이터: " + buffer.get()); // 1
|
||||
System.out.println("읽은 데이터: " + buffer.get()); // 2
|
||||
System.out.println("get() 후: " + buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `flip()`을 호출하면 `position`이 0이 되어 데이터를 읽을 준비를 한다.
|
||||
✔ `get()`을 호출할 때마다 `position`이 증가하여 다음 데이터를 읽을 수 있다.
|
||||
|
||||
---
|
||||
|
||||
### **(2) `compact()`와 `clear()` 차이점**
|
||||
```java
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteBufferCompactClearExample {
|
||||
public static void main(String[] args) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(10);
|
||||
buffer.put((byte) 10);
|
||||
buffer.put((byte) 20);
|
||||
buffer.put((byte) 30);
|
||||
|
||||
buffer.flip(); // 읽기 모드 전환
|
||||
System.out.println("flip() 후: " + buffer);
|
||||
|
||||
System.out.println("읽은 데이터: " + buffer.get()); // 10
|
||||
System.out.println("읽은 데이터: " + buffer.get()); // 20
|
||||
|
||||
buffer.compact(); // 읽지 않은 데이터 유지한 채 쓰기 모드 변경
|
||||
System.out.println("compact() 후: " + buffer);
|
||||
|
||||
buffer.clear(); // 완전히 초기화
|
||||
System.out.println("clear() 후: " + buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ **`compact()`는 읽지 않은 데이터를 유지하고 `position`을 이동**
|
||||
✔ **`clear()`는 전체를 초기화하고 `position=0`으로 변경**
|
||||
|
||||
---
|
||||
|
||||
### **(3) DirectBuffer 사용 (`allocateDirect()`)**
|
||||
```java
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class DirectByteBufferExample {
|
||||
public static void main(String[] args) {
|
||||
// DirectBuffer 생성
|
||||
ByteBuffer directBuffer = ByteBuffer.allocateDirect(10);
|
||||
|
||||
directBuffer.put((byte) 42);
|
||||
directBuffer.flip();
|
||||
|
||||
System.out.println("DirectBuffer 데이터: " + directBuffer.get()); // 42
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `allocateDirect()`를 사용하면 **JVM의 힙 메모리가 아닌 OS의 직접 메모리에 할당**하여 성능을 높일 수 있다.
|
||||
✔ 다만, **GC(가비지 컬렉터)가 자동으로 해제하지 않기 때문에 직접 관리해야 한다.**
|
||||
|
||||
---
|
||||
|
||||
## **5. ByteBuffer vs. 일반 배열**
|
||||
| 비교 항목 | `ByteBuffer` | 일반 `byte[]` 배열 |
|
||||
|----------|-------------|----------------|
|
||||
| 메모리 할당 | 힙(Heap) 또는 직접 메모리(Direct) | 힙 메모리 |
|
||||
| 데이터 조작 | position/limit을 활용한 조작 | 단순한 인덱스 접근 |
|
||||
| 성능 | `allocateDirect()` 사용 시 속도 향상 | 단순 배열보다 속도가 느릴 수 있음 |
|
||||
| 가비지 컬렉션 | DirectBuffer는 수동 해제 필요 | 자동 관리 |
|
||||
|
||||
✔ **파일 I/O, 네트워크 프로그래밍에서는 `ByteBuffer`를 사용**하는 것이 효율적이다.
|
||||
✔ **단순한 배열 조작이라면 `byte[]`가 더 간단하다.**
|
||||
|
||||
---
|
||||
|
||||
## **6. 결론**
|
||||
`ByteBuffer`는 **Java NIO에서 고성능 I/O 처리를 위한 핵심 클래스**로, 데이터를 읽고 쓰는 방식이 기존의 `InputStream`보다 **더 유연하고 효율적**이다.
|
||||
특히 **파일 I/O, 네트워크 전송, 바이너리 데이터 처리**에 적합하며, **DirectBuffer**를 사용하면 성능을 극대화할 수 있다.
|
||||
380
docs/Collection Framework.md
Normal file
380
docs/Collection Framework.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# **Java의 List, Set, Queue, Deque, Map 인터페이스 및 구현 클래스 비교**
|
||||
|
||||
Java의 **Collection Framework**는 데이터를 효율적으로 저장하고 관리할 수 있는 다양한 자료구조를 제공한다.
|
||||
그중에서 `List`, `Set`, `Queue`, `Deque`, `Map` 인터페이스는 각기 다른 특성을 가지며,
|
||||
이를 구현하는 여러 클래스가 존재한다.
|
||||
|
||||
---
|
||||
|
||||
## **📌 1. 인터페이스별 메서드 정리**
|
||||
|
||||
### **1️⃣ List 인터페이스 (순서 유지, 중복 허용)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `void add(int index, E element)` | 특정 위치에 요소 삽입 |
|
||||
| `boolean add(E element)` | 리스트 끝에 요소 추가 |
|
||||
| `E get(int index)` | 특정 위치의 요소 반환 |
|
||||
| `E remove(int index)` | 특정 위치의 요소 삭제 |
|
||||
| `boolean remove(Object o)` | 특정 요소 삭제 |
|
||||
| `int indexOf(Object o)` | 특정 요소의 첫 번째 인덱스 반환 |
|
||||
| `int lastIndexOf(Object o)` | 특정 요소의 마지막 인덱스 반환 |
|
||||
| `List<E> subList(int fromIndex, int toIndex)` | 특정 범위의 부분 리스트 반환 |
|
||||
|
||||
#### **구현 클래스 비교**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------|
|
||||
| `ArrayList<E>` | 배열 기반, 인덱스 접근 빠름, 삽입/삭제 느림 |
|
||||
| `LinkedList<E>` | 이중 연결 리스트, 삽입/삭제 빠름, 인덱스 접근 느림 |
|
||||
| `Vector<E>` | `ArrayList`와 유사하지만 동기화(Synchronized) 지원 |
|
||||
| `Stack<E>` | `Vector`를 상속받아 LIFO(후입선출) 기능 제공 |
|
||||
|
||||
---
|
||||
|
||||
### **2️⃣ Set 인터페이스 (중복 허용 X, 순서 보장 X)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `boolean add(E e)` | 요소 추가 (중복이면 추가되지 않음) |
|
||||
| `boolean remove(Object o)` | 특정 요소 제거 |
|
||||
| `boolean contains(Object o)` | 특정 요소 포함 여부 확인 |
|
||||
| `int size()` | 요소 개수 반환 |
|
||||
| `void clear()` | 모든 요소 제거 |
|
||||
|
||||
#### **구현 클래스 비교**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------|
|
||||
| `HashSet<E>` | 해시 테이블 기반, 요소 순서 유지 X, 검색 빠름 |
|
||||
| `LinkedHashSet<E>` | `HashSet`과 같지만 요소 삽입 순서 유지 |
|
||||
| `TreeSet<E>` | 이진 탐색 트리 기반, 요소가 **자동 정렬됨** |
|
||||
|
||||
---
|
||||
|
||||
### **3️⃣ Queue 인터페이스 (FIFO, 선입선출 구조)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `boolean add(E e)` | 요소 추가 (실패 시 예외 발생) |
|
||||
| `boolean offer(E e)` | 요소 추가 (실패 시 false 반환) |
|
||||
| `E remove()` | 첫 번째 요소 제거 (큐가 비었으면 예외 발생) |
|
||||
| `E poll()` | 첫 번째 요소 제거 (큐가 비었으면 null 반환) |
|
||||
| `E element()` | 첫 번째 요소 반환 (큐가 비었으면 예외 발생) |
|
||||
| `E peek()` | 첫 번째 요소 반환 (큐가 비었으면 null 반환) |
|
||||
|
||||
#### **구현 클래스 비교**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------|
|
||||
| `LinkedList<E>` | `Queue` 인터페이스 구현, 양방향 리스트 구조 |
|
||||
| `PriorityQueue<E>` | 내부적으로 힙(Heap) 사용, **우선순위에 따라 정렬** |
|
||||
| `ArrayDeque<E>` | 양방향 큐 (Deque) 지원, `Stack` 대체 가능 |
|
||||
|
||||
---
|
||||
|
||||
### **4️⃣ Deque 인터페이스 (양방향 큐)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `void addFirst(E e)` | 앞쪽에 요소 추가 |
|
||||
| `void addLast(E e)` | 뒤쪽에 요소 추가 |
|
||||
| `E removeFirst()` | 앞쪽 요소 제거 |
|
||||
| `E removeLast()` | 뒤쪽 요소 제거 |
|
||||
| `E getFirst()` | 첫 번째 요소 반환 |
|
||||
| `E getLast()` | 마지막 요소 반환 |
|
||||
| `boolean offerFirst(E e)` | 앞쪽에 요소 추가 (실패 시 false 반환) |
|
||||
| `boolean offerLast(E e)` | 뒤쪽에 요소 추가 (실패 시 false 반환) |
|
||||
|
||||
#### **구현 클래스 비교**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------|
|
||||
| `ArrayDeque<E>` | 배열 기반, 크기 자동 조절 |
|
||||
| `LinkedList<E>` | 이중 연결 리스트 기반 |
|
||||
|
||||
---
|
||||
|
||||
### **5️⃣ Map 인터페이스 (키-값 쌍 저장, 중복 키 X)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `V put(K key, V value)` | 키-값 쌍 추가 (같은 키가 존재하면 기존 값 덮어쓰기) |
|
||||
| `V get(Object key)` | 특정 키에 대한 값 반환 |
|
||||
| `V remove(Object key)` | 특정 키의 값 삭제 |
|
||||
| `boolean containsKey(Object key)` | 특정 키 포함 여부 확인 |
|
||||
| `boolean containsValue(Object value)` | 특정 값 포함 여부 확인 |
|
||||
| `Set<K> keySet()` | 모든 키 반환 |
|
||||
| `Collection<V> values()` | 모든 값 반환 |
|
||||
| `Set<Map.Entry<K,V>> entrySet()` | 모든 키-값 쌍 반환 |
|
||||
|
||||
#### **구현 클래스 비교**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------|
|
||||
| `HashMap<K,V>` | 해시 테이블 기반, 키 순서 보장 X, 빠른 검색 |
|
||||
| `LinkedHashMap<K,V>` | 삽입 순서 유지 |
|
||||
| `TreeMap<K,V>` | 키 기준으로 **자동 정렬** |
|
||||
|
||||
---
|
||||
|
||||
## **📌 2. 구현 클래스 비교 요약**
|
||||
| 인터페이스 | 주요 구현 클래스 | 특징 |
|
||||
|------------|--------------|------|
|
||||
| `List` | `ArrayList` | 배열 기반, 빠른 조회, 삽입/삭제 느림 |
|
||||
| | `LinkedList` | 연결 리스트 기반, 삽입/삭제 빠름, 조회 느림 |
|
||||
| | `Vector` | `ArrayList`와 유사하지만 동기화 지원 |
|
||||
| | `Stack` | `Vector`를 확장하여 LIFO(후입선출) 제공 |
|
||||
| `Set` | `HashSet` | 중복 X, 순서 보장 X |
|
||||
| | `LinkedHashSet` | 삽입 순서 유지 |
|
||||
| | `TreeSet` | 자동 정렬 (오름차순) |
|
||||
| `Queue` | `LinkedList` | FIFO (선입선출) |
|
||||
| | `PriorityQueue` | 우선순위 기반 정렬 |
|
||||
| | `ArrayDeque` | 양방향 큐 (Deque) |
|
||||
| `Deque` | `ArrayDeque` | 배열 기반, 크기 자동 조절 |
|
||||
| | `LinkedList` | 연결 리스트 기반 |
|
||||
| `Map` | `HashMap` | 중복 키 X, 순서 보장 X |
|
||||
| | `LinkedHashMap` | 삽입 순서 유지 |
|
||||
| | `TreeMap` | 키 기준 자동 정렬 |
|
||||
|
||||
---
|
||||
|
||||
## **📌 3. 예제 코드**
|
||||
### **✔ `List` 사용 예제**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class ListExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("Apple");
|
||||
list.add("Banana");
|
||||
list.add("Cherry");
|
||||
|
||||
System.out.println(list.get(1)); // 출력: Banana
|
||||
list.remove("Banana");
|
||||
System.out.println(list); // 출력: [Apple, Cherry]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **📌 4. 마무리**
|
||||
✔ **List**: 순서 유지, 중복 허용
|
||||
✔ **Set**: 순서 없음, 중복 불가
|
||||
✔ **Queue**: FIFO 구조, `PriorityQueue`는 우선순위 정렬
|
||||
✔ **Deque**: 양방향 큐
|
||||
✔ **Map**: 키-값 저장, 키 중복 불가
|
||||
✔ 각 자료구조의 **특성을 이해하고 상황에 맞게 선택**하자! 🚀
|
||||
|
||||
|
||||
---
|
||||
|
||||
# **Java 컬렉션 유틸리티 클래스 (Collections, Arrays, etc.)**
|
||||
|
||||
Java에서는 컬렉션을 쉽게 다룰 수 있도록 **유틸리티 클래스**를 제공한다.
|
||||
대표적으로 `Collections`와 `Arrays` 클래스가 있으며, 이들은 **정렬, 검색, 동기화, 불변 컬렉션 생성** 등의 기능을 제공한다.
|
||||
|
||||
---
|
||||
|
||||
## **📌 1. Collections 클래스의 주요 메서드 정리**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `sort(List<T> list)` | 오름차순 정렬 |
|
||||
| `sort(List<T> list, Comparator<? super T> c)` | 지정한 정렬 기준으로 정렬 |
|
||||
| `binarySearch(List<T> list, T key)` | 이진 탐색 (정렬된 리스트에서만 사용 가능) |
|
||||
| `reverse(List<?> list)` | 리스트 요소 순서를 반대로 변경 |
|
||||
| `shuffle(List<?> list)` | 리스트 요소를 무작위로 섞음 |
|
||||
| `min(Collection<? extends T> coll)` | 컬렉션에서 최소값 반환 |
|
||||
| `max(Collection<? extends T> coll)` | 컬렉션에서 최대값 반환 |
|
||||
| `fill(List<? super T> list, T obj)` | 리스트의 모든 요소를 특정 값으로 채움 |
|
||||
| `copy(List<? super T> dest, List<? extends T> src)` | 리스트 복사 |
|
||||
| `replaceAll(List<T> list, T oldVal, T newVal)` | 특정 값을 새로운 값으로 대체 |
|
||||
| `frequency(Collection<?> c, Object o)` | 특정 요소가 컬렉션에 몇 번 등장하는지 반환 |
|
||||
| `disjoint(Collection<?> c1, Collection<?> c2)` | 두 컬렉션이 공통 요소를 가지지 않으면 `true` 반환 |
|
||||
| `synchronizedList(List<T> list)` | 동기화된 리스트 반환 |
|
||||
| `synchronizedSet(Set<T> set)` | 동기화된 셋 반환 |
|
||||
| `synchronizedMap(Map<K,V> map)` | 동기화된 맵 반환 |
|
||||
| `synchronizedSortedMap(SortedMap<K,V> map)` | 동기화된 정렬된 맵 반환 |
|
||||
| `unmodifiableList(List<? extends T> list)` | 불변 리스트 반환 |
|
||||
| `unmodifiableSet(Set<? extends T> set)` | 불변 셋 반환 |
|
||||
| `unmodifiableMap(Map<? extends K,? extends V> map)` | 불변 맵 반환 |
|
||||
|
||||
---
|
||||
|
||||
## **📌 2. Arrays 클래스의 주요 메서드 정리**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `sort(T[] array)` | 배열 정렬 (오름차순) |
|
||||
| `sort(T[] array, Comparator<? super T> c)` | 사용자 정의 비교 기준으로 정렬 |
|
||||
| `binarySearch(T[] array, T key)` | 배열에서 이진 탐색 수행 |
|
||||
| `fill(T[] array, T val)` | 배열의 모든 요소를 특정 값으로 채움 |
|
||||
| `copyOf(T[] original, int newLength)` | 배열을 새로운 크기로 복사 |
|
||||
| `copyOfRange(T[] original, int from, int to)` | 배열의 특정 범위를 복사 |
|
||||
| `equals(T[] a, T[] b)` | 두 배열이 같은지 비교 |
|
||||
| `deepEquals(Object[] a, Object[] b)` | 다차원 배열 비교 |
|
||||
| `asList(T... a)` | 배열을 리스트로 변환 |
|
||||
| `toString(T[] a)` | 배열을 문자열로 변환 |
|
||||
|
||||
---
|
||||
|
||||
## **📌 3. 주요 메서드 설명 및 예제**
|
||||
|
||||
### **✔ `sort()` - 정렬**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class SortExample {
|
||||
public static void main(String[] args) {
|
||||
List<Integer> list = Arrays.asList(5, 3, 8, 1, 2);
|
||||
Collections.sort(list);
|
||||
System.out.println(list); // 출력: [1, 2, 3, 5, 8]
|
||||
}
|
||||
}
|
||||
```
|
||||
- 기본적으로 **오름차순 정렬**한다.
|
||||
- `Comparator`를 사용하면 정렬 기준을 변경할 수 있다.
|
||||
|
||||
```java
|
||||
Collections.sort(list, Comparator.reverseOrder()); // 내림차순 정렬
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `binarySearch()` - 이진 탐색**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class BinarySearchExample {
|
||||
public static void main(String[] args) {
|
||||
List<Integer> list = Arrays.asList(1, 2, 3, 5, 8);
|
||||
int index = Collections.binarySearch(list, 3);
|
||||
System.out.println("Index of 3: " + index); // 출력: Index of 3: 2
|
||||
}
|
||||
}
|
||||
```
|
||||
- **정렬된 리스트에서만 사용 가능**하다.
|
||||
- 찾는 값이 없으면 **음수 반환**.
|
||||
|
||||
---
|
||||
|
||||
### **✔ `shuffle()` - 무작위 섞기**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class ShuffleExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Arrays.asList("A", "B", "C", "D");
|
||||
Collections.shuffle(list);
|
||||
System.out.println(list); // 매 실행마다 다른 순서
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `min()` / `max()` - 최소값과 최대값 찾기**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class MinMaxExample {
|
||||
public static void main(String[] args) {
|
||||
List<Integer> list = Arrays.asList(3, 8, 1, 5, 2);
|
||||
System.out.println(Collections.min(list)); // 출력: 1
|
||||
System.out.println(Collections.max(list)); // 출력: 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `fill()` - 모든 요소를 특정 값으로 채우기**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class FillExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
|
||||
Collections.fill(list, "X");
|
||||
System.out.println(list); // 출력: [X, X, X]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `replaceAll()` - 특정 값 치환**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class ReplaceAllExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "apple"));
|
||||
Collections.replaceAll(list, "apple", "orange");
|
||||
System.out.println(list); // 출력: [orange, banana, orange]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `frequency()` - 특정 값 개수 세기**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class FrequencyExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Arrays.asList("apple", "banana", "apple", "cherry", "apple");
|
||||
System.out.println(Collections.frequency(list, "apple")); // 출력: 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `synchronizedList()` - 동기화 리스트**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class SynchronizedListExample {
|
||||
public static void main(String[] args) {
|
||||
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
synchronized (list) {
|
||||
list.add(1);
|
||||
list.add(2);
|
||||
list.add(3);
|
||||
}
|
||||
|
||||
System.out.println(list); // 출력: [1, 2, 3]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **✔ `unmodifiableList()` - 불변 리스트**
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class UnmodifiableListExample {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Arrays.asList("A", "B", "C");
|
||||
List<String> unmodifiableList = Collections.unmodifiableList(list);
|
||||
|
||||
unmodifiableList.add("D"); // 예외 발생
|
||||
}
|
||||
}
|
||||
```
|
||||
- `unmodifiableList`는 **수정 불가**.
|
||||
|
||||
---
|
||||
|
||||
## **📌 4. 정리**
|
||||
| 기능 | Collections 메서드 | Arrays 메서드 |
|
||||
|------|----------------|------------|
|
||||
| 정렬 | `sort()` | `sort()` |
|
||||
| 검색 | `binarySearch()` | `binarySearch()` |
|
||||
| 최소/최대값 | `min()`, `max()` | X |
|
||||
| 무작위 섞기 | `shuffle()` | X |
|
||||
| 요소 채우기 | `fill()` | `fill()` |
|
||||
| 요소 치환 | `replaceAll()` | X |
|
||||
| 특정 값 개수 | `frequency()` | X |
|
||||
| 동기화 컬렉션 | `synchronizedList()` | X |
|
||||
| 불변 컬렉션 | `unmodifiableList()` | X |
|
||||
|
||||
✔ `Collections`는 **컬렉션(List, Set, Map) 관련 기능** 제공
|
||||
✔ `Arrays`는 **배열 관련 기능** 제공
|
||||
|
||||
이제 Java 컬렉션을 더욱 편리하게 다룰 수 있다! 🚀
|
||||
151
docs/Collections.md
Normal file
151
docs/Collections.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# **자바 Collections 쉽게 배우기**
|
||||
|
||||
자바에서 `Collections Framework`는 **배열보다 더 강력한 자료구조**를 제공한다.
|
||||
배열은 크기가 고정되지만, 컬렉션은 **동적으로 크기를 조정할 수 있으며, 다양한 데이터 구조**를 제공한다.
|
||||
|
||||
컬렉션은 크게 다음과 같은 주요 인터페이스로 구성된다.
|
||||
- `List` : 순서가 있는 자료구조 (배열과 유사, 중복 허용)
|
||||
- `Set` : 중복을 허용하지 않는 자료구조
|
||||
- `Queue` : FIFO(선입선출) 방식의 자료구조
|
||||
- `Map` : 키-값 쌍을 저장하는 자료구조
|
||||
|
||||
---
|
||||
|
||||
## **1. 주요 컬렉션 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `List` 인터페이스를 구현하는 클래스 (`ArrayList`, `LinkedList`)**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------------------------------|
|
||||
| `ArrayList<E>` | 크기가 동적으로 변하는 배열 |
|
||||
| `LinkedList<E>` | 이중 연결 리스트 구조, 삽입·삭제 빠름 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `add(E e)` | 요소 추가 |
|
||||
| `get(int index)` | 특정 위치의 요소 반환 |
|
||||
| `remove(int index)` | 특정 위치의 요소 삭제 |
|
||||
| `size()` | 리스트 크기 반환 |
|
||||
| `contains(Object o)` | 특정 요소 포함 여부 확인 |
|
||||
| `indexOf(Object o)` | 특정 요소의 인덱스 반환 |
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("Apple");
|
||||
list.add("Banana");
|
||||
|
||||
System.out.println(list.get(0)); // Apple
|
||||
System.out.println(list.contains("Banana")); // true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Set` 인터페이스를 구현하는 클래스 (`HashSet`, `TreeSet`)**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------------------------------|
|
||||
| `HashSet<E>` | 중복 없는 요소 저장, 순서 보장 안 됨 |
|
||||
| `TreeSet<E>` | 정렬된 상태 유지 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `add(E e)` | 요소 추가 |
|
||||
| `remove(Object o)` | 요소 삭제 |
|
||||
| `contains(Object o)` | 요소 포함 여부 확인 |
|
||||
| `size()` | 크기 반환 |
|
||||
| `clear()` | 모든 요소 제거 |
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
Set<Integer> set = new HashSet<>();
|
||||
set.add(10);
|
||||
set.add(20);
|
||||
set.add(10); // 중복된 값 추가 X
|
||||
|
||||
System.out.println(set.size()); // 2
|
||||
System.out.println(set.contains(20)); // true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(3) `Queue` 인터페이스를 구현하는 클래스 (`LinkedList`, `PriorityQueue`)**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------------------------------|
|
||||
| `LinkedList<E>` | FIFO(선입선출) 방식으로 동작 |
|
||||
| `PriorityQueue<E>` | 우선순위에 따라 정렬 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `offer(E e)` | 요소 추가 (큐의 끝에) |
|
||||
| `poll()` | 첫 번째 요소 반환 후 제거 |
|
||||
| `peek()` | 첫 번째 요소 조회 (삭제 X) |
|
||||
| `isEmpty()` | 큐가 비어 있는지 확인 |
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
Queue<String> queue = new LinkedList<>();
|
||||
queue.offer("A");
|
||||
queue.offer("B");
|
||||
|
||||
System.out.println(queue.poll()); // A (먼저 들어온 요소가 제거됨)
|
||||
System.out.println(queue.peek()); // B (두 번째 요소 조회)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(4) `Map` 인터페이스를 구현하는 클래스 (`HashMap`, `TreeMap`)**
|
||||
| 클래스 | 특징 |
|
||||
|--------|------------------------------|
|
||||
| `HashMap<K, V>` | 키-값 쌍 저장, 순서 보장 안 됨 |
|
||||
| `TreeMap<K, V>` | 키 기준으로 정렬 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `put(K key, V value)` | 키-값 추가 |
|
||||
| `get(Object key)` | 키에 해당하는 값 반환 |
|
||||
| `remove(Object key)` | 키에 해당하는 요소 삭제 |
|
||||
| `containsKey(Object key)` | 특정 키 존재 여부 확인 |
|
||||
| `containsValue(Object value)` | 특정 값 존재 여부 확인 |
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
map.put("Apple", 100);
|
||||
map.put("Banana", 200);
|
||||
|
||||
System.out.println(map.get("Apple")); // 100
|
||||
System.out.println(map.containsKey("Banana")); // true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **2. `Collections` 유틸리티 클래스**
|
||||
자바에서는 `java.util.Collections` 클래스를 제공하여 **컬렉션을 조작하는 다양한 기능**을 지원한다.
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `sort(List<T> list)` | 리스트 정렬 |
|
||||
| `reverse(List<T> list)` | 리스트 역순 정렬 |
|
||||
| `shuffle(List<T> list)` | 요소 섞기 |
|
||||
| `max(Collection<T> coll)` | 최대값 반환 |
|
||||
| `min(Collection<T> coll)` | 최소값 반환 |
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
List<Integer> numbers = Arrays.asList(5, 3, 8, 1);
|
||||
Collections.sort(numbers);
|
||||
System.out.println(numbers); // [1, 3, 5, 8]
|
||||
|
||||
Collections.reverse(numbers);
|
||||
System.out.println(numbers); // [8, 5, 3, 1]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **3. 정리**
|
||||
✅ **`List`** : 순서가 있는 자료구조 (중복 허용)
|
||||
✅ **`Set`** : 중복을 허용하지 않는 자료구조
|
||||
✅ **`Queue`** : 선입선출(FIFO) 자료구조
|
||||
✅ **`Map`** : 키-값 쌍을 저장하는 자료구조
|
||||
✅ **`Collections` 유틸리티 클래스**를 활용하면 컬렉션을 더욱 편리하게 조작 가능!
|
||||
|
||||
즉, 자바의 컬렉션을 잘 활용하면 **효율적인 데이터 관리와 조작이 가능하다!**
|
||||
225
docs/Concurrency.md
Normal file
225
docs/Concurrency.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# 자바 동시성(Concurrency) API 정리 및 쉬운 설명
|
||||
|
||||
자바의 동시성(Concurrency) API는 **멀티스레딩을 효율적으로 관리하고 안전하게 실행할 수 있도록 지원하는 기능**을 제공한다.
|
||||
즉, **여러 작업을 동시에 실행하여 프로그램 성능을 향상**시킬 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 주요 클래스 및 메서드 정리
|
||||
|
||||
### (1) `Thread` 클래스 (기본 스레드 실행)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `start()` | 새로운 스레드를 실행 |
|
||||
| `run()` | 실행할 코드 정의 (직접 호출하면 동작하지 않음) |
|
||||
| `sleep(ms)` | 지정된 시간(ms) 동안 스레드 일시 정지 |
|
||||
| `join()` | 현재 스레드가 종료될 때까지 다른 스레드 대기 |
|
||||
| `interrupt()` | 실행 중인 스레드를 인터럽트 (깨우기) |
|
||||
| `isAlive()` | 스레드가 실행 중인지 확인 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
class MyThread extends Thread {
|
||||
public void run() {
|
||||
System.out.println("스레드 실행 중!");
|
||||
}
|
||||
}
|
||||
|
||||
MyThread t = new MyThread();
|
||||
t.start(); // 스레드 실행
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### (2) `Runnable` 인터페이스 (스레드 실행)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `run()` | 실행할 코드 정의 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
class MyRunnable implements Runnable {
|
||||
public void run() {
|
||||
System.out.println("Runnable 스레드 실행!");
|
||||
}
|
||||
}
|
||||
|
||||
Thread t = new Thread(new MyRunnable());
|
||||
t.start();
|
||||
```
|
||||
→ `Thread` 클래스를 직접 상속하지 않고 `Runnable`을 구현하여 사용.
|
||||
|
||||
---
|
||||
|
||||
### (3) `ExecutorService` (스레드 풀)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `submit(Runnable)` | 스레드를 실행 (결과 없음) |
|
||||
| `submit(Callable<T>)` | 스레드를 실행하고 결과 반환 |
|
||||
| `shutdown()` | 스레드 풀 종료 (기존 작업 수행 후 종료) |
|
||||
| `shutdownNow()` | 즉시 모든 작업 중단 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||
|
||||
executor.submit(() -> System.out.println("스레드 풀에서 실행"));
|
||||
executor.shutdown();
|
||||
```
|
||||
→ `Executors.newFixedThreadPool(2)`를 사용해 2개의 스레드를 관리.
|
||||
|
||||
---
|
||||
|
||||
### (4) `Callable` & `Future` (결과 반환이 필요한 작업)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `call()` | 실행할 코드 정의, 결과 반환 |
|
||||
| `get()` | `Future`에서 결과 가져오기 (블로킹) |
|
||||
| `isDone()` | 작업 완료 여부 확인 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
Callable<Integer> task = () -> {
|
||||
Thread.sleep(1000);
|
||||
return 10;
|
||||
};
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
Future<Integer> future = executor.submit(task);
|
||||
|
||||
System.out.println(future.get()); // 결과 가져오기
|
||||
executor.shutdown();
|
||||
```
|
||||
→ `Callable`을 사용하면 **스레드 실행 후 결과 값을 반환**할 수 있음.
|
||||
|
||||
---
|
||||
|
||||
### (5) `ReentrantLock` (락을 이용한 동기화)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `lock()` | 락 획득 |
|
||||
| `unlock()` | 락 해제 |
|
||||
| `tryLock()` | 락을 시도하고 성공하면 `true` 반환 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
System.out.println("임계영역 실행");
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
```
|
||||
→ `synchronized` 키워드 대신 `ReentrantLock`을 사용하여 더 정교한 동기화 가능.
|
||||
|
||||
---
|
||||
|
||||
### (6) `Semaphore` (동시 실행 제한)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `acquire()` | 리소스 사용 요청 (없으면 대기) |
|
||||
| `release()` | 사용한 리소스 반환 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
Semaphore semaphore = new Semaphore(2);
|
||||
|
||||
semaphore.acquire(); // 사용 가능하면 진행, 아니면 대기
|
||||
System.out.println("리소스 사용 중");
|
||||
semaphore.release();
|
||||
```
|
||||
→ 한 번에 2개의 스레드만 특정 코드 실행 가능.
|
||||
|
||||
---
|
||||
|
||||
### (7) `CountDownLatch` (스레드가 모두 끝날 때까지 대기)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `await()` | 모든 스레드가 완료될 때까지 대기 |
|
||||
| `countDown()` | 하나의 작업 완료 처리 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
CountDownLatch latch = new CountDownLatch(3);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
new Thread(() -> {
|
||||
System.out.println("작업 완료");
|
||||
latch.countDown();
|
||||
}).start();
|
||||
}
|
||||
|
||||
latch.await();
|
||||
System.out.println("모든 작업 완료");
|
||||
```
|
||||
→ 모든 작업(`countDown()` 3회)이 끝나야 `await()`이 풀림.
|
||||
|
||||
---
|
||||
|
||||
### (8) `CyclicBarrier` (스레드가 모두 도달할 때까지 대기)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `await()` | 지정된 개수의 스레드가 모일 때까지 대기 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
|
||||
System.out.println("모든 스레드 도착!");
|
||||
});
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
new Thread(() -> {
|
||||
System.out.println("스레드 실행 중");
|
||||
barrier.await();
|
||||
}).start();
|
||||
}
|
||||
```
|
||||
→ `CyclicBarrier`는 정해진 개수의 스레드가 모두 도착할 때까지 기다림.
|
||||
|
||||
---
|
||||
|
||||
## 2. 자바 동시성 쉽게 설명하기
|
||||
|
||||
자바에서 동시성을 다룰 때 중요한 개념은 **멀티스레딩과 스레드 동기화**다.
|
||||
즉, 여러 개의 스레드가 **같은 자원을 동시에 접근하면 충돌이 발생**할 수 있으므로, 이를 적절히 제어해야 한다.
|
||||
|
||||
### 1️⃣ **기본적인 멀티스레드**
|
||||
- `Thread` 또는 `Runnable`을 사용해 실행
|
||||
- `start()`로 실행, `join()`으로 대기
|
||||
|
||||
### 2️⃣ **스레드 풀 사용 (`ExecutorService`)**
|
||||
- 직접 스레드를 생성하면 관리가 어렵기 때문에, **스레드 풀을 사용하여 성능 최적화**
|
||||
- `submit()`으로 작업을 실행하고, `shutdown()`으로 종료
|
||||
|
||||
### 3️⃣ **결과를 받아야 한다면? (`Callable` & `Future`)**
|
||||
- `Callable`을 사용하면 작업이 끝난 후 결과 값을 반환 가능
|
||||
- `future.get()`을 호출하면 결과를 가져올 수 있음
|
||||
|
||||
### 4️⃣ **공유 자원 충돌 방지 (`ReentrantLock`, `synchronized`)**
|
||||
- `synchronized` 키워드는 단순하지만 유연성이 낮음
|
||||
- `ReentrantLock`은 더 정교한 동기화 제공
|
||||
|
||||
### 5️⃣ **특정 조건을 만족할 때 실행 (`CountDownLatch`, `CyclicBarrier`)**
|
||||
- `CountDownLatch` → 여러 개의 스레드가 끝날 때까지 대기
|
||||
- `CyclicBarrier` → 여러 스레드가 동시에 도달해야 실행
|
||||
|
||||
---
|
||||
|
||||
## 3. 정리
|
||||
|
||||
✅ 멀티스레딩을 쉽게 하려면? → `ExecutorService`
|
||||
✅ 스레드 실행 결과를 받아야 한다면? → `Callable`, `Future`
|
||||
✅ 여러 스레드가 동시에 공유 자원을 사용하면? → `ReentrantLock`, `synchronized`
|
||||
✅ 여러 작업이 끝난 후 실행되게 하려면? → `CountDownLatch`, `CyclicBarrier`
|
||||
|
||||
즉, **자바 동시성 API를 적절히 활용하면 멀티스레딩을 효율적으로 관리할 수 있다!**
|
||||
199
docs/Concurrency/01_Thread.md
Normal file
199
docs/Concurrency/01_Thread.md
Normal file
@@ -0,0 +1,199 @@
|
||||
자바의 `Thread` 클래스와 관련된 주요 메서드들을 표로 정리하고, 각 메서드에 대한 설명과 예시를 포함한 글을 작성하겠습니다. 아래는 요청하신 내용입니다.
|
||||
|
||||
---
|
||||
|
||||
### 자바 Thread 관련 메서드 표
|
||||
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-------------------------|-----------|--------------------------------------------------------------------------------------|
|
||||
| `start()` | `void` | 스레드를 시작하며, `run()` 메서드를 호출하여 실행 준비 상태로 만듦 |
|
||||
| `run()` | `void` | 스레드가 실행할 작업을 정의 (직접 호출 시 스레드 생성 없이 현재 스레드에서 실행) |
|
||||
| `sleep(long millis)` | `void` | 지정된 시간(밀리초) 동안 스레드를 일시 정지 (정적 메서드) |
|
||||
| `join()` | `void` | 해당 스레드가 종료될 때까지 현재 스레드가 기다림 |
|
||||
| `interrupt()` | `void` | 스레드에 인터럽트를 발생시켜 작업을 중단 요청 |
|
||||
| `isAlive()` | `boolean` | 스레드가 실행 중인지 여부를 확인 |
|
||||
| `setPriority(int n)` | `void` | 스레드의 우선순위를 설정 (1~10 사이 값) |
|
||||
| `getPriority()` | `int` | 스레드의 현재 우선순위를 반환 |
|
||||
| `yield()` | `void` | 현재 스레드가 다른 스레드에게 실행 기회를 양보 (정적 메서드) |
|
||||
| `currentThread()` | `Thread` | 현재 실행 중인 스레드 객체를 반환 (정적 메서드) |
|
||||
|
||||
---
|
||||
|
||||
### Thread 메서드 설명 및 예시
|
||||
|
||||
자바에서 스레드는 멀티태스킹을 구현하는 데 필수적인 요소입니다. `Thread` 클래스와 `Runnable` 인터페이스를 통해 스레드를 생성하고 관리할 수 있으며, 위 표에 정리된 메서드들은 스레드의 생명주기와 동작을 제어하는 데 사용됩니다. 아래에서는 주요 메서드들을 예시와 함께 설명하겠습니다.
|
||||
|
||||
#### 1. `start()`와 `run()`
|
||||
- **`start()`**: 새로운 스레드를 생성하고 실행 가능한 상태로 만들어 `run()` 메서드를 호출합니다.
|
||||
- **`run()`**: 실제 스레드가 수행할 작업을 정의합니다. 단, `run()`을 직접 호출하면 새로운 스레드가 생성되지 않고 현재 스레드에서 실행됩니다.
|
||||
|
||||
```java
|
||||
class MyThread extends Thread {
|
||||
public void run() {
|
||||
System.out.println("스레드 실행 중: " + Thread.currentThread().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadExample {
|
||||
public static void main(String[] args) {
|
||||
MyThread thread = new MyThread();
|
||||
thread.start(); // 새로운 스레드 생성 및 실행
|
||||
// thread.run(); // 이렇게 호출하면 메인 스레드에서 실행됨
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력: `스레드 실행 중: Thread-0`
|
||||
|
||||
#### 2. `sleep(long millis)`
|
||||
- 지정된 시간 동안 현재 스레드를 일시 정지합니다. 다른 스레드에게 CPU를 양보하며, `InterruptedException`을 처리해야 합니다.
|
||||
|
||||
```java
|
||||
public class SleepExample {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
System.out.println("2초 후에 깨어납니다.");
|
||||
Thread.sleep(2000); // 2초 대기
|
||||
System.out.println("깨어났습니다!");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력:
|
||||
```
|
||||
2초 후에 깨어납니다.
|
||||
(2초 대기 후)
|
||||
깨어났습니다!
|
||||
```
|
||||
|
||||
#### 3. `join()`
|
||||
- 다른 스레드가 종료될 때까지 현재 스레드가 기다리도록 합니다. 작업 순서를 보장할 때 유용합니다.
|
||||
|
||||
```java
|
||||
class WorkerThread extends Thread {
|
||||
public void run() {
|
||||
System.out.println("작업 시작");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("작업 완료");
|
||||
}
|
||||
}
|
||||
|
||||
public class JoinExample {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
WorkerThread worker = new WorkerThread();
|
||||
worker.start();
|
||||
worker.join(); // worker 스레드가 끝날 때까지 대기
|
||||
System.out.println("메인 스레드 계속 진행");
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력:
|
||||
```
|
||||
작업 시작
|
||||
(1초 후)
|
||||
작업 완료
|
||||
메인 스레드 계속 진행
|
||||
```
|
||||
|
||||
#### 4. `interrupt()`
|
||||
- 스레드에 인터럽트를 발생시켜 작업을 중단하도록 요청합니다. `sleep()`이나 `wait()` 중인 스레드는 `InterruptedException`을 발생시킵니다.
|
||||
|
||||
```java
|
||||
class InterruptThread extends Thread {
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
System.out.println("작업 중...");
|
||||
Thread.sleep(500);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("인터럽트 발생! 종료합니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InterruptExample {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
InterruptThread thread = new InterruptThread();
|
||||
thread.start();
|
||||
Thread.sleep(2000); // 2초 후 인터럽트
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력:
|
||||
```
|
||||
작업 중...
|
||||
작업 중...
|
||||
작업 중...
|
||||
인터럽트 발생! 종료합니다.
|
||||
```
|
||||
|
||||
#### 5. `setPriority(int n)`와 `getPriority()`
|
||||
- 스레드의 우선순위를 설정하고 확인합니다. 우선순위는 1(최저)에서 10(최고)까지이며, 기본값은 5입니다.
|
||||
|
||||
```java
|
||||
public class PriorityExample {
|
||||
public static void main(String[] args) {
|
||||
Thread thread1 = new Thread(() -> System.out.println("Thread 1 실행"));
|
||||
Thread thread2 = new Thread(() -> System.out.println("Thread 2 실행"));
|
||||
|
||||
thread1.setPriority(Thread.MIN_PRIORITY); // 1
|
||||
thread2.setPriority(Thread.MAX_PRIORITY); // 10
|
||||
|
||||
System.out.println("Thread 1 우선순위: " + thread1.getPriority());
|
||||
System.out.println("Thread 2 우선순위: " + thread2.getPriority());
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력 (실행 순서는 보장되지 않음):
|
||||
```
|
||||
Thread 1 우선순위: 1
|
||||
Thread 2 우선순위: 10
|
||||
Thread 2 실행
|
||||
Thread 1 실행
|
||||
```
|
||||
|
||||
#### 6. `yield()`
|
||||
- 현재 스레드가 다른 스레드에게 실행 기회를 양보합니다. CPU 스케줄링에 따라 결과가 달라질 수 있습니다.
|
||||
|
||||
```java
|
||||
class YieldThread extends Thread {
|
||||
public void run() {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.println(getName() + " 실행 중");
|
||||
Thread.yield(); // 다른 스레드에게 양보
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class YieldExample {
|
||||
public static void main(String[] args) {
|
||||
Thread thread1 = new YieldThread();
|
||||
Thread thread2 = new YieldThread();
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- 출력 (순서 무작위):
|
||||
```
|
||||
Thread-0 실행 중
|
||||
Thread-1 실행 중
|
||||
Thread-0 실행 중
|
||||
Thread-1 실행 중
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
자바의 `Thread` 클래스는 스레드 생성, 실행, 제어를 위한 다양한 메서드를 제공합니다. `start()`와 `run()`으로 스레드를 시작하고, `sleep()`, `join()`, `interrupt()`로 동작을 제어하며, `setPriority()`와 `yield()`로 스케줄링을 조정할 수 있습니다. 위 예시를 통해 각 메서드의 동작을 이해하고, 실제 멀티스레드 프로그래밍에 적용해 보세요!
|
||||
193
docs/Concurrency/02_synchronized.md
Normal file
193
docs/Concurrency/02_synchronized.md
Normal file
@@ -0,0 +1,193 @@
|
||||
### 자바 스레드 동기화에 대한 설명
|
||||
|
||||
스레드 동기화는 멀티스레드 환경에서 여러 스레드가 공유 자원(예: 변수, 객체 등)에 동시에 접근할 때 발생할 수 있는 데이터 불일치 문제를 해결하기 위해 사용됩니다. 자바에서는 동기화를 통해 특정 코드 블록이나 메서드가 한 번에 하나의 스레드만 실행하도록 보장합니다. 이를 통해 **경쟁 조건(race condition)**을 방지하고, 데이터의 무결성을 유지할 수 있습니다.
|
||||
|
||||
자바에서 스레드 동기화를 구현하는 주요 방법은 `synchronized` 키워드와 `java.util.concurrent` 패키지의 도구(예: `Lock`, `Semaphore` 등)를 사용하는 것입니다. 아래에서는 `synchronized` 키워드를 중심으로 설명하고, 예시를 제공하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### 스레드 동기화의 필요성
|
||||
예를 들어, 은행 계좌 잔액을 관리하는 프로그램에서 두 스레드가 동시에 잔액을 수정하려고 하면 문제가 발생할 수 있습니다. 동기화가 없으면 잔액이 잘못 계산될 가능성이 높습니다. 이를 **경쟁 조건**이라고 부르며, 동기화로 해결할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
### `synchronized` 키워드 사용 방법
|
||||
자바에서 동기화는 두 가지 방식으로 적용됩니다:
|
||||
1. **`synchronized` 메서드**: 메서드 전체를 동기화하여 한 번에 하나의 스레드만 실행하도록 만듭니다.
|
||||
2. **`synchronized` 블록**: 특정 코드 블록만 동기화하여 더 세밀한 제어가 가능합니다.
|
||||
|
||||
---
|
||||
|
||||
### 예시 1: 동기화 없는 경우 (문제 발생)
|
||||
아래는 동기화 없이 두 스레드가 계좌 잔액을 동시에 수정하는 예시입니다.
|
||||
|
||||
```java
|
||||
class BankAccount {
|
||||
private int balance = 1000; // 초기 잔액
|
||||
|
||||
public void withdraw(int amount) {
|
||||
if (balance >= amount) {
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount);
|
||||
balance -= amount; // 잔액 감소
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBalance() {
|
||||
return balance;
|
||||
}
|
||||
}
|
||||
|
||||
public class NoSyncExample {
|
||||
public static void main(String[] args) {
|
||||
BankAccount account = new BankAccount();
|
||||
|
||||
Thread t1 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드1");
|
||||
|
||||
Thread t2 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드2");
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시 (무작위 결과)**:
|
||||
```
|
||||
스레드1 출금 시도: 800
|
||||
스레드2 출금 시도: 800
|
||||
스레드1 출금 후 잔액: 200
|
||||
스레드2 출금 후 잔액: -600
|
||||
```
|
||||
- **문제**: 두 스레드가 동시에 `withdraw`를 호출하면서 잔액이 음수가 되는 비정상적인 결과 발생. 이는 `if (balance >= amount)` 조건을 확인한 후 `balance -= amount`가 실행되기 전에 다른 스레드가 끼어들었기 때문입니다.
|
||||
|
||||
---
|
||||
|
||||
### 예시 2: `synchronized` 메서드로 동기화
|
||||
`withdraw` 메서드에 `synchronized`를 적용하면 한 번에 하나의 스레드만 메서드를 실행할 수 있습니다.
|
||||
|
||||
```java
|
||||
class BankAccount {
|
||||
private int balance = 1000;
|
||||
|
||||
public synchronized void withdraw(int amount) {
|
||||
if (balance >= amount) {
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount);
|
||||
try {
|
||||
Thread.sleep(100); // 경쟁 조건 시뮬레이션
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
balance -= amount;
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance);
|
||||
} else {
|
||||
System.out.println(Thread.currentThread().getName() + " 잔액 부족");
|
||||
}
|
||||
}
|
||||
|
||||
public int getBalance() {
|
||||
return balance;
|
||||
}
|
||||
}
|
||||
|
||||
public class SyncMethodExample {
|
||||
public static void main(String[] args) {
|
||||
BankAccount account = new BankAccount();
|
||||
|
||||
Thread t1 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드1");
|
||||
|
||||
Thread t2 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드2");
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
스레드1 출금 시도: 800
|
||||
스레드1 출금 후 잔액: 200
|
||||
스레드2 출금 시도: 800
|
||||
스레드2 잔액 부족
|
||||
```
|
||||
- **설명**: `synchronized` 덕분에 첫 번째 스레드가 `withdraw`를 완료할 때까지 두 번째 스레드가 대기하며, 잔액이 음수로 떨어지지 않음.
|
||||
|
||||
---
|
||||
|
||||
### 예시 3: `synchronized` 블록으로 동기화
|
||||
메서드 전체가 아닌 특정 코드 블록만 동기화하려면 `synchronized` 블록을 사용합니다. 이때 동기화 대상 객체(락 객체)를 지정해야 합니다.
|
||||
|
||||
```java
|
||||
class BankAccount {
|
||||
private int balance = 1000;
|
||||
private final Object lock = new Object(); // 락 객체
|
||||
|
||||
public void withdraw(int amount) {
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount);
|
||||
synchronized (lock) { // 동기화 블록
|
||||
if (balance >= amount) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
balance -= amount;
|
||||
System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance);
|
||||
} else {
|
||||
System.out.println(Thread.currentThread().getName() + " 잔액 부족");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getBalance() {
|
||||
return balance;
|
||||
}
|
||||
}
|
||||
|
||||
public class SyncBlockExample {
|
||||
public static void main(String[] args) {
|
||||
BankAccount account = new BankAccount();
|
||||
|
||||
Thread t1 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드1");
|
||||
|
||||
Thread t2 = new Thread(() -> {
|
||||
account.withdraw(800);
|
||||
}, "스레드2");
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
스레드1 출금 시도: 800
|
||||
스레드2 출금 시도: 800
|
||||
스레드1 출금 후 잔액: 200
|
||||
스레드2 잔액 부족
|
||||
```
|
||||
- **설명**: `synchronized (lock)` 블록 내의 코드만 동기화되며, `lock` 객체를 통해 스레드 간 접근을 제어함. 메서드 전체를 동기화하는 것보다 유연성이 높음.
|
||||
|
||||
---
|
||||
|
||||
### 동기화의 장점과 주의점
|
||||
- **장점**:
|
||||
- 공유 자원에 대한 안전한 접근 보장.
|
||||
- 데이터 무결성 유지.
|
||||
- **주의점**:
|
||||
- 과도한 동기화는 성능 저하(데드락, 스레드 대기 시간 증가)를 유발할 수 있음.
|
||||
- 필요한 범위만 동기화하도록 설계해야 함.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
스레드 동기화는 멀티스레드 프로그래밍에서 필수적인 개념으로, 자바에서는 `synchronized` 키워드를 통해 간단히 구현할 수 있습니다. `synchronized` 메서드와 블록을 적절히 사용하면 경쟁 조건을 방지하고 안정적인 프로그램을 작성할 수 있습니다. 위 예시를 참고하여 실제 애플리케이션에서 동기화를 적용해 보세요!
|
||||
198
docs/Concurrency/03_Timer.md
Normal file
198
docs/Concurrency/03_Timer.md
Normal file
@@ -0,0 +1,198 @@
|
||||
### 자바의 `Timer`와 `TimerTask`에 대한 설명
|
||||
|
||||
자바에서 `Timer`와 `TimerTask`는 특정 작업을 일정 시간 간격으로 또는 지정된 시간에 실행하기 위한 유틸리티 클래스입니다. `java.util` 패키지에 포함되어 있으며, 스레드를 기반으로 동작하여 비동기적으로 작업을 스케줄링할 수 있습니다. 아래에서는 이와 관련된 클래스 및 메서드를 표로 정리하고, 예시와 함께 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### `Timer`와 `TimerTask` 관련 클래스 및 메서드 표
|
||||
|
||||
#### `Timer` 클래스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-----------|---------------------------------------------------------------------------------------|
|
||||
| `Timer()` | - | 새로운 `Timer` 객체를 생성 |
|
||||
| `schedule(TimerTask, long delay)`| `void` | 지정된 지연 시간(밀리초) 후에 `TimerTask`를 한 번 실행 |
|
||||
| `schedule(TimerTask, Date time)` | `void` | 지정된 시간(`Date`)에 `TimerTask`를 한 번 실행 |
|
||||
| `schedule(TimerTask, long delay, long period)` | `void` | 지정된 지연 시간 후 주기적으로(밀리초 단위) `TimerTask`를 반복 실행 |
|
||||
| `scheduleAtFixedRate(TimerTask, long delay, long period)` | `void` | 지정된 지연 시간 후 고정된 주기로 `TimerTask`를 반복 실행 (지연 보정 없음) |
|
||||
| `cancel()` | `void` | `Timer`를 종료하고 모든 스케줄링된 작업을 취소 |
|
||||
| `purge()` | `int` | 취소된 작업을 큐에서 제거하고 제거된 작업 수를 반환 |
|
||||
|
||||
#### `TimerTask` 클래스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-----------|---------------------------------------------------------------------------------------|
|
||||
| `run()` | `void` | `Timer`에 의해 실행될 작업을 정의 (추상 메서드, 오버라이드 필요) |
|
||||
| `cancel()` | `boolean` | 해당 `TimerTask`의 스케줄링을 취소하고 성공 여부를 반환 |
|
||||
| `scheduledExecutionTime()` | `long` | 해당 작업이 마지막으로 스케줄링된 시간을 반환 (밀리초 단위) |
|
||||
|
||||
---
|
||||
|
||||
### `Timer`와 `TimerTask` 설명 및 예시
|
||||
|
||||
`Timer`는 백그라운드 스레드를 생성하여 작업을 스케줄링하고, `TimerTask`는 실행할 작업을 정의하는 추상 클래스입니다. `TimerTask`를 상속받아 `run()` 메서드를 구현한 후, `Timer`의 `schedule` 메서드로 작업을 등록합니다. 주요 사용 사례는 주기적인 알림, 로그 기록, 상태 점검 등입니다.
|
||||
|
||||
#### 1. 기본 사용: 일정 시간 후 한 번 실행
|
||||
`schedule(TimerTask, long delay)`를 사용해 지정된 지연 시간 후 작업을 한 번 실행합니다.
|
||||
|
||||
```java
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class SimpleTimerExample {
|
||||
public static void main(String[] args) {
|
||||
Timer timer = new Timer();
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("작업 실행: " + System.currentTimeMillis());
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(task, 2000); // 2초 후 실행
|
||||
System.out.println("스케줄링 완료: " + System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
스케줄링 완료: 1678321234567
|
||||
(2초 후)
|
||||
작업 실행: 1678321236567
|
||||
```
|
||||
|
||||
#### 2. 주기적 실행: 고정 주기 반복
|
||||
`schedule(TimerTask, long delay, long period)`를 사용해 주기적으로 작업을 반복합니다.
|
||||
|
||||
```java
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class PeriodicTimerExample {
|
||||
public static void main(String[] args) {
|
||||
Timer timer = new Timer();
|
||||
TimerTask task = new TimerTask() {
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
count++;
|
||||
System.out.println("작업 실행 " + count + "회: " + System.currentTimeMillis());
|
||||
if (count == 3) {
|
||||
timer.cancel(); // 3회 실행 후 종료
|
||||
System.out.println("타이머 종료");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(task, 1000, 2000); // 1초 후 시작, 2초마다 반복
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
작업 실행 1회: 1678321235567
|
||||
(2초 후)
|
||||
작업 실행 2회: 1678321237567
|
||||
(2초 후)
|
||||
작업 실행 3회: 1678321239567
|
||||
타이머 종료
|
||||
```
|
||||
|
||||
#### 3. 고정 속도 실행: `scheduleAtFixedRate`
|
||||
`scheduleAtFixedRate`는 작업 간 간격을 고정하며, 지연이 발생하더라도 이를 보정하지 않고 계속 실행합니다.
|
||||
|
||||
```java
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class FixedRateTimerExample {
|
||||
public static void main(String[] args) {
|
||||
Timer timer = new Timer();
|
||||
TimerTask task = new TimerTask() {
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
count++;
|
||||
System.out.println("작업 실행 " + count + "회: " + System.currentTimeMillis());
|
||||
try {
|
||||
Thread.sleep(1500); // 작업 시간이 주기보다 길어짐
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (count == 3) {
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
timer.scheduleAtFixedRate(task, 1000, 1000); // 1초 후 시작, 1초마다 실행 시도
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
작업 실행 1회: 1678321235567
|
||||
(1.5초 후, 지연 발생)
|
||||
작업 실행 2회: 1678321237067
|
||||
(1.5초 후)
|
||||
작업 실행 3회: 1678321238567
|
||||
```
|
||||
- **설명**: `scheduleAtFixedRate`는 주기를 엄격히 유지하려 하지만, 작업 시간이 주기(1초)보다 길어지면 다음 실행이 바로 이어집니다.
|
||||
|
||||
#### 4. 작업 및 타이머 취소
|
||||
`TimerTask.cancel()`로 개별 작업을, `Timer.cancel()`로 모든 작업을 취소할 수 있습니다.
|
||||
|
||||
```java
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class CancelTimerExample {
|
||||
public static void main(String[] args) {
|
||||
Timer timer = new Timer();
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("작업 실행");
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(task, 1000, 1000); // 1초 후 시작, 1초마다 반복
|
||||
try {
|
||||
Thread.sleep(2500); // 2.5초 대기
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
task.cancel(); // 개별 작업 취소
|
||||
System.out.println("작업 취소됨");
|
||||
timer.cancel(); // 타이머 종료
|
||||
System.out.println("타이머 종료");
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
작업 실행
|
||||
(1초 후)
|
||||
작업 실행
|
||||
작업 취소됨
|
||||
타이머 종료
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Timer`와 `TimerTask`의 장점과 한계
|
||||
- **장점**:
|
||||
- 간단한 스케줄링 작업에 적합.
|
||||
- 구현이 쉬움.
|
||||
- **한계**:
|
||||
- 단일 스레드로 동작하므로 하나의 작업이 지연되면 다른 작업에도 영향을 미침.
|
||||
- 예외 발생 시 `Timer`가 종료될 수 있음.
|
||||
- 복잡한 스케줄링에는 `ScheduledExecutorService`가 더 적합.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`Timer`와 `TimerTask`는 주기적이거나 지연된 작업을 간단히 처리할 때 유용합니다. `schedule`과 `scheduleAtFixedRate`를 통해 다양한 스케줄링 요구를 충족할 수 있으며, `cancel`로 작업을 제어할 수 있습니다. 위 예시를 참고하여 실제 애플리케이션에서 활용해 보세요! 더 복잡한 경우에는 `java.util.concurrent.ScheduledExecutorService`를 고려하는 것도 좋은 대안입니다.
|
||||
226
docs/Concurrency/04_CompletableFuture.md
Normal file
226
docs/Concurrency/04_CompletableFuture.md
Normal file
@@ -0,0 +1,226 @@
|
||||
### `CompletableFuture`에 대한 설명
|
||||
|
||||
`CompletableFuture`는 자바의 `java.util.concurrent` 패키지에 포함된 클래스입니다. 비동기 작업을 처리하고 결과를 조합하며, 예외 처리를 유연하게 다룰 수 있는 강력한 도구입니다. `Future` 인터페이스를 확장하여 논블록킹(non-blocking) 방식으로 작업을 연결하고, 작업 완료 시 후속 작업을 정의할 수 있습니다. 아래에서 주요 메서드를 표로 정리하고, 예시를 통해 사용 방법을 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### `CompletableFuture` 주요 메서드 표
|
||||
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|---------------------------------------|----------------------------|----------------------------------------------------------------------------------------|
|
||||
| `runAsync(Runnable)` | `CompletableFuture<Void>` | 지정된 `Runnable`을 비동기적으로 실행 |
|
||||
| `supplyAsync(Supplier<T>)` | `CompletableFuture<T>` | 값을 제공하는 `Supplier`를 비동기적으로 실행하고 결과 반환 |
|
||||
| `thenApply(Function<T, U>)` | `CompletableFuture<U>` | 이전 작업의 결과에 함수를 적용하여 변환된 결과 반환 |
|
||||
| `thenAccept(Consumer<T>)` | `CompletableFuture<Void>` | 이전 작업의 결과를 소비(사용)하는 동작 수행 |
|
||||
| `thenRun(Runnable)` | `CompletableFuture<Void>` | 이전 작업 완료 후 결과와 관계없이 실행할 작업 정의 |
|
||||
| `thenCompose(Function<T, CompletableFuture<U>>)` | `CompletableFuture<U>` | 이전 결과에 따라 새로운 `CompletableFuture`를 반환 |
|
||||
| `exceptionally(Function<Throwable, T>)` | `CompletableFuture<T>` | 예외 발생 시 복구 로직을 정의 |
|
||||
| `handle(BiFunction<T, Throwable, U>)` | `CompletableFuture<U>` | 결과와 예외를 모두 처리하는 로직 정의 |
|
||||
| `complete(T)` | `boolean` | 작업을 수동으로 완료하고 결과 설정 |
|
||||
| `completeExceptionally(Throwable)` | `boolean` | 작업을 예외와 함께 수동으로 완료 |
|
||||
| `join()` | `T` | 작업 완료를 기다리고 결과 반환 (예외 발생 시 던짐) |
|
||||
| `get()` | `T` | `Future`처럼 결과를 기다리고 반환 (예외 처리 필요) |
|
||||
| `allOf(CompletableFuture<?>...)` | `CompletableFuture<Void>` | 여러 `CompletableFuture`가 모두 완료될 때까지 대기 |
|
||||
| `anyOf(CompletableFuture<?>...)` | `CompletableFuture<Object>`| 여러 `CompletableFuture` 중 하나가 완료되면 결과 반환 |
|
||||
| `whenComplete(BiConsumer<T, Throwable>)` | `CompletableFuture<T>` | 작업 완료 시 결과와 예외를 처리 |
|
||||
|
||||
---
|
||||
|
||||
### `CompletableFuture` 설명 및 예시
|
||||
|
||||
`CompletableFuture`는 비동기 프로그래밍을 단순화하며, 작업 간 의존성을 표현하고 예외를 처리하는 데 유용합니다. 기본적으로 `ForkJoinPool.commonPool()`을 사용하지만, 사용자 지정 `Executor`를 전달할 수도 있습니다.
|
||||
|
||||
#### 1. 기본 비동기 작업: `supplyAsync`
|
||||
비동기적으로 값을 생성합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class SupplyAsyncExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "비동기 작업 결과";
|
||||
});
|
||||
|
||||
System.out.println("작업 시작");
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 시작
|
||||
(1초 후)
|
||||
비동기 작업 결과
|
||||
```
|
||||
|
||||
#### 2. 결과 변환: `thenApply`
|
||||
이전 작업의 결과를 변환합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ThenApplyExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
|
||||
.thenApply(s -> s + " World")
|
||||
.thenApply(String::toUpperCase);
|
||||
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
HELLO WORLD
|
||||
```
|
||||
- **설명**: `thenApply`로 결과에 연속적인 변환을 적용.
|
||||
|
||||
#### 3. 결과 소비: `thenAccept`
|
||||
결과를 소비하고 후속 작업을 정의합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ThenAcceptExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture.supplyAsync(() -> "데이터 처리")
|
||||
.thenAccept(result -> System.out.println("결과: " + result))
|
||||
.thenRun(() -> System.out.println("작업 완료"));
|
||||
|
||||
Thread.sleep(1000); // 비동기 작업 완료 대기
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
결과: 데이터 처리
|
||||
작업 완료
|
||||
```
|
||||
|
||||
#### 4. 예외 처리: `exceptionally`
|
||||
예외 발생 시 복구 로직을 정의합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ExceptionallyExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
|
||||
if (true) throw new RuntimeException("에러 발생");
|
||||
return "성공";
|
||||
}).exceptionally(throwable -> "복구: " + throwable.getMessage());
|
||||
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
복구: java.lang.RuntimeException: 에러 발생
|
||||
```
|
||||
|
||||
#### 5. 작업 조합: `thenCompose`
|
||||
이전 결과에 따라 새로운 `CompletableFuture`를 연결합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ThenComposeExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "첫 번째")
|
||||
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " 두 번째"));
|
||||
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
첫 번째 두 번째
|
||||
```
|
||||
|
||||
#### 6. 여러 작업 조합: `allOf`
|
||||
여러 작업의 완료를 기다립니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AllOfExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
|
||||
try { Thread.sleep(1000); } catch (Exception e) {}
|
||||
return "작업 1";
|
||||
});
|
||||
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "작업 2");
|
||||
|
||||
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
|
||||
all.thenRun(() -> {
|
||||
try {
|
||||
System.out.println(future1.get() + " & " + future2.get());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 1 & 작업 2
|
||||
```
|
||||
|
||||
#### 7. 수동 완료: `complete`
|
||||
작업을 외부에서 수동으로 완료합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CompleteExample {
|
||||
public static void main(String[] args) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
future.complete("수동 완료");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
|
||||
String result = future.join();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
수동 완료
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `CompletableFuture`의 장점과 주의점
|
||||
- **장점**:
|
||||
- 비동기 작업의 연속적 처리와 조합 가능.
|
||||
- 예외 처리가 명시적이고 유연.
|
||||
- 논블록킹 방식으로 작업 연결 가능.
|
||||
- **주의점**:
|
||||
- `get()`이나 `join()`을 과도히 사용하면 블록킹으로 인해 비동기 이점이 줄어듦.
|
||||
- 복잡한 조합 시 코드 가독성 관리 필요.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`CompletableFuture`는 비동기 작업을 정의하고, 결과를 변환하며, 예외를 처리하는 데 탁월합니다. `supplyAsync`, `thenApply`, `exceptionally`, `allOf` 등 다양한 메서드를 활용하면 작업 흐름을 효율적으로 설계할 수 있습니다. 위 예시를 참고하여 실제 애플리케이션에서 비동기 프로그래밍을 구현해 보세요!
|
||||
401
docs/Concurrency/05_Executor.md
Normal file
401
docs/Concurrency/05_Executor.md
Normal file
@@ -0,0 +1,401 @@
|
||||
### 자바의 `Executor`와 관련된 설명
|
||||
|
||||
자바의 `Executor` 프레임워크는 스레드 풀을 관리하고 작업을 비동기적으로 실행하기 위한 강력한 도구로, `java.util.concurrent` 패키지에 포함되어 있습니다. `Executor` 인터페이스를 기반으로, 스레드 생성과 관리를 직접 다루는 대신 작업 단위로 실행을 위임하여 코드의 가독성과 효율성을 높입니다. 주요 클래스와 메서드를 표로 정리하고, 예시를 통해 사용 방법을 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### `Executor`와 관련된 클래스 및 메서드 표
|
||||
|
||||
#### `Executor` 인터페이스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-------------------------|-----------|---------------------------------------------------|
|
||||
| `execute(Runnable)` | `void` | `Runnable` 작업을 실행하도록 스레드 풀에 위임 |
|
||||
|
||||
#### `ExecutorService` 인터페이스 (확장 인터페이스)
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-------------------|----------------------------------------------------------------------|
|
||||
| `submit(Runnable)` | `Future<?>` | `Runnable` 작업을 실행하고 결과를 추적할 수 있는 `Future` 반환 |
|
||||
| `submit(Callable<T>)` | `Future<T>` | `Callable` 작업을 실행하고 결과를 반환하는 `Future` 반환 |
|
||||
| `shutdown()` | `void` | 새로운 작업을 받지 않고, 기존 작업 완료 후 종료 |
|
||||
| `shutdownNow()` | `List<Runnable>` | 즉시 종료 시도하고 실행 대기 중인 작업 목록 반환 |
|
||||
| `awaitTermination(long, TimeUnit)` | `boolean` | 지정된 시간 동안 종료를 기다리며, 완료 여부 반환 |
|
||||
| `invokeAll(Collection<Callable<T>>)` | `List<Future<T>>` | 모든 `Callable` 작업을 실행하고 결과 목록 반환 |
|
||||
| `invokeAny(Collection<Callable<T>>)` | `T` | 주어진 `Callable` 중 하나가 완료되면 그 결과를 반환 |
|
||||
|
||||
#### `Executors` 유틸리티 클래스 (정적 팩토리 메서드)
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-------------------|----------------------------------------------------------------------|
|
||||
| `newFixedThreadPool(int n)` | `ExecutorService` | 고정 크기의 스레드 풀 생성 |
|
||||
| `newSingleThreadExecutor()` | `ExecutorService` | 단일 스레드로 작업을 순차적으로 실행하는 풀 생성 |
|
||||
| `newCachedThreadPool()` | `ExecutorService` | 필요에 따라 스레드를 생성/재사용하는 동적 풀 생성 |
|
||||
| `newScheduledThreadPool(int n)` | `ScheduledExecutorService` | 주기적 작업을 스케줄링할 수 있는 스레드 풀 생성 |
|
||||
|
||||
#### `ScheduledExecutorService` 인터페이스 (확장 인터페이스)
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-------------------|----------------------------------------------------------------------|
|
||||
| `schedule(Runnable, long, TimeUnit)` | `ScheduledFuture<?>` | 지정된 지연 시간 후 작업 실행 |
|
||||
| `scheduleAtFixedRate(Runnable, long, long, TimeUnit)` | `ScheduledFuture<?>` | 고정 주기로 작업 반복 실행 |
|
||||
| `scheduleWithFixedDelay(Runnable, long, long, TimeUnit)` | `ScheduledFuture<?>` | 작업 완료 후 고정 지연을 두고 반복 실행 |
|
||||
|
||||
#### 관련 클래스
|
||||
| 클래스명 | 설명 |
|
||||
|-------------------------|----------------------------------------------------------------------|
|
||||
| `ThreadPoolExecutor` | 스레드 풀의 세부 설정(코어 풀 크기, 최대 풀 크기 등)을 커스터마이징 가능 |
|
||||
| `Future<T>` | 비동기 작업의 결과를 추적하거나 취소할 수 있는 인터페이스 |
|
||||
| `Callable<T>` | `Runnable`과 유사하나 결과를 반환하며 예외를 던질 수 있는 인터페이스 |
|
||||
|
||||
---
|
||||
|
||||
### `Executor` 설명 및 예시
|
||||
|
||||
`Executor` 프레임워크는 스레드 관리의 복잡성을 줄이고, 작업 실행을 추상화하여 재사용 가능한 스레드 풀을 제공합니다. 아래에서 주요 사용 사례를 예시로 설명합니다.
|
||||
|
||||
#### 1. 기본 사용: `ExecutorService`와 `newFixedThreadPool`
|
||||
고정 크기의 스레드 풀을 생성하고 작업을 실행합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class FixedThreadPoolExample {
|
||||
public static void main(String[] args) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(2); // 2개 스레드 풀
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int taskId = i;
|
||||
executor.execute(() -> {
|
||||
System.out.println("작업 " + taskId + " 실행 중: " + Thread.currentThread().getName());
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown(); // 작업 제출 후 종료 요청
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 0 실행 중: pool-1-thread-1
|
||||
작업 1 실행 중: pool-1-thread-2
|
||||
(1초 후)
|
||||
작업 2 실행 중: pool-1-thread-1
|
||||
작업 3 실행 중: pool-1-thread-2
|
||||
(1초 후)
|
||||
작업 4 실행 중: pool-1-thread-1
|
||||
```
|
||||
- **설명**: 2개의 스레드가 최대 2개 작업을 동시에 처리하며, 나머지는 대기 후 실행됨.
|
||||
|
||||
#### 2. `Callable`과 `Future`로 결과 받기
|
||||
`submit`을 사용해 결과를 반환하는 작업을 실행합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CallableExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
Callable<Integer> task = () -> {
|
||||
Thread.sleep(1000);
|
||||
return 42;
|
||||
};
|
||||
|
||||
Future<Integer> future = executor.submit(task);
|
||||
System.out.println("작업 제출 완료");
|
||||
|
||||
Integer result = future.get(); // 작업 완료까지 대기 후 결과 가져오기
|
||||
System.out.println("결과: " + result);
|
||||
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 제출 완료
|
||||
(1초 후)
|
||||
결과: 42
|
||||
```
|
||||
- **설명**: `Future.get()`은 작업이 완료될 때까지 블록하며, 결과를 반환받음.
|
||||
|
||||
#### 3. 주기적 실행: `ScheduledExecutorService`
|
||||
`scheduleAtFixedRate`로 주기적으로 작업을 실행합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ScheduledExecutorExample {
|
||||
public static void main(String[] args) {
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||
|
||||
Runnable task = () -> System.out.println("작업 실행: " + System.currentTimeMillis());
|
||||
|
||||
executor.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS); // 1초 후 시작, 2초 주기
|
||||
|
||||
try {
|
||||
Thread.sleep(5000); // 5초 동안 실행
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
작업 실행: 1678321234567
|
||||
(2초 후)
|
||||
작업 실행: 1678321236567
|
||||
(2초 후)
|
||||
작업 실행: 1678321238567
|
||||
```
|
||||
- **설명**: 작업이 고정된 2초 주기로 실행됨.
|
||||
|
||||
#### 4. `invokeAll`로 여러 작업 실행
|
||||
여러 `Callable` 작업을 한 번에 실행하고 결과를 수집합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class InvokeAllExample {
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||
|
||||
List<Callable<String>> tasks = Arrays.asList(
|
||||
() -> { Thread.sleep(1000); return "작업 1 완료"; },
|
||||
() -> { Thread.sleep(500); return "작업 2 완료"; },
|
||||
() -> { Thread.sleep(1500); return "작업 3 완료"; }
|
||||
);
|
||||
|
||||
List<Future<String>> futures = executor.invokeAll(tasks);
|
||||
|
||||
for (Future<String> future : futures) {
|
||||
System.out.println(future.get());
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 1 완료
|
||||
작업 2 완료
|
||||
작업 3 완료
|
||||
```
|
||||
- **설명**: 모든 작업이 완료될 때까지 대기한 후 결과를 순차적으로 출력.
|
||||
|
||||
---
|
||||
|
||||
### `Executor`의 장점과 주의점
|
||||
- **장점**:
|
||||
- 스레드 풀을 재사용하여 리소스 낭비 감소.
|
||||
- 작업 실행과 스레드 관리를 분리하여 코드 간소화.
|
||||
- 주기적 작업 및 결과 처리가 용이.
|
||||
- **주의점**:
|
||||
- `shutdown()`을 호출하지 않으면 프로그램이 종료되지 않을 수 있음.
|
||||
- 스레드 풀 크기와 작업 부하를 적절히 조정해야 성능 최적화 가능.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`Executor` 프레임워크는 스레드 관리의 복잡성을 줄이고, 다양한 실행 패턴(단일 실행, 주기적 실행, 병렬 실행 등)을 지원합니다. `ExecutorService`, `ScheduledExecutorService`, `Executors`를 활용하면 효율적인 멀티스레드 애플리케이션을 쉽게 구현할 수 있습니다. 위 예시를 참고하여 실제 프로젝트에 적용해 보세요!
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
### `ScheduledExecutorService`와 다른 스케줄링 도구 비교
|
||||
|
||||
자바에서 주기적이거나 지연된 작업을 스케줄링할 때 `ScheduledExecutorService`는 강력한 도구로 사용됩니다. 하지만 `java.util.Timer`와 `CompletableFuture`와 같은 대안도 있으며, 각각의 특징과 사용 사례가 다릅니다. 아래에서 `ScheduledExecutorService`를 `Timer`와 `CompletableFuture`와 비교하여 표로 정리하고, 예시와 함께 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### 비교 표
|
||||
|
||||
| **특징** | **`ScheduledExecutorService`** | **`Timer`** | **`CompletableFuture`** |
|
||||
|---------------------------|-----------------------------------------------------|------------------------------------------|------------------------------------------|
|
||||
| **패키지** | `java.util.concurrent` | `java.util` | `java.util.concurrent` |
|
||||
| **스레드 모델** | 스레드 풀 기반 (다중 스레드 지원) | 단일 스레드 기반 | 기본 풀(`ForkJoinPool`) 또는 사용자 지정 |
|
||||
| **작업 타입** | `Runnable`, `Callable` | `TimerTask` | `Runnable`, `Supplier` |
|
||||
| **스케줄링 메서드** | `schedule`, `scheduleAtFixedRate`, `scheduleWithFixedDelay` | `schedule`, `scheduleAtFixedRate` | `supplyAsync` + `then` 조합으로 간접 지원 |
|
||||
| **고정 주기 지원** | `scheduleAtFixedRate` (고정 주기), `scheduleWithFixedDelay` (고정 지연) | `scheduleAtFixedRate` (고정 주기) | 직접 지원 없음, 별도 로직 필요 |
|
||||
| **예외 처리** | 개별 작업 예외가 전체에 영향 없음 | 예외 발생 시 `Timer` 종료 가능 | `exceptionally` 등으로 명시적 처리 가능 |
|
||||
| **취소 기능** | `ScheduledFuture.cancel()` | `TimerTask.cancel()`, `Timer.cancel()` | `complete()`, `cancel()` |
|
||||
| **복잡한 작업 조합** | 제한적 | 없음 | `thenApply`, `allOf` 등으로 가능 |
|
||||
| **성능 및 확장성** | 높은 부하와 복잡한 작업에 적합 | 간단한 작업에 적합 | 비동기 작업 조합에 강력 |
|
||||
| **사용 용도** | 주기적 작업, 다중 스레드 스케줄링 | 간단한 단일 스레드 스케줄링 | 비동기 작업 흐름 관리 |
|
||||
|
||||
---
|
||||
|
||||
### 상세 설명 및 예시
|
||||
|
||||
#### 1. `ScheduledExecutorService`
|
||||
- **특징**: 스레드 풀을 활용하여 다중 스레드로 작업을 처리하며, 예외 발생 시 다른 작업에 영향을 주지 않습니다.
|
||||
- **예시**: 주기적으로 상태를 점검하는 작업.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ScheduledExecutorExample {
|
||||
public static void main(String[] args) {
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
|
||||
|
||||
Runnable task = () -> System.out.println("실행: " + System.currentTimeMillis());
|
||||
executor.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
실행: 1678321234567
|
||||
실행: 1678321236567
|
||||
실행: 1678321238567
|
||||
```
|
||||
|
||||
#### 2. `Timer`
|
||||
- **특징**: 단일 스레드로 동작하며, 작업 중 예외가 발생하면 `Timer`가 종료될 수 있습니다.
|
||||
- **예시**: 간단한 알림 기능.
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
|
||||
public class TimerExample {
|
||||
public static void main(String[] args) {
|
||||
Timer timer = new Timer();
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("실행: " + System.currentTimeMillis());
|
||||
}
|
||||
};
|
||||
|
||||
timer.scheduleAtFixedRate(task, 1000, 2000);
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
실행: 1678321234567
|
||||
실행: 1678321236567
|
||||
실행: 1678321238567
|
||||
```
|
||||
|
||||
#### 3. `CompletableFuture`
|
||||
- **특징**: 주기적 스케줄링은 직접 지원하지 않지만, 비동기 작업 조합과 예외 처리가 뛰어납니다.
|
||||
- **예시**: 작업 완료 후 후속 작업 실행.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CompletableFutureExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("실행: " + System.currentTimeMillis());
|
||||
}).thenRun(() -> System.out.println("후속 작업"));
|
||||
|
||||
future.get();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
실행: 1678321235567
|
||||
후속 작업
|
||||
```
|
||||
|
||||
#### 비교 예시: 예외 발생 시 동작
|
||||
- **`ScheduledExecutorService`**와 **`Timer`**의 차이점을 보여줍니다.
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ExceptionComparison {
|
||||
public static void main(String[] args) {
|
||||
// Timer 예시
|
||||
Timer timer = new Timer();
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
int count = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
count++;
|
||||
System.out.println("Timer 실행 " + count);
|
||||
if (count == 2) throw new RuntimeException("Timer 예외");
|
||||
}
|
||||
}, 0, 1000);
|
||||
|
||||
// ScheduledExecutorService 예시
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
int count = 0;
|
||||
count++;
|
||||
System.out.println("Executor 실행 " + count);
|
||||
if (count == 2) throw new RuntimeException("Executor 예외");
|
||||
}, 0, 1, TimeUnit.SECONDS);
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
timer.cancel();
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예상**:
|
||||
```
|
||||
Timer 실행 1
|
||||
Timer 실행 2
|
||||
(예외 발생 후 Timer 종료, 더 이상 실행 안 됨)
|
||||
|
||||
Executor 실행 1
|
||||
Executor 실행 2
|
||||
(예외 발생 후에도 다른 작업은 계속 실행 가능)
|
||||
```
|
||||
- **설명**: `Timer`는 예외로 종료되지만, `ScheduledExecutorService`는 영향을 받지 않음.
|
||||
|
||||
---
|
||||
|
||||
### 선택 가이드
|
||||
- **`ScheduledExecutorService`**:
|
||||
- 다중 스레드와 안정성이 필요한 주기적 작업.
|
||||
- 복잡한 작업 관리와 확장성 요구 시.
|
||||
- **`Timer`**:
|
||||
- 간단한 단일 스레드 작업.
|
||||
- 경량 애플리케이션에서 최소한의 스케줄링 필요 시.
|
||||
- **`CompletableFuture`**:
|
||||
- 비동기 작업 흐름을 조합하거나 단일 작업의 후속 처리가 필요할 때.
|
||||
- 주기적 스케줄링보다는 결과 기반 작업에 적합.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`ScheduledExecutorService`는 스레드 풀 기반으로 안정성과 유연성을 제공하며, `Timer`는 간단한 작업에 적합하지만 취약점이 있습니다. `CompletableFuture`는 주기적 스케줄링보다는 비동기 작업 조합에 강점을 가집니다. 요구사항에 따라 적절한 도구를 선택해 사용하세요!
|
||||
210
docs/Concurrency/06_Runnable,Callable,Future.md
Normal file
210
docs/Concurrency/06_Runnable,Callable,Future.md
Normal file
@@ -0,0 +1,210 @@
|
||||
### 자바의 `Runnable`, `Callable`, `Future`, `CompletableFuture`에 대한 설명
|
||||
|
||||
자바에서 멀티스레드 프로그래밍을 지원하는 주요 인터페이스와 클래스는 `Runnable`, `Callable`, `Future`, 그리고 `CompletableFuture`입니다. 이들은 작업을 정의하고, 실행하며, 결과를 처리하는 데 사용됩니다. 아래에서 각 클래스와 관련 메서드를 표로 정리하고, 예시를 통해 사용 방법을 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
### 관련 클래스 및 메서드 표
|
||||
|
||||
#### `Runnable` 인터페이스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------|-----------|---------------------------------------------------|
|
||||
| `run()` | `void` | 스레드가 실행할 작업을 정의 (추상 메서드) |
|
||||
|
||||
#### `Callable<V>` 인터페이스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------|-----------|---------------------------------------------------|
|
||||
| `call()` | `V` | 결과를 반환하며 예외를 던질 수 있는 작업 정의 |
|
||||
|
||||
#### `Future<V>` 인터페이스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-------------------------|-----------|---------------------------------------------------|
|
||||
| `get()` | `V` | 작업 결과를 반환 (완료될 때까지 대기) |
|
||||
| `get(long, TimeUnit)` | `V` | 지정된 시간 동안 대기 후 결과 반환 |
|
||||
| `cancel(boolean)` | `boolean` | 작업을 취소 시도하고 성공 여부 반환 |
|
||||
| `isDone()` | `boolean` | 작업이 완료되었는지 확인 |
|
||||
| `isCancelled()` | `boolean` | 작업이 취소되었는지 확인 |
|
||||
|
||||
#### `CompletableFuture<V>` 클래스
|
||||
| 메서드명 | 반환 타입 | 설명 |
|
||||
|-----------------------------------|-------------------------|----------------------------------------------------------------------|
|
||||
| `runAsync(Runnable)` | `CompletableFuture<Void>` | 비동기적으로 `Runnable` 실행 |
|
||||
| `supplyAsync(Supplier<V>)` | `CompletableFuture<V>` | 비동기적으로 값을 제공하는 작업 실행 |
|
||||
| `thenApply(Function<T, U>)` | `CompletableFuture<U>` | 이전 작업의 결과에 함수를 적용하여 변환된 결과 반환 |
|
||||
| `thenAccept(Consumer<T>)` | `CompletableFuture<Void>` | 결과에 대한 소비 동작 수행 |
|
||||
| `thenRun(Runnable)` | `CompletableFuture<Void>` | 결과와 상관없이 후속 작업 실행 |
|
||||
| `exceptionally(Function<Throwable, T>)` | `CompletableFuture<T>` | 예외 발생 시 처리 로직 정의 |
|
||||
| `complete(V)` | `boolean` | 작업을 수동으로 완료하고 결과 설정 |
|
||||
| `join()` | `V` | 작업 완료를 기다리고 결과 반환 (스레드 풀에서 사용 시 주의) |
|
||||
| `allOf(CompletableFuture<?>...)` | `CompletableFuture<Void>` | 여러 `CompletableFuture`가 모두 완료될 때까지 대기 |
|
||||
| `anyOf(CompletableFuture<?>...)` | `CompletableFuture<Object>` | 여러 `CompletableFuture` 중 하나가 완료되면 결과 반환 |
|
||||
|
||||
---
|
||||
|
||||
### 설명 및 예시
|
||||
|
||||
이들 클래스는 멀티스레드 작업의 정의와 실행, 결과 처리를 다룹니다. `Runnable`은 기본적인 작업 단위이고, `Callable`은 결과를 반환하며, `Future`는 결과를 추적하고, `CompletableFuture`는 비동기 작업의 조합과 예외 처리를 지원합니다.
|
||||
|
||||
#### 1. `Runnable`: 기본 작업 정의
|
||||
`Runnable`은 결과를 반환하지 않는 간단한 작업을 정의합니다.
|
||||
|
||||
```java
|
||||
public class RunnableExample {
|
||||
public static void main(String[] args) {
|
||||
Runnable task = () -> {
|
||||
System.out.println("Runnable 작업 실행: " + Thread.currentThread().getName());
|
||||
};
|
||||
|
||||
Thread thread = new Thread(task);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
Runnable 작업 실행: Thread-0
|
||||
```
|
||||
|
||||
#### 2. `Callable`과 `Future`: 결과 반환
|
||||
`Callable`은 값을 반환하며, `Future`로 결과를 받습니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CallableFutureExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
Callable<String> task = () -> {
|
||||
Thread.sleep(1000);
|
||||
return "Callable 작업 완료";
|
||||
};
|
||||
|
||||
Future<String> future = executor.submit(task);
|
||||
System.out.println("작업 제출 완료");
|
||||
|
||||
String result = future.get(); // 완료 대기
|
||||
System.out.println(result);
|
||||
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 제출 완료
|
||||
(1초 후)
|
||||
Callable 작업 완료
|
||||
```
|
||||
- **설명**: `Future.get()`은 작업이 끝날 때까지 대기하며 결과를 반환.
|
||||
|
||||
#### 3. `CompletableFuture`: 비동기 작업 조합
|
||||
`CompletableFuture`는 비동기 작업을 연결하고 결과를 처리하는 데 유용합니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CompletableFutureExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "비동기 결과";
|
||||
}).thenApply(result -> result + " 처리됨")
|
||||
.thenApply(result -> result.toUpperCase());
|
||||
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
비동기 결과 처리됨
|
||||
```
|
||||
- **설명**: `supplyAsync`로 값을 생성하고, `thenApply`로 결과를 변환.
|
||||
|
||||
#### 4. `CompletableFuture` 예외 처리 및 조합
|
||||
예외 처리와 여러 작업의 동시 실행을 다룹니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CompletableFutureAdvancedExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "작업 1";
|
||||
});
|
||||
|
||||
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
|
||||
throw new RuntimeException("작업 2 실패");
|
||||
}).exceptionally(throwable -> "복구: " + throwable.getMessage());
|
||||
|
||||
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
|
||||
all.thenRun(() -> {
|
||||
try {
|
||||
System.out.println(future1.get() + " & " + future2.get());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).join(); // 모든 작업 완료 대기
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
작업 1 & 복구: java.lang.RuntimeException: 작업 2 실패
|
||||
```
|
||||
- **설명**: `exceptionally`로 예외를 처리하고, `allOf`로 여러 작업을 조합.
|
||||
|
||||
#### 5. `CompletableFuture`로 수동 완료
|
||||
`complete`를 사용해 작업을 수동으로 완료할 수 있습니다.
|
||||
|
||||
```java
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ManualCompleteExample {
|
||||
public static void main(String[] args) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
future.complete("수동 완료");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
|
||||
String result = future.join();
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
- **출력 예시**:
|
||||
```
|
||||
(1초 후)
|
||||
수동 완료
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 비교와 특징
|
||||
- **`Runnable`**: 결과 없음, 단순 작업 실행.
|
||||
- **`Callable`**: 결과 반환, `Future`와 함께 사용.
|
||||
- **`Future`**: 비동기 작업 상태 추적, 블록킹 방식.
|
||||
- **`CompletableFuture`**: 비동기 작업 조합, 예외 처리, 논블록킹 지원.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
`Runnable`과 `Callable`은 작업을 정의하고, `Future`는 결과를 추적하며, `CompletableFuture`는 복잡한 비동기 작업을 유연하게 처리합니다. 상황에 따라 적합한 클래스를 선택해 사용하면 효율적인 멀티스레드 프로그래밍이 가능합니다. 위 예시를 참고하여 실제 애플리케이션에 적용해 보세요!
|
||||
142
docs/Date-Time API.md
Normal file
142
docs/Date-Time API.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# **자바 Date-Time API 쉽게 배우기**
|
||||
|
||||
## **1. Date-Time API란?**
|
||||
자바에서 날짜와 시간을 다루는 API는 크게 두 가지가 있다.
|
||||
1. `java.util.Date`, `java.util.Calendar` (구버전, 사용 비추천)
|
||||
2. `java.time` 패키지 (Java 8 이후, 최신 API)
|
||||
|
||||
Java 8부터 `java.time` 패키지가 추가되면서 날짜와 시간을 **더 직관적이고 강력하게 다룰 수 있게 되었다.**
|
||||
이번 글에서는 `java.time` 패키지를 중심으로 설명하겠다.
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `LocalDate` (날짜만 다루는 클래스)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `now()` | 현재 날짜 가져오기 |
|
||||
| `of(year, month, dayOfMonth)` | 특정 날짜 생성 |
|
||||
| `plusDays(n)`, `minusDays(n)` | n일 더하기 / 빼기 |
|
||||
| `getYear()`, `getMonth()`, `getDayOfMonth()` | 연도, 월, 일 가져오기 |
|
||||
| `isBefore(date)`, `isAfter(date)` | 날짜 비교 |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
LocalDate today = LocalDate.now();
|
||||
System.out.println(today); // 2025-03-06
|
||||
|
||||
LocalDate specificDate = LocalDate.of(2025, 12, 25);
|
||||
System.out.println(specificDate); // 2025-12-25
|
||||
|
||||
LocalDate nextWeek = today.plusDays(7);
|
||||
System.out.println(nextWeek); // 2025-03-13
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(2) `LocalTime` (시간만 다루는 클래스)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `now()` | 현재 시간 가져오기 |
|
||||
| `of(hour, minute, second)` | 특정 시간 생성 |
|
||||
| `plusHours(n)`, `minusMinutes(n)` | 시간 또는 분 더하기 / 빼기 |
|
||||
| `getHour()`, `getMinute()`, `getSecond()` | 시, 분, 초 가져오기 |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
LocalTime now = LocalTime.now();
|
||||
System.out.println(now); // 14:30:15 (예시)
|
||||
|
||||
LocalTime meetingTime = LocalTime.of(10, 30);
|
||||
System.out.println(meetingTime); // 10:30
|
||||
|
||||
LocalTime afterOneHour = now.plusHours(1);
|
||||
System.out.println(afterOneHour); // 15:30:15 (예시)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(3) `LocalDateTime` (날짜 + 시간)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `now()` | 현재 날짜와 시간 가져오기 |
|
||||
| `of(LocalDate, LocalTime)` | 특정 날짜와 시간 생성 |
|
||||
| `plusDays(n)`, `plusHours(n)` | 날짜/시간 더하기 |
|
||||
| `getYear()`, `getMonth()`, `getDayOfMonth()` | 날짜 정보 가져오기 |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
System.out.println(now); // 2025-03-06T14:30:15 (예시)
|
||||
|
||||
LocalDateTime eventTime = LocalDateTime.of(2025, 12, 25, 18, 30);
|
||||
System.out.println(eventTime); // 2025-12-25T18:30
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(4) `ZonedDateTime` (시간대 정보 포함)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `now(ZoneId.of("Asia/Seoul"))` | 특정 시간대의 현재 시간 가져오기 |
|
||||
| `of(year, month, day, hour, min, sec, ZoneId.of("UTC"))` | 특정 시간대의 날짜/시간 생성 |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
ZonedDateTime seoulTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
|
||||
System.out.println(seoulTime); // 2025-03-06T14:30:15+09:00[Asia/Seoul]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(5) 날짜 포맷 변환 (`DateTimeFormatter`)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `ofPattern("yyyy-MM-dd HH:mm")` | 날짜/시간 형식 지정 |
|
||||
| `format(DateTimeFormatter formatter)` | 날짜/시간을 문자열로 변환 |
|
||||
| `parse(String, DateTimeFormatter formatter)` | 문자열을 날짜/시간으로 변환 |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
|
||||
String formattedDate = now.format(formatter);
|
||||
System.out.println(formattedDate); // 2025-03-06 14:30
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(6) 날짜 차이 계산 (`Duration`, `Period`)**
|
||||
| 클래스 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `Duration.between(time1, time2)` | 시간 차이 계산 (초, 분, 시간 단위) |
|
||||
| `Period.between(date1, date2)` | 날짜 차이 계산 (년, 월, 일 단위) |
|
||||
|
||||
**예제 코드**
|
||||
```java
|
||||
LocalDate date1 = LocalDate.of(2025, 1, 1);
|
||||
LocalDate date2 = LocalDate.of(2025, 3, 6);
|
||||
|
||||
Period period = Period.between(date1, date2);
|
||||
System.out.println(period.getMonths() + "개월 " + period.getDays() + "일"); // 2개월 5일
|
||||
|
||||
LocalTime time1 = LocalTime.of(10, 0);
|
||||
LocalTime time2 = LocalTime.of(12, 30);
|
||||
|
||||
Duration duration = Duration.between(time1, time2);
|
||||
System.out.println(duration.toHours() + "시간 " + duration.toMinutes() + "분"); // 2시간 30분
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **3. 정리**
|
||||
✅ **`LocalDate`** : 날짜만 다룸 (`2025-03-06`)
|
||||
✅ **`LocalTime`** : 시간만 다룸 (`14:30:15`)
|
||||
✅ **`LocalDateTime`** : 날짜 + 시간 (`2025-03-06T14:30:15`)
|
||||
✅ **`ZonedDateTime`** : 시간대 정보 포함 (`+09:00[Asia/Seoul]`)
|
||||
✅ **`DateTimeFormatter`** : 날짜 형식 변환 (`yyyy-MM-dd HH:mm`)
|
||||
✅ **`Period` / `Duration`** : 날짜/시간 차이 계산
|
||||
|
||||
자바의 `java.time` 패키지를 활용하면 **날짜와 시간을 더욱 쉽게 관리할 수 있다!**
|
||||
163
docs/EqualsAndHashCode.md
Normal file
163
docs/EqualsAndHashCode.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# **자바의 `equals()` 와 `hashCode()` 메서드 완벽 정리**
|
||||
|
||||
## **1. `equals()` 와 `hashCode()`란?**
|
||||
자바에서 객체를 비교할 때, `==` 연산자는 **메모리 주소를 비교**한다.
|
||||
하지만 **객체의 내용이 같은지를 비교**하려면 `equals()`를 재정의해야 한다.
|
||||
|
||||
또한, **HashMap, HashSet 같은 컬렉션에서 객체를 올바르게 저장하고 검색하려면 `hashCode()`도 재정의**해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## **2. `equals()` 와 `hashCode()`의 기본 동작**
|
||||
### ✅ **1. `equals()` 기본 구현 (`Object` 클래스)**
|
||||
모든 클래스는 `Object` 클래스를 상속받으며, `Object`의 기본 `equals()`는 **메모리 주소를 비교**한다.
|
||||
```java
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj);
|
||||
}
|
||||
```
|
||||
즉, 기본적으로 `equals()`는 `==`과 동일한 동작을 한다.
|
||||
|
||||
### ✅ **2. `hashCode()` 기본 구현 (`Object` 클래스)**
|
||||
`Object`의 `hashCode()`는 **객체의 메모리 주소를 기반으로 해시 값을 반환**한다.
|
||||
```java
|
||||
public native int hashCode();
|
||||
```
|
||||
(※ `native`는 자바가 아닌 C/C++로 구현되었다는 의미)
|
||||
|
||||
---
|
||||
|
||||
## **3. `equals()`와 `hashCode()`를 함께 재정의해야 하는 이유**
|
||||
**예제:** `HashSet`에서 `equals()`만 재정의했을 때 발생하는 문제
|
||||
```java
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // false (올바르게 동작하지 않음!)
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ 이유: `HashSet`은 **`hashCode()`를 먼저 비교한 후 `equals()`를 실행**하는데, `hashCode()`가 재정의되지 않아서 `contains()`가 실패한다.
|
||||
|
||||
✅ 해결 방법: `hashCode()`를 `equals()`와 함께 재정의한다.
|
||||
|
||||
---
|
||||
|
||||
## **4. `equals()`와 `hashCode()` 올바르게 재정의하는 방법**
|
||||
### ✅ **올바른 `equals()` 재정의**
|
||||
1. **반사성** (`x.equals(x) == true`)
|
||||
2. **대칭성** (`x.equals(y) == true` 면 `y.equals(x) == true`)
|
||||
3. **추이성** (`x.equals(y) == true` & `y.equals(z) == true` 면 `x.equals(z) == true`)
|
||||
4. **일관성** (`x.equals(y)`의 결과가 변하지 않음)
|
||||
5. **null 비교 시 `false` 반환** (`x.equals(null) == false`)
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ **올바른 `hashCode()` 재정의**
|
||||
✔ **같은 객체는 같은 `hashCode()`를 가져야 한다.**
|
||||
✔ **다른 객체라도 같은 `hashCode()`를 가질 수는 있지만 가능하면 충돌을 줄이는 것이 좋다.**
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
```
|
||||
`Objects.hash()`는 `null` 체크까지 포함된 안전한 방식이다.
|
||||
|
||||
---
|
||||
|
||||
## **5. `equals()`와 `hashCode()`를 올바르게 구현한 예제**
|
||||
```java
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // true (올바르게 동작!)
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ `equals()`와 `hashCode()`를 함께 재정의했기 때문에 `contains()`가 정상적으로 동작한다.
|
||||
|
||||
---
|
||||
|
||||
## **6. `record`를 사용하면 자동 구현됨! (Java 14+)**
|
||||
자바 14부터 도입된 `record`는 `equals()`와 `hashCode()`를 자동 생성해준다.
|
||||
|
||||
```java
|
||||
record Person(String name) {}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // true
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `record`는 **자동으로 `equals()`와 `hashCode()`를 올바르게 구현**해준다.
|
||||
|
||||
---
|
||||
|
||||
## **7. 결론**
|
||||
- **`equals()`만 재정의하면 HashSet, HashMap 등의 컬렉션에서 문제가 발생할 수 있다.**
|
||||
- **`hashCode()`도 함께 재정의해야 컬렉션에서 정상 동작한다.**
|
||||
- **`record`를 사용하면 자동으로 `equals()`와 `hashCode()`가 구현된다.**
|
||||
|
||||
이제 `equals()`와 `hashCode()`를 제대로 이해했으니, 컬렉션을 사용할 때 예상치 못한 버그를 피할 수 있을 것이다!
|
||||
149
docs/Generic.md
Normal file
149
docs/Generic.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# **자바 제너릭(Generic) 쉽게 배우기**
|
||||
|
||||
## **1. 제너릭이 뭐야?**
|
||||
제너릭(Generic)은 **클래스나 메서드에서 사용할 데이터 타입을 미리 지정하지 않고, 나중에 정할 수 있게 해주는 기능**이다.
|
||||
쉽게 말해 **"박스를 만들되, 안에 뭐가 들어갈지는 나중에 정하는 것"**과 같다.
|
||||
|
||||
예를 들어, `List`는 여러 종류의 데이터를 저장할 수 있다.
|
||||
```java
|
||||
List<String> strList = new ArrayList<>();
|
||||
List<Integer> intList = new ArrayList<>();
|
||||
```
|
||||
여기서 `List<String>`은 문자열만, `List<Integer>`는 정수만 저장할 수 있다.
|
||||
이처럼 **제너릭을 사용하면 타입을 강제할 수 있어 코드의 안정성이 높아진다.**
|
||||
|
||||
---
|
||||
|
||||
## **2. 제너릭을 사용하지 않으면? (비교 예제)**
|
||||
제너릭을 사용하지 않는 코드:
|
||||
```java
|
||||
List list = new ArrayList();
|
||||
list.add("Hello");
|
||||
list.add(123); // 문자열 리스트인데 숫자가 들어감
|
||||
|
||||
String str = (String) list.get(1); // 실행 시 오류 발생 가능!
|
||||
```
|
||||
위 코드에서는 `List`가 **어떤 타입의 데이터를 가질지 정해져 있지 않다.**
|
||||
따라서 다른 타입의 데이터가 들어와도 **컴파일러가 오류를 잡아주지 못한다.**
|
||||
|
||||
---
|
||||
|
||||
## **3. 제너릭을 사용한 코드 (안전한 코드!)**
|
||||
```java
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("Hello");
|
||||
// list.add(123); // 컴파일 오류 발생! (안전성 증가)
|
||||
|
||||
String str = list.get(0); // 타입 캐스팅 없이 사용 가능
|
||||
```
|
||||
✅ **제너릭을 사용하면 타입을 제한할 수 있어 실수를 줄이고, 형 변환 없이 안전하게 데이터를 사용할 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
## **4. 제너릭 클래스 만들기**
|
||||
제너릭을 직접 만들어 보자!
|
||||
```java
|
||||
class Box<T> { // T는 타입 매개변수
|
||||
private T item;
|
||||
|
||||
public void setItem(T item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
```
|
||||
이제 `Box<T>`를 다양한 타입으로 사용할 수 있다.
|
||||
```java
|
||||
Box<String> strBox = new Box<>();
|
||||
strBox.setItem("Hello");
|
||||
System.out.println(strBox.getItem()); // Hello
|
||||
|
||||
Box<Integer> intBox = new Box<>();
|
||||
intBox.setItem(100);
|
||||
System.out.println(intBox.getItem()); // 100
|
||||
```
|
||||
✅ **제너릭을 사용하면 같은 코드로 여러 타입을 지원할 수 있어 코드의 재사용성이 높아진다!**
|
||||
|
||||
---
|
||||
|
||||
## **5. 제너릭 메서드 만들기**
|
||||
제너릭은 클래스뿐만 아니라 **메서드에서도 사용할 수 있다.**
|
||||
```java
|
||||
class Util {
|
||||
public static <T> void printItem(T item) {
|
||||
System.out.println(item);
|
||||
}
|
||||
}
|
||||
|
||||
Util.printItem("Hello"); // Hello
|
||||
Util.printItem(123); // 123
|
||||
Util.printItem(3.14); // 3.14
|
||||
```
|
||||
✅ **제너릭 메서드를 사용하면 여러 타입을 지원하는 함수를 쉽게 만들 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
## **6. 제너릭 타입 제한하기 (extends 사용)**
|
||||
어떤 타입이든 받을 수 있는 것도 좋지만, **특정 타입만 허용하고 싶을 때**가 있다.
|
||||
이럴 때 `extends` 키워드를 사용해 제한할 수 있다.
|
||||
```java
|
||||
class NumberBox<T extends Number> { // Number 또는 그 자식 타입만 가능
|
||||
private T num;
|
||||
|
||||
public NumberBox(T num) {
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
public double getDoubleValue() {
|
||||
return num.doubleValue();
|
||||
}
|
||||
}
|
||||
```
|
||||
사용 예시:
|
||||
```java
|
||||
NumberBox<Integer> intBox = new NumberBox<>(10);
|
||||
System.out.println(intBox.getDoubleValue()); // 10.0
|
||||
|
||||
NumberBox<Double> doubleBox = new NumberBox<>(5.5);
|
||||
System.out.println(doubleBox.getDoubleValue()); // 5.5
|
||||
|
||||
// NumberBox<String> strBox = new NumberBox<>("Hello"); // 오류 발생!
|
||||
```
|
||||
✅ **제너릭에 `extends`를 사용하면 특정 타입만 허용할 수 있어 더 안전한 코드 작성이 가능하다!**
|
||||
|
||||
---
|
||||
|
||||
## **7. 와일드카드 (`?`) 활용하기**
|
||||
제너릭을 사용하다 보면 **다양한 타입을 받아야 하지만, 타입을 정확히 모를 때**가 있다.
|
||||
이럴 때 **와일드카드 (`?`)** 를 사용할 수 있다.
|
||||
|
||||
```java
|
||||
public static void printList(List<?> list) {
|
||||
for (Object obj : list) {
|
||||
System.out.println(obj);
|
||||
}
|
||||
}
|
||||
```
|
||||
사용 예시:
|
||||
```java
|
||||
List<String> strList = Arrays.asList("A", "B", "C");
|
||||
List<Integer> intList = Arrays.asList(1, 2, 3);
|
||||
|
||||
printList(strList); // A, B, C
|
||||
printList(intList); // 1, 2, 3
|
||||
```
|
||||
✅ **`?`를 사용하면 어떤 타입이든 받을 수 있어 유연한 코드 작성이 가능하다!**
|
||||
|
||||
---
|
||||
|
||||
## **8. 정리**
|
||||
✅ **제너릭을 사용하면 코드의 타입 안정성이 증가하고, 불필요한 형 변환을 줄일 수 있다!**
|
||||
✅ **제너릭 클래스(`Box<T>`)를 사용하면 여러 타입을 지원하는 재사용 가능한 클래스를 만들 수 있다!**
|
||||
✅ **제너릭 메서드(`printItem<T>(T item)`)를 사용하면 다양한 타입을 처리하는 메서드를 쉽게 작성할 수 있다!**
|
||||
✅ **`extends`를 사용하면 특정 타입만 허용할 수 있다!**
|
||||
✅ **`?`(와일드카드)를 사용하면 유연한 코드 작성이 가능하다!**
|
||||
|
||||
자바 제너릭을 활용하면 **더 안전하고 재사용 가능한 코드**를 작성할 수 있다!
|
||||
141
docs/GlassFish.md
Normal file
141
docs/GlassFish.md
Normal file
@@ -0,0 +1,141 @@
|
||||
### Jakarta EE란 무엇인가?
|
||||
|
||||
Jakarta EE(Enterprise Edition)는 Java EE(Java Platform, Enterprise Edition)의 후속 프로젝트로, 엔터프라이즈급 애플리케이션 개발을 위한 표준화된 플랫폼입니다. 2017년 Oracle이 Java EE를 Eclipse 재단으로 이관하면서 이름이 Jakarta EE로 변경되었으며, 현재는 오픈 소스 커뮤니티 주도로 발전하고 있습니다. Jakarta EE는 분산 시스템, 웹 애플리케이션, 비즈니스 로직 처리 등 엔터프라이즈 환경에서 필요한 다양한 기술을 제공합니다.
|
||||
|
||||
현재 날짜 기준(2025년 3월 9일)으로, 최신 버전은 **Jakarta EE 10**이며, 이는 2022년에 출시되었습니다. Jakarta EE 11은 2025년 중반 출시를 목표로 개발 중입니다.
|
||||
|
||||
---
|
||||
|
||||
### Jakarta EE의 주요 세부 사항
|
||||
|
||||
Jakarta EE는 여러 API와 기술 스펙으로 구성되어 있으며, 이를 통해 개발자는 표준화된 방식으로 애플리케이션을 구축할 수 있습니다. 아래에서 주요 구성 요소와 세부 사항을 상세히 설명합니다.
|
||||
|
||||
#### 1. **핵심 구성 요소 (API와 기술)**
|
||||
|
||||
| **기술/API** | **설명** | **주요 용도** | **Jakarta EE 10에서의 특징** |
|
||||
|-------------------------|------------------------------------------------------------------------------------------|---------------------------------------|---------------------------------------------|
|
||||
| **Servlet** | HTTP 요청/응답 처리를 위한 웹 컴포넌트 | 웹 애플리케이션 개발 | Servlet 6.0: 비동기 처리 개선, HTTP/2 지원 |
|
||||
| **JSP (JavaServer Pages)** | 동적 웹 콘텐츠 생성을 위한 템플릿 엔진 | 웹 페이지 렌더링 | JSP 3.1: EL(Expression Language) 개선 |
|
||||
| **JSF (JavaServer Faces)** | 컴포넌트 기반의 웹 UI 프레임워크 | 복잡한 웹 UI 개발 | JSF 4.0: Facelets 개선, CDI 통합 강화 |
|
||||
| **EJB (Enterprise JavaBeans)** | 비즈니스 로직을 캡슐화한 서버 측 컴포넌트 | 트랜잭션 관리, 분산 컴포넌트 | EJB 4.0: 경량화 및 CDI와 통합 |
|
||||
| **JPA (Java Persistence API)** | 객체-관계 매핑(ORM)을 위한 표준 | 데이터베이스 액세스 | JPA 3.1: Criteria API 개선, Jakarta 네임스페이스 |
|
||||
| **JMS (Java Message Service)** | 메시지 기반 비동기 통신을 위한 API | 메시지 큐, Pub/Sub 모델 | JMS 3.0: 클라우드 환경 지원 강화 |
|
||||
| **CDI (Contexts and Dependency Injection)** | 의존성 주입과 컨텍스트 관리를 위한 표준 | 객체 생명주기 관리, DI | CDI 4.0: 모듈성 강화, 이벤트 처리 개선 |
|
||||
| **JAX-RS (RESTful Web Services)** | RESTful 웹 서비스를 위한 API | API 개발 | JAX-RS 3.1: 클라이언트 API 개선, JSON-B 통합 |
|
||||
| **JSON-P (JSON Processing)** | JSON 데이터 처리 API | JSON 파싱 및 생성 | JSON-P 2.1: 스트리밍 API 개선 |
|
||||
| **JSON-B (JSON Binding)** | 객체와 JSON 간 매핑 API | 데이터 직렬화/역직렬화 | JSON-B 3.0: 어노테이션 기반 매핑 강화 |
|
||||
| **Jakarta Mail** | 이메일 전송 및 수신을 위한 API | 이메일 기능 | Jakarta Mail 2.1: 보안 강화 |
|
||||
| **Jakarta Transactions (JTA)** | 분산 트랜잭션 관리 API | 트랜잭션 처리 | JTA 2.0: CDI 통합 개선 |
|
||||
| **Jakarta Security** | 인증 및 권한 부여를 위한 표준 API | 보안 관리 | Security 3.0: SSO(Single Sign-On) 지원 강화 |
|
||||
|
||||
---
|
||||
|
||||
#### 2. **버전별 주요 변경 사항**
|
||||
|
||||
- **Jakarta EE 8 (2019)**:
|
||||
Java EE 8의 리브랜딩 버전으로, 패키지 네임스페이스가 `javax.*`에서 `jakarta.*`로 변경. 기술적으로는 Java EE 8과 동일.
|
||||
|
||||
- **Jakarta EE 9 (2020)**:
|
||||
- 네임스페이스 전환 완료 (`javax.*` → `jakarta.*`).
|
||||
- API 변화는 최소화, 주로 호환성 유지에 초점.
|
||||
|
||||
- **Jakarta EE 9.1 (2021)**:
|
||||
- Java SE 11 지원 추가.
|
||||
- 기존 API의 안정화.
|
||||
|
||||
- **Jakarta EE 10 (2022)**:
|
||||
- Java SE 17 지원.
|
||||
- CDI와 JAX-RS의 개선, 마이크로서비스 친화적인 기능 추가.
|
||||
- Web Profile과 Full Platform 간 격차 축소.
|
||||
|
||||
- **Jakarta EE 11 (예정, 2025)**:
|
||||
- 클라우드 네이티브 지원 강화 (예: Kubernetes 통합).
|
||||
- 새로운 API 추가 및 기존 API의 경량화.
|
||||
|
||||
---
|
||||
|
||||
#### 3. **플랫폼 구성**
|
||||
|
||||
Jakarta EE는 두 가지 프로필로 제공됩니다:
|
||||
- **Web Profile**:
|
||||
웹 애플리케이션 개발에 초점을 맞춘 경량 프로필. Servlet, JSP, JSF, JPA, CDI, JAX-RS 등 포함.
|
||||
- **Full Platform**:
|
||||
모든 Jakarta EE 기술을 포함하며, EJB, JMS, JTA 등 엔터프라이즈 기능을 지원.
|
||||
|
||||
---
|
||||
|
||||
#### 4. **주요 구현체**
|
||||
|
||||
Jakarta EE 사양을 구현한 애플리케이션 서버는 여러 가지가 있으며, 대표적인 구현체는 다음과 같습니다:
|
||||
- **Eclipse GlassFish**: Jakarta EE의 공식 레퍼런스 구현.
|
||||
- **Payara Server**: GlassFish 기반의 포크로, 상용 지원과 추가 기능 제공.
|
||||
- **WildFly**: Red Hat에서 개발, 경량화와 성능에 초점.
|
||||
- **Apache TomEE**: Tomcat에 EE 기능을 추가한 구현체.
|
||||
- **Open Liberty**: IBM의 경량화된 오픈 소스 서버.
|
||||
|
||||
---
|
||||
|
||||
#### 5. **Jakarta EE의 장점**
|
||||
|
||||
- **표준화**: 모든 구현체가 동일한 API를 준수하므로, 서버 간 호환성이 높음.
|
||||
- **확장성**: 분산 시스템과 대규모 애플리케이션에 적합.
|
||||
- **생태계**: 방대한 오픈 소스 커뮤니티와 도구 지원 (Maven, Gradle, IDE 등).
|
||||
- **모듈성**: 필요한 기능만 선택해 사용할 수 있음.
|
||||
|
||||
---
|
||||
|
||||
#### 6. **Jakarta EE의 단점**
|
||||
|
||||
- **복잡성**: Full Platform은 초보자에게 다소 무겁고 복잡할 수 있음.
|
||||
- **경쟁**: Spring Boot와 같은 마이크로서비스 중심 프레임워크에 비해 전통적인 모놀리식 구조에 치우침.
|
||||
- **학습 곡선**: 다양한 API를 익히는 데 시간이 필요.
|
||||
|
||||
---
|
||||
|
||||
#### 7. **실제 사용 예시**
|
||||
|
||||
간단한 JAX-RS 기반 REST API 예시:
|
||||
```java
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/hello")
|
||||
public class HelloResource {
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String sayHello() {
|
||||
return "Hello, Jakarta EE!";
|
||||
}
|
||||
}
|
||||
```
|
||||
- 위 코드를 GlassFish에 배포하면 `http://localhost:8080/your-app/hello`로 접근 가능.
|
||||
|
||||
---
|
||||
|
||||
#### 8. **Jakarta EE와 Spring 비교**
|
||||
|
||||
| **항목** | **Jakarta EE** | **Spring** |
|
||||
|--------------------|--------------------------------|--------------------------------|
|
||||
| **표준 여부** | 표준 사양 (Jakarta EE) | 비표준 프레임워크 |
|
||||
| **구성** | 여러 독립 API | 통합된 생태계 (Spring Boot 등) |
|
||||
| **배포** | 애플리케이션 서버 필요 | 내장 서버 지원 |
|
||||
| **용도** | 전통적 엔터프라이즈 애플리케이션 | 마이크로서비스, 웹 애플리케이션 |
|
||||
| **경량화** | Web Profile로 가능 | 기본적으로 경량화 |
|
||||
|
||||
---
|
||||
|
||||
#### 9. **미래 전망 (2025년 기준)**
|
||||
|
||||
- **클라우드 네이티브**: Jakarta EE 11은 Kubernetes와 같은 클라우드 환경에 최적화될 예정.
|
||||
- **경쟁력 강화**: Spring Boot와의 경쟁에서 살아남기 위해 경량화와 모듈성에 더욱 집중.
|
||||
- **커뮤니티 활성화**: Eclipse 재단의 지속적인 지원으로 커뮤니티가 성장 중.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
|
||||
Jakarta EE는 엔터프라이즈급 애플리케이션 개발을 위한 견고한 표준 플랫폼으로, 다양한 API와 구현체를 통해 유연성과 확장성을 제공합니다. 최신 버전인 Jakarta EE 10은 현대적인 요구사항을 반영하며, 다가오는 Jakarta EE 11은 클라우드 네이티브 환경에 더 적합해질 전망입니다. 프로젝트의 규모와 요구사항에 따라 Web Profile 또는 Full Platform을 선택해 사용하면 됩니다.
|
||||
|
||||
추가로 궁금한 점이 있다면 말씀해주세요!
|
||||
262
docs/Guice.md
Normal file
262
docs/Guice.md
Normal file
@@ -0,0 +1,262 @@
|
||||
### Guice의 주요 어노테이션과 사용 예시
|
||||
|
||||
Google Guice는 의존성 주입(Dependency Injection, DI)을 간편하게 구현할 수 있는 경량 프레임워크로, 다양한 어노테이션을 통해 의존성을 정의하고 관리합니다. 아래에서는 Guice에서 자주 사용되는 주요 어노테이션들을 표로 정리하고, 각 어노테이션의 역할과 사용 예시를 설명하겠습니다.
|
||||
|
||||
---
|
||||
|
||||
#### Guice 주요 어노테이션 표
|
||||
|
||||
| 어노테이션 | 설명 | 사용 위치 | 주요 특징 및 용도 |
|
||||
|-------------------|----------------------------------------------------------------------|-----------------------|-------------------------------------------------------|
|
||||
| `@Inject` | 의존성을 주입할 위치를 지정합니다. 생성자, 필드, 메서드에 사용 가능합니다. | 생성자, 필드, Setter 메서드 | Guice가 자동으로 의존성을 주입하도록 지시. 선택적 주입 가능 (`@Inject(optional=true)`). |
|
||||
| `@Named` | 동일한 타입의 여러 구현체 중 특정 구현체를 선택하기 위해 사용됩니다. | 필드, 매개변수 | 문자열 키로 구체적인 바인딩을 식별. |
|
||||
| `@Singleton` | 클래스의 인스턴스가 단일 객체로 유지되도록 지정합니다. | 클래스 | 싱글턴 패턴을 구현하여 메모리 효율성 향상. |
|
||||
| `@Provides` | 모듈 내에서 의존성을 제공하는 메서드를 정의합니다. | 모듈의 메서드 | 복잡한 객체 생성 로직을 직접 작성 가능. |
|
||||
| `@ImplementedBy` | 인터페이스의 기본 구현체를 지정합니다. | 인터페이스 | 모듈 없이 기본 바인딩을 설정. |
|
||||
| `@ProvidedBy` | 동적으로 의존성을 제공하는 클래스를 지정합니다. | 인터페이스 | Provider 클래스를 통해 의존성 공급. |
|
||||
| `@Qualifier` | 사용자 정의 qualifier를 생성하기 위한 메타 어노테이션입니다. | 사용자 정의 어노테이션 | `@Named`보다 더 세밀한 의존성 구분 가능. |
|
||||
|
||||
---
|
||||
|
||||
### 어노테이션별 설명 및 예시
|
||||
|
||||
#### 1. `@Inject`
|
||||
- **설명**: Guice가 의존성을 주입할 위치를 나타냅니다. 생성자 주입, 필드 주입, Setter 주입에 사용됩니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class UserService {
|
||||
private final Database database;
|
||||
|
||||
@Inject
|
||||
public UserService(Database database) { // 생성자 주입
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Inject
|
||||
private Logger logger; // 필드 주입
|
||||
|
||||
@Inject
|
||||
public void setConfig(Config config) { // Setter 주입
|
||||
// 설정 로직
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: 생성자 주입이 가장 권장되며, 불변성을 보장합니다. 필드 주입은 간단하지만 테스트 시 불편할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
#### 2. `@Named`
|
||||
- **설명**: 동일한 인터페이스를 구현한 여러 클래스가 있을 때, 특정 구현체를 선택할 때 사용됩니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
interface Database {
|
||||
void connect();
|
||||
}
|
||||
|
||||
class MySQLDatabase implements Database {
|
||||
public void connect() { System.out.println("MySQL 연결"); }
|
||||
}
|
||||
|
||||
class PostgresDatabase implements Database {
|
||||
public void connect() { System.out.println("Postgres 연결"); }
|
||||
}
|
||||
|
||||
class MyModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Database.class).annotatedWith(Names.named("mysql")).to(MySQLDatabase.class);
|
||||
bind(Database.class).annotatedWith(Names.named("postgres")).to(PostgresDatabase.class);
|
||||
}
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject @Named("mysql")
|
||||
private Database database;
|
||||
|
||||
public void run() {
|
||||
database.connect(); // 출력: MySQL 연결
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: 문자열 기반으로 동작하며, 오타에 주의해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
#### 3. `@Singleton`
|
||||
- **설명**: 클래스의 인스턴스를 싱글턴으로 관리하도록 지정합니다. Guice 인젝터가 단일 인스턴스를 재사용합니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@Singleton
|
||||
class Cache {
|
||||
public void store(String key, String value) {
|
||||
System.out.println("캐시에 저장: " + key + " -> " + value);
|
||||
}
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject
|
||||
private Cache cache;
|
||||
|
||||
public void run() {
|
||||
cache.store("user", "data"); // 동일한 인스턴스 사용
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: 메모리 효율성을 높이지만, 상태를 공유하므로 주의가 필요합니다.
|
||||
|
||||
---
|
||||
|
||||
#### 4. `@Provides`
|
||||
- **설명**: 모듈 내에서 복잡한 객체 생성 로직을 정의할 때 사용됩니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
interface Client {
|
||||
void send(String msg);
|
||||
}
|
||||
|
||||
class HttpClient implements Client {
|
||||
private final String url;
|
||||
public HttpClient(String url) { this.url = url; }
|
||||
public void send(String msg) { System.out.println("Sending to " + url + ": " + msg); }
|
||||
}
|
||||
|
||||
class MyModule extends AbstractModule {
|
||||
@Provides
|
||||
public Client provideClient() {
|
||||
return new HttpClient("https://api.example.com");
|
||||
}
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
public void run() {
|
||||
client.send("Hello"); // 출력: Sending to https://api.example.com: Hello
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: 동적 생성 로직을 커스터마이징할 수 있어 유연성이 높습니다.
|
||||
|
||||
---
|
||||
|
||||
#### 5. `@ImplementedBy`
|
||||
- **설명**: 인터페이스의 기본 구현체를 지정하여 모듈 설정 없이도 의존성을 주입할 수 있습니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.ImplementedBy;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@ImplementedBy(DefaultService.class)
|
||||
interface Service {
|
||||
void execute();
|
||||
}
|
||||
|
||||
class DefaultService implements Service {
|
||||
public void execute() { System.out.println("기본 서비스 실행"); }
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject
|
||||
private Service service;
|
||||
|
||||
public void run() {
|
||||
service.execute(); // 출력: 기본 서비스 실행
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: 모듈 설정이 필요 없어 간단하지만, 유연성이 제한적입니다.
|
||||
|
||||
---
|
||||
|
||||
#### 6. `@ProvidedBy`
|
||||
- **설명**: Provider 클래스를 통해 동적으로 의존성을 제공합니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.ProvidedBy;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@ProvidedBy(DynamicServiceProvider.class)
|
||||
interface Service {
|
||||
void execute();
|
||||
}
|
||||
|
||||
class DynamicService implements Service {
|
||||
public void execute() { System.out.println("동적 서비스 실행"); }
|
||||
}
|
||||
|
||||
class DynamicServiceProvider implements Provider<Service> {
|
||||
@Override
|
||||
public Service get() {
|
||||
return new DynamicService();
|
||||
}
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject
|
||||
private Service service;
|
||||
|
||||
public void run() {
|
||||
service.execute(); // 출력: 동적 서비스 실행
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: Provider를 통해 런타임에 객체를 생성할 수 있어 동적 처리가 가능합니다.
|
||||
|
||||
---
|
||||
|
||||
#### 7. `@Qualifier`
|
||||
- **설명**: 사용자 정의 어노테이션을 만들어 더 세밀한 의존성 구분을 가능하게 합니다.
|
||||
- **예시**:
|
||||
```java
|
||||
import com.google.inject.Qualifier;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Qualifier
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface PrimaryDB {}
|
||||
|
||||
class MyModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Database.class).annotatedWith(PrimaryDB.class).to(MySQLDatabase.class);
|
||||
}
|
||||
}
|
||||
|
||||
class App {
|
||||
@Inject @PrimaryDB
|
||||
private Database database;
|
||||
|
||||
public void run() {
|
||||
database.connect(); // 출력: MySQL 연결
|
||||
}
|
||||
}
|
||||
```
|
||||
- **특징**: `@Named`보다 타입 안전성이 높고, 오타 문제를 방지할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
### Guice 어노테이션 사용 팁
|
||||
- **생성자 주입 우선**: `@Inject`를 생성자에 사용해 불변성과 명확성을 유지하세요.
|
||||
- **Qualifier 활용**: `@Named` 대신 사용자 정의 `@Qualifier`를 사용하면 더 안전합니다.
|
||||
- **싱글턴 주의**: `@Singleton`은 상태를 공유하므로 스레드 안전성을 고려해야 합니다.
|
||||
- **모듈 분리**: 복잡한 프로젝트에서는 `@Provides`를 활용해 모듈을 깔끔하게 관리하세요.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
Guice의 어노테이션들은 의존성 주입을 간단하고 유연하게 만들어줍니다. 위 예시를 통해 각 어노테이션의 사용법과 장점을 이해할 수 있으며, 프로젝트 요구사항에 맞게 적절히 선택해 사용하면 됩니다. 추가 질문이 있다면 언제든 물어보세요!
|
||||
240
docs/JAXB.md
Normal file
240
docs/JAXB.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# JAXB (Java Architecture for XML Binding)
|
||||
|
||||
## **1. JAXB란?**
|
||||
JAXB(Java Architecture for XML Binding)는 **Java 객체를 XML로 변환하거나, XML을 Java 객체로 변환하는 기술**이다.
|
||||
XML 데이터를 다룰 때, DOM이나 SAX보다 훨씬 간편하다.
|
||||
|
||||
**JAXB 주요 기능**
|
||||
✔ **Java 객체 → XML 변환 (Marshalling)**
|
||||
✔ **XML → Java 객체 변환 (Unmarshalling)**
|
||||
✔ **XML Schema 기반 Java 클래스 자동 생성 가능**
|
||||
|
||||
---
|
||||
|
||||
## **2. JAXB 어노테이션 정리**
|
||||
JAXB는 여러 어노테이션을 제공하여 XML과 Java 객체 간 매핑을 설정할 수 있다.
|
||||
|
||||
| 어노테이션 | 설명 |
|
||||
|-----------|-----------------------------------------------------|
|
||||
| `@XmlRootElement` | XML의 루트 요소를 정의 |
|
||||
| `@XmlElement` | 특정 필드를 XML 요소로 매핑 |
|
||||
| `@XmlAttribute` | 특정 필드를 XML 속성으로 매핑 |
|
||||
| `@XmlAccessorType` | 필드 접근 방식 설정 (`FIELD`, `PROPERTY`, `NONE`, `PUBLIC_MEMBER`) |
|
||||
| `@XmlTransient` | 특정 필드를 XML에 포함하지 않음 |
|
||||
| `@XmlElementWrapper` | 컬렉션 요소를 감싸는 XML 요소를 추가 |
|
||||
| `@XmlType` | XML 요소의 순서를 지정 |
|
||||
| `@XmlEnum` | Enum을 XML 요소로 변환 |
|
||||
| `@XmlValue` | XML 요소의 값을 직접 지정 |
|
||||
| `@XmlJavaTypeAdapter` | 사용자 정의 변환을 적용 |
|
||||
|
||||
---
|
||||
|
||||
## **3. JAXB 사용 예제**
|
||||
|
||||
### **✔ Java 객체 → XML 변환 (Marshalling)**
|
||||
|
||||
```java
|
||||
import javax.xml.bind.annotation.*;
|
||||
import javax.xml.bind.*;
|
||||
|
||||
@XmlRootElement(name = "person")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class Person {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public Person() {} // JAXB는 기본 생성자가 필요함
|
||||
|
||||
public Person(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public int getAge() { return age; }
|
||||
public void setAge(int age) { this.age = age; }
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Person person = new Person("홍길동", 30);
|
||||
|
||||
// JAXB 객체 생성
|
||||
JAXBContext context = JAXBContext.newInstance(Person.class);
|
||||
Marshaller marshaller = context.createMarshaller();
|
||||
|
||||
// XML 출력 형식 설정 (예쁘게 출력)
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
|
||||
// Java 객체를 XML로 변환 후 출력
|
||||
marshaller.marshal(person, System.out);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과 (XML 출력)**
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<person>
|
||||
<name>홍길동</name>
|
||||
<age>30</age>
|
||||
</person>
|
||||
```
|
||||
|
||||
✅ `@XmlRootElement`를 사용하여 `person`을 XML 루트 요소로 지정했다.
|
||||
✅ `@XmlAccessorType(XmlAccessType.FIELD)`을 사용하여 **클래스의 필드 기준으로 XML을 자동 생성**했다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ XML → Java 객체 변환 (Unmarshalling)**
|
||||
|
||||
위에서 생성한 XML을 다시 Java 객체로 변환하는 코드:
|
||||
|
||||
```java
|
||||
import java.io.StringReader;
|
||||
|
||||
public class XMLToJava {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String xmlData = """
|
||||
<person>
|
||||
<name>홍길동</name>
|
||||
<age>30</age>
|
||||
</person>
|
||||
""";
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(Person.class);
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
|
||||
// XML을 Java 객체로 변환
|
||||
Person person = (Person) unmarshaller.unmarshal(new StringReader(xmlData));
|
||||
|
||||
System.out.println("이름: " + person.getName());
|
||||
System.out.println("나이: " + person.getAge());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
이름: 홍길동
|
||||
나이: 30
|
||||
```
|
||||
✅ `Unmarshaller`를 사용하여 XML 데이터를 Java 객체로 변환했다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ XML 속성 처리 (`@XmlAttribute`)**
|
||||
필드를 XML 속성으로 변환할 수도 있다.
|
||||
|
||||
```java
|
||||
@XmlRootElement(name = "person")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class Person {
|
||||
@XmlAttribute
|
||||
private int id;
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public Person() {}
|
||||
|
||||
public Person(int id, String name, int age) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 XML 출력 결과**
|
||||
```xml
|
||||
<person id="1">
|
||||
<name>홍길동</name>
|
||||
<age>30</age>
|
||||
</person>
|
||||
```
|
||||
✅ `@XmlAttribute`를 사용하면 특정 필드를 **XML의 속성(attribute) 형태**로 변환할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ 컬렉션 처리 (`@XmlElementWrapper`)**
|
||||
리스트(List) 같은 컬렉션을 XML로 변환할 때 `@XmlElementWrapper`를 사용하면 요소를 그룹으로 감쌀 수 있다.
|
||||
|
||||
```java
|
||||
import java.util.List;
|
||||
|
||||
@XmlRootElement(name = "classroom")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class Classroom {
|
||||
@XmlElementWrapper(name = "students")
|
||||
@XmlElement(name = "student")
|
||||
private List<Person> students;
|
||||
|
||||
public Classroom() {}
|
||||
|
||||
public Classroom(List<Person> students) {
|
||||
this.students = students;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 XML 출력 결과**
|
||||
```xml
|
||||
<classroom>
|
||||
<students>
|
||||
<student>
|
||||
<name>홍길동</name>
|
||||
<age>30</age>
|
||||
</student>
|
||||
<student>
|
||||
<name>이몽룡</name>
|
||||
<age>25</age>
|
||||
</student>
|
||||
</students>
|
||||
</classroom>
|
||||
```
|
||||
✅ `@XmlElementWrapper(name = "students")`를 사용하여 `students` 요소를 감쌌다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ XML 요소 순서 지정 (`@XmlType`)**
|
||||
XML 요소의 순서를 지정하려면 `@XmlType`을 사용한다.
|
||||
|
||||
```java
|
||||
@XmlRootElement(name = "book")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = { "title", "author", "price" }) // 요소 순서 지정
|
||||
public class Book {
|
||||
private String title;
|
||||
private String author;
|
||||
private double price;
|
||||
|
||||
public Book() {}
|
||||
|
||||
public Book(String title, String author, double price) {
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.price = price;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 XML 출력 결과**
|
||||
```xml
|
||||
<book>
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000.0</price>
|
||||
</book>
|
||||
```
|
||||
✅ `@XmlType(propOrder = { "title", "author", "price" })`을 사용하여 요소 순서를 지정했다.
|
||||
|
||||
---
|
||||
|
||||
## **4. 정리**
|
||||
✔ **JAXB는 Java 객체와 XML 간 변환을 쉽게 할 수 있도록 도와주는 기술**
|
||||
✔ `@XmlRootElement`, `@XmlElement`, `@XmlAttribute` 등을 활용하여 XML 구조를 커스텀 가능
|
||||
✔ `Marshaller`를 사용하여 Java → XML 변환 (Marshalling)
|
||||
✔ `Unmarshaller`를 사용하여 XML → Java 변환 (Unmarshalling)
|
||||
✔ 컬렉션, 속성, 요소 순서 등도 세밀하게 제어 가능
|
||||
|
||||
✅ **JAXB를 활용하면 XML을 쉽게 다룰 수 있으며, 웹 서비스 (REST, SOAP) 개발에도 유용하다!**
|
||||
231
docs/JDBC.md
Normal file
231
docs/JDBC.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# **JDBC (Java Database Connectivity) 쉽게 배우기**
|
||||
|
||||
## **1. JDBC란?**
|
||||
JDBC는 **자바에서 데이터베이스와 연결**하여 **SQL을 실행하고 결과를 처리**하는 API이다.
|
||||
MySQL, PostgreSQL, Oracle 등 **다양한 DBMS에서 공통적으로 사용**할 수 있음.
|
||||
|
||||
✔ **주요 기능**
|
||||
- 데이터베이스 연결 (`Connection`)
|
||||
- SQL 실행 (`Statement`, `PreparedStatement`)
|
||||
- 결과 가져오기 (`ResultSet`)
|
||||
- 트랜잭션 처리
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **📌 JDBC 연결 관련 클래스**
|
||||
| 클래스 | 설명 |
|
||||
|--------|------|
|
||||
| `DriverManager` | 데이터베이스 연결을 관리하는 클래스 |
|
||||
| `Connection` | 데이터베이스와의 연결을 나타내는 객체 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `DriverManager.getConnection(url, user, password)` | DB 연결 생성 |
|
||||
| `Connection.close()` | DB 연결 종료 |
|
||||
| `Connection.setAutoCommit(boolean autoCommit)` | 자동 커밋 설정 |
|
||||
| `Connection.commit()` | 트랜잭션 커밋 |
|
||||
| `Connection.rollback()` | 트랜잭션 롤백 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 SQL 실행 관련 클래스**
|
||||
| 클래스 | 설명 |
|
||||
|--------|------|
|
||||
| `Statement` | SQL 쿼리를 실행하는 객체 (단순 SQL 실행) |
|
||||
| `PreparedStatement` | SQL 쿼리를 실행하는 객체 (파라미터 지원) |
|
||||
| `CallableStatement` | 저장 프로시저(Stored Procedure) 호출 객체 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `Statement.executeQuery(String sql)` | SELECT 실행 후 `ResultSet` 반환 |
|
||||
| `Statement.executeUpdate(String sql)` | INSERT, UPDATE, DELETE 실행 |
|
||||
| `PreparedStatement.setXXX(int index, value)` | SQL의 `?`에 값 바인딩 |
|
||||
| `PreparedStatement.executeQuery()` | SELECT 실행 후 `ResultSet` 반환 |
|
||||
| `PreparedStatement.executeUpdate()` | INSERT, UPDATE, DELETE 실행 |
|
||||
| `Statement.close()` | Statement 종료 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 결과 조회 관련 클래스**
|
||||
| 클래스 | 설명 |
|
||||
|--------|------|
|
||||
| `ResultSet` | SQL 실행 결과를 저장하는 객체 |
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `ResultSet.next()` | 다음 행으로 이동 |
|
||||
| `ResultSet.getString("column")` | 문자열 값 가져오기 |
|
||||
| `ResultSet.getInt("column")` | 정수 값 가져오기 |
|
||||
| `ResultSet.close()` | 결과 집합 종료 |
|
||||
|
||||
---
|
||||
|
||||
## **3. JDBC 기본 사용 예제**
|
||||
|
||||
### **✔ 데이터베이스 연결 및 SELECT 예제**
|
||||
아래 코드는 **JDBC를 이용하여 MySQL에 연결하고, 데이터를 조회하는 예제**이다.
|
||||
|
||||
```java
|
||||
import java.sql.*;
|
||||
|
||||
public class JdbcExample {
|
||||
public static void main(String[] args) {
|
||||
// 1. DB 연결 정보 설정
|
||||
String url = "jdbc:mysql://localhost:3306/mydb";
|
||||
String user = "root";
|
||||
String password = "1234";
|
||||
|
||||
// 2. JDBC를 이용한 DB 연결 및 데이터 조회
|
||||
try {
|
||||
// 3. 데이터베이스 연결
|
||||
Connection conn = DriverManager.getConnection(url, user, password);
|
||||
System.out.println("DB 연결 성공!");
|
||||
|
||||
// 4. SQL 실행
|
||||
String sql = "SELECT id, name, age FROM users";
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
// 5. 결과 출력
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
int age = rs.getInt("age");
|
||||
System.out.println("ID: " + id + ", 이름: " + name + ", 나이: " + age);
|
||||
}
|
||||
|
||||
// 6. 리소스 해제
|
||||
rs.close();
|
||||
stmt.close();
|
||||
conn.close();
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
DB 연결 성공!
|
||||
ID: 1, 이름: 홍길동, 나이: 30
|
||||
ID: 2, 이름: 이몽룡, 나이: 25
|
||||
```
|
||||
|
||||
✅ `DriverManager.getConnection(url, user, password)`을 이용해 DB 연결
|
||||
✅ `Statement.executeQuery(sql)`을 이용해 SELECT 실행
|
||||
✅ `ResultSet.getInt()`, `getString()`을 이용해 결과 가져오기
|
||||
|
||||
---
|
||||
|
||||
## **4. PreparedStatement 사용 (SQL Injection 방지)**
|
||||
|
||||
`PreparedStatement`는 **SQL에 `?`를 사용하여 값을 안전하게 전달**할 수 있음.
|
||||
**SQL Injection을 방지**하고 **반복 실행 시 성능 최적화** 가능.
|
||||
|
||||
```java
|
||||
import java.sql.*;
|
||||
|
||||
public class PreparedStatementExample {
|
||||
public static void main(String[] args) {
|
||||
String url = "jdbc:mysql://localhost:3306/mydb";
|
||||
String user = "root";
|
||||
String password = "1234";
|
||||
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(url, user, password);
|
||||
|
||||
// SQL에 ?를 사용하여 값 바인딩
|
||||
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
|
||||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||||
pstmt.setString(1, "김철수"); // 첫 번째 ?에 값 설정
|
||||
pstmt.setInt(2, 28); // 두 번째 ?에 값 설정
|
||||
|
||||
int rows = pstmt.executeUpdate();
|
||||
System.out.println(rows + "개의 행이 삽입됨!");
|
||||
|
||||
pstmt.close();
|
||||
conn.close();
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
1개의 행이 삽입됨!
|
||||
```
|
||||
|
||||
✅ `PreparedStatement.setString(1, "김철수")`로 안전한 데이터 전달
|
||||
✅ `executeUpdate()`를 사용하여 INSERT 실행
|
||||
|
||||
---
|
||||
|
||||
## **5. 트랜잭션 처리 예제**
|
||||
기본적으로 **JDBC는 자동 커밋 모드**이므로, 수동으로 커밋하려면 `setAutoCommit(false)`를 설정해야 함.
|
||||
|
||||
```java
|
||||
import java.sql.*;
|
||||
|
||||
public class TransactionExample {
|
||||
public static void main(String[] args) {
|
||||
String url = "jdbc:mysql://localhost:3306/mydb";
|
||||
String user = "root";
|
||||
String password = "1234";
|
||||
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(url, user, password);
|
||||
conn.setAutoCommit(false); // 자동 커밋 해제
|
||||
|
||||
// 1. 첫 번째 INSERT 실행
|
||||
PreparedStatement pstmt1 = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)");
|
||||
pstmt1.setString(1, "박영희");
|
||||
pstmt1.setInt(2, 27);
|
||||
pstmt1.executeUpdate();
|
||||
|
||||
// 2. 일부러 오류 발생시키기 (age 컬럼에 문자 입력)
|
||||
PreparedStatement pstmt2 = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)");
|
||||
pstmt2.setString(1, "오류발생");
|
||||
pstmt2.setString(2, "문자"); // 오류 발생
|
||||
pstmt2.executeUpdate();
|
||||
|
||||
conn.commit(); // 커밋 (이 코드까지 실행되면 모든 변경이 적용됨)
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
System.out.println("롤백 실행");
|
||||
conn.rollback(); // 오류 발생 시 롤백
|
||||
} catch (SQLException rollbackEx) {
|
||||
rollbackEx.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
롤백 실행
|
||||
```
|
||||
|
||||
✅ `setAutoCommit(false)`를 설정하여 수동 커밋 모드로 변경
|
||||
✅ `commit()`이 실행되기 전에 오류가 발생하면 `rollback()`으로 변경 사항 취소
|
||||
|
||||
---
|
||||
|
||||
## **6. 정리**
|
||||
✔ **JDBC는 자바에서 DB와 상호작용하는 API**
|
||||
✔ `DriverManager.getConnection()`을 사용하여 DB 연결
|
||||
✔ `Statement`와 `PreparedStatement`를 사용하여 SQL 실행
|
||||
✔ `ResultSet`을 사용하여 데이터 조회
|
||||
✔ **트랜잭션 관리 (`commit()`, `rollback()`)를 활용하여 데이터 정합성 유지**
|
||||
|
||||
✅ **SQL 실행이 많다면 `PreparedStatement`를 사용하자!**
|
||||
✅ **트랜잭션 처리를 위해 `setAutoCommit(false)`를 활용하자!**
|
||||
162
docs/Jackson Streaming API.md
Normal file
162
docs/Jackson Streaming API.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# **Jackson Streaming API 정리 및 예제**
|
||||
|
||||
## **1. Jackson Streaming API란?**
|
||||
**Jackson Streaming API**는 대량의 JSON 데이터를 메모리 효율적으로 처리할 수 있도록 설계된 API다.
|
||||
기본적으로 **이벤트 기반 방식**(push/pull 방식)을 사용하며, JSON을 한 번에 모두 로드하지 않고 **스트림 방식으로 읽고 쓸 수 있다**.
|
||||
|
||||
**✔ 주요 특징:**
|
||||
✅ 대량의 JSON 데이터를 처리할 때 **메모리 사용량이 적다**
|
||||
✅ 기존 `ObjectMapper` 방식보다 **빠른 속도로 JSON을 처리 가능**
|
||||
✅ `JsonParser`와 `JsonGenerator`를 사용하여 **JSON을 읽고 쓸 수 있다**
|
||||
|
||||
---
|
||||
|
||||
## **2. Jackson Streaming API 주요 메서드 정리**
|
||||
|
||||
### **📌 JSON 읽기 (`JsonParser`)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `nextToken()` | 다음 JSON 토큰으로 이동 |
|
||||
| `getCurrentToken()` | 현재 위치의 JSON 토큰 가져오기 |
|
||||
| `getText()` | 현재 위치의 JSON 문자열 값 가져오기 |
|
||||
| `getIntValue()` | 현재 위치의 JSON 정수 값 가져오기 |
|
||||
| `getBooleanValue()` | 현재 위치의 JSON 불리언 값 가져오기 |
|
||||
| `getDoubleValue()` | 현재 위치의 JSON 실수 값 가져오기 |
|
||||
| `getCurrentName()` | 현재 JSON 필드명 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 JSON 쓰기 (`JsonGenerator`)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `writeStartObject()` | JSON 객체 시작 (`{` 추가) |
|
||||
| `writeEndObject()` | JSON 객체 종료 (`}` 추가) |
|
||||
| `writeStartArray()` | JSON 배열 시작 (`[` 추가) |
|
||||
| `writeEndArray()` | JSON 배열 종료 (`]` 추가) |
|
||||
| `writeFieldName(String name)` | JSON 필드명 작성 |
|
||||
| `writeString(String text)` | JSON 문자열 값 작성 |
|
||||
| `writeNumber(int value)` | JSON 숫자 값 작성 |
|
||||
| `writeBoolean(boolean value)` | JSON 불리언 값 작성 |
|
||||
| `writeNull()` | JSON `null` 값 작성 |
|
||||
|
||||
---
|
||||
|
||||
## **3. Jackson Streaming API 예제**
|
||||
|
||||
### **✔ JSON 읽기 (`JsonParser`) 예제**
|
||||
아래 JSON 파일을 스트리밍 방식으로 읽어보자.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "홍길동",
|
||||
"age": 30,
|
||||
"isAdmin": false
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 JSON을 스트리밍 방식으로 읽는 코드**
|
||||
```java
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import java.io.File;
|
||||
|
||||
public class JsonParserExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
JsonFactory factory = new JsonFactory();
|
||||
JsonParser parser = factory.createParser(new File("data.json"));
|
||||
|
||||
String fieldName = null;
|
||||
while (!parser.isClosed()) {
|
||||
JsonToken token = parser.nextToken();
|
||||
|
||||
if (token == null) break;
|
||||
|
||||
if (token.isStructStart() || token.isStructEnd()) {
|
||||
// JSON의 {, } 같은 기호는 스킵
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == JsonToken.FIELD_NAME) {
|
||||
fieldName = parser.getCurrentName();
|
||||
} else if (fieldName != null) {
|
||||
switch (fieldName) {
|
||||
case "name":
|
||||
System.out.println("이름: " + parser.getText());
|
||||
break;
|
||||
case "age":
|
||||
System.out.println("나이: " + parser.getIntValue());
|
||||
break;
|
||||
case "isAdmin":
|
||||
System.out.println("관리자 여부: " + parser.getBooleanValue());
|
||||
break;
|
||||
}
|
||||
fieldName = null;
|
||||
}
|
||||
}
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
이름: 홍길동
|
||||
나이: 30
|
||||
관리자 여부: false
|
||||
```
|
||||
✅ **JSON을 한 줄씩 읽으면서 처리하는 방식이라 메모리 사용량이 적다!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ JSON 쓰기 (`JsonGenerator`) 예제**
|
||||
**🔹 JSON을 스트리밍 방식으로 생성하는 코드**
|
||||
```java
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import java.io.File;
|
||||
|
||||
public class JsonGeneratorExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
JsonFactory factory = new JsonFactory();
|
||||
JsonGenerator generator = factory.createGenerator(new File("output.json"), com.fasterxml.jackson.core.JsonEncoding.UTF8);
|
||||
|
||||
generator.writeStartObject();
|
||||
generator.writeStringField("name", "홍길동");
|
||||
generator.writeNumberField("age", 30);
|
||||
generator.writeBooleanField("isAdmin", false);
|
||||
generator.writeEndObject();
|
||||
|
||||
generator.close();
|
||||
System.out.println("JSON 파일 생성 완료!");
|
||||
}
|
||||
}
|
||||
```
|
||||
**🔹 생성된 `output.json` 파일 내용**
|
||||
```json
|
||||
{
|
||||
"name": "홍길동",
|
||||
"age": 30,
|
||||
"isAdmin": false
|
||||
}
|
||||
```
|
||||
✅ **한 줄씩 JSON을 생성하는 방식이라 메모리 사용량이 적다!**
|
||||
|
||||
---
|
||||
|
||||
## **4. Jackson Streaming API vs 일반 Jackson API**
|
||||
| 비교 항목 | Jackson Streaming API | 일반 Jackson (`ObjectMapper`) |
|
||||
|------------|------------------|----------------|
|
||||
| 데이터 처리 방식 | **스트리밍 방식 (한 줄씩 처리)** | **전체 로딩 방식 (객체 매핑)** |
|
||||
| 메모리 사용량 | **적음 (대량 데이터에 적합)** | 많음 (작은 데이터에 적합) |
|
||||
| 속도 | 빠름 | 비교적 느림 |
|
||||
| 사용 예시 | **대량의 JSON 데이터를 읽고 쓸 때** | **소규모 JSON 데이터를 객체로 변환할 때** |
|
||||
|
||||
---
|
||||
|
||||
## **5. 정리**
|
||||
✔ **Jackson Streaming API**는 JSON을 한 줄씩 읽고 쓰는 방식이라 **대량의 JSON 데이터를 처리할 때 매우 유용**하다.
|
||||
✔ `JsonParser`를 사용하면 JSON을 **이벤트 기반으로 파싱 가능**하다.
|
||||
✔ `JsonGenerator`를 사용하면 JSON을 **메모리 효율적으로 생성 가능**하다.
|
||||
✔ 일반 `ObjectMapper`보다 **메모리 사용량이 적고, 속도가 빠르다!**
|
||||
|
||||
✅ **대량의 JSON 데이터를 처리해야 한다면, Jackson Streaming API를 사용하자!**
|
||||
168
docs/Jackson.md
Normal file
168
docs/Jackson.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# **Jackson 어노테이션 정리 및 예제**
|
||||
|
||||
## **1. Jackson이란?**
|
||||
**Jackson**은 **자바 객체를 JSON으로 변환하거나 JSON을 자바 객체로 변환하는 라이브러리**다.
|
||||
Spring Boot에서도 기본적으로 사용되며, 속도가 빠르고 사용법이 간단하다.
|
||||
|
||||
**✅ Jackson의 주요 기능**
|
||||
✔ **자바 객체 → JSON 변환 (`ObjectMapper.writeValue()`)**
|
||||
✔ **JSON → 자바 객체 변환 (`ObjectMapper.readValue()`)**
|
||||
✔ **JSON 포맷 커스터마이징 (필드 제외, 포맷 변경 등)**
|
||||
|
||||
---
|
||||
|
||||
## **2. Jackson 주요 어노테이션 정리**
|
||||
|
||||
### **📌 직렬화 (객체 → JSON) 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|------------|------|
|
||||
| `@JsonProperty` | 필드의 JSON 속성명을 변경 |
|
||||
| `@JsonIgnore` | 특정 필드를 JSON 변환에서 제외 |
|
||||
| `@JsonIgnoreProperties` | 여러 필드를 JSON 변환에서 제외 |
|
||||
| `@JsonInclude` | `null` 값 또는 기본값을 JSON에서 제외 |
|
||||
| `@JsonGetter` | `getter` 메서드의 JSON 속성명을 변경 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 역직렬화 (JSON → 객체) 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|------------|------|
|
||||
| `@JsonCreator` | JSON 데이터를 객체로 변환할 때 생성자 지정 |
|
||||
| `@JsonSetter` | `setter` 메서드의 JSON 속성명을 변경 |
|
||||
| `@JsonAlias` | 여러 개의 JSON 키를 하나의 필드로 매핑 |
|
||||
| `@JsonIgnoreProperties(ignoreUnknown = true)` | JSON에 없는 필드 무시 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 날짜/시간 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|------------|------|
|
||||
| `@JsonFormat` | 날짜/시간 포맷을 지정 |
|
||||
| `@JsonDeserialize` | 커스텀 역직렬화 지정 |
|
||||
| `@JsonSerialize` | 커스텀 직렬화 지정 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 고급 기능 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|------------|------|
|
||||
| `@JsonUnwrapped` | 중첩 객체의 필드를 부모 객체에 포함 |
|
||||
| `@JsonTypeInfo` | 다형성(Polymorphism) 처리를 위한 타입 정보 추가 |
|
||||
|
||||
---
|
||||
|
||||
## **3. Jackson 어노테이션 예제**
|
||||
|
||||
### **✔ 1) `@JsonProperty` - JSON 속성명 변경**
|
||||
```java
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
class User {
|
||||
@JsonProperty("full_name")
|
||||
private String name;
|
||||
|
||||
private int age;
|
||||
|
||||
public User(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
// Getter & Setter 생략
|
||||
}
|
||||
|
||||
public class JacksonExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
User user = new User("홍길동", 30);
|
||||
|
||||
String json = objectMapper.writeValueAsString(user);
|
||||
System.out.println(json); // 출력: {"full_name":"홍길동","age":30}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **필드명이 `name`이지만, JSON에서는 `full_name`으로 출력됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 2) `@JsonIgnore` - 특정 필드 제외**
|
||||
```java
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
class User {
|
||||
private String name;
|
||||
|
||||
@JsonIgnore
|
||||
private String password; // JSON에서 제외됨
|
||||
|
||||
public User(String name, String password) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
```
|
||||
**🔹 변환 결과**
|
||||
```json
|
||||
{"name":"홍길동"}
|
||||
```
|
||||
✅ **`password` 필드는 JSON에서 제외됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 3) `@JsonInclude` - `null` 값 제외**
|
||||
```java
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
class User {
|
||||
private String name;
|
||||
private String nickname; // null 값일 경우 JSON에서 제외됨
|
||||
|
||||
public User(String name, String nickname) {
|
||||
this.name = name;
|
||||
this.nickname = nickname;
|
||||
}
|
||||
}
|
||||
```
|
||||
**🔹 변환 결과 (`nickname`이 `null`일 때)**
|
||||
```json
|
||||
{"name":"홍길동"}
|
||||
```
|
||||
✅ **`nickname`이 `null`이면 JSON에서 제외됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 4) `@JsonAlias` - 여러 개의 키를 하나의 필드로 매핑**
|
||||
```java
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
|
||||
class User {
|
||||
@JsonAlias({"full_name", "user_name"})
|
||||
private String name;
|
||||
}
|
||||
```
|
||||
✅ **JSON의 `"full_name"` 또는 `"user_name"`이 모두 `name` 필드로 매핑됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 5) `@JsonFormat` - 날짜 포맷 변경**
|
||||
```java
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import java.util.Date;
|
||||
|
||||
class Event {
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date eventDate;
|
||||
}
|
||||
```
|
||||
✅ **날짜를 `"2025-03-06 14:30:00"` 형식으로 변환 가능!**
|
||||
|
||||
---
|
||||
|
||||
## **4. Jackson 정리**
|
||||
✔ **Jackson은 JSON 변환을 위한 강력한 라이브러리**
|
||||
✔ **어노테이션을 활용하면 JSON 변환을 세밀하게 제어 가능**
|
||||
✔ **필드명 변경, 필드 제외, 날짜 포맷 지정 등 다양한 기능 지원**
|
||||
|
||||
✅ **Jackson을 사용하면 JSON 변환이 훨씬 쉽고 간결해짐!**
|
||||
190
docs/Lombok.md
Normal file
190
docs/Lombok.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# **Lombok 어노테이션 정리 및 예제**
|
||||
|
||||
## **1. Lombok이란?**
|
||||
Lombok은 **자바에서 반복적인 코드 작성을 줄여주는 라이브러리**이다.
|
||||
특히, **Getter, Setter, 생성자, toString, equals, hashCode** 등의 메서드를 **자동으로 생성**해준다.
|
||||
|
||||
**✅ Lombok을 사용하면 다음과 같은 장점이 있다.**
|
||||
✔ **코드가 간결해짐** (Getter, Setter, 생성자 등의 반복 코드 제거)
|
||||
✔ **가독성이 향상됨** (핵심 로직만 보이므로 더 쉽게 이해 가능)
|
||||
✔ **컴파일 타임에 코드가 자동 생성됨** (성능 저하 없음)
|
||||
|
||||
---
|
||||
|
||||
## **2. Lombok 주요 어노테이션 정리**
|
||||
|
||||
### **📌 클래스 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|---|---|
|
||||
| `@Getter` | 모든 필드의 Getter 메서드 생성 |
|
||||
| `@Setter` | 모든 필드의 Setter 메서드 생성 |
|
||||
| `@ToString` | `toString()` 메서드 자동 생성 |
|
||||
| `@EqualsAndHashCode` | `equals()` 및 `hashCode()` 자동 생성 |
|
||||
| `@NoArgsConstructor` | 기본 생성자 자동 생성 |
|
||||
| `@AllArgsConstructor` | 모든 필드를 포함한 생성자 자동 생성 |
|
||||
| `@RequiredArgsConstructor` | `final` 또는 `@NonNull` 필드만 포함하는 생성자 생성 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 필드 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|---|---|
|
||||
| `@NonNull` | 필드가 `null`이 될 수 없도록 설정 (생성자에서 `null` 체크) |
|
||||
| `@Value` | 불변(immutable) 클래스를 만들 때 사용 (`@Getter`, `@AllArgsConstructor`, `final` 자동 적용) |
|
||||
|
||||
---
|
||||
|
||||
### **📌 빌더 패턴 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|---|---|
|
||||
| `@Builder` | 빌더 패턴을 자동 생성 |
|
||||
| `@Singular` | `@Builder`에서 컬렉션(List, Set 등)을 초기화할 때 사용 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 로깅 관련 어노테이션**
|
||||
| 어노테이션 | 설명 |
|
||||
|---|---|
|
||||
| `@Slf4j` | `org.slf4j.Logger`를 자동 생성 |
|
||||
| `@Log` | `java.util.logging.Logger`를 자동 생성 |
|
||||
|
||||
---
|
||||
|
||||
## **3. Lombok 어노테이션 예제**
|
||||
|
||||
### **✔ 1) Getter, Setter 자동 생성**
|
||||
```java
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class User {
|
||||
private String name;
|
||||
private int age;
|
||||
}
|
||||
```
|
||||
**🔹 사용 예시**
|
||||
```java
|
||||
User user = new User();
|
||||
user.setName("홍길동");
|
||||
user.setAge(30);
|
||||
System.out.println(user.getName()); // 출력: 홍길동
|
||||
```
|
||||
✅ `getName()`, `setName()`, `getAge()`, `setAge()` 메서드가 자동 생성됨!
|
||||
|
||||
---
|
||||
|
||||
### **✔ 2) `@ToString` 자동 생성**
|
||||
```java
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class User {
|
||||
private String name;
|
||||
private int age;
|
||||
}
|
||||
```
|
||||
**🔹 사용 예시**
|
||||
```java
|
||||
User user = new User();
|
||||
user.setName("홍길동");
|
||||
user.setAge(30);
|
||||
System.out.println(user); // 출력: User(name=홍길동, age=30)
|
||||
```
|
||||
✅ `toString()`을 직접 작성할 필요 없음!
|
||||
|
||||
---
|
||||
|
||||
### **✔ 3) `@NoArgsConstructor`, `@AllArgsConstructor` 자동 생성**
|
||||
```java
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class User {
|
||||
private String name;
|
||||
private int age;
|
||||
}
|
||||
```
|
||||
**🔹 사용 예시**
|
||||
```java
|
||||
User user1 = new User(); // 기본 생성자 사용
|
||||
User user2 = new User("홍길동", 30); // 모든 필드를 받는 생성자 사용
|
||||
```
|
||||
✅ **기본 생성자와 모든 필드를 포함한 생성자가 자동 생성됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 4) `@RequiredArgsConstructor` 사용 (final 필드만 포함)**
|
||||
```java
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class User {
|
||||
private final String name;
|
||||
private int age;
|
||||
}
|
||||
```
|
||||
**🔹 사용 예시**
|
||||
```java
|
||||
User user = new User("홍길동"); // name 필드만 포함된 생성자
|
||||
```
|
||||
✅ **`final` 필드만 포함하는 생성자가 자동 생성됨!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 5) `@Builder`를 이용한 빌더 패턴**
|
||||
```java
|
||||
import lombok.Builder;
|
||||
|
||||
@Builder
|
||||
public class User {
|
||||
private String name;
|
||||
private int age;
|
||||
}
|
||||
```
|
||||
**🔹 사용 예시**
|
||||
```java
|
||||
User user = User.builder()
|
||||
.name("홍길동")
|
||||
.age(30)
|
||||
.build();
|
||||
System.out.println(user);
|
||||
```
|
||||
✅ **빌더 패턴이 자동 생성되어 `User.builder().name("홍길동").age(30).build();`로 객체 생성 가능!**
|
||||
|
||||
---
|
||||
|
||||
### **✔ 6) `@Slf4j`를 이용한 로그 출력**
|
||||
```java
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class LogExample {
|
||||
public static void main(String[] args) {
|
||||
log.info("Hello, Lombok!");
|
||||
}
|
||||
}
|
||||
```
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
INFO - Hello, Lombok!
|
||||
```
|
||||
✅ **Logger 객체를 직접 생성할 필요 없이 `log.info("Hello, Lombok!")`처럼 바로 사용 가능!**
|
||||
|
||||
---
|
||||
|
||||
## **4. Lombok 정리**
|
||||
✔ **Lombok은 자바에서 반복적인 코드 작성을 줄여주는 라이브러리**
|
||||
✔ **Getter, Setter, 생성자, toString, equals, hashCode 등을 자동 생성**
|
||||
✔ **빌더 패턴 및 로깅도 쉽게 적용 가능**
|
||||
✔ **코드를 간결하게 만들고 가독성을 향상시킴**
|
||||
|
||||
✅ **IDE에서 Lombok을 사용하려면?**
|
||||
- **Lombok 라이브러리를 추가** (`pom.xml` 또는 `build.gradle`)
|
||||
- **IntelliJ → Plugins에서 Lombok 플러그인 설치**
|
||||
- **설정에서 "Enable annotation processing" 활성화** (`Settings > Build, Execution, Deployment > Compiler > Annotation Processors`)
|
||||
|
||||
✅ **Lombok을 사용하면 코드가 간결해지고, 유지보수가 편리해짐!**
|
||||
164
docs/Optional.md
Normal file
164
docs/Optional.md
Normal file
@@ -0,0 +1,164 @@
|
||||
## 자바 `Optional` 주요 메서드 정리
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|-----|-----|
|
||||
| `of(T value)` | `null`이 아닌 값을 감싸는 `Optional` 생성 |
|
||||
| `ofNullable(T value)` | `null`일 수도 있는 값을 감싸는 `Optional` 생성 |
|
||||
| `empty()` | 비어 있는 `Optional` 생성 |
|
||||
| `isPresent()` | 값이 존재하면 `true`, 없으면 `false` 반환 |
|
||||
| `isEmpty()` | 값이 없으면 `true`, 있으면 `false` 반환 |
|
||||
| `get()` | 값이 존재하면 반환, 없으면 `NoSuchElementException` 발생 |
|
||||
| `orElse(T other)` | 값이 존재하면 반환, 없으면 기본값 반환 |
|
||||
| `orElseGet(Supplier)` | 값이 존재하면 반환, 없으면 함수 실행 결과 반환 |
|
||||
| `orElseThrow()` | 값이 존재하면 반환, 없으면 예외 발생 |
|
||||
| `or(Supplier)` | 값이 존재하면 현재 `Optional` 반환, 없으면 다른 `Optional` 반환 |
|
||||
| `ifPresent(Consumer)` | 값이 존재하면 실행할 코드 지정 |
|
||||
| `ifPresentOrElse(Consumer, Runnable)` | 값이 존재하면 실행할 코드, 없으면 실행할 코드 지정 |
|
||||
| `map(Function)` | 값을 변환하여 새로운 `Optional` 반환 |
|
||||
| `flatMap(Function)` | 중첩된 `Optional`을 평탄화하여 반환 |
|
||||
| `filter(Predicate)` | 조건을 만족하면 `Optional` 유지, 아니면 비움 |
|
||||
|
||||
---
|
||||
|
||||
## 자바 `Optional` 쉽게 설명하기
|
||||
|
||||
### `Optional`이란?
|
||||
자바에서는 `null`이 자주 등장한다. 하지만 `null`을 잘못 다루면 `NullPointerException`(NPE)이 발생한다.
|
||||
이 문제를 해결하기 위해 나온 것이 **`Optional`(옵셔널)**이다.
|
||||
|
||||
> **"옵셔널은 값이 있을 수도 있고 없을 수도 있는 박스다."**
|
||||
|
||||
즉, **값을 안전하게 감싸고 다룰 수 있도록 도와주는 도구**다.
|
||||
값이 있으면 꺼내서 사용하고, 없으면 `null` 대신 기본값을 사용하거나 예외를 발생시킬 수 있다.
|
||||
|
||||
---
|
||||
|
||||
### `Optional` 사용 예제
|
||||
|
||||
#### 1. `Optional` 생성하기
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice"); // 값이 있는 Optional
|
||||
Optional<String> emptyName = Optional.empty(); // 비어 있는 Optional
|
||||
Optional<String> nullableName = Optional.ofNullable(null); // null 가능
|
||||
```
|
||||
|
||||
- `Optional.of(value)`: `null`이 아닌 값을 감쌈 (null이면 예외 발생)
|
||||
- `Optional.empty()`: 비어 있는 `Optional` 생성
|
||||
- `Optional.ofNullable(value)`: `null`일 수도 있는 값을 감쌈
|
||||
|
||||
---
|
||||
|
||||
#### 2. 값이 있는지 확인하기
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice");
|
||||
|
||||
System.out.println(name.isPresent()); // true
|
||||
System.out.println(name.isEmpty()); // false
|
||||
```
|
||||
- `isPresent()`: 값이 있으면 `true`
|
||||
- `isEmpty()`: 값이 없으면 `true`
|
||||
|
||||
---
|
||||
|
||||
#### 3. 값 가져오기
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice");
|
||||
|
||||
System.out.println(name.get()); // "Alice"
|
||||
```
|
||||
하지만 **값이 없을 때 `get()`을 호출하면 예외가 발생**하므로 주의해야 한다.
|
||||
|
||||
```java
|
||||
Optional<String> emptyName = Optional.empty();
|
||||
System.out.println(emptyName.get()); // NoSuchElementException 발생!
|
||||
```
|
||||
|
||||
그래서 **`orElse()` 또는 `orElseGet()`을 사용하는 것이 안전하다.**
|
||||
|
||||
```java
|
||||
Optional<String> emptyName = Optional.empty();
|
||||
|
||||
String result1 = emptyName.orElse("Unknown"); // 기본값 제공
|
||||
String result2 = emptyName.orElseGet(() -> "Generated Name"); // 함수로 기본값 제공
|
||||
|
||||
System.out.println(result1); // "Unknown"
|
||||
System.out.println(result2); // "Generated Name"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. 값이 없을 때 예외 던지기
|
||||
```java
|
||||
Optional<String> emptyName = Optional.empty();
|
||||
|
||||
String name = emptyName.orElseThrow(() -> new IllegalArgumentException("이름이 없습니다!"));
|
||||
```
|
||||
값이 없으면 `IllegalArgumentException`을 던진다.
|
||||
|
||||
---
|
||||
|
||||
#### 5. 값이 있을 때만 실행하기 (`ifPresent`)
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice");
|
||||
|
||||
name.ifPresent(n -> System.out.println("이름: " + n)); // 이름이 있으면 출력
|
||||
```
|
||||
값이 없으면 아무 일도 일어나지 않는다.
|
||||
|
||||
---
|
||||
|
||||
#### 6. 값이 있으면 실행, 없으면 다른 작업 수행 (`ifPresentOrElse`)
|
||||
```java
|
||||
Optional<String> name = Optional.empty();
|
||||
|
||||
name.ifPresentOrElse(
|
||||
n -> System.out.println("이름: " + n), // 값이 있을 때 실행
|
||||
() -> System.out.println("이름이 없습니다!") // 값이 없을 때 실행
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 7. 값 변환하기 (`map`)
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice");
|
||||
|
||||
Optional<Integer> length = name.map(String::length);
|
||||
|
||||
System.out.println(length.get()); // 5
|
||||
```
|
||||
- `map(Function)`: 값이 있으면 변환하고, 없으면 `Optional.empty()` 유지
|
||||
|
||||
---
|
||||
|
||||
#### 8. 중첩된 `Optional`을 평탄화 (`flatMap`)
|
||||
```java
|
||||
Optional<Optional<String>> nested = Optional.of(Optional.of("Alice"));
|
||||
|
||||
Optional<String> flat = nested.flatMap(n -> n);
|
||||
|
||||
System.out.println(flat.get()); // "Alice"
|
||||
```
|
||||
- `flatMap(Function)`: 중첩된 `Optional`을 단일 `Optional`로 변환
|
||||
|
||||
---
|
||||
|
||||
#### 9. 조건에 맞는 값만 유지하기 (`filter`)
|
||||
```java
|
||||
Optional<String> name = Optional.of("Alice");
|
||||
|
||||
Optional<String> filtered = name.filter(n -> n.startsWith("A"));
|
||||
|
||||
System.out.println(filtered.get()); // "Alice"
|
||||
```
|
||||
- `filter(Predicate)`: 조건을 만족하면 `Optional` 유지, 아니면 `Optional.empty()` 반환
|
||||
|
||||
---
|
||||
|
||||
### `Optional`을 사용하면 좋은 이유
|
||||
1. **`null` 체크가 필요 없음** → `if (value != null)` 같은 코드 제거 가능
|
||||
2. **코드가 더 읽기 쉬움** → `Optional` 메서드를 통해 명확한 의도를 전달
|
||||
3. **안전한 값 처리 가능** → `orElse()`, `ifPresent()` 등을 활용하여 `null`을 안전하게 대체
|
||||
|
||||
즉, **"옵셔널은 값이 있을 수도 있고 없을 수도 있는 안전한 박스"**다.
|
||||
무작정 `null`을 다루는 것보다 `Optional`을 활용하면 **예외 없이 더 깔끔하고 안전한 코드**를 작성할 수 있다.
|
||||
196
docs/Reflection.md
Normal file
196
docs/Reflection.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# **자바 리플렉션(Reflection) 쉽게 배우기**
|
||||
|
||||
## **1. 리플렉션이란?**
|
||||
리플렉션(Reflection)이란 **실행 중에 클래스의 정보를 조회하고, 필드·메서드·생성자를 동적으로 조작하는 기능**이다.
|
||||
즉, **컴파일 시점이 아니라 런타임(실행 중)에 클래스 내부를 들여다보고 조작할 수 있다.**
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `Class` 클래스 (클래스 정보 조회)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `Class.forName("클래스명")` | 클래스 객체 가져오기 (정적 로드) |
|
||||
| `Object.getClass()` | 인스턴스로부터 클래스 객체 얻기 |
|
||||
| `getName()` | 클래스 전체 이름 (`패키지.클래스명`) 반환 |
|
||||
| `getSimpleName()` | 클래스 단순 이름 반환 |
|
||||
| `getDeclaredFields()` | 선언된 모든 필드(`Field[]`) 조회 |
|
||||
| `getDeclaredMethods()` | 선언된 모든 메서드(`Method[]`) 조회 |
|
||||
| `getDeclaredConstructors()` | 선언된 모든 생성자(`Constructor[]`) 조회 |
|
||||
|
||||
**예제 코드 (클래스 정보 출력)**
|
||||
```java
|
||||
Class<?> clazz = Class.forName("java.util.ArrayList");
|
||||
|
||||
System.out.println("클래스 이름: " + clazz.getName());
|
||||
System.out.println("간단한 이름: " + clazz.getSimpleName());
|
||||
System.out.println("패키지: " + clazz.getPackage().getName());
|
||||
```
|
||||
✅ **클래스의 이름과 패키지 정보를 확인할 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Field` 클래스 (필드 정보 조회 및 수정)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `getName()` | 필드 이름 가져오기 |
|
||||
| `getType()` | 필드 타입 가져오기 |
|
||||
| `getModifiers()` | 접근 제어자 가져오기 |
|
||||
| `setAccessible(true)` | private 필드 접근 가능하게 설정 |
|
||||
| `get(Object obj)` | 특정 객체의 필드 값 가져오기 |
|
||||
| `set(Object obj, Object value)` | 특정 객체의 필드 값 변경 |
|
||||
|
||||
**예제 코드 (필드 조회 및 값 변경)**
|
||||
```java
|
||||
import java.lang.reflect.*;
|
||||
|
||||
class Person {
|
||||
private String name = "John";
|
||||
}
|
||||
|
||||
public class ReflectionExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Person person = new Person();
|
||||
Class<?> clazz = person.getClass();
|
||||
|
||||
Field field = clazz.getDeclaredField("name");
|
||||
field.setAccessible(true); // private 접근 허용
|
||||
|
||||
System.out.println("기존 값: " + field.get(person));
|
||||
field.set(person, "Alice"); // 값 변경
|
||||
System.out.println("변경된 값: " + field.get(person));
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **private 필드도 강제로 조작할 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
### **(3) `Method` 클래스 (메서드 정보 조회 및 호출)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `getName()` | 메서드 이름 가져오기 |
|
||||
| `getParameterTypes()` | 메서드 매개변수 타입 가져오기 |
|
||||
| `getReturnType()` | 반환 타입 가져오기 |
|
||||
| `invoke(Object obj, Object... args)` | 메서드 실행 |
|
||||
|
||||
**예제 코드 (메서드 실행)**
|
||||
```java
|
||||
import java.lang.reflect.*;
|
||||
|
||||
class Calculator {
|
||||
private int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReflectionExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Calculator calc = new Calculator();
|
||||
Class<?> clazz = calc.getClass();
|
||||
|
||||
Method method = clazz.getDeclaredMethod("add", int.class, int.class);
|
||||
method.setAccessible(true); // private 접근 허용
|
||||
|
||||
int result = (int) method.invoke(calc, 5, 10); // 메서드 실행
|
||||
System.out.println("결과: " + result);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **private 메서드도 실행할 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
### **(4) `Constructor` 클래스 (생성자 정보 조회 및 인스턴스 생성)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `getParameterTypes()` | 생성자 매개변수 타입 가져오기 |
|
||||
| `newInstance(Object... initargs)` | 새로운 인스턴스 생성 |
|
||||
|
||||
**예제 코드 (객체 동적 생성)**
|
||||
```java
|
||||
import java.lang.reflect.*;
|
||||
|
||||
class Person {
|
||||
private String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReflectionExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Class<?> clazz = Person.class;
|
||||
|
||||
Constructor<?> constructor = clazz.getConstructor(String.class);
|
||||
Object person = constructor.newInstance("Charlie"); // 동적 생성
|
||||
|
||||
Method method = clazz.getMethod("getName");
|
||||
System.out.println("이름: " + method.invoke(person)); // Charlie 출력
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **리플렉션을 이용해 생성자를 실행하고 객체를 만들 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
### **(5) `Modifier` 클래스 (접근 제어자 확인)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `isPublic(int mod)` | public 여부 확인 |
|
||||
| `isPrivate(int mod)` | private 여부 확인 |
|
||||
| `isStatic(int mod)` | static 여부 확인 |
|
||||
|
||||
**예제 코드 (필드 접근 제어자 확인)**
|
||||
```java
|
||||
import java.lang.reflect.*;
|
||||
|
||||
class Sample {
|
||||
private int num;
|
||||
public static String text;
|
||||
}
|
||||
|
||||
public class ReflectionExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Field field = Sample.class.getDeclaredField("text");
|
||||
int modifiers = field.getModifiers();
|
||||
|
||||
System.out.println("static인가? " + Modifier.isStatic(modifiers)); // true
|
||||
System.out.println("public인가? " + Modifier.isPublic(modifiers)); // true
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **필드나 메서드가 `public`, `private`, `static`인지 확인할 수 있다!**
|
||||
|
||||
---
|
||||
|
||||
## **3. 리플렉션의 활용 예시**
|
||||
✔ **DI(의존성 주입) 프레임워크(Spring)** → 리플렉션으로 객체를 생성하고 자동 주입
|
||||
✔ **JUnit 테스트 프레임워크** → `@Test` 붙은 메서드 자동 실행
|
||||
✔ **JSON 라이브러리(Jackson, Gson)** → 객체를 JSON으로 변환할 때 필드 조회
|
||||
✔ **프록시 패턴(AOP, 동적 프록시)** → 런타임에 동적 메서드 실행
|
||||
|
||||
---
|
||||
|
||||
## **4. 리플렉션의 단점과 주의점**
|
||||
❌ **성능 저하** → 리플렉션은 일반 메서드 호출보다 느리다. 자주 사용하면 성능 문제가 생길 수 있다.
|
||||
❌ **보안 문제** → `setAccessible(true)`로 private 필드/메서드에 접근할 수 있어 보안 위험이 있다.
|
||||
❌ **컴파일 타임 체크 불가능** → 오타나 잘못된 메서드 호출은 실행 시점에서야 오류가 발생한다.
|
||||
|
||||
✅ **따라서, 꼭 필요한 경우에만 사용하고 남용하지 않는 것이 좋다!**
|
||||
|
||||
---
|
||||
|
||||
## **5. 정리**
|
||||
✅ **리플렉션은 실행 중 클래스 정보를 조회하고 조작할 수 있는 기능이다!**
|
||||
✅ **`Class`, `Field`, `Method`, `Constructor` 클래스를 활용하면 필드 값 변경, 메서드 실행, 객체 생성 등이 가능하다!**
|
||||
✅ **Spring, JSON 파싱, 테스트 프레임워크 등에서 널리 사용된다!**
|
||||
✅ **하지만 성능 저하와 보안 문제를 고려하여 신중히 사용해야 한다!**
|
||||
|
||||
✔ **리플렉션을 잘 활용하면, 자바 프로그램을 더 유연하게 만들 수 있다!**
|
||||
170
docs/Regex.md
Normal file
170
docs/Regex.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# **자바 정규 표현식(Regex) 쉽게 배우기**
|
||||
|
||||
자바에서는 정규 표현식(Regex)을 **문자열 검색, 패턴 매칭, 텍스트 변환** 등에 사용할 수 있다.
|
||||
정규 표현식을 활용하면 **복잡한 문자열을 간단한 패턴으로 처리**할 수 있다.
|
||||
|
||||
자바에서 정규 표현식을 다룰 때 주로 사용하는 클래스는 `Pattern`과 `Matcher`이다.
|
||||
또한, `String` 클래스에서도 정규 표현식을 지원하는 몇 가지 메서드가 있다.
|
||||
|
||||
---
|
||||
|
||||
## **1. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `Pattern` 클래스 (정규식 컴파일)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `Pattern.compile(String regex)` | 정규식을 컴파일하여 `Pattern` 객체 생성 |
|
||||
| `Pattern.matches(String regex, CharSequence input)` | 전체 문자열이 정규식과 일치하는지 확인 |
|
||||
| `pattern()` | 정규식을 문자열 형태로 반환 |
|
||||
| `split(CharSequence input)` | 정규식을 기준으로 문자열을 분리 |
|
||||
| `matcher(CharSequence input)` | `Matcher` 객체를 생성하여 세부적인 패턴 검색 수행 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
Pattern pattern = Pattern.compile("\\d+"); // 숫자 찾기
|
||||
Matcher matcher = pattern.matcher("abc123");
|
||||
|
||||
System.out.println(matcher.find()); // true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Matcher` 클래스 (패턴 검색)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `find()` | 정규식과 일치하는 부분이 있는지 확인 |
|
||||
| `matches()` | 전체 문자열이 정규식과 일치하는지 확인 |
|
||||
| `lookingAt()` | 문자열의 **시작 부분**이 정규식과 일치하는지 확인 |
|
||||
| `group()` | 최근에 매칭된 문자열을 반환 |
|
||||
| `group(int group)` | 특정 그룹의 매칭 결과를 반환 |
|
||||
| `start()` | 매칭된 문자열의 시작 인덱스 반환 |
|
||||
| `end()` | 매칭된 문자열의 끝 인덱스 반환 |
|
||||
| `replaceAll(String replacement)` | 모든 매칭된 부분을 대체 |
|
||||
| `replaceFirst(String replacement)` | 첫 번째 매칭된 부분만 대체 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
Pattern pattern = Pattern.compile("\\d+");
|
||||
Matcher matcher = pattern.matcher("abc123xyz456");
|
||||
|
||||
while (matcher.find()) {
|
||||
System.out.println("찾은 숫자: " + matcher.group());
|
||||
}
|
||||
```
|
||||
출력 결과:
|
||||
```
|
||||
찾은 숫자: 123
|
||||
찾은 숫자: 456
|
||||
```
|
||||
→ `find()`를 사용하면 여러 번 매칭 가능.
|
||||
|
||||
---
|
||||
|
||||
### **(3) `String` 클래스의 정규 표현식 관련 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `matches(String regex)` | 전체 문자열이 정규식과 일치하는지 확인 |
|
||||
| `split(String regex)` | 정규식을 기준으로 문자열을 분리하여 배열 반환 |
|
||||
| `replaceAll(String regex, String replacement)` | 모든 매칭된 부분을 대체 |
|
||||
| `replaceFirst(String regex, String replacement)` | 첫 번째 매칭된 부분만 대체 |
|
||||
|
||||
**사용 예시:**
|
||||
```java
|
||||
String text = "apple,banana,grape";
|
||||
String[] fruits = text.split(",");
|
||||
|
||||
for (String fruit : fruits) {
|
||||
System.out.println(fruit);
|
||||
}
|
||||
```
|
||||
출력 결과:
|
||||
```
|
||||
apple
|
||||
banana
|
||||
grape
|
||||
```
|
||||
→ `split()`을 사용하면 쉼표(`,`)를 기준으로 문자열을 쉽게 분리할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## **2. 정규 표현식 쉽게 설명하기**
|
||||
|
||||
정규 표현식은 **특정한 패턴을 가진 문자열을 검색하고, 추출하고, 변환할 수 있는 도구**이다.
|
||||
아래는 자주 사용하는 정규 표현식 패턴이다.
|
||||
|
||||
| 정규식 | 설명 | 예제 |
|
||||
|--------|------|------|
|
||||
| `.` | 임의의 한 글자 | `a.b` → "acb", "a1b" (O) / "ab", "acdb" (X) |
|
||||
| `\d` | 숫자 (0-9) | `\d+` → "123", "4567" |
|
||||
| `\D` | 숫자가 아닌 문자 | `\D+` → "abc", "!@#" |
|
||||
| `\w` | 알파벳, 숫자, `_` | `\w+` → "hello_123" |
|
||||
| `\W` | 알파벳, 숫자, `_` 제외한 문자 | `\W+` → "!@#" |
|
||||
| `\s` | 공백 (스페이스, 탭, 줄바꿈) | `\s+` → " " |
|
||||
| `\S` | 공백이 아닌 문자 | `\S+` → "Hello" |
|
||||
| `^` | 문자열의 시작 | `^abc` → "abcdef" (O) / "zabc" (X) |
|
||||
| `$` | 문자열의 끝 | `xyz$` → "abcxyz" (O) / "xyzabc" (X) |
|
||||
| `*` | 0개 이상 반복 | `a*` → "", "a", "aa", "aaaa" |
|
||||
| `+` | 1개 이상 반복 | `a+` → "a", "aa", "aaaa" (O) / "" (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" |
|
||||
|
||||
---
|
||||
|
||||
## **3. 자주 쓰는 정규식 예제**
|
||||
|
||||
### **(1) 이메일 검증**
|
||||
```java
|
||||
String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$";
|
||||
String email = "test@example.com";
|
||||
|
||||
boolean isValid = Pattern.matches(regex, email);
|
||||
System.out.println("이메일 검증: " + isValid); // true
|
||||
```
|
||||
→ 이메일 형식인지 확인할 때 사용.
|
||||
|
||||
---
|
||||
|
||||
### **(2) 전화번호 검증 (010-xxxx-xxxx)**
|
||||
```java
|
||||
String regex = "^010-\\d{4}-\\d{4}$";
|
||||
String phone = "010-1234-5678";
|
||||
|
||||
boolean isValid = Pattern.matches(regex, phone);
|
||||
System.out.println("전화번호 검증: " + isValid); // true
|
||||
```
|
||||
→ `010-xxxx-xxxx` 형식의 전화번호인지 확인.
|
||||
|
||||
---
|
||||
|
||||
### **(3) 숫자만 포함된 문자열 체크**
|
||||
```java
|
||||
String regex = "^\\d+$";
|
||||
String input = "123456";
|
||||
|
||||
boolean isNumber = Pattern.matches(regex, input);
|
||||
System.out.println("숫자만 포함?: " + isNumber); // true
|
||||
```
|
||||
→ 숫자로만 이루어진 문자열인지 검증할 때 사용.
|
||||
|
||||
---
|
||||
|
||||
### **(4) 특정 문자 제거 (`replaceAll()`)**
|
||||
```java
|
||||
String text = "Hello123World";
|
||||
String result = text.replaceAll("\\d", ""); // 숫자 제거
|
||||
|
||||
System.out.println(result); // "HelloWorld"
|
||||
```
|
||||
→ 숫자(`\d`)를 찾아서 제거.
|
||||
|
||||
---
|
||||
|
||||
## **4. 정리**
|
||||
|
||||
✅ **정규 표현식을 사용하면 복잡한 문자열 처리를 간단하게 해결 가능!**
|
||||
✅ **`Pattern` & `Matcher` 클래스를 사용하면 세부적인 패턴 매칭이 가능!**
|
||||
✅ **`String` 클래스에서도 `matches()`, `split()`, `replaceAll()` 등을 활용할 수 있음!**
|
||||
|
||||
즉, **자바의 정규 표현식 API를 잘 활용하면 문자열 검색, 치환, 검증이 훨씬 쉬워진다!**
|
||||
231
docs/Swing.md
Normal file
231
docs/Swing.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# **Java Swing 쉽게 배우기**
|
||||
|
||||
## **1. Swing이란?**
|
||||
**Java Swing**은 **GUI(그래픽 사용자 인터페이스)를 만드는 라이브러리**다.
|
||||
버튼, 창, 메뉴, 입력 필드 등을 쉽게 만들 수 있다.
|
||||
|
||||
**Swing의 특징**
|
||||
✔ **AWT보다 강력한 GUI 제공** (AWT는 운영체제에 의존, Swing은 독립적)
|
||||
✔ **더 많은 UI 컴포넌트 제공** (버튼, 리스트, 테이블, 트리 등)
|
||||
✔ **이식성이 높음** (운영체제에 상관없이 동일한 UI 제공)
|
||||
✔ **이벤트 기반 프로그래밍 가능**
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 Swing 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `JFrame` 클래스 (메인 창)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setSize(int width, int height)` | 창의 크기를 설정 |
|
||||
| `setVisible(boolean b)` | 창을 화면에 표시 (`true`) 또는 숨김 (`false`) |
|
||||
| `setDefaultCloseOperation(int operation)` | 창 닫기 동작 설정 (예: `EXIT_ON_CLOSE`) |
|
||||
| `add(Component comp)` | 창에 컴포넌트 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JPanel` 클래스 (패널 - 여러 컴포넌트를 담는 컨테이너)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `add(Component comp)` | 패널에 컴포넌트 추가 |
|
||||
| `setLayout(LayoutManager mgr)` | 패널의 배치 관리자 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `JButton` 클래스 (버튼)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setText(String text)` | 버튼의 텍스트 설정 |
|
||||
| `addActionListener(ActionListener l)` | 버튼 클릭 이벤트 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(4) `JLabel` 클래스 (텍스트 라벨)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setText(String text)` | 라벨의 텍스트 변경 |
|
||||
| `setIcon(Icon icon)` | 라벨에 이미지 아이콘 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(5) `JTextField` 클래스 (한 줄 입력 필드)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setText(String text)` | 입력 필드에 텍스트 설정 |
|
||||
| `getText()` | 입력된 텍스트 가져오기 |
|
||||
| `addActionListener(ActionListener l)` | 엔터 키 입력 이벤트 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(6) `JTextArea` 클래스 (여러 줄 입력 필드)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setText(String text)` | 입력 필드에 텍스트 설정 |
|
||||
| `getText()` | 입력된 텍스트 가져오기 |
|
||||
| `setLineWrap(boolean wrap)` | 자동 줄바꿈 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(7) `JCheckBox` 클래스 (체크박스)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setSelected(boolean b)` | 체크 상태 설정 |
|
||||
| `isSelected()` | 체크 여부 확인 |
|
||||
|
||||
---
|
||||
|
||||
### **(8) `JRadioButton` 클래스 (라디오 버튼)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setSelected(boolean b)` | 선택 상태 설정 |
|
||||
| `isSelected()` | 선택 여부 확인 |
|
||||
|
||||
---
|
||||
|
||||
### **(9) `JComboBox` 클래스 (드롭다운 목록)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `addItem(Object item)` | 목록에 항목 추가 |
|
||||
| `getSelectedItem()` | 선택된 항목 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **(10) `JList` 클래스 (리스트 박스)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setListData(Object[] data)` | 리스트 아이템 설정 |
|
||||
| `getSelectedValue()` | 선택된 값 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **(11) `JTable` 클래스 (테이블)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `setModel(TableModel model)` | 테이블 데이터 설정 |
|
||||
| `getValueAt(int row, int column)` | 특정 위치의 값 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **(12) `JMenuBar`, `JMenu`, `JMenuItem` 클래스 (메뉴)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `JMenuBar.add(JMenu menu)` | 메뉴바에 메뉴 추가 |
|
||||
| `JMenu.add(JMenuItem item)` | 메뉴에 항목 추가 |
|
||||
|
||||
---
|
||||
|
||||
## **3. 간단한 Swing 예제**
|
||||
### **✔ 기본 창 만들기 (`JFrame`)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
|
||||
public class SwingExample {
|
||||
public static void main(String[] args) {
|
||||
// 프레임 생성
|
||||
JFrame frame = new JFrame("Swing 기본 창");
|
||||
frame.setSize(400, 300);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
// 창을 보이게 설정
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ `JFrame`을 사용하여 **기본적인 창을 생성**하는 코드다.
|
||||
✅ `setSize()`로 크기 설정, `setVisible(true)`로 창을 표시한다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ 버튼 추가 및 이벤트 처리 (`JButton`)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
public class ButtonExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("버튼 예제");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JButton button = new JButton("클릭하세요!");
|
||||
button.addActionListener(e -> JOptionPane.showMessageDialog(null, "버튼이 눌렸습니다!"));
|
||||
|
||||
frame.add(button);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ `JButton`을 추가하고, **버튼 클릭 이벤트를 처리**하는 코드다.
|
||||
✅ `JOptionPane.showMessageDialog()`를 사용해 **팝업 메시지 출력**이 가능하다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ 텍스트 입력 필드 (`JTextField`)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
|
||||
public class TextFieldExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("텍스트 입력 예제");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JTextField textField = new JTextField(20);
|
||||
JButton button = new JButton("확인");
|
||||
|
||||
button.addActionListener(e -> JOptionPane.showMessageDialog(null, "입력한 값: " + textField.getText()));
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.add(textField);
|
||||
panel.add(button);
|
||||
|
||||
frame.add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ `JTextField`를 사용하여 **사용자로부터 입력을 받는 GUI**다.
|
||||
✅ 버튼 클릭 시 **입력된 값이 팝업으로 출력**된다.
|
||||
|
||||
---
|
||||
|
||||
### **✔ 체크박스 (`JCheckBox`)**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
|
||||
public class CheckBoxExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("체크박스 예제");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JCheckBox checkBox = new JCheckBox("동의합니다.");
|
||||
JButton button = new JButton("확인");
|
||||
|
||||
button.addActionListener(e -> {
|
||||
if (checkBox.isSelected()) {
|
||||
JOptionPane.showMessageDialog(null, "체크됨!");
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "체크 안됨!");
|
||||
}
|
||||
});
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.add(checkBox);
|
||||
panel.add(button);
|
||||
|
||||
frame.add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **체크박스를 추가하고 상태를 확인**하는 코드다.
|
||||
|
||||
---
|
||||
|
||||
## **4. 정리**
|
||||
✔ **Swing은 Java에서 GUI를 만들기 위한 라이브러리!**
|
||||
✔ **버튼, 입력 필드, 체크박스, 리스트, 테이블 등 다양한 UI 제공**
|
||||
✔ **이벤트 리스너를 통해 사용자 입력 처리 가능**
|
||||
|
||||
✅ **Swing을 활용하면 간단한 GUI 프로그램부터 복잡한 데스크톱 애플리케이션까지 개발 가능!**
|
||||
213
docs/XML DOM Parser.md
Normal file
213
docs/XML DOM Parser.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# **XML DOM Parser 쉽게 배우기**
|
||||
|
||||
## **1. XML DOM Parser란?**
|
||||
DOM(Document Object Model) Parser는 XML 문서를 **트리 구조의 객체 모델**로 메모리에 로드하여 다루는 방식이다.
|
||||
XML 데이터를 **읽고, 수정하고, 추가하고, 삭제**할 수 있다.
|
||||
|
||||
✔ **장점**:
|
||||
- 문서를 **전체 로드**하므로 **빠른 검색 및 수정 가능**
|
||||
- XML을 **객체처럼 다룰 수 있음**
|
||||
|
||||
✔ **단점**:
|
||||
- **메모리 사용량이 큼** (큰 XML을 처리할 때 부담)
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 메서드 정리**
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|-----|----|
|
||||
| `DocumentBuilderFactory.newInstance()` | `DocumentBuilderFactory` 객체 생성 |
|
||||
| `DocumentBuilderFactory.setNamespaceAware(true)` | 네임스페이스를 인식하도록 설정 |
|
||||
| `DocumentBuilderFactory.setIgnoringElementContentWhitespace(true)` | 공백 무시 |
|
||||
| `DocumentBuilder.newDocumentBuilder()` | `DocumentBuilder` 객체 생성 |
|
||||
| `DocumentBuilder.parse(File file)` | XML 파일을 `Document` 객체로 변환 |
|
||||
| `Document.getDocumentElement()` | XML의 루트 요소 가져오기 |
|
||||
| `Document.getElementsByTagName(String tag)` | 특정 태그 이름으로 요소 리스트 가져오기 |
|
||||
| `Node.getNodeName()` | 노드의 이름 반환 |
|
||||
| `Node.getTextContent()` | 노드의 텍스트 내용 반환 |
|
||||
| `Node.getAttributes()` | 노드의 속성 반환 |
|
||||
| `Element.getAttribute(String name)` | 특정 속성 값 가져오기 |
|
||||
| `Element.setAttribute(String name, String value)` | 속성 추가 또는 변경 |
|
||||
| `Element.appendChild(Node node)` | 하위 노드 추가 |
|
||||
| `NodeList.getLength()` | `NodeList`의 크기 반환 |
|
||||
| `NodeList.item(int index)` | `NodeList`에서 특정 인덱스의 노드 반환 |
|
||||
| `TransformerFactory.newInstance()` | `TransformerFactory` 객체 생성 |
|
||||
| `TransformerFactory.newTransformer()` | `Transformer` 객체 생성 |
|
||||
| `Transformer.transform(Source, Result)` | XML을 파일 또는 콘솔에 출력 |
|
||||
|
||||
---
|
||||
|
||||
## **3. XML 문서 예제**
|
||||
아래 XML을 사용하여 실습해보자.
|
||||
|
||||
```xml
|
||||
<books>
|
||||
<book id="1">
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000</price>
|
||||
</book>
|
||||
<book id="2">
|
||||
<title>데이터베이스 개론</title>
|
||||
<author>이몽룡</author>
|
||||
<price>25000</price>
|
||||
</book>
|
||||
</books>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. XML 읽기 (Parsing)**
|
||||
|
||||
### **✔ XML을 읽어서 출력하기**
|
||||
아래 코드는 XML을 `Document` 객체로 로드하고, `NodeList`를 이용하여 책 정보를 출력한다.
|
||||
|
||||
```java
|
||||
import org.w3c.dom.*;
|
||||
import javax.xml.parsers.*;
|
||||
import java.io.*;
|
||||
|
||||
public class XMLParserExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 1. XML 파일을 로드하여 Document 객체로 변환
|
||||
File xmlFile = new File("books.xml");
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document document = builder.parse(xmlFile);
|
||||
|
||||
// 2. 루트 요소 가져오기
|
||||
Element root = document.getDocumentElement();
|
||||
System.out.println("루트 요소: " + root.getNodeName());
|
||||
|
||||
// 3. 모든 book 요소 가져오기
|
||||
NodeList bookList = document.getElementsByTagName("book");
|
||||
|
||||
for (int i = 0; i < bookList.getLength(); i++) {
|
||||
Node bookNode = bookList.item(i);
|
||||
|
||||
if (bookNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element book = (Element) bookNode;
|
||||
|
||||
// 속성 값 가져오기
|
||||
String id = book.getAttribute("id");
|
||||
|
||||
// 하위 요소 가져오기
|
||||
String title = book.getElementsByTagName("title").item(0).getTextContent();
|
||||
String author = book.getElementsByTagName("author").item(0).getTextContent();
|
||||
String price = book.getElementsByTagName("price").item(0).getTextContent();
|
||||
|
||||
System.out.println("책 ID: " + id);
|
||||
System.out.println("제목: " + title);
|
||||
System.out.println("저자: " + author);
|
||||
System.out.println("가격: " + price);
|
||||
System.out.println("----");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
루트 요소: books
|
||||
책 ID: 1
|
||||
제목: 자바 프로그래밍
|
||||
저자: 홍길동
|
||||
가격: 30000
|
||||
----
|
||||
책 ID: 2
|
||||
제목: 데이터베이스 개론
|
||||
저자: 이몽룡
|
||||
가격: 25000
|
||||
----
|
||||
```
|
||||
|
||||
✅ `DocumentBuilder.parse(xmlFile)`로 XML을 **Document 객체로 변환**
|
||||
✅ `getElementsByTagName("book")`으로 **모든 `<book>` 요소 가져오기**
|
||||
✅ `getAttribute("id")`로 **속성 값 읽기**
|
||||
✅ `getElementsByTagName("title").item(0).getTextContent()`로 **텍스트 값 가져오기**
|
||||
|
||||
---
|
||||
|
||||
## **5. XML 수정 (노드 추가, 수정, 삭제)**
|
||||
|
||||
### **✔ 새로운 `<book>` 요소 추가하기**
|
||||
|
||||
```java
|
||||
import javax.xml.transform.*;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
public class XMLAddElementExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
File xmlFile = new File("books.xml");
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document document = builder.parse(xmlFile);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
|
||||
// 새로운 book 요소 생성
|
||||
Element newBook = document.createElement("book");
|
||||
newBook.setAttribute("id", "3");
|
||||
|
||||
Element title = document.createElement("title");
|
||||
title.setTextContent("네트워크 프로그래밍");
|
||||
Element author = document.createElement("author");
|
||||
author.setTextContent("강감찬");
|
||||
Element price = document.createElement("price");
|
||||
price.setTextContent("35000");
|
||||
|
||||
// 새 book 요소에 자식 노드 추가
|
||||
newBook.appendChild(title);
|
||||
newBook.appendChild(author);
|
||||
newBook.appendChild(price);
|
||||
|
||||
// 루트 노드에 새로운 book 추가
|
||||
root.appendChild(newBook);
|
||||
|
||||
// 변경된 내용을 파일에 저장
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.transform(new DOMSource(document), new StreamResult(new File("books.xml")));
|
||||
|
||||
System.out.println("새로운 책 추가 완료!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 후 XML 파일 (`books.xml`)**
|
||||
```xml
|
||||
<books>
|
||||
<book id="1">
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000</price>
|
||||
</book>
|
||||
<book id="2">
|
||||
<title>데이터베이스 개론</title>
|
||||
<author>이몽룡</author>
|
||||
<price>25000</price>
|
||||
</book>
|
||||
<book id="3">
|
||||
<title>네트워크 프로그래밍</title>
|
||||
<author>강감찬</author>
|
||||
<price>35000</price>
|
||||
</book>
|
||||
</books>
|
||||
```
|
||||
|
||||
✅ **새로운 `<book>` 요소를 추가하고 XML 파일을 업데이트**
|
||||
|
||||
---
|
||||
|
||||
## **6. 정리**
|
||||
✔ **DOM Parser는 XML을 메모리에 로드하여 트리 구조로 다룬다.**
|
||||
✔ `DocumentBuilder`를 사용해 XML을 `Document` 객체로 변환
|
||||
✔ `getElementsByTagName()`으로 특정 태그의 노드 가져오기
|
||||
✔ `appendChild()`로 새로운 노드 추가 가능
|
||||
✔ `Transformer`를 사용하여 변경 내용을 XML 파일로 저장
|
||||
|
||||
✅ **XML을 다룰 때, 읽기/수정/추가가 필요한 경우 DOM Parser가 매우 유용하다!**
|
||||
187
docs/XML SAX Parser.md
Normal file
187
docs/XML SAX Parser.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# **XML SAX Parser 쉽게 배우기**
|
||||
|
||||
## **1. SAX Parser란?**
|
||||
SAX(Simple API for XML) Parser는 XML 문서를 **한 줄씩 읽으며 이벤트 기반으로 처리하는 방식**이다.
|
||||
|
||||
✔ **장점**:
|
||||
- XML 문서를 **메모리에 전부 로드하지 않음 → 메모리 효율적**
|
||||
- **빠르게 읽기 가능**
|
||||
|
||||
✔ **단점**:
|
||||
- **문서 내 특정 요소를 수정하거나 추가하기 어려움**
|
||||
- **XML을 트리 형태로 다루지 않음 → 앞에서 읽은 데이터 유지 불가**
|
||||
|
||||
**➡ 적절한 사용 사례:**
|
||||
- **대용량 XML 파일을 빠르게 읽을 때**
|
||||
- **읽기 전용(XML을 수정할 필요가 없는 경우)**
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 메서드 정리**
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|-----|----|
|
||||
| `SAXParserFactory.newInstance()` | `SAXParserFactory` 객체 생성 |
|
||||
| `SAXParserFactory.setNamespaceAware(true)` | 네임스페이스 인식 설정 |
|
||||
| `SAXParserFactory.setValidating(true)` | XML 유효성 검사 설정 |
|
||||
| `SAXParserFactory.newSAXParser()` | `SAXParser` 객체 생성 |
|
||||
| `SAXParser.parse(File file, DefaultHandler handler)` | XML 파일을 읽고 핸들러로 전달 |
|
||||
| `DefaultHandler.startDocument()` | 문서의 시작을 처리 |
|
||||
| `DefaultHandler.endDocument()` | 문서의 끝을 처리 |
|
||||
| `DefaultHandler.startElement(String uri, String localName, String qName, Attributes attributes)` | 요소의 시작을 처리 |
|
||||
| `DefaultHandler.endElement(String uri, String localName, String qName)` | 요소의 끝을 처리 |
|
||||
| `DefaultHandler.characters(char[] ch, int start, int length)` | 요소의 텍스트 데이터를 처리 |
|
||||
|
||||
---
|
||||
|
||||
## **3. XML 문서 예제**
|
||||
|
||||
아래 XML 문서를 이용하여 SAX Parser를 적용해 보자.
|
||||
|
||||
```xml
|
||||
<books>
|
||||
<book id="1">
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000</price>
|
||||
</book>
|
||||
<book id="2">
|
||||
<title>데이터베이스 개론</title>
|
||||
<author>이몽룡</author>
|
||||
<price>25000</price>
|
||||
</book>
|
||||
</books>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. SAX Parser로 XML 읽기**
|
||||
|
||||
### **✔ SAX Parser를 사용하여 XML 데이터 출력하기**
|
||||
아래 코드는 `SAXParser`를 사용하여 XML 파일을 읽고, 책 정보를 출력하는 예제이다.
|
||||
|
||||
```java
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.parsers.*;
|
||||
import java.io.File;
|
||||
|
||||
public class SAXParserExample {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 1. SAXParserFactory 생성
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser saxParser = factory.newSAXParser();
|
||||
|
||||
// 2. 핸들러 설정
|
||||
DefaultHandler handler = new DefaultHandler() {
|
||||
boolean isTitle = false;
|
||||
boolean isAuthor = false;
|
||||
boolean isPrice = false;
|
||||
|
||||
// 문서의 시작
|
||||
public void startDocument() {
|
||||
System.out.println("XML 파싱 시작");
|
||||
}
|
||||
|
||||
// 문서의 끝
|
||||
public void endDocument() {
|
||||
System.out.println("XML 파싱 종료");
|
||||
}
|
||||
|
||||
// 요소의 시작 태그
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) {
|
||||
if (qName.equalsIgnoreCase("book")) {
|
||||
System.out.println("\n책 ID: " + attributes.getValue("id"));
|
||||
} else if (qName.equalsIgnoreCase("title")) {
|
||||
isTitle = true;
|
||||
} else if (qName.equalsIgnoreCase("author")) {
|
||||
isAuthor = true;
|
||||
} else if (qName.equalsIgnoreCase("price")) {
|
||||
isPrice = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 요소의 끝 태그
|
||||
public void endElement(String uri, String localName, String qName) {
|
||||
if (qName.equalsIgnoreCase("title")) {
|
||||
isTitle = false;
|
||||
} else if (qName.equalsIgnoreCase("author")) {
|
||||
isAuthor = false;
|
||||
} else if (qName.equalsIgnoreCase("price")) {
|
||||
isPrice = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 요소 안의 텍스트 처리
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String text = new String(ch, start, length).trim();
|
||||
if (!text.isEmpty()) {
|
||||
if (isTitle) {
|
||||
System.out.println("제목: " + text);
|
||||
} else if (isAuthor) {
|
||||
System.out.println("저자: " + text);
|
||||
} else if (isPrice) {
|
||||
System.out.println("가격: " + text);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 3. XML 파일 파싱
|
||||
File xmlFile = new File("books.xml");
|
||||
saxParser.parse(xmlFile, handler);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
XML 파싱 시작
|
||||
|
||||
책 ID: 1
|
||||
제목: 자바 프로그래밍
|
||||
저자: 홍길동
|
||||
가격: 30000
|
||||
|
||||
책 ID: 2
|
||||
제목: 데이터베이스 개론
|
||||
저자: 이몽룡
|
||||
가격: 25000
|
||||
|
||||
XML 파싱 종료
|
||||
```
|
||||
|
||||
✅ `startElement()`에서 태그 시작을 감지
|
||||
✅ `characters()`에서 태그 안의 텍스트를 처리
|
||||
✅ `endElement()`에서 태그 종료를 감지
|
||||
|
||||
---
|
||||
|
||||
## **5. SAX vs DOM 비교**
|
||||
|
||||
| 특징 | SAX Parser | DOM Parser |
|
||||
|---|-----|-----|
|
||||
| **메모리 사용량** | 적음 | 많음 |
|
||||
| **속도** | 빠름 (한 줄씩 읽음) | 느림 (전체 로드) |
|
||||
| **트리 구조 지원** | ❌ | ✅ |
|
||||
| **수정 가능 여부** | ❌ (읽기 전용) | ✅ |
|
||||
| **사용 사례** | 대용량 XML 읽기 | XML을 수정할 때 |
|
||||
|
||||
✅ **DOM Parser는 XML을 객체처럼 다루는 데 유리**
|
||||
✅ **SAX Parser는 대용량 XML을 읽기만 할 때 유리**
|
||||
|
||||
---
|
||||
|
||||
## **6. 정리**
|
||||
✔ **SAX Parser는 이벤트 기반으로 XML을 한 줄씩 읽으며 처리**
|
||||
✔ `startElement()`와 `characters()`를 이용해 데이터를 가져올 수 있음
|
||||
✔ **대용량 XML을 처리할 때 유용하지만, 수정 기능이 없음**
|
||||
✔ **DOM Parser와의 차이를 이해하고 적절한 방식 선택**
|
||||
|
||||
✅ **읽기 전용 & 성능이 중요한 경우 SAX Parser를 사용하자!**
|
||||
239
docs/XML StAX Parser.md
Normal file
239
docs/XML StAX Parser.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# **XML StAX Parser 쉽게 배우기**
|
||||
|
||||
## **1. StAX Parser란?**
|
||||
StAX (Streaming API for XML) Parser는 XML 문서를 읽고 쓰는 **스트리밍 방식의 API**이다.
|
||||
DOM이 **전체 XML을 메모리에 로드**하고, SAX가 **이벤트 기반으로 읽기만 가능**한 것과 다르게,
|
||||
StAX는 **"Pull 방식"** 으로 데이터를 직접 가져올 수 있어 **더 유연하게 읽고 쓸 수 있음**.
|
||||
|
||||
✔ **장점:**
|
||||
- **필요한 데이터만 직접 가져올 수 있음 (Pull 방식)**
|
||||
- **SAX보다 코드가 더 직관적**
|
||||
- **읽기(Parsing)뿐만 아니라 XML 파일 쓰기(XMLStreamWriter)도 지원**
|
||||
|
||||
✔ **단점:**
|
||||
- **트리 구조 접근 불가 (DOM처럼 노드를 조작할 수 없음)**
|
||||
- **SAX보다 약간 무겁지만 여전히 DOM보다는 가벼움**
|
||||
|
||||
**➡ 적절한 사용 사례:**
|
||||
- **대용량 XML 파일에서 특정 데이터만 가져올 때**
|
||||
- **XML을 읽을 뿐만 아니라, 새로운 XML을 생성해야 할 때**
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 메서드 정리**
|
||||
|
||||
### **📌 XMLStreamReader (읽기)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `XMLInputFactory.newInstance()` | `XMLStreamReader` 객체를 생성하는 팩토리 메서드 |
|
||||
| `XMLStreamReader.next()` | 다음 XML 요소로 이동 |
|
||||
| `XMLStreamReader.hasNext()` | 다음 요소가 있는지 확인 |
|
||||
| `XMLStreamReader.getEventType()` | 현재 XML 이벤트 유형 반환 |
|
||||
| `XMLStreamReader.getLocalName()` | 현재 태그의 이름 반환 |
|
||||
| `XMLStreamReader.getAttributeValue(int index)` | 특정 인덱스의 속성 값 반환 |
|
||||
| `XMLStreamReader.getText()` | 현재 태그의 텍스트 값 반환 |
|
||||
| `XMLStreamReader.close()` | XML 리더 닫기 |
|
||||
|
||||
---
|
||||
|
||||
### **📌 XMLStreamWriter (쓰기)**
|
||||
| 메서드 | 설명 |
|
||||
|-----|---|
|
||||
| `XMLOutputFactory.newInstance()` | `XMLStreamWriter` 객체를 생성하는 팩토리 메서드 |
|
||||
| `XMLStreamWriter.writeStartDocument()` | XML 문서 시작 선언 |
|
||||
| `XMLStreamWriter.writeStartElement(String name)` | XML 태그 시작 |
|
||||
| `XMLStreamWriter.writeAttribute(String name, String value)` | 태그에 속성 추가 |
|
||||
| `XMLStreamWriter.writeCharacters(String text)` | 태그 안에 텍스트 추가 |
|
||||
| `XMLStreamWriter.writeEndElement()` | 현재 태그 종료 |
|
||||
| `XMLStreamWriter.writeEndDocument()` | XML 문서 종료 |
|
||||
| `XMLStreamWriter.close()` | XML 작성기 닫기 |
|
||||
|
||||
---
|
||||
|
||||
## **3. XML 문서 예제**
|
||||
|
||||
아래 XML 파일을 **StAX Parser를 사용하여 읽고 출력**해 보자.
|
||||
|
||||
```xml
|
||||
<books>
|
||||
<book id="1">
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000</price>
|
||||
</book>
|
||||
<book id="2">
|
||||
<title>데이터베이스 개론</title>
|
||||
<author>이몽룡</author>
|
||||
<price>25000</price>
|
||||
</book>
|
||||
</books>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. StAX Parser로 XML 읽기 (Pull 방식)**
|
||||
|
||||
### **✔ XMLStreamReader를 사용하여 XML 데이터 출력하기**
|
||||
아래 코드는 `StAXParser`를 사용하여 XML 파일을 읽고, 책 정보를 출력하는 예제이다.
|
||||
|
||||
```java
|
||||
import javax.xml.stream.*;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
public class StAXParserExample {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 1. XMLStreamReader 생성
|
||||
XMLInputFactory factory = XMLInputFactory.newInstance();
|
||||
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("books.xml"));
|
||||
|
||||
// 2. XML 데이터 읽기
|
||||
String currentElement = "";
|
||||
while (reader.hasNext()) {
|
||||
int event = reader.next();
|
||||
|
||||
// 요소의 시작
|
||||
if (event == XMLStreamReader.START_ELEMENT) {
|
||||
currentElement = reader.getLocalName();
|
||||
|
||||
// <book> 태그의 id 속성 읽기
|
||||
if (currentElement.equals("book")) {
|
||||
System.out.println("\n책 ID: " + reader.getAttributeValue(0));
|
||||
}
|
||||
}
|
||||
// 요소 안의 텍스트 읽기
|
||||
else if (event == XMLStreamReader.CHARACTERS) {
|
||||
String text = reader.getText().trim();
|
||||
if (!text.isEmpty()) {
|
||||
switch (currentElement) {
|
||||
case "title":
|
||||
System.out.println("제목: " + text);
|
||||
break;
|
||||
case "author":
|
||||
System.out.println("저자: " + text);
|
||||
break;
|
||||
case "price":
|
||||
System.out.println("가격: " + text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 실행 결과**
|
||||
```
|
||||
책 ID: 1
|
||||
제목: 자바 프로그래밍
|
||||
저자: 홍길동
|
||||
가격: 30000
|
||||
|
||||
책 ID: 2
|
||||
제목: 데이터베이스 개론
|
||||
저자: 이몽룡
|
||||
가격: 25000
|
||||
```
|
||||
|
||||
✅ `reader.next()`를 이용해 **한 단계씩 이동하며 직접 데이터를 가져옴**
|
||||
✅ `reader.getLocalName()`을 이용해 현재 태그 이름 확인
|
||||
✅ `reader.getText()`로 태그 안의 텍스트 값 가져오기
|
||||
|
||||
---
|
||||
|
||||
## **5. StAX Parser로 XML 쓰기 (쓰기 예제)**
|
||||
|
||||
XML 데이터를 **파일로 저장하는 경우** `XMLStreamWriter`를 사용한다.
|
||||
|
||||
### **✔ XMLStreamWriter를 사용하여 XML 파일 생성**
|
||||
```java
|
||||
import javax.xml.stream.*;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
public class StAXWriterExample {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 1. XMLStreamWriter 생성
|
||||
XMLOutputFactory factory = XMLOutputFactory.newInstance();
|
||||
XMLStreamWriter writer = factory.createXMLStreamWriter(new FileOutputStream("new_books.xml"), "UTF-8");
|
||||
|
||||
// 2. XML 문서 작성
|
||||
writer.writeStartDocument("UTF-8", "1.0");
|
||||
writer.writeStartElement("books");
|
||||
|
||||
// 첫 번째 책
|
||||
writer.writeStartElement("book");
|
||||
writer.writeAttribute("id", "1");
|
||||
|
||||
writer.writeStartElement("title");
|
||||
writer.writeCharacters("자바 프로그래밍");
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeStartElement("author");
|
||||
writer.writeCharacters("홍길동");
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeStartElement("price");
|
||||
writer.writeCharacters("30000");
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeEndElement(); // book 종료
|
||||
|
||||
// XML 종료
|
||||
writer.writeEndElement(); // books 종료
|
||||
writer.writeEndDocument();
|
||||
writer.close();
|
||||
|
||||
System.out.println("XML 파일 생성 완료!");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔹 생성된 XML 파일 (new_books.xml)**
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<books>
|
||||
<book id="1">
|
||||
<title>자바 프로그래밍</title>
|
||||
<author>홍길동</author>
|
||||
<price>30000</price>
|
||||
</book>
|
||||
</books>
|
||||
```
|
||||
|
||||
✅ `writeStartElement()`를 이용해 태그 생성
|
||||
✅ `writeCharacters()`로 태그 안의 텍스트 추가
|
||||
✅ `writeAttribute()`를 이용해 속성 추가
|
||||
|
||||
---
|
||||
|
||||
## **6. StAX vs SAX vs DOM 비교**
|
||||
|
||||
| 특징 | StAX Parser | SAX Parser | DOM Parser |
|
||||
|---|-----|-----|-----|
|
||||
| **메모리 사용량** | 적음 | 적음 | 많음 |
|
||||
| **속도** | 빠름 | 빠름 | 느림 |
|
||||
| **데이터 접근 방식** | Pull 방식 | Push 방식 | 트리 기반 |
|
||||
| **읽기** | ✅ | ✅ | ✅ |
|
||||
| **쓰기** | ✅ | ❌ | ✅ |
|
||||
| **수정** | ❌ | ❌ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## **7. 정리**
|
||||
✔ **StAX Parser는 Pull 방식으로 XML을 읽고 쓸 수 있는 API**
|
||||
✔ **필요한 데이터만 직접 가져올 수 있어 SAX보다 유연함**
|
||||
✔ **쓰기 기능(XMLStreamWriter)을 지원하므로 XML 생성에도 적합**
|
||||
✔ **대용량 XML을 다룰 때 효율적인 선택**
|
||||
|
||||
✅ **읽기 & 쓰기가 필요한 경우 StAX Parser를 사용하자!**
|
||||
201
docs/io.md
Normal file
201
docs/io.md
Normal file
@@ -0,0 +1,201 @@
|
||||
## 자바 IO (Input/Output) 관련 클래스 및 주요 메서드 정리
|
||||
|
||||
자바 IO는 **데이터를 스트림(Stream) 기반으로 읽고 쓰는 전통적인 입출력 API**다.
|
||||
주로 **파일, 콘솔, 네트워크, 메모리 등과 데이터를 주고받는 데 사용**된다.
|
||||
|
||||
---
|
||||
|
||||
### 1. `InputStream` (바이트 입력 스트림)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `read()` | 1바이트를 읽고 정수(0~255)로 반환, 끝이면 -1 반환 |
|
||||
| `read(byte[] b)` | 바이트 배열에 데이터를 읽음 |
|
||||
| `available()` | 읽을 수 있는 바이트 수 반환 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
**주요 하위 클래스:**
|
||||
- `FileInputStream` (파일에서 바이트 읽기)
|
||||
- `ByteArrayInputStream` (바이트 배열에서 읽기)
|
||||
- `BufferedInputStream` (버퍼링으로 성능 향상)
|
||||
|
||||
---
|
||||
|
||||
### 2. `OutputStream` (바이트 출력 스트림)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `write(int b)` | 1바이트 쓰기 |
|
||||
| `write(byte[] b)` | 바이트 배열을 스트림에 쓰기 |
|
||||
| `flush()` | 버퍼에 있는 데이터를 즉시 출력 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
**주요 하위 클래스:**
|
||||
- `FileOutputStream` (파일에 바이트 쓰기)
|
||||
- `ByteArrayOutputStream` (바이트 배열에 쓰기)
|
||||
- `BufferedOutputStream` (버퍼링으로 성능 향상)
|
||||
|
||||
---
|
||||
|
||||
### 3. `Reader` (문자 입력 스트림)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `read()` | 문자 하나 읽기 |
|
||||
| `read(char[] cbuf)` | 문자 배열에 읽기 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
**주요 하위 클래스:**
|
||||
- `FileReader` (파일에서 문자 읽기)
|
||||
- `BufferedReader` (버퍼링으로 성능 향상)
|
||||
- `StringReader` (문자열에서 읽기)
|
||||
|
||||
---
|
||||
|
||||
### 4. `Writer` (문자 출력 스트림)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `write(int c)` | 문자 하나 쓰기 |
|
||||
| `write(char[] cbuf)` | 문자 배열을 스트림에 쓰기 |
|
||||
| `flush()` | 버퍼 비우기 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
**주요 하위 클래스:**
|
||||
- `FileWriter` (파일에 문자 쓰기)
|
||||
- `BufferedWriter` (버퍼링으로 성능 향상)
|
||||
- `StringWriter` (문자열에 쓰기)
|
||||
|
||||
---
|
||||
|
||||
### 5. `File` 클래스 (파일 및 디렉토리 관리)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `exists()` | 파일이 존재하는지 확인 |
|
||||
| `createNewFile()` | 새 파일 생성 |
|
||||
| `delete()` | 파일 삭제 |
|
||||
| `mkdir()` | 새 디렉토리 생성 |
|
||||
| `listFiles()` | 디렉토리 내 파일 목록 반환 |
|
||||
|
||||
---
|
||||
|
||||
### 6. `BufferedReader`와 `BufferedWriter` (성능 향상)
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `readLine()` | 한 줄씩 읽기 (`BufferedReader` 전용) |
|
||||
| `write(String s)` | 문자열 출력 (`BufferedWriter` 전용) |
|
||||
| `flush()` | 버퍼 비우기 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
---
|
||||
|
||||
### 자바 IO 쉽게 설명하기
|
||||
|
||||
자바의 IO는 데이터를 **스트림(Stream) 기반으로 입출력**하는 방식이다.
|
||||
즉, **한 번에 하나의 데이터(바이트 or 문자)를 순차적으로 처리**하는 구조다.
|
||||
|
||||
---
|
||||
|
||||
### 1. **파일에서 데이터 읽기 (`FileReader`)**
|
||||
|
||||
```java
|
||||
FileReader reader = new FileReader("example.txt");
|
||||
int data;
|
||||
while ((data = reader.read()) != -1) {
|
||||
System.out.print((char) data);
|
||||
}
|
||||
reader.close();
|
||||
```
|
||||
- `read()` → 한 글자씩 읽음
|
||||
- `close()` → 파일 닫기
|
||||
|
||||
---
|
||||
|
||||
### 2. **파일에 데이터 쓰기 (`FileWriter`)**
|
||||
|
||||
```java
|
||||
FileWriter writer = new FileWriter("example.txt");
|
||||
writer.write("Hello, Java IO!");
|
||||
writer.close();
|
||||
```
|
||||
- `write()` → 파일에 문자열 저장
|
||||
- `close()` → 파일 닫기
|
||||
|
||||
---
|
||||
|
||||
### 3. **파일을 빠르게 읽기 (`BufferedReader`)**
|
||||
|
||||
```java
|
||||
BufferedReader br = new BufferedReader(new FileReader("example.txt"));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
}
|
||||
br.close();
|
||||
```
|
||||
- `readLine()` → 한 줄씩 읽기 (효율적)
|
||||
- `BufferedReader` → 버퍼링으로 속도 향상
|
||||
|
||||
---
|
||||
|
||||
### 4. **파일을 빠르게 쓰기 (`BufferedWriter`)**
|
||||
|
||||
```java
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter("example.txt"));
|
||||
bw.write("Buffered Writer 사용!");
|
||||
bw.newLine(); // 줄바꿈
|
||||
bw.close();
|
||||
```
|
||||
- `newLine()` → 줄바꿈 추가
|
||||
- `BufferedWriter` → 속도 향상
|
||||
|
||||
---
|
||||
|
||||
### 5. **바이트 단위 파일 복사 (`FileInputStream`, `FileOutputStream`)**
|
||||
|
||||
```java
|
||||
FileInputStream fis = new FileInputStream("input.txt");
|
||||
FileOutputStream fos = new FileOutputStream("output.txt");
|
||||
|
||||
int data;
|
||||
while ((data = fis.read()) != -1) {
|
||||
fos.write(data);
|
||||
}
|
||||
|
||||
fis.close();
|
||||
fos.close();
|
||||
```
|
||||
- `read()` → 1바이트씩 읽기
|
||||
- `write()` → 1바이트씩 출력
|
||||
|
||||
---
|
||||
|
||||
### 6. **문자 단위 파일 복사 (`FileReader`, `FileWriter`)**
|
||||
|
||||
```java
|
||||
FileReader fr = new FileReader("input.txt");
|
||||
FileWriter fw = new FileWriter("output.txt");
|
||||
|
||||
int data;
|
||||
while ((data = fr.read()) != -1) {
|
||||
fw.write(data);
|
||||
}
|
||||
|
||||
fr.close();
|
||||
fw.close();
|
||||
```
|
||||
- `FileReader` → 문자 기반 파일 읽기
|
||||
- `FileWriter` → 문자 기반 파일 쓰기
|
||||
|
||||
---
|
||||
|
||||
### 정리
|
||||
|
||||
자바 IO는 **스트림 기반으로 데이터를 읽고 쓰는 방식**을 제공한다.
|
||||
- **바이트 단위 (`InputStream`, `OutputStream`)** → 이미지, 동영상, 바이너리 파일 처리
|
||||
- **문자 단위 (`Reader`, `Writer`)** → 텍스트 파일 처리
|
||||
- **버퍼링 (`BufferedReader`, `BufferedWriter`)** → 성능 최적화
|
||||
|
||||
**즉, IO 스트림을 적절히 활용하면 다양한 입출력 작업을 쉽게 수행할 수 있다!**
|
||||
202
docs/javax.sound.midi.md
Normal file
202
docs/javax.sound.midi.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# **Java `javax.sound.midi` 쉽게 배우기**
|
||||
|
||||
## **1. `javax.sound.midi`란?**
|
||||
`javax.sound.midi` 패키지는 **자바에서 MIDI(악기 디지털 인터페이스) 데이터를 다루는 기능**을 제공한다.
|
||||
이를 이용하면 **MIDI 파일을 재생, 편집, 녹음, 생성**할 수 있다.
|
||||
|
||||
**주요 기능**
|
||||
✔ **MIDI 파일 재생** (악기 소리 출력)
|
||||
✔ **MIDI 이벤트 생성 및 수정** (노트 추가/삭제)
|
||||
✔ **실시간 MIDI 입력/출력** (MIDI 키보드 연결 가능)
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `MidiSystem` 클래스 (MIDI 시스템 제어)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `getSequencer()` | MIDI 파일을 재생하는 `Sequencer` 객체 반환 |
|
||||
| `getSynthesizer()` | 소프트웨어 신시사이저(`Synthesizer`) 반환 |
|
||||
| `getReceiver()` | MIDI 데이터를 출력할 `Receiver` 반환 |
|
||||
| `getTransmitter()` | MIDI 데이터를 입력받을 `Transmitter` 반환 |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Sequencer` 클래스 (MIDI 재생기)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `open()` | `Sequencer`를 초기화하고 사용 준비 |
|
||||
| `setSequence(Sequence sequence)` | 재생할 MIDI 시퀀스 설정 |
|
||||
| `start()` | MIDI 재생 시작 |
|
||||
| `stop()` | MIDI 재생 중지 |
|
||||
| `setLoopCount(int count)` | MIDI 반복 재생 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `Synthesizer` 클래스 (신시사이저 - 소리 생성)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `open()` | 신시사이저를 활성화 |
|
||||
| `getChannels()` | MIDI 채널(`MidiChannel[]`) 가져오기 |
|
||||
| `getReceiver()` | 신시사이저에 MIDI 메시지를 보내는 `Receiver` 반환 |
|
||||
|
||||
---
|
||||
|
||||
### **(4) `MidiChannel` 클래스 (MIDI 음원 채널)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `noteOn(int note, int velocity)` | 특정 음을 연주 (노트 켜기) |
|
||||
| `noteOff(int note)` | 특정 음을 멈춤 (노트 끄기) |
|
||||
| `setInstrument(Instrument instrument)` | 채널의 악기 변경 |
|
||||
|
||||
---
|
||||
|
||||
### **(5) `Sequence` 클래스 (MIDI 데이터 관리)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `createTrack()` | 새로운 `Track`(MIDI 트랙) 생성 |
|
||||
| `getTracks()` | 시퀀스에 포함된 `Track` 목록 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **(6) `Track` 클래스 (MIDI 트랙)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `add(MidiEvent event)` | MIDI 이벤트 추가 |
|
||||
| `remove(MidiEvent event)` | MIDI 이벤트 제거 |
|
||||
|
||||
---
|
||||
|
||||
### **(7) `MidiEvent` 클래스 (MIDI 이벤트)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `new MidiEvent(MidiMessage message, long tick)` | 특정 시간(tick)에 MIDI 메시지 추가 |
|
||||
|
||||
---
|
||||
|
||||
## **3. MIDI 파일 재생 예제**
|
||||
### **✔ MIDI 파일을 재생하는 코드 (`Sequencer` 사용)**
|
||||
```java
|
||||
import javax.sound.midi.*;
|
||||
import java.io.File;
|
||||
|
||||
public class MidiPlayer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// MIDI 파일 로드
|
||||
File midiFile = new File("song.mid");
|
||||
Sequence sequence = MidiSystem.getSequence(midiFile);
|
||||
|
||||
// Sequencer 생성 및 열기
|
||||
Sequencer sequencer = MidiSystem.getSequencer();
|
||||
sequencer.open();
|
||||
sequencer.setSequence(sequence);
|
||||
|
||||
// 재생 시작
|
||||
sequencer.start();
|
||||
System.out.println("MIDI 재생 시작!");
|
||||
|
||||
// 재생이 끝날 때까지 대기
|
||||
while (sequencer.isRunning()) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
// 종료 처리
|
||||
sequencer.close();
|
||||
System.out.println("MIDI 재생 완료!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **MIDI 파일을 불러와 재생하는 코드이다.**
|
||||
✅ **`Sequencer`를 사용하여 MIDI를 관리하고 재생한다.**
|
||||
|
||||
---
|
||||
|
||||
## **4. 실시간으로 MIDI 연주하기**
|
||||
### **✔ 신시사이저를 사용해 실시간으로 MIDI 노트 출력**
|
||||
```java
|
||||
import javax.sound.midi.*;
|
||||
|
||||
public class MidiSynthesizer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 신시사이저 열기
|
||||
Synthesizer synthesizer = MidiSystem.getSynthesizer();
|
||||
synthesizer.open();
|
||||
|
||||
// MIDI 채널 가져오기
|
||||
MidiChannel[] channels = synthesizer.getChannels();
|
||||
MidiChannel channel = channels[0]; // 첫 번째 채널 사용
|
||||
|
||||
// 특정 노트 연주 (도(C4) - MIDI 번호 60)
|
||||
int note = 60;
|
||||
channel.noteOn(note, 80); // 음을 켬 (velocity: 80)
|
||||
System.out.println("도(C4) 연주 중...");
|
||||
|
||||
Thread.sleep(1000); // 1초 동안 연주
|
||||
|
||||
channel.noteOff(note); // 음을 끔
|
||||
System.out.println("연주 종료!");
|
||||
|
||||
// 신시사이저 닫기
|
||||
synthesizer.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **`Synthesizer`를 사용해 실시간으로 MIDI 소리를 출력한다.**
|
||||
✅ **`noteOn()`으로 음을 켜고 `noteOff()`로 끈다.**
|
||||
|
||||
---
|
||||
|
||||
## **5. 새로운 MIDI 파일 생성하기**
|
||||
### **✔ MIDI 노트를 추가하여 새로운 MIDI 파일 생성**
|
||||
```java
|
||||
import javax.sound.midi.*;
|
||||
import java.io.File;
|
||||
|
||||
public class MidiCreator {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 새로운 시퀀스 생성
|
||||
Sequence sequence = new Sequence(Sequence.PPQ, 24);
|
||||
Track track = sequence.createTrack();
|
||||
|
||||
// 노트 ON (C4 = 60)
|
||||
ShortMessage noteOn = new ShortMessage();
|
||||
noteOn.setMessage(ShortMessage.NOTE_ON, 0, 60, 80);
|
||||
track.add(new MidiEvent(noteOn, 1));
|
||||
|
||||
// 노트 OFF (C4 = 60)
|
||||
ShortMessage noteOff = new ShortMessage();
|
||||
noteOff.setMessage(ShortMessage.NOTE_OFF, 0, 60, 80);
|
||||
track.add(new MidiEvent(noteOff, 24));
|
||||
|
||||
// MIDI 파일로 저장
|
||||
File midiFile = new File("new_song.mid");
|
||||
MidiSystem.write(sequence, 1, midiFile);
|
||||
|
||||
System.out.println("새로운 MIDI 파일이 생성되었습니다: " + midiFile.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **새로운 MIDI 파일을 만들고, C4(도) 음을 추가하여 저장한다.**
|
||||
✅ **`MidiEvent`를 사용하여 MIDI 데이터를 구성한다.**
|
||||
|
||||
---
|
||||
|
||||
## **6. 정리**
|
||||
✔ **`javax.sound.midi`는 자바에서 MIDI 데이터를 다루는 API이다!**
|
||||
✔ **MIDI 파일을 재생(`Sequencer`), 실시간 연주(`Synthesizer`), 노트 추가(`Track`, `MidiEvent`) 가능**
|
||||
✔ **기본적으로 자바에서 제공하는 소프트웨어 신시사이저를 이용하여 다양한 MIDI 작업 가능**
|
||||
|
||||
✅ **MIDI 파일 플레이어, 가상 피아노, 미디 편집기 등을 만들 수 있다!**
|
||||
175
docs/javax.sound.sampled.md
Normal file
175
docs/javax.sound.sampled.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# **Java `javax.sound.sampled` 쉽게 배우기**
|
||||
|
||||
## **1. `javax.sound.sampled`란?**
|
||||
`javax.sound.sampled` 패키지는 **자바에서 오디오(소리)를 다루는 기능**을 제공한다.
|
||||
이를 이용하면 **오디오 파일을 재생, 녹음, 변환, 편집**할 수 있다.
|
||||
|
||||
**주요 기능**
|
||||
✔ 오디오 파일(WAV, AIFF 등) 재생
|
||||
✔ 마이크 입력 받아 녹음
|
||||
✔ 오디오 데이터 변환
|
||||
|
||||
---
|
||||
|
||||
## **2. 주요 클래스 및 메서드 정리**
|
||||
|
||||
### **(1) `AudioSystem` 클래스 (오디오 시스템 제어)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `getClip()` | 짧은 오디오를 재생하는 `Clip` 객체 반환 |
|
||||
| `getAudioInputStream(File file)` | 오디오 파일을 `AudioInputStream`으로 가져오기 |
|
||||
| `getMixerInfo()` | 사용 가능한 믹서(입출력 장치) 목록 가져오기 |
|
||||
| `getLine(DataLine.Info info)` | 특정 오디오 라인(스피커, 마이크 등) 가져오기 |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `Clip` 클래스 (짧은 오디오 재생)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `open(AudioInputStream stream)` | 오디오 데이터를 로드하여 준비 |
|
||||
| `start()` | 오디오 재생 시작 |
|
||||
| `stop()` | 오디오 재생 중지 |
|
||||
| `loop(int count)` | 오디오를 반복 재생 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `SourceDataLine` 클래스 (실시간 오디오 출력)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `open(AudioFormat format)` | 특정 형식의 오디오를 출력하도록 설정 |
|
||||
| `write(byte[] data, int offset, int length)` | 오디오 데이터를 출력(재생) |
|
||||
| `start()` | 오디오 출력 시작 |
|
||||
| `stop()` | 오디오 출력 중지 |
|
||||
|
||||
---
|
||||
|
||||
### **(4) `TargetDataLine` 클래스 (마이크 입력 녹음)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `open(AudioFormat format)` | 특정 형식의 오디오를 입력받도록 설정 |
|
||||
| `start()` | 녹음 시작 |
|
||||
| `read(byte[] buffer, int offset, int length)` | 마이크 입력 데이터를 읽기 |
|
||||
| `stop()` | 녹음 중지 |
|
||||
|
||||
---
|
||||
|
||||
### **(5) `AudioInputStream` 클래스 (오디오 데이터 스트림)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------|
|
||||
| `read(byte[] buffer, int offset, int length)` | 오디오 데이터를 읽어오기 |
|
||||
| `skip(long n)` | 지정된 바이트 수만큼 건너뛰기 |
|
||||
| `close()` | 스트림 닫기 |
|
||||
|
||||
---
|
||||
|
||||
## **3. 자바로 오디오 재생하기**
|
||||
### **✔ WAV 파일 재생 예제 (`Clip` 사용)**
|
||||
```java
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.File;
|
||||
|
||||
public class AudioPlayer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
File audioFile = new File("sound.wav"); // 재생할 파일
|
||||
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
|
||||
|
||||
Clip clip = AudioSystem.getClip();
|
||||
clip.open(audioStream);
|
||||
clip.start(); // 재생 시작
|
||||
|
||||
Thread.sleep(clip.getMicrosecondLength() / 1000); // 재생 시간만큼 대기
|
||||
clip.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **`Clip`을 사용하면 짧은 오디오 파일을 쉽게 재생할 수 있다.**
|
||||
|
||||
---
|
||||
|
||||
## **4. 자바로 마이크 녹음하기**
|
||||
### **✔ 마이크 입력 받아 WAV 파일 저장**
|
||||
```java
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class AudioRecorder {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
AudioFormat format = new AudioFormat(44100, 16, 2, true, true);
|
||||
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
|
||||
TargetDataLine microphone = (TargetDataLine) AudioSystem.getLine(info);
|
||||
|
||||
microphone.open(format);
|
||||
microphone.start();
|
||||
|
||||
File outputFile = new File("recorded.wav");
|
||||
AudioInputStream audioStream = new AudioInputStream(microphone);
|
||||
|
||||
System.out.println("녹음 시작! 5초간 녹음합니다...");
|
||||
AudioSystem.write(audioStream, AudioFileFormat.Type.WAVE, outputFile);
|
||||
|
||||
Thread.sleep(5000);
|
||||
microphone.stop();
|
||||
microphone.close();
|
||||
|
||||
System.out.println("녹음 완료! 파일 저장: " + outputFile.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **마이크로 5초 동안 녹음하고 WAV 파일로 저장한다.**
|
||||
|
||||
---
|
||||
|
||||
## **5. 자바에서 실시간 오디오 재생**
|
||||
### **✔ `SourceDataLine`을 사용한 실시간 오디오 출력**
|
||||
```java
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RealTimeAudioPlayer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
File audioFile = new File("sound.wav");
|
||||
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
|
||||
AudioFormat format = audioStream.getFormat();
|
||||
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
|
||||
SourceDataLine sourceLine = (SourceDataLine) AudioSystem.getLine(info);
|
||||
sourceLine.open(format);
|
||||
sourceLine.start();
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = audioStream.read(buffer)) != -1) {
|
||||
sourceLine.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
sourceLine.drain();
|
||||
sourceLine.close();
|
||||
audioStream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **오디오 데이터를 한 번에 조금씩 읽어 재생하는 방식으로, 큰 파일도 재생할 수 있다.**
|
||||
|
||||
---
|
||||
|
||||
## **6. 정리**
|
||||
✔ **`javax.sound.sampled`는 자바에서 오디오를 다루는 표준 API이다!**
|
||||
✔ **파일 재생 (`Clip`) / 실시간 출력 (`SourceDataLine`) / 마이크 입력 (`TargetDataLine`) 등 다양한 기능 제공**
|
||||
✔ **오디오 포맷 변환, 녹음 및 편집도 가능**
|
||||
|
||||
✅ **자바로 간단한 음악 플레이어나 음성 녹음 프로그램을 만들 수 있다!**
|
||||
357
docs/jgit.md
Normal file
357
docs/jgit.md
Normal file
@@ -0,0 +1,357 @@
|
||||
자바의 JGit 라이브러리에 대해 설명하자면, JGit은 Git 버전 관리 시스템을 자바 애플리케이션에서 사용할 수 있도록 해주는 순수 자바로 작성된 오픈소스 라이브러리입니다. 이 라이브러리는 Eclipse 재단에서 개발되었으며, Git의 기능을 프로그래밍 방식으로 활용할 수 있게 설계되었습니다. JGit을 사용하면 별도의 Git 클라이언트 설치 없이 자바 코드로 Git 저장소를 생성, 조작, 관리할 수 있습니다.
|
||||
|
||||
### 주요 특징
|
||||
1. **순수 자바 구현**: JGit은 네이티브 Git 명령어에 의존하지 않고 자바로 작성되었기 때문에 플랫폼 독립적입니다. Windows, Linux, macOS 등 어디서나 동일하게 동작합니다.
|
||||
2. **Git 기능 지원**: JGit은 Git의 주요 기능을 대부분 지원합니다. 예를 들어, 저장소 초기화, 커밋, 브랜치 관리, 머지, 푸시/풀, 로그 조회 등이 가능합니다.
|
||||
3. **임베디드 사용**: 외부 Git 설치 없이 애플리케이션에 임베드하여 사용할 수 있어, Git 기반 도구나 워크플로우를 자바로 쉽게 통합할 수 있습니다.
|
||||
4. **Eclipse와의 통합**: JGit은 Eclipse IDE의 Git 플러그인(EGit)의 핵심 구성 요소로 사용되며, IDE 내에서 Git 작업을 지원합니다.
|
||||
|
||||
### 주요 기능 예시
|
||||
JGit을 사용하면 다음과 같은 작업을 자바 코드로 수행할 수 있습니다:
|
||||
- **저장소 초기화**:
|
||||
```java
|
||||
Git.init().setDirectory(new File("/path/to/repo")).call();
|
||||
```
|
||||
- **파일 추가 및 커밋**:
|
||||
```java
|
||||
Git git = Git.open(new File("/path/to/repo/.git"));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
git.commit().setMessage("Initial commit").call();
|
||||
```
|
||||
- **브랜치 생성**:
|
||||
```java
|
||||
git.branchCreate().setName("feature-branch").call();
|
||||
```
|
||||
- **원격 저장소에서 풀**:
|
||||
```java
|
||||
git.pull().call();
|
||||
```
|
||||
|
||||
### 사용 사례
|
||||
- **자동화 도구**: CI/CD 파이프라인에서 Git 작업을 스크립트로 자동화.
|
||||
- **커스텀 Git 클라이언트**: GUI나 CLI 기반의 자체 Git 도구 개발.
|
||||
- **프로젝트 관리**: 소스 코드 관리나 버전 관리 기능을 애플리케이션에 통합.
|
||||
|
||||
### 장단점
|
||||
#### 장점
|
||||
- 설치 필요 없이 자바 환경에서 바로 사용 가능.
|
||||
- Git 명령어를 호출하는 대신 API로 세밀한 제어 가능.
|
||||
- 크로스 플랫폼 호환성.
|
||||
|
||||
#### 단점
|
||||
- 네이티브 Git에 비해 일부 고급 기능이 제한적이거나 느릴 수 있음.
|
||||
- 대규모 저장소에서 성능 최적화가 필요할 수 있음.
|
||||
|
||||
### 의존성 추가 (Maven 예시)
|
||||
JGit을 프로젝트에 추가하려면 Maven을 사용하는 경우 `pom.xml`에 다음 의존성을 추가하면 됩니다:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.8.0.202311291450-r</version> <!-- 최신 버전 확인 필요 -->
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 참고 자료
|
||||
- 공식 사이트: [JGit GitHub](https://github.com/eclipse/jgit)
|
||||
- 문서: [JGit User Guide](https://www.eclipse.org/jgit/documentation/)
|
||||
|
||||
추가로 궁금한 점이 있거나 특정 기능에 대해 더 알고 싶다면 말씀해주세요!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
서버의 컨트롤러에서 Git 저장소를 제공한다는 것은 웹 애플리케이션의 백엔드에서 Git 저장소를 생성하고, 클라이언트가 이를 HTTP(S)를 통해 접근하거나 다운로드할 수 있도록 하는 것을 의미합니다. 여기서는 Spring Boot 기반 서버에서 JGit을 활용해 Git 저장소를 제공하는 방법을 단계별로 설명하겠습니다. 클라이언트가 저장소를 클론(clone)하거나 파일을 다운로드할 수 있는 시나리오를 중심으로 구성합니다.
|
||||
|
||||
---
|
||||
|
||||
### 목표
|
||||
- 서버에서 Git 저장소를 동적으로 생성하거나 관리.
|
||||
- HTTP 엔드포인트를 통해 저장소에 접근하거나 `.git` 디렉토리를 제공.
|
||||
- JGit을 사용해 저장소 초기화 및 기본 파일 추가.
|
||||
|
||||
---
|
||||
|
||||
### 1. **프로젝트 설정**
|
||||
Spring Boot와 JGit을 사용해 기본 환경을 설정합니다.
|
||||
|
||||
#### (1) 의존성 추가 (Maven)
|
||||
`pom.xml`에 필요한 의존성을 추가:
|
||||
```xml
|
||||
<dependencies>
|
||||
<!-- Spring Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- Thymeleaf (선택적, UI용) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<!-- JGit -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.8.0.202311291450-r</version>
|
||||
</dependency>
|
||||
<!-- Lombok (선택적) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
#### (2) 프로젝트 구조
|
||||
```
|
||||
src/
|
||||
├── main/
|
||||
│ ├── java/
|
||||
│ │ └── com/
|
||||
│ │ └── example/
|
||||
│ │ └── gitserver/
|
||||
│ │ ├── controller/ # 컨트롤러
|
||||
│ │ ├── service/ # Git 로직
|
||||
│ │ └── GitServerApplication.java
|
||||
│ └── resources/
|
||||
│ └── application.properties # 설정
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **Git 서비스 구현**
|
||||
`GitService` 클래스에서 저장소를 생성하고 관리하는 로직을 작성합니다.
|
||||
|
||||
```java
|
||||
package com.example.gitserver.service;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
@Service
|
||||
public class GitService {
|
||||
|
||||
// 저장소 생성 및 초기화
|
||||
public File createRepository(String repoName) throws GitAPIException, IOException {
|
||||
File repoDir = new File("./repos/" + repoName); // 서버 내 저장소 디렉토리
|
||||
if (!repoDir.exists()) {
|
||||
repoDir.mkdirs();
|
||||
}
|
||||
|
||||
try (Git git = Git.init().setDirectory(repoDir).call()) {
|
||||
// 초기 파일 추가 (선택적)
|
||||
File readme = new File(repoDir, "README.md");
|
||||
Files.write(readme.toPath(), "# Sample Repository".getBytes());
|
||||
git.add().addFilepattern("README.md").call();
|
||||
git.commit().setMessage("Initial commit").call();
|
||||
}
|
||||
return repoDir;
|
||||
}
|
||||
|
||||
// 저장소 디렉토리 반환
|
||||
public File getRepository(String repoName) {
|
||||
return new File("./repos/" + repoName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **컨트롤러에서 Git 저장소 제공**
|
||||
`GitController`에서 HTTP 엔드포인트를 통해 저장소를 제공합니다. 두 가지 방식으로 접근할 수 있습니다:
|
||||
1. **저장소 전체 다운로드**: `.git` 디렉토리를 ZIP 파일로 제공.
|
||||
2. **Git 프로토콜 지원**: HTTP를 통해 `git clone` 가능하도록 설정 (추가 설정 필요).
|
||||
|
||||
#### (1) 저장소 생성 및 ZIP 다운로드 방식
|
||||
```java
|
||||
package com.example.gitserver.controller;
|
||||
|
||||
import com.example.gitserver.service.GitService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/git")
|
||||
public class GitController {
|
||||
|
||||
@Autowired
|
||||
private GitService gitService;
|
||||
|
||||
// 저장소 생성
|
||||
@PostMapping("/create/{repoName}")
|
||||
public ResponseEntity<String> createRepository(@PathVariable String repoName) {
|
||||
try {
|
||||
gitService.createRepository(repoName);
|
||||
return ResponseEntity.ok("Repository '" + repoName + "' created successfully");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 저장소 다운로드 (ZIP 형식)
|
||||
@GetMapping("/download/{repoName}")
|
||||
public ResponseEntity<Resource> downloadRepository(@PathVariable String repoName) throws Exception {
|
||||
File repoDir = gitService.getRepository(repoName);
|
||||
if (!repoDir.exists()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// .git 디렉토리만 압축
|
||||
File gitDir = new File(repoDir, ".git");
|
||||
Path zipPath = Files.createTempFile(repoName, ".zip");
|
||||
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipPath))) {
|
||||
zipDirectory(gitDir, gitDir.getName(), zos);
|
||||
}
|
||||
|
||||
Resource resource = new FileSystemResource(zipPath.toFile());
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + repoName + ".zip")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
// 디렉토리를 ZIP으로 압축하는 헬퍼 메서드
|
||||
private void zipDirectory(File folder, String parentFolder, ZipOutputStream zos) throws IOException {
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
zipDirectory(file, parentFolder + "/" + file.getName(), zos);
|
||||
continue;
|
||||
}
|
||||
ZipEntry ze = new ZipEntry(parentFolder + "/" + file.getName());
|
||||
zos.putNextEntry(ze);
|
||||
Files.copy(file.toPath(), zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### (2) 실행 및 테스트
|
||||
- 애플리케이션 실행 후:
|
||||
- `POST /git/create/myrepo`: 저장소 생성.
|
||||
- `GET /git/download/myrepo`: `.git` 디렉토리를 ZIP으로 다운로드.
|
||||
- 클라이언트는 다운로드한 ZIP을 풀고 `git clone --bare`로 로컬 저장소로 변환 가능.
|
||||
|
||||
---
|
||||
|
||||
### 4. **HTTP를 통한 Git 프로토콜 제공 (고급)**
|
||||
ZIP 다운로드 대신 클라이언트가 `git clone https://your-server/git/myrepo`로 직접 클론하도록 하려면 Git HTTP 백엔드를 구현해야 합니다. JGit은 기본적으로 Git 서버 프로토콜을 제공하지 않으므로 추가 설정이 필요합니다.
|
||||
|
||||
#### (1) JGit Servlet 사용
|
||||
JGit은 `GitServlet`을 제공해 HTTP 기반 Git 프로토콜을 지원합니다.
|
||||
|
||||
- **의존성 추가**: JGit HTTP 서버 의존성 추가.
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.http.server</artifactId>
|
||||
<version>6.8.0.202311291450-r</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- **Servlet 설정**:
|
||||
```java
|
||||
package com.example.gitserver.config;
|
||||
|
||||
import org.eclipse.jgit.http.server.GitServlet;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import java.io.File;
|
||||
|
||||
@Configuration
|
||||
public class GitServletConfig {
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<Servlet> gitServlet() {
|
||||
GitServlet gitServlet = new GitServlet();
|
||||
gitServlet.setRepositoryResolver((req, name) -> {
|
||||
File repoDir = new File("./repos/" + name + "/.git");
|
||||
if (!repoDir.exists()) {
|
||||
throw new IllegalStateException("Repository not found: " + name);
|
||||
}
|
||||
return new org.eclipse.jgit.internal.storage.file.FileRepository(repoDir);
|
||||
});
|
||||
|
||||
ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(gitServlet, "/git/*");
|
||||
bean.setLoadOnStartup(1);
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **저장소 준비**: 저장소는 **bare repository** 형식이어야 합니다.
|
||||
```java
|
||||
public File createBareRepository(String repoName) throws GitAPIException {
|
||||
File repoDir = new File("./repos/" + repoName);
|
||||
if (!repoDir.exists()) {
|
||||
repoDir.mkdirs();
|
||||
}
|
||||
try (Git git = Git.init().setDirectory(repoDir).setBare(true).call()) {
|
||||
System.out.println("Bare repository created: " + repoName);
|
||||
}
|
||||
return repoDir;
|
||||
}
|
||||
```
|
||||
|
||||
- **컨트롤러 수정**:
|
||||
```java
|
||||
@PostMapping("/create-bare/{repoName}")
|
||||
public ResponseEntity<String> createBareRepository(@PathVariable String repoName) {
|
||||
try {
|
||||
gitService.createBareRepository(repoName);
|
||||
return ResponseEntity.ok("Bare repository '" + repoName + "' created successfully");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### (2) 클라이언트에서 클론
|
||||
- 서버가 `http://localhost:8080`에서 실행 중이라면:
|
||||
```bash
|
||||
git clone http://localhost:8080/git/myrepo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. **추가 고려사항**
|
||||
- **보안**: 인증(예: Spring Security)을 추가해 저장소 접근을 제어.
|
||||
- **저장소 경로**: `./repos/`는 서버의 파일 시스템에 저장되므로, 실제 배포 시 경로를 환경 변수로 관리.
|
||||
- **성능**: 대규모 저장소의 경우 JGit 대신 네이티브 Git 서버(GitLab, Gitea 등)를 고려.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
- **ZIP 방식**: 간단히 `.git` 디렉토리를 제공하며, 클라이언트가 수동으로 처리.
|
||||
- **Git 프로토콜 방식**: `GitServlet`을 사용해 HTTP로 직접 클론 가능하지만 설정이 복잡.
|
||||
필요에 따라 두 방식 중 하나를 선택하거나 혼합해 사용할 수 있습니다. 추가 질문이나 특정 기능 확장이 필요하면 말씀해주세요!
|
||||
@@ -6,24 +6,24 @@
|
||||
|
||||
### JUnit 5 주요 어노테이션 표
|
||||
|
||||
| 어노테이션 | 설명 |
|
||||
|----------------------------|----------------------------------------------------------------------------------------|
|
||||
| `@Test` | 해당 메서드가 테스트 메서드임을 나타냄 |
|
||||
| `@BeforeAll` | 모든 테스트 메서드 실행 전에 한 번만 실행되는 초기화 메서드 (static 필요) |
|
||||
| `@AfterAll` | 모든 테스트 메서드 실행 후에 한 번만 실행되는 정리 메서드 (static 필요) |
|
||||
| `@BeforeEach` | 각 테스트 메서드 실행 전에 실행되는 초기화 메서드 |
|
||||
| `@AfterEach` | 각 테스트 메서드 실행 후에 실행되는 정리 메서드 |
|
||||
| `@Disabled` | 테스트 클래스나 메서드를 비활성화하여 실행하지 않음 |
|
||||
| `@DisplayName` | 테스트 클래스나 메서드에 사용자 정의 이름을 지정 |
|
||||
| `@ParameterizedTest` | 동일한 테스트를 여러 입력값으로 반복 실행 |
|
||||
| `@ValueSource` | `@ParameterizedTest`와 함께 사용되며, 단일 값의 배열을 제공 |
|
||||
| `@CsvSource` | `@ParameterizedTest`와 함께 사용되며, CSV 형식의 데이터를 제공 |
|
||||
| `@MethodSource` | `@ParameterizedTest`와 함께 사용되며, 메서드에서 제공되는 데이터를 사용 |
|
||||
| `@RepeatedTest` | 테스트를 지정된 횟수만큼 반복 실행 |
|
||||
| `@Tag` | 테스트에 태그를 지정하여 필터링 가능 |
|
||||
| `@Nested` | 테스트 클래스 내에 중첩된 테스트 클래스를 정의 |
|
||||
| `@Timeout` | 테스트 실행 시간이 지정된 시간을 초과하면 실패 처리 |
|
||||
| `@ExtendWith` | 사용자 정의 확장을 등록하여 테스트 동작을 커스터마이징 |
|
||||
| 어노테이션 | 설명 |
|
||||
|----|----|
|
||||
| `@Test` | 해당 메서드가 테스트 메서드임을 나타냄 |
|
||||
| `@BeforeAll` | 모든 테스트 메서드 실행 전에 한 번만 실행되는 초기화 메서드 (static 필요) |
|
||||
| `@AfterAll` | 모든 테스트 메서드 실행 후에 한 번만 실행되는 정리 메서드 (static 필요) |
|
||||
| `@BeforeEach` | 각 테스트 메서드 실행 전에 실행되는 초기화 메서드 |
|
||||
| `@AfterEach` | 각 테스트 메서드 실행 후에 실행되는 정리 메서드 |
|
||||
| `@Disabled` | 테스트 클래스나 메서드를 비활성화하여 실행하지 않음 |
|
||||
| `@DisplayName` | 테스트 클래스나 메서드에 사용자 정의 이름을 지정 |
|
||||
| `@ParameterizedTest` | 동일한 테스트를 여러 입력값으로 반복 실행 |
|
||||
| `@ValueSource` | `@ParameterizedTest`와 함께 사용되며, 단일 값의 배열을 제공 |
|
||||
| `@CsvSource` | `@ParameterizedTest`와 함께 사용되며, CSV 형식의 데이터를 제공 |
|
||||
| `@MethodSource` | `@ParameterizedTest`와 함께 사용되며, 메서드에서 제공되는 데이터를 사용 |
|
||||
| `@RepeatedTest` | 테스트를 지정된 횟수만큼 반복 실행 |
|
||||
| `@Tag` | 테스트에 태그를 지정하여 필터링 가능 |
|
||||
| `@Nested` | 테스트 클래스 내에 중첩된 테스트 클래스를 정의 |
|
||||
| `@Timeout` | 테스트 실행 시간이 지정된 시간을 초과하면 실패 처리 |
|
||||
| `@ExtendWith` | 사용자 정의 확장을 등록하여 테스트 동작을 커스터마이징 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
200
docs/nio.md
Normal file
200
docs/nio.md
Normal file
@@ -0,0 +1,200 @@
|
||||
## 자바 NIO (New Input/Output) 관련 클래스 및 주요 메서드 정리
|
||||
|
||||
자바 NIO는 기존의 `java.io`보다 **더 빠르고 효율적인 비동기식 입출력 처리**를 제공하는 API다.
|
||||
NIO의 핵심 개념은 **버퍼(Buffer), 채널(Channel), 셀렉터(Selector)**이다.
|
||||
|
||||
---
|
||||
|
||||
### 1. `Buffer` 클래스 및 주요 메서드
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|-----------------------------|
|
||||
| `allocate(int capacity)` | 지정된 크기의 버퍼 생성 (`ByteBuffer.allocate(1024)`) |
|
||||
| `wrap(byte[] array)` | 기존 배열을 감싸는 버퍼 생성 |
|
||||
| `put(T value)` | 버퍼에 데이터 저장 |
|
||||
| `get()` | 버퍼에서 데이터 읽기 |
|
||||
| `flip()` | 읽기 모드로 전환 (쓰기 → 읽기) |
|
||||
| `clear()` | 버퍼를 초기화 (데이터 삭제 X, 포인터 리셋) |
|
||||
| `compact()` | 읽지 않은 데이터를 앞으로 이동하고, 쓰기 모드로 전환 |
|
||||
| `position()` | 현재 읽기/쓰기 위치 반환 |
|
||||
| `limit()` | 읽기/쓰기 가능한 최대 위치 반환 |
|
||||
| `remaining()` | 남은 읽기/쓰기 가능 데이터 개수 반환 |
|
||||
|
||||
**버퍼의 주요 종류**:
|
||||
- `ByteBuffer` (바이트 저장)
|
||||
- `CharBuffer` (문자 저장)
|
||||
- `IntBuffer`, `FloatBuffer` 등
|
||||
|
||||
---
|
||||
|
||||
### 2. `Channel` 인터페이스 및 주요 메서드
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|-----------------------------------|
|
||||
| `open(Path path, OpenOption...)` | 파일 채널 열기 (`FileChannel.open(path)`) |
|
||||
| `read(ByteBuffer dst)` | 데이터를 버퍼로 읽음 |
|
||||
| `write(ByteBuffer src)` | 버퍼의 데이터를 채널에 씀 |
|
||||
| `close()` | 채널 닫기 |
|
||||
| `position()` | 현재 위치 반환 |
|
||||
| `size()` | 파일 크기 반환 |
|
||||
| `truncate(long size)` | 파일 크기를 지정한 크기로 자름 |
|
||||
| `force(boolean metaData)` | 버퍼 내용을 강제로 디스크에 저장 |
|
||||
|
||||
**채널의 주요 종류**:
|
||||
- `FileChannel` (파일 입출력)
|
||||
- `SocketChannel` (TCP 소켓 통신)
|
||||
- `ServerSocketChannel` (TCP 서버 소켓)
|
||||
- `DatagramChannel` (UDP 통신)
|
||||
|
||||
---
|
||||
|
||||
### 3. `Selector` 클래스 및 주요 메서드
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|-----------------------------|
|
||||
| `open()` | 새로운 셀렉터 생성 |
|
||||
| `select()` | I/O 이벤트가 발생할 때까지 대기 |
|
||||
| `selectNow()` | 즉시 이벤트 확인 (블로킹 X) |
|
||||
| `select(timeout)` | 지정된 시간 동안 대기 |
|
||||
| `keys()` | 등록된 모든 채널 반환 |
|
||||
| `selectedKeys()` | 이벤트가 발생한 채널 반환 |
|
||||
| `wakeup()` | 블로킹 상태에서 셀렉터를 깨움 |
|
||||
| `close()` | 셀렉터 닫기 |
|
||||
|
||||
**관련 클래스**:
|
||||
- `SelectionKey.OP_READ` (읽기 가능)
|
||||
- `SelectionKey.OP_WRITE` (쓰기 가능)
|
||||
- `SelectionKey.OP_CONNECT` (연결 가능)
|
||||
- `SelectionKey.OP_ACCEPT` (새로운 연결 가능)
|
||||
|
||||
---
|
||||
|
||||
## 자바 NIO 쉽게 설명하기
|
||||
|
||||
### NIO란 무엇인가?
|
||||
NIO는 기존의 `java.io`보다 **더 빠르고 비동기적으로 입출력을 처리**할 수 있는 기술이다.
|
||||
|
||||
- **기존 방식 (IO)**
|
||||
- 데이터를 **스트림(Stream)** 단위로 처리
|
||||
- 블로킹 방식 (데이터를 읽거나 쓸 때 작업이 끝날 때까지 대기)
|
||||
- 한 번에 하나의 입출력만 가능
|
||||
|
||||
- **NIO 방식**
|
||||
- 데이터를 **버퍼(Buffer)와 채널(Channel)**을 이용해 처리
|
||||
- 논블로킹 방식 (데이터를 읽거나 쓸 때 대기하지 않고 바로 진행)
|
||||
- 하나의 스레드가 여러 채널을 관리 가능 (셀렉터 사용)
|
||||
|
||||
---
|
||||
|
||||
### 1. **버퍼(Buffer) 이해하기**
|
||||
버퍼는 **데이터를 담아두는 공간**이다.
|
||||
IO에서는 데이터를 바로 읽고 쓰지만, **NIO에서는 데이터를 버퍼에 담고 처리**한다.
|
||||
|
||||
```java
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024); // 1KB 크기의 버퍼 생성
|
||||
buffer.put("Hello NIO".getBytes()); // 데이터 쓰기
|
||||
|
||||
buffer.flip(); // 읽기 모드로 변경
|
||||
byte[] data = new byte[buffer.remaining()];
|
||||
buffer.get(data); // 데이터 읽기
|
||||
System.out.println(new String(data)); // "Hello NIO"
|
||||
```
|
||||
- `put()` → 데이터를 버퍼에 저장
|
||||
- `flip()` → 읽기 모드로 변경
|
||||
- `get()` → 버퍼에서 데이터 읽기
|
||||
|
||||
---
|
||||
|
||||
### 2. **채널(Channel) 이해하기**
|
||||
채널은 **데이터가 오가는 통로**다.
|
||||
기존의 `InputStream` / `OutputStream` 대신 **파일, 소켓 등과 데이터를 주고받는 역할**을 한다.
|
||||
|
||||
#### **파일을 읽는 예제 (FileChannel)**
|
||||
```java
|
||||
Path path = Paths.get("example.txt");
|
||||
FileChannel channel = FileChannel.open(path, StandardOpenOption.READ);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
channel.read(buffer); // 파일에서 데이터를 읽어 버퍼에 저장
|
||||
|
||||
buffer.flip();
|
||||
byte[] data = new byte[buffer.remaining()];
|
||||
buffer.get(data);
|
||||
|
||||
System.out.println(new String(data)); // 파일 내용 출력
|
||||
channel.close();
|
||||
```
|
||||
- `FileChannel.open()` → 파일을 열고 채널 생성
|
||||
- `read(ByteBuffer)` → 파일 데이터를 버퍼에 읽기
|
||||
- `flip()` → 읽기 모드로 변경
|
||||
- `get()` → 버퍼에서 데이터 가져오기
|
||||
|
||||
---
|
||||
|
||||
### 3. **논블로킹 소켓 (SocketChannel) 사용하기**
|
||||
NIO의 강점은 **네트워크 프로그래밍에서 논블로킹 소켓**을 사용할 수 있다는 점이다.
|
||||
즉, 하나의 스레드가 여러 소켓을 동시에 처리할 수 있다.
|
||||
|
||||
#### **비동기 TCP 클라이언트 예제**
|
||||
```java
|
||||
SocketChannel socketChannel = SocketChannel.open();
|
||||
socketChannel.configureBlocking(false); // 논블로킹 모드 설정
|
||||
socketChannel.connect(new InetSocketAddress("localhost", 8080));
|
||||
|
||||
while (!socketChannel.finishConnect()) {
|
||||
System.out.println("연결 시도 중...");
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap("Hello Server".getBytes());
|
||||
socketChannel.write(buffer); // 서버로 데이터 전송
|
||||
|
||||
socketChannel.close();
|
||||
```
|
||||
- `configureBlocking(false)` → 논블로킹 모드 설정
|
||||
- `connect()` → 서버에 연결
|
||||
- `write(ByteBuffer)` → 서버로 데이터 전송
|
||||
|
||||
---
|
||||
|
||||
### 4. **Selector로 다중 채널 관리하기**
|
||||
셀렉터(Selector)를 사용하면 **하나의 스레드로 여러 채널을 관리**할 수 있다.
|
||||
즉, 효율적인 **멀티플렉싱** 처리가 가능하다.
|
||||
|
||||
#### **비동기 서버 예제**
|
||||
```java
|
||||
Selector selector = Selector.open();
|
||||
ServerSocketChannel serverChannel = ServerSocketChannel.open();
|
||||
serverChannel.bind(new InetSocketAddress(8080));
|
||||
serverChannel.configureBlocking(false);
|
||||
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
while (true) {
|
||||
selector.select(); // I/O 이벤트 발생할 때까지 대기
|
||||
|
||||
for (SelectionKey key : selector.selectedKeys()) {
|
||||
if (key.isAcceptable()) { // 클라이언트 연결 요청 처리
|
||||
SocketChannel client = serverChannel.accept();
|
||||
client.configureBlocking(false);
|
||||
client.register(selector, SelectionKey.OP_READ);
|
||||
} else if (key.isReadable()) { // 데이터 읽기 처리
|
||||
SocketChannel client = (SocketChannel) key.channel();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
client.read(buffer);
|
||||
buffer.flip();
|
||||
System.out.println("Received: " + new String(buffer.array()).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `Selector.open()` → 셀렉터 생성
|
||||
- `select()` → I/O 이벤트 발생 대기
|
||||
- `register()` → 채널을 셀렉터에 등록
|
||||
|
||||
---
|
||||
|
||||
### 정리
|
||||
자바 NIO는 **버퍼 + 채널 + 셀렉터**를 이용해
|
||||
1. **파일 입출력을 빠르게 처리**하고
|
||||
2. **네트워크 소켓을 논블로킹 방식으로 다룰 수 있게 해준다.**
|
||||
|
||||
**즉, 적은 스레드로 많은 연결을 처리하는 고성능 서버를 만들 때 강력한 도구가 된다!**
|
||||
59
docs/swing/01_Swing Components.md
Normal file
59
docs/swing/01_Swing Components.md
Normal file
@@ -0,0 +1,59 @@
|
||||
다음은 **Swing의 모든 주요 컴포넌트**를 정리한 표이다.
|
||||
|
||||
### **1. 기본 컴포넌트**
|
||||
| 컴포넌트 | 설명 |
|
||||
|----------|---------------------------------------------|
|
||||
| `JLabel` | 텍스트 또는 아이콘을 표시하는 레이블 |
|
||||
| `JButton` | 클릭 가능한 버튼 |
|
||||
| `JToggleButton` | ON/OFF 상태를 가질 수 있는 버튼 |
|
||||
| `JCheckBox` | 다중 선택이 가능한 체크박스 |
|
||||
| `JRadioButton` | 단일 선택이 가능한 라디오 버튼 |
|
||||
| `JTextField` | 한 줄의 텍스트 입력 필드 |
|
||||
| `JPasswordField` | 비밀번호 입력 필드 (입력값이 가려짐) |
|
||||
| `JTextArea` | 여러 줄의 텍스트를 입력할 수 있는 영역 |
|
||||
| `JEditorPane` | HTML, RTF 등을 표시할 수 있는 편집 가능한 텍스트 영역 |
|
||||
| `JComboBox` | 드롭다운 목록을 제공하는 콤보 박스 |
|
||||
| `JList` | 여러 개의 아이템을 선택할 수 있는 리스트 |
|
||||
|
||||
### **2. 컨테이너 컴포넌트**
|
||||
| 컴포넌트 | 설명 |
|
||||
|----------|---------------------------------------------|
|
||||
| `JPanel` | 기본 컨테이너 패널 |
|
||||
| `JScrollPane` | 내부 컴포넌트에 스크롤 기능을 추가 |
|
||||
| `JSplitPane` | 두 개의 컴포넌트를 수평/수직으로 나눌 수 있는 패널 |
|
||||
| `JTabbedPane` | 여러 개의 탭을 제공하는 패널 |
|
||||
| `JLayeredPane` | 컴포넌트들을 여러 레이어로 배치할 수 있는 패널 |
|
||||
| `JDesktopPane` | MDI(Multiple Document Interface)를 위한 내부 프레임 지원 패널 |
|
||||
|
||||
### **3. 고급 컴포넌트**
|
||||
| 컴포넌트 | 설명 |
|
||||
|----------|---------------------------------------------|
|
||||
| `JTable` | 데이터를 표 형태로 표시하는 테이블 |
|
||||
| `JTree` | 계층 구조를 표시하는 트리 |
|
||||
| `JProgressBar` | 작업 진행 상태를 표시하는 프로그레스 바 |
|
||||
| `JSlider` | 슬라이더를 통한 값 조정 가능 |
|
||||
| `JSpinner` | 숫자 또는 값 목록을 조정할 수 있는 스피너 |
|
||||
|
||||
### **4. 메뉴 및 다이얼로그**
|
||||
| 컴포넌트 | 설명 |
|
||||
|----------|---------------------------------------------|
|
||||
| `JMenuBar` | 메뉴바를 제공하는 컨테이너 |
|
||||
| `JMenu` | 메뉴 항목을 포함하는 메뉴 |
|
||||
| `JMenuItem` | 개별적인 메뉴 항목 |
|
||||
| `JCheckBoxMenuItem` | 체크 가능한 메뉴 항목 |
|
||||
| `JRadioButtonMenuItem` | 라디오 버튼 형태의 메뉴 항목 |
|
||||
| `JPopupMenu` | 우클릭 팝업 메뉴 |
|
||||
| `JToolBar` | 도구 모음(툴바) 패널 |
|
||||
| `JOptionPane` | 알림, 확인, 입력을 위한 다이얼로그 제공 |
|
||||
| `JFileChooser` | 파일 선택 다이얼로그 |
|
||||
| `JColorChooser` | 색상 선택 다이얼로그 |
|
||||
|
||||
### **5. 윈도우 관련 컴포넌트**
|
||||
| 컴포넌트 | 설명 |
|
||||
|----------|---------------------------------------------|
|
||||
| `JFrame` | 기본 윈도우 프레임 |
|
||||
| `JDialog` | 모달 또는 모델리스 다이얼로그 창 |
|
||||
| `JWindow` | 테두리 없는 창 |
|
||||
| `JInternalFrame` | `JDesktopPane` 내에서 사용할 수 있는 내부 프레임 |
|
||||
|
||||
위 컴포넌트들을 조합하면 다양한 GUI 애플리케이션을 만들 수 있다.
|
||||
190
docs/swing/ActionListener.md
Normal file
190
docs/swing/ActionListener.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# **ActionListener와 ActionCommand 사용법**
|
||||
|
||||
`ActionListener`는 버튼 클릭, 메뉴 항목 선택 등 사용자 입력에 대한 동작을 처리하는 인터페이스입니다.
|
||||
`ActionCommand`는 특정 이벤트를 식별하는 문자열로, `ActionListener`가 어떤 동작을 수행해야 하는지 구분하는 데 사용됩니다.
|
||||
|
||||
---
|
||||
|
||||
## **1. ActionListener 기본 사용법**
|
||||
`ActionListener`를 사용하려면 `ActionListener` 인터페이스를 구현한 후 `actionPerformed()` 메서드를 오버라이드해야 합니다.
|
||||
|
||||
### **(1) 버튼 클릭 이벤트 처리 예제**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ActionListenerExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("ActionListener Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
JButton button = new JButton("클릭하세요");
|
||||
|
||||
// ActionListener 추가
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JOptionPane.showMessageDialog(null, "버튼이 클릭되었습니다!");
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(button);
|
||||
frame.add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
### **설명**
|
||||
1. `JButton`을 생성하고 `"클릭하세요"` 라벨을 설정.
|
||||
2. `button.addActionListener()`를 사용하여 `ActionListener` 추가.
|
||||
3. 버튼을 클릭하면 `actionPerformed()`가 호출되어 메시지 창이 표시됨.
|
||||
|
||||
---
|
||||
|
||||
## **2. ActionCommand 활용하기**
|
||||
기본적으로 버튼의 텍스트가 `ActionCommand`로 설정되지만, 직접 지정할 수도 있습니다.
|
||||
|
||||
### **(1) 여러 버튼을 구분하는 예제**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ActionCommandExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("ActionCommand Example");
|
||||
frame.setSize(400, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JButton btn1 = new JButton("버튼 1");
|
||||
JButton btn2 = new JButton("버튼 2");
|
||||
|
||||
// ActionCommand 설정
|
||||
btn1.setActionCommand("BUTTON_ONE");
|
||||
btn2.setActionCommand("BUTTON_TWO");
|
||||
|
||||
// 공통 ActionListener
|
||||
ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String command = e.getActionCommand();
|
||||
if ("BUTTON_ONE".equals(command)) {
|
||||
JOptionPane.showMessageDialog(null, "버튼 1이 클릭됨!");
|
||||
} else if ("BUTTON_TWO".equals(command)) {
|
||||
JOptionPane.showMessageDialog(null, "버튼 2가 클릭됨!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
btn1.addActionListener(listener);
|
||||
btn2.addActionListener(listener);
|
||||
|
||||
frame.add(btn1);
|
||||
frame.add(btn2);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **설명**
|
||||
1. 두 개의 버튼(`btn1`, `btn2`)을 생성.
|
||||
2. `setActionCommand("BUTTON_ONE")` 및 `setActionCommand("BUTTON_TWO")`로 명령어 설정.
|
||||
3. **하나의 `ActionListener`** 에서 `e.getActionCommand()`를 사용하여 어떤 버튼이 눌렸는지 판별.
|
||||
|
||||
---
|
||||
|
||||
## **3. 별도의 ActionListener 클래스로 분리하기**
|
||||
코드를 깔끔하게 유지하려면 `ActionListener`를 별도의 클래스로 분리하는 것이 좋습니다.
|
||||
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
class MyActionListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JOptionPane.showMessageDialog(null, "이벤트가 발생했습니다! (" + e.getActionCommand() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public class SeparateActionListenerExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("Separate ActionListener Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
JButton button = new JButton("클릭");
|
||||
button.setActionCommand("CLICK_BUTTON");
|
||||
|
||||
// 별도의 클래스 사용
|
||||
button.addActionListener(new MyActionListener());
|
||||
|
||||
panel.add(button);
|
||||
frame.add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **설명**
|
||||
- `MyActionListener` 클래스를 만들어 `ActionListener`를 구현.
|
||||
- `JButton`에 `MyActionListener`를 추가.
|
||||
- 클릭하면 `"이벤트가 발생했습니다! (CLICK_BUTTON)"` 메시지를 표시.
|
||||
|
||||
---
|
||||
|
||||
## **4. Lambda 표현식 사용**
|
||||
Java 8 이상에서는 람다를 사용하여 `ActionListener`를 더 간결하게 작성할 수 있습니다.
|
||||
|
||||
```java
|
||||
import javax.swing.*;
|
||||
|
||||
public class LambdaActionListenerExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("Lambda ActionListener");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
JButton button = new JButton("눌러보세요");
|
||||
|
||||
// Lambda 표현식 사용
|
||||
button.addActionListener(e -> JOptionPane.showMessageDialog(null, "람다식으로 처리된 이벤트!"));
|
||||
|
||||
panel.add(button);
|
||||
frame.add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **설명**
|
||||
- `ActionListener`를 람다 표현식(`e -> { }`)으로 간단하게 구현.
|
||||
- 코드가 더 짧고 가독성이 좋아짐.
|
||||
|
||||
---
|
||||
|
||||
## **5. 총정리**
|
||||
| 개념 | 설명 |
|
||||
|------|------------------------------------------------|
|
||||
| `ActionListener` | 버튼, 메뉴 항목 등의 액션 이벤트를 처리하는 인터페이스 |
|
||||
| `actionPerformed(ActionEvent e)` | 버튼 클릭 등 액션이 발생하면 호출됨 |
|
||||
| `setActionCommand(String command)` | 특정 동작을 식별하기 위한 명령어 설정 |
|
||||
| `getActionCommand()` | 이벤트 발생 시 설정된 `ActionCommand` 값 가져오기 |
|
||||
| **구현 방식** | - **익명 클래스** (`new ActionListener() {}`) 사용<br>- **별도 클래스** 구현<br>- **람다 표현식** (`e -> { }`) 사용 |
|
||||
|
||||
---
|
||||
|
||||
## **6. 어떤 방식을 사용할까?**
|
||||
- **버튼 수가 적다면?** → `익명 클래스` 또는 `람다 표현식`
|
||||
- **여러 개의 버튼을 한 곳에서 처리해야 한다면?** → `ActionCommand` 활용
|
||||
- **큰 프로젝트에서 여러 UI 요소가 같은 이벤트를 공유해야 한다면?** → `별도 클래스`로 `ActionListener` 분리
|
||||
|
||||
위 내용을 이해하면 버튼 클릭뿐만 아니라 메뉴 항목 선택, 키보드 단축키 처리 등 다양한 Swing 이벤트 처리에 활용할 수 있습니다.
|
||||
166
docs/swing/ComboBox, JList.md
Normal file
166
docs/swing/ComboBox, JList.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# **Swing의 `JComboBox`, `JList` 및 관련 클래스 정리**
|
||||
|
||||
## **1. 주요 메서드 정리**
|
||||
|
||||
### **(1) `JComboBox` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `addItem(E item)` | 콤보박스에 항목 추가 |
|
||||
| `removeItem(Object item)` | 특정 항목 제거 |
|
||||
| `removeItemAt(int index)` | 지정한 인덱스의 항목 제거 |
|
||||
| `removeAllItems()` | 모든 항목 제거 |
|
||||
| `setSelectedItem(Object item)` | 특정 항목을 선택 상태로 설정 |
|
||||
| `getSelectedItem()` | 현재 선택된 항목 반환 |
|
||||
| `setSelectedIndex(int index)` | 특정 인덱스를 선택 상태로 설정 |
|
||||
| `getSelectedIndex()` | 현재 선택된 항목의 인덱스 반환 |
|
||||
| `setEditable(boolean b)` | 콤보박스를 편집 가능하도록 설정 |
|
||||
| `isEditable()` | 편집 가능 여부 확인 |
|
||||
| `addActionListener(ActionListener l)` | 항목 선택 시 이벤트 리스너 추가 |
|
||||
| `addItemListener(ItemListener l)` | 아이템이 선택 또는 변경될 때 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JList` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setListData(E[] listData)` | 리스트 데이터를 설정 |
|
||||
| `getSelectedValue()` | 현재 선택된 값 반환 |
|
||||
| `getSelectedValuesList()` | 여러 개의 선택된 값 리스트 반환 |
|
||||
| `setSelectedIndex(int index)` | 특정 인덱스를 선택 |
|
||||
| `setSelectedIndices(int[] indices)` | 여러 개의 항목을 선택 |
|
||||
| `getSelectedIndex()` | 현재 선택된 인덱스 반환 |
|
||||
| `getSelectedIndices()` | 선택된 인덱스 배열 반환 |
|
||||
| `setSelectionMode(int mode)` | 선택 모드 설정 (`SINGLE_SELECTION`, `MULTIPLE_INTERVAL_SELECTION` 등) |
|
||||
| `addListSelectionListener(ListSelectionListener l)` | 항목 선택 변경 시 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `DefaultComboBoxModel<E>` (콤보박스 데이터 모델)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `addElement(E item)` | 모델에 항목 추가 |
|
||||
| `insertElementAt(E item, int index)` | 특정 위치에 항목 삽입 |
|
||||
| `removeElement(E obj)` | 특정 항목 제거 |
|
||||
| `removeElementAt(int index)` | 특정 인덱스의 항목 제거 |
|
||||
| `getElementAt(int index)` | 특정 인덱스의 항목 가져오기 |
|
||||
| `getSize()` | 항목 개수 반환 |
|
||||
|
||||
---
|
||||
|
||||
### **(4) `DefaultListModel<E>` (리스트 데이터 모델)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `addElement(E item)` | 리스트에 항목 추가 |
|
||||
| `insertElementAt(E item, int index)` | 특정 위치에 항목 삽입 |
|
||||
| `removeElement(E obj)` | 특정 항목 제거 |
|
||||
| `removeElementAt(int index)` | 특정 인덱스의 항목 제거 |
|
||||
| `getElementAt(int index)` | 특정 인덱스의 항목 가져오기 |
|
||||
| `getSize()` | 항목 개수 반환 |
|
||||
|
||||
---
|
||||
|
||||
## **2. 관련 이벤트 정리**
|
||||
| 이벤트 리스너 | 관련 컴포넌트 | 설명 |
|
||||
|--------------|-------------|----------------------------------|
|
||||
| `ActionListener` | `JComboBox` | 콤보박스에서 항목이 선택될 때 발생 |
|
||||
| `ItemListener` | `JComboBox`, `JList` | 항목이 선택되거나 해제될 때 발생 |
|
||||
| `ListSelectionListener` | `JList` | 리스트의 선택 항목이 변경될 때 발생 |
|
||||
| `MouseListener` | `JList` | 리스트 항목을 마우스로 클릭할 때 발생 |
|
||||
|
||||
---
|
||||
|
||||
## **3. 예제 코드 및 설명**
|
||||
|
||||
### **(1) `JComboBox` 예제 – 항목 선택 시 라벨에 출력**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class JComboBoxExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JComboBox Example");
|
||||
frame.setSize(300, 150);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JLabel label = new JLabel("선택된 항목: ");
|
||||
JComboBox<String> comboBox = new JComboBox<>(new String[]{"사과", "바나나", "체리"});
|
||||
|
||||
comboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
label.setText("선택된 항목: " + comboBox.getSelectedItem());
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(comboBox);
|
||||
frame.add(label);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JComboBox`에 "사과", "바나나", "체리" 항목을 추가.
|
||||
- 사용자가 선택하면 `ActionListener`가 실행되어 선택된 항목을 `JLabel`에 출력.
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JList` 예제 – 여러 개 항목 선택 가능**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
public class JListExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JList Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new BorderLayout());
|
||||
|
||||
DefaultListModel<String> listModel = new DefaultListModel<>();
|
||||
listModel.addElement("Java");
|
||||
listModel.addElement("Python");
|
||||
listModel.addElement("C++");
|
||||
listModel.addElement("JavaScript");
|
||||
|
||||
JList<String> list = new JList<>(listModel);
|
||||
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
JScrollPane scrollPane = new JScrollPane(list);
|
||||
|
||||
JButton button = new JButton("선택 확인");
|
||||
JLabel label = new JLabel("선택된 항목: ");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
List<String> selectedValues = list.getSelectedValuesList();
|
||||
label.setText("선택된 항목: " + selectedValues);
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(scrollPane, BorderLayout.CENTER);
|
||||
frame.add(button, BorderLayout.SOUTH);
|
||||
frame.add(label, BorderLayout.NORTH);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JList`에 "Java", "Python", "C++", "JavaScript" 항목 추가.
|
||||
- 여러 개의 항목을 선택할 수 있도록 `setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)` 설정.
|
||||
- 버튼을 누르면 선택된 항목이 `JLabel`에 출력됨.
|
||||
|
||||
---
|
||||
|
||||
## **4. 총정리**
|
||||
- **`JComboBox`**: 드롭다운 목록에서 하나의 항목 선택 가능. `ActionListener`와 `ItemListener`를 사용하여 선택 이벤트 처리.
|
||||
- **`JList`**: 여러 개의 항목을 리스트 형태로 표시. 선택 모드 설정 가능 (`SINGLE_SELECTION`, `MULTIPLE_INTERVAL_SELECTION`).
|
||||
- **`DefaultComboBoxModel`, `DefaultListModel`**: 동적으로 항목을 추가/삭제할 수 있는 모델 제공.
|
||||
- **관련 이벤트**: `ActionListener` (콤보박스 선택 이벤트), `ListSelectionListener` (리스트 선택 변경 감지).
|
||||
|
||||
위 내용을 바탕으로 다양한 UI 요소를 조합하여 인터랙티브한 프로그램을 만들 수 있다!
|
||||
177
docs/swing/JEditorPane.md
Normal file
177
docs/swing/JEditorPane.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# **Swing의 `JEditorPane` 및 `EditorKit` 정리**
|
||||
|
||||
## **1. 주요 메서드 정리**
|
||||
|
||||
### **(1) `JEditorPane` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setContentType(String type)` | 문서의 MIME 타입 설정 (`text/plain`, `text/html`, `text/rtf` 등) |
|
||||
| `getContentType()` | 현재 설정된 콘텐츠 타입 반환 |
|
||||
| `setText(String text)` | 편집기에 텍스트 설정 |
|
||||
| `getText()` | 현재 편집기의 텍스트 반환 |
|
||||
| `setPage(String url)` | 지정된 URL의 내용을 로드하여 표시 |
|
||||
| `setPage(URL url)` | `URL` 객체를 이용하여 페이지 로드 |
|
||||
| `read(Reader in, Object desc)` | 입력 스트림을 이용하여 문서 로드 |
|
||||
| `write(Writer out)` | 현재 내용을 출력 스트림에 저장 |
|
||||
| `setEditable(boolean b)` | 편집 가능 여부 설정 |
|
||||
| `isEditable()` | 편집 가능 여부 확인 |
|
||||
| `setEditorKit(EditorKit kit)` | 특정 `EditorKit`을 사용하여 편집 동작 변경 |
|
||||
| `getEditorKit()` | 현재 사용 중인 `EditorKit` 반환 |
|
||||
| `replaceSelection(String content)` | 현재 선택된 텍스트를 새 텍스트로 대체 |
|
||||
| `getSelectedText()` | 현재 선택된 텍스트 반환 |
|
||||
| `addHyperlinkListener(HyperlinkListener l)` | 하이퍼링크 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `EditorKit` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `createDefaultDocument()` | 새 기본 문서 생성 |
|
||||
| `read(Reader in, Document doc, int pos)` | 주어진 리더(`Reader`)에서 문서를 읽어옴 |
|
||||
| `write(Writer out, Document doc, int pos, int len)` | 문서를 지정된 범위 내에서 출력 스트림으로 저장 |
|
||||
| `install(JEditorPane c)` | 특정 `JEditorPane`에 `EditorKit`을 설치 |
|
||||
| `deinstall(JEditorPane c)` | `JEditorPane`에서 `EditorKit`을 제거 |
|
||||
| `getViewFactory()` | 문서의 뷰를 생성하는 `ViewFactory` 반환 |
|
||||
|
||||
---
|
||||
|
||||
## **2. 관련 이벤트 정리**
|
||||
| 이벤트 리스너 | 관련 컴포넌트 | 설명 |
|
||||
|--------------|-------------|----------------------------------|
|
||||
| `HyperlinkListener` | `JEditorPane` | HTML 문서에서 하이퍼링크 클릭 시 발생 |
|
||||
| `DocumentListener` | `JEditorPane` | 문서의 변경(삽입, 삭제 등)이 발생할 때 감지 |
|
||||
| `CaretListener` | `JEditorPane` | 커서(캐럿) 위치가 변경될 때 발생 |
|
||||
| `KeyListener` | `JEditorPane` | 키보드 입력을 감지 |
|
||||
|
||||
---
|
||||
|
||||
## **3. 예제 코드 및 설명**
|
||||
|
||||
### **(1) 기본적인 `JEditorPane` 사용**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JEditorPaneExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JEditorPane Example");
|
||||
frame.setSize(500, 400);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new BorderLayout());
|
||||
|
||||
JEditorPane editorPane = new JEditorPane();
|
||||
editorPane.setContentType("text/plain");
|
||||
editorPane.setText("여기에 텍스트를 입력하세요.");
|
||||
editorPane.setEditable(true);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(editorPane);
|
||||
frame.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JEditorPane`을 생성하고 `setContentType("text/plain")`을 설정하여 일반 텍스트를 편집할 수 있도록 함.
|
||||
- `setEditable(true)`로 설정하여 편집 가능하도록 함.
|
||||
- `JScrollPane`을 사용하여 스크롤 가능하도록 추가.
|
||||
|
||||
---
|
||||
|
||||
### **(2) HTML 문서 표시 및 하이퍼링크 이벤트 처리**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JEditorPaneHyperlinkExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JEditorPane - HTML Example");
|
||||
frame.setSize(600, 400);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new BorderLayout());
|
||||
|
||||
JEditorPane editorPane = new JEditorPane();
|
||||
editorPane.setContentType("text/html");
|
||||
editorPane.setText("<html><body>"
|
||||
+ "<h1>HTML 지원</h1>"
|
||||
+ "<p>이것은 <b>HTML</b> 형식의 문서입니다.</p>"
|
||||
+ "<a href='https://www.example.com'>이 링크를 클릭하세요</a>"
|
||||
+ "</body></html>");
|
||||
editorPane.setEditable(false);
|
||||
|
||||
// 하이퍼링크 이벤트 처리
|
||||
editorPane.addHyperlinkListener(new HyperlinkListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
try {
|
||||
editorPane.setPage(e.getURL()); // 링크 클릭 시 해당 페이지 로드
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(editorPane);
|
||||
frame.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JEditorPane`을 생성하고 `setContentType("text/html")`을 설정하여 HTML을 표시할 수 있도록 함.
|
||||
- `addHyperlinkListener()`를 이용해 하이퍼링크 클릭 시 이벤트를 감지하여 `setPage(e.getURL())`로 페이지를 로드하도록 함.
|
||||
- `setEditable(false)`로 설정하여 사용자가 직접 편집하지 못하도록 함.
|
||||
|
||||
---
|
||||
|
||||
### **(3) `EditorKit`을 활용한 RTF 문서 편집**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.rtf.RTFEditorKit;
|
||||
import java.awt.*;
|
||||
|
||||
public class JEditorPaneRTFExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JEditorPane - RTF Example");
|
||||
frame.setSize(600, 400);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new BorderLayout());
|
||||
|
||||
JEditorPane editorPane = new JEditorPane();
|
||||
editorPane.setEditorKit(new RTFEditorKit()); // RTF 전용 EditorKit 설정
|
||||
editorPane.setText("{\\rtf1\\ansi This is RTF formatted text.}");
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(editorPane);
|
||||
frame.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `RTFEditorKit`을 사용하여 RTF(리치 텍스트 포맷) 문서를 편집할 수 있도록 설정.
|
||||
- `setEditorKit(new RTFEditorKit())`을 통해 `JEditorPane`에 적용.
|
||||
- RTF 포맷의 기본 문서를 설정하여 편집 가능하도록 함.
|
||||
|
||||
---
|
||||
|
||||
## **4. 총정리**
|
||||
- **`JEditorPane`**: `JTextPane`과 유사하지만 HTML, RTF, 일반 텍스트 등 다양한 문서 형식을 지원.
|
||||
- **`EditorKit`**: 특정 문서 포맷을 편집할 수 있도록 지원하는 클래스 (`RTFEditorKit`, `HTMLEditorKit` 등).
|
||||
- **이벤트 처리**:
|
||||
- `HyperlinkListener` → HTML 문서에서 링크 클릭 감지.
|
||||
- `DocumentListener` → 문서 변경 감지.
|
||||
- `CaretListener` → 커서 이동 감지.
|
||||
- **활용 예제**:
|
||||
- 일반 텍스트 편집기
|
||||
- HTML 문서 뷰어 및 하이퍼링크 처리
|
||||
- RTF 문서 편집기
|
||||
|
||||
위 내용을 활용하면 웹 기반 문서 편집기나 간단한 브라우저 기능을 구현할 수도 있다!
|
||||
69
docs/swing/README.md
Normal file
69
docs/swing/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
다음은 실무 위주의 **자바 Swing** 책 목차 초안이다.
|
||||
|
||||
---
|
||||
|
||||
# **실무에서 바로 쓰는 자바 Swing**
|
||||
|
||||
## **1. 개요 및 환경 설정**
|
||||
1.1. Swing이란? (AWT와의 차이)
|
||||
1.2. JavaFX와 비교: 언제 Swing을 선택할까?
|
||||
1.3. JDK 설치 및 개발 환경 설정
|
||||
1.4. IntelliJ IDEA와 Eclipse에서 Swing 프로젝트 생성
|
||||
|
||||
## **2. Swing의 기본 구성 요소**
|
||||
2.1. JFrame과 창 다루기
|
||||
2.2. JPanel과 레이아웃 기본
|
||||
2.3. JLabel, JButton, JTextField의 활용
|
||||
2.4. 이벤트 리스너 개념 및 적용
|
||||
|
||||
## **3. 레이아웃 매니저 활용**
|
||||
3.1. FlowLayout, BorderLayout, GridLayout
|
||||
3.2. BoxLayout, CardLayout, GridBagLayout
|
||||
3.3. 사용자 정의 레이아웃 적용하기
|
||||
3.4. 레이아웃 없이 직접 컴포넌트 배치하기
|
||||
|
||||
## **4. 고급 컴포넌트 활용**
|
||||
4.1. JTable을 활용한 데이터 표시
|
||||
4.2. JTree를 활용한 계층 구조 표현
|
||||
4.3. JList와 JComboBox의 활용
|
||||
4.4. JTabbedPane, JSplitPane을 활용한 UI 구성
|
||||
|
||||
## **5. 이벤트 처리 심화**
|
||||
5.1. ActionListener, MouseListener, KeyListener
|
||||
5.2. 익명 클래스와 람다 표현식 활용
|
||||
5.3. Adapter 클래스 활용
|
||||
5.4. 키보드 단축키 및 마우스 이벤트 조합
|
||||
|
||||
## **6. 멀티스레딩과 Swing**
|
||||
6.1. Swing에서의 스레드 문제 (EDT 개념)
|
||||
6.2. `SwingWorker`를 활용한 비동기 처리
|
||||
6.3. ProgressBar와 백그라운드 작업 처리
|
||||
6.4. 실시간 UI 업데이트 기법
|
||||
|
||||
## **7. 파일 및 데이터베이스 연동**
|
||||
7.1. JFileChooser를 활용한 파일 입출력
|
||||
7.2. CSV, JSON 파일 읽고 쓰기
|
||||
7.3. JDBC를 이용한 MySQL, PostgreSQL 연동
|
||||
7.4. 테이블 데이터와 JTable 연동
|
||||
|
||||
## **8. UI 커스터마이징과 테마 적용**
|
||||
8.1. 기본 Look and Feel 변경하기
|
||||
8.2. FlatLaf, JTattoo 등을 이용한 테마 적용
|
||||
8.3. 사용자 정의 컴포넌트 만들기
|
||||
8.4. 애니메이션 효과 적용
|
||||
|
||||
## **9. 네트워크 및 외부 API 연동**
|
||||
9.1. Swing에서 HTTP 요청 보내기
|
||||
9.2. WebSocket을 활용한 실시간 데이터 표시
|
||||
9.3. JSON 데이터 파싱 및 UI 적용
|
||||
9.4. RSS, REST API 데이터를 Swing UI에서 보여주기
|
||||
|
||||
## **10. 배포 및 실전 프로젝트**
|
||||
10.1. JAR 파일 및 실행 파일 생성
|
||||
10.2. Java Web Start 및 JNLP 활용
|
||||
10.3. Spring Boot와 연동하여 관리 UI 개발
|
||||
10.4. 실전 프로젝트: CRUD 기능이 포함된 데스크톱 앱 제작
|
||||
|
||||
---
|
||||
|
||||
이 책은 실무에서 Swing을 활용하여 즉시 사용할 수 있는 내용을 중심으로 구성되었다. 필요한 부분이 있으면 추가해도 좋겠다.
|
||||
192
docs/swing/입력 컴포넌트.md
Normal file
192
docs/swing/입력 컴포넌트.md
Normal file
@@ -0,0 +1,192 @@
|
||||
## **1. 주요 입력 컴포넌트 메서드 정리**
|
||||
|
||||
### **(1) `JTextField` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 텍스트 필드에 문자열을 설정 |
|
||||
| `getText()` | 현재 입력된 텍스트를 반환 |
|
||||
| `setEditable(boolean b)` | 편집 가능 여부 설정 (`false`이면 읽기 전용) |
|
||||
| `setColumns(int columns)` | 텍스트 필드의 너비(문자 개수 단위) 설정 |
|
||||
| `addActionListener(ActionListener l)` | Enter 키 입력 시 이벤트 리스너 추가 |
|
||||
| `setHorizontalAlignment(int alignment)` | 텍스트 정렬 방식 설정 (`LEFT`, `CENTER`, `RIGHT`) |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JPasswordField` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 비밀번호 필드에 문자열 설정 |
|
||||
| `getPassword()` | 현재 입력된 비밀번호를 `char[]` 배열로 반환 (보안상 `getText()` 대신 사용) |
|
||||
| `setEchoChar(char c)` | 입력된 문자 대신 표시할 마스킹 문자 설정 (`*` 등) |
|
||||
| `setEditable(boolean b)` | 편집 가능 여부 설정 |
|
||||
| `addActionListener(ActionListener l)` | Enter 키 입력 시 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `JTextArea` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 텍스트 영역에 문자열 설정 |
|
||||
| `getText()` | 현재 입력된 텍스트를 반환 |
|
||||
| `append(String text)` | 현재 텍스트 뒤에 새로운 문자열 추가 |
|
||||
| `setEditable(boolean b)` | 편집 가능 여부 설정 |
|
||||
| `setRows(int rows)` | 텍스트 영역의 행 개수 설정 |
|
||||
| `setColumns(int columns)` | 텍스트 영역의 열 개수 설정 |
|
||||
| `setLineWrap(boolean b)` | 자동 줄바꿈 여부 설정 (`true`이면 줄이 길어지면 자동 개행) |
|
||||
| `setWrapStyleWord(boolean b)` | 단어 단위로 줄바꿈할지 설정 (`true`이면 단어 기준으로 개행) |
|
||||
|
||||
---
|
||||
|
||||
## **2. 관련 이벤트 정리**
|
||||
|
||||
| 이벤트 리스너 | 관련 컴포넌트 | 설명 |
|
||||
|--------------|-------------|----------------------------------|
|
||||
| `ActionListener` | `JTextField`, `JPasswordField` | 사용자가 Enter 키를 눌렀을 때 발생 |
|
||||
| `KeyListener` | `JTextField`, `JPasswordField`, `JTextArea` | 키보드 입력(키 누름, 해제, 입력) 감지 |
|
||||
| `FocusListener` | `JTextField`, `JPasswordField`, `JTextArea` | 입력 필드에 포커스가 들어오거나 빠질 때 감지 |
|
||||
| `DocumentListener` | `JTextField`, `JPasswordField`, `JTextArea` | 텍스트 변경 감지 (입력, 삭제, 변경) |
|
||||
|
||||
---
|
||||
|
||||
## **3. Swing 입력 컴포넌트 예제 코드와 설명**
|
||||
|
||||
### **(1) `JTextField` 예제 – 입력값을 버튼 클릭 시 표시**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class TextFieldExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JTextField Example");
|
||||
frame.setSize(300, 150);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JLabel label = new JLabel("이름:");
|
||||
JTextField textField = new JTextField(15);
|
||||
JButton button = new JButton("확인");
|
||||
JLabel resultLabel = new JLabel("");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String name = textField.getText();
|
||||
resultLabel.setText("입력된 이름: " + name);
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(label);
|
||||
frame.add(textField);
|
||||
frame.add(button);
|
||||
frame.add(resultLabel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JTextField`에 이름을 입력하고,
|
||||
- 버튼 클릭 시 `JLabel`에 입력된 값이 표시됨.
|
||||
- `ActionListener`를 사용하여 버튼 클릭 이벤트를 처리.
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JPasswordField` 예제 – 비밀번호 입력 확인**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PasswordFieldExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JPasswordField Example");
|
||||
frame.setSize(300, 150);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JLabel label = new JLabel("비밀번호:");
|
||||
JPasswordField passwordField = new JPasswordField(15);
|
||||
JButton button = new JButton("로그인");
|
||||
JLabel resultLabel = new JLabel("");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
char[] password = passwordField.getPassword();
|
||||
if (Arrays.equals(password, "1234".toCharArray())) {
|
||||
resultLabel.setText("로그인 성공!");
|
||||
} else {
|
||||
resultLabel.setText("로그인 실패!");
|
||||
}
|
||||
Arrays.fill(password, ' '); // 보안상 배열 비우기
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(label);
|
||||
frame.add(passwordField);
|
||||
frame.add(button);
|
||||
frame.add(resultLabel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JPasswordField`에 비밀번호를 입력하고,
|
||||
- 버튼 클릭 시 비밀번호가 `"1234"`와 일치하면 "로그인 성공!", 아니면 "로그인 실패!" 출력.
|
||||
- `getPassword()`를 사용하여 보안 강화 (`getText()` 대신 사용).
|
||||
|
||||
---
|
||||
|
||||
### **(3) `JTextArea` 예제 – 여러 줄 입력 필드**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class TextAreaExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("JTextArea Example");
|
||||
frame.setSize(400, 250);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new BorderLayout());
|
||||
|
||||
JTextArea textArea = new JTextArea(10, 30);
|
||||
textArea.setLineWrap(true);
|
||||
textArea.setWrapStyleWord(true);
|
||||
|
||||
JButton button = new JButton("입력 내용 확인");
|
||||
JLabel resultLabel = new JLabel("출력: ");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
resultLabel.setText("출력: " + textArea.getText());
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(new JScrollPane(textArea), BorderLayout.CENTER);
|
||||
frame.add(button, BorderLayout.SOUTH);
|
||||
frame.add(resultLabel, BorderLayout.NORTH);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JTextArea`는 여러 줄 입력이 가능.
|
||||
- `setLineWrap(true)`와 `setWrapStyleWord(true)`를 설정하면 단어 단위로 자동 줄바꿈.
|
||||
- `JScrollPane`을 사용하면 스크롤 기능 추가 가능.
|
||||
- 버튼 클릭 시 입력된 내용을 `JLabel`에 표시.
|
||||
|
||||
---
|
||||
|
||||
### **총정리**
|
||||
- **`JTextField`**: 한 줄 입력 필드, Enter 키 감지 가능.
|
||||
- **`JPasswordField`**: 보안 입력 필드, `getPassword()`를 사용해야 함.
|
||||
- **`JTextArea`**: 여러 줄 입력 가능, 자동 줄바꿈 기능 제공.
|
||||
- **관련 이벤트**: `ActionListener` (Enter 키 감지), `KeyListener` (키 입력 감지), `FocusListener` (포커스 감지).
|
||||
|
||||
위 내용을 기반으로 다양한 입력 폼을 만들 수 있다!
|
||||
215
docs/swing/컴포넌트.md
Normal file
215
docs/swing/컴포넌트.md
Normal file
@@ -0,0 +1,215 @@
|
||||
## **1. 주요 Swing 컴포넌트 메서드 정리**
|
||||
|
||||
### **(1) `JLabel` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 레이블의 텍스트를 설정 |
|
||||
| `getText()` | 레이블의 현재 텍스트 반환 |
|
||||
| `setIcon(Icon icon)` | 아이콘을 설정 |
|
||||
| `getIcon()` | 현재 설정된 아이콘 반환 |
|
||||
| `setHorizontalAlignment(int alignment)` | 텍스트/아이콘의 수평 정렬 설정 (`SwingConstants.LEFT`, `CENTER`, `RIGHT`) |
|
||||
| `setVerticalAlignment(int alignment)` | 텍스트/아이콘의 수직 정렬 설정 (`TOP`, `CENTER`, `BOTTOM`) |
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JButton` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 버튼의 텍스트 설정 |
|
||||
| `getText()` | 버튼의 현재 텍스트 반환 |
|
||||
| `setEnabled(boolean b)` | 버튼 활성화/비활성화 |
|
||||
| `setIcon(Icon icon)` | 버튼의 아이콘 설정 |
|
||||
| `addActionListener(ActionListener l)` | 클릭 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(3) `JToggleButton` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setSelected(boolean b)` | 토글 버튼의 선택 상태 설정 |
|
||||
| `isSelected()` | 현재 선택 상태 반환 |
|
||||
| `addItemListener(ItemListener l)` | 상태 변경 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(4) `JCheckBox` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setSelected(boolean b)` | 체크박스의 선택 여부 설정 |
|
||||
| `isSelected()` | 현재 선택 상태 반환 |
|
||||
| `addItemListener(ItemListener l)` | 체크박스 상태 변경 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(5) `JRadioButton` 메서드**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setSelected(boolean b)` | 라디오 버튼의 선택 상태 설정 |
|
||||
| `isSelected()` | 현재 선택 상태 반환 |
|
||||
| `setText(String text)` | 버튼의 텍스트 설정 |
|
||||
| `addItemListener(ItemListener l)` | 상태 변경 이벤트 리스너 추가 |
|
||||
|
||||
---
|
||||
|
||||
### **(6) `JComponent` 메서드 (모든 Swing 컴포넌트의 공통 메서드)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setBackground(Color c)` | 배경색 설정 |
|
||||
| `setForeground(Color c)` | 전경색(글자색) 설정 |
|
||||
| `setFont(Font f)` | 폰트 설정 |
|
||||
| `setBounds(int x, int y, int width, int height)` | 컴포넌트의 위치와 크기 설정 |
|
||||
| `setToolTipText(String text)` | 툴팁(마우스 오버 시 표시되는 설명) 설정 |
|
||||
|
||||
---
|
||||
|
||||
### **(7) `AbstractButton` 메서드 (JButton, JToggleButton, JCheckBox, JRadioButton의 공통 메서드)**
|
||||
| 메서드 | 설명 |
|
||||
|--------|------------------------------------------|
|
||||
| `setText(String text)` | 버튼의 텍스트 설정 |
|
||||
| `getText()` | 버튼의 현재 텍스트 반환 |
|
||||
| `setIcon(Icon icon)` | 버튼의 아이콘 설정 |
|
||||
| `setSelected(boolean b)` | 선택 여부 설정 (토글 버튼 및 체크박스에서 사용) |
|
||||
| `isSelected()` | 현재 선택 여부 반환 |
|
||||
| `addActionListener(ActionListener l)` | 액션 이벤트 추가 |
|
||||
|
||||
---
|
||||
|
||||
## **2. 관련 이벤트 정리**
|
||||
|
||||
| 이벤트 리스너 | 관련 컴포넌트 | 설명 |
|
||||
|--------------|-------------|----------------------------------|
|
||||
| `ActionListener` | `JButton`, `JToggleButton`, `JCheckBox`, `JRadioButton` | 버튼 클릭 시 동작 |
|
||||
| `ItemListener` | `JToggleButton`, `JCheckBox`, `JRadioButton` | 선택 상태가 변경될 때 동작 |
|
||||
| `MouseListener` | `JLabel`, `JButton`, `JToggleButton`, `JCheckBox`, `JRadioButton` | 마우스 클릭, 이동 등 감지 |
|
||||
|
||||
---
|
||||
|
||||
## **3. Swing 컴포넌트 예제 코드와 설명**
|
||||
|
||||
### **(1) `JLabel`과 `JButton`을 이용한 간단한 GUI**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class SwingExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("Swing Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JLabel label = new JLabel("버튼을 눌러보세요!");
|
||||
JButton button = new JButton("클릭");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
label.setText("버튼이 클릭됨!");
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(label);
|
||||
frame.add(button);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JLabel`을 사용해 텍스트를 출력하고,
|
||||
- `JButton`을 클릭하면 `JLabel`의 텍스트가 변경된다.
|
||||
- `ActionListener`를 사용하여 버튼 이벤트를 감지한다.
|
||||
|
||||
---
|
||||
|
||||
### **(2) `JToggleButton`과 `JCheckBox` 사용 예제**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
|
||||
public class ToggleExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("Toggle Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JToggleButton toggleButton = new JToggleButton("OFF");
|
||||
JCheckBox checkBox = new JCheckBox("체크박스");
|
||||
|
||||
toggleButton.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (toggleButton.isSelected()) {
|
||||
toggleButton.setText("ON");
|
||||
} else {
|
||||
toggleButton.setText("OFF");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(toggleButton);
|
||||
frame.add(checkBox);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JToggleButton`은 클릭할 때마다 "ON"/"OFF"로 상태가 바뀜.
|
||||
- `ItemListener`를 사용하여 상태 변경을 감지.
|
||||
- `JCheckBox`는 별도의 기능 없이 화면에 추가됨.
|
||||
|
||||
---
|
||||
|
||||
### **(3) `JRadioButton`을 사용한 단일 선택 예제**
|
||||
```java
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class RadioButtonExample {
|
||||
public static void main(String[] args) {
|
||||
JFrame frame = new JFrame("Radio Button Example");
|
||||
frame.setSize(300, 200);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
JRadioButton option1 = new JRadioButton("옵션 1");
|
||||
JRadioButton option2 = new JRadioButton("옵션 2");
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
group.add(option1);
|
||||
group.add(option2);
|
||||
|
||||
JButton button = new JButton("선택 확인");
|
||||
JLabel label = new JLabel("선택된 옵션: 없음");
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (option1.isSelected()) {
|
||||
label.setText("선택된 옵션: 옵션 1");
|
||||
} else if (option2.isSelected()) {
|
||||
label.setText("선택된 옵션: 옵션 2");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frame.add(option1);
|
||||
frame.add(option2);
|
||||
frame.add(button);
|
||||
frame.add(label);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
**설명:**
|
||||
- `JRadioButton`을 사용해 하나의 옵션만 선택 가능하도록 `ButtonGroup`을 사용.
|
||||
- `JButton`을 클릭하면 선택된 옵션이 `JLabel`에 표시됨.
|
||||
|
||||
---
|
||||
|
||||
이제 **Swing의 주요 컴포넌트, 이벤트, 예제 코드**까지 완벽하게 정리되었다.
|
||||
155
docs/모듈.md
Normal file
155
docs/모듈.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# **자바 모듈 시스템(Module System) 완벽 정리**
|
||||
|
||||
## **1. 자바 모듈이란?**
|
||||
자바 9에서 도입된 **모듈 시스템**은 **코드를 논리적으로 분리**하고, **불필요한 의존성을 줄이며**, **캡슐화를 강화**하는 기능이다.
|
||||
기존의 **JAR 파일을 묶는 개념**이 아니라, 모듈 자체가 **의존성 관리 및 접근 제어 기능**을 갖는다.
|
||||
|
||||
---
|
||||
|
||||
## **2. 모듈의 핵심 개념**
|
||||
### ✅ **모듈의 구성 요소**
|
||||
1. `module-info.java` 파일 (모듈의 정의 파일)
|
||||
2. 패키지 (캡슐화할 클래스 포함)
|
||||
3. 다른 모듈에 대한 의존성 선언
|
||||
|
||||
### ✅ **모듈의 주요 키워드**
|
||||
| 키워드 | 설명 |
|
||||
|--------|------|
|
||||
| `module` | 모듈을 정의하는 키워드 |
|
||||
| `requires` | 다른 모듈을 가져올 때 사용 |
|
||||
| `exports` | 특정 패키지를 외부에서 사용 가능하도록 공개 |
|
||||
| `opens` | 리플렉션(Reflection)을 허용 (ex: Jackson, Hibernate) |
|
||||
| `provides ... with` | 서비스 제공자 패턴 구현 시 사용 |
|
||||
| `uses` | 특정 서비스 인터페이스 사용 시 명시 |
|
||||
|
||||
---
|
||||
|
||||
## **3. 모듈 예제**
|
||||
### ✅ **모듈 프로젝트 구성**
|
||||
다음과 같은 두 개의 모듈이 있다고 가정하자.
|
||||
- **`com.example.moduleA`** (API를 제공하는 모듈)
|
||||
- **`com.example.moduleB`** (`moduleA`의 기능을 사용하는 모듈)
|
||||
|
||||
📁 **프로젝트 구조**
|
||||
```
|
||||
/project
|
||||
├── moduleA
|
||||
│ ├── src/com/example/moduleA/MyService.java
|
||||
│ ├── src/module-info.java
|
||||
│
|
||||
├── moduleB
|
||||
│ ├── src/com/example/moduleB/MainApp.java
|
||||
│ ├── src/module-info.java
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ **1. `moduleA` (API 제공 모듈)**
|
||||
📌 **`moduleA`는 `MyService` 클래스를 제공**하며, `moduleB`에서 사용 가능하도록 공개해야 한다.
|
||||
|
||||
#### 📌 **📄 moduleA/src/module-info.java**
|
||||
```java
|
||||
module com.example.moduleA {
|
||||
exports com.example.moduleA; // 패키지 공개
|
||||
}
|
||||
```
|
||||
|
||||
#### 📌 **📄 moduleA/src/com/example/moduleA/MyService.java**
|
||||
```java
|
||||
package com.example.moduleA;
|
||||
|
||||
public class MyService {
|
||||
public String getMessage() {
|
||||
return "Hello from Module A!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ **2. `moduleB` (API 사용 모듈)**
|
||||
📌 **`moduleB`는 `moduleA`의 클래스를 사용**하므로, `requires` 키워드로 `moduleA`를 포함해야 한다.
|
||||
|
||||
#### 📌 **📄 moduleB/src/module-info.java**
|
||||
```java
|
||||
module com.example.moduleB {
|
||||
requires com.example.moduleA; // moduleA를 사용하기 위해 선언
|
||||
}
|
||||
```
|
||||
|
||||
#### 📌 **📄 moduleB/src/com/example/moduleB/MainApp.java**
|
||||
```java
|
||||
package com.example.moduleB;
|
||||
|
||||
import com.example.moduleA.MyService;
|
||||
|
||||
public class MainApp {
|
||||
public static void main(String[] args) {
|
||||
MyService service = new MyService();
|
||||
System.out.println(service.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. 모듈 프로젝트 실행 방법**
|
||||
📌 터미널에서 모듈을 직접 컴파일하고 실행할 수도 있다.
|
||||
|
||||
### ✅ **1. 모듈별 컴파일**
|
||||
```sh
|
||||
# moduleA 컴파일
|
||||
javac -d out/moduleA src/moduleA/module-info.java src/moduleA/com/example/moduleA/MyService.java
|
||||
|
||||
# moduleB 컴파일 (moduleA를 classpath에 추가)
|
||||
javac -d out/moduleB --module-path out/moduleA src/moduleB/module-info.java src/moduleB/com/example/moduleB/MainApp.java
|
||||
```
|
||||
|
||||
### ✅ **2. 프로그램 실행**
|
||||
```sh
|
||||
java --module-path out/moduleA:out/moduleB --module com.example.moduleB/com.example.moduleB.MainApp
|
||||
```
|
||||
✔ `Hello from Module A!` 출력
|
||||
|
||||
---
|
||||
|
||||
## **5. `exports`와 `opens` 차이점**
|
||||
| 키워드 | 설명 |
|
||||
|--------|------|
|
||||
| `exports 패키지명;` | 패키지를 외부 모듈에서 사용할 수 있도록 공개 |
|
||||
| `opens 패키지명;` | 리플렉션(Reflection)으로 접근 가능하지만 일반적인 import는 불가능 |
|
||||
|
||||
📌 예를 들어, **Jackson, Hibernate 같은 프레임워크는 리플렉션을 사용**하므로 `opens`가 필요하다.
|
||||
```java
|
||||
module com.example.moduleA {
|
||||
opens com.example.moduleA to jackson.databind;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **6. 모듈 시스템의 장점**
|
||||
✅ **1. 강력한 캡슐화:**
|
||||
필요한 패키지만 `exports` 가능하여, **불필요한 내부 코드 노출 방지**
|
||||
|
||||
✅ **2. 명확한 의존성 관리:**
|
||||
`requires` 키워드로 **명확한 모듈 간 의존성**을 선언
|
||||
|
||||
✅ **3. 경량화된 런타임:**
|
||||
JVM이 필요 없는 모듈을 제외하고 실행 가능 (예: `jlink` 사용)
|
||||
|
||||
---
|
||||
|
||||
## **7. 모듈 시스템의 단점**
|
||||
❌ **1. 기존 라이브러리와의 호환성 문제:**
|
||||
기존 JAR 파일 중에는 **모듈 시스템을 지원하지 않는 것들이 있음**
|
||||
|
||||
❌ **2. 학습 곡선이 존재:**
|
||||
모듈 시스템을 처음 접하는 경우, **기존 classpath 방식보다 설정이 다소 복잡**
|
||||
|
||||
---
|
||||
|
||||
## **8. 결론**
|
||||
자바 모듈 시스템은 **의존성을 명확하게 관리하고 캡슐화를 강화**하는 강력한 기능이다.
|
||||
하지만, 기존 라이브러리와의 호환성을 고려해야 하며, **소규모 프로젝트에서는 필요하지 않을 수도 있다.**
|
||||
대규모 프로젝트에서는 **모듈을 도입하면 유지보수성과 성능이 향상**될 수 있다.
|
||||
156
docs/자바 스트림 API .md
Normal file
156
docs/자바 스트림 API .md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Java Stream API
|
||||
|
||||
Java의 **Stream API**는 컬렉션, 배열 등에서 요소를 효율적으로 처리할 수 있도록 제공되는 기능이다.
|
||||
Stream은 **중간 연산(Intermediate Operation)**과 **최종 연산(Terminal Operation)**으로 나뉘며,
|
||||
스트림을 사용하면 **반복문 없이 데이터 변환, 필터링, 집계 등을 간결하게 처리 가능**하다.
|
||||
|
||||
|
||||
## Stream API 메서드
|
||||
|
||||
### 스트림 생성 메서드
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `Stream.of(T... values)` | 여러 개의 값을 직접 입력하여 스트림 생성 |
|
||||
| `Stream.ofNullable(T t)` | 값이 `null`이면 비어있는 스트림 반환 |
|
||||
| `Arrays.stream(T[] array)` | 배열을 스트림으로 변환 |
|
||||
| `Collection.stream()` | 컬렉션(List, Set 등)에서 스트림 생성 |
|
||||
| `Collection.parallelStream()` | 병렬 스트림 생성 |
|
||||
| `IntStream.range(int start, int end)` | 특정 범위의 숫자 스트림 생성 (end 미포함) |
|
||||
| `IntStream.rangeClosed(int start, int end)` | 특정 범위의 숫자 스트림 생성 (end 포함) |
|
||||
| `Stream.iterate(T seed, UnaryOperator<T> f)` | 초기값과 람다식을 사용하여 무한 스트림 생성 |
|
||||
| `Stream.generate(Supplier<T> s)` | Supplier를 사용해 무한 스트림 생성 |
|
||||
|
||||
|
||||
### 필터링 및 매핑 (중간 연산)
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `filter(Predicate<T> predicate)` | 조건에 맞는 요소만 포함하는 스트림 반환 |
|
||||
| `map(Function<T, R> mapper)` | 각 요소를 변환하여 새로운 스트림 생성 |
|
||||
| `flatMap(Function<T, Stream<R>> mapper)` | 각 요소를 스트림으로 변환한 후 하나의 스트림으로 결합 |
|
||||
| `distinct()` | 중복 요소 제거 |
|
||||
| `sorted()` | 요소들을 정렬 (기본 정렬 기준 사용) |
|
||||
| `sorted(Comparator<T> comparator)` | 지정한 비교자를 사용하여 정렬 |
|
||||
| `peek(Consumer<T> action)` | 스트림의 각 요소를 확인 (디버깅용) |
|
||||
|
||||
|
||||
### 요소 제한 및 조작 (중간 연산)
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `limit(long maxSize)` | 지정된 개수만큼 요소를 제한 |
|
||||
| `skip(long n)` | 앞의 n개 요소를 건너뛰고 나머지 반환 |
|
||||
| `takeWhile(Predicate<T> predicate)` | 조건을 만족하는 동안 요소를 유지하고 이후 요소는 제외 |
|
||||
| `dropWhile(Predicate<T> predicate)` | 조건을 만족하는 동안 요소를 버리고 이후 요소만 유지 |
|
||||
|
||||
|
||||
### 집계 및 종료 연산 (최종 연산)
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `count()` | 스트림 내 요소 개수 반환 |
|
||||
| `min(Comparator<T> comparator)` | 최소값 반환 |
|
||||
| `max(Comparator<T> comparator)` | 최대값 반환 |
|
||||
| `findFirst()` | 첫 번째 요소 반환 (Optional) |
|
||||
| `findAny()` | 임의의 요소 반환 (Optional) |
|
||||
| `allMatch(Predicate<T> predicate)` | 모든 요소가 조건을 만족하는지 검사 (boolean) |
|
||||
| `anyMatch(Predicate<T> predicate)` | 하나 이상의 요소가 조건을 만족하는지 검사 (boolean) |
|
||||
| `noneMatch(Predicate<T> predicate)` | 모든 요소가 조건을 만족하지 않는지 검사 (boolean) |
|
||||
| `forEach(Consumer<T> action)` | 각 요소에 대해 작업 수행 (최종 연산) |
|
||||
|
||||
|
||||
### 스트림을 컬렉션으로 변환
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `toArray()` | 스트림을 배열로 변환 |
|
||||
| `collect(Collectors.toList())` | 스트림을 리스트로 변환 |
|
||||
| `collect(Collectors.toSet())` | 스트림을 집합(Set)으로 변환 |
|
||||
| `collect(Collectors.toMap(Function<T,K>, Function<T,V>))` | 스트림을 Map으로 변환 |
|
||||
|
||||
|
||||
### 요소를 조합하여 결과 생성
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `reduce(BinaryOperator<T> accumulator)` | 요소를 하나의 값으로 축약 |
|
||||
| `reduce(T identity, BinaryOperator<T> accumulator)` | 초기값을 사용하여 축약 |
|
||||
| `reduce(T identity, BiFunction<T, U, T> accumulator, BinaryOperator<T> combiner)` | 병렬 처리용 축약 연산 |
|
||||
|
||||
|
||||
## Stream API 예제
|
||||
|
||||
### 스트림 생성 및 변환
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
public class StreamExample {
|
||||
public static void main(String[] args) {
|
||||
// 리스트에서 스트림 생성
|
||||
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
|
||||
|
||||
// 이름 길이가 4 이상인 요소 필터링
|
||||
List<String> result = names.stream()
|
||||
.filter(name -> name.length() >= 4)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println(result); // 출력: [Alice, Charlie, David]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### `map()`을 이용한 변환
|
||||
```java
|
||||
List<String> words = Arrays.asList("apple", "banana", "cherry");
|
||||
|
||||
List<Integer> lengths = words.stream()
|
||||
.map(String::length)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println(lengths); // 출력: [5, 6, 6]
|
||||
```
|
||||
|
||||
|
||||
### `flatMap()`을 이용한 중첩 리스트 평탄화
|
||||
```java
|
||||
List<List<String>> listOfLists = Arrays.asList(
|
||||
Arrays.asList("a", "b"),
|
||||
Arrays.asList("c", "d"),
|
||||
Arrays.asList("e", "f")
|
||||
);
|
||||
|
||||
List<String> flatList = listOfLists.stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println(flatList); // 출력: [a, b, c, d, e, f]
|
||||
```
|
||||
|
||||
|
||||
### `reduce()`를 이용한 합산
|
||||
```java
|
||||
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
|
||||
|
||||
int sum = numbers.stream()
|
||||
.reduce(0, Integer::sum);
|
||||
|
||||
System.out.println(sum); // 출력: 15
|
||||
```
|
||||
|
||||
|
||||
### `groupingBy()`를 이용한 그룹화
|
||||
```java
|
||||
List<String> items = Arrays.asList("apple", "banana", "apple", "orange", "banana");
|
||||
|
||||
Map<String, Long> itemCount = items.stream()
|
||||
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
|
||||
|
||||
System.out.println(itemCount);
|
||||
// 출력: {banana=2, apple=2, orange=1}
|
||||
```
|
||||
|
||||
|
||||
## 정리
|
||||
- **Java Stream API**는 **중간 연산**과 **최종 연산**을 조합하여 데이터를 효과적으로 변환하고 처리하는 도구다.
|
||||
- **`filter()`**, **`map()`**, **`flatMap()`**을 사용하여 데이터를 가공할 수 있다.
|
||||
- **`collect()`**를 사용하여 스트림을 **리스트, 셋, 맵**으로 변환할 수 있다.
|
||||
- **`reduce()`**, **`groupingBy()`** 등의 메서드를 활용하면 데이터 집계를 손쉽게 수행할 수 있다.
|
||||
- 대량의 데이터를 **간결하고 효율적으로 처리하는 경우 Stream API를 적극 활용**하자!
|
||||
@@ -19,3 +19,4 @@ include(
|
||||
"model-mapper", "slf4j"
|
||||
)
|
||||
include("feed")
|
||||
include("user-agent-parser")
|
||||
|
||||
9
user-agent-parser/build.gradle.kts
Normal file
9
user-agent-parser/build.gradle.kts
Normal file
@@ -0,0 +1,9 @@
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/nl.basjes.parse.useragent/yauaa
|
||||
implementation("com.github.ua-parser:uap-java:1.6.1")
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package kr.pe.elex.ua_parser;
|
||||
|
||||
import ua_parser.Client;
|
||||
import ua_parser.Parser;
|
||||
|
||||
public class UserAgentParser {
|
||||
|
||||
public static void main(String... args){
|
||||
final String uaStr = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36,gzip(gfe)";
|
||||
|
||||
Parser parser = new Parser();
|
||||
Client client = parser.parse(uaStr);
|
||||
System.out.println(client);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user