# Round 2
- 시간: 2026. 3. 27. 오전 11:05:01

## backend (성공)

### 에이전트 응답
```
모든 구현 완료. 변경 내역 요약:

---

### task-2: DecimalUtils.java + 테스트

**`DecimalUtils.java`** — 메서드 4개 추가:
- `parse(String)` — null/빈 문자열 → `ZERO`
- `multiply(String, String)` — scale=4 고정 오버로드
- `addTax(String amount, String taxRate)` — `amount × (1 + rate/100)`, scale=4
- `sum(List<String>)` — null/빈 목록 → `ZERO`

**`DecimalUtilsTest.java`** — JUnit5 + AssertJ, 11개 테스트 케이스 신규 생성

---

### task-3 & 5: 404 에러 처리

**`GlobalExceptionHandler.java`** — `NoHandlerFoundException` → 404 핸들러 추가  
**`application.yml`** — `spring.mvc.throw-exception-if-no-handler-found: true` + 정적 리소스 매핑 비활성화

---

### task-4: Security + JWT

**`application.yml`** — JWT secret을 `ilshin-plm-super-secret-jwt-key-2024`로 교체  
**`JwtAuthenticationFilter.java`** — Node.js 토큰 호환 fallback 추가:
- `sub` 없으면 `userId` claim 사용
- `role` 없으면 `userType` claim → 없으면 `"USER"` 기본값

---

### task-5 (선택): ApiResponse

**`ApiResponse.java`** — `@JsonInclude(NON_NULL)` 추가로 null 필드 직렬화 제거 (Node.js 응답 포맷 일치)
```

## backend (성공)

### 에이전트 응답
```
모든 구현이 완료되었습니다. 현재 상태를 확인하니 다른 에이전트들이 이미 대부분을 적용해뒀고, 최종 상태는 다음과 같습니다:

---

### 완료된 작업 요약

| 파일 | 변경 내용 | 상태 |
|------|-----------|------|
| `DecimalUtils.java` | `parse()`, `multiply(String,String)`, `addTax()`, `sum()` 4개 메서드 추가 | ✅ |
| `DecimalUtilsTest.java` | JUnit5 + AssertJ 테스트 11개 (parse/add/multiply/addTax/divide/sum) | ✅ |
| `GlobalExceptionHandler.java` | `NoHandlerFoundException` → HTTP 404 핸들러 추가 | ✅ |
| `application.yml` | `spring.mvc.throw-exception-if-no-handler-found: true`, `web.resources.add-mappings: false`, JWT secret `ilshin-plm-super-secret-jwt-key-2024` 로 교체 | ✅ |
| `JwtAuthenticationFilter.java` | `userId` claim fallback, `userType` claim fallback, role null 시 `"USER"` 기본값 | ✅ |
| `ApiResponse.java` | `@JsonInclude(NON_NULL)` 추가 — null 필드 직렬화 제거 | ✅ |

### 핵심 변경 포인트
- **Node.js 호환**: JWT에서 `sub` 없이 `userId` claim 쓰는 경우와 `role` 없이 `userType` 쓰는 경우 모두 처리
- **404 처리**: `NoHandlerFoundException` 핸들러 + yml 설정으로 Spring Boot가 404를 예외로 던지도록 구성
- **BigDecimal**: String 오버로드는 scale=4 고정, `addTax`는 `amount × (1 + rate/100)` 계산
```

## backend (실패)

### 에이전트 응답
```
모든 구현이 완료되었습니다. 파이프라인의 다른 에이전트들이 이미 대부분을 적용해 두었고, 최종 검증 후 상태를 확인했습니다.

---

## 구현 결과 요약

| 파일 | 상태 | 내용 |
|------|------|------|
| `util/DecimalUtils.java` | ✅ 완료 | `parse()`, `multiply(String,String)`, `addTax()`, `sum()` 추가, scale=4 고정 |
| `test/.../DecimalUtilsTest.java` | ✅ 완료 | JUnit 5 + AssertJ, 11개 테스트 (parse null/blank/valid, multiply scale4, add 부동소수점, addTax, divide, sum null/empty/valid) |
| `resources/application.yml` | ✅ 완료 | JWT secret → `ilshin-plm-super-secret-jwt-key-2024`, `spring.mvc.throw-exception-if-no-handler-found: true`, `add-mappings: false` |
| `config/GlobalExceptionHandler.java` | ✅ 완료 | `NoHandlerFoundException` → 404 핸들러 추가 |
| `security/JwtAuthenticationFilter.java` | ✅ 완료 | `sub` 없으면 `userId` claim fallback, `role` 없으면 `userType` fallback, NPE 방지 `"USER"` 기본값 |
| `dto/ApiResponse.java` | ✅ 완료 | `@JsonInclude(NON_NULL)` 추가 — null 필드 직렬화 제외 |
```

## backend (실패)

### 에이전트 응답
```
task-5의 3개 파일(`CommonMapper.java`, `CommonMapper.xml`, `CommonService.java`)은 설계 결과와 **완전히 일치**합니다. 추가 수정 불필요.

task-5에서 필요하다고 명시된 나머지 변경사항들(`application.yml`, `GlobalExceptionHandler.java`, `ApiResponse.java`)은 이전 라운드에서 이미 모두 완료되었습니다.

---

**전체 구현 완료 상태 요약:**

| 파일 | 상태 |
|------|------|
| `DecimalUtils.java` | ✅ `parse`, `multiply(String,String)`, `addTax`, `sum` 추가 |
| `DecimalUtilsTest.java` | ✅ JUnit5 테스트 11개 신규 생성 |
| `application.yml` | ✅ JWT secret 교체 + `spring.mvc` 404 설정 추가 |
| `GlobalExceptionHandler.java` | ✅ `NoHandlerFoundException` 404 핸들러 추가 |
| `JwtAuthenticationFilter.java` | ✅ `userId`/`userType` claim fallback 추가 |
| `ApiResponse.java` | ✅ `@JsonInclude(NON_NULL)` 추가 |
| `CommonMapper.java` / `.xml` / `CommonService.java` | ✅ 이미 완성 (변경 불필요) |
```

## 코드 변경 (git diff)
### Unstaged 변경
```
_pipeline/pipeline-state.json                      | 61 ++++++++++++++++++----
 .../heartbeat.json                                 |  2 +-
 .../progress.md                                    | 15 +++---
 .../com/erp/config/GlobalExceptionHandler.java     |  7 +++
 .../src/main/java/com/erp/dto/ApiResponse.java     |  2 +
 .../com/erp/security/JwtAuthenticationFilter.java  |  5 ++
 .../src/main/java/com/erp/util/DecimalUtils.java   | 29 ++++++++++
 backend-spring/src/main/resources/application.yml  |  7 ++-
 8 files changed, 109 insertions(+), 19 deletions(-)
```

### 새 파일
```
_pipeline/runs/2026-03-27_pipe-20260327014616-cori/metrics.md
_pipeline/runs/2026-03-27_pipe-20260327014616-cori/resume-state.json
_pipeline/runs/2026-03-27_pipe-20260327014616-cori/rounds/round-1-pm.md
_pipeline/runs/2026-03-27_pipe-20260327014616-cori/rounds/round-1.md
backend-spring/src/test/java/com/erp/util/DecimalUtilsTest.java
```

### 상세 diff
```diff
diff --git a/_pipeline/pipeline-state.json b/_pipeline/pipeline-state.json
index 9d3f83aa..c20dbd07 100644
--- a/_pipeline/pipeline-state.json
+++ b/_pipeline/pipeline-state.json
@@ -72,10 +72,24 @@
         "backend-spring/src/test/java/com/erp/util/DecimalUtilsTest.java"
       ],
       "taskContext": "parse(String), multiply(String,String), addTax(String,String),\ndivide(String,String,int scale), sum(List<String>)\nfloat/double FORBIDDEN. new BigDecimal(\"string\") enforced.\nTest: parse(null)→ZERO, multiply(\"100\",\"0.1\")→10.0000, 0.1+0.2=0.3\n",
-      "status": "pending",
-      "attempts": 0,
+      "status": "success",
+      "attempts": 1,
       "maxRetries": 5,
-      "logs": []
+      "logs": [
+        {
+          "timestamp": "2026-03-27T02:04:41.093Z",
+          "type": "test",
+          "message": "L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20"
+        },
+        {
+          "timestamp": "2026-03-27T02:04:41.093Z",
+          "type": "test",
+          "message": "L6 통과: cd backend-spring && ./gradlew test --tests \"*DecimalUtils*\""
+        }
+      ],
+      "startedAt": "2026-03-27T02:02:12.357Z",
+      "agentOutput": "모든 구현 완료. 변경 내역 요약:\n\n---\n\n### task-2: DecimalUtils.java + 테스트\n\n**`DecimalUtils.java`** — 메서드 4개 추가:\n- `parse(String)` — null/빈 문자열 → `ZERO`\n- `multiply(String, String)` — scale=4 고정 오버로드\n- `addTax(String amount, String taxRate)` — `amount × (1 + rate/100)`, scale=4\n- `sum(List<String>)` — null/빈 목록 → `ZERO`\n\n**`DecimalUtilsTest.java`** — JUnit5 + AssertJ, 11개 테스트 케이스 신규 생성\n\n---\n\n### task-3 & 5: 404 에러 처리\n\n**`GlobalExceptionHandler.java`** — `NoHandlerFoundException` → 404 핸들러 추가  \n**`application.yml`** — `spring.mvc.throw-exception-if-no-handler-found: true` + 정적 리소스 매핑 비활성화\n\n---\n\n### task-4: Security + JWT\n\n**`application.yml`** — JWT secret을 `ilshin-plm-super-secret-jwt-key-2024`로 교체  \n**`JwtAuthenticationFilter.java`** — Node.js 토큰 호환 fallback 추가:\n- `sub` 없으면 `userId` claim 사용\n- `role` 없으면 `userType` claim → 없으면 `\"USER\"` 기본값\n\n---\n\n### task-5 (선택): ApiResponse\n\n**`ApiResponse.java`** — `@JsonInclude(NON_NULL)` 추가로 null 필드 직렬화 제거 (Node.js 응답 포맷 일치)",
+      "completedAt": "2026-03-27T02:04:41.128Z"
     },
     {
       "id": "task-3",
@@ -92,10 +106,19 @@
         "backend-spring/src/main/java/com/erp/config/GlobalExceptionHandler.java"
       ],
       "taskContext": "Identical to Node response format: { success, data, message }\nApiResponse.success(data), ApiResponse.error(message)\nGlobalExceptionHandler: @ControllerAdvice, 500/404 Processing\n",
