gradle

5 개의 포스트

line5분 읽기큐레이션 요약

AI 에이전트를 위한 Android CLI: 대규모 모바일 개발 환경에 적용하기

LINE Android처럼 수백 개의 Gradle 모듈과 대규모 코드를 가진 저장소에서는 단순한 `grep`·`glob` 검색만으로 AI 에이전트가 의미론적 질문에 답하기 어렵고, 불필요한 결과와 재시도로 토큰 비용이 급증합니다. LINE 개발팀은 Android CLI를 문서 검색과 Android Studio 연동의 기반으로 활용하되, 고정된 바이너리·래퍼·스킬·프롬프트를 조합해 보안, 환경 일관성, 오류 처리, 토큰 효율성을 보완하고 있습니다. 핵심은 CLI를 에이전트에 그대로 노출하지 않고, 저장소 규모와 조직 환경에 맞게 통제된 인터페이스로 제공하는 것입니다. ## 대규모 Android 저장소에서 AI 에이전트가 겪는 문제 - 수백 개의 Gradle 모듈과 방대한 코드 때문에 검색 한 번에도 지나치게 많은 결과가 반환됩니다. - 검색 결과가 에이전트 컨텍스트에 포함되면서 토큰과 비용이 빠르게 증가합니다. - 텍스트 검색만으로는 다음과 같은 의미론적 질문에 정확히 답하기 어렵습니다. - 특정 심볼의 선언 위치 - 심볼의 실제 참조 위치 - IDE가 판단하는 사용되지 않는 코드나 경고 - 의존성 내부 심볼의 위치 - 관련 없는 결과를 바탕으로 에이전트가 반복 검색과 재시도를 수행하면서 비효율이 커집니다. - 따라서 Android CLI를 그대로 적용하기보다는 래퍼, 스킬, 프롬프트를 통한 보완이 필요합니다. ## 문서 검색을 Android CLI로 전환 - Android CLI는 빌드·배포, SDK 관리, 환경 진단, 공식 문서 검색, Android Studio 연동 등을 제공합니다. - 첫 적용 대상은 Android, Jetpack Compose, AndroidX, Firebase 등의 공식 문서 검색이었습니다. - 모델의 사전 학습 지식은 최신 API 변경을 반영하지 못할 수 있어 잘못된 시그니처를 생성하는 환각이 발생할 수 있습니다. - 최신 Android Knowledge Base를 직접 참조하면 문서의 권위와 최신성을 확보할 수 있습니다. - 기존에는 Google Cloud Knowledge MCP 서버를 사용했지만 다음 운영 부담이 있었습니다. - 개발자별 Google Cloud API 인증 설정 - 인증 프록시 운영 - API 할당량 제한 대응 로직 유지 - Android CLI의 `docs` 명령은 다음 두 단계로 사용됩니다. - `docs search`: 키워드로 공식 문서 검색 - `docs fetch`: 검색 결과의 KB URL로 문서 본문 조회 - 이 기능은 `get-android-dev-knowledge` 스킬로 에이전트에 제공됩니다. - 기존 MCP 방식과 비교해 인증, 프록시, 할당량 처리 인프라를 제거하고 더 적은 토큰으로 최신 문서를 참조할 수 있습니다. ## CLI 바이너리를 저장소에 번들링한 이유 LINE 팀은 전역 설치된 `android` 명령 대신 `.agents/tools/android-cli/android`처럼 저장소 내부의 고정 경로에 바이너리를 포함했습니다. ### 환경 파편화 방지 - 개발자마다 CLI 버전, 설치 위치, 운영체제가 달라지는 문제를 줄입니다. - 저장소를 클론하면 동일한 버전을 사용할 수 있습니다. - 개발자 장비뿐 아니라 CI와 AI 에이전트 실행 호스트에서도 동일한 환경을 보장합니다. ### 보안 정책과 래퍼 적용 - Android CLI는 호출 과정에서 일부 데이터를 수집할 수 있습니다. - 이를 막으려면 호출마다 `--no-metrics` 인자를 지정해야 합니다. - 에이전트가 해당 인자를 누락할 수 있으므로 모든 호출을 통과시키는 래퍼에서 강제하는 방식이 안전합니다. - 래퍼가 실제 바이너리를 안정적으로 찾으려면 바이너리 위치가 고정되어 있어야 하므로 저장소 번들링이 유리합니다. - 대규모 저장소에 이미 `git-lfs`가 적용되어 있어 바이너리 포함 비용도 감당할 수 있었습니다. ## `--no-metrics` 관련 버그와 오류 출력 개선 - Android CLI 1.0 도입 과정에서 정보 수집 기능이 `--no-metrics` 처리 전에 초기화되는 버그가 발견되었습니다. - 이 때문에 메트릭 수집을 비활성화해도 `~/.android/cli`에 쓰기를 시도할 수 있습니다. - 샌드박스나 파일 시스템 권한으로 쓰기가 차단되면 여러 페이지의 Java 스택 트레이스가 출력됩니다. - 장황한 스택 트레이스는 에이전트 컨텍스트를 불필요하게 차지하고 문제 해결도 어렵게 만듭니다. - 래퍼는 CLI 실행 전에 다음을 수행합니다. - `~/.android/cli` 디렉터리 생성 시도 - 임시 파일을 만들어 쓰기 권한 확인 - 쓰기가 막히면 한 줄짜리 파싱 가능한 오류 출력 - 에이전트는 이제 긴 예외 대신 “쓰기 권한을 부여한 뒤 재시도하라”는 원인과 조치를 직접 전달받습니다. - 반복적으로 발생하는 오류를 에이전트가 처리하기 쉬운 구조화된 메시지로 변환하는 방식은 이후 Android Studio 연동에도 적용됩니다. ## Android Studio 연동의 의미론적 기능 Android CLI 1.0에서는 실행 중인 Android Studio와 연결해 IDE 수준의 분석을 명령줄에서 수행할 수 있게 되었습니다. - `studio check` - Android Studio 실행 여부를 확인합니다. - 대상 프로젝트가 열려 있는지 확인합니다. - 프로젝트 인덱싱이 완료되었는지 확인합니다. - 다른 Studio 기능을 사용하기 위한 전제 조건입니다. - `studio analyze-file` - 빌드하지 않고 단일 파일에 IDE 인스펙션을 적용합니다. - 에러와 경고를 반환합니다. - `is never used`처럼 단순한 `grep`으로 파악하기 어려운 의미론적 문제도 찾을 수 있습니다. - `studio find-declaration` - 심볼의 선언 위치를 찾습니다. - 프로젝트 코드뿐 아니라 `.aar`, `.jar` 의존성 내부도 검색합니다. - `studio find-usages` - 특정 심볼이 사용된 위치를 찾습니다. - `studio render-compose-preview` - `@Preview`가 적용된 Jetpack Compose 화면을 PNG 이미지로 렌더링합니다. ## CLI를 래퍼와 스킬로 감싼 설계 - Android Studio 기능도 CLI를 에이전트에 직접 노출하지 않고 얇은 래퍼와 스킬을 통해 제공합니다. - 가장 먼저 `studio-check` 스킬을 만들었습니다. - 에이전트가 CLI의 복잡한 입출력과 환경 전제 조건을 직접 처리하지 않도록 하는 것이 목적입니다. - 래퍼는 권한 확인, 실행 환경 검증, 오류 메시지 정규화 같은 공통 처리를 담당합니다. - 스킬은 에이전트가 언제 어떤 기능을 사용해야 하는지 안내하는 고수준 인터페이스 역할을 합니다. 실무적으로는 대규모 저장소에서 Android CLI를 전역 도구로 배포하기보다, 버전을 고정한 바이너리를 저장소에 포함하고 모든 호출을 래퍼로 통제하는 방식을 추천할 수 있습니다. 특히 검색 결과를 무작정 늘리는 대신 문서 검색, 심볼 탐색, IDE 분석처럼 목적에 맞는 의미론적 명령을 스킬로 제공하면 토큰 낭비와 에이전트의 반복 작업을 크게 줄일 수 있습니다.

