Skip to content

[SETTING]: 테스트 파일 세팅#235

Closed
phonil wants to merge 6 commits intodevelopfrom
setting/k6-home-script
Closed

[SETTING]: 테스트 파일 세팅#235
phonil wants to merge 6 commits intodevelopfrom
setting/k6-home-script

Conversation

@phonil
Copy link
Copy Markdown
Contributor

@phonil phonil commented May 2, 2026

📝 작업 내용

이번 PR에서 작업한 내용을 적어주세요

  • JWT 토큰 자동 생성기
  • K6 Config
  • K6 홈 화면 진입 시나리오 스크립트
  • K6 홈 화면 진입 시나리오 자동 실행기
  • K6 홈 화면 진입 시나리오 결과 요약 생성기
  • influxDB Docker Compose 스크립트

📷 스크린샷

☑️ 체크 리스트

체크 리스트를 확인해주세요

  • 테스트는 잘 통과했나요?
  • 충돌을 해결했나요?
  • 이슈는 등록했나요?
  • 라벨은 등록했나요?

#️⃣ 연관된 이슈

ex) # 이슈번호

closes #233

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 예외 처리를 이렇게 해도 괜찮을까요? / ~~부분 주의 깊게 봐주세요

노션 및 티스토리 참고

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 테스트 로그인 엔드포인트 추가 (/auth/test-login) - 개발 및 테스트 목적의 간편한 인증
  • Tests

    • k6 기반 부하 테스트 인프라 구성 (여러 VU 레벨 및 데이터 세트 지원)
    • 자동화된 성능 테스트 실행 및 결과 분석 스크립트 추가
    • InfluxDB 모니터링 스택 통합
  • Chores

    • JWT 토큰 만료 시간 설정 업데이트
    • P6Spy SQL 로깅 활성화
    • .gitignore에 테스트 결과 및 에이전트 바이너리 경로 추가

@phonil phonil self-assigned this May 2, 2026
@phonil phonil added chore 설정 파일 등 변경 (.gitignore, .yml 등) docs 문서 작업 feat 새로운 기능 구현 labels May 2, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 2, 2026

Walkthrough

K6 부하 테스트 프레임워크와 테스트 인증 기능을 구현합니다. 백엔드에서는 JWT 토큰 생성기, 테스트 로그인 엔드포인트, 관련 서비스를 추가하고, k6 시나리오 스크립트, 자동화 스크립트, 모니터링 설정, 결과 분석 도구를 도입합니다.


Changes

K6 부하 테스트 프레임워크

Layer / File(s) Summary
Configuration
k6/config.js
BASE_URL, THRESHOLDS (latency/failure-rate), STAGES (smoke, vu100, vu500, vu1000, vu5000, vu10000) 상수를 정의합니다.
Authentication Helper
k6/helpers/auth.js
SharedArrayk6/data/tokens.json을 로드하고, VU별로 Bearer 토큰을 할당하며 getAuthHeaders(), getCurrentMemberId() 함수를 노출합니다.
Load Test Scenario
k6/scenarios/home-screen.js
홈 화면 진입 시나리오를 구현: 6개의 HTTP GET 요청을 그룹화하고, 각 엔드포인트에 p(95)<500 임계값을 설정하며, 인증 헤더와 함께 1초 think time을 포함합니다.
Automation & Orchestration
k6/run-all-before.ps1, k6/run-all-before.sh
여러 데이터 시드와 VU 레벨에 걸쳐 k6 테스트를 자동 실행합니다: DB 초기화, 결과 디렉토리 생성, 각 조합에 대해 k6 run 호출 및 JSON 요약 내보내기를 수행합니다.
Result Parsing
k6/parse-results.ps1, k6/parse-results.sh
k6 JSON 요약으로부터 Markdown 리포트를 생성합니다: 전체 응답 시간/오류율/RPS 테이블과 API별 p95 테이블을 구성합니다.
Monitoring Infrastructure
k6/docker-compose.monitoring.yml
InfluxDB 1.8 서비스를 프로비저닝하여 k6 시계열 데이터를 저장하고, 포트 8086을 노출합니다.
Documentation
k6/README.md
k6 부하 테스트 설정, 사전 조건, 실행 워크플로우, 매개변수 테이블, 시드 프리셋, VU 스테이지, 메트릭 정의, 결과 형식을 문서화합니다.

테스트 인증 및 토큰 생성

