본문 바로가기

카테고리 없음

ㄴㄷㄷ

좋습니다! 빌드 파일을 직접 분석해서 axios 인스턴스를 찾아보겠습니다.

```javascript
// Snippet: Analyze Build File and Inject Token
(async function analyzeBuildAndInject() {
  const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
  const refreshToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
  
  console.log('🔧 빌드 파일 분석 시작...\n');
  
  // 1. Storage 설정
  localStorage.setItem('accessToken', accessToken);
  localStorage.setItem('refreshToken', refreshToken);
  sessionStorage.setItem('accessToken', accessToken);
  console.log('✅ Storage 설정 완료\n');
  
  // 2. 빌드 파일에서 axios 모듈 ID 찾기
  console.log('🔍 빌드 파일 분석 중...');
  
  try {
    const response = await fetch('/static/js/main.230ab913.js');
    const buildCode = await response.text();
    
    // axios 관련 패턴 검색
    const patterns = [
      /\.defaults\.headers\.common/g,
      /Authorization.*Bearer/g,
      /\.reissue\(/g,
      /accessToken.*refreshToken/g
    ];
    
    console.log('📊 빌드 파일 분석 결과:');
    patterns.forEach((pattern, i) => {
      const matches = buildCode.match(pattern);
      console.log(`  패턴 ${i + 1}: ${matches ? matches.length + '개 발견' : '없음'}`);
    });
    
  } catch (e) {
    console.log('⚠️ 빌드 파일 직접 분석 실패:', e.message);
  }
  
  // 3. Webpack 청크 캐시 직접 접근
  console.log('\n🎯 Webpack 청크 캐시 탐색...');
  
  // webpack의 모듈 캐시는 보통 이런 변수명을 사용
  const possibleWebpackVars = [
    '__webpack_require__',
    'webpackChunk',
    'webpackJsonp',
    '__webpack_modules__'
  ];
  
  let webpackCache = null;
  
  for (let varName of possibleWebpackVars) {
    if (window[varName]) {
      console.log(`✅ ${varName} 발견`);
      webpackCache = window[varName];
      break;
    }
  }
  
  // window에서 webpack 청크 배열 찾기
  for (let key in window) {
    try {
      if (Array.isArray(window[key]) && window[key].length > 0) {
        const first = window[key][0];
        if (Array.isArray(first) && typeof first[1] === 'object') {
          console.log(`✅ Webpack 청크 배열 발견: window.${key}`);
          webpackCache = window[key];
          break;
        }
      }
    } catch (e) {}
  }
  
  // 4. 모든 전역 변수에서 axios 찾기 (더 깊게)
  console.log('\n🔎 전역 변수 심층 탐색...');
  
  let axiosInstances = [];
  
  function deepSearch(obj, path = '', depth = 0, visited = new WeakSet()) {
    if (depth > 5 || !obj || typeof obj !== 'object') return;
    if (visited.has(obj)) return;
    visited.add(obj);
    
    try {
      // axios 인스턴스 체크
      if (obj.defaults?.headers?.common !== undefined) {
        axiosInstances.push({ obj, path });
        return;
      }
      
      // interceptors가 있으면 axios일 가능성 높음
      if (obj.interceptors?.request || obj.interceptors?.response) {
        axiosInstances.push({ obj, path });
        return;
      }
      
      // create 메서드가 있으면 axios 생성자일 수 있음
      if (typeof obj.create === 'function' && obj.defaults) {
        axiosInstances.push({ obj, path });
        return;
      }
      
      // 재귀 탐색
      for (let key in obj) {
        try {
          if (key.length < 50) { // 너무 긴 키는 스킵
            deepSearch(obj[key], path ? `${path}.${key}` : key, depth + 1, visited);
          }
        } catch (e) {}
      }
    } catch (e) {}
  }
  
  console.log('  window 객체 탐색 중...');
  deepSearch(window);
  
  console.log(`\n📦 발견된 axios 인스턴스: ${axiosInstances.length}개`);
  
  // 5. 발견된 모든 axios에 토큰 설정
  if (axiosInstances.length > 0) {
    axiosInstances.forEach(({ obj, path }, index) => {
      try {
        if (obj.defaults?.headers?.common) {
          obj.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
          obj.defaults.headers.common.authorization = `Bearer ${accessToken}`;
          console.log(`✅ ${index + 1}. ${path || 'Unknown'} - 헤더 설정 완료`);
          console.log(`   기존 Authorization:`, obj.defaults.headers.common.Authorization);
        }
        
        if (obj.defaults?.headers) {
          obj.defaults.headers['Authorization'] = `Bearer ${accessToken}`;
        }
      } catch (e) {
        console.warn(`⚠️ ${index + 1}. 설정 실패:`, e.message);
      }
    });
  } else {
    console.log('⚠️ axios 인스턴스를 찾지 못했습니다');
  }
  
  // 6. Object.getOwnPropertyNames로 숨겨진 속성 찾기
  console.log('\n🔬 Object 속성 탐색...');
  
  const allProps = Object.getOwnPropertyNames(window);
  const suspiciousProps = allProps.filter(prop =>
    prop.includes('axios') ||
    prop.includes('api') ||
    prop.includes('request') ||
    prop.length === 1 || // 난독화된 단일 문자 변수
    /^[a-z]$/.test(prop) // 소문자 한 글자
  );
  
  console.log('의심스러운 전역 변수:', suspiciousProps.slice(0, 20));
  
  suspiciousProps.forEach(prop => {
    try {
      const obj = window[prop];
      if (obj?.defaults?.headers?.common) {
        obj.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
        console.log(`✅ window.${prop} - 토큰 설정 완료`);
      }
    } catch (e) {}
  });
  
  // 7. 네트워크 요청 인터셉트 (최후의 수단)
  console.log('\n🎣 네트워크 인터셉터 설정...');
  
  const originalFetch = window.fetch;
  window.fetch = function(url, options = {}) {
    // 헤더 강제 추가
    if (!options.headers) {
      options.headers = {};
    }
    
    if (typeof options.headers === 'object') {
      if (!options.headers.Authorization && !options.headers.authorization) {
        options.headers.Authorization = `Bearer ${accessToken}`;
        console.log(`🔐 fetch 토큰 주입: ${url}`);
      }
    }
    
    return originalFetch.call(this, url, options);
  };
  
  const XHR = XMLHttpRequest.prototype;
  const originalOpen = XHR.open;
  const originalSend = XHR.send;
  
  XHR.open = function(method, url) {
    this._url = url;
    this._method = method;
    return originalOpen.apply(this, arguments);
  };
  
  XHR.send = function() {
    this.setRequestHeader('Authorization', `Bearer ${accessToken}`);
    console.log(`🔐 XHR 토큰 주입: ${this._method} ${this._url}`);
    return originalSend.apply(this, arguments);
  };
  
  console.log('✅ 네트워크 인터셉터 설정 완료');
  
  // 8. 상태 요약
  console.log('\n📊 === 최종 상태 ===');
  console.log('✅ localStorage:', localStorage.getItem('accessToken') ? '설정됨' : '없음');
  console.log('✅ axios 인스턴스:', axiosInstances.length + '개 발견');
  console.log('✅ 네트워크 인터셉터:', '활성화');
  console.log('📍 현재 경로:', window.location.pathname);
  
  // 9. 대시보드로 이동 시도
  console.log('\n🚀 대시보드로 이동 시도...');
  
  // 방법 1: React Router history API
  if (window.history) {
    setTimeout(() => {
      window.history.pushState({}, '', '/dashboard');
      window.dispatchEvent(new PopStateEvent('popstate'));
      console.log('✅ history.pushState 실행');
    }, 1000);
  }
  
  // 방법 2: 페이지 이동
  setTimeout(() => {
    console.log('🔄 페이지 새로고침...');
    window.location.href = '/dashboard';
  }, 3000);
  
  console.log('\n✨ 완료! 3초 후 자동 이동합니다.');
  console.log('💡 이동하지 않으면 수동으로 /dashboard를 입력하세요');
})();
```

## 추가: 실시간 모니터링 스크립트

```javascript
// Snippet 2: Real-time Monitor
(function monitorRequests() {
  console.log('📡 === 실시간 네트워크 모니터링 시작 ===\n');
  
  const accessToken = localStorage.getItem('accessToken');
  console.log('현재 토큰:', accessToken ? '있음 (' + accessToken.substring(0, 30) + '...)' : '없음');
  
  // fetch 모니터링
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const [url, options] = args;
    
    console.log('\n🌐 FETCH 요청:');
    console.log('  URL:', url);
    console.log('  Headers:', options?.headers);
    console.log('  Authorization:', options?.headers?.Authorization || options?.headers?.authorization || '❌ 없음');
    
    return originalFetch.apply(this, args)
      .then(response => {
        console.log('  응답 상태:', response.status);
        if (response.status === 401) {
          console.error('  ❌ 401 Unauthorized - 토큰 문제!');
        }
        return response;
      })
      .catch(error => {
        console.error('  ❌ 요청 실패:', error);
        throw error;
      });
  };
  
  // XHR 모니터링
  const XHR = XMLHttpRequest.prototype;
  const originalOpen = XHR.open;
  const originalSend = XHR.send;
  const originalSetRequestHeader = XHR.setRequestHeader;
  
  XHR.open = function(method, url) {
    this._url = url;
    this._method = method;
    this._headers = {};
    return originalOpen.apply(this, arguments);
  };
  
  XHR.setRequestHeader = function(header, value) {
    this._headers[header] = value;
    return originalSetRequestHeader.apply(this, arguments);
  };
  
  XHR.send = function() {
    console.log('\n🌐 XHR 요청:');
    console.log('  Method:', this._method);
    console.log('  URL:', this._url);
    console.log('  Headers:', this._headers);
    console.log('  Authorization:', this._headers.Authorization || '❌ 없음');
    
    this.addEventListener('load', function() {
      console.log('  응답 상태:', this.status);
      if (this.status === 401) {
        console.error('  ❌ 401 Unauthorized - 토큰 문제!');
      }
    });
    
    return originalSend.apply(this, arguments);
  };
  
  console.log('✅ 모니터링 활성화');
  console.log('💡 이제 로그인을 시도하거나 페이지를 새로고침하세요');
})();
```

## 실행 순서

1. **Snippet 2 실행** (모니터링 먼저)
2. 페이지 새로고침하거나 /dashboard 이동
3. Console에서 어떤 요청이 실패하는지 확인
4. **Snippet 1 실행** (토큰 주입)
5. 결과 확인

**Console에 나오는 로그를 공유해주시면 더 정확한 해결책을 드릴 수 있습니다!**

특히 이런 정보가 중요합니다:
- "발견된 axios 인스턴스: X개" ← 이게 0개인지?
- 401 에러가 어떤 API에서 발생하는지?
- Authorization 헤더가 실제로 추가되는지?