들어가며
이전 글에서 Github Actions와 PR-Agent를 이용해서 알고리즘 문제 풀이 환경을 자동화했었다. 특정 브랜치에 코드를 푸시하면 PR이 자동으로 생성되고, 코드 리뷰까지 받을 수 있는 워크플로우였다. 꽤 만족스럽게 써왔는데, 한가지 불편한 점이 남아 있었다.
처음에 만들었던 문제 파일 생성 스크립트가 너무 불편하다는 것이었다.
문제를 풀 때 마다 스크립트를 이용해서 문제 파일을 생성했는데, 매번 npm create <문제 url> 을 실행하는 것이 생각보다 번거로웠다. 그리고 스크립트를 이용하더라도 헤더 주석 편집, README 업데이트, 디렉토리 이동 같은 것들을 직접 해야 하는 부분이 꽤 있었는데, 이것도 매번 하다보니 번거로웠다.
그러다 이런저런 Claude Code 활용기를 접하게 되었고, 내 상황에도 뭔가 적용해 볼 수 있을 것 같아서 간단한 MCP 서버를 직접 만들어서 이 과정을 좀 더 편하게 만들어보기로 했다.
설계하기
전체적인 플로우는 꽤 단순했다.
- 문제 링크를 Claude Code에 전달
- 자동으로 문제 정보(제목과 난이도) 조회
- 날짜에 맞는 브랜치 생성 + 파일 생성 + README 업데이트
- 요청하는 경우 풀이 가이드 제공
- 문제를 풀고 나면 정해진 포맷으로 커밋 + 코드 피드백
- PR 생성을 요청해서 PR 생성 + 머지까지 로컬에서 가능하도록
MCP 서버 만들기
문제 정보 조회는 Claude Code에서 직접 API를 호출하는 것 보다는 MCP 서버를 만드는 것이 더 깔끔하다고 판단했다. 저장소 내부에 TypeScript로 간단히 MCP 서버를 구성했다.
백준
solved.ac를 활용했다. 문제 번호로 문제 정보를 가져오는 api를 이용했다.
server.registerTool("get_backjoon_problem", {description: "백준 문제 번호로 문제 제목과 난이도를 조회합니다.",inputSchema: {problemId: z.number().int().positive().describe("백준 문제 번호 (예: 1000)"),}}, async ({ problemId }) => {const res = await fetch(`https://solved.ac/api/v3/problem/show?problemId=${problemId}`,{ headers: { Accept: "application/json" } });const data = await res.json();return {content: [{type: "text" as const,text: JSON.stringify({title: data.titleKo,tier: tierNumberToString(data.level),})}]}});
문제 난이도가 숫자로 내려오기 때문에, 이를 실제로 사용할 티어 이름으로 변환하는 함수는 따로 만들었다. (tierNumberToString)
리트코드
리트코드는 공개된 API는 없지만 내부적으로 GraphQL API를 이용하고 있다. 문제 url의 slug를 이용해서 문제 정보를 가져올 수 있도록 했다.
/*** 리트코드 문제 정보 조회 (LeetCode API 사용)* @see https://leetcode.com/discuss/post/1297705/is-there-public-api-endpoints-available-h0661/comments/1078937/*/server.registerTool("get_leetcode_problem", {description: "리트코드 문제 slug로 문제 제목과 난이도를 조회합니다.",inputSchema: {problemSlug: z.string().describe("리트코드 문제 slug (예: two-sum)"),}}, async ({ problemSlug }) => {const query = `query getQuestionDetail($titleSlug: String!) {question(titleSlug: $titleSlug) {titledifficulty}`;const res = await fetch("https://leetcode.com/graphql", {method: "POST",headers: {"Content-Type": "application/json","Referer": "https://leetcode.com",},body: JSON.stringify({query,variables: { titleSlug: problemSlug },}),});const data = await res.json();const question = data.data.question;return {content: [{type: "text" as const,text: JSON.stringify({title: question.title,difficulty: question.difficulty,})}]}});
프로그래머스
프로그래머스는 공개 api가 없다. HTML 파싱도 시도해봤지만 문제 링크만으로는 난이도 정보를 가져오기 어려웠다. 그래서 문제 파일 생성 전에 사용자에게 난이도를 직접 물어보는 방식으로 처리했다.
CLAUDE.md 작성하기
MCP 서버를 준비했으니 이 MCP 서버를 Claude Code가 어떻게 사용해야 할지를 CLAUDE.md에 정의해줘야 한다.
이 과정에서 문제 풀이 플로우를 정리해야 했는데, 핵심은 문제 준비 플로우와 풀이 후 플로우를 분리한 것이다. 처음에는 하나로 이어진 플로우로 설계했는데, 이렇게 하니 파일을 생성하자마자 커밋까지 진행된다는 것을 깨달았다. 당연하지만 내가 문제를 풀어야 커밋이 되어야 했다.
- 문제 준비 플로우 (내가 문제 링크를 제공했을때)
- 브랜치 확인과 생성
- 문제 정보 수집 (MCP tool 호출)
- 파일 생성 + README 업데이트
- 풀이 후 플로우 (풀이 완료 시)
- 커밋
- 코드 피드백
- (요청 시) PR 생성
- (요청 시) PR 머지
코드 피드백은 이전에 PR-Agent에서 쓰던 프롬프트를 참고해서 두 가지 관점으로 정의했다.
- 리뷰 : 시간/공간 복잡도 분석, 대안 자료구조/패턴 제안
- 개선 : 가독성, 네이밍, 리팩토링 스니펫 제공
써봅시다
오늘 첫 문제 + 풀었던 문제를 푸는 경우
브랜치 생성 + 문제 파일 업데이트 (오늘 날짜 주석 추가)

풀이 완료 후 커밋과 피드백

새로운 문제를 푸는 경우 + 가이드
문제 파일 생성 + README 업데이트

풀이 가이드 요청

풀이 완료 후 커밋과 피드백

PR 생성과 머지
PR 생성 요청

PR이 생겼다.

rebase 머지와 main 브랜치 업데이트

끝
Github Actions로 자동화했을 때도 꽤 만족스러웠는데, 이번엔 문제 파일 생성부터 커밋, 코드 피드백까지 한 흐름으로 이뤄지게 되어서 엄청 만족스럽다. 게다가 CLAUDE.md를 잘 정리해서인지 (처음 써봐서 잘 모른다) 다음에 내가 하려는 것을 자동완성으로 만들어줘서 공부 경험 (...?) 이 되게 좋았다.
뭔가 공부하기 싫어서 방정리하는 심리인가? 왠지 알고리즘 스터디 레포지토리에 이런저런 생산성 향상 시도를 계속 하고 있는데 재밌다. 환경이 마음에 들어야 오래 할 수 있다고 믿기 때문에, 이 핑계로 알고리즘 공부를 꾸준히 이어갈 수 있으면 좋겠다.