원문 읽기(새 탭에서 열림)
gitlab4분 읽기큐레이션 요약

GitLab 19.0 | GitLab 문서

GitLab 19.0은 그룹 단위 AI 코드 리뷰 지침, 사용자 정의 작업 항목, Secrets Manager 오픈 베타 등 협업·보안 기능을 확장한 릴리스입니다. 또한 GitLab Duo를 에이전트 중심으로 강화하고, SBOM 기반 의존성 스캔을 정식 제공하며, 사용량 기반 과금과 새로운 모델·트리거를 도입했습니다. 이번 릴리스는 여러 프로젝트를 아우르는 표준화와 에이전트 기반 개발 자동화에 초점을 둡니다. ## 그룹 단위 GitLab Duo 코드 리뷰 지침 - 기존에는 프로젝트별로만 `.gitlab/duo/mr-review-instructions.yaml`을 설정할 수 있었습니다. - 이제 그룹과 하위 그룹 전체에 공통 리뷰 지침을 적용할 수 있습니다. - 그룹 내 특정 프로젝트를 템플릿으로 지정하면, GitLab Duo가 그룹 지침과 개별 프로젝트 지침을 결합해 코드 리뷰를 수행합니다. - Code Review Flow와 GitLab Duo Code Review 모두 지원합니다. - Premium·Ultimate 등급에서 GitLab.com, Self-Managed, Dedicated 환경에 제공됩니다. ## 사용자 정의 작업 항목 유형 - 프로젝트에서 `Issue`, `Task` 외에 `User Story`, `Bug`, `Maintenance` 같은 작업 항목 유형을 직접 생성하거나 이름을 변경할 수 있습니다. - 각 유형은 고유한 이름과 아이콘을 가지며, 사용자 정의 필드와 상태 라이프사이클을 지원합니다. - 저장된 보기와 이슈 보드에서도 유형을 기준으로 작업을 관리할 수 있습니다. - 최상위 그룹 또는 조직에서 설정한 유형 구성이 하위 프로젝트로 전파됩니다. - 프로젝트별로 특정 유형을 활성화하거나 비활성화할 수 있으며, 유형을 비활성화해도 기존 작업 항목에는 영향을 주지 않습니다. ## GitLab Secrets Manager 오픈 베타 - 기존 폐쇄형 베타에서 Premium·Ultimate 고객 대상 오픈 베타로 확대되었습니다. - GitLab.com과 GitLab Self-Managed에서 사용할 수 있습니다. - 프로젝트 및 그룹 Owner가 CI/CD 시크릿을 GitLab에 저장·조회·참조할 수 있습니다. - 시크릿은 프로젝트 또는 그룹 범위로 제한되며, 명시적으로 요청한 파이프라인 작업만 접근할 수 있습니다. - 아직 베타 지원 정책이 적용되므로 운영 환경에 사용하기 전 안정성과 제한 사항을 검토해야 합니다. ## GitLab Duo Developer의 MR 자동화 - 이슈 할당, `Generate MR` 선택, 이슈·MR 토론에서의 `@mention` 등 여러 방식으로 Developer를 실행할 수 있습니다. - 피드백, To-do, 디자인 관련 질문을 코드 변경, 후속 MR, 조사 요약으로 전환할 수 있습니다. - `AGENTS.md`와 `agent-config.yml`을 설정하면 커밋 전에 테스트와 검사를 실행하도록 구성할 수 있습니다. - 최상위 그룹 또는 인스턴스 관리자가 Developer Flow를 활성화하면 대상 프로젝트에 멘션 및 할당 트리거가 자동으로 추가됩니다. ## SBOM 기반 의존성 스캔 정식 제공 - Maven, Gradle, Python 프로젝트에서 직접 선언한 의존성뿐 아니라 전이 의존성까지 분석합니다. - lockfile이나 해석된 의존성 그래프가 없으면 Maven·Gradle·Python 도구를 자동 실행해 전체 그래프를 생성합니다. - v2 Dependency Scanning 템플릿을 포함하는 것 외에 별도 설정이 거의 필요하지 않습니다. - 의존성 해석이 불가능한 경우 `pom.xml`, `requirements.txt`, `build.gradle`, `build.gradle.kts`를 분석하는 매니페스트 스캔으로 대체됩니다. - 매니페스트 스캔은 직접 의존성만 제공하므로, 전이 의존성까지 확인하려면 lockfile·그래프 파일 또는 의존성 해석 기능이 필요합니다. - Ultimate 등급 기능입니다. ## GitLab Duo Core의 사용량 기반 과금 - GitLab Duo Core가 19.0부터 사용량 기반 과금으로 변경됩니다. - Web IDE와 데스크톱 IDE의 Code Suggestions 사용량이 GitLab Credits를 소비합니다. - Duo Chat은 GitLab Duo Agent Platform 기반의 에이전트 방식으로 변경됩니다. - GitLab UI나 데스크톱 IDE에서 Chat을 사용하려면 인스턴스 또는 최상위 그룹에서 Agent Platform을 활성화해야 합니다. ## 코드 검색과 MR 자동화 트리거 - 정확한 코드 검색 결과를 `repo:` 구문으로 특정 저장소에 한정할 수 있습니다. - 예를 들어 `def authenticate repo:my-group/my-project`처럼 검색하면 지정한 저장소의 결과만 확인할 수 있습니다. - 부분 경로나 패턴을 사용해 여러 저장소를 한 번에 검색할 수도 있습니다. - Draft MR이 리뷰 준비 상태로 전환될 때 Flow나 외부 에이전트를 실행하는 `Merge request ready` 이벤트 트리거가 추가되었습니다. - 프로젝트의 `AI > Triggers`에서 설정하며, `merge_request_ready_flow_trigger` 기능 플래그 뒤에 있고 기본값은 비활성화입니다. ## GitLab Duo Agent Platform의 모델 확장 - Claude Opus 4.7을 Agent Platform에서 사용할 수 있습니다. - 복잡한 다단계 작업, 지시 준수, 결과 검증이 필요한 CI/CD, 코드 리뷰, 취약점 수정 플로우에 적합하도록 개선되었습니다. - GitLab Self-Managed용 Duo Agent Platform은 자체 호스팅 Gemini 모델도 지원하기 시작했습니다. - 제공된 글 내용은 Gemini 지원이 여러 플로우를 지원한다는 설명 중간에서 끝나므로, 세부 지원 범위는 공식 문서를 확인해야 합니다. ## 릴리스의 방향 - 여러 프로젝트에 공통 AI 리뷰 정책과 작업 유형을 적용해 그룹 단위 표준화를 강화했습니다. - 에이전트가 이슈와 MR에서 직접 코드를 수정하고 검증하는 개발 흐름을 확대했습니다. - SBOM 및 전이 의존성 분석으로 공급망 보안 가시성을 높였습니다. - Duo 사용량 기반 과금과 Secrets Manager 베타 도입에 따라 비용 및 운영 정책 검토가 중요해졌습니다. 도입 시에는 그룹 공통 리뷰 지침과 작업 유형을 먼저 표준화하고, SBOM 스캔에서 전이 의존성 해석이 실제로 활성화되었는지 확인하는 것이 좋습니다. Secrets Manager와 새로운 Agent Platform 기능은 베타·기능 플래그 상태와 과금 영향을 검증한 뒤 점진적으로 적용하는 것을 권장합니다.

