Loading the catalog…
Loading the catalog…
오늘은 최근에 알게된 프로젝트에 대해서 분석해보는 글을 작성해 보려고 한다. 다만 프로젝트의 익명성을 위하여 프로젝트에 관한 내용은 일절 다루지 않고, 오로지 프로젝트의 구조와 규칙에 대해서 조사해봤다. 즉, 이번 보고서의 분석 대상은 서비스가 무엇을 하는가 가 아니라, 프로젝트를 어떤 구조로 나누고 어떤 규칙으로 개발하는가 이다. 이 구조를 무엇이라고 부를 수 있을까? 내가 참고한 이 프로젝트는 크게 두 구조가 결합되어 있다. 1. 기능 중심 레이어드 모놀리스 Feature-first Layered Monolith 2. 문서·테스트·AI를 이용한 개발 하네스 Documentation-driven AI Development Harness 첫 번째 구조는 코드를 정리한다. 기능 └── Controller └── Service └── Repository └── Database / External API 두 번째 구조는 개발 과정을 정리한다. 작업 규칙 ├── 문서 ├── AI 역할 ├── 테스트 ├── Git Hook └── CI 즉, 하나는 코드가 엉키지 않게 하는 구조 이고, 다른 하나는 개발 과정이 엉키지 않게 하는 구조 다. 질문 1. 우리가 공부하려는 것은 서비스일까, 구조일까? 프로젝트를 분석할 때 다음 두 가지를 혼동하기 쉽다. 서비스 분석 └── 어떤 기능을 제공하는가? 구조 분석 └── 기능이 늘어나도 어떻게 정리하고 관리하는가? 예를 들어 주문 기능 , 회원 기능 , 검색 기능 이 있다는 사실은 서비스 분석이다. 반면 다음은 구조 분석이다. 기능별로 패키지를 나누는가? Controller와 Service의 책임은 무엇인가? 공통 규칙은 어디에 기록하는가? AI가 코드를 수정할 수 있는가? 변경이 안전한지 어떻게 검증하는가? 누가 최종 책임을 가지는가? 해결 방법 서비스 이름과 도메인 용어를 제거하고도 남는 규칙을 본다. order member catalog 위 이름을 다른 것으로 바꿔도 구조가 유지된다면 재사용 가능한 구조다. payment reservation notification 이 문서에서는 기능 이름이 아니라 다음 요소를 중심으로 본다. 코드 경계 의존 방향 문서 체계 개발 순서 검증 방식 AI 역할 전체 구조에서 담당하는 부분 이 구분은 앞으로 분석의 기준점이다. 기능은 프로젝트마다 바뀌지만, 안전하게 기능을 추가하는 구조는 다른 프로젝트에도 재사용할 수 있다. 질문 2. 처음부터 안전한 프로젝트를 만들려면 거대한 구조를 먼저 만들어야 할까? 안전한 프로젝트를 만들고 싶으면 처음부터 다음을 모두 준비해야 할 것처럼 느껴진다. 멀티모듈 클린 아키텍처 포트와 어댑터 이벤트 시스템 공통 라이브러리 여러 AI 에이전트 복잡한 CI 하지만 분석 대상 프로젝트는 그렇게 시작하지 않았다. 해결 방법 가장 작은 실행 가능한 프로젝트에서 출발했다. backend/ ├── build.gradle ├── BackendApplication.java ├── application.properties └── BackendApplicationTests.java 그 뒤 실제 필요가 생긴 순서대로 추가했다. 최소 Spring Boot 프로젝트 ↓ Docker 실행 환경 ↓ Git·코드 컨벤션 ↓ 개발 하네스 ↓ 도메인 모델 ↓ 외부 데이터 연동 ↓ PostgreSQL 통합 테스트 ↓ API·인증·검색 왜 필요한가? 초기부터 완성형 구조를 만들면 아직 존재하지 않는 문제를 예상해 코드를 작성하게 된다. 그 결과 다음 문제가 생긴다. 구현체가 하나뿐인 인터페이스 사용하지 않는 공통 모듈 필요하지 않은 이벤트 시스템 실제 변경보다 구조 유지 비용이 더 큰 프로젝트 팀원이 이해하지 못하는 추상화 실제 적용 방법 처음에는 다음 정도면 충분하다. my-project/ ├── backend/ │ ├── build.gradle │ └── src/ │ ├── main/ │ └── test/ ├── README.md └── compose.yaml 첫 번째 기능이 생겼을 때 해당 기능 패키지를 추가한다. src/main/java/com/example/ └── order/ ├── controller/ ├── service/ ├── repository/ └── domain/ 전체 구조에서 담당하는 부분 이 단계는 프로젝트의 기술적 출발점 을 담당한다. 유의 사항 빈 계층을 미리 만들지 않는다. 실제 코드가 생길 때 패키지를 만든다. 미래 확장을 위한 인터페이스를 먼저 만들지 않는다. 단일 모듈로 해결되는 동안에는 멀티모듈로 나누지 않는다. 질문 3. 코드는 기술별로 나눠야 할까, 기능별로 나눠야 할까? 가장 단순한 레이어드 구조는 다음과 같다. controller/ ├── OrderController ├── MemberController └── CatalogController service/ ├── OrderService ├── MemberService └── CatalogService 처음에는 깔끔해 보이지만 기능이 늘어나면 하나의 기능을 이해하기 위해 여러 디렉터리를 이동해야 한다. 해결 방법 최상위는 기능으로 나누고, 기능 내부를 계층으로 나눈다. order/ ├── controller/ ├── service/ ├── repository/ ├── domain/ ├── dto/ └── exception/ member/ ├── controller/ ├── service/ ├── repository/ ├── domain/ └── dto/ 이를 기능 중심 패키지 구조 라고 볼 수 있다. 어디서 어떻게 사용되는가? 새로운 주문 요구사항을 조사한다면 order 안에서 대부분의 흐름을 찾을 수 있다. OrderController ↓ OrderService ↓ OrderRepository ↓ Order 기능의 API, 유스케이스, 데이터 접근, 도메인 규칙이 가까운 위치에 모인다. 왜 필요한가? 프로젝트의 실제 변경은 대개 기술 단위가 아니라 기능 단위로 발생한다. “Service를 변경한다” 보다는 다음과 같은 요청이 많다. “주문 취소 기능을 변경한다” “회원 탈퇴 규칙을 수정한다” 따라서 변경 이유가 같은 코드를 가까이 두는 편이 이해하기 쉽다. 실제 적용 방법 com.example.backend/ ├── order/ ├── member/ ├── catalog/ ├── notification/ └── global/ 각 기능 안에는 필요한 계층만 둔다. notification/ ├── service/ └── repository/ Controller나 Domain이 없다면 빈 디렉터리를 만들 필요도 없다. 전체 구조에서 담당하는 부분 이 구조는 기능 간 경계와 코드 탐색 범위 를 담당한다. 유의 사항 기능 패키지를 너무 잘게 나누지 않는다. 단순 클래스 종류를 기능으로 착각하지 않는다. common , util , shared 에 코드를 성급히 모으지 않는다. 두 기능에서 실제로 안정적으로 공유될 때만 공통 영역으로 이동한다. 질문 4. 각 계층은 무엇을 담당해야 안전할까? 기능별 패키지를 만들었더라도 모든 로직을 Service에 넣으면 결국 거대한 Service가 된다. 그러면 각 계층이 맡아야 하는 책임을 정해야 한다. 해결 방법 의존 방향을 한 방향으로 제한한다. Controller ↓ Service ↓ Repository ↓ Database / External API Service ↓ Domain Controller는 무엇을 하는가? Controller는 외부 요청과 애플리케이션 사이의 경계다. 담당 ├── HTTP 요청 수신 ├── 요청 값 검증 ├── DTO 변환 ├── Service 호출 └── HTTP 응답 생성 Controller가 직접 Repository를 호출하지 않는다. // 피해야 하는 형태 orderRepository.findById(id); Controller는 유스케이스를 Service에 요청한다. orderService.findOrder(id); Service는 무엇을 하는가? Service는 하나의 사용자 작업을 완성하는 흐름을 담당한다. 담당 ├── 유스케이스 순서 ├── 트랜잭션 범위 ├── 여러 Repository 협력 ├── Domain 객체 호출 └── 실패 전파 Service가 모든 비즈니스 규칙을 계산할 필요는 없다. 상태와 직접 관련된 규칙은 Domain 객체가 소유하는 편이 좋다. Repository는 무엇을 하는가? Repository는 데이터가 어디서 오는지 숨긴다. 담당 ├── 데이터베이스 조회·저장 ├── 쿼리 ├── 외부 API 접근 └── 영속성 변환 분석 대상 구조에서는 DB와 외부 API 접근 모두 Repository 계층에 포함한다. 이것은 실용적인 레이어드 구조이며, 엄격한 포트·어댑터 구조는 아니다. Domain은 무엇을 하는가? Domain은 상태와 그 상태를 지키는 규칙을 담당한다. 담당 ├── 상태 ├── 상태 변경 행위 ├── 불변식 ├── 값 검증 └── 도메인 계산 Domain은 다음을 알지 못해야 한다. HTTP Controller DTO JSON 응답 형식 Service 화면 구조 전체 구조에서 담당하는 부분 계층 규칙은 책임 혼합과 순환 의존을 방지 한다. 유의 사항 Controller에서 Repository를 직접 호출하지 않는다. Repository가 Service를 호출하지 않는다. Domain이 DTO나 Web 기술에 의존하지 않는다. JPA Entity를 API 응답으로 직접 반환하지 않는다. Service가 단순 전달만 한다면 정말 필요한 계층인지 검토한다. 질문 5. 모든 Controller 옆에 지침 문서를 두면 관리하기 쉬울까? 다음처럼 파일마다 설명서를 두고 싶을 수 있다. OrderController.java OrderController.md MemberController.java MemberController.md 하지만 코드가 바뀔 때 문서가 함께 수정되지 않으면 서로 다른 설명이 남는다. 해결 방법 문서는 클래스별이 아니라 질문과 규칙별로 중앙화 한다. docs/ ├── README.md ├── architecture.md ├── layer-boundaries.md ├── development-cycle.md ├── api-conventions.md ├── persistence.md ├── testing.md ├── security.md └── quality-gates.md 어디서 어떻게 사용되는가? docs/README.md 가 문서 지도 역할을 한다. API를 변경한다 └── api-conventions.md DB를 변경한다 └── persistence.md 구조를 변경한다 ├── architecture.md └── layer-boundaries.md 작업을 완료한다 └── quality-gates.md 왜 필요한가? 동일 규칙이 여러 문서에 복제되면 서로 다른 내용으로 변한다. 예를 들어 모든 Controller 문서에 오류 응답 규칙이 들어 있으면, 규칙을 변경할 때 모든 문서를 수정해야 한다. 대신 다음처럼 하나의 원본만 둔다. 오류 응답 규칙의 원본 └── exception-handling.md 실제 적용 방법 문서를 세 단계로 구성할 수 있다. 루트 AGENTS.md └── 프로젝트 전체 작업 지도 backend/AGENTS.md └── 백엔드 작업 규칙 backend/docs/README.md └── 상황별 상세 문서 라우터 전체 구조에서 담당하는 부분 이 문서 구조는 사람과 AI가 어떤 규칙을 읽어야 하는지 안내하는 내비게이션 이다. 유의 사항 같은 규칙을 여러 문서에 복사하지 않는다. 문서의 원본 위치를 명확히 한다. 코드만 보면 알 수 있는 내용을 문서로 반복하지 않는다. 클래스의 실제 동작은 테스트로 설명한다. 복잡한 업무 흐름만 별도 문서로 승격한다. 질문 6. 개발 하네스란 무엇이고 왜 필요한가? 문서에 규칙을 적어도 아무도 읽지 않으면 규칙은 지켜지지 않는다. 그렇다면 문서를 실제 개발 과정과 어떻게 연결할 수 있을까? 해결 방법 문서, AI 역할, 자동 검사를 하나의 흐름으로 연결한다. 개발 하네스 ├── AGENTS.md ├── 작업별 문서 ├── AI 역할 정의 ├── 검증 스크립트 ├── Git Hook ├── 테스트 └── CI 하네스는 특정 프레임워크가 아니다. 개발자가 안전한 순서에서 벗어나지 않도록 둘러싼 작업 환경 전체다. 어디서 어떻게 사용되는가? 작업 시작 └── AGENTS.md가 읽을 문서를 안내 코드 작성 전 └── 개발 절차와 계층 경계 확인 커밋 전 └── Git Hook으로 규칙 검사 PR 생성 후 └── CI에서 테스트와 문서 구조 검사 완료 전 └── Quality Gate 확인 왜 필요한가? 문서만 있으면 권고 사항에 머문다. 자동 검사를 연결하면 일부 규칙이 실행 가능한 계약이 된다. “필수 문서가 있어야 한다” → 스크립트로 파일 존재 검사 “에이전트는 읽기 전용이어야 한다” → 설정 파일 검사 “커밋 제목은 일정한 형식이어야 한다” → commit-msg hook 검사 “백엔드는 DB 통합 테스트를 통과해야 한다” → CI에서 PostgreSQL 실행 후 Gradle check 전체 구조에서 담당하는 부분 하네스는 코드 구조 자체가 아니라 코드를 변경하는 과정을 보호 한다. 유의 사항 모든 규칙을 문서 검사로 해결할 수는 없다. 예를 들어 다음 규칙은 별도의 구조 테스트가 없다면 문서와 리뷰에 의존한다. Controller가 Repository를 직접 호출하지 않는다. 이 규칙이 자주 깨진다면 ArchUnit 같은 자동 검사를 추가할 수 있다. 한 번도 깨지지 않았다면 미리 추가할 필요는 없다. 질문 7. 여러 AI가 동시에 코드를 작성하면 더 빠르지 않을까? AI가 여러 대라면 기능을 나눠 동시에 구현하는 것이 빠르게 보인다. 하지만 같은 코드베이스를 여러 AI가 수정하면 다음 문제가 생길 수 있다. 동일 파일 충돌 서로 다른 설계 도입 중복 구현 최종 책임 불명확 한 AI의 전제를 다른 AI가 모름 해결 방법 Single Writer, Multiple Readers 구조를 사용한다. 메인 작업자 ├── 요구사항 판단 ├── 코드 작성 ├── 결과 통합 └── 최종 검증 책임 읽기 전용 보조 AI ├── Explorer ├── Architect └── Reviewer 메인 작업자는 사람일 수도 있고 AI일 수도 있다. Explorer는 무엇을 하는가? Controller ↓ Service ↓ Repository ↓ Domain 이 흐름을 추적해 변경 영향 범위를 조사한다. 직접 수정하지 않고 다음을 보고한다. 관련 파일 호출 흐름 데이터 흐름 기존 테스트 미확인 사항 Architect는 무엇을 하는가? 다음을 검토한다. 기능 경계 계층 의존 API 계약 트랜잭션 범위 데이터 일관성 마이그레이션 영향 Architect도 코드를 수정하지 않는다. Reviewer는 무엇을 하는가? 구현이 끝난 뒤 독립적인 관점에서 다음을 본다. 정확성 회귀 인증·인가 입력 검증 동시성 성능 테스트 누락 비밀 노출 왜 필요한가? 구현한 주체는 자신의 전제를 당연하다고 생각하기 쉽다. 읽기 전용 Reviewer는 구현 과정에 참여하지 않았기 때문에 다른 관점에서 볼 수 있다. 전체 구조에서 담당하는 부분 AI 역할 분리는 병렬 구현 보다 독립적인 조사와 검증 을 담당한다. 유의 사항 모든 변경에 여러 AI를 사용하지 않는다. 단순 변경은 메인 작업자 하나로 충분하다. 구조·보안·DB 변경처럼 위험한 작업에만 보조 역할을 추가한다. AI 리뷰를 테스트 통과로 간주하지 않는다. 최종 책임은 항상 메인 작업자에게 둔다. 질문 8. 기능 하나를 실제로 어떤 순서로 개발해야 할까? 바로 코드를 작성하면 빠르지만, 중간에 요구사항이나 영향 범위를 잘못 이해했다는 사실을 발견할 수 있다. 해결 방법 작업을 다음 흐름으로 고정한다. 계약 → 탐색 → 설계 → Red → Green → Refactor → Verify → Review 1단계: 작업 계약 코드를 작성하기 전에 네 가지를 적는다. Goal 사용자가 얻을 결과는 무엇인가? Context 관련 코드와 테스트는 어디에 있는가? Constraints 기술·범위·안전 제한은 무엇인가? Done 무엇을 실행해 완료를 증명할 것인가? 2단계: 탐색 Controller ↓ Service ↓ Repository ↓ Domain 운영 코드만 보지 않고 가까운 테스트를 함께 읽는다. 또한 다음을 찾는다. 공개 API 계약 트랜잭션 경계 외부 시스템 실패 경로 기존 사용자 영향 3단계: 설계 게이트 변경할 객체마다 한 문장으로 책임을 설명한다. OrderController → 주문 취소 HTTP 요청을 검증하고 Service에 전달한다. OrderService → 주문 취소 유스케이스와 트랜잭션을 관리한다. Order → 현재 상태에서 취소 가능한지 판단한다. 설명이 어렵다면 책임이 섞였을 가능성이 있다. 4단계: Red → Green → Refactor Red └── 필요한 행동을 보여주는 실패 테스트 Green └── 테스트를 통과하는 최소 구현 Refactor └── 행동을 유지하면서 이름·책임·중복 개선 5단계: Verify → Review 가장 가까운 테스트 ↓ 기능 전체 테스트 ↓ DB 통합 테스트 ↓ 전체 검사 ↓ 최종 diff 리뷰 전체 구조에서 담당하는 부분 이 순서는 잘못된 요구사항을 큰 코드로 확장하기 전에 멈추게 하는 역할 을 한다. 유의 사항 컴파일 오류를 Red 단계의 성공으로 보지 않는다. 테스트만 통과하도록 값을 하드코딩하지 않는다. 하나의 작업에서 여러 행동을 한꺼번에 변경하지 않는다. 실행하지 못한 검사는 통과했다고 기록하지 않는다. 질문 9. 테스트는 어느 계층에 얼마나 작성해야 할까? 모든 클래스를 단위 테스트하면 테스트 수는 많아지지만 실제 서비스 동작을 보장하지 못할 수 있다. 반대로 통합 테스트만 작성하면 느리고 실패 원인을 찾기 어렵다. 해결 방법 계층별 위험에 맞는 테스트를 둔다. Domain └── 빠른 단위 테스트 행위, 불변식, 경계값 Service └── 유스케이스 테스트 흐름, 협력, 실패 전파 Controller └── HTTP 계약 테스트 검증, 상태 코드, 오류 응답 Repository └── DB 통합 테스트 매핑, 쿼리, 제약조건 External API └── 계약·통합 테스트 요청, 응답, timeout, 변환 왜 필요한가? 각 계층에서 발생하는 문제의 성격이 다르기 때문이다. Domain 오류 → 잘못된 비즈니스 판단 Controller 오류 → 잘못된 HTTP 계약 Repository 오류 → 실제 DB와 다른 동작 외부 API 오류 → timeout이나 응답 형식 변화 실제 적용 방법 테스트 디렉터리가 운영 코드 구조를 따라가도록 한다. src/main/java/com/example/order/ ├── controller/ ├── service/ ├── repository/ └── domain/ src/test/java/com/example/order/ ├── controller/ ├── service/ ├── repository/ └── domain/ 전체 구조에서 담당하는 부분 테스트는 문서보다 더 정확하게 현재 코드가 실제로 보장하는 동작 을 설명한다. 유의 사항 구현 세부사항보다 외부 행동을 검증한다. 테스트 하나는 행동 하나를 검증한다. 실제 DB 경계가 중요하면 mock으로 대체하지 않는다. 버그 수정은 실제 증상을 재현하는 테스트에서 시작한다. flaky 테스트는 재실행 성공만으로 통과시키지 않는다. 질문 10. 언제 별도의 업무 문서를 만들어야 할까? 모든 기능에 상세 설계 문서를 만들면 관리 비용이 커진다. 반대로 아무 문서도 없으면 복잡한 업무 흐름을 코드만 보고 이해해야 한다. 해결 방법 코드만으로 파악하기 어려운 흐름에만 별도 문서를 둔다. 별도 문서가 유용한 경우는 다음과 같다. 여러 단계로 이어지는 데이터 파이프라인 중단과 재시작이 있는 작업 외부 데이터 동기화 데이터 수명주기 마이그레이션 순서 장애 복구 절차 운영자가 직접 실행하는 작업 예시 docs/ ├── data-pipeline-execution.md ├── source-data-lifecycle.md └── address-reference-import.md 이 문서들은 클래스 설명서가 아니다. 어떤 순서로 실행되는가? 중간에 실패하면 어떻게 되는가? 재실행해
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
기능 중심 백엔드와 AI 협업 이해하기. 오늘은 최근에 알게된 프로젝트에 대해서 분석해보는 글을 작성해 보려고 한다. 다만 프로젝트의 익명성을 위하여 프로젝트에 관한 내용은 일절 다루지 않고, 오로지 프로젝트의 구조와 규칙에 대해서 조사해봤다. 즉, 이번 보고서의 분석 대상은 서비스가 무엇을 하는가 가 아니라, 프로젝트를 어떤 구조로 나누고 어떤 규칙으로 개발하는가 이다. 이 구조를 무엇이라고 부를 수 있을까? 내가 참고한 이 프로젝트는 크게 두 구조가 결합되어 있다. 1. 기능 중심 레이어드 모놀리스 Feature-first Layered Monolith 2. 문서·테스트·AI를 이용한 개발 하네스 Documentation-driven AI Development Harness 첫 번째 구조는 코드를…
Open source