2024.11.10 - [Android/AI] - [Cursor] Cursor AI IDE를 사용한 Android 개발 - rules 사용.
이전 글에서는, rules 설정을 활용하여 보다 더 퀄리티가 높은 코드를 구현하는데 도움을 받았다.
rules 파일을 작성하면 코드의 퀄리티는 확실히 높아진다는 것은 확인하였는데, 우리가 실무에서 cursor를 사용하면서 보다 효율적인 개발을 하기 위해서는 이것만 사용하기에는 상당히 부족하다는 것을 느낀다.
이왕 "AI"라는 것을 사용한다면, 보다 효율적으로 지금 내가 개발하는, 혹은 현재 팀이 개발하고 있는 개발 스타일에 맞춰서 알아서 코드를 구현해 준다면 우리가 후처리 작업으로 변경해야 하는 코드가 줄어들 것이 아닌가?
필자는 이런 생각을 하고, 별도의 docs 패키지에 md파일을 만들어 두고 사용하여 위와 같은 생각을 cursor에서 직접적으로 구현하여 사용하였다.
이번 글에서는 md 파일을 어떠한 스타일로 작성해야하는지에 대해 간단하게 알아보고자 한다.
물론,
각자의 스타일에 맞춰서 이 형태는 변경하여 사용해야 할 것이니 참고하는 정도로만 봐주면 좋을 것 같다.
필자는 간단하게 항상 사용하는
ComposeSample Repo에서 md 파일을 추가하였으니, md 파일의 전체가 궁금하면 해당 프로젝트를 확인하면 된다.
우선,
md 파일을 사용하고, 사용하지 않았을 때의 간단한 차이를 보고 넘어가자.

Cursor IDE가 업데이트 되고, 사용하는 모델들이 지속적으로 업데이트가 되고 있기 때문에 이것만으로도 상당히 만족스러운 결과를 보여준다.
컨벤션이 없는 상태에서 개발을 Cursor IDE를 사용해 진행했다면, 아무런 조건이 없이 커서가 만들어주는 코드 기반으로 구현하면 동일한 컨벤션으로 개발이 가능하겠지만 실무에서는 그렇지 않다.
별도의 정해둔 코드 스타일과 컨벤션이 존재하기 때문에 이를 맞춰줄 필요가 있다.
다음은 md 파일을 사용했을 때의 결과를 확인해 보자.

안타깝게도, md파일을 자동으로 삽입해 주는 방법은 없기 때문에 필요할 때마다 이렇게 직접 태깅하여 작업하거나, 해당 파일에 들어가서 chat을 생성해서 작업을 해야 한다.
하지만, 작성한 가이드라인이 어떻든 기존에 아무런 파일을 태깅하지 않았을 때와 다른 결과물을 보여주고 있음을 알 수 있다.
이와 같이 특정한 코딩 스타일, 컨벤션을 지켜 개발하는 것이 효율적인 협업하는 환경에서는 귀찮지만 하나의 파일을 만들어둔 상태에서 작업을 진행하는 것이 보다 효율성을 높여주고, 후처리에 들어가는 시간을 줄일 수 있을 것이다.
그렇다면 어떻게 만들어야 할까?
첫 번째 방식은,
커서의 힘을 빌려서 간단하게 딸깍으로 만드는 방식이다.
필자는 Compose 샘플 프로젝트에 존재하는 DI에 대한 조건을 추가해 보았다.

알아서 뭔가 만들어 준다.
그런데 이렇게만 하면, 단순한 작성된 코드들의 설명들만 추가되는 것 같으니 원하는 것을 더 추가하기 위해 한번 더 질문한다.

