
캡스톤 프로젝트를 진행하면서, 개발자들의 개발 과정을 쉽게 기록할 수 있게해주는 서비스를 기획하고 한 학기 동안 개발했다.
그 중 정말 어렵고 힘들었던 IntelliJ Plugin을 하면서 했던 요구사항 구현과 트러블 슈팅을 기록해보고자 한다.
우리 서비스는 개발자들의 개발 흐름을 벗어나지 않으면서, 사용자가 저장하고 싶은 코드&트러블 슈팅 기록을 개발중에 저장하여 웹서비스에서 해당 데이터를 체계적으로 정리하고, AI등을 활용하여 포트폴리오나 PR작성등 개발자들에게 도움을 주기위해 만들었다.
이 과정에서 개발자들이 자신의 소스코드를 직접 선택하고 저장하기 위해서는 IDE와 관련된 플러그인을 개발해야했다.
우리의 요구사항은 사용자의 "개발 과정"을 기록하기였고, 과정을 기록하기 위해서는 어떤 내용들을 기록 해야할까 고민했다.
"과정 자체"를 기록하기 위해 개발하면서 실제로 구현한 코드 + 지웠지만 이유를 남겨놓고 싶은 코드가 있을 수 있다고 생각했고, 우리는 사용자가 기록하고 싶은 코드를 하나의 코드 스니펫으로 설정해놓으면 그것을 계속해서 실시간으로 트래킹하고, 지워지면 지워지기 전의 소스코드를 남겨두는 것으로 기획했다.
그리고 모든 데이터들은 commit 시점에 서버로 보내기로 결정했다.
사용자가 소스 코드를 드래그한 후 기록과 함께 저장하면 커밋 이벤트가 발생할 때 까지 실시간으로 관리되도록 구현했다.
이 기능을 구현 하기 위해 난 IntelliJ Plugin Tool의 RangeMarker와 Document Listener를 이용했다.
reference : https://plugins.jetbrains.com/docs/intellij/documents.html#how-do-i-get-notified-when-documents-change

RangeMarker는 IntelliJ 자체에서 소스 코드를 저장하기 위한 기능이고 이벤트기반 리스너와 같이 사용한다. 그림에서 보다시피 코드 스니펫의 해당 파일 기준 start_offset과 end_offset을 넣어 객체 형태로 관리한다.

우리는 사용자가 드래그 한 코드 스니펫을 RangeMarker 형태로 저장하고 UUID를 만들어 그 스니펫을 식별했다, 그리고 RangeMarker는 소스코드가 있는 파일정보를 Document 필드로 넣어주어 한 파일에 RangeMarker가 종속되게 만들었다.
이렇게 객체형태로

이런식으로 하나의 소스코드 파일에 사용자가 트래킹하고 있는 코드 스니펫이 한 소스코드 파일 (예: .java .py... 등)에 종속되게 하는것이다.
아래와같이 사용자가 저장하고 싶은 소스코드를 드래그하고, 우클릭후 저장하기를 누른 후 코드 스니펫의 제목, 내가 기록할 내용, 카테코리 등을 설정해 관리 설정 가능하다.

저장하고 나면 아래처럼 하이라이팅이 되게 만들었고, 이 소스코드는 트래킹 되고있다는 것을 사용자가 알게 만들었다 그리고 모든 코드 스니펫은 Redis에 저장되게 했는데,

Redis에는 Hash 자료구조를 사용했고,
user:(user_Id) - key, snippet_Id - field , 객체의 내용(제목, 소스코드, 기록 내용 , 카테고리) - value 형태로 구성했다.
여기서 캐시를 사용한 이유는 사용자가 기록하는것을 바로바로 업데이트 해야했기 때문에 속도와 성능이 굉장히 중요하다고 생각했고, 캐시에 보관해놨다가 특정 이벤트가 발생했을때, DB로 저장하면 알맞다고 생각했다. 또한 개인화 서비스이므로 일관성 문제가 생길 일이 거의 없다고 생각했다.
실시간으로 트래킹 하기 위해서 플러그인의 EventListener인 Document Listener을 이용했다.
Document Listener는 프로그램이 실행할때, 모든 문서에 대해 Listener를 심어놓고 모든 문서에대해 자동으로 변화를 감지해준다, 그때 마다 난 함수를 만들어 구현했다.

onDocumentChange 함수를 보면 파라미터로 이 Project, 현재 띄워진 document를 받아 실행한다.
변화가 감지되면 이 함수가 실행되고 project에 해당하는 RangeMarker객체를 모두 돌면서, RangeMarker에 대한 유효성 검사와 변화하는 새로운 StartOffset, EndOffset을 계산하여 새로운 스니펫을 만들어 update 요청을 서버에 전송하는 것으로 구현했다.
그리고 플러그인은 기본적으로 Action 기반으로 동작해 모든 저장, ui띄우기, 등등 직접 구현해야했고, 로그인했을때 기존 스니펫을 가져오는 함수, 스니펫으로 설정했을때 색칠하는 함수등,, 많은 함수가 있지만 주요한 기능이 아니므로 넘어가도록 하겠다.
지금까지는 사용자가 스니펫을 설정한 부분을 어떻게 저장하고, 트래킹하는지 알아봤다 하지만 우리는 개발 과정 자체를 저장해야하므로, 지워졌던 흔적들도 모두 저장해야한다. 그렇게 하기위해 정말 고민이 많았다.
다음 포스팅에서는 그것에 대한 이야기를 풀어보겠다.
끗 - 🎉