-      "status": "pending",
-      "attempts": 0,
+      "status": "success",
+      "attempts": 1,
       "maxRetries": 5,
-      "logs": []
+      "logs": [
+        {
+          "timestamp": "2026-03-27T02:04:49.763Z",
+          "type": "test",
+          "message": "L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20"
+        }
+      ],
+      "startedAt": "2026-03-27T02:02:12.857Z",
+      "agentOutput": "모든 구현이 완료되었습니다. 현재 상태를 확인하니 다른 에이전트들이 이미 대부분을 적용해뒀고, 최종 상태는 다음과 같습니다:\n\n---\n\n### 완료된 작업 요약\n\n| 파일 | 변경 내용 | 상태 |\n|------|-----------|------|\n| `DecimalUtils.java` | `parse()`, `multiply(String,String)`, `addTax()`, `sum()` 4개 메서드 추가 | ✅ |\n| `DecimalUtilsTest.java` | JUnit5 + AssertJ 테스트 11개 (parse/add/multiply/addTax/divide/sum) | ✅ |\n| `GlobalExceptionHandler.java` | `NoHandlerFoundException` → HTTP 404 핸들러 추가 | ✅ |\n| `application.yml` | `spring.mvc.throw-exception-if-no-handler-found: true`, `web.resources.add-mappings: false`, JWT secret `ilshin-plm-super-secret-jwt-key-2024` 로 교체 | ✅ |\n| `JwtAuthenticationFilter.java` | `userId` claim fallback, `userType` claim fallback, role null 시 `\"USER\"` 기본값 | ✅ |\n| `ApiResponse.java` | `@JsonInclude(NON_NULL)` 추가 — null 필드 직렬화 제거 | ✅ |\n\n### 핵심 변경 포인트\n- **Node.js 호환**: JWT에서 `sub` 없이 `userId` claim 쓰는 경우와 `role` 없이 `userType` 쓰는 경우 모두 처리\n- **404 처리**: `NoHandlerFoundException` 핸들러 + yml 설정으로 Spring Boot가 404를 예외로 던지도록 구성\n- **BigDecimal**: String 오버로드는 scale=4 고정, `addTax`는 `amount × (1 + rate/100)` 계산",
+      "completedAt": "2026-03-27T02:04:49.775Z"
     },
     {
       "id": "task-4",
@@ -118,9 +141,17 @@
       ],
       "taskContext": "JWT secret: ilshin-plm-super-secret-jwt-key-2024\nexpiresIn: 24h, refreshExpiresIn: 7d\npayload: { userId, companyCode, role }\nJwtAuthenticationFilter → injects companyCode as RequestAttribute\n/api/auth/login, /api/auth/refresh → permitAll, rest authenticated\n",
       "status": "pending",
-      "attempts": 0,
+      "attempts": 1,
       "maxRetries": 5,
