백준허브 확장프로그램 구름 플랫폼 업로드 문제 해결하가

현재 노트: KR-010.00 f 백준허브 확장프로그램 구름 플랫폼 업로드 문제 해결하가
상위 분류: KR-010.00

관련 이슈 정리
https://github.com/BaekjoonHub/BaekjoonHub/issues/295
#디버깅 #Debugging

백준허브 구름LEVEL 자동 업로드 오류 해결기: DOM 구조 변화로 인한 웹 확장 프로그램 트러블슈팅

코딩 테스트 플랫폼에서 문제 풀이 후 GitHub에 자동으로 코드를 저장해주는 크롬 확장 프로그램 '백준허브(BaekjoonHub)'는 개발자들에게 매우 유용한 도구입니다. 하지만 최근 구름LEVEL 문제를 풀고 제출하는 과정에서 이 편리한 자동 업로드 기능이 작동하지 않는 문제를 겪었습니다.

이 글은 백준허브 플러그인이 구름IDE (정확히는 구름LEVEL 서비스)에서 동작하지 않는 문제를 해결하기 위한 트러블슈팅 과정을 상세히 기록한 글입니다. 다른 사람이 만든 웹 확장 프로그램에서 생긴 문제를 진단 및 분석하는 값진 결과를 얻었습니다.


1. 문제 상황: 구름LEVEL 제출 후 백준허브 자동 업로드 미작동

백준허브 확장 프로그램 버전 1.2.5 환경에서, 구름LEVEL(GoormLEVEL) 문제 제출 시 자동 업로드 기능이 작동하지 않는 현상이 발생했습니다. 문제 제출 후 GitHub 업로드를 알리는 스피너(로딩 인디케이터)가 표시되지 않았고, 브라우저 개발자 도구(Console)에도 백준허브 관련 명확한 오류 메시지가 나타나지 않아 초기 원인 파악에 어려움이 있었습니다.

2. 해결 시도: 웹 확장 프로그램 분석을 통한 문제 디버깅 과정

문제가 발생했을 때, 우선적으로 백준허브가 정상적으로 작동할 때의 '정답지'를 상상하며 역추적했습니다. 보통 제출 후 성공 메시지와 함께 GitHub 업로드 스피너가 나타나고, 최종적으로 코드가 GitHub 저장소에 푸시되어야 합니다. 이러한 정상 동작이 보이지 않았으므로, 하나씩 문제의 원인을 찾아나가기 시작했습니다.

단계 1: 개발자 도구(Console, Network)를 통한 초기 점검

단계 2: 확장 프로그램 소스 코드 확인 및 추론 ("감"에 의존)

백준허브 확장 프로그램은 크롬 스토어에서 다운로드받을 수 있지만, 실제 파일들은 로컬에 저장되어 있습니다. (일반적인 경로: C:\Users\USERNAME\AppData\Local\Google\Chrome\User Data\Default\Extensions\ccammcjdkpgjmcpijpahlehmapgmphmk\1.2.5_0). manifest.json 파일을 통해 확장 프로그램의 구조를 대략 파악한 뒤, '제출 후 자동 업로드' 기능에 문제가 있으니, 제출 결과 확인 및 데이터 파싱을 담당하는 부분부터 직관적으로 확인했습니다. 특히 구름LEVEL 관련 로직은 scripts/goormlevel 폴더에 있을 것이라고 추론했습니다.

단계 3: 주요 함수 및 변수 식별, 로깅을 통한 심층 디버깅

단계 4: DOM 구조 분석 및 원인 파악

parsedData.code가 왜 비어있는지 알아내기 위해, 실제로 코드를 파싱하는 로직이 담긴 scripts/goormlevel/parsing.js 파일을 집중적으로 분석했습니다. parseData() 함수 내 코드 추출 로직은 다음과 같았습니다.

// scripts/goormlevel/parsing.js 파일 내 parseData() 함수 일부
// const code = [...document.querySelectorAll('#fileEditor textarea[readonly]')].map(($element) => $element.value)[currentLanguageIndex] || '';

이 코드는 #fileEditor 내부의 textarea[readonly] 요소를 찾아 그 value 속성에서 코드를 가져오려고 합니다. 그러나 구름LEVEL 사이트의 코드 에디터 부분을 개발자 도구로 확인해본 결과, 코드 에디터가 더 이상 textarea 태그를 사용하지 않고 div 기반(CodeMirror와 유사한) 구조로 변경되었음을 알 수 있었습니다.

[구름LEVEL 코드 에디터의 DOM 구조 스크린샷 (예시)]
Pasted image 20250528160521.png

이러한 DOM 구조 변화가 기존 백준허브의 코드 추출 로직과 맞지 않아 parsedData.code가 빈 문자열로 반환되었던 것입니다. 초기 백준허브 개발 당시에는 textarea 기반 에디터였을 것이고, 이후 구름LEVEL 사이트 업데이트로 에디터 구조가 변경되었을 가능성이 높습니다.

3. 해결 방법: div 기반 에디터에 맞춰 코드 추출 로직 수정

원인이 명확해졌으므로, 로컬 환경에서 scripts/goormlevel/parsing.js 파일의 parseData() 함수 내 소스 코드 추출 로직을 구름LEVEL의 새로운 div 기반 에디터 구조에 맞춰 수정했습니다.

[수정된 scripts/goormlevel/parsing.js 파일의 parseData() 함수 핵심 코드]

// scripts/goormlevel/parsing.js 파일 내 parseData() 함수 수정
function parseData() {
  // ... (생략된 기존 코드)

  // 기존 코드 추출 로직 (주석 처리 또는 제거)
  // const code_old = [...document.querySelectorAll('#fileEditor textarea[readonly]')].map(($element) => $element.value)[currentLanguageIndex] || '';

  /* 새로운 코드 추출 로직 - div 기반 에디터 대응 */
  let extractedCode = "";
  // 코드 에디터의 주요 컨텐츠 영역을 나타내는 div 요소 선택
  const allCodeEditorContentDivsInFileEditor = document.querySelectorAll("#fileEditor div.cm-content.cm-lineWrapping");

  if (currentLanguageIndex >= 0 && allCodeEditorContentDivsInFileEditor.length > currentLanguageIndex) {
    // 현재 선택된 언어 인덱스에 해당하는 에디터가 존재할 경우
    const targetEditorDiv = allCodeEditorContentDivsInFileEditor[currentLanguageIndex];
    if (targetEditorDiv) {
      const lines = [];
      const lineDivs = targetEditorDiv.querySelectorAll("div.cm-line"); // 각 줄을 나타내는 div 요소 선택
      lineDivs.forEach((lineDiv) => {
        lines.push(lineDiv.textContent); // 각 줄의 텍스트 콘텐츠 추출
      });
      extractedCode = lines.join("\n"); // 추출된 줄들을 줄바꿈 문자로 합치기
    }
  } else if (allCodeEditorContentDivsInFileEditor.length === 1 && currentLanguageIndex < 0) {
    // 단일 에디터이거나 언어 인덱스가 명확하지 않을 경우 (예: 하나의 파일만 있을 때) 첫 번째 에디터 사용
    const targetEditorDiv = allCodeEditorContentDivsInFileEditor[0];
    const lines = [];
    const lineDivs = targetEditorDiv.querySelectorAll("div.cm-line");
    lineDivs.forEach((lineDiv) => {
      lines.push(lineDiv.textContent);
    });
    extractedCode = lines.join("\n");
  }

  const code = extractedCode; // 최종적으로 이 변수에 추출된 코드가 담깁니다.

  // ... (생략된 기존 코드)
  // 모든 파싱된 데이터를 포함하는 객체 반환
  const parsedData = {
    // ... 기존 parsedData 객체 구성 로직
    code: code // 추출된 코드를 parsedData.code에 할당
  };
  return parsedData;
}

이 수정된 확장 프로그램을 로컬에서 테스트하기 위해, 크롬 브라우저에서 chrome://extensions/로 이동하여 "개발자 모드"를 활성화한 후 "압축 해제된 확장 프로그램을 로드합니다" 버튼을 통해 수정된 백준허브 확장 프로그램 폴더를 선택하여 로드했습니다.

4. 해결 후 정리: 자동 업로드 기능 정상 작동 확인

위와 같이 로컬에서 코드 추출 로직을 수정한 후 다시 구름LEVEL에서 문제를 제출해본 결과, parsedData.code에 현재 선택된 언어의 실제 소스 코드가 정상적으로 채워지는 것을 확인했습니다.

그 결과 isNotEmpty(parsedData) (또는 !isEmpty(parsedData)) 조건이 true를 반환하여 업로드 로직이 실행되었고, 백준허브 스피너가 정상적으로 나타난 후 GitHub에 코드가 성공적으로 업로드되는 것을 확인했습니다.

5. 추가 참고 및 회고: 웹 확장 프로그램과 웹 서비스의 변화

이번 디버깅을 통해 웹사이트의 UI/UX 개선이나 내부 라이브러리 변경이 크롬 확장 프로그램과 같은 외부 도구에 얼마나 큰 영향을 미칠 수 있는지 다시 한번 체감했습니다. 구름LEVEL이 더 나은 에디터 경험을 제공하기 위해 내부 DOM 구조를 textarea에서 div 기반으로 변경했고, 이로 인해 기존 백준허브의 textarea 기반 추출 로직이 무력화된 것이 핵심 원인이었습니다.

이러한 문제는 웹 확장 프로그램 개발 시 늘 고려해야 할 부분이며, 웹 서비스 제공자가 DOM 구조를 변경하면 확장 프로그램 또한 업데이트가 필요할 수 있음을 상기시켜주었습니다. 백준허브처럼 유용한 오픈소스 프로젝트에 기여할 기회가 된다면, 이 수정 사항을 Pull Request로 제안하여 다른 사용자들도 동일한 문제를 겪지 않도록 돕고 싶습니다.

문제 상황 파악부터 개발자 도구 활용, 소스 코드 분석, DOM 구조 이해, 그리고 직접적인 코드 수정까지의 과정을 경험하며, 웹 확장 프로그램의 동작 원리와 웹 트러블슈팅 능력에 대한 이해를 한층 더 높일 수 있었던 값진 경험이었습니다.