Layer / File(s) Summary
Data Access
modules/domain/src/main/java/com/ott/domain/member/repository/MemberRepository.java
findByEmail(String email) 메서드를 추가하여 이메일로 회원을 조회합니다.
Service Layer
apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java
findOrCreateTestMember(email)을 구현합니다: 이메일로 회원을 조회하고, 없으면 새 회원(provider=LOCAL, onboardingCompleted=true)을 생성하고 기본 MemberRadarPreference를 저장합니다.
Token Generation
apps/api-user/src/main/java/com/ott/api_user/perf/TokenGeneratorRunner.java
Spring CommandLineRunner로 1,000개의 JWT 액세스 토큰을 회원 ID 1~1000에 대해 생성하고, k6/data/tokens.json에 JSON으로 직렬화합니다.
Controller & HTTP Layer
apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java
POST /auth/test-login 엔드포인트를 노출합니다: TestAuthService로 회원을 조회/생성하고, JwtTokenProvider로 액세스/리프레시 토큰을 생성한 후 CookieUtil로 쿠키에 설정합니다.
Security Configuration
apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java
/auth/test-login을 인증 화이트리스트에 추가합니다.
Application Configuration
apps/api-user/src/main/resources/application.yml
JWT access-token-expiry를 1800000(30분)에서 1209600000(14일)으로 변경하고, P6Spy SQL 로깅을 활성화합니다.

빌드 및 무시 파일

Layer / File(s) Summary
Version Control
.gitignore
Pinpoint Agent 바이너리(apps/monitoring/pinpoint/agent/)와 k6 결과/토큰 데이터(k6/results/, k6/data/)를 무시합니다.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant TestAuthController
    participant TestAuthService
    participant MemberRepository
    participant JwtTokenProvider
    participant CookieUtil
    participant Response

    Client->>TestAuthController: POST /auth/test-login<br/>(email)
    TestAuthController->>TestAuthService: findOrCreateTestMember(email)
    TestAuthService->>MemberRepository: findByEmail(email)
    MemberRepository-->>TestAuthService: Optional<Member>
    alt Member not found
        TestAuthService->>MemberRepository: save(newMember)
        MemberRepository-->>TestAuthService: Member
    else Member found
        TestAuthService-->>TestAuthService: return existing
    end
    TestAuthService-->>TestAuthController: Member
    TestAuthController->>JwtTokenProvider: generateAccessToken(memberId)
    JwtTokenProvider-->>TestAuthController: accessToken
    TestAuthController->>JwtTokenProvider: generateRefreshToken(memberId)
    JwtTokenProvider-->>TestAuthController: refreshToken
    TestAuthController->>CookieUtil: setAccessTokenCookie(response, token)
    CookieUtil->>Response: addCookie(accessCookie)
    TestAuthController->>CookieUtil: setRefreshTokenCookie(response, token)
    CookieUtil->>Response: addCookie(refreshCookie)
    TestAuthController->>Response: 204 No Content
    Response-->>Client: 204 + Set-Cookie headers
Loading
sequenceDiagram
    participant App as Spring App<br/>(perf profile)
    participant TokenGenerator as TokenGeneratorRunner
    participant JwtProvider as JwtTokenProvider
    participant FileSystem as k6/data/<br/>tokens.json
    participant K6 as K6 Process
    participant AuthHelper as k6/helpers/auth.js
    participant K6Scenario as home-screen.js
    participant API as API Server

    App->>TokenGenerator: run() on startup
    TokenGenerator->>JwtProvider: generateAccessToken(memberId)<br/>for 1..1000
    JwtProvider-->>TokenGenerator: accessToken
    TokenGenerator->>FileSystem: write tokens.json<br/>{ memberId, token }[]
    TokenGenerator->>App: System.exit(0)
    
    K6->>K6Scenario: load script
    K6Scenario->>AuthHelper: import getAuthHeaders
    AuthHelper->>FileSystem: load SharedArray(tokens.json)
    FileSystem-->>AuthHelper: tokens[]
    
    K6->>K6Scenario: iterate VUs
    K6Scenario->>AuthHelper: getAuthHeaders()
    AuthHelper->>AuthHelper: select token by (__VU-1) % length
    AuthHelper-->>K6Scenario: { Authorization: Bearer }
    K6Scenario->>API: GET /playlists/trending<br/>+ auth header
    API-->>K6Scenario: 200 OK
    K6Scenario->>API: GET /playlists/recommend
    API-->>K6Scenario: 200 OK
    K6Scenario->>API: GET /playlists/tags/top/*<br/>(3 requests)
    API-->>K6Scenario: 200 OK
    K6Scenario->>API: GET /playlists/history
    API-->>K6Scenario: 200 OK
    K6Scenario->>K6: sleep(1) + next iteration
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60분

이 PR은 여러 언어/도메인에 걸친 상당한 구조 편집을 포함합니다: 백엔드 Spring 컴포넌트(컨트롤러, 서비스, 설정), JavaScript k6 시나리오 및 헬퍼, PowerShell/Bash 자동화 스크립트, Docker Compose, 광범위한 문서. 로직 밀도는 중간 수준(토큰 생성, 회원 생성, k6 그룹화 및 임계값)이며, 파일 다양성과 각 부분의 고유한 목적으로 인해 일관성 있는 검토가 필요합니다.


Possibly Related PRs


Suggested Reviewers

  • marulog
  • yubin012
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 일부 변경 사항이 링크된 이슈의 직접적인 목표를 벗어납니다. TestAuthController와 TestAuthService는 K6 토큰 생성에 보조적인 기능이며, application.yml의 decorator.datasource.p6spy 설정과 access-token-expiry 변경은 링크된 이슈의 범위에 명시되지 않았습니다. TestAuthController, TestAuthService, p6spy 설정 등 K6 세팅 이슈 #233의 범위 밖의 변경 사항에 대해 추가 설명이 필요합니다. 별도의 이슈와 연결하거나 변경 사항을 제거하는 것을 고려하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive 제목이 PR의 주요 변경 사항을 명확하게 설명하지 못하고 있습니다. '테스트 파일 세팅'은 K6 성능 테스트, JWT 토큰 생성기, 여러 스크립트 등 다양한 변경 사항들을 아우르는 매우 광범위한 설명입니다. 제목을 더 구체적으로 수정하여 핵심 변경 사항을 명확히 해주세요. 예: '[SETTING]: K6 성능 테스트 세팅 및 자동화 스크립트 추가'
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR의 모든 코드 변경 사항이 링크된 이슈 #233의 목표와 일치합니다. JWT 토큰 생성기, K6 설정, 홈 화면 시나리오 스크립트, 자동 실행기, 결과 요약 생성기, InfluxDB Docker Compose 스크립트 등 모든 필수 항목이 구현되었습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch setting/k6-home-script

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배포 시 profile 설정 후 실행하면 적용됨 (토큰 유효기간(14일) 내 1회만 실행하면 됨)
-> 불편하다면 토큰 관련 다른 방식 적용 필요

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grafana에서 2587 템플릿 적용 시 필요
-> k6와 grafana 연동

Comment thread k6/parse-results.ps1
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PowerShell에서 사용 시 해당 스크립트 실행

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java (1)

59-71: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

/auth/test-loginpermitAll 추가는 머지 차단급 보안 리스크입니다.

Line 67로 인해 인증 없이 토큰 발급 플로우에 진입할 수 있습니다. 이 엔드포인트는 비운영 프로파일로 격리하거나(권장), 최소한 별도 강한 보호장치 없이 whitelist에 두면 안 됩니다.

As per coding guidelines, "permitAll 허용 대상은 /actuator/, /swagger-ui/, /v3/api-docs/** 등 인프라만(이번 PR의 /auth/test-login whitelist 추가는 보안 요구사항 관점에서 특히 P0 검토 필요)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java`
around lines 59 - 71, The merge adds "/auth/test-login" to the permitAll
whitelist in SecurityConfig, allowing unauthenticated token issuance; remove
"/auth/test-login" from the requestMatchers().permitAll() list in the
HttpSecurity configuration (locate the SecurityConfig class and the method
configuring HttpSecurity/requestMatchers) and instead restrict this endpoint to
a non-production profile or protect it with proper authentication/authorization
(e.g., require a specific role or a profile-gated condition); ensure any
test-only endpoints are guarded by profile checks or feature flags rather than
permitAll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java`:
- Around line 20-24: The TestAuthController exposes a test login endpoint that
can be active in production because the `@Profile` annotation is commented out;
re-enable isolation by adding a profile restriction (e.g., annotate
TestAuthController with `@Profile`("!prod") or `@Profile`("perf")) so the controller
(and its test-login mapping) is not active in prod, or alternatively annotate
the specific test-login handler method (the /auth/test-login endpoint) with
`@Profile`("!prod"); ensure the chosen profile value matches your deployment
profiles and remove any commented-out `@Profile` to prevent accidental activation
in production.
- Line 45: Update the TestAuthController method parameter annotation to
explicitly name the request parameter: in TestAuthController.java locate the
method parameter annotated with `@RequestParam`(defaultValue =
"testuser1@test.com") String email and change the annotation to include name =
"email" (i.e., `@RequestParam`(name = "email", defaultValue = "...")) so it
conforms to the controller rule for `@RequestParam` naming.

In
`@apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java`:
- Line 17: Remove the class-level `@Transactional` on TestAuthService and instead
annotate individual methods: add `@Transactional`(readOnly = true) to all
query/lookup methods and add plain `@Transactional` to state-changing methods
(e.g., create/update/delete methods in TestAuthService). Ensure no leftover
class-level transaction annotation remains and import the correct Spring
annotation for each method.
- Around line 15-18: Change the test-only beans to be active only under the perf
profile: replace the commented/incorrect `@Profile`({"!prod"}) with
`@Profile`("perf") on TestAuthService and TestAuthController so they are not
available in dev/prod; remove the class-level `@Transactional` on TestAuthService
and instead annotate individual methods—mark read-only operations with
`@Transactional`(readOnly = true) and annotate state-changing methods with plain
`@Transactional`; finally, make the /auth/test-login whitelist in SecurityConfig
conditional on the perf profile (only register or add that permit pattern when
activeProfile == "perf") so the test-login endpoint is not globally exposed.

In `@apps/api-user/src/main/resources/application.yml`:
- Around line 73-74: The access-token-expiry is incorrectly set equal to
refresh-token-expiry (both 14 days); revert access-token-expiry to a short, safe
default (e.g., 1800000 for 30 minutes) and keep refresh-token-expiry at
1209600000 (14 days) to reduce risk, and if you need a long-lived access token
for perf testing, move that override into application-perf.yml (or an
env-specific config) rather than changing the default; update the inline comment
next to access-token-expiry to reflect the corrected duration.
- Around line 97-102: The current default config enables P6Spy SQL logging
("decorator.datasource.p6spy.enable-logging: true"), which is unsafe for
production; change the default in application.yml to disable it (set
enable-logging to false) and move/override the true setting into
environment-specific configs (e.g., application-dev.yml and
application-test.yml) or profile-specific blocks so only development/test
profiles enable p6spy logging; ensure the "decorator.datasource.p6spy.logging:
slf4j" entry is only present alongside the profile-specific enable-logging true
to avoid enabling SQL logging in production.

In `@k6/config.js`:
- Around line 8-76: k6/config.js uses ESM exports (export const BASE_URL,
THRESHOLDS, STAGES) but the project is parsed as CommonJS, so add a Biome
override to parse k6/** as ESM: update biome.json overrides to include the k6/**
path and set javascript.parser.sourceType to "module" so the ESM syntax in
k6/config.js is accepted; ensure the override entry targets the k6 directory and
does not change other project parsing rules.

In `@k6/docker-compose.monitoring.yml`:
- Around line 31-33: The InfluxDB ports mapping currently binds to all
interfaces ("8086:8086"), exposing the service beyond localhost; change the
docker-compose ports entry to bind to localhost (e.g., "127.0.0.1:8086:8086") so
the InfluxDB service only listens on the host loopback. Locate the service ports
section that contains the line '- "8086:8086"' and replace it with a
localhost-bound mapping, keeping the same container port and host port numbers.

In `@k6/helpers/auth.js`:
- Around line 28-30: The SharedArray initialization for users (const users = new
SharedArray(...)) does not guard against an empty tokens.json; add an immediate
length check after parsing the open('../data/tokens.json') result and explicitly
fail-fast (throw an Error or call k6's fail()) with a clear message when
users.length === 0 so the script stops with a helpful error rather than
producing obscure runtime indexing errors later.

In `@k6/parse-results.ps1`:
- Around line 186-187: The script currently calls Resolve-Path on $ResultsDir
which throws if the directory does not exist; modify the flow to ensure the
directory exists (create it if missing) before building the output path, and use
Join-Path to construct $outputPath instead of string concatenation; update
references around Resolve-Path, $ResultsDir, $outputPath and the subsequent
[System.IO.File]::WriteAllLines call so the report file is written to the
created directory reliably.
- Around line 33-39: Get-Metric 함수에서 k6 요약 JSON 구조를 잘못 참조하고 있어
metrics.<name>.values.<field> 경로로 수정해야 합니다: Get-Metric의 내부에서
$metric.Value.PSObject.Properties 접근 전에 .values 객체를 참조하도록 변경하여 Field를 values 내에서
찾게 하고, 값 못 찾으면 null 반환 로직 유지하십시오; 또한 summary 처리 시 http_req_failed를 읽는 곳(현재 코드에서
http_req_failed 사용 구문)을 찾아 "value" 필드가 아니라 "rate" 필드를 사용하도록 변경하세요. 이때 참고할 심볼은
Get-Metric 함수와 출력/요약에서 http_req_failed를 참조하는 코드입니다.

In `@k6/parse-results.sh`:
- Around line 62-66: The current parsing pipeline uses jq piped into xargs
printf for p50, p95, p99, err, and rps which breaks when jq yields "-" (missing)
and can produce wrong values or exit under set -e; modify the logic around the
jq calls (the variables p50, p95, p99, err, rps reading from "$file") to first
capture the raw jq output into a temp variable, test whether it equals "-" or is
a valid number, and only call printf when numeric—otherwise set the variable to
"-" (or leave as-is); ensure you reference the existing variable names (p50,
p95, p99, err, rps) and the jq expressions used for
.metrics.http_req_duration/.http_req_failed/.http_reqs so the change is
localized and avoids piping "-" into printf.

In `@k6/README.md`:
- Line 9: The markdown has several fenced code blocks without a language tag
which triggers MD040; update each unlabeled triple-backtick fence in
k6/README.md (the blocks that currently contain "k6/", "1. Before 측정 ...", "시드
배열 순회:", and "시드 디렉토리 순회:") to include a language identifier such as text (e.g.,
replace ``` with ```text) so each code fence is labeled and the MD040 warning is
resolved.
- Around line 237-240: Update the README paragraph describing parse-results.ps1
to match the actual implementation: replace the claim that it extracts metrics
using jq with a description stating it uses PowerShell's ConvertFrom-Json to
parse the summary JSON and extract p50/p95/p99/error rate/RPS, and note that
API-specific p95 metrics come from tags via the PowerShell parser; reference the
script name parse-results.ps1 and the ConvertFrom-Json usage so readers
understand the real parsing method.

In `@k6/run-all-before.ps1`:
- Around line 21-23: The Seeds parameter is interpolated directly into SQL; add
strict whitelist validation before using it by defining an AllowedSeeds array
(e.g., "small","medium","large","xl") and filter/verify every element of the
incoming $Seeds against that list, rejecting or erroring on any unexpected
value; after validation, map the validated seeds to the exact expected SQL-safe
tokens (do not use raw user strings) and only then join them into the SQL
statement (or use parameterized queries if available). Also apply the same
whitelist check for any similar parameter usages (e.g., VuLevels) where user
input is embedded into SQL.

In `@k6/run-all-before.sh`:
- Line 76: The k6 invocation currently injects the LOAD environment variable
without quotes (the string "--env LOAD=$vu"); update the k6 run call in
run-all-before.sh to quote the variable so word-splitting and globbing are
prevented (use "--env LOAD=\"$vu\"" when constructing the command), i.e., locate
the k6 run line and replace the unquoted $vu with a quoted "$vu" in the --env
argument.

In `@k6/scenarios/home-screen.js`:
- Line 45: The current lookup stages: STAGES[__ENV.LOAD || 'vu100'] can return
undefined when __ENV.LOAD is set to an invalid key; change it to validate the
key before indexing: compute a safe key (e.g., check
STAGES.hasOwnProperty(__ENV.LOAD) or STAGES[__ENV.LOAD] !== undefined) and fall
back to 'vu100', then assign stages: STAGES[safeKey]; update the code that
references __ENV.LOAD, STAGES, and stages accordingly so invalid LOAD values use
the 'vu100' profile.

In
`@modules/domain/src/main/java/com/ott/domain/member/repository/MemberRepository.java`:
- Line 35: The current repository method Optional<Member> findByEmail(String
email) on MemberRepository is too broad and can return members with different
provider or status; replace or overload it with a derived query that includes
provider (and status when relevant), e.g., add methods like
findByEmailAndProvider(String email, Provider provider) and/or
findByEmailAndProviderAndStatus(String email, Provider provider, MemberStatus
status), update all call sites and tests to use these narrow methods, and remove
or deprecate the single-argument findByEmail to prevent accidental wide queries;
reference MemberRepository, findByEmail, and the Member fields provider and
status when making the change.

---

Outside diff comments:
In `@apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java`:
- Around line 59-71: The merge adds "/auth/test-login" to the permitAll
whitelist in SecurityConfig, allowing unauthenticated token issuance; remove
"/auth/test-login" from the requestMatchers().permitAll() list in the
HttpSecurity configuration (locate the SecurityConfig class and the method
configuring HttpSecurity/requestMatchers) and instead restrict this endpoint to
a non-production profile or protect it with proper authentication/authorization
(e.g., require a specific role or a profile-gated condition); ensure any
test-only endpoints are guarded by profile checks or feature flags rather than
permitAll.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 14f0e9c6-4076-4219-b0f8-618d4ac58bf3

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae9ef1 and 0c63dd6.

📒 Files selected for processing (16)
  • .gitignore
  • apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java
  • apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java
  • apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java
  • apps/api-user/src/main/java/com/ott/api_user/perf/TokenGeneratorRunner.java
  • apps/api-user/src/main/resources/application.yml
  • k6/README.md
  • k6/config.js
  • k6/docker-compose.monitoring.yml
  • k6/helpers/auth.js
  • k6/parse-results.ps1
  • k6/parse-results.sh
  • k6/run-all-before.ps1
  • k6/run-all-before.sh
  • k6/scenarios/home-screen.js
  • modules/domain/src/main/java/com/ott/domain/member/repository/MemberRepository.java

Comment on lines +20 to +24
//@Profile({"!prod"}) // 운영 환경에서는 비활성화
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class TestAuthController {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

운영 환경에서 무인증 토큰 발급 엔드포인트가 활성화됩니다.

@Profile이 비활성화된 상태(Line 20)에서 Line 43-57 로직이 살아 있으면, 운영에서도 테스트 로그인 토큰 발급이 열릴 수 있습니다. 최소 @Profile("perf") 또는 @Profile("!prod")로 강제 격리해 주세요.

As per coding guidelines, "(1) P0 머지 차단: 보안 인가 누락 … (5) Security … 이번 PR의 /auth/test-login whitelist 추가는 보안 요구사항 관점에서 특히 P0 검토 필요".

Also applies to: 43-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java`
around lines 20 - 24, The TestAuthController exposes a test login endpoint that
can be active in production because the `@Profile` annotation is commented out;
re-enable isolation by adding a profile restriction (e.g., annotate
TestAuthController with `@Profile`("!prod") or `@Profile`("perf")) so the controller
(and its test-login mapping) is not active in prod, or alternatively annotate
the specific test-login handler method (the /auth/test-login endpoint) with
`@Profile`("!prod"); ensure the chosen profile value matches your deployment
profiles and remove any commented-out `@Profile` to prevent accidental activation
in production.


@PostMapping("/test-login")
public ResponseEntity<Void> testLogin(
@RequestParam(defaultValue = "testuser1@test.com") String email,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

@RequestParamname을 명시해 컨트롤러 규칙을 맞춰주세요.

Line 45는 @RequestParam의 이름 명시 규칙을 충족하지 않습니다. 규칙에 맞게 name = "email"을 명시해 주세요.

제안 패치
-            `@RequestParam`(defaultValue = "testuser1@test.com") String email,
+            `@RequestParam`(name = "email", defaultValue = "testuser1@test.com") String email,

As per coding guidelines, "Controller … @RequestParam/@PathVariable은 name(value) 명시."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@RequestParam(defaultValue = "testuser1@test.com") String email,
`@RequestParam`(name = "email", defaultValue = "testuser1@test.com") String email,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java`
at line 45, Update the TestAuthController method parameter annotation to
explicitly name the request parameter: in TestAuthController.java locate the
method parameter annotated with `@RequestParam`(defaultValue =
"testuser1@test.com") String email and change the annotation to include name =
"email" (i.e., `@RequestParam`(name = "email", defaultValue = "...")) so it
conforms to the controller rule for `@RequestParam` naming.

Comment on lines +15 to +18
@Service
@RequiredArgsConstructor
@Transactional
public class TestAuthService {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Test auth 엔드포인트/보안 화이트리스트/프로필 제한 여부 확인
rg -n -C3 --type=java 'class\s+TestAuthController|/auth/test-login|@Profile\("perf"\)|requestMatchers|permitAll|authorizeHttpRequests'

Repository: OpenTheTaste/backend

Length of output: 6164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1. TestAuthService 전체 내용 확인
echo "=== TestAuthService.java ==="
cat apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java

# 2. TestAuthController 전체 내용 확인
echo ""
echo "=== TestAuthController.java ==="
cat apps/api-user/src/main/java/com/ott/api_user/auth/controller/TestAuthController.java

# 3. SecurityConfig에서 perf 프로필 조건 확인
echo ""
echo "=== SecurityConfig 전문 확인 ==="
cat apps/api-user/src/main/java/com/ott/api_user/config/SecurityConfig.java | head -80

Repository: OpenTheTaste/backend

Length of output: 8099


테스트 인증 서비스는 perf 프로필로 강제 제한이 필요합니다.

현재 주석 처리된 @Profile({"!prod"})@Profile("perf")로 변경하여 TestAuthControllerTestAuthService에 적용해 주세요. 테스트 로그인 흐름이 개발/스테이징 환경에 노출되면 인증 우회 경로가 될 수 있습니다.

추가로:

  • TestAuthService의 클래스 레벨 @Transactional → 메서드 단위 명시 필요 (가이드라인: 조회 readOnly=true, 변경 @Transactional)
  • SecurityConfig/auth/test-login 화이트리스트도 프로필 조건부로 제한 필요
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java`
around lines 15 - 18, Change the test-only beans to be active only under the
perf profile: replace the commented/incorrect `@Profile`({"!prod"}) with
`@Profile`("perf") on TestAuthService and TestAuthController so they are not
available in dev/prod; remove the class-level `@Transactional` on TestAuthService
and instead annotate individual methods—mark read-only operations with
`@Transactional`(readOnly = true) and annotate state-changing methods with plain
`@Transactional`; finally, make the /auth/test-login whitelist in SecurityConfig
conditional on the perf profile (only register or add that permit pattern when
activeProfile == "perf") so the test-login endpoint is not globally exposed.


@Service
@RequiredArgsConstructor
@Transactional
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

@Transactional은 메서드 단위로 분리해 주세요.

클래스 레벨 대신 변경 메서드에만 @Transactional을 붙이고, 조회 메서드는 readOnly=true로 명시하는 형태가 가이드와 일치합니다.

As per coding guidelines Service: @transactional위치를 메서드 단위로 명시(조회 readOnly=true, 변경@transactional).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api-user/src/main/java/com/ott/api_user/auth/service/TestAuthService.java`
at line 17, Remove the class-level `@Transactional` on TestAuthService and instead
annotate individual methods: add `@Transactional`(readOnly = true) to all
query/lookup methods and add plain `@Transactional` to state-changing methods
(e.g., create/update/delete methods in TestAuthService). Ensure no leftover
class-level transaction annotation remains and import the correct Spring
annotation for each method.

Comment on lines +73 to +74
access-token-expiry: 1209600000 # 30분: 1800000
refresh-token-expiry: 1209600000 # 14일: 1209600000
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Access 토큰 만료시간이 Refresh와 동일(14일)로 설정되어 보안 위험이 큽니다.

기본 설정에서 access 토큰을 장기화하면 탈취 시 피해 기간이 과도하게 늘어납니다. perf 전용 값이 필요하면 application-perf.yml로 분리하고, 기본값은 짧은 만료(예: 기존 30분)로 유지하는 것이 안전합니다.

As per coding guidelines apps/**/src/main/resources/application.yml: Verify environment and runtime safety and No insecure production defaults are introduced.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api-user/src/main/resources/application.yml` around lines 73 - 74, The
access-token-expiry is incorrectly set equal to refresh-token-expiry (both 14
days); revert access-token-expiry to a short, safe default (e.g., 1800000 for 30
minutes) and keep refresh-token-expiry at 1209600000 (14 days) to reduce risk,
and if you need a long-lived access token for perf testing, move that override
into application-perf.yml (or an env-specific config) rather than changing the
default; update the inline comment next to access-token-expiry to reflect the
corrected duration.

Comment thread k6/README.md
Comment on lines +237 to +240
```
시드 디렉토리 순회:
1. summary JSON에서 jq로 수치 추출 (p50, p95, p99, 에러율, RPS)
2. API별 태그 메트릭에서 p95 추출
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

