# JSP 보충자료

### JSP 관련 라이브러리

#### 1. **`tomcat-embed-jasper`**

```properties
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
```

* **용도**:
  * JSP 파일을 처리하기 위한 JSP 컴파일러입니다.
  * Spring Boot는 기본적으로 내장 Tomcat을 사용하며, JSP를 구동하려면 이 의존성을 추가해야 합니다.
* **기능**:
  * JSP 파일을 `.java` 파일로 컴파일하고 이를 서블릿으로 변환합니다.
  * JSP 실행 엔진 역할을 합니다.

#### 2. **`jakarta.servlet.jsp.jstl-api`**

```properties
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
```

* **용도**:
  * JSTL(JavaServer Pages Standard Tag Library)의 API를 제공합니다.
* **기능**:
  * JSP 페이지에서 JSTL 태그(예: `<c:forEach>`, `<c:if>`)를 사용할 수 있도록 지원합니다.

#### 3. **`org.glassfish.web:jakarta.servlet.jsp.jstl`**

```properties
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
```

* **용도**:
  * JSTL의 실제 구현체입니다.
* **기능**:
  * API에서 제공하는 태그를 실행하기 위한 구현체로, JSTL 태그를 JSP 페이지에서 사용할 때 필요한 라이브러리입니다.

#### 4. **`jakarta.servlet-api`**

```properties
implementation 'jakarta.servlet:jakarta.servlet-api'
```

* **용도**:
  * 서블릿 API를 제공합니다.
* **기능**:
  * JSP는 서블릿 위에서 동작하기 때문에 서블릿 API가 필수적입니다.
  * JSP와 서블릿의 상호작용을 처리합니다.

## **JSP의 동작 과정**

**1. JSP 요청 처리 흐름**

1. **클라이언트 요청**:
   * 클라이언트가 브라우저에서 JSP 파일(예: `example.jsp`)에 대한 요청을 보냅니다.
   * 예: `http://localhost:8080/example.jsp`.
2. **JSP 파일 처리 (서버)**:
   * 웹 컨테이너(Tomcat 등)가 JSP 파일을 처리하기 위해 다음 단계를 수행합니다:
     1. JSP 파일을 **서블릿 자바 코드로 변환**.
     2. 변환된 자바 코드를 **컴파일**하여 `.class` 파일 생성.
     3. 생성된 클래스 파일을 로드하고 인스턴스화하여 실행.
3. **서블릿 실행**:
   * 변환된 서블릿이 클라이언트 요청을 처리하고 동적인 HTML 또는 데이터를 생성.
4. **응답 반환**:
   * 생성된 HTML이 클라이언트(브라우저)로 전송되어 웹 페이지로 렌더링됩니다.

***

#### **JSP의 내부 동작: 서블릿과의 연관**

**1. JSP가 서블릿으로 변환**

* JSP 파일은 실제로 **`HttpServlet`** 클래스를 상속받는 서블릿 코드로 변환됩니다.
* JSP 파일의 각 부분은 서블릿 코드로 매핑됩니다:
  * **HTML**: `out.print()` 메서드로 변환.
  * **JSP 스크립트릿(Java 코드)**: 서블릿의 메서드 내부에 포함.

**예제 JSP 파일** (`example.jsp`):

```html
<html>
<body>
    <h1>Welcome!</h1>
    <%
        String username = "John";
        out.println("Hello, " + username + "!");
    %>
</body>
</html>
```

**변환된 서블릿 코드** (`example_jsp.java`):

```java
public final class example_jsp extends HttpServlet {
    public void _jspService(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        response.setContentType("text/html");
        JspWriter out = response.getWriter();
        try {
            out.write("<html><body>");
            out.write("<h1>Welcome!</h1>");
            String username = "John";
            out.println("Hello, " + username + "!");
            out.write("</body></html>");
        } catch (Throwable t) {
            throw new ServletException(t);
        }
    }
}
```

***

**2. 서블릿의 생명주기와 JSP**

JSP가 서블릿으로 변환되면, 해당 서블릿은 일반 서블릿처럼 생명주기를 가집니다:

1. **초기화** (`init()` 메서드):
   * JSP 서블릿이 메모리에 로드되며 한 번만 실행됩니다.
2. **서비스** (`service()` 또는 `_jspService()`):
   * 클라이언트 요청을 처리하며, HTTP 요청(GET, POST 등)에 따라 동작.
3. **종료** (`destroy()`):
   * JSP 서블릿이 메모리에서 해제될 때 호출됩니다.

***

#### **JSP와 서블릿의 비교**

| **특징**       | **JSP**                      | **서블릿**                |
| ------------ | ---------------------------- | ---------------------- |
| **코드 작성 방식** | HTML에 Java 코드를 삽입            | 순수 Java 코드로 HTML을 생성   |
| **목적**       | HTML 중심 페이지에 동적인 Java 코드를 추가 | Java 중심으로 HTML 페이지를 생성 |
| **변환 과정**    | JSP → 서블릿으로 변환 후 실행          | 직접 작성한 Java 클래스 실행     |
| **가독성**      | HTML 중심이므로 비개발자도 수정 가능       | Java 중심이므로 개발자 친화적     |