커서를 사용하면서 가장 짜증 나는 케이스이다.
중간 코드가 작성되는 경우 이와 같이 하나의 페이지로 만들어지지 않아 복사하기 힘들게 만들어둔다.
이렇게 만들어 준 것들을 읽어보면, 얼추 스타일이 맞기는 하지만 실무에 사용하는 스타일로 뽑아주는 md파일을 만들기는 어려울 것 같다.
그럼 두 번째,
ai를 사용하기 위해서 ai에게 파일을 만들어 달라고 요청하는 방식은 아직 사용하기 힘든 것 같으니 당연하지만 직접 작성한다.
직접 작성할 때 중요한 부분은,
이런 기본적인 것까지 작성해야 돼?
라는 생각이 들 정도로 기본적인 것들을 작성해주어야 한다는 것이다.
그렇게 하기 위해서는, 확실히 해당 콘텐츠가 어떤 순서로 설정이 되어야 하는지 명확하게 알고 있어야 한다.
모듈에 케이스를 추가하여 Koin DI를 적용한다고 가정해 보자.
그러면 순서는 대충 이와 같이 흘러간다.
- 현재 사용하고 있는 DI 라이브러리가 무엇인지 확인한다.
- 기존 모듈에 아이템을 추가하고자 한다면, 각 모듈에 맞춰 어떤 컨벤션이 있는지 확인한다.
- 새로운 모듈이 추가된다면,현재 사용하고 있는 모듈 리스트에 아이템을 추가한다.
- DI 라이브러리에 맞춰 모듈 초기화 작업을 추가한다.
- DI 라이브러리에 맞춘 방식을 사용하여 DI 작업을 수행한다.
이와 같은 순서에 맞출 필요는 없지만, 이에 해당하는 내용을 작성해 주면 된다.
필자는 위에 언급한 Compose 샘플 프로젝트 기반으로 설명하도록 하겠다.
- 나는 Kotlin을 사용하고, Koin을 사용하여 DI를 작업한다.
- InjectModules 파일 내부의 선언된 변수를 사용하여 module 리스트를 초기화한다.
- 각 모듈은 ~ 조건을 갖고 있으며, ~ 방식으로 선언하여 사용한다.
- 각 모듈에서 예제를 추가할 때는 ~ 조건을 만족해야 한다.
- 오류는 ~ 방식으로 처리한다.
- 각 방식에 대한 예제.
여기서 중요한 부분은, 각 방식에 대한 예제라고 볼 수 있다.
1~5번의 조건에 맞춰서 코드를 구현하고, 보여주는데 이런 텍스트만 사용해서는 확실히 어떤 스타일로 작성해야 하는지 알 수 없게 된다.
따라서 사용하는 부분에 따른 간단한 예제를 추가해 주면 보다 완성도 높은 템플릿을 만들 수 있을 것이다.
위의 내용을 작성해 보면 다음과 같다.
### Framework Configuration
```
DI_FRAMEWORK: Koin
SYNTAX: Koin DSL
```
...
### 1. Entry Point Module (`InjectModules.kt`)
```kotlin
// PATTERN: Module aggregation
val KoinModules = listOf(
apiModule,
viewModelModule,
networkModule,
ktorModule
)
```
...
### 3. ViewModel Module (`ViewModelModule.kt`)
```kotlin
// PATTERN: ViewModel dependency provision
RULES:
- One ViewModel per declaration
- Named qualifiers for API dependencies
- SavedStateHandle via parametersOf
// IMPLEMENTATION
viewModel {
SampleViewModel(
api = get(named("ApiName")),
state = get()
)
}
...
### 3. Best Practices
```
REQUIRED_PRACTICES:
1. API Dependencies:
- MUST use named qualifiers
- MUST specify base URL
- MUST include error handling
```
...
## Usage Examples
### 1. API Dependency
```kotlin
// PATTERN: ViewModel with API dependency
class SampleViewModel(
@Named("apiName") private val api: ApiInterface
) : ViewModel()
```
...
작성된 md파일의 일부이다.
어떠한 형식으로 작성되어야 하는지 얼추 알 수 있을 것이다.
"선언 당 하나의 viewModel"
"base URL 지정 필수"
이런 것처럼 아주 기본적인 세팅에 대해서도 작성을 해주어야 한다.
cursor를 사용하면, 알아서 추론하여 작성하기 때문에 코드의 내용을 보고 생략이 가능하다고 판단하면 컨벤션이 다르다고 하더라도 효율적으로 이를 제거하고 작성하는 경우가 생각보다 많기 때문에 이렇게 작성해주어야 한다.
디테일하게 작성하면 작성할수록, 우리가 생각하고 필요로 하는 그런 형태로 코드를 짜주게 되는데, 이게 생각보다 많은 시간이 걸린다.
그래서 필자는
우선 간단하게 cursor에 해당 md파일을 만들어 달라고 하는 대신, 다양한 조건을 넣어서 작성해 달라고 한다.
그 다움, md파일을 한번 읽고 수정한 다음 사용하다가 불필요한 부분이 있거나 추가 및 수정 되어야 하는 부분이 있다고 판단이 되면 md파일을 계속해서 수정해 가면서 고도화시켰다.
물론, 해당 프로젝트에 들어가 있는 파일은 그렇게 고도화되지 않았고 많은 부분을 cursor에게 작성해 달라고 요청한 부분이기는 하지만, 이 정도로만 나와도 충분히 원하는 결과를 볼 수 있다.
작성한 md파일을 기준으로 어떻게 다른 파일을 만드는지 확인해 보자.

아무런 작업을 하지 않은 상태로 만들어보라고 하면, 이와 같이 이상한 헛소리를 한다.
그렇다면 아까 만들어둔 md파일을 참고해 보자.

한눈에 보기에도 확실히 다른 결과물을 나타낸다.
샘플 코드와 디테일한 조건을 넣어준 md파일 하나를 추가함으로써, cursor에 질문할 때 사용할 많은 프롬프트들을 줄이면서도 원하는 결과 값을 보다 빠른 시간 내에 얻을 수 있는 것이다.
여기까지 글을 읽으면,
우선 생각보다 많은 내용을 작성해야 한다는 것에 생각보다 쉽지 않다.라고 생각할 수 있다.
하지만, 한번 작업을 해두고 나면 정말 효율 적으로 사용할 수 있다는 것이 감탄하게 될 것이다.
그런데 말이다.
실제로 구현을 하다 보면 이렇게 조건을 넣어주지 못하는 경우도 있다.
아주 간단하게 생각해 보면, 별도의 UI를 만들 때는 모든 케이스에 대해 이렇게 조건을 추가하기가 쉽지 않다.
필자는 이런 UI를 그릴 때,
기본적으로 큰 틀을 잡을 때도 md파일을 사용하여 작업할 때가 많은데 다음과 같은 방식 선언해서 사용하였다.
# UI 코드 생성 Snippet
생성형 AI를 사용할때 하기의 가이드 라인을 따라 프롬프트를 작성합니다.
## 기본 정보
- **작업 유형**: UI 생성/구현/생성
## 기술 세부사항
- **언어**: Kotlin
- **프레임워크**: Jetpack Compose
- **아키텍처**: MVVM + 클린 아키텍처
- **주석 작성 시**: 한국어로 작성
- **파일의 모든 부분을**: 생략하지 않고 작성
- **제거해야 할 부분이 있다면**: 제거하지 말고 설명 작성
- **Color 및 Style 사용 예시**:
```kotlin
Text(
modifier = Modifier,
text = title,
color = Color.LightGray,
style = getTextStyle(12)
)
```
- **ViewModel 사용 시**: @DIRules.md 파일 참조
- **ViewModel 사용 시**: Koin을 DI로 사용
## UI 분석
1. [UI 분석 1]
2. [UI 분석 2]
3. [UI 분석 3]
## 요구사항
1. [요구사항 1]
2. [요구사항 2]
3. [요구사항 3]
## 기대되는 동작
- [기대되는 동작 설명]
- [주요 기능 설명]
## 참고자료
- **프로젝트 규칙**: [관련 규칙 파일 경로]
- **관련 파일**: [관련 파일 경로]
## 원하는 결과
- 이미지와 일치하는 UI 구현
- 더미 데이터를 사용하여 빌드 유지
- 질문의 전체 맥락을 표현
- 읽은 내용을 질문의 맥락에 기반하여 전체 결과로 표현
- 길이가 너무 길어 표현이 어려운 경우, 다음 질문으로 이어서 진행
그냥 프롬프트를 작성하는 방식을 작성한 것이 아닌가?라고 생각할 수 있다.
프롬프트를 이런 형태로 작성하면 물론 원하는 결과가 나올 것이지만, 우리는 매번 작성할 때마다 이렇게 프롬프트를 길게 작성할 시간이 없다.
그렇기 때문에 이와 같이 간단하게 프롬프트를 추가하면, 알아서 해당 md파일에 해당하게끔 내용을 넣어서 알아서 처리해 준다.

하지만 이렇게만 처리하면, 실무에서는 생각보다 사용할 일이 많이 없다.
그래서 필자는 아까 작성한 DIRules 파일을 포함한 3가지 md파일을 더 추가하였는데, 결과부터 확인해 보자.

위의 프롬프트에서, 간단하게 4줄을 더 추가하였다.
API를 추가하여 연동하여 UI와 바인딩해주고, DI도 알아서 추가해 줘.라는 내용이다.



디테일하게 작성하지 않은 내용에 대하여 알아서 추가해 주었기 때문에 후처리 작업이 필요하지만,
아주 간단하게 만들어둔 md파일을 활용하여 프롬프트 한 번으로 API와 UI 생성 및 바인딩까지 끝내버린다.
그렇다고 위에 추가한 프롬프트에 많은 내용이 작성되어 있는가? 하면 별 내용도 없다.
글이 길어지기 때문에 해당 내용은 직접 프로젝트에 들어가서 확인해 보면 되겠지만, 주로 이런 내용이 작성되어있다.
## 요구사항
1. **API 호출 함수**
- API 문서와 일치하는 API 호출 함수를 작성합니다.
2. **가이드라인**
- `Create API and UI Binding Guide`에 명시된 규칙을 따릅니다.
...
## 요구사항
1. **DTO 생성**
- API 문서와 일치하는 DTO를 생성
2. **API 인터페이스**
- 이미 선언된 API 인터페이스 내에 API 호출 함수 추가
- 적절한 API 인터페이스가 없는 경우에만 새로 생성
...
그냥 API 문서가 있으면, 그걸 첨부해 주면 알아서 읽고 그것과 일치하는 DTO를 만들어주고 인터페이스를 선언해서 만들어달라는 내용이다.
이처럼 별로 길지 않은 md파일이지만, 많이 사용하는 케이스를 작성해서 가이드를 해놓는다면 아주 간단하게 md파일을 사용하여 작업을 처리할 수 있게 되는 것이다.
글을 작성하다 보니,
현재 필자가 작성해 둔 md 파일의 내용을 토대로 설명을 하게 되었는데
중요한 부분은 다음과 같다.
상황에 맞춰서 필요한 내용을 "미리 작성해 두고" 그것을 태그 하여 사용한다.
라는 것이다.
결국 cursor IDE를 통해서 코드를 작성하고, 작업을 하게 되면 어쨌든 프롬프트를 써서 원하는 결과를 도출해내야 한다.
그 과정에서 작업을 좀 해보다 보면 불필요하게 같은 말을 반복하거나, 이전에 작성한 내용을 복사하여 붙여 넣고 일부만 수정해서 작성하고는 한다.
이러한 내용들을 미리 작성해 두어, 이를 태그 해서 사용하게 되면 간단하게 많은 프롬프트를 줄일 수 있고,
질문하는 횟수도 줄어들어 확실히 효율적인 사용이 가능해진다고 생각한다.
무엇보다, 직접 md파일을 작성하면서 각 md파일 간의 연계를 생각할 수 있다면,
각각의 프롬프트를 통해 결과를 도출해 내야 하는 것들을 한 번의 질문으로 해결할 수 있을 것이다.
물론,
필자도 cursor를 사용하면서 불편했던 부분에 대해 어떻게 개선해야 할까?라는 생각에 의해서 작성했던 내용이기 때문에
어디서 문제가 발생할 수 있고, 더 효율적인 방법이 있을 수 있다.
하지만,
이 정도로만 작성을 해도 충분히 효율적으로 사용할 수 있기 때문에 이러한 방법이 있구나.
라는 정도로만 보고 사용했으면 한다.
해당 게시글에 사용한 코드는 Github에 올려두었다.
https://github.com/HeeGyeong/ComposeSample
GitHub - HeeGyeong/ComposeSample: This project provides various examples needed to actually use Jetpack Compose.
This project provides various examples needed to actually use Jetpack Compose. - HeeGyeong/ComposeSample
github.com