parse-results.ps1 설명이 구현과 불일치합니다.

Line 239에 jq 기반 추출로 적혀 있지만, 실제 PowerShell 파서는 ConvertFrom-Json을 사용합니다. 문구를 맞춰 두는 게 좋습니다.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 237-237: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@k6/README.md` around lines 237 - 240, Update the README paragraph describing
parse-results.ps1 to match the actual implementation: replace the claim that it
extracts metrics using jq with a description stating it uses PowerShell's
ConvertFrom-Json to parse the summary JSON and extract p50/p95/p99/error
rate/RPS, and note that API-specific p95 metrics come from tags via the
PowerShell parser; reference the script name parse-results.ps1 and the
ConvertFrom-Json usage so readers understand the real parsing method.

Comment thread k6/run-all-before.ps1
Comment on lines +21 to +23
[string[]]$Seeds = @("small", "medium", "large", "xl"),
[string[]]$VuLevels = @("vu100", "vu500", "vu1000", "vu5000", "vu10000")
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Seeds 파라미터가 SQL에 직접 삽입되어 있습니다.

Line 52는 사용자 입력(-Seeds)이 그대로 SQL 문자열로 들어갑니다. 허용된 seed 프리셋만 통과시키는 검증이 필요합니다.

제안 패치
 param(
     [string]$Phase = "before",
     [string[]]$Seeds = @("small", "medium", "large", "xl"),
     [string[]]$VuLevels = @("vu100", "vu500", "vu1000", "vu5000", "vu10000")
 )
+
+$AllowedSeeds = @("small", "medium", "large", "xl")
+foreach ($s in $Seeds) {
+    if ($AllowedSeeds -notcontains $s) {
+        throw "Invalid seed: $s. Allowed values: $($AllowedSeeds -join ', ')"
+    }
+}
@@
-    docker exec ott-mysql mysql -u ott -pottpw ott -e "CALL seed_all('$seed');"
+    docker exec ott-mysql mysql -u ott -pottpw ott -e "CALL seed_all('$seed');"

Also applies to: 52-52

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'run-all-before.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@k6/run-all-before.ps1` around lines 21 - 23, The Seeds parameter is
interpolated directly into SQL; add strict whitelist validation before using it
by defining an AllowedSeeds array (e.g., "small","medium","large","xl") and
filter/verify every element of the incoming $Seeds against that list, rejecting
or erroring on any unexpected value; after validation, map the validated seeds
to the exact expected SQL-safe tokens (do not use raw user strings) and only
then join them into the SQL statement (or use parameterized queries if
available). Also apply the same whitelist check for any similar parameter usages
(e.g., VuLevels) where user input is embedded into SQL.