원문 읽기(새 탭에서 열림)
netflix5분 읽기큐레이션 요약

Nebula ArchRules로 ArchUnit 확장하기

Netflix는 수만 개의 Java 저장소에서 공통 아키텍처 규칙과 기술 부채를 일관되게 점검하기 위해 ArchUnit을 여러 저장소에 배포·적용하는 Nebula ArchRules를 구축했다. ArchUnit은 AST가 아닌 JVM 바이트코드를 분석하므로 Java뿐 아니라 Kotlin·Scala에도 적용할 수 있고, 타입 안전한 Java API로 규칙을 작성·테스트할 수 있다. Nebula 플러그인은 이를 공유 가능한 Gradle 규칙 라이브러리로 확장해 조직 전체의 라이브러리 사용 규칙과 API 생명주기를 자동 검증한다. ## Netflix의 문제: 라이브러리 생명주기와 기술 부채 - Netflix는 수만 개의 Java polyrepo를 운영하므로 공통 빌드 로직과 품질 규칙을 여러 저장소에 배포해야 한다. - 하위 호환성을 깨는 라이브러리 변경 사고를 계기로, deprecated API를 언제 안전하게 제거할 수 있는지 파악할 필요가 생겼다. - API에는 다음과 같은 생명주기 애너테이션을 사용한다. - `@Deprecated`: 더 이상 사용하지 않도록 지정된 API - `@Public`: 하위 프로젝트가 사용하도록 공개된 API - `@Experimental`: 아직 안정성이 보장되지 않는 신규 API - 그 외 API: 기본적으로 내부 구현으로 간주 - 문제는 라이브러리 작성자가 어떤 downstream 프로젝트가 내부 API나 deprecated API를 잘못 사용하고 있는지 알기 어렵다는 점이다. - Spring Boot 주요 버전 업그레이드 같은 fleet-wide migration에서도 deprecated API 사용 현황을 파악하는 것이 중요하다. ## ArchUnit을 선택한 이유 - ArchUnit은 JUnit 테스트 안에서 아키텍처 규칙을 검증하는 오픈소스 라이브러리다. - ASM 기반으로 JVM 바이트코드를 분석하므로 특정 소스 언어의 문법에 덜 의존한다. - Java, Kotlin, Scala처럼 서로 다른 JVM 언어로 작성된 코드에도 같은 규칙을 적용할 수 있다. - 소스 코드의 syntactic sugar에 가려진 실제 실행 바이트코드까지 검사할 수 있다. - 규칙 작성 방식은 두 가지다. - 대부분의 규칙은 fluent builder API로 간결하게 작성한다. - 복잡한 분석에는 더 낮은 수준의 API와 클래스 관계 정보에 접근할 수 있다. - 분석 대상 전체 classpath를 읽고 클래스 간 의존성, 호출 관계, 소유자 등을 그래프로 유지한다. - 단순한 파일 단위 검사를 넘어 호출 사이트와 클래스 관계를 기준으로 규칙을 작성할 수 있다. ## AST 기반 도구와의 차이 - PMD 같은 AST 기반 도구는 소스 문법 구조를 직접 검사한다. - 언어별 문법 차이 때문에 Kotlin이나 Scala 지원 시 규칙을 별도로 작성해야 할 수 있다. - 예상하지 못한 문법적 표현으로 규칙을 우회할 가능성도 있다. - ArchUnit 규칙은 컴파일 결과인 바이트코드를 대상으로 하므로 코드가 어떤 JVM 언어로 작성됐는지보다 실제 동작 구조가 중요해진다. - PMD의 XPath 문자열 규칙과 달리 ArchUnit은 타입 안전한 Java 코드와 fluent API를 사용한다. - IDE 자동완성의 도움을 받을 수 있다. - 별도의 분석 프로세스를 구성하지 않고 규칙 객체와 클래스 목록을 직접 전달해 단위 테스트할 수 있다. ## 규칙 작성의 편의성 - ArchUnit에서는 다음과 같은 형태로 규칙을 표현할 수 있다. - 특정 클래스가 특정 타입에 의존하지 않아야 함 - 특정 생성자를 호출할 때 필수 인자를 전달해야 함 - 특정 패키지 간 의존성을 금지함 - 예를 들어 `DateTime` 객체를 시간대 정보 없이 생성하지 못하게 하는 규칙을 fluent API로 작성할 수 있다. - 규칙은 일반적인 Java 코드이므로 다음 작업이 쉽다. - 리팩터링 - IDE 기반 탐색 - 컴파일 시 오류 검출 - 단위 테스트 - 클래스 관계 그래프를 활용하면 단순한 이름 매칭보다 풍부한 맥락을 가진 규칙을 만들 수 있다. ## 공유 가능한 ArchRules 라이브러리 - 기본 ArchUnit은 하나의 저장소에서 JUnit 테스트로 사용하는 구조다. - Nebula ArchRules는 규칙을 별도 라이브러리로 패키징해 여러 Gradle 저장소에서 재사용할 수 있도록 한다. - `ArchRules Library Plugin`은 Gradle 프로젝트에 `archRules`라는 별도 source set을 추가한다. - 이 source set에는 `ArchRulesService`를 구현하는 클래스를 작성한다. - `ArchRulesService`는 `Map<String, ArchRule>`을 반환하는 단일 추상 메서드를 가진다. - map의 key는 규칙 이름이고 value는 실제 ArchUnit 규칙이다. - 규칙 코드는 본 애플리케이션 코드와 분리되어 별도의 JAR로 패키징된다. - 산출물에는 `arch-rules` classifier가 붙는다. - Gradle Module Metadata를 통해 `arch-rules` usage variant로 배포된다. - 따라서 downstream 프로젝트가 규칙을 사용하려면 Gradle Module Metadata 기반 의존성 해결이 필요하다. ## 독립형 규칙 라이브러리 - Standalone rule library는 본체 애플리케이션 코드 없이 `archRules`만 포함한다. - 다음과 같은 외부 코드 검사에 적합하다. - Java 표준 API 사용 규칙 - Guava 같은 오픈소스 라이브러리 사용 규칙 - 조직 공통 규칙 - `@Deprecated` API 사용 금지 - Netflix는 누구나 사용할 수 있는 OSS standalone 규칙 라이브러리 모음을 유지하며, 직접 규칙을 작성할 때 참고할 수 있는 예제로도 활용한다. ## 번들형 규칙 라이브러리 - Bundled rule library는 일반 라이브러리 코드와 해당 라이브러리 사용 규칙을 함께 배포한다. - `main` source set에는 라이브러리의 실제 기능을 담고, `archRules` source set에는 해당 라이브러리를 사용할 때 지켜야 할 규칙을 담는다. - 예를 들어 특정 라이브러리가 제공하는 공개 API의 올바른 사용법이나, 내부 API·deprecated API에 대한 접근 제한을 규칙으로 포함할 수 있다. - 라이브러리와 사용 규칙을 함께 배포하면 라이브러리 작성자가 권장 사용 방식을 downstream 빌드 과정에서 직접 검증할 수 있다. ## 실용적인 결론 ArchUnit은 단순한 단일 저장소용 아키텍처 테스트를 넘어, 바이트코드와 클래스 관계를 활용하는 범용 JVM 정적 분석 도구로 활용할 수 있다. 조직 차원에서는 Nebula ArchRules처럼 규칙을 별도 Gradle 라이브러리로 배포해 deprecated API 사용, 내부 API 접근, 라이브러리별 권장 패턴을 모든 저장소의 빌드 과정에서 자동 검증하는 방식이 효과적이다.

