소프트웨어 개발 과정에서 코드의 품질을 객관적으로 측정하는 것은 매우 중요하다.
특히 코드 재사용성은 유지보수성과 효율성을 결정하는 핵심 지표다.
처음에는 코드 재사용성을 직접 측정해 보려 했지만, 예상대로 쉽지 않았다
이 과정에서 "AI는 어떻게 코드의 라인 수와 중복 코드를 파악할까?"라는 궁금증이 생겼고, 이에 대해 탐구해 보았다.
1. 라인 수 측정 과정
1.1 초기 접근 방식 (부정확했던 방법)
- 추정 기반: "Constants 약 50라인" 등 대략적 추정
- 결과: 실제와 큰 차이 (Constants는 실제 93라인)
1.2 정확한 측정 방법 요청
// REPL(analysis tool)을 사용한 실제 라인 수 계산
const commonCode = `업로드된 실제 코드 전문`;
console.log("라인 수:", commonCode.split('\n').length);
// 결과: Constants.kt = 93라인, 전체 common = 316라인 등
2. 중복 코드 식별 과정
2.1 AI의 텍스트 패턴 분석 방식
(처음에는 육안으로 검사했다고 해서 당황했다..)
A. 텍스트 패턴 매칭
1. 업로드된 코드 텍스트를 메모리에 로드
2. 함수명, 클래스명으로 동일한 구조 찾기
3. 코드 블록을 텍스트로 비교 분석
4. 문자열 유사도 계산 (하지만 정확하지는 않음)
B. 코드 구조 인식
// CustomerClient에서 이런 패턴을 찾음
private suspend fun sendRequest(command: String, data: JsonElement = JsonNull) {
try {
val request = Request(command, data)
val json = JsonHelper.json.encodeToString(request)
// ...
}
}
// ADClient에서 동일한 패턴 발견
private suspend fun sendRequest(command: String, data: JsonElement = JsonNull) {
// 정확히 같은 내용
}
2.2 완전 중복 식별 기준
- 문자열 100% 일치: 공백, 주석까지 동일한 경우
- 발견된 완전 중복:
sendRequest()메서드: 15라인parseJsonResponse()메서드: 8라인
2.3 구조적 유사성 분석 방법
✅ 실제 사용한 객관적 방법:
- 명확한 수학 공식: 유사도 = (공통 요소 수) / (전체 요소 수) × 100
- 예시: TCP 연결 = (Socket+Writer+Reader) / (Socket+Writer+Reader+UDP) = 3/4 = 75%
⚠️ 주관적 판단이 필요한 부분:
- 요소 분류: "Socket 생성"을 하나의 단위로 볼 것인가?
- 범위 정의: "TCP 연결 설정"의 경계를 어디까지로 할 것인가?
3. AI 분석의 실제 과정
3.1 텍스트 처리 단계
1. 업로드된 파일 내용을 텍스트로 읽기
2. 코드 블록을 구문별로 분할
3. 함수/클래스 단위로 패턴 인식
4. 유사한 구조의 코드 블록 매칭
5. 문자열 비교를 통한 유사도 추정
3.2 라인 수 계산의 정확성
// 이 부분은 정확함 - 실제 REPL에서 계산
const totalClientLines = 454 + 363; // = 817
const duplicatedLines = 23 * 2; // = 46
const duplicationRate = 46 / 817; // = 5.6% (완전 중복만)
3.3 구조적 유사도 분석의 정확성
✅ 객관적인 수학적 계산:
- 공식: 유사도 = (공통 요소 수) / (전체 요소 수) × 100
- TCP 연결: 3개 공통 ÷ 4개 전체 = 75%
- 메시지 리스너: 4개 공통 ÷ 6개 전체 = 67%
- cleanup: 3개 공통 ÷ 4개 전체 = 75%
⚠️ 분석자 판단이 필요한 부분:
- 요소를 어떻게 나누고 분류할지
- 코드 블록의 범위를 어디까지 볼지
4. 분석 방법의 한계와 개선점
4.1 AI 분석의 한계와 장점
장점: 객관적 측정 부분
- 라인 수 계산: REPL로 정확한 수치 측정
- 완전 중복 식별: 문자열 100% 일치 여부는 명확
- 수학적 유사도: 명확한 공식 기반 계산
유사도 = (공통 요소 수) / (전체 요소 수) × 100 TCP 연결: 3÷4 = 75% 메시지 리스너: 4÷6 = 67%
한계: 판단이 필요한 부분
- 요소 분류 기준: "Socket 생성"을 하나로 볼지, 세부 단계로 나눌지
- 범위 정의: 어디서부터 어디까지를 하나의 코드 블록으로 볼지
- 의미적 분석: 코드가 실제로 같은 기능을 하는지 정확히 파악하기 어려움
4.2 더 정확한 분석 방법들
A. 정적 분석 도구
# SonarQube - 업계 표준 코드 품질 분석
sonar-scanner \
-Dsonar.projectKey=shopping-system \
-Dsonar.sources=src \
-Dsonar.language=kotlin
# 결과: 정확한 중복률, 복잡도, 유지보수성 지수
B. 코드 클론 감지 도구
# CPD (Copy/Paste Detector)
cpd --minimum-tokens 50 --files src/ --language kotlin
# 결과: 정확한 중복 코드 블록과 위치
C. AST 기반 구조 분석
# 구조적 유사성을 정확히 측정
import ast
def structural_similarity(code1, code2):
tree1 = ast.parse(code1)
tree2 = ast.parse(code2)
# 변수명 무시하고 구조만 비교
return compare_ast_structure(tree1, tree2)
5. 실제 분석 프로세스 정리
5.1 정확했던 부분
✅ 라인 수 계산
- REPL에서 split('\n').length로 정확히 측정
- Constants.kt: 93라인
- CustomerClient.kt: 454라인
- ADClient.kt: 363라인
✅ 완전 동일 코드 식별
- sendRequest(), parseJsonResponse()는 문자열 100% 일치
- 이 부분은 명확한 중복
5.2 수학적 계산 기반 분석
✅ 객관적 수치 계산
- 유사도 공식: (공통 요소) / (전체 요소) × 100
- TCP 연결 설정: 3 ÷ 4 = 75%
- 메시지 리스너: 4 ÷ 6 = 67%
- cleanup 메서드: 3 ÷ 4 = 75%
- 사용자 입력 루프: 5 ÷ 6 = 83%
- 명령어 처리 팩토리: 3 ÷ 5 = 60%
⚠️ 분석자 판단 의존 부분
- 요소 분류: "Socket 생성"을 어떻게 세분화할지
- 범위 설정: 코드 블록의 경계를 어디로 정할지
- 가중치 적용: 각 요소의 중요도를 어떻게 평가할지
6. 권장하는 정확한 분석 방법
6.1 자동화된 측정 도구 사용
// build.gradle.kts에 추가
plugins {
id("io.gitlab.arturbosch.detekt") version "1.22.0"
}
detekt {
config = files("detekt-config.yml")
reports {
html.required.set(true)
xml.required.set(false)
txt.required.set(false)
}
}
6.2 지속적인 품질 측정
# .github/workflows/code-quality.yml
- name: Run Detekt
run: ./gradlew detekt
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
6.3 팀 기반 코드 리뷰
1. Pull Request마다 코드 품질 자동 체크
2. 동료 개발자의 수동 리뷰
3. 아키텍처 리뷰를 통한 구조적 개선
7. 결론: AI 분석의 한계 인정
7.1 AI가 객관적으로 측정할 수 있는 것
- ✅ 정확한 라인 수 계산: REPL 도구 활용
- ✅ 완전 중복 식별: 문자열 100% 일치 판정
- ✅ 수학적 유사도 계산: 명확한 공식 기반 수치
- ✅ 코드 구조 패턴 인식: 함수명, 클래스명 등 구조 분석
- ✅ 개선 방향 제안: 패턴 기반 리팩토링 아이디어
7.2 분석자 판단이 필요한 부분
- ⚠️ 요소 분류 기준: 코드를 어떤 단위로 나눌지
- ⚠️ 범위 정의: 분석 대상의 경계 설정
- ⚠️ 가중치 결정: 각 요소의 중요도 평가
- ⚠️ 의미적 해석: 코드의 실제 기능과 목적 이해
- ⚠️ 개선 효과 예측: 리팩토링 후 실제 효과 추정
7.3 분석 결과의 신뢰도 평가
높은 신뢰도 (90%+ 정확)
- 라인 수: REPL로 실제 측정 → 100% 정확
- 완전 중복: 문자열 일치 여부 → 100% 정확
- 수학적 계산: 공식 기반 유사도 → 계산 자체는 100% 정확
중간 신뢰도 (70-90% 정확)
- 구조적 유사성: 수학적 계산이지만 요소 분류에 판단 필요
- 패턴 인식: 명확한 구조는 잘 인식하지만 미묘한 차이는 놓칠 수 있음
낮은 신뢰도 (50-70% 정확)
- 개선 효과 예측: 경험 기반 추정, 실제 측정 필요
- 복잡한 의미 분석: 코드의 실제 동작과 목적 해석
7.4 최종 권장사항
AI 분석을 활용하는 올바른 방법:
- 1차 스크리닝으로 활용: 대략적인 현황 파악
- 수학적 부분은 신뢰: 라인 수, 완전 중복, 유사도 계산
- 전문 도구로 검증: SonarQube, Detekt로 정밀 분석
- 팀 리뷰로 보완: 동료와 함께 의미적 분석
- 실제 측정으로 확인: 리팩토링 전후 비교
결론: 수학적 계산은 신뢰할 수 있지만, 요소 분류와 해석에는 인간의 판단이 필요합니다.
단순 육안으로 검사하는 것이 아닌, 이런 접근방법에 대해 배울 수 있었다.
참고로 IntelliJ에서도 중복 코드를 경고로 알려주고 있고, 무료 정적 코드 분석 도구 (SonarQube 등)이 많다.
다음에는 AI가 아닌 이런 정적 코드 분석기로 분석해 보고싶다.
'Uncompiled Thoughts' 카테고리의 다른 글
| 블로그 스킨 변경 - hELLO (4) | 2025.08.18 |
|---|---|
| 벨로그에서 티스토리로, 블로그를 옮긴 이유 (0) | 2025.03.13 |