***

#### **JSP 동작 과정 상세**

1. **JSP 파일 요청**:
   * 클라이언트가 `example.jsp`를 요청.
2. **JSP 파일이 서블릿으로 변환**:
   * 웹 컨테이너는 JSP를 Java 서블릿 코드로 변환합니다.
   * 변환된 서블릿 파일은 `/work` 디렉토리 같은 임시 폴더에 저장됩니다.
     * 예: `apache-tomcat/work/Catalina/localhost/yourapp/org/apache/jsp/example_jsp.java`.
3. **컴파일**:
   * 변환된 서블릿 파일(`example_jsp.java`)이 컴파일되어 `.class` 파일이 생성됩니다.
4. **서블릿 실행**:
   * 생성된 서블릿이 요청을 처리하고 HTML 응답을 생성합니다.
5. **결과 반환**:
   * 서버는 응답 데이터를 클라이언트에게 전송하여 웹 페이지를 표시합니다.

## 레이어드 아키텍처(Layered Architecture)

#### **레이어드 아키텍처(Layered Architecture)란?**

레이어드 아키텍처는 애플리케이션을 **책임과 역할**에 따라 여러 계층으로 나누어 설계하는 방법입니다. 각 계층은 독립적으로 동작하며, 서로 명확하게 정의된 인터페이스를 통해 통신합니다.

***

#### **주요 레이어**

1. **Domain Layer (도메인 계층)**:
   * 애플리케이션의 **핵심 비즈니스 로직**과 **도메인 모델**을 정의.
   * Entity, Value Object, Aggregate Root 등의 객체가 포함.
   * 비즈니스 규칙, 상태 관리, 핵심 기능을 캡슐화.
2. **Repository Layer (저장소 계층)**:
   * 데이터베이스와 도메인 계층 간의 **데이터 접근 로직**을 관리.
   * CRUD 작업을 담당하며, 도메인 객체의 영속성을 처리.
3. **Service Layer (서비스 계층)**:
   * 도메인 계층과 애플리케이션 계층을 연결하는 중간 계층.
   * 비즈니스 로직과 트랜잭션 관리를 처리하며, 도메인 계층의 여러 기능을 조합.
4. **Presentation Layer (프레젠테이션 계층)**:
   * 사용자 인터페이스와 관련된 모든 로직.
   * 사용자의 요청을 받아 Service Layer로 전달하고, 결과를 반환.
5. **Application Layer (애플리케이션 계층)**:
   * 도메인 로직을 실행하고 흐름을 관리.
   * 주로 애플리케이션의 유스케이스를 구현.

***

#### **계층 나눔의 표현**

1. **레이어드 아키텍처** (Layered Architecture):
   * 애플리케이션을 논리적 계층으로 분리.
   * 예: Presentation Layer, Service Layer, Repository Layer, Domain Layer.
2. **DDD 용어**:
   * **Domain Layer**:
     * 비즈니스 로직과 핵심 개념을 관리.
   * **Infrastructure Layer**:
     * 데이터베이스 및 외부 시스템과의 통신을 관리.
   * **Application Layer**:
     * 유스케이스를 구현하고 흐름을 관리.
3. **Hexagonal Architecture (Ports and Adapters)**:
   * 계층 아키텍처를 더 유연하게 표현한 구조.
   * Domain은 중심이고, Repository와 Service는 Port와 Adapter로 구분.

***

#### **레이어드 아키텍처를 나누는 이유**

* **책임 분리(SRP)**: 각 레이어가 특정 역할에 집중.
* **재사용성**: 특정 계층의 로직을 다른 프로젝트에서도 재사용 가능.
* **테스트 용이성**: 각 계층을 독립적으로 테스트 가능.
* **유지보수성**: 계층 간 의존성이 낮아, 수정 시 다른 계층에 영향이 적음.

## **Static Import란?**

**Static Import**는 Java에서 특정 클래스의 **static 멤버**(필드나 메서드)를 호출할 때 **클래스 이름 없이 직접 사용할 수 있게 해주는 기능**입니다. 일반적인 import와 달리 **`static` 키워드**를 사용하여 특정 클래스의 static 멤버를 가져옵니다.

***

#### **1. Static Import의 문법**

```java
import static 패키지명.클래스명.필드명;
import static 패키지명.클래스명.메서드명;
```

또는, 클래스의 모든 static 멤버를 가져오려면 다음과 같이 사용합니다:

```java
import static 패키지명.클래스명.*;
```

***

#### **2. Static Import의 동작 방식**

* Static Import는 클래스의 static 멤버를 전역 변수 또는 전역 메서드처럼 사용할 수 있게 합니다.
* 이를 통해 코드의 가독성을 높이고 반복적인 클래스 이름 입력을 줄일 수 있습니다.