원문 읽기(새 탭에서 열림)
kakao5분 읽기큐레이션 요약

학생에서 개발자로: 로또 구현부터 레거시 개선까지, 서버의 흐름을 배우다

서버 개발은 복잡해 보이지만, 설계 이유를 질문하고 검증하는 과정을 거치면 막연함을 줄일 수 있다는 것이 글의 핵심 주장입니다. 카카오의 기술 온보딩은 TDD·객체지향 구현, 레거시 인수 테스트, 리팩터링을 단계적으로 수행하며 유지보수 가능한 구조와 안전한 변경 능력을 길렀습니다. 결국 좋은 개발자는 코드를 작성하는 데 그치지 않고, 설계·테스트·협업·AI 활용의 기준을 스스로 세우는 사람이라는 결론입니다. ## 기술 온보딩의 목표와 구성 - 온보딩은 총 3단계로 진행되었습니다. 1. TDD와 OOP 기반 기능 구현 2. 레거시 코드에 대한 인수 테스트 작성 3. 테스트로 보호된 레거시 코드 리팩터링 - 정답을 전달하기보다 다음과 같은 질문을 반복하며 설계의 근거를 고민하게 했습니다. - 왜 이렇게 설계했는가? - 이 책임은 정말 해당 객체가 가져야 하는가? - 이 테스트는 무엇을 보호하는가? - 목표는 유지보수 가능한 구조 설계, 레거시 분석 및 안전한 개선, 협업과 AI를 포함한 책임 있는 개발 역량을 기르는 것이었습니다. - 서버뿐 아니라 FE, Android, iOS 직군도 참여했으며, 기술 스택과 관계없이 좋은 엔지니어링의 기준은 공유될 수 있다는 점을 강조했습니다. ## 질문과 협업으로 서버 개발의 막연함 줄이기 - 트래픽, 동시성, 확장성, 데이터베이스 설계처럼 추상적으로 느껴지는 주제를 실제 구현과 리뷰를 통해 구체화했습니다. - 매일 데일리 미팅에서 트러블슈팅을 공유하고, 페어 프로그래밍으로 설계를 논의하며, PR 리뷰에서 구현 이유를 설명했습니다. - 이를 통해 개발은 개인의 코딩 능력만으로 완성되는 일이 아니라는 점을 체감했습니다. - 코드의 동작 여부보다 스스로 설계를 설명하고 변경의 영향을 예측하는 능력을 중요하게 다뤘습니다. ## 로또 게임 구현: TDD와 객체지향 설계 - 첫 번째 미션은 로또 가격, 자동·수동 발급, 당첨 통계를 구현하는 과제였습니다. - 다음과 같은 제약 조건이 설계 개선을 유도했습니다. - 들여쓰기 깊이 1단계 유지 - 메서드 10라인 이하 - 원시값 포장과 일급 컬렉션 사용 - `else` 사용을 줄이고 Early Return 활용 - TDD 방식으로 테스트를 먼저 작성해 요구사항과 설계를 점검했습니다. ### 랜덤 로직의 테스트 가능성 확보 - 랜덤 번호 생성은 실행마다 결과가 달라 테스트가 어려웠습니다. - 이를 해결하기 위해: - 번호 생성 전략을 인터페이스로 추상화하고 - 생성 전략을 외부에서 주입받으며 - 테스트 전용 Generator를 별도로 구현했습니다. - 그 결과 테스트에서 생성 값을 통제할 수 있었고, 구현체에 대한 결합도도 낮아졌습니다. - TDD는 단순히 테스트를 추가하는 방식이 아니라, 테스트 가능한 구조를 설계하게 만드는 도구로 작용했습니다. ### 값 객체와 캐싱에 대한 고민 - 같은 값을 가진 객체를 매번 새로 생성할지, 재사용할지 고민하며 객체의 정체성과 값의 동일성을 구분했습니다. - 1부터 45까지의 로또 번호처럼 값의 범위가 제한된 경우 캐싱 전략을 검토할 수 있었습니다. - 이 미션을 통해 기능 구현보다 객체의 책임, 생성 방식, 재사용 가능성 등 설계 기준을 고민하게 되었습니다. ## 인수 테스트: 레거시를 안전하게 이해하기 - 두 번째 미션에서는 실제 서비스 수준의 레거시 코드를 바로 수정하지 않고, 먼저 인수 테스트를 작성했습니다. - 테스트가 보호해야 할 대상은 다음과 같았습니다. - 사용자의 행동 - 시스템의 반환 결과 - 외부에서 관찰 가능한 상태 변화 - 단순히 성공 여부만 확인하는 것이 아니라, 결과가 정확한지 검증하는 Strong Assertion 전략을 적용했습니다. - Cucumber 기반 BDD를 사용해 비개발자도 이해할 수 있는 시나리오 형태로 테스트를 구성했습니다. - 테스트를 개발자만의 코드가 아니라 팀 전체가 공유하는 실행 가능한 명세로 바라보았습니다. ### 운영 환경과 테스트 환경 맞추기 - “내 컴퓨터에서는 동작한다”는 문제를 줄이기 위해 Production Parity를 적용했습니다. - 구체적으로: - H2 대신 운영과 같은 PostgreSQL 사용 - Docker 기반으로 실행 환경 통일 - Gradle Task를 이용한 테스트 자동화 - 환경 차이로 인한 테스트 결과의 불일치를 줄이고, 누구나 동일한 조건에서 테스트를 실행할 수 있게 했습니다. ### 테스트 데이터 격리 - 테스트 간 데이터 의존성을 제거하기 위해 다음 전략을 사용했습니다. - 외래 키 관계를 고려한 역순 삭제 - `TRUNCATE ... CASCADE` - 공통 Cleanup 유틸리티 작성 - 모든 테스트가 초기화된 동일한 상태에서 시작하도록 보장해 테스트의 재현성과 안정성을 높였습니다. - 중요한 것은 특정 도구를 사용하는 것보다 상황에 맞는 데이터 격리 방법을 선택하는 판단 기준이라고 설명합니다. ## 레거시 리팩터링: 구조와 동작의 분리 - 세 번째 미션의 핵심은 레거시 코드를 단순히 “클린 코드”로 바꾸는 것이 아니라, 안전한 변경의 기준을 세우는 것이었습니다. - 가장 중요한 원칙은 구조 변경과 동작 변경을 분리하는 것입니다. - 구조를 개선할 때는 기존 동작을 유지 - 동작을 변경할 때는 구조 개선과 섞지 않기 - 이렇게 변경 목적을 분리하면 코드 리뷰와 테스트를 통해 변경 범위를 명확히 검증할 수 있습니다. - 의도하지 않은 동작 변화가 발생하면 리뷰에서 이를 찾아내고, 변경을 통제하는 능력을 기를 수 있었습니다. ## AI와 협업할 때의 검증 범위 - 리팩터링 과정에서 AI를 활용해 넓은 범위의 코드 개선을 빠르게 시도했습니다. - 그러나 AI에게 한 번에 큰 범위의 변경을 요청하면 수정량이 커져 검증이 어려워지는 문제가 발생했습니다. - 따라서 AI의 제안을 그대로 수용하기보다 변경 범위를 작게 나누고, 각 변경을 테스트와 리뷰로 확인하는 방식이 필요하다는 교훈을 얻었습니다. - AI는 개발자를 대신하는 도구가 아니라, 개발자가 책임 있게 검토하고 통제해야 하는 협업 도구로 다뤄졌습니다. 실무에서는 기능 구현 전에 책임과 설계 이유를 설명할 수 있는지 확인하고, 레거시 코드는 먼저 외부 동작을 보호하는 테스트를 마련하는 것이 좋습니다. 이후 구조 변경과 동작 변경을 분리해 작은 단위로 개선하며, AI를 사용할 때도 변경 범위를 제한하고 반드시 테스트와 리뷰로 검증하는 접근이 안전합니다.

원문 읽기(새 탭에서 열림)
airbnb원문

에어비앤비 (새 탭에서 열림)

에어비앤비는 4.5년에 걸쳐 수천만 라인의 Java, Kotlin, Scala 코드로 구성된 대규모 JVM 모노레포를 Gradle에서 Bazel로 성공적으로 이전했습니다. 이번 마이그레이션을 통해 빌드 속도는 3~5배, IDE 동기화 및 배포 속도는 2~3배 향상되었으며, 개발자 만족도(CSAT)가 38%에서 68%로 크게 올랐습니다. Bazel의 밀폐성(Hermeticity)과 원격 실행 기능을 활용하여 대규모 코드베이스에서도 안정적이고 확장 가능한 빌드 시스템을 구축한 것이 핵심 성과입니다. **Gradle에서 Bazel로 전환한 이유** * **빌드 속도의 혁신:** Bazel의 원격 빌드 실행(RBE)을 통해 수천 개의 작업을 병렬로 처리하며, 'Build without the Bytes' 기능을 도입하여 필요한 아티팩트만 다운로드함으로써 대역폭 소모를 줄였습니다. * **빌드 안정성 및 밀폐성:** Gradle과 달리 샌드박스 환경을 제공하여 빌드 작업이 지정된 입력 외의 파일 시스템(예: /tmp 디렉토리)에 접근하는 것을 차단하고, 환경 차이로 인한 빌드 실패를 방지했습니다. * **통일된 인프라 구축:** 에어비앤비 내의 웹, iOS, Python, Go 등 다양한 언어의 레포지토리를 Bazel로 단일화하여 원격 캐싱, 로깅, 변경된 타겟 계산 로직을 공유할 수 있게 되었습니다. **단계적 마이그레이션과 개념 증명(PoC)** * **Viaduct 플랫폼 선정:** 에어비앤비에서 가장 크고 복잡한 서비스 중 하나인 GraphQL 모놀리스 'Viaduct'를 첫 타겟으로 선정하여, 가장 까다로운 케이스에서 성능 이점을 증명했습니다. * **공존 전략:** 초기에는 개발자가 Gradle과 Bazel 중 선택해서 사용할 수 있도록 두 시스템을 병렬로 운영하여 서비스 중단 위험을 최소화했습니다. * **개발자 설득:** 단순한 성능 향상을 넘어, 초기 단계에서 발생한 버그와 통합 문제를 해결하여 개발자들이 자발적으로 Bazel을 선택하도록 유도했습니다. **자동 빌드 파일 생성 및 유지보수** * **커스텀 생성기 개발:** Bazel 빌드 파일(BUILD)을 수동으로 관리하는 불편을 줄이기 위해 소스 코드의 패키지와 임포트 구문을 분석하여 의존성 그래프를 그리는 자동 생성기를 구축했습니다. * **Gazelle의 영감:** Go 언어의 Gazelle 도구에서 아이디어를 얻었으나, JVM 언어의 특성과 성능 요구사항을 맞추기 위해 캐싱 기능을 포함한 자체 도구로 발전시켰습니다. * **CI 통합:** 모든 커밋 전에 자동 생성기를 실행하여 Gradle과 Bazel의 빌드 그래프가 항상 일치하도록 유지했습니다. **IDE 사용자 경험 개선** * **IntelliJ 동기화 최적화:** 대규모 모노레포에서 Gradle 동기화가 최대 40분까지 소요되던 문제를 Bazel의 병렬 분석과 'Query Sync(실험적 기능)' 도입을 통해 3~10분 수준으로 단축했습니다. * **IntelliJ Aspect 활용:** Bazel의 Aspect 기능을 사용하여 프로젝트 구조 정보를 추출함으로써 IDE가 소스 코드와 의존성을 더 효율적으로 이해하도록 돕습니다. **성공적인 전환을 위한 교훈** 대규모 마이그레이션에서 가장 중요한 것은 **성능에 대한 집착**과 **개발자 경험(DevEx)에 대한 투자**입니다. 빌드 속도가 빨라지면 개발자들은 자연스럽게 새로운 도구를 수용하게 되며, 특히 IntelliJ와 같은 IDE와의 매끄러운 통합이 프로젝트의 성패를 좌우합니다. 또한 빌드 파일 생성과 같은 반복적인 작업을 자동화하여 개발자가 시스템 환경 설정이 아닌 코드 작성에만 집중할 수 있는 환경을 조성하는 것이 필수적입니다.