Comment thread k6/run-all-before.sh
echo ""
echo "--- [$run_count/$total_runs] $seed @ $vu ---"

k6 run --env LOAD=$vu \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

LOAD 환경변수 전달은 인용 처리해 두는 편이 안전합니다.

Line 76은 현재도 동작하지만, 값이 늘어날 때 단어 분리/글로빙을 예방하려면 인용이 안전합니다.

제안 패치
-        k6 run --env LOAD=$vu \
+        k6 run --env "LOAD=$vu" \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
k6 run --env LOAD=$vu \
k6 run --env "LOAD=$vu" \
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 76-76: Double quote to prevent globbing and word splitting.

(SC2086)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@k6/run-all-before.sh` at line 76, The k6 invocation currently injects the
LOAD environment variable without quotes (the string "--env LOAD=$vu"); update
the k6 run call in run-all-before.sh to quote the variable so word-splitting and
globbing are prevented (use "--env LOAD=\"$vu\"" when constructing the command),
i.e., locate the k6 run line and replace the unquoted $vu with a quoted "$vu" in
the --env argument.

// -------------------------------------------------------

export const options = {
stages: STAGES[__ENV.LOAD || 'vu100'],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

LOAD 값이 잘못되면 stages가 undefined가 됩니다.

유효하지 않은 __ENV.LOAD 입력에도 안전하게 vu100으로 폴백하도록 방어 로직을 추가해 주세요.

제안 diff
 export const options = {
-    stages: STAGES[__ENV.LOAD || 'vu100'],
+    stages: STAGES[__ENV.LOAD] || STAGES.vu100,
     thresholds: {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
stages: STAGES[__ENV.LOAD || 'vu100'],
export const options = {
stages: STAGES[__ENV.LOAD] || STAGES.vu100,
thresholds: {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@k6/scenarios/home-screen.js` at line 45, The current lookup stages:
STAGES[__ENV.LOAD || 'vu100'] can return undefined when __ENV.LOAD is set to an
invalid key; change it to validate the key before indexing: compute a safe key
(e.g., check STAGES.hasOwnProperty(__ENV.LOAD) or STAGES[__ENV.LOAD] !==
undefined) and fall back to 'vu100', then assign stages: STAGES[safeKey]; update
the code that references __ENV.LOAD, STAGES, and stages accordingly so invalid
LOAD values use the 'vu100' profile.