***

#### **3. Static Import의 사용 예제**

**예제 1: Math 클래스**

`Math` 클래스의 static 메서드와 필드 사용 예제:

```java
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

public class StaticImportExample {
    public static void main(String[] args) {
        double radius = 5;
        double area = PI * radius * radius;  // Math.PI 대신 PI 사용
        double root = sqrt(16);              // Math.sqrt 대신 sqrt 사용

        System.out.println("Area: " + area);
        System.out.println("Square Root: " + root);
    }
}
```

* **`Math.PI` → `PI`**: 클래스 이름 없이 사용.
* **`Math.sqrt` → `sqrt`**: 클래스 이름 없이 사용.

***

**예제 2: Assertions (JUnit Test)**

JUnit 테스트에서 static import를 사용해 간결한 코드를 작성:

```java
import static org.junit.jupiter.api.Assertions.assertEquals;

class StaticImportTest {
    @Test
    void testAddition() {
        assertEquals(4, 2 + 2);  // Assertions.assertEquals 대신 간단히 assertEquals 사용
    }
}
```

* **`Assertions.assertEquals` → `assertEquals`**: 간결하게 표현 가능.

## **ConcurrentHashMap: 개요**

`ConcurrentHashMap`은 **Java의 동시성 컬렉션** 중 하나로, **멀티스레드 환경에서 안전하게 사용**할 수 있는 Map 구현체입니다. 여러 스레드가 동시에 접근해도 데이터의 일관성과 안정성을 보장하면서도 높은 성능을 제공합니다.

***

#### **1. 주요 특징**

1. **멀티스레드 안전(Thread-safe)**:
   * 내부적으로 **세그먼트 락(Segment Lock)** 또는 **버킷 락킹** 메커니즘을 사용하여 동기화를 제공합니다.
   * Map 전체를 잠그는 대신, 특정 버킷에만 락을 걸어 성능을 최적화.
2. **Non-blocking 읽기(Read)**:
   * 대부분의 읽기 연산(`get`)은 락을 사용하지 않고 비차단(non-blocking) 방식으로 수행됩니다.
3. **성능 최적화**:
   * 동기화된 Map(`Collections.synchronizedMap()`)에 비해 더 높은 동시성을 제공합니다.
   * 내부적으로 **CAS(Compare-And-Swap)** 연산과 **락 스트라이핑(Lock Stripping)** 기법을 사용해 성능 향상.
4. **NULL 키와 값 허용 불가**:
   * `null` 키와 `null` 값을 허용하지 않습니다.

***

#### **2. 동작 원리**

**기존 `Hashtable`과의 차이점**

* `Hashtable`은 모든 메서드에서 Map 전체를 동기화(Synchronized)하여 동시성을 보장하지만, 성능이 낮습니다.
* `ConcurrentHashMap`은 데이터를 여러 세그먼트로 나누고 필요한 세그먼트에만 락을 걸어 동시성 이슈를 해결하면서 성능을 유지합니다.

**락 스트라이핑(Lock Stripping)**

* `ConcurrentHashMap`은 Map의 데이터를 여러 **버킷**(Segment)으로 분리합니다.
* 각 버킷은 독립적으로 락이 걸리므로, 다른 스레드가 동시에 다른 버킷에 접근할 수 있습니다.
* 예: 한 스레드가 `key1`을 업데이트하고 있을 때, 다른 스레드는 `key2`에 자유롭게 접근 가능.

**CAS(Compare-And-Swap)**

* 쓰기 연산 중에도 일부는 **CAS**를 사용하여 락 없이 동작할 수 있습니다.
* 예: 특정 버킷에서 값 삽입이나 교체 시 CAS로 값을 안전하게 업데이트.

***

#### **3. 주요 메서드**

**1) 데이터 삽입**

```java
map.put(key, value);
```

* 키와 값을 Map에 삽입. 필요 시 락을 걸어 동기화를 보장.

**2) 데이터 읽기**

```java
map.get(key);
```

* 락 없이 데이터를 읽음. 매우 빠른 비차단(non-blocking) 동작.

**3) 데이터 삭제**

```java
map.remove(key);
```

* 특정 키에 해당하는 데이터를 삭제.

**4) 동시 업데이트**

* `compute`, `merge`, `putIfAbsent` 등의 메서드를 통해 동시 업데이트를 안전하게 수행.

```java
map.compute(key, (k, v) -> (v == null) ? 1 : v + 1); // 값이 없으면 1, 있으면 1 증가
```

**5) 데이터 탐색**

```java
map.forEach(1, (key, value) -> {
    System.out.println(key + ": " + value);
});
```

* 병렬적으로 데이터를 탐색 가능. (파라미터는 병렬 스레드 수)

***

#### **4. 사용해야 하는 이유**

**1) 멀티스레드 환경에서 안전한 Map 필요**

* 여러 스레드가 동시에 Map을 읽고 쓰는 상황에서 `ConcurrentHashMap`은 동기화 문제를 해결합니다.
* 예: 웹 애플리케이션의 캐시, 상태 관리.

**2) 성능 최적화**

* 기존의 `Hashtable`이나 `synchronizedMap`보다 더 높은 동시성과 성능 제공.
* 읽기와 쓰기를 동시에 수행해야 하는 환경에서 효율적.

**3) 데이터 무결성 유지**

* 여러 스레드가 값을 삽입하거나 업데이트하는 상황에서도 데이터의 무결성을 유지.

***

#### **5. 사용 사례**

**1) 애플리케이션 캐시**

* 데이터를 읽는 작업이 많고 쓰는 작업이 적은 경우.
* 예: 웹 애플리케이션에서 자주 조회되는 데이터를 캐싱.

```java
ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
cache.put("user1", "John");
cache.put("user2", "Jane");

String user = cache.get("user1"); // 빠른 읽기
```

**2) 실시간 사용자 세션 관리**

* 채팅 애플리케이션 등에서 사용자의 세션 데이터를 저장.

```java
ConcurrentHashMap<String, UserSession> sessions = new ConcurrentHashMap<>();
sessions.put(sessionId, new UserSession(userId));
```

**3) 통계 데이터 수집**

* 다수의 스레드가 동시에 통계 데이터를 업데이트.

```java
ConcurrentHashMap<String, Integer> stats = new ConcurrentHashMap<>();
stats.compute("hits", (key, value) -> (value == null) ? 1 : value + 1);
```

**4) 동시성 제어**

* 여러 작업이 동시에 진행되는 환경에서 값 업데이트 및 제어.

```java
map.putIfAbsent("key1", "value1"); // 키가 없으면 삽입
map.computeIfPresent("key1", (k, v) -> v + "_updated"); // 키가 있을 때 값 수정
```

***

#### **6. 주의사항**

1. **NULL 키와 값 허용 불가**:
   * `ConcurrentHashMap`은 `null` 키나 값을 허용하지 않습니다.
   * 이유: 동시성 환경에서 `null` 처리는 혼란을 초래할 수 있기 때문.
2. **전체 락 필요 작업**:
   * `size()` 또는 `containsValue()` 같은 작업은 전체 Map을 순회하므로 성능이 저하될 수 있습니다.
3. **일관성 제한**:
   * 병렬 접근 중 일부 작업에서 최신 상태가 보장되지 않을 수 있습니다.

***

#### **7. 예제 코드**

**멀티스레드 환경에서의 활용**

```java
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        // 쓰레드 1: 값 추가
        Thread t1 = new Thread(() -> {
            map.put("A", 1);
            map.put("B", 2);
        });

        // 쓰레드 2: 값 업데이트
        Thread t2 = new Thread(() -> {
            map.compute("A", (key, value) -> (value == null) ? 1 : value + 1);
        });

        // 쓰레드 3: 읽기
        Thread t3 = new Thread(() -> {
            System.out.println("Value of A: " + map.get("A"));
        });

        t1.start();
        t2.start();
        t3.start();
    }
}
```

***

#### \*\*8. `ConcurrentHashMap` vs `synchronizedMap` vs `Hashtable`

| **특징**          | **ConcurrentHashMap** | **synchronizedMap** | **Hashtable** |
| --------------- | --------------------- | ------------------- | ------------- |
| **멀티스레드 안전**    | 예                     | 예                   | 예             |
| **성능**          | 높음                    | 낮음 (Map 전체 락)       | 낮음 (Map 전체 락) |
| **NULL 키/값 허용** | 허용하지 않음               | 허용 가능               | 허용하지 않음       |
| **부분 락 지원**     | 예 (버킷 단위 락)           | 아니요 (전체 락)          | 아니요 (전체 락)    |
| **주요 사용 사례**    | 고성능 멀티스레드 환경          | 간단한 동기화 필요          | 레거시 코드        |

***

#### **결론**

* **왜 사용해야 하는가?**
  * 멀티스레드 환경에서 Map을 동기화하며, 동시에 높은 성능을 제공하기 때문.
* **사용처**:
  * 실시간 데이터 처리, 캐싱, 통계 수집, 세션 관리 등 동시성 이슈가 발생하는 상황.

## **Synchronized 키워드 정리**

\*\*`synchronized`\*\*는 멀티스레드 환경에서 \*\*동기화(Synchronization)\*\*를 제공하여 여러 스레드가 공유 자원에 동시에 접근할 때 발생하는 문제를 방지합니다. 이를 통해 데이터의 무결성과 일관성을 유지할 수 있습니다.

***

#### **1. `synchronized`란?**

1. **임계영역 (Critical Section)**:
   * 여러 스레드가 공유 자원에 동시에 접근하지 못하도록 제한하는 코드 영역.
   * `synchronized`는 임계영역을 보호하여 한 번에 **한 스레드만** 실행 가능하게 합니다.
2. **모니터 락 (Monitor Lock)**:
   * Java에서 모든 객체는 \*\*모니터 락(Monitor Lock)\*\*을 가지고 있습니다.
   * `synchronized` 키워드는 이 락을 이용해 스레드 접근을 제어합니다.
   * 스레드가 `synchronized` 영역에 진입하려면 해당 객체의 락을 획득해야 합니다.
3. **락 획득 과정**:
   * 스레드는 `synchronized` 메서드나 블록에 진입하려면 객체의 락을 획득.
   * 락이 이미 다른 스레드에 의해 사용 중이면 현재 스레드는 **Blocked 상태**로 대기.
   * 락을 해제하면 대기 중인 스레드 중 하나가 락을 획득하고 실행 가능.

***

#### **2. `synchronized`의 장점과 단점**

**장점**

1. **데이터 무결성 유지**:
   * 여러 스레드가 동시에 데이터를 수정할 때 발생하는 **Race Condition(경합 상태)** 문제 방지.
2. **데이터 일관성 보장**:
   * 스레드 간 데이터 일관성 유지.

**단점**

1. **성능 저하**:
   * 동기화로 인해 스레드가 대기 상태로 전환되므로 실행 속도가 느려질 수 있음.
2. **무한 대기**:
   * 특정 스레드가 락을 반환하지 않으면 다른 스레드가 무한히 대기 상태에 빠질 수 있음.
3. **공정성 문제**:
   * 락이 해제되었을 때 어떤 스레드가 락을 획득할지 예측할 수 없음.

***

#### **3. `synchronized`의 종류와 사용법**

**(1) 메서드 동기화**

* `synchronized` 키워드를 메서드에 붙여 메서드 전체를 임계영역으로 설정.
* 객체의 **인스턴스 락**을 사용.

**예제 코드:**

```java
public class BankAccount {
    private int balance = 100;

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount + ", Remaining Balance: " + balance);
        } else {
            System.out.println("Insufficient Balance.");
        }
    }
}
```

* **특징**:
  * 한 번에 하나의 스레드만 `withdraw` 메서드를 실행 가능.
  * 다른 스레드가 `withdraw` 호출 시 대기.

***

**(2) 블록 동기화**

* `synchronized` 키워드를 특정 코드 블록에 적용.
* 객체의 특정 부분만 보호하여 성능 최적화 가능.

**예제 코드:**

```java
public class BankAccount {
    private int balance = 100;

    public void withdraw(int amount) {
        synchronized (this) {
            if (balance >= amount) {
                balance -= amount;
                System.out.println("Withdrawn: " + amount + ", Remaining Balance: " + balance);
            } else {
                System.out.println("Insufficient Balance.");
            }
        }
    }
}
```

* **특징**:
  * `this` 객체의 락만 필요한 부분에 걸어 성능 최적화.
  * 락의 범위를 최소화하여 불필요한 대기를 줄임.

***

**(3) 클래스 동기화 (정적 메서드)**

* `synchronized`를 정적 메서드에 사용하면 클래스 수준의 락이 적용.
* **클래스 락**(Class Object Lock)을 사용.

**예제 코드:**

```java
public class BankAccount {
    private static int totalBalance = 1000;

    public static synchronized void deposit(int amount) {
        totalBalance += amount;
        System.out.println("Deposited: " + amount + ", Total Balance: " + totalBalance);
    }
}
```

* **특징**:
  * 클래스 전체에서 공유되는 자원(`totalBalance`)에 대해 동기화.
  * 클래스 락을 사용하므로 인스턴스와는 독립적으로 동작.

***

#### **4. `synchronized`를 사용한 문제 해결**

**(1) Race Condition (경합 상태) 방지**

* 여러 스레드가 동시에 공유 자원을 수정할 때 발생하는 문제.
* `synchronized`를 사용해 문제 해결.

**문제 코드:**

```java
public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
```

* 위 코드에서 여러 스레드가 동시에 `increment`를 호출하면 `count` 값이 정확하지 않음.

**해결 코드:**

```java
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}
```

***

**(2) 데이터 일관성 유지**

**문제 상황**:

* 은행 계좌에서 여러 스레드가 동시에 입출금을 처리하는 경우.

**문제 코드:**

```java
public class BankAccount {
    private int balance = 100;

    public void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }
}
```

* 위 코드에서 두 스레드가 동시에 `withdraw`를 호출하면, 잔액이 음수가 될 수 있음.

**해결 코드:**

```java
public class BankAccount {
    private int balance = 100;

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }
}
```

***

#### **5. 주의사항**

1. **지역 변수는 동기화 대상이 아님**:
   * 지역 변수는 스레드마다 별도로 할당되므로 동기화 걱정이 필요 없음.
   * 공유 자원에만 동기화를 적용.
2. **`final` 변수는 동기화 불필요**:
   * `final` 변수는 불변이므로 동기화할 필요 없음.

**예제:**

```java
public void method() {
    final int localVariable = 10; // 동기화 필요 없음
}
```

