149 lines
7.3 KiB
Markdown
149 lines
7.3 KiB
Markdown
# 이벤트 접속 국가별 언어 필터 구현 리뷰
|
|
|
|
## 1. 리뷰 정보
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 리뷰 대상 | Phase 1~2 전체 구현과 `P1-GATE`, `P2-GATE` |
|
|
| 기준 commit 또는 working tree | 기준 `2ad30f90699a03162000d3b87be39f45b92740af` + 미커밋 구현 변경 |
|
|
| 리뷰 일자 | 2026-08-20 |
|
|
| 리뷰어 | Codex, 독립 리뷰 agent `event_language_review` |
|
|
| 기준 문서 | `prd.md`, `plan-task.md` |
|
|
| 리뷰 상태 | 수정 검증 완료 |
|
|
|
|
## 2. 리뷰 목적과 범위
|
|
|
|
### 목적
|
|
|
|
- 구현이 `EVENT-LANG-001~009`와 기존 API 경계를 충족하는지 확인한다.
|
|
- 완료 기록과 실제 코드·테스트 결과가 일치하는지 확인한다.
|
|
|
|
### 포함 범위
|
|
|
|
- 코드: 관리자 이벤트 등록·목록, 앱 이벤트 controller/service/repository, `Event`, `SecurityConfig`
|
|
- 테스트: 관리자·앱 controller 통합, service, repository, 기존 국가 판정 통합 테스트
|
|
- 문서: `prd.md`, `plan-task.md`, 운영 DDL
|
|
|
|
### 제외 범위
|
|
|
|
- 운영 DB DDL 실제 실행과 배포
|
|
- 이벤트 언어와 관계없는 기능
|
|
|
|
## 3. 판정 기준
|
|
|
|
| 심각도 | 기준 |
|
|
|---|---|
|
|
| Blocker | 보안·데이터 손실 위험 또는 핵심 흐름 불능 |
|
|
| High | 확정 요구사항·기존 API 경계 위반 또는 주요 회귀 |
|
|
| Medium | 제한된 조건의 기능·복구 문제 |
|
|
| Low | 비핵심 유지보수성·문서 정합성 문제 |
|
|
|
|
## 4. 검토한 근거
|
|
|
|
### 문서와 코드
|
|
|
|
- 요구사항: `EVENT-LANG-001~009`
|
|
- 계획: `P1-T1~P2-GATE`
|
|
- 코드: `AdminEventBannerController`, `AdminEventBannerService`, `AdminEventBannerRepository`, `EventController`, `EventService`, `EventRepository`, `Event`, `SecurityConfig`
|
|
- 테스트: `AdminEventBannerControllerIntegrationTest`, `EventControllerIntegrationTest`, `EventServiceTest`, `EventRepositoryTest`, `MemberContentPreferenceIntegrationTest`
|
|
|
|
### 실행 환경
|
|
|
|
```text
|
|
OS: Darwin 25.0.0 x86_64
|
|
Java: OpenJDK 17.0.15
|
|
Build: Gradle Wrapper 8.1.1
|
|
DB: 테스트용 H2 MySQL mode
|
|
```
|
|
|
|
### 실행한 검증
|
|
|
|
| 명령 또는 수동 검증 | 결과 | 핵심 증거 |
|
|
|---|---|---|
|
|
| 직접 영향 5개 클래스 `cleanTest test` | 성공 | 겹침 제거 후 exit code 0, 27개 테스트 통과, `BUILD SUCCESSFUL in 46s` |
|
|
| 독립 리뷰의 동일 5개 클래스 실행 | 성공 | exit code 0, `BUILD SUCCESSFUL in 56s` |
|
|
| 전체 호출자·기준 HEAD 보안 설정 diff 대조 | 발견 | 기준 HEAD의 `/event/popup`은 `anyRequest().authenticated()` 적용 |
|
|
| 수정 후 독립 정적 재리뷰 | 성공 | Critical·Important·Minor 추가 발견 없음, Ready to merge 판정 |
|
|
| `git diff --check HEAD` | 성공 | exit code 0, whitespace 오류 없음 |
|
|
|
|
동시에 실행된 두 Gradle `cleanTest`가 같은 XML 결과 경로를 사용해 한 차례 writer 경합이 발생했다. 테스트 assertion은 모두 통과했고, 병행 실행을 중단한 뒤 동일 명령을 재실행해 성공 종료를 확인했다.
|
|
|
|
## 5. 발견 사항 요약
|
|
|
|
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
|
|---|---|---|---|---|---|
|
|
| `REV-EVENT-LANG-001` | High | 수정 완료 | `GET /event/popup`의 기존 인증 경계가 익명 공개로 확장됨 | `P2-T1` | `P2-R1` |
|
|
|
|
언어 저장·관리자 전체 언어 조회·앱 KO/JA QueryDSL 필터·콘텐츠 메인 null 언어 경로에서는 추가 확정 문제를 찾지 못했다.
|
|
|
|
## 6. 발견 사항 상세
|
|
|
|
### REV-EVENT-LANG-001 — `GET /event/popup`의 기존 인증 경계가 익명 공개로 확장됨
|
|
|
|
- **심각도:** High
|
|
- **상태:** 수정 완료
|
|
- **관련 요구사항:** `EVENT-LANG-005`, 기존 접근 정책 유지
|
|
- **소유 Task:** `P2-R1`
|
|
|
|
**관찰 내용**
|
|
|
|
기준 HEAD의 `SecurityConfig`는 `GET /event`만 `permitAll()`이고, `/event/popup`은 마지막 `anyRequest().authenticated()`를 적용받는다. 수정 전 구현은 `/event/popup` exact matcher를 추가해 익명 요청도 허용했다. 국가별 언어 필터에는 인증 정책 변경이 필요하지 않으므로 요청 범위를 넘어선 보안 경계 확장이었다.
|
|
|
|
**근거**
|
|
|
|
- 기준 코드: `SecurityConfig.kt`의 `/event` matcher 다음에 `/event/popup` matcher가 없고 마지막 규칙은 `anyRequest().authenticated()`다.
|
|
- 수정 전 코드: `SecurityConfig.kt`에 `GET /event/popup` `permitAll()` 한 줄이 추가됐다.
|
|
- 수정 전 테스트: `EventControllerIntegrationTest`의 팝업 2개가 익명 200을 기대해 확장된 동작을 고정했다.
|
|
- 문서: `prd.md`가 두 API 모두 기존 로그인·비로그인 접근이라고 잘못 기술했다.
|
|
|
|
**재현 또는 검증 절차**
|
|
|
|
1. 수정 전 security filter를 켠 상태로 인증 없이 `GET /event/popup`을 호출한다.
|
|
2. 수정 전 결과는 HTTP 200이다.
|
|
3. 기준 HEAD의 matcher 순서에서는 HTTP 401이다.
|
|
4. 요구되는 결과는 기존 인증 경계를 유지하는 HTTP 401이며, 인증 사용자의 국가별 팝업 필터는 계속 동작해야 한다.
|
|
|
|
**영향**
|
|
|
|
명시 승인 없이 endpoint 접근 범위가 넓어지고, PRD가 실제 기준 동작과 불일치한다.
|
|
|
|
**권장 조치**
|
|
|
|
`SecurityConfig`의 신규 matcher 한 줄을 제거한다. 통합 테스트에는 익명 팝업 401 회귀를 추가하고, JP·국가 누락 팝업 언어 테스트는 `MemberAdapter` 인증 사용자로 실행한다. `/event`의 기존 익명 접근과 언어 조회 구현은 변경하지 않는다.
|
|
|
|
**판정 기록**
|
|
|
|
- 2026-08-20 — 기준 HEAD·현재 diff·security-on 통합 테스트를 대조해 확정했다.
|
|
- 2026-08-20 — `P2-R1`에서 exact `permitAll()`을 제거하고 익명 401·인증 사용자 KO/JA 팝업과 직접 영향 28개 테스트를 통과해 수정 완료로 판정했다.
|
|
|
|
## 7. 확정 항목의 plan·goal 전환
|
|
|
|
`REV-EVENT-LANG-001`을 `plan-task.md`의 `P2-R1` 회귀 수정 Goal로 전환했다.
|
|
|
|
## 8. 리뷰 종료 판정
|
|
|
|
| 판정 항목 | 결과 | 근거 |
|
|
|---|---|---|
|
|
| 리뷰 범위 전체 확인 | 충족 | 문서·production diff·전체 호출자·DDL·직접 영향 테스트 확인 |
|
|
| 후보 항목 판정 완료 | 충족 | 1건 확정, 나머지 substantive issue 없음 |
|
|
| 확정 항목 plan 반영 | 충족 | `P2-R1` 추가 |
|
|
| 검증 명령과 결과 기록 | 충족 | 4절 기록 |
|
|
|
|
**최종 결론:** 수정 검증 완료
|
|
|
|
**남은 항목:** 없음
|
|
|
|
## 9. 수정 후 검증 기록
|
|
|
|
### 1차 수정 검증 — 2026-08-20
|
|
|
|
- 무엇을: `REV-EVENT-LANG-001`의 익명 팝업 공개를 제거하고 기존 인증 경계를 복원했다.
|
|
- 왜: 국가별 언어 필터와 무관한 보안 접근 범위 확장을 제거하기 위해서다.
|
|
- 어떻게:
|
|
- RED: `EventControllerIntegrationTest` 5개 중 익명 팝업 401만 실제 200으로 실패했고 인증 팝업·익명 목록 4개는 통과했다.
|
|
- GREEN: `SecurityConfig`의 exact matcher 한 줄 제거 후 controller 통합 테스트 5개가 `BUILD SUCCESSFUL in 38s`로 통과했다.
|
|
- 회귀: 직접 영향 5개 클래스 28개가 failures `0`, errors `0`, skipped `0`, `BUILD SUCCESSFUL in 42s`로 통과했다.
|
|
- 정적 검증: `ktlintCheck`, `tasks --all`, `git diff --check HEAD`가 모두 성공했고 기준 HEAD 대비 `SecurityConfig` diff가 없다.
|
|
- 독립 재리뷰: 수정 후 최종 diff와 문서를 다시 검토해 Critical·Important·Minor 모두 추가 발견 없음으로 판정했다.
|
|
- 남은 항목: 운영 DB DDL 실제 반영은 기존 범위대로 미실행이며, 리뷰 확정 항목은 남아 있지 않다.
|