JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

Description

@jjoonleo

배경

현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

  • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
  • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

측정 지표

  • 인증 요청 1건당 DB query 수
  • 인증 요청 p50/p95 latency
  • JwtAuthenticationFilter 처리 시간
  • 동일 access token 요청 반복 시 DB read QPS

개선 방향

  • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
  • Active Session 검증 책임을 한 module/seam에 모은다.
  • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
  • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
  • 기존 단일 Active Session 정책은 유지한다.

완료 조건

  • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
  • 개선 전 인증 요청 p50/p95 latency를 측정한다.
  • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
  • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
  • refresh token 재발급 경로가 기존과 호환된다.
  • ./gradlew check가 통과한다.

참고 코드

  • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
  • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
  • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

      Description

      @jjoonleo

      배경

      현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

      • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
      • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

      이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

      측정 지표

      • 인증 요청 1건당 DB query 수
      • 인증 요청 p50/p95 latency
      • JwtAuthenticationFilter 처리 시간
      • 동일 access token 요청 반복 시 DB read QPS

      개선 방향

      • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
      • Active Session 검증 책임을 한 module/seam에 모은다.
      • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
      • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
      • 기존 단일 Active Session 정책은 유지한다.

      완료 조건

      • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
      • 개선 전 인증 요청 p50/p95 latency를 측정한다.
      • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
      • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
      • refresh token 재발급 경로가 기존과 호환된다.
      • ./gradlew check가 통과한다.

      참고 코드

      • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
      • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
      • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

          Description

          @jjoonleo

          배경

          현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

          • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
          • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

          이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

          측정 지표

          • 인증 요청 1건당 DB query 수
          • 인증 요청 p50/p95 latency
          • JwtAuthenticationFilter 처리 시간
          • 동일 access token 요청 반복 시 DB read QPS

          개선 방향

          • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
          • Active Session 검증 책임을 한 module/seam에 모은다.
          • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
          • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
          • 기존 단일 Active Session 정책은 유지한다.

          완료 조건

          • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
          • 개선 전 인증 요청 p50/p95 latency를 측정한다.
          • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
          • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
          • refresh token 재발급 경로가 기존과 호환된다.
          • ./gradlew check가 통과한다.

          참고 코드

          • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
          • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
          • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

              Description

              @jjoonleo

              배경

              현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

              • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
              • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

              이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

              측정 지표

              • 인증 요청 1건당 DB query 수
              • 인증 요청 p50/p95 latency
              • JwtAuthenticationFilter 처리 시간
              • 동일 access token 요청 반복 시 DB read QPS

              개선 방향

              • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
              • Active Session 검증 책임을 한 module/seam에 모은다.
              • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
              • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
              • 기존 단일 Active Session 정책은 유지한다.

              완료 조건

              • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
              • 개선 전 인증 요청 p50/p95 latency를 측정한다.
              • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
              • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
              • refresh token 재발급 경로가 기존과 호환된다.
              • ./gradlew check가 통과한다.

              참고 코드

              • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
              • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
              • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

                  Description

                  @jjoonleo

                  배경

                  현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

                  • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
                  • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

                  이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

                  측정 지표

                  • 인증 요청 1건당 DB query 수
                  • 인증 요청 p50/p95 latency
                  • JwtAuthenticationFilter 처리 시간
                  • 동일 access token 요청 반복 시 DB read QPS

                  개선 방향

                  • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
                  • Active Session 검증 책임을 한 module/seam에 모은다.
                  • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
                  • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
                  • 기존 단일 Active Session 정책은 유지한다.

                  완료 조건

                  • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
                  • 개선 전 인증 요청 p50/p95 latency를 측정한다.
                  • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
                  • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
                  • refresh token 재발급 경로가 기존과 호환된다.
                  • ./gradlew check가 통과한다.

                  참고 코드

                  • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
                  • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
                  • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

                  Activity

                  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

                      Description

                      @jjoonleo

                      배경

                      현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

                      • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
                      • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

                      이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

                      측정 지표

                      • 인증 요청 1건당 DB query 수
                      • 인증 요청 p50/p95 latency
                      • JwtAuthenticationFilter 처리 시간
                      • 동일 access token 요청 반복 시 DB read QPS

                      개선 방향

                      • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
                      • Active Session 검증 책임을 한 module/seam에 모은다.
                      • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
                      • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
                      • 기존 단일 Active Session 정책은 유지한다.

                      완료 조건

                      • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
                      • 개선 전 인증 요청 p50/p95 latency를 측정한다.
                      • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
                      • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
                      • refresh token 재발급 경로가 기존과 호환된다.
                      • ./gradlew check가 통과한다.

                      참고 코드

                      • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
                      • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
                      • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

                      Activity

                      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

                          Description

                          @jjoonleo

                          배경

                          현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

                          • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
                          • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

                          이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

                          측정 지표

                          • 인증 요청 1건당 DB query 수
                          • 인증 요청 p50/p95 latency
                          • JwtAuthenticationFilter 처리 시간
                          • 동일 access token 요청 반복 시 DB read QPS

                          개선 방향

                          • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
                          • Active Session 검증 책임을 한 module/seam에 모은다.
                          • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
                          • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
                          • 기존 단일 Active Session 정책은 유지한다.

                          완료 조건

                          • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
                          • 개선 전 인증 요청 p50/p95 latency를 측정한다.
                          • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
                          • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
                          • refresh token 재발급 경로가 기존과 호환된다.
                          • ./gradlew check가 통과한다.

                          참고 코드

                          • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
                          • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
                          • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

                          Activity

                          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              JWT 인증 필터의 중복 User 조회 제거 및 요청당 DB 쿼리 수 개선 #325

                              Description

                              @jjoonleo

                              배경

                              현재 JWT 인증 경로에서 access token 검증과 Spring Security authentication 저장 과정이 각각 User를 조회합니다.

                              • JwtTokenProvider.isAccessTokenValid(...)에서 token signature/claim 검증 후 User를 조회하고, 저장된 access token과 비교합니다.
                              • 이후 JwtAuthenticationFilter.checkAccessTokenAndAuthentication(...)에서 access token에서 userId를 다시 추출하고 User를 다시 조회해 authentication을 저장합니다.

                              이 구조는 인증된 API 요청마다 DB 조회가 중복되어, 트래픽 증가 시 요청 latency와 DB 부하에 직접 영향을 줄 수 있습니다.

                              측정 지표

                              • 인증 요청 1건당 DB query 수
                              • 인증 요청 p50/p95 latency
                              • JwtAuthenticationFilter 처리 시간
                              • 동일 access token 요청 반복 시 DB read QPS

                              개선 방향

                              • access token 검증과 authentication 저장에서 User 조회를 한 번만 수행하도록 인증 흐름을 정리한다.
                              • Active Session 검증 책임을 한 module/seam에 모은다.
                              • JwtTokenProvider가 HTTP/Spring Security authentication 저장까지 알지 않도록 역할을 줄인다.
                              • 컨트롤러에서 HttpServletRequest를 통해 userId를 재파싱하는 흐름을 점진적으로 줄일 수 있는지 검토한다.
                              • 기존 단일 Active Session 정책은 유지한다.

                              완료 조건

                              • 개선 전 인증 요청 1건당 DB query 수를 측정한다.
                              • 개선 전 인증 요청 p50/p95 latency를 측정한다.
                              • access token 인증 성공 경로에서 User 조회가 2회에서 1회로 줄었음을 테스트 또는 계측으로 확인한다.
                              • invalid/expired/replaced access token 응답 동작이 기존과 호환된다.
                              • refresh token 재발급 경로가 기존과 호환된다.
                              • ./gradlew check가 통과한다.

                              참고 코드

                              • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtAuthenticationFilter.java
                              • ontime-back/src/main/java/devkor/ontime_back/global/jwt/JwtTokenProvider.java
                              • ontime-back/src/main/java/devkor/ontime_back/service/AuthTokenService.java

                              Activity

                              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions