Programmers 개발 일지

vscode확장

Programmers Problem Helper 개발 기록

프로그래머스 문제를 풀 때마다 반복되는 준비 과정이 있었다. 문제 페이지를 열고, 예제 입력을 확인하고, VS Code에 파일을 만들고, 테스트 코드를 붙이고, 다시 웹으로 돌아가 제출했다. 한두 문제를 풀 때는 괜찮지만, 꾸준히 연습하려고 하면 이 과정이 생각보다 집중력을 많이 가져갔다.

그래서 처음 만들고 싶었던 것은 거창한 서비스가 아니었다. 문제 번호만 입력하면 VS Code 안에서 문제 설명과 풀이 파일이 열리고, 예제 테스트를 바로 돌릴 수 있는 작은 도구였다. 이 기록은 2026년 4월 21일 첫 커밋부터 2026년 5월 5일 v0.6.0까지, 이 확장이 어떤 문제를 만나며 바뀌어 왔는지 정리한 글이다.

2026-04-21: 문제 번호에서 풀이 환경까지

처음 목표는 단순했다. VS Code Activity Bar에 Programmers 사이드바를 만들고, 사용자가 문제 번호를 입력하면 프로그래머스 문제 페이지를 가져와 로컬에 문제 폴더를 만드는 것이었다.

문제 하나는 problem.md, solution.cpp, .programmers-helper/programmers.json으로 저장했다. 문제 설명과 초기 풀이 파일, 예제와 메타데이터를 로컬에 남기는 구조였다.

처음부터 중요한 기준은 “내 풀이 데이터는 내 컴퓨터 안에 남아야 한다”는 점이었다. 별도 서버를 만들지 않고, 문제 페이지를 가져오는 것 외에는 풀이 코드나 테스트 입력을 외부로 보내지 않는 방향으로 잡았다.

2026-04-22: 테스트 실행

문제 파일을 만드는 것만으로는 부족했다. 실제 풀이 중 가장 자주 하는 일은 코드를 고치고 예제를 다시 실행하는 것이기 때문이다.

그래서 C++ 풀이 파일을 기준으로 테스트 러너를 만들고, 샘플 입력과 예상 출력을 케이스별로 실행하도록 했다. 전체 로그는 Output 패널에 남기되, 사이드바에서는 통과와 실패를 빠르게 볼 수 있게 했다. 무한 루프나 느린 코드를 대비해 케이스별 제한 시간과 실행 중지 기능도 넣었다.

이때부터 확장은 단순한 파일 생성기가 아니라, 풀이 중 계속 옆에 붙어 있는 도구에 가까워졌다.

2026-04-24: 커스텀 테스트와 문제 목록

샘플 테스트만으로는 부족했다. 직접 떠올린 반례나 경계값을 저장하고 다시 실행할 수 있어야 했다. 그래서 문제별 커스텀 테스트를 저장하고, 문제를 다시 열었을 때 복원하도록 만들었다.

사이드바도 작은 작업 공간처럼 바뀌었다. 문제 검색, 저장된 문제 목록, 삭제, notes.md 메모 열기, 풀이 기록 기능이 차례로 들어갔다. 새풀이를 누르면 현재 풀이를 스냅샷으로 보관하고 새로운 풀이 파일에서 다시 시작할 수 있게 한 것도 이 시기의 중요한 변화였다.

2026-04-25: Docker 실행과 런타임 분리

로컬에서 C++을 바로 컴파일하는 방식은 간단하지만, 운영체제와 컴파일러 차이에 취약했다. 그래서 Docker 런타임을 도입했다. Docker 모드에서는 helper 컨테이너 안에서 테스트 러너를 컴파일하고 실행해 환경 차이를 줄였다.

이 무렵부터 코드도 역할별로 나누기 시작했다. 문제 저장, 테스트 실행, Docker 런타임, 사이드바 렌더링이 한 덩어리로 섞이면 이후 기능을 추가하기 어려울 것 같았다. 그래서 각 책임을 조금씩 분리했다.

2026-04-27: 성능과 안정성

문제 수가 늘어나면 폴더 전체를 매번 훑는 방식은 느려질 수밖에 없다. 그래서 문제 목록을 인덱스 파일로 캐시하고, 생성/삭제/다시 풀기 같은 변경이 있을 때만 갱신하도록 바꿨다. Webview에서는 목록 row를 재사용해 불필요한 DOM 재생성을 줄였다.

에디터 레이아웃도 계속 다듬었다. 문제를 바꾸거나 풀이를 초기화할 때 탭이 깜빡이거나 엉뚱한 파일이 실행 대상이 되는 문제를 막기 위해, 현재 문제의 problem.md와 실제 풀이 파일을 더 엄격하게 구분했다. 실행 모드도 dockerlocal 중 선택할 수 있게 정리했다.

2026-04-28: 다중 언어와 Marketplace

v0.1.0에서는 기본 단축키를 추가했다. Ctrl+Alt+T로 샘플 테스트를 실행하고, Ctrl+Alt+S로 실행을 중지할 수 있게 했다.

이후 Java 지원을 넣으면서 구조를 다중 언어 기준으로 바꿨다. 언어별 파일명, 초기 템플릿, Programmers URL 파라미터, runner builder를 분리해 C++과 Java의 차이를 한곳에 가두려 했다. 덕분에 Python 지원도 비교적 자연스럽게 이어졌다.

같은 날 Marketplace 배포 준비도 진행했다. 아이콘, publisher, 키워드, README, 개인정보 안내, 면책 문구를 정리하고, GitHub Actions를 통해 배포 흐름을 만들었다.

2026-04-29: Python, 저장 구조, 타이머

Python은 별도 컴파일이 필요 없기 때문에 runner가 importlibsolution.py를 로드해 solution(...)을 호출하도록 했다. 대신 오류가 났을 때 사용자가 봐야 할 위치는 generated runner가 아니라 자기 풀이 파일이어야 했으므로, traceback 요약을 보강했다.

.programmers-helper 내부도 초기 코드, 풀이 기록, 생성 runner, artifact, fingerprint로 나누었다. 사용자가 남긴 데이터와 확장이 다시 만들 수 있는 생성물을 분리하기 위해서였다.

이후 문제별 타이머를 추가했다. 시작/중지/초기화와 30/60/90/120분 목표 시간을 지원했고, 타이머 상태는 문제 폴더 안의 .programmers-helper/timer.json에 저장하도록 바꿨다. 타이머도 문제 풀이 기록의 일부라고 봤기 때문이다.

2026-04-30: 개발 환경과 사이드바 UX

초기 Dev Container와 symlink 기반 개발 흐름을 제거하고, 로컬 VS Code의 Run Extension을 기준으로 개발 환경을 단순화했다. 개발판과 배포판은 VS Code의 extensionMode로 구분했다.

사이드바 UX도 손봤다. 문제 생성 중에는 입력창과 버튼을 잠그고, 현재 열린 문제는 배지와 스타일로 구분했다. 필터와 검색 영역은 고정하고 문제 row만 스크롤되게 했다. 문제를 많이 풀수록 이런 작은 상태 유지가 실제 사용감에 크게 영향을 줬다.

2026-05-01: 다시 열었을 때의 상태

VS Code를 다시 열었을 때 마지막 문제를 자동으로 복원하도록 했다. 다만 Docker 모드에서는 마지막 문제를 열었다고 바로 컨테이너를 시작하지는 않았다. 컨테이너 준비는 사용자가 실제로 문제를 열거나 테스트를 실행할 때까지 미뤘다.

사용자가 지금 하려는 행동에 필요한 만큼만 준비하고, 무거운 작업은 가능한 한 늦춘다는 기준을 여기에도 적용했다.

2026-05-02: Git 동기화

v0.5.0에서는 Git 기반 동기화를 선택 기능으로 추가했다. 여러 환경에서 같은 문제 폴더와 풀이 상태를 이어 쓰고 싶다는 요구가 생겼기 때문이다.

Setup Sync로 원격 저장소와 브랜치를 설정하고, Sync Now를 실행하면 저장, commit, fetch, merge, conflict marker 검사, push가 진행되도록 했다. HTTPS GitHub token은 VS Code SecretStorage에 저장하고, Git 명령 인자에는 직접 넣지 않았다.

충돌이 생기면 conflict resolver Webview를 열어 처리하도록 했고, 삭제/수정 충돌은 문제 폴더를 유지할지 삭제를 유지할지 선택할 수 있게 했다. 이 기능을 넣으면서 src 구조도 core, problems, runners, sync, timers, ui로 크게 재정리했다.

2026-05-04: 탭 유지와 동기화 안정화

v0.5.2에서는 에디터 탭 정리 방식을 설정으로 만들었다. 같은 문제 안에서 현재 풀이와 이전 풀이 기록을 비교하고 싶을 때가 많았기 때문이다.

programmersHelper.tabResetMode는 세 가지 모드를 지원한다.

  • always: 현재 문제와 선택한 풀이 탭만 남긴다.
  • onProblemChange: 다른 문제로 넘어갈 때만 탭을 정리한다.
  • never: 문제를 바꿔도 풀이/풀이 기록 탭을 가능한 한 유지한다.

기본값은 onProblemChange로 잡았다. 문제를 바꿀 때는 화면을 정리해 주고, 같은 문제 안에서는 비교하던 탭을 살려 두는 편이 자연스럽다고 봤다.

이후 v0.5.3부터 v0.5.6까지는 Git 동기화와 개발/배포 환경이 섞이지 않도록 안정성을 높였다. 개발판과 배포판의 remote URL 저장 키를 분리하고, sync storage와 인증 상태를 확인하는 Show Sync Info를 추가했다. GitHub token만 따로 저장하거나 교체하는 명령도 넣었다.

Docker 런타임 컨테이너는 네트워크 비활성 상태로 생성하도록 바꿨다. 테스트 중인 제출 코드가 외부 네트워크에 접근하지 못하도록 실행 환경의 경계를 조금 더 단단하게 만든 것이다.

2026-05-05: 문제 목록을 더 가볍게

v0.6.0에서는 문제 목록 갱신 흐름을 캐시 중심으로 다시 정리했다. 다시 풀기 체크, 현재 문제 인덱스 갱신, 삭제 같은 동작에서 problem-index.json을 바로 읽기보다 메모리 문제 목록을 먼저 사용하도록 했다.

다시 풀기 checkbox는 Webview에서 즉시 반영하고, 짧은 debounce를 거쳐 문제별 최신 상태만 저장하도록 했다. 사용자가 checkbox를 빠르게 여러 번 눌러도 매번 디스크에 쓰지 않고, 마지막 상태만 반영한다. 저장 후에는 이미 알고 있는 review 값으로 인덱스를 patch하므로 각 문제 폴더를 다시 읽을 필요도 줄었다.

문제 목록 row에는 문제 번호, 난이도, 풀이 기록 수를 함께 표시하고, 다시 풀기 상태를 바꿔도 스크롤 위치가 유지되도록 했다. 새로고침 버튼은 명시적인 전체 재스캔으로 취급하고, 일반적인 사이드바 복원은 메모리 캐시, index JSON, 파일시스템 scan 순서로 fallback하도록 했다.

설계하면서 계속 지키려고 한 것

이 프로젝트를 만들면서 계속 같은 기준으로 돌아오려고 했다.

첫째, 문제 풀이의 중심은 사용자의 코드다. 확장은 문제 설명을 가져오고, 파일을 열고, 테스트를 실행하고, 기록을 관리하지만 제출이나 풀이 자체를 대신하지 않는다.

둘째, 사용자가 만든 데이터와 확장이 만든 생성물을 분리한다. 풀이 파일, 메모, 커스텀 테스트, 타이머, 풀이 기록은 오래 남아야 하는 데이터이고, runner, artifact, fingerprint, 문제 목록 인덱스는 다시 만들 수 있는 캐시다.

셋째, 무거운 작업은 늦게 하고 상태는 분명히 보여준다. 확장 활성화 시점에는 가볍게 시작하고, 실제 명령이나 Webview 액션이 들어왔을 때 필요한 서비스를 준비한다.

넷째, 언어별 차이는 작게 가둔다. C++, Java, Python은 템플릿, 컴파일, 실행, 에러 포맷이 다르지만, 테스트 실행의 큰 흐름은 공통으로 유지하려고 했다.

다섯째, 매일 쓰는 도구는 작은 UI 상태가 중요하다. 문제 목록 스크롤, 현재 문제 표시, 풀이 기록 선택 상태, 탭 유지 방식, 커스텀 테스트 저장 표시 같은 것들이 실제 사용감에 계속 영향을 준다.

지금 상태

현재 v0.6.0 기준으로 Programmers Problem Helper는 문제 생성, Markdown 미리보기, C++/Java/Python 풀이 템플릿, 샘플/커스텀 테스트 실행, 풀이 기록, 메모, 타이머, Marketplace 배포, Git 동기화, 캐시 중심의 문제 목록 갱신까지 갖춘 VS Code 확장이 됐다.

처음에는 “문제 번호를 넣으면 파일을 만들어 주는 도구”였지만, 만들다 보니 알고리즘 연습 과정 전체를 조금씩 품게 됐다. 문제를 가져오고, 풀고, 테스트하고, 다시 풀고, 기록하고, 여러 환경에서 이어서 작업하는 흐름까지 연결됐다.

아직 더 할 일은 많다. 더 많은 언어를 지원할 수도 있고, 테스트 결과 UI를 더 읽기 쉽게 만들 수도 있고, 동기화 충돌 경험을 더 부드럽게 만들 수도 있다. 그래도 방향은 분명하다. 이 확장은 알고리즘을 대신 풀어 주는 도구가 아니라, 사람이 문제를 푸는 동안 반복 작업과 환경 차이를 조용히 줄여 주는 도구로 계속 다듬어 가고 싶다.