들어가기 전에..
최근에 Claude Max플랜을 구독을 시작했습니다.
기존에 Pro플랜을 구독할때는 필요할때만 사용하고 어차피 대화 2~3번 하면 세션만료되는 claude code도 사용을 안하다시피 했습니다.
opus 4.5는 구경도 못했죠.
근데, 100달러짜리 Max를 구독하고 나니까 돈이 아까워서 억지로라도 AI 잘 쓰는 방법, 여러 도구들을 찾아보고 쓰게되고 개발을 더 하게되는듯 합니다.
아무튼? Max 구독과 동시에 토큰 사용량 제한이 늘어났더라도 제한된 사용량에서 가성비 있게 쓰는 방법에 대해 고민하던 중 한 가지 실험이 하고싶어졌습니다.
" 사람이 변경하기 좋은 형태, 사람이 확장하기 좋은 형태의 코드도 AI한테도 동일할까??"
라는 의문에서부터 시작하게 되었습니다.
먼저, 변경하기 좋은 형태, 확장성이 좋은 형태의 코드에 대해 말을 해봐야 할 것 같습니다.
저는 Java와 Spring으로 개발을 하고있고, 공부를 오래 해왔습니다.
Java와 Spring의 핵심은 무엇일까요?
Java는 객체지향 프로그래밍 언어입니다.
Spring은 제가 생각하기에 OOP를 더 잘 하게해주는 프레임워크라고 생각합니다.
개인적으로 Spring 프레임워크의 가장 강력한 기능은 "테스트"라고 생각합니다. DI컨테이너를 통한 런타임 의존성 주입으로 테스트를 짜기 굉장히 좋은 환경이라고 생각합니다.
취업준비를 하던 시절에 저는 "기술" 혹은 "패러다임" 그 자체에 몰입해 있었습니다. MQ(카프카, RabbitMQ) , 레디스, MSA, DDD, 헥사고날... 등등
해당 시기에 가장 관심이 있었던 것은 "헥사고날 아키텍처" 였습니다. 그냥 이름도 멋있었고, 그림도 멋있었거든요.
헥사고날 아키텍처에 대해서 대충 보고, 무작정 헥사고날 아키텍처를 적용하려고 하니 잘 안되었습니다.
그 안의 본질을 이해하지 못한 채로 활용을 하려다 보니 잘 쓸리가 없었죠.
또, 제가 한창 공부를 하던 시기에 여러 패러다임들이 화두가 되었었습니다.
도메인 모델 패턴, DDD, 헥사고날 아키텍처, 클린 아키텍처, 양파 아키텍처, 모듈러 모놀리스, MSA, TDD...
본질을 이해하지 못한상태에서 위 키워드만 봐도 머리가 아파왔습니다. "나는 개발 못하겠다" 같은 생각도 했습니다.
하지만 이제는 깨달았습니다.
위 키워드 전부 한 가지 이야기를 하고있는 것이고, 이름만 바꾼채로 새로운"척"하는 패러다임이라는 것을요.
그리고, 깨달음을 통해 앞으로 기술들을 어떻게 바라봐야 하는지도요.
도메인 모델 패턴, DDD, 헥사고날 아키텍처, 클린 아키텍처, 양파 아키텍처, 모듈러 모놀리스, MSA, TDD 는 모두 OOP에 대해서 이야기 하고 있습니다.
그럼 OOP에 대해 이야기 해볼까요?
OOP는 왜 쓸까요? OOP의 원칙이라고 하는 SOLID는 왜 지켜야 하는것일까요?
혹시 결합도는 낮추고 응집도는 높이면서, 이해하기 좋은 코드를 짤 수 있기 때문이라고 기계적으로 답변이 나오시나요?
저도 그렇고 Java로 취업준비하는 아무나 붙잡고 SOLID에 대해 각각이 무엇인지 물어보면 대부분은 대답을 잘 합니다.
SRP는 단일책임원칙으로써, 클래스가 한 가지 책임만 가져야 하는것입니다.
OCP는 개방폐쇄 원칙으로써, 수정에는 닫혀있고 확장에는 열려있어야 하는것입니다.
LSP는 리스코프치환 원칙으로써, S 타입의 객체 o1과 T 타입 객체 o2가 있고, T 타입을 이용해서 정의한 프로그램 P 에서 o2를 o1으로 치환해도 P의 행위가 변하지 않는다면, S는 T의 하위타입임을 말하는 것입니다.
ISP는 인터페이스분리 원칙으로써, 인터페이스를 분리해야 한다는 원칙입니다.
DIP는 의존성 역전 원칙으로써, 의존성을 역전시킬 수 있다는 것입니다.
위처럼, 저도 기계적으로 답변은 잘 했습니다.
그런데 "그래서 그걸 왜 해야하는데? 그걸 적용하면 어떤점 때문에 좋은데?" 에 대한 대답은 잘 못했습니다.
OOP, SOLID 의 핵심은 결국 "변경 범위를 줄이자" 입니다.
위에서 말한 답변들을 보면 결국 다 변경 범위를 최소화하기 위함임을 알 수 있어요.
DIP만 정의에서 왜 변경이 줄어들지? 에 대한 의문이 있을 수 있는데,
DIP만 잠깐 짚고 넘어가 볼게요.

혹시 위와 같은 형태로, 인터페이스 없이 각 구체클래스가 다른 구체클래스를 직접 참조하도록 개발해보신 경험이 있으신가요?
화살표는 의존성의 방향을 나타냅니다. 그리고, 의존성의 방향의 정반대가 변경 가능성의 방향입니다.
이 말은 무엇이냐, repository계층이 변경되면, service 계층에 변경 가능성이 생깁니다. 그리고 service계층이 변경되면 controller계층에도 변경 가능성이 생깁니다. 그러면, repository 하나 변경됐는데 최악의 경우에는 service, controller까지 변경해야 할수도 있다는 말이 됩니다.
지금은 클래스 3개라서 별거 아닌것 처럼 보여도,
A -> B -> C -> D -> E -> ....
와 같은 의존성을 갖게 되면 하나 바꾸려면 전체를 바꿔야 하는 경우가 생길것이고, 사람이 하는 일이다 보니 누락되거나 실수하는 경우가 생길 수 있을것입니다.
그럼 이제 위 구조에서 interface를 두어볼까요?

Controller는 추상화된 Service에 의존하고, 그걸 구현하는 impl이 있고, ServiceImpl은 추상화된 Repository를 의존하고, 그걸 구현한 Repository Impl이 있게 됩니다.
그리고 앞서, 의존성의 방향의 정반대가 변경 가능성의 방향이다. 라고 했죠?
위 구조에서 화살표 방향을 보면, Controller의 변경의 이유는 Service 인터페이스에 의존하고있고, 인터페이스의 구현체가 어떻게 바뀌어도 Controller에는 영향이 없습니다.
ServiceImpl도 마찬가지고요.
첫 번째 구조와 다르게, ServiceImpl계층이 위쪽 추상화계층을 의존하고있습니다. 의존성이 역전되었습니다.
의존성의 방향이 바뀌었기 때문에 추상적인 것들이 바뀌지 않는 한, 구체적인 것들에 변경전파는 되지 않습니다.
이것이 "구체화에 의존하지 말고 추상화에 의존하라", "상위계층, 하위계층 모두 추상화에 의존해야한다" 를 말하는 DIP입니다.
결국? 변경범위를 줄이자!
도메인 모델 패턴, DDD, 헥사고날 아키텍처, 클린 아키텍처, 양파 아키텍처, 모듈러 모놀리스, MSA, TDD 는요?
그냥 OOP를 잘 하다보면 위 패턴이나 패러다임을 따라갈 수 밖에 없습니다.
위 그림인 DIP를 적용한 상태에서 domain이라는 도메인로직이 들어간 클래스를 추가해봅시다

이렇게 되는데, 형태를 조금만 바꿔볼게요.

그냥 배치만 다르게 한겁니다. 의존방향은 여전히 똑같습니다.
이제, 상하 반전을 시켜볼게요.

반전된거 말고 여전히 DIP를 잘 지키는 구조입니다.
근데, 여기에서 Controller, Service, Repository의 "명칭"만 바꿔보겠습니다.

응~ 형이야~ port & adapter 패턴~
여기에 육각형을 그리면?

"나야, 헥사고날"
결국 클린 아키텍처나 헥사고날 아키텍처도 의존 방향과 경계 관리 이야기입니다. OOP의 본질이죠.
그럼 DDD? 도메인모델패턴은요? Bounded Context를 강조하지 않던가요?
Bounded Context도 결국엔 경계 나누기입니다. "클래스 수준의 캡슐화를 컨텍스트 수준으로" 확장한것입니다. 또 OOP네?
"클래스 수준의 캡슐화를 컨텍스트 수준으로" 에서 "클래스 수준의 캡슐화를 모듈 수준으로" 확장하면
모듈러 모놀리스가 됩니다. 얘도 OOP네?
모듈러 모놀리스에서 각 모듈을 떼어내고 별도 서버로 배포하면? MSA입니다.
???
결국, 본질은 같다.
서론에서 말했던 " 도메인 모델 패턴, DDD, 헥사고날 아키텍처, 클린 아키텍처, 양파 아키텍처, 모듈러 모놀리스, MSA, TDD 는 모두 OOP에 대해서 이야기 하고 있습니다." 에서 TDD를 제외한 나머지는 OOP에 대해서 얘기하고있다는걸 깨달았습니다.
그럼 TDD는요?!
설계는 무시하고 TDD만 하면 된다? red - green -refactor만 잘 지키면 된다? 아닙니다.
경계가 무너지면 테스트도 무너집니다. TDD의 본질은 경계를 잘 잡아놨을때 그 경계를 지켜주고, 리팩토링 속도를 올려주는 역할이라고 생각합니다. 설계 먼저, 테스트는 그 다음.
TDD를 하면서 테스트가 어려워지고 복잡해진다고 느껴지면, 설계를 리팩토링하라는 신호를 TDD가 보내는 것입니다.
자, 이제. 화려한 설계원칙, 패러다임들의 근본은 전부 하나를 가르킨다는 것을 알았습니다. 그리고 그것은 사람을 위한, 변경 범위를 최소화 하기 위한 것이라는 것도요. 소프트웨어의 요구사항은 늘 바뀌고 그걸 고치는 것은 사람이 직접했습니다. 그래서 다양한 방법론, 패러다임이 나온것이고요.
근데 요즘은 AI시대입니다.
AI가 코드베이스를 파악하고 AI가 직접 고치는데, 읽기 좋은 코드가 의미가 있을까? 에 대한 실험을 해보려고 하는겁니다.
실험 조건은 다음과 같습니다.
모델 : Opus 4.5
플러그인 : lsp
1. 동일한 요구사항을 구현한다.
2. 한 쪽은 OOP와 헥사고날 아키텍처를 따라서 구현한다.
3. 다른 한 쪽은 트랜잭션 스크립트로 구현하고 최대한 "더럽게" 구현한다.
4. 요구사항 하나를 완료할 때마다 exit 후 사용 토큰을 측정하고 기록한다.
5. 다시 클로드 세션을 열어서 다음 task를 진행한다.
그래서 CleanCode라는 프로젝트와, messyCode라는 프로젝트를 만들고, 각각 claude code에게 구현을 시켜볼겁니다.
먼저, 두 프로젝트가 구현할 api-spec은 아래와 같습니다.
api-spec.md
# Anonymous Board API Specification
## 공통 사항
- Base URL: `/api`
- Content-Type: `application/json`
- 비밀번호 검증 실패 시: `403 Forbidden`
- 리소스 없음: `404 Not Found`
- 유효성 검증 실패: `400 Bad Request`
- 좋아요 사용자 식별: `X-Guest-Id` 헤더 (UUID)
---
## 1. 게시글 CRUD
### 1.1 게시글 작성
`POST /api/posts`
**Request Body:**
```json
{
"title": "string (필수, 1~200자)",
"content": "string (필수)",
"author": "string (필수, 1~50자)",
"password": "string (필수, 4자 이상)",
"hashtags": ["string"] // 선택, 최대 5개, 각 태그 1~30자
}
```
**Response:** `201 Created`
```json
{
"id": 1,
"title": "제목",
"content": "내용",
"author": "작성자",
"hashtags": ["태그1", "태그2"],
"viewCount": 0,
"likeCount": 0,
"commentCount": 0,
"createdAt": "2024-01-01T00:00:00",
"updatedAt": "2024-01-01T00:00:00"
}
```
### 1.2 게시글 상세 조회
`GET /api/posts/{id}`
- 호출 시 조회수(viewCount) 1 증가
**Response:** `200 OK`
```json
{
"id": 1,
"title": "제목",
"content": "내용",
"author": "작성자",
"hashtags": ["태그1", "태그2"],
"viewCount": 43,
"likeCount": 0,
"commentCount": 0,
"createdAt": "2024-01-01T00:00:00",
"updatedAt": "2024-01-01T00:00:00"
}
```
- 이 단계에서는 댓글, 좋아요 관련 필드는 기본값(0, 빈 목록)으로 반환
### 1.3 게시글 수정
`PUT /api/posts/{id}`
**Request Body:**
```json
{
"title": "수정된 제목",
"content": "수정된 내용",
"password": "비밀번호",
"hashtags": ["태그1", "태그3"]
}
```
- 비밀번호 일치 시에만 수정 가능
**Response:** `200 OK` (게시글 상세와 동일한 형태, 단 viewCount 증가 없음)
### 1.4 게시글 삭제
`DELETE /api/posts/{id}`
**Request Body:**
```json
{
"password": "비밀번호"
}
```
- 비밀번호 일치 시에만 삭제 (Hard Delete)
**Response:** `204 No Content`
---
## 2. 댓글 (Comments)
### 2.1 댓글 작성
`POST /api/posts/{postId}/comments`
**Request Body:**
```json
{
"author": "string (필수, 1~50자)",
"password": "string (필수, 4자 이상)",
"content": "string (필수)"
}
```
**Response:** `201 Created`
```json
{
"id": 1,
"author": "댓글작성자",
"content": "댓글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false
}
```
### 2.2 댓글 목록 조회 (더보기)
`GET /api/posts/{postId}/comments`
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-----------------------|
| page | int | 0 | 페이지 번호 (0-based) |
| size | int | 5 | 페이지 크기 (고정 5) |
**Response:** `200 OK`
```json
{
"content": [
{
"id": 1,
"author": "댓글작성자",
"content": "댓글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false
}
],
"page": 0,
"size": 5,
"totalElements": 12,
"hasMore": true
}
```
- 정렬: 최신순 (createdAt DESC)
- `deleted`가 `true`인 댓글은 `content`를 `"삭제된 댓글입니다."` 로 반환
### 2.3 댓글 삭제
`DELETE /api/posts/{postId}/comments/{commentId}`
**Request Body:**
```json
{
"password": "비밀번호"
}
```
- 비밀번호 일치 시 Soft Delete (deleted 플래그 처리)
**Response:** `204 No Content`
### 2.4 게시글 상세 조회에 댓글 포함
`GET /api/posts/{id}` 응답이 아래와 같이 확장된다:
```json
{
"id": 1,
"title": "제목",
"content": "내용",
"author": "작성자",
"hashtags": ["태그1", "태그2"],
"viewCount": 43,
"likeCount": 0,
"commentCount": 5,
"createdAt": "2024-01-01T00:00:00",
"updatedAt": "2024-01-01T00:00:00",
"comments": {
"content": [
{
"id": 10,
"author": "댓글작성자",
"content": "댓글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false
}
],
"page": 0,
"size": 5,
"totalElements": 12,
"hasMore": true
}
}
```
- `comments`: 최신순 5건
- `deleted`가 `true`인 댓글은 `content`를 `"삭제된 댓글입니다."` 로 반환
- `commentCount`: 삭제되지 않은 댓글의 총 수
- 게시글 삭제 시 연관 댓글도 함께 삭제
---
## 3. 게시글 목록 + 검색
### 3.1 게시글 목록 조회
`GET /api/posts`
**Query Parameters:**
| Parameter | Type | Default | Description |
|------------|--------|---------|--------------------------------------------|
| page | int | 0 | 페이지 번호 (0-based) |
| size | int | 10 | 페이지 크기 (10 또는 20만 허용) |
| searchType | string | - | 검색 유형: title, author, hashtag, content |
| keyword | string | - | 검색 키워드 |
**Response:** `200 OK`
```json
{
"totalPostCount": 100,
"totalCommentCount": 523,
"posts": [
{
"id": 1,
"title": "제목",
"author": "작성자",
"createdAt": "2024-01-01T00:00:00",
"commentCount": 5,
"viewCount": 42,
"likeCount": 3,
"isNew": true
}
],
"page": 0,
"size": 10,
"totalPages": 10,
"totalElements": 100
}
```
- `totalPostCount`: 전체 게시글 수 (검색 필터 무관)
- `totalCommentCount`: 전체 댓글 수 (검색 필터 무관, 삭제된 댓글 제외)
- `isNew`: 작성일이 현재 기준 3일 이내이면 `true`
- 정렬: 최신순 (createdAt DESC)
### 3.2 검색
- `searchType`과 `keyword`가 모두 있을 때만 검색 적용
- title, author, content: 부분 일치 검색 (LIKE '%keyword%')
- hashtag: 해시태그 이름과 정확히 일치
---
## 4. 좋아요 (Likes)
### 4.1 좋아요
`POST /api/posts/{postId}/likes`
**Required Header:** `X-Guest-Id: {uuid}`
- `X-Guest-Id` 헤더가 없으면: `400 Bad Request`
- 이미 좋아요한 상태에서 재요청 시: `409 Conflict`
**Response:** `200 OK`
```json
{
"likeCount": 4,
"liked": true
}
```
### 4.2 좋아요 취소
`DELETE /api/posts/{postId}/likes`
**Required Header:** `X-Guest-Id: {uuid}`
- `X-Guest-Id` 헤더가 없으면: `400 Bad Request`
- 좋아요하지 않은 상태에서 요청 시: `409 Conflict`
**Response:** `200 OK`
```json
{
"likeCount": 3,
"liked": false
}
```
### 4.3 게시글 상세 조회에 좋아요 포함
`GET /api/posts/{id}` 응답에 아래 필드가 추가된다:
- `liked`: `X-Guest-Id` 헤더 기준으로 좋아요 여부 (`true`/`false`)
- `X-Guest-Id` 헤더가 없으면 `liked`는 `false`
- `likeCount`: 실제 좋아요 수
- 게시글 삭제 시 연관 좋아요 데이터도 함께 삭제
그리고,
CleanCode 프로젝트에 들어갈 CLAUDE.md는 아래와 같습니다.
# Project: Anonymous Board (Clean Architecture)
## Tech Stack
- Java 17+
- Spring Boot 3.x
- Spring Data JPA
- H2 Database (in-memory)
- BCrypt for password hashing
## Architecture: Hexagonal (Ports & Adapters)
### Package Structure
```
com.board.clean
├── domain/ # 핵심 도메인 (외부 의존성 없음)
│ ├── model/ # 도메인 모델 (Entity가 아닌 순수 도메인 객체)
│ ├── port/
│ │ ├── in/ # Inbound Port (UseCase 인터페이스)
│ │ └── out/ # Outbound Port (Repository 인터페이스)
│ └── exception/ # 도메인 예외
├── application/ # 유스케이스 구현
│ └── service/ # Port(in) 구현체
├── adapter/
│ ├── in/
│ │ └── web/ # Controller + Request/Response DTO
│ └── out/
│ └── persistence/ # JPA Entity + Repository 구현체 (Port(out) 구현)
└── config/ # Spring 설정
```
### Architecture Rules (반드시 준수)
1. **Domain Layer는 외부 프레임워크에 의존하지 않는다**
- `domain/model`에는 JPA 어노테이션, Spring 어노테이션 사용 금지
- 순수 Java 객체로만 구성
- 도메인 모델 안에 비즈니스 규칙을 캡슐화한다
2. **UseCase별 Port 정의**
- 각 유스케이스는 별도의 Inbound Port 인터페이스를 갖는다
- 예: `CreatePostUseCase`, `GetPostListUseCase`, `LikePostUseCase` 등
3. **Adapter는 Port를 통해서만 통신한다**
- Controller → Inbound Port → Service → Outbound Port → Repository
- Controller에서 Repository 직접 접근 금지
4. **DTO 변환은 Adapter 레이어에서 처리한다**
- Web Adapter: Request DTO → Domain Model, Domain Model → Response DTO
- Persistence Adapter: Domain Model ↔ JPA Entity
5. **입력 유효성 검증**
- 형식 검증(길이, 필수값 등): Web Adapter (Controller or Request DTO)
- 비즈니스 규칙 검증: Domain Model 또는 Application Service
6. **에러 처리**
- 도메인 예외를 정의하고, Web Adapter에서 HTTP 상태코드로 변환
- `@RestControllerAdvice`로 글로벌 예외 처리
## Testing
### 테스트 전략
- **통합 테스트 (필수)**: `@SpringBootTest` + `MockMvc`로 API 엔드포인트 검증
- **도메인 단위 테스트 (권장)**: 도메인 모델의 비즈니스 규칙을 순수 단위 테스트로 검증
### 통합 테스트 규칙
- 각 API 엔드포인트별로 정상 케이스와 주요 에러 케이스를 테스트한다
- 테스트 데이터는 각 테스트 메서드에서 직접 설정한다
- H2 in-memory DB를 사용하므로 별도 테스트 DB 설정 불필요
- `@Transactional`로 테스트 간 데이터 격리
### 검증 항목 (모든 API 공통)
- HTTP 상태 코드
- 응답 본문의 필수 필드 존재 여부
- 비즈니스 규칙 (비밀번호 불일치 시 403, 리소스 없음 시 404 등)
## API Specification
- 상위 디렉토리의 `api-spec.md` 참조
## Build & Run
```bash
./gradlew bootRun
./gradlew test
```
그다음, messyCode에 들어갈 CLAUDE.md는 아래와 같습니다.
# Project: Anonymous Board (Messy)
## Tech Stack
- Java 17+
- Spring Boot 3.x
- Spring Data JPA
- H2 Database (in-memory)
- BCrypt for password hashing
## Architecture Rules (반드시 준수)
이 프로젝트는 의도적으로 아래의 스타일로 작성한다. 리팩토링하지 말 것.
### Package Structure
```
com.board.messy
├── controller/ # Controller (비즈니스 로직 포함 가능)
├── service/ # Service (God Class 스타일)
├── repository/ # JPA Repository
├── entity/ # JPA Entity (DTO 겸용)
└── config/ # Spring 설정
```
### 코딩 스타일 (반드시 따를 것)
1. **Service는 기능 도메인별로 하나씩만 만든다**
- `PostService` 하나에 게시글 관련 모든 로직 포함
- `CommentService` 하나에 댓글 관련 모든 로직 포함
- 메서드가 길어져도 분리하지 않는다
2. **JPA Entity를 API 응답에 그대로 사용한다**
- 별도의 Response DTO 클래스를 만들지 않는다
- `@JsonIgnore`로 숨길 필드를 처리한다
- 필요하면 Entity에 API 응답용 필드/메서드를 추가한다
- 목록 조회 등 별도 형태가 필요한 경우에만 `Map<String, Object>` 또는 inline DTO를 사용한다
3. **비즈니스 로직을 Controller에 일부 포함시킨다**
- 예: 비밀번호 검증, 조건 분기 등을 Controller에서 직접 처리해도 된다
- Service와 Controller 사이에 로직이 분산되어도 괜찮다
4. **중복 코드를 허용한다**
- 비슷한 로직이 여러 곳에 있어도 공통 메서드로 추출하지 않는다
- 각 메서드가 독립적으로 전체 로직을 포함한다
5. **에러 처리는 각 메서드에서 직접 한다**
- 글로벌 예외 핸들러를 만들지 않는다
- 각 Controller 메서드에서 try-catch 또는 if-else로 직접 처리한다
- `ResponseEntity`로 상태코드를 직접 반환한다
6. **매직 넘버와 하드코딩을 사용한다**
- 상수를 별도로 정의하지 않는다
- 예: 페이지 크기를 `10`, 해시태그 최대 개수를 `5`로 코드에 직접 작성
7. **도메인 검증 로직을 Entity나 별도 클래스로 분리하지 않는다**
- 모든 검증은 Service 또는 Controller 메서드 내에서 if문으로 처리
## Testing
### 테스트 규칙
- `@SpringBootTest` + `MockMvc`로 API 엔드포인트를 통합 테스트한다
- 단위 테스트는 작성하지 않는다. 통합 테스트만 작성한다
- 하나의 테스트 클래스에 관련 API 테스트를 모두 포함한다 (예: `PostApiTest`에 게시글 CRUD 전부)
- 테스트 데이터는 각 테스트 메서드에서 직접 설정한다
- H2 in-memory DB를 사용하므로 별도 테스트 DB 설정 불필요
- `@Transactional`로 테스트 간 데이터 격리
### 검증 항목 (모든 API 공통)
- HTTP 상태 코드
- 응답 본문의 필수 필드 존재 여부
- 비즈니스 규칙 (비밀번호 불일치 시 403, 리소스 없음 시 404 등)
## API Specification
- 상위 디렉토리의 `api-spec.md` 참조
## Build & Run
```bash
./gradlew bootRun
./gradlew test
```
이제 7가지의 Task를 던질겁니다. 1~4번은 단순히 새로운 기능 추가이고, 5~7번은 기존 요구사항 변경입니다.
1. 게시글 CRUD
Spring Boot 프로젝트를 초기 세팅하고, 게시글 CRUD API를 구현해줘.
## 구현할 API
상위 디렉토리의 `api-spec.md`를 참조하여 아래 API를 구현해줘:
1. **게시글 작성** (POST /api/posts)
- 제목, 내용, 작성자, 비밀번호, 해시태그(최대 5개)를 입력받는다
- 비밀번호는 BCrypt로 암호화하여 저장한다
- 유효성 검증: 제목 1~200자, 작성자 1~50자, 비밀번호 4자 이상, 해시태그 최대 5개/각 1~30자
2. **게시글 상세 조회** (GET /api/posts/{id})
- 조회 시 viewCount를 1 증가시킨다
- 댓글과 좋아요 관련 필드는 이 단계에서는 기본값(0, 빈 목록)으로 반환한다
3. **게시글 수정** (PUT /api/posts/{id})
- 비밀번호가 일치해야 수정 가능
- 제목, 내용, 해시태그를 수정할 수 있다
4. **게시글 삭제** (DELETE /api/posts/{id})
- 비밀번호가 일치해야 삭제 가능
- Hard Delete
## 테스트
구현한 API에 대한 테스트 코드를 작성해줘:
- 각 엔드포인트의 정상 케이스
- 주요 에러 케이스 (유효성 검증 실패, 비밀번호 불일치, 존재하지 않는 리소스 등)
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
2. 댓글 CRUD
댓글 기능을 구현해줘.
## 구현할 API
상위 디렉토리의 `api-spec.md`를 참조하여 아래 API를 구현해줘:
1. **댓글 작성** (POST /api/posts/{postId}/comments)
- 작성자, 비밀번호, 내용을 입력받는다
- 비밀번호는 BCrypt로 암호화하여 저장한다
- 존재하지 않는 게시글에 댓글 작성 시 404
2. **댓글 목록 조회** (GET /api/posts/{postId}/comments)
- 최신순 정렬, 5건씩 페이징
- `deleted`가 true인 댓글은 content를 `"삭제된 댓글입니다."`로 반환
- `hasMore`로 다음 페이지 존재 여부 반환
3. **댓글 삭제** (DELETE /api/posts/{postId}/comments/{commentId})
- 비밀번호 일치 시 Soft Delete (deleted 플래그 true 처리)
- 실제 데이터는 삭제하지 않음
4. **게시글 상세 조회 수정** (GET /api/posts/{id})
- 기존 게시글 상세 응답에 댓글 정보를 포함시킨다
- 최신 5건의 댓글을 함께 반환
- `deleted`가 true인 댓글은 content를 `"삭제된 댓글입니다."`로 반환
- commentCount 필드를 실제 댓글 수로 반환
## 테스트
구현한 API에 대한 테스트 코드를 작성해줘:
- 각 엔드포인트의 정상 케이스
- 주요 에러 케이스 (존재하지 않는 게시글, 비밀번호 불일치, 삭제된 댓글 표시 등)
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
3. 게시글 조회/검색
게시글 목록 조회와 검색 기능을 구현해줘.
## 구현할 API
상위 디렉토리의 `api-spec.md`를 참조하여 아래 기능을 구현해줘:
1. **게시글 목록 조회** (GET /api/posts)
- 최신순(createdAt DESC) 정렬
- 페이징: page(0-based), size(10 또는 20만 허용, 기본값 10)
- 응답에 포함할 정보:
- `totalPostCount`: 전체 게시글 수 (검색 필터 무관, 전체)
- `totalCommentCount`: 전체 댓글 수 (검색 필터 무관, 전체, 삭제된 댓글 제외)
- 각 게시글: id, title, author, createdAt, commentCount, viewCount, likeCount
- `isNew`: 작성일이 현재 기준 3일 이내이면 true
- 페이징 메타: page, size, totalPages, totalElements
2. **검색 기능**
- `searchType` 파라미터: title, author, hashtag, content 중 하나
- `keyword` 파라미터: 검색어
- searchType과 keyword가 모두 있을 때만 검색 적용
- 부분 일치 검색 (LIKE '%keyword%')
- hashtag 검색은 해시태그 이름과 정확히 일치
## 테스트
구현한 API에 대한 테스트 코드를 작성해줘:
- 페이징 정상 동작 (size 10, 20)
- 허용되지 않는 size 값 처리
- 각 searchType별 검색 동작
- totalPostCount, totalCommentCount 정확성
- isNew 플래그 동작
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
4. 좋아요 기능
좋아요(Like) 기능을 구현해줘.
## 구현할 API
상위 디렉토리의 `api-spec.md`를 참조하여 아래 API를 구현해줘:
1. **좋아요** (POST /api/posts/{postId}/likes)
- `X-Guest-Id` 헤더(UUID)로 사용자를 식별한다
- `X-Guest-Id` 헤더가 없으면 400 Bad Request
- 이미 좋아요한 상태에서 재요청 시 409 Conflict
- 좋아요 후 해당 게시글의 likeCount와 liked 상태를 반환
2. **좋아요 취소** (DELETE /api/posts/{postId}/likes)
- `X-Guest-Id` 헤더(UUID)로 사용자를 식별한다
- `X-Guest-Id` 헤더가 없으면 400 Bad Request
- 좋아요하지 않은 상태에서 요청 시 409 Conflict
- 취소 후 해당 게시글의 likeCount와 liked 상태를 반환
3. **게시글 상세 조회 수정** (GET /api/posts/{id})
- 기존 응답에 `liked` 필드를 추가한다
- `X-Guest-Id` 헤더가 있으면 해당 guest의 좋아요 여부를 반환
- `X-Guest-Id` 헤더가 없으면 `liked`는 false
- `likeCount`를 실제 좋아요 수로 반환
4. **게시글 삭제 시**
- 게시글 삭제 시 연관된 좋아요 데이터도 함께 삭제
## 테스트
구현한 API에 대한 테스트 코드를 작성해줘:
- 좋아요 / 좋아요 취소 정상 동작
- 중복 좋아요 시 409
- 좋아요하지 않은 상태에서 취소 시 409
- X-Guest-Id 헤더 없을 때 400
- 게시글 상세에서 liked 필드 정확성
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
5. 비밀번호 변경 기능 추가
게시글 비밀번호 변경 기능을 추가해줘.
## 변경 요구사항
기존에 게시글 수정/삭제 시에만 사용하던 비밀번호를 **별도로 변경**할 수 있는 API를 추가한다.
### 새 API: 게시글 비밀번호 변경
`PATCH /api/posts/{id}/password`
**Request Body:**
```json
{
"currentPassword": "현재 비밀번호",
"newPassword": "새 비밀번호 (4자 이상)"
}
```
**Response:**
- 성공 시: `200 OK` (본문 없음 또는 `{"message": "비밀번호가 변경되었습니다."}`)
- 현재 비밀번호 불일치: `403 Forbidden`
- 새 비밀번호 유효성 실패 (4자 미만): `400 Bad Request`
- 게시글 없음: `404 Not Found`
### 구현 시 고려사항
- 새 비밀번호도 BCrypt로 암호화하여 저장
- 기존 비밀번호 검증 로직을 재사용하거나 참고할 것
## 테스트
- 비밀번호 변경 정상 동작
- 변경 후 새 비밀번호로 게시글 수정/삭제 가능 확인
- 현재 비밀번호 불일치 시 403
- 새 비밀번호 4자 미만 시 400
- 존재하지 않는 게시글 404
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
6. 대댓글 기능 추가 (1-depth)
댓글에 대댓글(답글) 기능을 추가해줘.
## 변경 요구사항
기존 댓글 시스템을 확장하여 **댓글에 대한 답글**을 달 수 있게 한다. 답글은 1단계만 허용한다 (답글에 답글 불가).
### API 변경사항
#### 1. 답글 작성
`POST /api/posts/{postId}/comments/{commentId}/replies`
**Request Body:**
```json
{
"author": "string (필수, 1~50자)",
"password": "string (필수, 4자 이상)",
"content": "string (필수)"
}
```
**Response:** `201 Created`
```json
{
"id": 2,
"parentId": 1,
"author": "답글작성자",
"content": "답글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false
}
```
- 부모 댓글이 이미 답글인 경우 (parentId가 있는 경우): `400 Bad Request`
- 부모 댓글이 삭제된 경우에도 답글 작성 가능
#### 2. 댓글 목록 조회 변경
`GET /api/posts/{postId}/comments` 응답에서:
```json
{
"content": [
{
"id": 1,
"parentId": null,
"author": "댓글작성자",
"content": "댓글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false,
"replyCount": 3
}
],
"page": 0,
"size": 5,
"totalElements": 12,
"hasMore": true
}
```
- `parentId`: 답글인 경우 부모 댓글 ID, 일반 댓글은 `null`
- `replyCount`: 해당 댓글의 답글 수 (삭제된 답글 제외)
- 목록에는 **일반 댓글만** 표시 (답글은 별도 조회)
#### 3. 답글 목록 조회 (새 API)
`GET /api/posts/{postId}/comments/{commentId}/replies`
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| page | int | 0 | 페이지 번호 |
| size | int | 5 | 페이지 크기 |
**Response:** `200 OK`
```json
{
"content": [
{
"id": 2,
"parentId": 1,
"author": "답글작성자",
"content": "답글 내용",
"createdAt": "2024-01-02T00:00:00",
"deleted": false
}
],
"page": 0,
"size": 5,
"totalElements": 3,
"hasMore": false
}
```
- 정렬: 오래된순 (createdAt ASC) — 대화 흐름 유지
- 삭제된 답글은 `"삭제된 댓글입니다."` 처리 동일
#### 4. 댓글 삭제 시
- 답글이 있는 댓글을 삭제해도 답글은 유지됨
- 답글 삭제는 기존 댓글 삭제 API와 동일하게 동작
### 기존 API 영향
- `GET /api/posts/{id}` (게시글 상세): comments 필드에 `replyCount` 추가
- `commentCount`는 일반 댓글 + 답글 전체 수
## 테스트
- 답글 작성 정상 동작
- 답글에 답글 시도 시 400
- 답글 목록 조회 (오래된순 정렬)
- 삭제된 부모 댓글에 답글 작성 가능
- replyCount 정확성
- 부모 댓글 삭제 후 답글 유지 확인
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
7. 게시글 정렬 옵션 추가
게시글 목록에 정렬 옵션을 추가해줘.
## 변경 요구사항
기존에 최신순(createdAt DESC)으로만 정렬되던 게시글 목록에 **정렬 옵션**을 추가한다.
### API 변경사항
`GET /api/posts`
**추가 Query Parameter:**
| Parameter | Type | Default | Description |
|-----------|--------|----------|-------------|
| sort | string | latest | 정렬 기준: latest, views, likes |
**정렬 기준:**
- `latest`: 최신순 (createdAt DESC) — 기존과 동일
- `views`: 조회수 높은순 (viewCount DESC), 동일 시 최신순
- `likes`: 좋아요 많은순 (likeCount DESC), 동일 시 최신순
**에러 처리:**
- 허용되지 않은 sort 값: `400 Bad Request`
### 구현 시 고려사항
- 기존 검색 기능과 함께 동작해야 함 (searchType + keyword + sort 조합)
- 페이징과 함께 동작해야 함
## 테스트
- sort=latest 정상 동작 (기존과 동일)
- sort=views 정렬 확인
- sort=likes 정렬 확인
- 동일 값일 때 2차 정렬(최신순) 확인
- 검색 + 정렬 조합 동작
- 허용되지 않은 sort 값 시 400
- sort 파라미터 없을 때 기본값(latest) 동작
- CLAUDE.md의 테스트 규칙을 따를 것
구현 완료 후 `./gradlew test`로 테스트가 통과하는지 확인해줘.
CLAUDE.md에 정의된 아키텍처 규칙을 반드시 따라서 구현해줘.
구현된 코드와 사용한 md, 토큰추출 스크립트 등은 아래 레포지토리에서 확인 가능합니다.
https://github.com/yoon-yoo-tak/Clean_Architecture_Experiment
GitHub - yoon-yoo-tak/Clean_Architecture_Experiment
Contribute to yoon-yoo-tak/Clean_Architecture_Experiment development by creating an account on GitHub.
github.com
이제, 각 기능을 수행할 때마다 기록을 할것입니다.
클로드코드는 세션마다 세션 로그 JSONL파일에 모든 API호출의 토큰 사용량을 기록합니다. 그래서 각 세션에서 작업 수행하고 세션을 나갔다 들어오는 행위를 반복한 것입니다.
기록할 템플릿은 아래와 같습니다.
# Experiment Results: [messy/clean]
## Phase 1: 프로젝트 초기 세팅 + 게시글 CRUD
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 2: 댓글 기능
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 3: 게시글 목록 + 검색
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 4: 좋아요 기능
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 5: 비밀번호 변경 기능 (기능 변경)
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 6: 대댓글 기능 (기능 변경)
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Phase 7: 정렬 옵션 (기능 변경)
| Metric | Value |
|---------------------|---|
| Input Tokens | |
| Output Tokens | |
| Total Tokens | |
| Cache Creation | |
| Cache Read | |
| Tool Calls | |
| Conversation Turns | |
| Build Success | |
| API Test Pass | |
| Notes | |
## Total Summary
| Metric | Value |
|---------------------|-------|
| Total Input Tokens | |
| Total Output Tokens | |
| Total Tokens | |
| Total Tool Calls | |
| Total Turns | |
결과는??
(모든 사진에서 왼쪽이 clean, 오른쪽이 messy입니다.)
첫 번째 기능 구현 결과입니다.


두 번째 기능 구현 결과입니다.


세 번째 기능 구현 결과입니다.


네 번째 기능 구현 결과입니다.


다섯 번째 기능 구현 결과입니다.


여섯 번째 기능 구현 결과입니다.


마지막 기능 구현 결과입니다.


Total Summary입니다.


또한, 평균적으로 수행시간도 Clean쪽이 더 길었습니다.
Total Summary 비교입니다.
Metric: 총 사용 토큰
CleanCode: 12,890,844
MessyCode: 6,908,059
차이: Clean이 1.87배 더 사용
────────────────────────────────────────
Metric: 출력 토큰
CleanCode: 87,805
MessyCode: 58,300
차이: Clean이 1.51배 더 사용
────────────────────────────────────────
Metric: Tool 호출
CleanCode: 237
MessyCode: 143
차이: Clean이 1.66배 더 사용
────────────────────────────────────────
Metric: 대화 턴
CleanCode: 381
MessyCode: 214
차이: Clean이 1.78배 더 사용
사실, 실험을 하기 전에 당연히 이해하기 쉬운 코드가 초기에는 토큰이 더 들더라도 가면 갈수록 격차가 줄어들 줄 알았습니다.
그런데, 거의 모든 구간에서 조금 난잡한? 코드가 비교적 토큰 사용을 효율적으로 하는 것을 볼 수 있습니다.
왜 그럴까???
토큰 사용의 기조를 보면,
1. 파일 수가 토큰 사용량을 지배한다.
CleanCode의 구조는 하나의 기능을 구현하거나 수정할 때, 경계를 지키고 분리를 한 탓에 6~8개의 파일을 건드려야 합니다.
AI는 각 파일을 읽고, 관계를 파악하고, 일관성 있게 수정해야 합니다.
MessyCode의 구조로는 2~3개의 파일만 건드리면 됩니다. Controller에 로직이 있고, Service에 나머지 로직이 있습니다. Entity가 곧 DTO입니다.
2. 기능 추가보다 기능 변경에서 차이가 커진다.

기능 변경은 기존 코드를 "이해"해야 합니다.
clean architecture를 따르는 코드에서는
"비밀번호 검증로직이 어디있지?" Domain? Service? Adapter?
"이 Port를 구현한건 뭐지?" -> Adapter를 찾야아함
"이 변경이 다른 UseCase에 영향을 주나?" -> 모든 관련 Port/Service확인
Messy에서는
"비밀번호 검증 로직이 어디 있지?" → PostService.java 하나 열면 다 있음
끝.
3. 대댓글이 아키텍처 차이를 극명하게 보여준다.
대댓글 기능은 기존 도메인 모델을 변경해야 합니다.
- Comment에 parentId 추가
- 자기참조 관계 설정
- 조회 로직 변경 (일반 댓글 vs 답글 분리)
- 새 API 엔드포인트 추가
Clean (3,696,764 토큰, 62 tool calls, 94 turns)
- Domain Model(Comment) 수정
- Outbound Port 수정
- Persistence Adapter(JPA Entity + Repository) 수정
- Inbound Port(UseCase 인터페이스) 추가
- Application Service 수정
- Web Adapter(Controller + DTO) 수정
- 각 레이어 간 매핑 로직 수정
Edit:23번이 이걸 말해줍니다. 23개 수정 지점이 있었어요.
Messy (790,704 토큰, 17 tool calls, 23 turns)
- Entity 수정 (자기참조 추가)
- Repository에 메서드 추가
- Service에 로직 추가
- Controller에 엔드포인트 추가
Edit:9번. 모든 게 한 곳에 모여있으니 수정도 간단합니다.
4. Output Tokens도 Clean이 더 많다.

Phase1에서 Messy가 더 많은건 아마도 보일러 플레이트가 적어서 한 번에 많이 썼기 때문인것 같습니다.
그러나 Phase 2, 6처럼 기존 코드 수정이 필요한 경우, Clean은 여러 파일을 수정하면서 각각 생성해야 하고, Messy는 적은 파일을 수정하므로 생성량도 적다는 것을 유추할 수 있습니다.
그래서 추측컨데,,,,(진짜 개인적인 추측임)
1. AI는 "찾기"에 비용이 많이 든다
Clean 아키텍처의 장점은 인간이 책임을 분리해서 이해하기 쉽다는 것입니다.
하지만 AI는?
- 전체 구조를 한 번에 파악하기 어렵다 (파일을 하나씩 읽어야 함)
- "이 기능의 진입점이 어디지?"를 찾는 데 탐색이 필요하다
- 레이어 간 매핑을 이해하는 데 추가 읽기가 필요하다
Messy는 모든 게 한 파일에 있습니다. AI가 PostService.java 하나 읽으면 게시글 관련 모든 로직이 눈에 들어옵니다.
2. AI는 "일관성 유지"에 비용이 많이 든다
Clean에서 Comment 도메인을 수정하면
- Domain Model 수정
- JPA Entity 수정 (매핑 일치해야 함)
- Port 인터페이스 수정
- Service에서 변환 로직 수정
- DTO 수정
5곳을 일관성 있게 수정해야 합니다. AI는 각 수정 후 다른 곳과 맞는지 확인합니다.
Messy에서는 Entity 수정하면 끝입니다. Entity가 곧 DTO니까. ㄹㅇ ㅋㅋ
3. 인간의 "읽기 좋음"과 AI의 "처리 효율"은 다르다
인간은:
- 한 번에 화면에 보이는 양이 제한적이다
- 파일을 분리하면 "이 파일은 이 책임"이라고 머릿속에서 캐싱한다
- 분리된 구조가 인지 부하를 줄여준다
AI는:
- 컨텍스트 윈도우 안에서는 "한 번에" 다 본다
- 파일이 분리되면 오히려 매번 새로 읽어야 한다
- 분리된 구조가 탐색 비용을 증가시킨다
라고 정리할 수 있을것 같습니다.
물론, 이 실험이 말해주지 않는 것도 있습니다.
1. 정확성: 둘 다 Build Success, API Test Pass가 Y입니다. 토큰을 더 썼다고 더 정확한 건 아닐수도 있습니다.
2. 유지보수성: 6개월 후 다른 개발자가 코드를 볼 때의 이해도는 측정하지 않았습니다.
3. 확장성: 프로젝트가 10배 커졌을 때 어떻게 될지는 모릅니다. Messy는 God Class가 되면 AI도 읽기 어려워질 수 있습니다.
4. 재현성: 1회 실험입니다. AI의 비결정성 때문에 다시 하면 다른 결과가 나올 수 있습니다.
결론
"인간이 읽기 좋은 코드가 AI한테도 좋을까?"
이 실험이서의 답: 아니요, 적어도 토큰 효율성 측면에서는 그렇지 않습니다.
- Clean 아키텍처는 파일 분리로 인한 탐색 비용이 큽니다.
- 특히 기능 변경 시 여러 레이어를 수정해야 해서 비용이 급증합니다.
- Messy 코드는 "모든 게 한 곳에"라서 AI가 빠르게 찾고, 빠르게 수정합니다.
하지만 이게 "Messy가 좋다"는 의미는 절대! 아닙니다. 오히려 AI 도구 사용 시 아키텍처 선택의 트레이드오프를 보여줍니다.
- Clean: 토큰 비용 높음, 인간 유지보수 용이
- Messy: 토큰 비용 낮음, 인간 유지보수 어려움 (코드가 커지면)
소감
솔직히, 이 실험을 해보는거 어떻겠냐고 주변에 말했을때 거의 모두가 "무조건 클린 아키텍처가 토큰을 덜 먹을거다" 라는 말을 했습니다.
저 또한 그렇게 생각했고, AI한테 물어봤을때도 똑같이 말했거든요.
아래의 글들은 AI시대에 아키텍처의 중요성에 대해 말하는 글들입니다.
https://shapedthoughts.io/ai-coding-structured-requirements-enterprise-software/
이런 글이 있다는것도 조사하면서 알았습니다.
그래서 그냥 일단 해보자 마인드로 실험을 해봤는데, 의외의 결과가 나와서 놀랐고 재미있었습니다.
물론, 단순한 기능만을 구현해서 이런결과가 나왔을 수도 있습니다.
매 Task를 수행하고 나서 /exit으로 나가버려서 그랬을 수도 있습니다.
plan모드를 사용하지 않아서 그랬을 수도 있습니다.
영향을 주는 요소는 엄청 많을 것입니다.
확실한건, 단순한 기능을 구현하는데에는 아키텍처가 가성비가 안좋을수도 있다. 입니다.
그렇다고 제가 앞으로 AI를 이용해서 개발할때 유지보수성 박살난 코드를 만들라고 하지 않을겁니다.
저도 유지보수성 좋은 아키텍처, 사람이 이해하기 쉬운 아키텍처를 선호합니다. 어쨋든 아직까지는 인간이 체킹을 하고, 개입을 해야하긴 하니까요. 먼 미래 AI를 블랙박스처럼 사용하게 되는 날이 온다면 어떻게 될지도 궁금하긴 합니다.
그 전까지 AI를 잘 쓰는 방법, 잘 부려먹는 방법에 대해 끊임없이 연구하고 실험하겠습니다. 긴 글 읽어주셔서 감사합니다.
끝.
참고 문서
https://tech.kakaobank.com/posts/2411-solid-truth-or-myths-for-developers/
모든 개발자가 알아야 할 SOLID의 진실 혹은 거짓
기술 면접 자리에서 SOLID 5대 원칙에 대한 질문을 받아보신 분이라면 주목! 이 글에서는 SOLID 원칙의 역사와 장점, 그리고 각각의 원칙에서 중요한 점을 면접 상황 예시를 통해 가볍게 풀어보았습
tech.kakaobank.com
자바/스프링 개발자를 위한 실용주의 프로그래밍 | 김우근 - 교보문고
자바/스프링 개발자를 위한 실용주의 프로그래밍 | 소프트웨어 개발을 잘하고 싶다면 ‘개발’ 공부를 해야 합니다! 자바 개발자가 코틀린 같은 신생 언어를 다룰 수 있게 된다고 해서 개발을
product.kyobobook.co.kr
'TW' 카테고리의 다른 글
| 다음에 들어올 개발자분에게 보여주고 싶은 것 (4) | 2026.02.12 |
|---|