3. **임계영역 최소화**:
   * 성능 최적화를 위해 동기화 범위를 최소화.
   * 불필요하게 `synchronized`를 남용하지 말 것.

***

#### **6. Synchronized의 단점**

1. **무한 대기 (Deadlock)**:
   * 락을 반환하지 않는 상태가 지속되면 다른 스레드가 무한히 대기.
2. **공정성 문제**:
   * 여러 스레드가 락을 대기할 때 어떤 스레드가 락을 획득할지 알 수 없음.
   * 특정 스레드가 장시간 대기할 가능성 존재.
3. **성능 저하**:
   * 동기화는 성능에 영향을 미침. 특히, 불필요한 동기화는 병목현상을 초래.

***

#### **7. 결론**

* `synchronized`는 멀티스레드 환경에서 데이터 무결성과 일관성을 보장하는 강력한 도구.
* 사용 시 성능 최적화를 위해 **임계영역 최소화**와 **동기화 범위 제한**을 염두에 두어야 함.
* 더 복잡한 동기화 요구사항이 있다면 **ReentrantLock** 또는 **Concurrent Collections**를 고려.

***

```java
public class ConcurrentTest {

        static class SynchronizedExample {
            private int balance = 100;

            public void withdraw(int amount) {
                synchronized (this) {  // 임계영역 최소화
                    if (balance >= amount) {
                        balance -= amount;
                        System.out.println("Withdrawn: " + amount + ", Remaining Balance: " + balance);
                    } else {
                        System.out.println("Insufficient Balance.");
                    }
                }
            }

            public synchronized void deposit(int amount) {  // 메서드 전체 동기화
                balance += amount;
                System.out.println("Deposited: " + amount + ", New Balance: " + balance);
            }

            public synchronized int getBalance() {
                return balance;
            }
        }

    static class SynchronizedExampleWithoutSync {
        private int balance = 100;

        public void withdraw(int amount) {
            if (balance >= amount) {
                try {
                    // 인위적으로 스레드가 멈추도록 해서 동시성을 유발
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                balance -= amount;
                System.out.println("Withdrawn: " + amount + ", Remaining Balance: " + balance);
            } else {
                System.out.println("Insufficient Balance.");
            }
        }

        public void deposit(int amount) {
            balance += amount;
            System.out.println("Deposited: " + amount + ", New Balance: " + balance);
        }

        public int getBalance() {
            return balance;
        }
    }

    @Test
        public void testWithdrawAndDepositSingleThread() {
            SynchronizedExample account = new SynchronizedExample();

            // Test deposit
            account.deposit(50);
            assertEquals(150, account.getBalance());

            // Test withdraw
            account.withdraw(30);
            assertEquals(120, account.getBalance());

            // Test insufficient funds
            account.withdraw(200);  // Should not decrease balance
            assertEquals(120, account.getBalance());
        }

    @Test
    public void testSynchronizedWithdraw() throws InterruptedException {
        SynchronizedExample account = new SynchronizedExample();

        Thread t1 = new Thread(() -> account.withdraw(80));
        Thread t2 = new Thread(() -> account.withdraw(60));

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        // synchronized: 음수 잔액 발생하지 않음
        assertTrue(account.getBalance() >= 0, "Balance should not be negative");
    }

    @Test
    public void testNonSynchronizedWithdraw() throws InterruptedException {
        SynchronizedExampleWithoutSync account = new SynchronizedExampleWithoutSync();

        Thread t1 = new Thread(() -> account.withdraw(80));
        Thread t2 = new Thread(() -> account.withdraw(60));

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        // synchronized 없는 경우: 음수 잔액 발생 가능
        System.out.println("Final Balance: " + account.getBalance());
        assertTrue(account.getBalance() < 0, "Balance should be negative due to race condition");
    }
}

```

## **즉시 평가(Eager Evaluation)와의 차이**

* **즉시 평가 (Eager Evaluation)**:

  * 값이나 표현식을 **즉시 계산**.
  * 대부분의 프로그래밍 언어에서 기본적으로 사용하는 방식.
  * 계산 결과가 실제로 사용되지 않아도 계산이 실행됨.

  **예제 (즉시 평가)**:

  ```java
  int result = 5 * 5;  // 바로 계산되어 result에 25 저장
  ```
* **지연 평가 (Lazy Evaluation)**:

  * 값이나 표현식을 **필요할 때 계산**.
  * 계산이 필요하지 않은 경우 실행되지 않음.
  * 메모리 효율과 실행 속도를 높일 수 있음.

  **예제 (지연 평가)**:

  ```java
  Supplier<Integer> result = () -> 5 * 5; // 계산을 미룸
  System.out.println(result.get());      // 이 시점에서 계산 수행
  ```

***

#### **계산을 미룬다는 것의 의미**