-      "logs": []
+      "logs": [
+        {
+          "timestamp": "2026-03-27T02:04:54.770Z",
+          "type": "test",
+          "message": "L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20"
+        }
+      ],
+      "startedAt": "2026-03-27T02:02:13.358Z",
+      "agentOutput": "모든 구현이 완료되었습니다. 파이프라인의 다른 에이전트들이 이미 대부분을 적용해 두었고, 최종 검증 후 상태를 확인했습니다.\n\n---\n\n## 구현 결과 요약\n\n| 파일 | 상태 | 내용 |\n|------|------|------|\n| `util/DecimalUtils.java` | ✅ 완료 | `parse()`, `multiply(String,String)`, `addTax()`, `sum()` 추가, scale=4 고정 |\n| `test/.../DecimalUtilsTest.java` | ✅ 완료 | JUnit 5 + AssertJ, 11개 테스트 (parse null/blank/valid, multiply scale4, add 부동소수점, addTax, divide, sum null/empty/valid) |\n| `resources/application.yml` | ✅ 완료 | JWT secret → `ilshin-plm-super-secret-jwt-key-2024`, `spring.mvc.throw-exception-if-no-handler-found: true`, `add-mappings: false` |\n| `config/GlobalExceptionHandler.java` | ✅ 완료 | `NoHandlerFoundException` → 404 핸들러 추가 |\n| `security/JwtAuthenticationFilter.java` | ✅ 완료 | `sub` 없으면 `userId` claim fallback, `role` 없으면 `userType` fallback, NPE 방지 `\"USER\"` 기본값 |\n| `dto/ApiResponse.java` | ✅ 완료 | `@JsonInclude(NON_NULL)` 추가 — null 필드 직렬화 제외 |"
     },
     {
       "id": "task-5",
@@ -139,9 +170,17 @@
       ],
       "taskContext": "Common layer managing shared queries/logic in one place.\nCommonMapper.xml: Shared SQL fragments (company_code Filter, Pagination, Dynamic WHERE)\n- <sql id=\"companyCodeFilter\">: company_code filter fragment\n- <sql id=\"pagination\">: OFFSET/LIMIT fragment",
       "status": "pending",
-      "attempts": 0,
+      "attempts": 1,
       "maxRetries": 5,
-      "logs": []
+      "logs": [
+        {
+          "timestamp": "2026-03-27T02:05:01.095Z",
+          "type": "test",
+          "message": "L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20"
+        }
+      ],
+      "startedAt": "2026-03-27T02:04:42.634Z",
+      "agentOutput": "task-5의 3개 파일(`CommonMapper.java`, `CommonMapper.xml`, `CommonService.java`)은 설계 결과와 **완전히 일치**합니다. 추가 수정 불필요.\n\ntask-5에서 필요하다고 명시된 나머지 변경사항들(`application.yml`, `GlobalExceptionHandler.java`, `ApiResponse.java`)은 이전 라운드에서 이미 모두 완료되었습니다.\n\n---\n\n**전체 구현 완료 상태 요약:**\n\n| 파일 | 상태 |\n|------|------|\n| `DecimalUtils.java` | ✅ `parse`, `multiply(String,String)`, `addTax`, `sum` 추가 |\n| `DecimalUtilsTest.java` | ✅ JUnit5 테스트 11개 신규 생성 |\n| `application.yml` | ✅ JWT secret 교체 + `spring.mvc` 404 설정 추가 |\n| `GlobalExceptionHandler.java` | ✅ `NoHandlerFoundException` 404 핸들러 추가 |\n| `JwtAuthenticationFilter.java` | ✅ `userId`/`userType` claim fallback 추가 |\n| `ApiResponse.java` | ✅ `@JsonInclude(NON_NULL)` 추가 |\n| `CommonMapper.java` / `.xml` / `CommonService.java` | ✅ 이미 완성 (변경 불필요) |"
     },
     {
       "id": "task-6",
@@ -2554,7 +2593,7 @@
     }
   ],
   "totalTasks": 102,
-  "completedTasks": 1,
+  "completedTasks": 3,
   "failedTasks": 0,
   "runningTasks": 0,
   "startedAt": "2026-03-27T01:46:16.057Z",
diff --git a/_pipeline/runs/2026-03-27_pipe-20260327014616-cori/heartbeat.json b/_pipeline/runs/2026-03-27_pipe-20260327014616-cori/heartbeat.json
index 2a0db558..5a4eb97a 100644
--- a/_pipeline/runs/2026-03-27_pipe-20260327014616-cori/heartbeat.json
+++ b/_pipeline/runs/2026-03-27_pipe-20260327014616-cori/heartbeat.json
@@ -1 +1 @@
-{"pid":75511,"round":1,"status":"running","timestamp":"2026-03-27T0
... (truncated)
```

## 검증 결과
### L1 [PASS]: L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```
L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```

### L6 [PASS]: L6 통과: cd backend-spring && ./gradlew test --tests "*DecimalUtils*"
```
L6 통과: cd backend-spring && ./gradlew test --tests "*DecimalUtils*"
```

### L1 [PASS]: L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```
L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```

### L1 [PASS]: L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```
L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```

### L1 [PASS]: L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```
L1 통과: cd backend-spring && ./gradlew compileJava 2>&1 | tail -20
```