""")
void softDeleteByMemberId(@Param("memberId") Long memberId);

Optional<Member> findByEmail(String email);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

조회 조건이 너무 넓어 잘못된 회원을 집을 수 있습니다.

email 단독 조회는 provider가 다른 계정(예: KAKAO/LOCAL)이나 상태가 다른 계정을 섞어 선택할 수 있습니다. 테스트 인증 용도라면 provider(필요 시 status)까지 포함한 파생 메서드로 제한해 주세요.

제안 diff
-    Optional<Member> findByEmail(String email);
+    Optional<Member> findByEmailAndProvider(String email, Provider provider);
+    // 필요 시 상태까지 제한
+    // Optional<Member> findByEmailAndProviderAndStatus(String email, Provider provider, Status status);

As per coding guidelines P0 머지 차단: ... 데이터 무결성 훼손.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@modules/domain/src/main/java/com/ott/domain/member/repository/MemberRepository.java`
at line 35, The current repository method Optional<Member> findByEmail(String
email) on MemberRepository is too broad and can return members with different
provider or status; replace or overload it with a derived query that includes
provider (and status when relevant), e.g., add methods like
findByEmailAndProvider(String email, Provider provider) and/or
findByEmailAndProviderAndStatus(String email, Provider provider, MemberStatus
status), update all call sites and tests to use these narrow methods, and remove
or deprecate the single-argument findByEmail to prevent accidental wide queries;
reference MemberRepository, findByEmail, and the Member fields provider and
status when making the change.

@phonil phonil closed this May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore 설정 파일 등 변경 (.gitignore, .yml 등) docs 문서 작업 feat 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SETTING]: K6 세팅 파일 생성

1 participant