1. **계산을 "나중으로" 미룬다**:

   * 값을 즉시 계산하지 않고, 계산에 필요한 표현식(함수 또는 연산)을 기억해둠.
   * 이후 프로그램 실행 중 해당 값이 필요할 때 계산을 수행.

   **예제**:

   ```java
   Supplier<Integer> lazyValue = () -> 10 + 20; // 계산 미룸
   System.out.println("값을 요청하기 전까지 계산하지 않음");
   System.out.println("결과: " + lazyValue.get()); // 여기서 계산 수행
   ```
2. **필요하지 않으면 계산하지 않는다**:

   * 특정 조건에 따라 값이 사용되지 않으면, 계산이 전혀 실행되지 않음.

   **예제**:

   ```java
   Supplier<Integer> expensiveComputation = () -> {
       System.out.println("계산 수행 중...");
       return 10 + 20;
   };

   // 실제 값이 필요하지 않으면 계산 수행 안 됨
   if (false) {
       System.out.println(expensiveComputation.get()); // 실행되지 않음
   }
   ```

## **`webapp` 디렉터리란?**

1. **Java 웹 애플리케이션의 루트 디렉터리**:
   * `src/main/webapp`은 Maven 및 Gradle 프로젝트의 표준 디렉터리 구조에서 **웹 애플리케이션 리소스**를 배치하는 디렉터리입니다.
   * WAR(Web Application Archive) 파일 생성 시, `webapp` 디렉터리는 애플리케이션의 루트로 간주됩니다.
2. **웹 리소스를 포함**:
   * HTML, CSS, JavaScript, 이미지와 같은 정적 리소스와 JSP 파일을 포함합니다.
   * 서버에 배포될 때 이 디렉터리 구조가 그대로 유지됩니다.

***

#### **`webapp`의 디렉터리 구조**

`webapp` 디렉터리의 표준 구조는 다음과 같습니다:

```css
src/main/webapp/
├── META-INF/
├── WEB-INF/
│   ├── web.xml
│   ├── views/
│   │   └── example.jsp
├── static/
│   ├── css/
│   │   └── styles.css
│   ├── js/
│   │   └── scripts.js
│   └── images/
│       └── logo.png
├── index.html
```

**1. `META-INF/`**

* **역할**:
  * Java 애플리케이션의 메타데이터 파일이 포함됩니다.
* **내용**:
  * 일반적으로 JAR 또는 WAR 파일의 설정 파일을 저장합니다.
  * `MANIFEST.MF` 파일 등이 포함될 수 있습니다.

**2. `WEB-INF/`**

* **역할**:
  * 외부에서 접근할 수 없는 **보안 리소스**를 저장합니다.
* **내용**:
  * `web.xml`: 애플리케이션의 배포 서술자(Deployment Descriptor) 파일.
  * `views/`: JSP 파일을 저장하는 디렉터리로 클라이언트가 직접 접근할 수 없습니다.

**3. `static/`**

* **역할**:
  * 정적 리소스를 저장합니다.
* **내용**:
  * CSS, JavaScript, 이미지 등의 정적 파일.
  * Spring Boot에서는 `src/main/resources/static`에 배치된 정적 리소스가 기본적으로 매핑됩니다.

**4. HTML 파일**

* **역할**:
  * 애플리케이션의 정적 HTML 파일이 포함됩니다.
  * `index.html`은 애플리케이션의 기본 페이지로 사용됩니다.

***

#### **`webapp` 디렉터리의 특징**

1. **서버에서의 동작**:
   * `webapp` 디렉터리는 애플리케이션이 서버에 배포될 때, `ROOT` 디렉터리로 변환됩니다.
   * 예: `src/main/webapp/index.html` → `http://localhost:8080/index.html`
2. **JSP 파일의 처리**:
   * `WEB-INF/views`에 있는 JSP 파일은 직접 접근할 수 없으며, 반드시 서블릿이나 컨트롤러를 통해 호출해야 합니다.
3. **보안**:
   * `WEB-INF`에 저장된 파일들은 외부 클라이언트가 직접 접근할 수 없습니다.
   * 정적 리소스는 `/static`, `/public`, 또는 `/resources` 디렉터리 아래에 배치해 접근 가능합니다.
4. **WAR 파일 생성**:
   * 프로젝트 빌드 시 `webapp` 디렉터리는 `.war` 파일로 패키징됩니다.
   * WAR 파일의 구조는 다음과 같습니다:

     ```arduino
     arduino코드 복사example.war
     ├── META-INF/
     ├── WEB-INF/
     │   ├── web.xml
     │   ├── views/
     │   │   └── example.jsp
     ├── static/
     │   ├── css/
     │   ├── js/
     │   └── images/
     └── index.html
     ```

***

####

## **`/WEB-INF` 디렉터리의 역할**

`/WEB-INF` 디렉터리는 Java 웹 애플리케이션의 구조에서 **보안상 외부 클라이언트로부터 접근할 수 없는 리소스**를 포함합니다. 이는 JSP, `web.xml`, 설정 파일 등이 위치하는 특별한 디렉터리로, 다음과 같은 특징이 있습니다.

**1. 외부 접근 불가**

* `/WEB-INF`에 위치한 파일들은 클라이언트가 직접 URL로 접근할 수 없습니다.
  * 예: `http://example.com/WEB-INF/views/example.jsp`는 접근 불가.
* 이는 보안상의 이유로 설계된 것이며, 내부적으로만 접근 가능하도록 보장합니다.
  * **예외**: 서버 내부에서 `RequestDispatcher`를 통해 포워딩하여 사용할 수 있습니다.

***

#### **`web.xml`의 역할**

`/WEB-INF/web.xml`은 Java EE 표준 스펙에서 \*\*배포 서술자(Deployment Descriptor)\*\*로 사용됩니다.

**주요 역할**

1. **서블릿 및 서블릿 매핑 정의**
   * 특정 URL 요청이 어떤 서블릿으로 처리될지 설정합니다.
   * 예:

     ```xml
     <servlet>
         <servlet-name>exampleServlet</servlet-name>
         <servlet-class>com.example.ExampleServlet</servlet-class>
     </servlet>
     <servlet-mapping>
         <servlet-name>exampleServlet</servlet-name>
         <url-pattern>/example</url-pattern>
     </servlet-mapping>
     ```
2. **필터 설정**
   * 요청과 응답을 가로채거나 수정하는 필터를 설정합니다.
   * 예:

     ```xml
     <filter>
         <filter-name>exampleFilter</filter-name>
         <filter-class>com.example.ExampleFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>exampleFilter</filter-name>
         <url-pattern>/*</url-pattern>
     </filter-mapping>
     ```
3. **리스너 설정**
   * 애플리케이션의 생명주기를 관리하거나 초기화 로직을 정의하는 리스너를 등록합니다.

**스프링 이후의 변화**

* `web.xml`의 역할은 대부분 스프링에서 **어노테이션**으로 대체되었습니다.
  * 예: 서블릿과 매핑 설정은 `@WebServlet`과 같은 어노테이션으로 처리.
  * `DispatcherServlet`도 자바 설정(`@Configuration`)으로 초기화 가능.

***

#### **`/WEB-INF/views`**

**특징**

1. **JSP 파일의 저장 위치**
   * JSP 파일을 `/WEB-INF/views` 디렉터리에 배치하는 것이 관례입니다.
   * 이 디렉터리에 배치된 JSP 파일은 **클라이언트가 직접 요청할 수 없으며**, 반드시 컨트롤러를 통해 접근해야 합니다.
2. **뷰 리졸버(View Resolver) 설정**
   * 스프링 MVC에서는 뷰 리졸버를 통해 JSP 파일을 렌더링합니다.
   * 예: `application.properties` 설정

     ```properties
     spring.mvc.view.prefix=/WEB-INF/views/
     spring.mvc.view.suffix=.jsp
     ```
   * 위 설정에 따라 `return "example";`을 호출하면 `/WEB-INF/views/example.jsp`가 렌더링됩니다.
3. **보안 강화**
   * 클라이언트가 JSP 파일에 직접 접근할 수 없으므로, JSP 파일이 무분별하게 노출되는 것을 방지할 수 있습니다.
   * 컨트롤러를 통해서만 접근 가능하므로, 요청을 적절히 검증하고 처리할 수 있습니다.

***

#### **동작 원리**

1. 클라이언트가 `/example` 요청을 보냅니다.
2. `DispatcherServlet`이 해당 요청을 처리할 컨트롤러를 찾습니다.
3. 컨트롤러에서 JSP 파일로 포워딩하거나 데이터를 전달합니다.
4. JSP 파일은 `/WEB-INF/views` 내부에서 로드되고 렌더링된 HTML이 클라이언트에 반환됩니다.

***

#### **장점**

1. **보안성**:
   * JSP 파일이 외부로 노출되지 않아 무단 접근을 방지할 수 있습니다.
2. **구조화**:
   * 웹 애플리케이션의 리소스를 체계적으로 관리할 수 있습니다.
3. **컨트롤러 중심 설계**:
   * 모든 요청은 컨트롤러를 통해 처리되므로 로직 분리가 명확합니다.

***

#### **요약**

| **디렉터리/파일**      | **설명**                                               |
| ---------------- | ---------------------------------------------------- |
| `/WEB-INF`       | 외부 접근이 불가능한 디렉터리로, JSP와 설정 파일들이 포함됨.                 |
| `web.xml`        | 서블릿 매핑, 필터 설정 등을 정의하는 배포 서술자.                        |
| `/WEB-INF/views` | JSP 파일이 위치하는 디렉터리. 클라이언트가 직접 접근할 수 없으며 컨트롤러를 통해 사용됨. |

위 구조는 보안성과 유지보수성을 높이기 위한 표준 설계 방식입니다. Spring Boot와 같은 현대적인 프레임워크에서는 어노테이션과 Java Config를 통해 많은 부분을 대체하지만, 여전히 기본 원칙은 유지됩니다.
