Skip to content

[Release] develop 을 운영에 반영한다 - 관리자 페이지 개편, 배포 전 새 컨테이너 확인, 모니터링 알림, 훈장과 미션 문구 - #344

Merged
KiSeungMin merged 19 commits into
mainfrom
develop
Sep 21, 2026
Merged

KiSeungMin merged 19 commits into
mainfrom
develop

Conversation

@KiSeungMin

Copy link
Copy Markdown
Contributor

✔️ 연관 이슈

develop 에 쌓인 PR 6개를 운영에 반영합니다. 연결된 이슈(#332, #333, #335, #338, #340, #341)는 모두 닫혀 있습니다.

PR 내용
#334 격리 환경 테스트를 통과해야 main 과 develop 에 머지되게 한다
#336 모니터링 알림을 디스코드로 연결하고 대시보드의 빈 패널과 틀린 값을 고친다
#337 스모크 테스트가 토큰 갱신과 오답노트 등록을 보고, prod 배포가 nginx 전환 전에 새 컨테이너를 확인한다
#339 대시보드 평균 응답시간, 외부 API 성공률, Redis 히트율 패널의 값과 색을 고친다
#342 관리자 페이지 개편 (디자인, 로그인 유지, 데이터 조회, 통계)
#343 훈장 설명과 미션 제목의 한글 수를 숫자로 바꾼다

📝 작업 내용

👤 사용자 영향

  • 앱 화면에서는 훈장 설명과 미션 제목 문구만 바뀝니다. 앱은 받은 문자열을 그대로 그려서 앱 업데이트 없이 반영됩니다.
  • 관리자는 배포 직후 한 번 다시 로그인해야 합니다. 로그인 유지 쿠키는 이 배포 이후 로그인부터 발급됩니다.
  • 나머지는 CI, 배포 스크립트, 모니터링 변경이라 사용자에게 보이지 않습니다.

🔌 API 호환성

  • 요청/응답 DTO 변경 없음
  • 기존 Flutter 앱 버전과 호환 확인
  • breaking change 있음

앱이 부르는 /api/** 는 응답 문자열 값만 바뀌고 필드는 그대로입니다. 바뀐 엔드포인트는 관리자 화면(/admin/**, /login)뿐입니다.

🗄️ DB migration

  • 없음

  • 있음: V47 하나입니다.

  • V47 use_digits_in_mission_titles: mission_definition 의 title 6행을 UPDATE ... WHERE code = ... 로 바꿉니다. 스키마 변경은 없고 code, 목표값, 보상은 그대로라 진행도에는 영향이 없습니다.

🔐 인증/권한

  • 영향 없음
  • 사용자별 데이터 소유권 검증 확인
  • 관리자/특수 권한 영향 확인

관리자 세션 만료가 30분에서 24시간으로 늘고 로그인 유지 쿠키가 생깁니다(#342). 관리자 엔드포인트 인가 테스트는 새 동작에 맞춰 고쳐져 있습니다.

✅ 검증 결과

  • develop 최신(979fb1b) 기준 GitHub CI 격리 환경 테스트 가 통과했습니다.
  • 같은 커밋이 dev 서버에 배포됐습니다(CI Build & Deploy Dev 성공).
  • dev 에서 앱의 훈장 화면과 미션 화면 문구, 관리자 통계 화면 응답 시간은 아직 눈으로 확인하지 않았습니다. main 머지 전에 확인하는 게 좋을 것 같습니다.

🚀 배포 리스크

  1. 이번 배포부터 prod 배포 방식이 바뀝니다([Feat] 스모크 테스트가 토큰 갱신과 오답노트 등록을 보고, prod 배포가 nginx 전환 전에 새 컨테이너를 확인한다 #337). 전환 전 확인이 잘못 실패하면 배포가 막히고, 그때는 deploy-prod.yml(수동 배포)이 우회로입니다. 맥미니 러너에서는 아직 돌려 보지 않았고, python3 3.9 이상이 없으면 스모크만 건너뛰고 경고를 남깁니다.
  2. 디스코드 알림이 prod 에서 처음 실제로 나갑니다([Chore] 모니터링 알림을 디스코드로 연결하고 대시보드의 빈 패널과 틀린 값을 고친다 #336). dev 에는 Alertmanager 를 두지 않아서 전송 경로는 이번에 처음 동작합니다. 앱 에러 알림과 같은 채널로 갑니다.
  3. 관리자 통계 쿼리가 늘었습니다([Feat] 관리자 페이지 개편 (디자인, 로그인 유지, 데이터 조회, 통계) #342). 통계와 홈 화면이 집계 쿼리를 한 번에 수십 개 보냅니다. 모두 기간 조건과 GROUP BY 한 번이라 크지는 않을 것 같지만, 운영 데이터 규모에서는 재 본 적이 없습니다.

↩️ 롤백/대응 방법

⚠️ main 머지 전에 확인할 것

KiSeungMin and others added 19 commits September 21, 2026 15:00
- ci-prod 는 수동 실행이 아니라 main push 로 바로 배포를 시작하고, 이 워크플로를 기다리지 않는다
- 그래서 배포를 막는 것은 PR 단계이고, main 과 develop 의 branch protection 이 격리 환경 테스트를 required check 로 건다
[Chore] 격리 환경 테스트를 통과해야 main 과 develop 에 머지되게 한다
- ono-alerts.yml 규칙 7개는 평가되고 있었지만 prometheus.prod.yml 에 alerting 설정이 없어서 어디로도 가지 않았다. Alertmanager 를 붙이고 앱 에러 알림과 같은 DISCORD_WEBHOOK_URL 로 보낸다. 웹훅 주소는 설정 파일에 적지 않으려고 컨테이너가 뜰 때 환경변수를 파일로 옮겨 webhook_url_file 로 읽는다
- Redis 와 RabbitMQ 는 떠 있는데 수집을 안 해서 Dependency 대시보드 패널 4개가 항상 비어 있었다. redis-exporter 를 추가하고, RabbitMQ 는 이미지에 내장된 rabbitmq_prometheus 의 detailed 엔드포인트에서 큐별 적체와 컨슈머 수를 가져온다. 합계만 보면 큐 하나의 컨슈머가 죽어도 티가 안 나기 때문이다
- 5xx 비율은 단위가 0~1 인데 경고 기준이 5 라서 에러율이 100% 여도 초록이었고, 평균 응답시간은 기준이 5ms 라 평소에도 빨강이었다. 기준을 고치고 평균 대신 p50 을 둔다
- Server/Process Memory 패널은 JVM 이 내보내지 않는 process_resident_memory_bytes 를 그려서 실제로는 mysql-exporter 메모리를 보여주고 있었다. JVM 메모리로 바꾸고, CPU 도 호스트 전체가 아닌 앱 프로세스 기준으로 바꾼다
- 하루 요청의 96% 가 스캐너라 요청 지표를 /api 로 한정하고, 행동 지표에서는 security_rejected, not_found, unmapped_request 를 뺀다. 트래픽이 적어 5분 창은 너무 튀기 때문에 1시간 창으로 보고 p99 는 뺐다
- 외부 API 성공률은 호출이 없으면 0% 빨강으로 보였다. 호출이 없을 때는 값을 비우고 호출 없음으로 표시한다
- promtail 이 Loki 자기 로그를 수집해서 하루 로그의 97% 를 차지하고 있었다. 수집 대상에서 빼고 Alertmanager 로그를 넣는다
- 배포와 모니터링 재시작 워크플로에 새 컨테이너를 추가하고, restart-monitoring 의 .env.prod 에 빠져 있던 DISCORD_WEBHOOK_URL 을 넣는다

Closes #335
[Chore] 모니터링 알림을 디스코드로 연결하고 대시보드의 빈 패널과 틀린 값을 고친다
- read 부터 서버에 저장된 적 없는 refresh token 으로 토큰 갱신 경로가 1002 로 답하는지 본다. 서버는 저장 전에 끝나서 쓰기가 없다
- full 에서 이미지 업로드 URL 발급과 오답노트 등록, 조회, 삭제, 404 를 확인한다. X-App-Version 헤더로 XP 적립을 막는다
- 흔적이 남는 GET(presigned URL)은 full 에서만 재시도 없이 보내고, refresh 는 확인용 토큰만 허용한다
- nginx 전환 직전에 새 컨테이너를 확인하는 pre_switch_check.sh 를 추가한다 (actuator health 후 새 포트로 read)
- 자체 테스트 42개에서 64개, README 와 smoke-test.yml 의 서명 키 설명을 실제 설정에 맞게 고친다
- healthy 확인과 nginx 전환 사이에서 pre_switch_check.sh 를 부르고, 실패하면 새 컨테이너를 이름으로 지우고 배포를 끝낸다
- 새 컨테이너가 남으면 다음 배포가 현재 색을 잘못 읽을 수 있어서, 지우지 못하면 에러로 남긴다
- 배포를 끝내지 못하면 prod-latest 를 이전 색이 돌리던 이미지로 되돌린다
- 돌고 있던 앱이 없던 복구 배포는 확인이 실패해도 경고만 하고 전환한다
- refresh 서명 키는 서버와 같은지 아직 확인되지 않아 전환 전 확인에는 넘기지 않는다
[Feat] 스모크 테스트가 토큰 갱신과 오답노트 등록을 보고, prod 배포가 nginx 전환 전에 새 컨테이너를 확인한다
- 평균 응답시간은 분모를 clamp_min(…, 1e-9) 에서 > 0 으로 바꾼다. 앱이 재시작되면 같은 라벨의 요청 수는 그대로인데 시간 합만 늘어나는 경우가 있어서, 요청 수 증가량 0 을 1e-9 로 바꿔 나누면 1,250만 ms 같은 값이 나왔다. 이제 그 URI 는 값을 비운다
- 외부 API 성공률은 호출이 없을 때 "최근 1시간 호출 없음" 이 임계값 기준색(빨강) 위에 떴다. null 매핑으로 회색을 준다
- Redis 히트율은 인증 요청마다 로그아웃 토큰과 탈퇴 사용자 블랙리스트를 조회하고 두 키 모두 대부분 없는 것이 정상이라 5% 안팎이 평소 값이다. 경고색을 없애고 패널 설명에 이유를 적는다

Closes #338
[Chore] 대시보드 평균 응답시간, 외부 API 성공률, Redis 히트율 패널의 값과 색을 고친다
- 로그인 성공 시 세션 유휴 만료를 24시간으로 늘린다 (설정이 없어 톰캣 기본값 30분이 적용되고 있었다)
- 로그인 유지 쿠키를 붙여 배포로 컨테이너가 바뀌어도 24시간 동안 다시 로그인하지 않는다
- logoutUrl 에 전체 URL 이 들어가 어떤 요청과도 맞지 않던 설정을 경로로 고친다
- 로그인이 풀린 채 관리자 화면을 열면 JSON 401 대신 로그인 화면으로 보낸다
- 관리자 화면의 유저 삭제 엔드포인트를 없앤다
- 온보딩 페이지의 브랜드 초록(#1cba1a)과 회색 바탕 위 흰 카드 구성으로 공통 스타일을 만든다
- 사이드바, 페이지 이동, 탭 밑줄, 숫자 카운트업, 화면 전환 효과를 공통 조각과 스크립트로 둔다
- 페이지 링크가 지금 걸린 필터를 유지하도록 AdminPager 를 추가한다
- 공지는 실사용자 전원에게 뜨므로 걸기 전과 내리기 전에 한 번 더 확인받는다
- 유저 목록에 검색과 가입 경로 필터, 정렬 7종을 넣고 상세를 치장, 업적, 미션, 스터디룸까지 탭 7개로 나눈다
- 복습 기록을 mission_log 대신 problem_solve 기준으로 바꾸고 복습노트 상세 화면을 추가한다
- 오답노트와 복습 기록, 복습노트 목록에 날짜와 유저, 상태 필터를 넣는다
- 스터디룸 상세에 챌린지, 공유 문제와 댓글, 피드, 주간 리포트를 추가한다
- 미션, 업적, 치장 현황 화면을 새로 만든다
- 통계를 사용자, 학습, AI 분석, 성장, 스터디룸으로 나누고 재방문율과 고착도, 직전 기간 비교를 넣는다
- 날짜별 표의 숫자를 누르면 그날 목록으로 이동한다
- 권한 테스트에 새 엔드포인트와 공지 엔드포인트를 넣는다
- 그리드 안 두 번째 카드에만 위 여백이 붙어 나란한 박스 높이가 어긋나던 규칙을 고친다
- 화면이 뜬 뒤 카드를 투명하게 되돌리던 등장 효과와 숫자 카운트업을 없애 이동할 때 깜빡이지 않게 한다
- 탭과 세그먼트를 화면이 그려지기 전에 붙이고, 페이지 전환은 브라우저 기본 교차 전환만 짧게 쓴다
- 로고를 OnO 전체 브랜드색으로 칠한다
- 데스크톱은 사이드바를 접고 펼 수 있고 상태를 기억한다. 모바일은 상단 메뉴로 여닫는 서랍으로 바꾼다
- 미션 달성률을 시작한 유저 대비 달성으로 이름을 바꾸고 계산 기준을 적는다
- 출시 때 섞인 게스트와 스모크 테스트용 게스트가 지표를 부풀려서 모든 집계 쿼리에 같은 조건을 붙인다
- 조건은 AdminSqlFilters 한 곳에 두고 통계, 홈, 유저, 학습 기록, 스터디룸, 성장 집계에 적용한다
- 목록의 행과 상세 화면은 그대로 두고, 게스트 수와 비율 지표는 없앤다
- 게스트가 만든 데이터가 집계에서 빠지는지 영역별 테스트를 추가한다
- 관리자 컨트롤러의 인자 없는 LocalDate.now() 를 Asia/Seoul 기준으로 바꾼다 (PersistenceRulesTest 위반)
- 로그인 없이 관리자 화면을 열면 로그인 화면으로 보내는 동작을 AuthApiTest 에 반영하고, 화면이 아닌 요청은 401 인지 따로 확인한다
[Feat] 관리자 페이지 개편 (디자인, 로그인 유지, 데이터 조회, 통계)
- 훈장 설명 12개를 숫자로 바꾸고 말투를 담백하게 고친다. 새벽반과 올빼미는 판정 시간(SolveScan 의 05~08시, 00~03시)을 설명에 적는다
- 미션 제목 6개는 V47 마이그레이션의 UPDATE 로 바꾼다. V30, V33 을 고치면 이미 적용된 환경에서 Flyway 체크섬이 어긋난다
- 테스트 시드가 V47 까지 읽게 하고, 옛 문구를 비교하던 테스트를 새 문구로 맞춘다

Closes #341
…copy

[Chore] 훈장 설명과 미션 제목의 한글 수를 숫자로 바꾼다
@KiSeungMin KiSeungMin self-assigned this Sep 21, 2026
@KiSeungMin
KiSeungMin merged commit 74c3d41 into main Sep 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant